xref: /third_party/lame/frontend/parse.c (revision 159b3361)
1/*
2 *      Command line parsing related functions
3 *
4 *      Copyright (c) 1999 Mark Taylor
5 *                    2000-2017 Robert Hegemann
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23/* $Id$ */
24
25#ifdef HAVE_CONFIG_H
26# include <config.h>
27#endif
28
29#include <assert.h>
30#include <ctype.h>
31#include <math.h>
32
33#ifdef STDC_HEADERS
34# include <stdio.h>
35# include <stdlib.h>
36# include <string.h>
37#else
38# ifndef HAVE_STRCHR
39#  define strchr index
40#  define strrchr rindex
41# endif
42char   *strchr(), *strrchr();
43# ifndef HAVE_MEMCPY
44#  define memcpy(d, s, n) bcopy ((s), (d), (n))
45#  define memmove(d, s, n) bcopy ((s), (d), (n))
46# endif
47#endif
48
49
50#ifdef HAVE_LIMITS_H
51# include <limits.h>
52#endif
53
54#include "lame.h"
55
56#include "parse.h"
57#include "main.h"
58#include "get_audio.h"
59#include "version.h"
60#include "console.h"
61
62#undef dimension_of
63#define dimension_of(array) (sizeof(array)/sizeof(array[0]))
64
65#ifdef WITH_DMALLOC
66#include <dmalloc.h>
67#endif
68
69
70#ifdef HAVE_ICONV
71#include <iconv.h>
72#include <errno.h>
73#ifdef HAVE_LANGINFO_H
74#include <locale.h>
75#include <langinfo.h>
76#endif
77#if defined(__KLIBC__) && !defined(iconv_open)
78/* kLIBC iconv_open() does not support UTF-16LE and //TRANSLIT */
79static iconv_t os2_iconv_open (const char *tocode, const char *fromcode)
80{
81    char to[strlen(tocode) + 1];
82    char from[strlen(fromcode) + 1];
83    char *p;
84
85    strcpy(to, tocode);
86    strcpy(from, fromcode);
87
88    if (!strncmp(to, "UTF-16", 6))
89    {
90        strcpy(to, "UCS-2");
91        memmove(to + 5, to + 6, strlen(to + 6));
92    }
93
94    p = strstr(to, "//");
95    if (p)
96        *p = '\0';
97
98    if (!strncmp(from, "UTF-16", 6))
99    {
100        strcpy(from, "UCS-2");
101        memmove(from + 5, from + 6, strlen(from + 6));
102    }
103
104    p = strstr(from, "//");
105    if (p)
106        *p = '\0';
107
108    return iconv_open(to, from);
109}
110
111#define iconv_open(t, f) os2_iconv_open(t, f)
112#endif /* KLIBC iconv */
113#endif /* HAVE_ICONV */
114
115#if defined _ALLOW_INTERNAL_OPTIONS
116#define INTERNAL_OPTS 1
117#else
118#define INTERNAL_OPTS 0
119#endif
120
121#if (INTERNAL_OPTS!=0)
122#include "set_get.h"
123#define DEV_HELP(a) a
124#else
125#define DEV_HELP(a)
126#define lame_set_tune(a,b) (void)0
127#define lame_set_short_threshold(a,b,c) (void)0
128#define lame_set_maskingadjust(a,b) (void)0
129#define lame_set_maskingadjust_short(a,b) (void)0
130#define lame_set_ATHcurve(a,b) (void)0
131#define lame_set_preset_notune(a,b) (void)0
132#define lame_set_substep(a,b) (void)0
133#define lame_set_subblock_gain(a,b) (void)0
134#define lame_set_sfscale(a,b) (void)0
135#endif
136
137static int const lame_alpha_version_enabled = LAME_ALPHA_VERSION;
138static int const internal_opts_enabled = INTERNAL_OPTS;
139
140/* GLOBAL VARIABLES.  set by parse_args() */
141/* we need to clean this up */
142
143ReaderConfig global_reader = { sf_unknown, 0, 0, 0, 0 };
144WriterConfig global_writer = { 0 };
145
146UiConfig global_ui_config = {0,0,0,0};
147
148DecoderConfig global_decoder;
149
150RawPCMConfig global_raw_pcm =
151{ /* in_bitwidth */ 16
152, /* in_signed   */ -1
153, /* in_endian   */ ByteOrderLittleEndian
154};
155
156
157
158/* possible text encodings */
159typedef enum TextEncoding
160{ TENC_RAW     /* bytes will be stored as-is into ID3 tags, which are Latin1 per definition */
161, TENC_LATIN1  /* text will be converted from local encoding to Latin1, as ID3 needs it */
162, TENC_UTF16   /* text will be converted from local encoding to Unicode, as ID3v2 wants it */
163} TextEncoding;
164
165#ifdef HAVE_ICONV
166#define ID3TAGS_EXTENDED
167/* search for Zero termination in multi-byte strings */
168static size_t
169strlenMultiByte(char const* str, size_t w)
170{
171    size_t n = 0;
172    if (str != 0) {
173        size_t i;
174        for (n = 0; ; ++n) {
175            size_t x = 0;
176            for (i = 0; i < w; ++i) {
177                x += *str++ == 0 ? 1 : 0;
178            }
179            if (x == w) {
180                break;
181            }
182        }
183    }
184    return n;
185}
186
187static char*
188currentCharacterEncoding()
189{
190#ifdef HAVE_LANGINFO_H
191    char* cur_code = nl_langinfo(CODESET);
192#else
193    char* env_lang = getenv("LANG");
194    char* xxx_code = env_lang == NULL ? NULL : strrchr(env_lang, '.');
195    char* cur_code = xxx_code == NULL ? "" : xxx_code+1;
196#endif
197    return cur_code;
198}
199
200static size_t
201currCharCodeSize(void)
202{
203    size_t n = 1;
204    char dst[32];
205    char* src = "A";
206    char* cur_code = currentCharacterEncoding();
207    iconv_t xiconv = iconv_open(cur_code, "ISO_8859-1");
208    if (xiconv != (iconv_t)-1) {
209        for (n = 0; n < 32; ++n) {
210            char* i_ptr = src;
211            char* o_ptr = dst;
212            size_t srcln = 1;
213            size_t avail = n;
214            size_t rc = iconv(xiconv, &i_ptr, &srcln, &o_ptr, &avail);
215            if (rc != (size_t)-1) {
216                break;
217            }
218        }
219        iconv_close(xiconv);
220    }
221    return n;
222}
223
224#if 0
225static
226char* fromLatin1( char* src )
227{
228    char* dst = 0;
229    if (src != 0) {
230        size_t const l = strlen(src);
231        size_t const n = l*4;
232        dst = calloc(n+4, 4);
233        if (dst != 0) {
234            char* cur_code = currentCharacterEncoding();
235            iconv_t xiconv = iconv_open(cur_code, "ISO_8859-1");
236            if (xiconv != (iconv_t)-1) {
237                char* i_ptr = src;
238                char* o_ptr = dst;
239                size_t srcln = l;
240                size_t avail = n;
241                iconv(xiconv, &i_ptr, &srcln, &o_ptr, &avail);
242                iconv_close(xiconv);
243            }
244        }
245    }
246    return dst;
247}
248
249static
250char* fromUtf16( char* src )
251{
252    char* dst = 0;
253    if (src != 0) {
254        size_t const l = strlenMultiByte(src, 2);
255        size_t const n = l*4;
256        dst = calloc(n+4, 4);
257        if (dst != 0) {
258            char* cur_code = currentCharacterEncoding();
259            iconv_t xiconv = iconv_open(cur_code, "UTF-16LE");
260            if (xiconv != (iconv_t)-1) {
261                char* i_ptr = (char*)src;
262                char* o_ptr = dst;
263                size_t srcln = l*2;
264                size_t avail = n;
265                iconv(xiconv, &i_ptr, &srcln, &o_ptr, &avail);
266                iconv_close(xiconv);
267            }
268        }
269    }
270    return dst;
271}
272#endif
273
274static
275char* toLatin1( char* src )
276{
277    size_t w = currCharCodeSize();
278    char* dst = 0;
279    if (src != 0) {
280        size_t const l = strlenMultiByte(src, w);
281        size_t const n = l*4;
282        dst = calloc(n+4, 4);
283        if (dst != 0) {
284            char* cur_code = currentCharacterEncoding();
285            iconv_t xiconv = iconv_open("ISO_8859-1//TRANSLIT", cur_code);
286            if (xiconv != (iconv_t)-1) {
287                char* i_ptr = (char*)src;
288                char* o_ptr = dst;
289                size_t srcln = l*w;
290                size_t avail = n;
291                iconv(xiconv, &i_ptr, &srcln, &o_ptr, &avail);
292                iconv_close(xiconv);
293            }
294        }
295    }
296    return dst;
297}
298
299
300static
301char* toUtf16( char* src )
302{
303    size_t w = currCharCodeSize();
304    char* dst = 0;
305    if (src != 0) {
306        size_t const l = strlenMultiByte(src, w);
307        size_t const n = (l+1)*4;
308        dst = calloc(n+4, 4);
309        if (dst != 0) {
310            char* cur_code = currentCharacterEncoding();
311            iconv_t xiconv = iconv_open("UTF-16LE//TRANSLIT", cur_code);
312            dst[0] = 0xff;
313            dst[1] = 0xfe;
314            if (xiconv != (iconv_t)-1) {
315                char* i_ptr = (char*)src;
316                char* o_ptr = &dst[2];
317                size_t srcln = l*w;
318                size_t avail = n;
319                iconv(xiconv, &i_ptr, &srcln, &o_ptr, &avail);
320                iconv_close(xiconv);
321            }
322        }
323    }
324    return dst;
325}
326#endif
327
328#if defined( _WIN32 ) && !defined(__MINGW32__)
329#define ID3TAGS_EXTENDED
330
331char* toLatin1(char const* s)
332{
333    return utf8ToLatin1(s);
334}
335
336unsigned short* toUtf16(char const* s)
337{
338    return utf8ToUtf16(s);
339}
340#endif
341
342static int evaluateArgument(char const* token, char const* arg, char* _EndPtr)
343{
344    if (arg != 0 && arg != _EndPtr)
345        return 1;
346    error_printf("WARNING: argument missing for '%s'\n", token);
347    return 0;
348}
349
350static int getDoubleValue(char const* token, char const* arg, double* ptr)
351{
352    char *_EndPtr=0;
353    double d = strtod(arg, &_EndPtr);
354    if (ptr != 0) {
355        *ptr = d;
356    }
357    return evaluateArgument(token, arg, _EndPtr);
358}
359
360static int getIntValue(char const* token, char const* arg, int* ptr)
361{
362    char *_EndPtr=0;
363    long d = strtol(arg, &_EndPtr, 10);
364    if (ptr != 0) {
365        *ptr = d;
366    }
367    return evaluateArgument(token, arg, _EndPtr);
368}
369
370#ifdef ID3TAGS_EXTENDED
371static int
372set_id3v2tag(lame_global_flags* gfp, int type, unsigned short const* str)
373{
374    switch (type)
375    {
376        case 'a': return id3tag_set_textinfo_utf16(gfp, "TPE1", str);
377        case 't': return id3tag_set_textinfo_utf16(gfp, "TIT2", str);
378        case 'l': return id3tag_set_textinfo_utf16(gfp, "TALB", str);
379        case 'g': return id3tag_set_textinfo_utf16(gfp, "TCON", str);
380        case 'c': return id3tag_set_comment_utf16(gfp, 0, 0, str);
381        case 'n': return id3tag_set_textinfo_utf16(gfp, "TRCK", str);
382        case 'y': return id3tag_set_textinfo_utf16(gfp, "TYER", str);
383        case 'v': return id3tag_set_fieldvalue_utf16(gfp, str);
384    }
385    return 0;
386}
387#endif
388
389static int
390set_id3tag(lame_global_flags* gfp, int type, char const* str)
391{
392    switch (type)
393    {
394        case 'a': return id3tag_set_artist(gfp, str), 0;
395        case 't': return id3tag_set_title(gfp, str), 0;
396        case 'l': return id3tag_set_album(gfp, str), 0;
397        case 'g': return id3tag_set_genre(gfp, str);
398        case 'c': return id3tag_set_comment(gfp, str), 0;
399        case 'n': return id3tag_set_track(gfp, str);
400        case 'y': return id3tag_set_year(gfp, str), 0;
401        case 'v': return id3tag_set_fieldvalue(gfp, str);
402    }
403    return 0;
404}
405
406static int
407id3_tag(lame_global_flags* gfp, int type, TextEncoding enc, char* str)
408{
409    void* x = 0;
410    int result;
411    if (enc == TENC_UTF16 && type != 'v' ) {
412        id3_tag(gfp, type, TENC_LATIN1, str); /* for id3v1 */
413    }
414    switch (enc)
415    {
416        default:
417#ifdef ID3TAGS_EXTENDED
418        case TENC_LATIN1: x = toLatin1(str); break;
419        case TENC_UTF16:  x = toUtf16(str);   break;
420#else
421        case TENC_RAW:    x = strdup(str);   break;
422#endif
423    }
424    switch (enc)
425    {
426        default:
427#ifdef ID3TAGS_EXTENDED
428        case TENC_LATIN1: result = set_id3tag(gfp, type, x);   break;
429        case TENC_UTF16:  result = set_id3v2tag(gfp, type, x); break;
430#else
431        case TENC_RAW:    result = set_id3tag(gfp, type, x);   break;
432#endif
433    }
434    free(x);
435    return result;
436}
437
438
439
440
441/************************************************************************
442*
443* license
444*
445* PURPOSE:  Writes version and license to the file specified by fp
446*
447************************************************************************/
448
449static int
450lame_version_print(FILE * const fp)
451{
452    const char *b = get_lame_os_bitness();
453    const char *v = get_lame_version();
454    const char *u = get_lame_url();
455    const size_t lenb = strlen(b);
456    const size_t lenv = strlen(v);
457    const size_t lenu = strlen(u);
458    const size_t lw = 80;       /* line width of terminal in characters */
459    const size_t sw = 16;       /* static width of text */
460
461    if (lw >= lenb + lenv + lenu + sw || lw < lenu + 2)
462        /* text fits in 80 chars per line, or line even too small for url */
463        if (lenb > 0)
464            fprintf(fp, "LAME %s version %s (%s)\n\n", b, v, u);
465        else
466            fprintf(fp, "LAME version %s (%s)\n\n", v, u);
467    else {
468        int const n_white_spaces = (int)((lenu+2) > lw ? 0 : lw-2-lenu);
469        /* text too long, wrap url into next line, right aligned */
470        if (lenb > 0)
471            fprintf(fp, "LAME %s version %s\n%*s(%s)\n\n", b, v, n_white_spaces, "", u);
472        else
473            fprintf(fp, "LAME version %s\n%*s(%s)\n\n", v, n_white_spaces, "", u);
474    }
475    if (lame_alpha_version_enabled)
476        fprintf(fp, "warning: alpha versions should be used for testing only\n\n");
477
478
479    return 0;
480}
481
482static int
483print_license(FILE * const fp)
484{                       /* print version & license */
485    lame_version_print(fp);
486    fprintf(fp,
487            "Copyright (c) 1999-2011 by The LAME Project\n"
488            "Copyright (c) 1999,2000,2001 by Mark Taylor\n"
489            "Copyright (c) 1998 by Michael Cheng\n"
490            "Copyright (c) 1995,1996,1997 by Michael Hipp: mpglib\n" "\n");
491    fprintf(fp,
492            "This library is free software; you can redistribute it and/or\n"
493            "modify it under the terms of the GNU Library General Public\n"
494            "License as published by the Free Software Foundation; either\n"
495            "version 2 of the License, or (at your option) any later version.\n"
496            "\n");
497    fprintf(fp,
498            "This library is distributed in the hope that it will be useful,\n"
499            "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
500            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
501            "Library General Public License for more details.\n"
502            "\n");
503    fprintf(fp,
504            "You should have received a copy of the GNU Library General Public\n"
505            "License along with this program. If not, see\n"
506            "<http://www.gnu.org/licenses/>.\n");
507    return 0;
508}
509
510
511/************************************************************************
512*
513* usage
514*
515* PURPOSE:  Writes command line syntax to the file specified by fp
516*
517************************************************************************/
518
519int
520usage(FILE * const fp, const char *ProgramName)
521{                       /* print general syntax */
522    lame_version_print(fp);
523    fprintf(fp,
524            "usage: %s [options] <infile> [outfile]\n"
525            "\n"
526            "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
527            "\n"
528            "Try:\n"
529            "     \"%s --help\"           for general usage information\n"
530            " or:\n"
531            "     \"%s --preset help\"    for information on suggested predefined settings\n"
532            " or:\n"
533            "     \"%s --longhelp\"\n"
534            "  or \"%s -?\"              for a complete options list\n\n",
535            ProgramName, ProgramName, ProgramName, ProgramName, ProgramName);
536    return 0;
537}
538
539
540/************************************************************************
541*
542* usage
543*
544* PURPOSE:  Writes command line syntax to the file specified by fp
545*           but only the most important ones, to fit on a vt100 terminal
546*
547************************************************************************/
548
549int
550short_help(const lame_global_flags * gfp, FILE * const fp, const char *ProgramName)
551{                       /* print short syntax help */
552    lame_version_print(fp);
553    fprintf(fp,
554            "usage: %s [options] <infile> [outfile]\n"
555            "\n"
556            "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
557            "\n" "RECOMMENDED:\n" "    lame -V2 input.wav output.mp3\n" "\n", ProgramName);
558    fprintf(fp,
559            "OPTIONS:\n"
560            "    -b bitrate      set the bitrate, default 128 kbps\n"
561            "    -h              higher quality, but a little slower.\n"
562            "    -f              fast mode (lower quality)\n"
563            "    -V n            quality setting for VBR.  default n=%i\n"
564            "                    0=high quality,bigger files. 9.999=smaller files\n",
565            lame_get_VBR_q(gfp));
566    fprintf(fp,
567            "    --preset type   type must be \"medium\", \"standard\", \"extreme\", \"insane\",\n"
568            "                    or a value for an average desired bitrate and depending\n"
569            "                    on the value specified, appropriate quality settings will\n"
570            "                    be used.\n"
571            "                    \"--preset help\" gives more info on these\n" "\n");
572    fprintf(fp,
573#if defined(WIN32)
574            "    --priority type  sets the process priority\n"
575            "                     0,1 = Low priority\n"
576            "                     2   = normal priority\n"
577            "                     3,4 = High priority\n" "\n"
578#endif
579#if defined(__OS2__)
580            "    --priority type  sets the process priority\n"
581            "                     0 = Low priority\n"
582            "                     1 = Medium priority\n"
583            "                     2 = Regular priority\n"
584            "                     3 = High priority\n"
585            "                     4 = Maximum priority\n" "\n"
586#endif
587            "    --help id3      ID3 tagging related options\n" "\n"
588            DEV_HELP(
589            "    --help dev      developer options\n" "\n"
590            )
591            "    --longhelp      full list of options\n" "\n"
592            "    --license       print License information\n\n"
593            );
594
595    return 0;
596}
597
598/************************************************************************
599*
600* usage
601*
602* PURPOSE:  Writes command line syntax to the file specified by fp
603*
604************************************************************************/
605
606static void
607wait_for(FILE * const fp, int lessmode)
608{
609    if (lessmode) {
610        fflush(fp);
611        getchar();
612    }
613    else {
614        fprintf(fp, "\n");
615    }
616    fprintf(fp, "\n");
617}
618
619static void
620help_id3tag(FILE * const fp)
621{
622    fprintf(fp,
623            "  ID3 tag options:\n"
624            "    --tt <title>    audio/song title (max 30 chars for version 1 tag)\n"
625            "    --ta <artist>   audio/song artist (max 30 chars for version 1 tag)\n"
626            "    --tl <album>    audio/song album (max 30 chars for version 1 tag)\n"
627            "    --ty <year>     audio/song year of issue (1 to 9999)\n"
628            "    --tc <comment>  user-defined text (max 30 chars for v1 tag, 28 for v1.1)\n");
629    fprintf(fp,
630            "    --tn <track[/total]>   audio/song track number and (optionally) the total\n"
631            "                           number of tracks on the original recording. (track\n"
632            "                           and total each 1 to 255. just the track number\n"
633            "                           creates v1.1 tag, providing a total forces v2.0).\n");
634    fprintf(fp,
635            "    --tg <genre>    audio/song genre (name or number in list)\n"
636            "    --ti <file>     audio/song albumArt (jpeg/png/gif file, v2.3 tag)\n"
637            "    --tv <id=value> user-defined frame specified by id and value (v2.3 tag)\n"
638            "                    syntax: --tv \"TXXX=description=content\"\n"
639            );
640    fprintf(fp,
641            "    --add-id3v2     force addition of version 2 tag\n"
642            "    --id3v1-only    add only a version 1 tag\n"
643            "    --id3v2-only    add only a version 2 tag\n"
644#ifdef ID3TAGS_EXTENDED
645            "    --id3v2-utf16   add following options in unicode text encoding\n"
646            "    --id3v2-latin1  add following options in latin-1 text encoding\n"
647#endif
648            "    --space-id3v1   pad version 1 tag with spaces instead of nulls\n"
649            "    --pad-id3v2     same as '--pad-id3v2-size 128'\n"
650            "    --pad-id3v2-size <value> adds version 2 tag, pad with extra <value> bytes\n"
651            "    --genre-list    print alphabetically sorted ID3 genre list and exit\n"
652            "    --ignore-tag-errors  ignore errors in values passed for tags\n" "\n"
653            );
654    fprintf(fp,
655            "    Note: A version 2 tag will NOT be added unless one of the input fields\n"
656            "    won't fit in a version 1 tag (e.g. the title string is longer than 30\n"
657            "    characters), or the '--add-id3v2' or '--id3v2-only' options are used,\n"
658            "    or output is redirected to stdout.\n"
659            );
660}
661
662static void
663help_developer_switches(FILE * const fp)
664{
665    if ( !internal_opts_enabled ) {
666    fprintf(fp,
667            "    Note: Almost all of the following switches aren't available in this build!\n\n"
668            );
669    }
670    fprintf(fp,
671            "  ATH related:\n"
672            "    --noath         turns ATH down to a flat noise floor\n"
673            "    --athshort      ignore GPSYCHO for short blocks, use ATH only\n"
674            "    --athonly       ignore GPSYCHO completely, use ATH only\n"
675            "    --athtype n     selects between different ATH types [0-4]\n"
676            "    --athlower x    lowers ATH by x dB\n"
677            );
678    fprintf(fp,
679            "    --athaa-type n  ATH auto adjust: 0 'no' else 'loudness based'\n"
680/** OBSOLETE "    --athaa-loudapprox n   n=1 total energy or n=2 equal loudness curve\n"*/
681            "    --athaa-sensitivity x  activation offset in -/+ dB for ATH auto-adjustment\n"
682            "\n");
683    fprintf(fp,
684            "  PSY related:\n"
685            "    --short         use short blocks when appropriate\n"
686            "    --noshort       do not use short blocks\n"
687            "    --allshort      use only short blocks\n"
688            );
689    fprintf(fp,
690            "(1) --temporal-masking x   x=0 disables, x=1 enables temporal masking effect\n"
691            "    --nssafejoint   M/S switching criterion\n"
692            "    --nsmsfix <arg> M/S switching tuning [effective 0-3.5]\n"
693            "(2) --interch x     adjust inter-channel masking ratio\n"
694            "    --ns-bass x     adjust masking for sfbs  0 -  6 (long)  0 -  5 (short)\n"
695            "    --ns-alto x     adjust masking for sfbs  7 - 13 (long)  6 - 10 (short)\n"
696            "    --ns-treble x   adjust masking for sfbs 14 - 21 (long) 11 - 12 (short)\n"
697            );
698    fprintf(fp,
699            "    --ns-sfb21 x    change ns-treble by x dB for sfb21\n"
700            "    --shortthreshold x,y  short block switching threshold,\n"
701            "                          x for L/R/M channel, y for S channel\n"
702            "    -Z [n]          always do calculate short block maskings\n");
703    fprintf(fp,
704            "  Noise Shaping related:\n"
705            "(1) --substep n     use pseudo substep noise shaping method types 0-2\n"
706            "(1) -X n[,m]        selects between different noise measurements\n"
707            "                    n for long block, m for short. if m is omitted, m = n\n"
708            " 1: CBR, ABR and VBR-old encoding modes only\n"
709            " 2: ignored\n"
710           );
711}
712
713int
714long_help(const lame_global_flags * gfp, FILE * const fp, const char *ProgramName, int lessmode)
715{                       /* print long syntax help */
716    lame_version_print(fp);
717    fprintf(fp,
718            "usage: %s [options] <infile> [outfile]\n"
719            "\n"
720            "    <infile> and/or <outfile> can be \"-\", which means stdin/stdout.\n"
721            "\n" "RECOMMENDED:\n" "    lame -V2 input.wav output.mp3\n" "\n", ProgramName);
722    fprintf(fp,
723            "OPTIONS:\n"
724            "  Input options:\n"
725            "    --scale <arg>   scale input (multiply PCM data) by <arg>\n"
726            "    --scale-l <arg> scale channel 0 (left) input (multiply PCM data) by <arg>\n"
727            "    --scale-r <arg> scale channel 1 (right) input (multiply PCM data) by <arg>\n"
728            "    --swap-channel  swap L/R channels\n"
729            "    --ignorelength  ignore file length in WAV header\n"
730            "    --gain <arg>    apply Gain adjustment in decibels, range -20.0 to +12.0\n"
731           );
732#if (defined HAVE_MPGLIB || defined AMIGA_MPEGA)
733    fprintf(fp,
734            "    --mp1input      input file is a MPEG Layer I   file\n"
735            "    --mp2input      input file is a MPEG Layer II  file\n"
736            "    --mp3input      input file is a MPEG Layer III file\n"
737           );
738#endif
739    fprintf(fp,
740            "    --nogap <file1> <file2> <...>\n"
741            "                    gapless encoding for a set of contiguous files\n"
742            "    --nogapout <dir>\n"
743            "                    output dir for gapless encoding (must precede --nogap)\n"
744            "    --nogaptags     allow the use of VBR tags in gapless encoding\n"
745            "    --out-dir <dir> output dir, must exist\n"
746           );
747    fprintf(fp,
748            "\n"
749            "  Input options for RAW PCM:\n"
750            "    -r              input is raw pcm\n"
751            "    -s sfreq        sampling frequency of input file (kHz) - default 44.1 kHz\n"
752            "    --signed        input is signed (default)\n"
753            "    --unsigned      input is unsigned\n"
754            "    --bitwidth w    input bit width is w (default 16)\n"
755            "    -x              force byte-swapping of input\n"
756            "    --little-endian input is little-endian (default)\n"
757            "    --big-endian    input is big-endian\n"
758            "    -a              downmix from stereo to mono file for mono encoding\n"
759           );
760
761    wait_for(fp, lessmode);
762    fprintf(fp,
763            "  Operational options:\n"
764            "    -m <mode>       (j)oint, (s)imple, (f)orce, (d)ual-mono, (m)ono (l)eft (r)ight\n"
765            "                    default is (j)\n"
766            "                    joint  = Uses the best possible of MS and LR stereo\n"
767            "                    simple = force LR stereo on all frames\n"
768            "                    force  = force MS stereo on all frames.\n"
769	   );
770    fprintf(fp,
771            "    --preset type   type must be \"medium\", \"standard\", \"extreme\", \"insane\",\n"
772            "                    or a value for an average desired bitrate and depending\n"
773            "                    on the value specified, appropriate quality settings will\n"
774            "                    be used.\n"
775            "                    \"--preset help\" gives more info on these\n"
776            "    --comp  <arg>   choose bitrate to achieve a compression ratio of <arg>\n");
777    fprintf(fp, "    --replaygain-fast   compute RG fast but slightly inaccurately (default)\n"
778#ifdef DECODE_ON_THE_FLY
779            "    --replaygain-accurate   compute RG more accurately and find the peak sample\n"
780#endif
781            "    --noreplaygain  disable ReplayGain analysis\n"
782#ifdef DECODE_ON_THE_FLY
783            "    --clipdetect    enable --replaygain-accurate and print a message whether\n"
784            "                    clipping occurs and how far the waveform is from full scale\n"
785#endif
786        );
787    fprintf(fp,
788            "    --flush         flush output stream as soon as possible\n"
789            "    --freeformat    produce a free format bitstream\n"
790            "    --decode        input=mp3 file, output=wav\n"
791            "    -t              disable writing wav header when using --decode\n");
792
793    wait_for(fp, lessmode);
794    fprintf(fp,
795            "  Verbosity:\n"
796            "    --disptime <arg>print progress report every arg seconds\n"
797            "    -S              don't print progress report, VBR histograms\n"
798            "    --nohist        disable VBR histogram display\n"
799            "    --quiet         don't print anything on screen\n"
800            "    --silent        don't print anything on screen, but fatal errors\n"
801            "    --brief         print more useful information\n"
802            "    --verbose       print a lot of useful information\n" "\n");
803    fprintf(fp,
804            "  Noise shaping & psycho acoustic algorithms:\n"
805            "    -q <arg>        <arg> = 0...9.  Default  -q 3 \n"
806            "                    -q 0:  Highest quality, very slow \n"
807            "                    -q 9:  Poor quality, but fast \n"
808            "    -h              Same as -q 2.   \n"
809            "    -f              Same as -q 7.   Fast, ok quality\n");
810
811    wait_for(fp, lessmode);
812    fprintf(fp,
813            "  CBR (constant bitrate, the default) options:\n"
814            "    -b <bitrate>    set the bitrate in kbps, default 128 kbps\n"
815            "    --cbr           enforce use of constant bitrate\n"
816            "\n"
817            "  ABR options:\n"
818            "    --abr <bitrate> specify average bitrate desired (instead of quality)\n" "\n");
819    fprintf(fp,
820            "  VBR options:\n"
821            "    -V n            quality setting for VBR.  default n=%i\n"
822            "                    0=high quality,bigger files. 9=smaller files\n"
823            "    -v              the same as -V 4\n"
824            "    --vbr-old       use old variable bitrate (VBR) routine\n"
825            "    --vbr-new       use new variable bitrate (VBR) routine (default)\n"
826            "    -Y              lets LAME ignore noise in sfb21, like in CBR\n"
827			"                    (Default for V3 to V9.999)\n"
828            ,
829            lame_get_VBR_q(gfp));
830    fprintf(fp,
831            "    -b <bitrate>    specify minimum allowed bitrate, default  32 kbps\n"
832            "    -B <bitrate>    specify maximum allowed bitrate, default 320 kbps\n"
833            "    -F              strictly enforce the -b option, for use with players that\n"
834            "                    do not support low bitrate mp3\n"
835            "    -t              disable writing LAME Tag\n"
836            "    -T              enable and force writing LAME Tag\n");
837
838    wait_for(fp, lessmode);
839    DEV_HELP(
840        help_developer_switches(fp);
841        wait_for(fp, lessmode);
842            )
843
844    fprintf(fp,
845            "  MP3 header/stream options:\n"
846            "    -e <emp>        de-emphasis n/5/c  (obsolete)\n"
847            "    -c              mark as copyright\n"
848            "    -o              mark as non-original\n"
849            "    -p              error protection.  adds 16 bit checksum to every frame\n"
850            "                    (the checksum is computed correctly)\n"
851            "    --nores         disable the bit reservoir\n"
852            "    --strictly-enforce-ISO   comply as much as possible to ISO MPEG spec\n");
853    fprintf(fp,
854            "    --buffer-constraint <constraint> available values for constraint:\n"
855            "                                     default, strict, maximum\n"
856            "\n"
857            );
858    fprintf(fp,
859            "  Filter options:\n"
860            "  --lowpass <freq>        frequency(kHz), lowpass filter cutoff above freq\n"
861            "  --lowpass-width <freq>  frequency(kHz) - default 15%% of lowpass freq\n"
862            "  --highpass <freq>       frequency(kHz), highpass filter cutoff below freq\n"
863            "  --highpass-width <freq> frequency(kHz) - default 15%% of highpass freq\n");
864    fprintf(fp,
865            "  --resample <sfreq>  sampling frequency of output file(kHz)- default=automatic\n");
866
867    wait_for(fp, lessmode);
868    help_id3tag(fp);
869    fprintf(fp,
870#if defined(WIN32)
871            "\n\nMS-Windows-specific options:\n"
872            "    --priority <type>  sets the process priority:\n"
873            "                         0,1 = Low priority (IDLE_PRIORITY_CLASS)\n"
874            "                         2 = normal priority (NORMAL_PRIORITY_CLASS, default)\n"
875            "                         3,4 = High priority (HIGH_PRIORITY_CLASS))\n"
876            "    Note: Calling '--priority' without a parameter will select priority 0.\n"
877#endif
878#if defined(__OS2__)
879            "\n\nOS/2-specific options:\n"
880            "    --priority <type>  sets the process priority:\n"
881            "                         0 = Low priority (IDLE, delta = 0)\n"
882            "                         1 = Medium priority (IDLE, delta = +31)\n"
883            "                         2 = Regular priority (REGULAR, delta = -31)\n"
884            "                         3 = High priority (REGULAR, delta = 0)\n"
885            "                         4 = Maximum priority (REGULAR, delta = +31)\n"
886            "    Note: Calling '--priority' without a parameter will select priority 0.\n"
887#endif
888            "\nMisc:\n    --license       print License information\n\n"
889        );
890
891#if defined(HAVE_NASM)
892    wait_for(fp, lessmode);
893    fprintf(fp,
894            "  Platform specific:\n"
895            "    --noasm <instructions> disable assembly optimizations for mmx/3dnow/sse\n");
896    wait_for(fp, lessmode);
897#endif
898
899    display_bitrates(fp);
900
901    return 0;
902}
903
904static void
905display_bitrate(FILE * const fp, const char *const version, const int d, const int indx)
906{
907    int     i;
908    int nBitrates = 14;
909    if (d == 4)
910        nBitrates = 8;
911
912
913    fprintf(fp,
914            "\nMPEG-%-3s layer III sample frequencies (kHz):  %2d  %2d  %g\n"
915            "bitrates (kbps):", version, 32 / d, 48 / d, 44.1 / d);
916    for (i = 1; i <= nBitrates; i++)
917        fprintf(fp, " %2i", lame_get_bitrate(indx, i));
918    fprintf(fp, "\n");
919}
920
921int
922display_bitrates(FILE * const fp)
923{
924    display_bitrate(fp, "1", 1, 1);
925    display_bitrate(fp, "2", 2, 0);
926    display_bitrate(fp, "2.5", 4, 0);
927    fprintf(fp, "\n");
928    fflush(fp);
929    return 0;
930}
931
932
933/*  note: for presets it would be better to externalize them in a file.
934    suggestion:  lame --preset <file-name> ...
935            or:  lame --preset my-setting  ... and my-setting is defined in lame.ini
936 */
937
938/*
939Note from GB on 08/25/2002:
940I am merging --presets and --alt-presets. Old presets are now aliases for
941corresponding abr values from old alt-presets. This way we now have a
942unified preset system, and I hope than more people will use the new tuned
943presets instead of the old unmaintained ones.
944*/
945
946
947
948/************************************************************************
949*
950* usage
951*
952* PURPOSE:  Writes presetting info to #stdout#
953*
954************************************************************************/
955
956
957static void
958presets_longinfo_dm(FILE * msgfp)
959{
960    fprintf(msgfp,
961            "\n"
962            "The --preset switches are aliases over LAME settings.\n"
963            "\n" "\n");
964    fprintf(msgfp,
965            "To activate these presets:\n"
966            "\n" "   For VBR modes (generally highest quality):\n" "\n");
967    fprintf(msgfp,
968            "     --preset medium      This preset should provide near transparency to most\n"
969            "                          people on most music.\n"
970            "\n"
971            "     --preset standard    This preset should generally be transparent to most\n"
972            "                          people on most music and is already quite high\n"
973            "                          in quality.\n" "\n");
974    fprintf(msgfp,
975            "     --preset extreme     If you have extremely good hearing and similar\n"
976            "                          equipment, this preset will generally provide\n"
977            "                          slightly higher quality than the \"standard\" mode.\n" "\n");
978    fprintf(msgfp,
979            "   For CBR 320kbps (highest quality possible from the --preset switches):\n"
980            "\n"
981            "     --preset insane      This preset will usually be overkill for most people\n"
982            "                          and most situations, but if you must have the\n"
983            "                          absolute highest quality with no regard to filesize,\n"
984            "                          this is the way to go.\n" "\n");
985    fprintf(msgfp,
986            "   For ABR modes (high quality per given bitrate but not as high as VBR):\n"
987            "\n"
988            "     --preset <kbps>      Using this preset will usually give you good quality\n"
989            "                          at a specified bitrate. Depending on the bitrate\n"
990            "                          entered, this preset will determine the optimal\n"
991            "                          settings for that particular situation. For example:\n"
992            "                          \"--preset 185\" activates this preset and uses 185\n"
993            "                          as an average kbps.\n" "\n");
994    fprintf(msgfp,
995            "   \"cbr\"  - If you use the ABR mode (read above) with a significant\n"
996            "            bitrate such as 80, 96, 112, 128, 160, 192, 224, 256, 320,\n"
997            "            you can use the \"cbr\" option to force CBR mode encoding\n"
998            "            instead of the standard abr mode. ABR does provide higher\n"
999            "            quality but CBR may be useful in situations such as when\n"
1000            "            streaming an mp3 over the internet may be important.\n" "\n");
1001    fprintf(msgfp,
1002            "    For example:\n"
1003            "\n"
1004            "    --preset standard <input file> <output file>\n"
1005            " or --preset cbr 192 <input file> <output file>\n"
1006            " or --preset 172 <input file> <output file>\n"
1007            " or --preset extreme <input file> <output file>\n" "\n" "\n");
1008    fprintf(msgfp,
1009            "A few aliases are also available for ABR mode:\n"
1010            "phone => 16kbps/mono        phon+/lw/mw-eu/sw => 24kbps/mono\n"
1011            "mw-us => 40kbps/mono        voice => 56kbps/mono\n"
1012            "fm/radio/tape => 112kbps    hifi => 160kbps\n"
1013            "cd => 192kbps               studio => 256kbps\n");
1014}
1015
1016
1017static int
1018presets_set(lame_t gfp, int fast, int cbr, const char *preset_name, const char *ProgramName)
1019{
1020    int     mono = 0;
1021
1022    if ((strcmp(preset_name, "help") == 0) && (fast < 1)
1023        && (cbr < 1)) {
1024        lame_version_print(stdout);
1025        presets_longinfo_dm(stdout);
1026        return -1;
1027    }
1028
1029    /*aliases for compatibility with old presets */
1030
1031    if (strcmp(preset_name, "phone") == 0) {
1032        preset_name = "16";
1033        mono = 1;
1034    }
1035    if ((strcmp(preset_name, "phon+") == 0) ||
1036        (strcmp(preset_name, "lw") == 0) ||
1037        (strcmp(preset_name, "mw-eu") == 0) || (strcmp(preset_name, "sw") == 0)) {
1038        preset_name = "24";
1039        mono = 1;
1040    }
1041    if (strcmp(preset_name, "mw-us") == 0) {
1042        preset_name = "40";
1043        mono = 1;
1044    }
1045    if (strcmp(preset_name, "voice") == 0) {
1046        preset_name = "56";
1047        mono = 1;
1048    }
1049    if (strcmp(preset_name, "fm") == 0) {
1050        preset_name = "112";
1051    }
1052    if ((strcmp(preset_name, "radio") == 0) || (strcmp(preset_name, "tape") == 0)) {
1053        preset_name = "112";
1054    }
1055    if (strcmp(preset_name, "hifi") == 0) {
1056        preset_name = "160";
1057    }
1058    if (strcmp(preset_name, "cd") == 0) {
1059        preset_name = "192";
1060    }
1061    if (strcmp(preset_name, "studio") == 0) {
1062        preset_name = "256";
1063    }
1064
1065    if (strcmp(preset_name, "medium") == 0) {
1066        lame_set_VBR_q(gfp, 4);
1067        lame_set_VBR(gfp, vbr_default);
1068        return 0;
1069    }
1070
1071    if (strcmp(preset_name, "standard") == 0) {
1072        lame_set_VBR_q(gfp, 2);
1073        lame_set_VBR(gfp, vbr_default);
1074        return 0;
1075    }
1076
1077    else if (strcmp(preset_name, "extreme") == 0) {
1078        lame_set_VBR_q(gfp, 0);
1079        lame_set_VBR(gfp, vbr_default);
1080        return 0;
1081    }
1082
1083    else if ((strcmp(preset_name, "insane") == 0) && (fast < 1)) {
1084
1085        lame_set_preset(gfp, INSANE);
1086
1087        return 0;
1088    }
1089
1090    /* Generic ABR Preset */
1091    if (((atoi(preset_name)) > 0) && (fast < 1)) {
1092        if ((atoi(preset_name)) >= 8 && (atoi(preset_name)) <= 320) {
1093            lame_set_preset(gfp, atoi(preset_name));
1094
1095            if (cbr == 1)
1096                lame_set_VBR(gfp, vbr_off);
1097
1098            if (mono == 1) {
1099                lame_set_mode(gfp, MONO);
1100            }
1101
1102            return 0;
1103
1104        }
1105        else {
1106            lame_version_print(Console_IO.Error_fp);
1107            error_printf("Error: The bitrate specified is out of the valid range for this preset\n"
1108                         "\n"
1109                         "When using this mode you must enter a value between \"32\" and \"320\"\n"
1110                         "\n" "For further information try: \"%s --preset help\"\n", ProgramName);
1111            return -1;
1112        }
1113    }
1114
1115    lame_version_print(Console_IO.Error_fp);
1116    error_printf("Error: You did not enter a valid profile and/or options with --preset\n"
1117                 "\n"
1118                 "Available profiles are:\n"
1119                 "\n"
1120                 "                 medium\n"
1121                 "                 standard\n"
1122                 "                 extreme\n"
1123                 "                 insane\n"
1124                 "          <cbr> (ABR Mode) - The ABR Mode is implied. To use it,\n"
1125                 "                             simply specify a bitrate. For example:\n"
1126                 "                             \"--preset 185\" activates this\n"
1127                 "                             preset and uses 185 as an average kbps.\n" "\n");
1128    error_printf("    Some examples:\n"
1129                 "\n"
1130                 " or \"%s --preset standard <input file> <output file>\"\n"
1131                 " or \"%s --preset cbr 192 <input file> <output file>\"\n"
1132                 " or \"%s --preset 172 <input file> <output file>\"\n"
1133                 " or \"%s --preset extreme <input file> <output file>\"\n"
1134                 "\n"
1135                 "For further information try: \"%s --preset help\"\n", ProgramName, ProgramName,
1136                 ProgramName, ProgramName, ProgramName);
1137    return -1;
1138}
1139
1140static void
1141genre_list_handler(int num, const char *name, void *cookie)
1142{
1143    (void) cookie;
1144    console_printf("%3d %s\n", num, name);
1145}
1146
1147
1148/************************************************************************
1149*
1150* parse_args
1151*
1152* PURPOSE:  Sets encoding parameters to the specifications of the
1153* command line.  Default settings are used for parameters
1154* not specified in the command line.
1155*
1156* If the input file is in WAVE or AIFF format, the sampling frequency is read
1157* from the AIFF header.
1158*
1159* The input and output filenames are read into #inpath# and #outpath#.
1160*
1161************************************************************************/
1162
1163/* would use real "strcasecmp" but it isn't portable */
1164static int
1165local_strcasecmp(const char *s1, const char *s2)
1166{
1167    unsigned char c1;
1168    unsigned char c2;
1169
1170    do {
1171        c1 = (unsigned char) tolower(*s1);
1172        c2 = (unsigned char) tolower(*s2);
1173        if (!c1) {
1174            break;
1175        }
1176        ++s1;
1177        ++s2;
1178    } while (c1 == c2);
1179    return c1 - c2;
1180}
1181
1182static int
1183local_strncasecmp(const char *s1, const char *s2, int n)
1184{
1185    unsigned char c1 = 0;
1186    unsigned char c2 = 0;
1187    int     cnt = 0;
1188
1189    do {
1190        if (cnt == n) {
1191            break;
1192        }
1193        c1 = (unsigned char) tolower(*s1);
1194        c2 = (unsigned char) tolower(*s2);
1195        if (!c1) {
1196            break;
1197        }
1198        ++s1;
1199        ++s2;
1200        ++cnt;
1201    } while (c1 == c2);
1202    return c1 - c2;
1203}
1204
1205
1206
1207/* LAME is a simple frontend which just uses the file extension */
1208/* to determine the file type.  Trying to analyze the file */
1209/* contents is well beyond the scope of LAME and should not be added. */
1210static int
1211filename_to_type(const char *FileName)
1212{
1213    size_t  len = strlen(FileName);
1214
1215    if (len < 4)
1216        return sf_unknown;
1217
1218    FileName += len - 4;
1219    if (0 == local_strcasecmp(FileName, ".mpg"))
1220        return sf_mp123;
1221    if (0 == local_strcasecmp(FileName, ".mp1"))
1222        return sf_mp123;
1223    if (0 == local_strcasecmp(FileName, ".mp2"))
1224        return sf_mp123;
1225    if (0 == local_strcasecmp(FileName, ".mp3"))
1226        return sf_mp123;
1227    if (0 == local_strcasecmp(FileName, ".wav"))
1228        return sf_wave;
1229    if (0 == local_strcasecmp(FileName, ".aif"))
1230        return sf_aiff;
1231    if (0 == local_strcasecmp(FileName, ".raw"))
1232        return sf_raw;
1233    if (0 == local_strcasecmp(FileName, ".ogg"))
1234        return sf_ogg;
1235    return sf_unknown;
1236}
1237
1238static int
1239resample_rate(double freq)
1240{
1241    if (freq >= 1.e3)
1242        freq *= 1.e-3;
1243
1244    switch ((int) freq) {
1245    case 8:
1246        return 8000;
1247    case 11:
1248        return 11025;
1249    case 12:
1250        return 12000;
1251    case 16:
1252        return 16000;
1253    case 22:
1254        return 22050;
1255    case 24:
1256        return 24000;
1257    case 32:
1258        return 32000;
1259    case 44:
1260        return 44100;
1261    case 48:
1262        return 48000;
1263    default:
1264        error_printf("Illegal resample frequency: %.3f kHz\n", freq);
1265        return 0;
1266    }
1267}
1268
1269#ifdef _WIN32
1270#define SLASH '\\'
1271#define COLON ':'
1272#elif __OS2__
1273#define SLASH '\\'
1274#else
1275#define SLASH '/'
1276#endif
1277
1278static
1279size_t scanPath(char const* s, char const** a, char const** b)
1280{
1281    char const* s1 = s;
1282    char const* s2 = s;
1283    if (s != 0) {
1284        for (; *s; ++s) {
1285            switch (*s) {
1286            case SLASH:
1287#ifdef _WIN32
1288            case COLON:
1289#endif
1290                s2 = s;
1291                break;
1292            }
1293        }
1294#ifdef _WIN32
1295        if (*s2 == COLON) {
1296            ++s2;
1297        }
1298#endif
1299    }
1300    if (a) {
1301        *a = s1;
1302    }
1303    if (b) {
1304        *b = s2;
1305    }
1306    return s2-s1;
1307}
1308
1309static
1310size_t scanBasename(char const* s, char const** a, char const** b)
1311{
1312    char const* s1 = s;
1313    char const* s2 = s;
1314    if (s != 0) {
1315        for (; *s; ++s) {
1316            switch (*s) {
1317            case SLASH:
1318#ifdef _WIN32
1319            case COLON:
1320#endif
1321                s1 = s2 = s;
1322                break;
1323            case '.':
1324                s2 = s;
1325                break;
1326            }
1327        }
1328        if (s2 == s1) {
1329            s2 = s;
1330        }
1331        if (*s1 == SLASH
1332#ifdef _WIN32
1333          || *s1 == COLON
1334#endif
1335           ) {
1336            ++s1;
1337        }
1338    }
1339    if (a != 0) {
1340        *a = s1;
1341    }
1342    if (b != 0) {
1343        *b = s2;
1344    }
1345    return s2-s1;
1346}
1347
1348static
1349int isCommonSuffix(char const* s_ext)
1350{
1351    char const* suffixes[] =
1352    { ".WAV", ".RAW", ".MP1", ".MP2"
1353    , ".MP3", ".MPG", ".MPA", ".CDA"
1354    , ".OGG", ".AIF", ".AIFF", ".AU"
1355    , ".SND", ".FLAC", ".WV", ".OFR"
1356    , ".TAK", ".MP4", ".M4A", ".PCM"
1357    , ".W64"
1358    };
1359    size_t i;
1360    for (i = 0; i < dimension_of(suffixes); ++i) {
1361        if (local_strcasecmp(s_ext, suffixes[i]) == 0) {
1362            return 1;
1363        }
1364    }
1365    return 0;
1366}
1367
1368
1369int generateOutPath(char const* inPath, char const* outDir, char const* s_ext, char* outPath)
1370{
1371    size_t const max_path = PATH_MAX;
1372#if 1
1373    size_t i = 0;
1374    int out_dir_used = 0;
1375
1376    if (outDir != 0 && outDir[0] != 0) {
1377        out_dir_used = 1;
1378        while (*outDir) {
1379            outPath[i++] = *outDir++;
1380            if (i >= max_path) {
1381                goto err_generateOutPath;
1382            }
1383        }
1384        if (i > 0 && outPath[i-1] != SLASH) {
1385            outPath[i++] = SLASH;
1386            if (i >= max_path) {
1387                goto err_generateOutPath;
1388            }
1389        }
1390        outPath[i] = 0;
1391    }
1392    else {
1393        char const* pa;
1394        char const* pb;
1395        size_t j, n = scanPath(inPath, &pa, &pb);
1396        if (i+n >= max_path) {
1397            goto err_generateOutPath;
1398        }
1399        for (j = 0; j < n; ++j) {
1400            outPath[i++] = pa[j];
1401        }
1402        if (n > 0) {
1403            outPath[i++] = SLASH;
1404            if (i >= max_path) {
1405                goto err_generateOutPath;
1406            }
1407        }
1408        outPath[i] = 0;
1409    }
1410    {
1411        int replace_suffix = 0;
1412        char const* na;
1413        char const* nb;
1414        size_t j, n = scanBasename(inPath, &na, &nb);
1415        if (i+n >= max_path) {
1416            goto err_generateOutPath;
1417        }
1418        for (j = 0; j < n; ++j) {
1419            outPath[i++] = na[j];
1420        }
1421        outPath[i] = 0;
1422        if (isCommonSuffix(nb) == 1) {
1423            replace_suffix = 1;
1424            if (out_dir_used == 0) {
1425                if (local_strcasecmp(nb, s_ext) == 0) {
1426                    replace_suffix = 0;
1427                }
1428            }
1429        }
1430        if (replace_suffix == 0) {
1431            while (*nb) {
1432                outPath[i++] = *nb++;
1433                if (i >= max_path) {
1434                    goto err_generateOutPath;
1435                }
1436            }
1437            outPath[i] = 0;
1438        }
1439    }
1440    if (i+5 >= max_path) {
1441        goto err_generateOutPath;
1442    }
1443    while (*s_ext) {
1444        outPath[i++] = *s_ext++;
1445    }
1446    outPath[i] = 0;
1447    return 0;
1448err_generateOutPath:
1449    error_printf( "error: output file name too long\n" );
1450    return 1;
1451#else
1452    strncpy(outPath, inPath, PATH_MAX + 1 - 4);
1453    strncat(outPath, s_ext, 4);
1454    return 0;
1455#endif
1456}
1457
1458
1459static int
1460set_id3_albumart(lame_t gfp, char const* file_name)
1461{
1462    int ret = -1;
1463    FILE *fpi = 0;
1464
1465    if (file_name == 0) {
1466        return 0;
1467    }
1468    fpi = lame_fopen(file_name, "rb");
1469    if (!fpi) {
1470        ret = 1;
1471    }
1472    else {
1473        size_t size;
1474        char *albumart = 0;
1475
1476        fseek(fpi, 0, SEEK_END);
1477        size = ftell(fpi);
1478        fseek(fpi, 0, SEEK_SET);
1479        albumart = (char *)malloc(size);
1480        if (!albumart) {
1481            ret = 2;
1482        }
1483        else {
1484            if (fread(albumart, 1, size, fpi) != size) {
1485                ret = 3;
1486            }
1487            else {
1488                ret = id3tag_set_albumart(gfp, albumart, size) ? 4 : 0;
1489            }
1490            free(albumart);
1491        }
1492        fclose(fpi);
1493    }
1494    switch (ret) {
1495    case 1: error_printf("Could not find: '%s'.\n", file_name); break;
1496    case 2: error_printf("Insufficient memory for reading the albumart.\n"); break;
1497    case 3: error_printf("Read error: '%s'.\n", file_name); break;
1498    case 4: error_printf("Unsupported image: '%s'.\nSpecify JPEG/PNG/GIF image\n", file_name); break;
1499    default: break;
1500    }
1501    return ret;
1502}
1503
1504
1505enum ID3TAG_MODE
1506{ ID3TAG_MODE_DEFAULT
1507, ID3TAG_MODE_V1_ONLY
1508, ID3TAG_MODE_V2_ONLY
1509};
1510
1511static int dev_only_with_arg(char const* str, char const* token, char const* nextArg, int* argIgnored, int* argUsed)
1512{
1513    if (0 != local_strcasecmp(token,str)) return 0;
1514    *argUsed = 1;
1515    if (internal_opts_enabled) return 1;
1516    *argIgnored = 1;
1517    error_printf("WARNING: ignoring developer-only switch --%s %s\n", token, nextArg);
1518    return 0;
1519}
1520
1521static int dev_only_without_arg(char const* str, char const* token, int* argIgnored)
1522{
1523    if (0 != local_strcasecmp(token,str)) return 0;
1524    if (internal_opts_enabled) return 1;
1525    *argIgnored = 1;
1526    error_printf("WARNING: ignoring developer-only switch --%s\n", token);
1527    return 0;
1528}
1529
1530/* Ugly, NOT final version */
1531
1532#define T_IF(str)          if ( 0 == local_strcasecmp (token,str) ) {
1533#define T_ELIF(str)        } else if ( 0 == local_strcasecmp (token,str) ) {
1534#define T_ELIF2(str1,str2) } else if ( 0 == local_strcasecmp (token,str1)  ||  0 == local_strcasecmp (token,str2) ) {
1535#define T_ELSE             } else {
1536#define T_END              }
1537
1538#define T_ELIF_INTERNAL(str) \
1539                           } else if (dev_only_without_arg(str,token,&argIgnored)) {
1540
1541#define T_ELIF_INTERNAL_WITH_ARG(str) \
1542                           } else if (dev_only_with_arg(str,token,nextArg,&argIgnored,&argUsed)) {
1543
1544
1545static int
1546parse_args_(lame_global_flags * gfp, int argc, char **argv,
1547           char *const inPath, char *const outPath, char **nogap_inPath, int *num_nogap)
1548{
1549    char    outDir[PATH_MAX+1] = "";
1550    int     input_file = 0;  /* set to 1 if we parse an input file name  */
1551    int     i;
1552    int     autoconvert = 0;
1553    int     nogap = 0;
1554    int     nogap_tags = 0;  /* set to 1 to use VBR tags in NOGAP mode */
1555    const char *ProgramName = argv[0];
1556    int     count_nogap = 0;
1557    int     noreplaygain = 0; /* is RG explicitly disabled by the user */
1558    int     id3tag_mode = ID3TAG_MODE_DEFAULT;
1559    int     ignore_tag_errors = 0;  /* Ignore errors in values passed for tags */
1560#ifdef ID3TAGS_EXTENDED
1561    enum TextEncoding id3_tenc = TENC_UTF16;
1562#else
1563    enum TextEncoding id3_tenc = TENC_LATIN1;
1564#endif
1565
1566#ifdef HAVE_LANGINFO_H
1567    setlocale(LC_CTYPE, "");
1568#endif
1569    inPath[0] = '\0';
1570    outPath[0] = '\0';
1571    /* turn on display options. user settings may turn them off below */
1572    global_ui_config.silent = 0; /* default */
1573    global_ui_config.brhist = 1;
1574    global_decoder.mp3_delay = 0;
1575    global_decoder.mp3_delay_set = 0;
1576    global_decoder.disable_wav_header = 0;
1577    global_ui_config.print_clipping_info = 0;
1578    id3tag_init(gfp);
1579
1580    /* process args */
1581    for (i = 0; ++i < argc;) {
1582        char   *token;
1583        int     argUsed;
1584        int     argIgnored=0;
1585
1586        token = argv[i];
1587        if (*token++ == '-') {
1588            char   *nextArg = i + 1 < argc ? argv[i + 1] : "";
1589            argUsed = 0;
1590            if (!*token) { /* The user wants to use stdin and/or stdout. */
1591                input_file = 1;
1592                if (inPath[0] == '\0')
1593                    strncpy(inPath, argv[i], PATH_MAX + 1);
1594                else if (outPath[0] == '\0')
1595                    strncpy(outPath, argv[i], PATH_MAX + 1);
1596            }
1597            if (*token == '-') { /* GNU style */
1598                double  double_value = 0;
1599                int     int_value = 0;
1600                token++;
1601
1602                T_IF("resample")
1603                    argUsed = getDoubleValue(token, nextArg, &double_value);
1604                    if (argUsed)
1605                        (void) lame_set_out_samplerate(gfp, resample_rate(double_value));
1606
1607                T_ELIF("vbr-old")
1608                    lame_set_VBR(gfp, vbr_rh);
1609
1610                T_ELIF("vbr-new")
1611                    lame_set_VBR(gfp, vbr_mt);
1612
1613                T_ELIF("vbr-mtrh")
1614                    lame_set_VBR(gfp, vbr_mtrh);
1615
1616                T_ELIF("cbr")
1617                    lame_set_VBR(gfp, vbr_off);
1618
1619                T_ELIF("abr")
1620                    /* values larger than 8000 are bps (like Fraunhofer), so it's strange to get 320000 bps MP3 when specifying 8000 bps MP3 */
1621                    argUsed = getIntValue(token, nextArg, &int_value);
1622                    if (argUsed) {
1623                        if (int_value >= 8000) {
1624                            int_value = (int_value + 500) / 1000;
1625                        }
1626                        if (int_value > 320) {
1627                            int_value = 320;
1628                        }
1629                        if (int_value < 8) {
1630                            int_value = 8;
1631                        }
1632                        lame_set_VBR(gfp, vbr_abr);
1633                        lame_set_VBR_mean_bitrate_kbps(gfp, int_value);
1634                    }
1635
1636                T_ELIF("r3mix")
1637                    lame_set_preset(gfp, R3MIX);
1638
1639                T_ELIF("bitwidth")
1640                    argUsed = getIntValue(token, nextArg, &int_value);
1641                    if (argUsed)
1642                        global_raw_pcm.in_bitwidth = int_value;
1643
1644                T_ELIF("signed")
1645                    global_raw_pcm.in_signed = 1;
1646
1647                T_ELIF("unsigned")
1648                    global_raw_pcm.in_signed = 0;
1649
1650                T_ELIF("little-endian")
1651                    global_raw_pcm.in_endian = ByteOrderLittleEndian;
1652
1653                T_ELIF("big-endian")
1654                    global_raw_pcm.in_endian = ByteOrderBigEndian;
1655
1656                T_ELIF("mp1input")
1657                    global_reader.input_format = sf_mp1;
1658
1659                T_ELIF("mp2input")
1660                    global_reader.input_format = sf_mp2;
1661
1662                T_ELIF("mp3input")
1663                    global_reader.input_format = sf_mp3;
1664
1665                T_ELIF("ogginput")
1666                    error_printf("sorry, vorbis support in LAME is deprecated.\n");
1667                return -1;
1668
1669                T_ELIF("decode")
1670                    (void) lame_set_decode_only(gfp, 1);
1671
1672                T_ELIF("flush")
1673                    global_writer.flush_write = 1;
1674
1675                T_ELIF("decode-mp3delay")
1676                    argUsed = getIntValue(token, nextArg, &int_value);
1677                    if (argUsed) {
1678                        global_decoder.mp3_delay = int_value;
1679                        global_decoder.mp3_delay_set = 1;
1680                    }
1681
1682                T_ELIF("nores")
1683                    lame_set_disable_reservoir(gfp, 1);
1684
1685                T_ELIF("strictly-enforce-ISO")
1686                    lame_set_strict_ISO(gfp, MDB_STRICT_ISO);
1687
1688                T_ELIF("buffer-constraint")
1689                  argUsed = 1;
1690                if (strcmp(nextArg, "default") == 0)
1691                  (void) lame_set_strict_ISO(gfp, MDB_DEFAULT);
1692                else if (strcmp(nextArg, "strict") == 0)
1693                  (void) lame_set_strict_ISO(gfp, MDB_STRICT_ISO);
1694                else if (strcmp(nextArg, "maximum") == 0)
1695                  (void) lame_set_strict_ISO(gfp, MDB_MAXIMUM);
1696                else {
1697                    error_printf("unknown buffer constraint '%s'\n", nextArg);
1698                    return -1;
1699                }
1700
1701                T_ELIF("scale")
1702                    argUsed = getDoubleValue(token, nextArg, &double_value);
1703                    if (argUsed)
1704                        (void) lame_set_scale(gfp, (float) double_value);
1705
1706                T_ELIF("scale-l")
1707                    argUsed = getDoubleValue(token, nextArg, &double_value);
1708                    if (argUsed)
1709                        (void) lame_set_scale_left(gfp, (float) double_value);
1710
1711                T_ELIF("scale-r")
1712                    argUsed = getDoubleValue(token, nextArg, &double_value);
1713                    if (argUsed)
1714                        (void) lame_set_scale_right(gfp, (float) double_value);
1715
1716                T_ELIF("gain")
1717                    argUsed = getDoubleValue(token, nextArg, &double_value);
1718                    if (argUsed) {
1719                        double gain = double_value;
1720                        gain = gain > -20.f ? gain : -20.f;
1721                        gain = gain < 12.f ? gain : 12.f;
1722                        gain = pow(10.f, gain*0.05);
1723                        (void) lame_set_scale(gfp, (float) gain);
1724                    }
1725
1726                T_ELIF("noasm")
1727                    argUsed = 1;
1728                if (!strcmp(nextArg, "mmx"))
1729                    (void) lame_set_asm_optimizations(gfp, MMX, 0);
1730                if (!strcmp(nextArg, "3dnow"))
1731                    (void) lame_set_asm_optimizations(gfp, AMD_3DNOW, 0);
1732                if (!strcmp(nextArg, "sse"))
1733                    (void) lame_set_asm_optimizations(gfp, SSE, 0);
1734
1735                T_ELIF("freeformat")
1736                    lame_set_free_format(gfp, 1);
1737
1738                T_ELIF("replaygain-fast")
1739                    lame_set_findReplayGain(gfp, 1);
1740
1741#ifdef DECODE_ON_THE_FLY
1742                T_ELIF("replaygain-accurate")
1743                    lame_set_decode_on_the_fly(gfp, 1);
1744                lame_set_findReplayGain(gfp, 1);
1745#endif
1746
1747                T_ELIF("noreplaygain")
1748                    noreplaygain = 1;
1749                lame_set_findReplayGain(gfp, 0);
1750
1751
1752#ifdef DECODE_ON_THE_FLY
1753                T_ELIF("clipdetect")
1754                    global_ui_config.print_clipping_info = 1;
1755                    lame_set_decode_on_the_fly(gfp, 1);
1756#endif
1757
1758                T_ELIF("nohist")
1759                    global_ui_config.brhist = 0;
1760
1761#if defined(__OS2__) || defined(WIN32)
1762                T_ELIF("priority")
1763                    argUsed = getIntValue(token, nextArg, &int_value);
1764                    if (argUsed)
1765                        setProcessPriority(int_value);
1766#endif
1767
1768                /* options for ID3 tag */
1769#ifdef ID3TAGS_EXTENDED
1770                T_ELIF2("id3v2-utf16","id3v2-ucs2") /* id3v2-ucs2 for compatibility only */
1771                    id3_tenc = TENC_UTF16;
1772                    id3tag_add_v2(gfp);
1773
1774                T_ELIF("id3v2-latin1")
1775                    id3_tenc = TENC_LATIN1;
1776                    id3tag_add_v2(gfp);
1777#endif
1778
1779                T_ELIF("tt")
1780                    argUsed = 1;
1781                    id3_tag(gfp, 't', id3_tenc, nextArg);
1782
1783                T_ELIF("ta")
1784                    argUsed = 1;
1785                    id3_tag(gfp, 'a', id3_tenc, nextArg);
1786
1787                T_ELIF("tl")
1788                    argUsed = 1;
1789                    id3_tag(gfp, 'l', id3_tenc, nextArg);
1790
1791                T_ELIF("ty")
1792                    argUsed = 1;
1793                    id3_tag(gfp, 'y', id3_tenc, nextArg);
1794
1795                T_ELIF("tc")
1796                    argUsed = 1;
1797                    id3_tag(gfp, 'c', id3_tenc, nextArg);
1798
1799                T_ELIF("tn")
1800                    int ret = id3_tag(gfp, 'n', id3_tenc, nextArg);
1801                    argUsed = 1;
1802                    if (ret != 0) {
1803                        if (0 == ignore_tag_errors) {
1804                            if (id3tag_mode == ID3TAG_MODE_V1_ONLY) {
1805                                if (global_ui_config.silent < 9) {
1806                                    error_printf("The track number has to be between 1 and 255 for ID3v1.\n");
1807                                }
1808                                return -1;
1809                            }
1810                            else if (id3tag_mode == ID3TAG_MODE_V2_ONLY) {
1811                                /* track will be stored as-is in ID3v2 case, so no problem here */
1812                            }
1813                            else {
1814                                if (global_ui_config.silent < 9) {
1815                                    error_printf("The track number has to be between 1 and 255 for ID3v1, ignored for ID3v1.\n");
1816                                }
1817                            }
1818                        }
1819                    }
1820
1821                T_ELIF("tg")
1822                    int ret = 0;
1823                    argUsed = 1;
1824                    if (nextArg != 0 && strlen(nextArg) > 0) {
1825                        ret = id3_tag(gfp, 'g', id3_tenc, nextArg);
1826                    }
1827                    if (ret != 0) {
1828                        if (0 == ignore_tag_errors) {
1829                            if (ret == -1) {
1830                                error_printf("Unknown ID3v1 genre number: '%s'.\n", nextArg);
1831                                return -1;
1832                            }
1833                            else if (ret == -2) {
1834                                if (id3tag_mode == ID3TAG_MODE_V1_ONLY) {
1835                                    error_printf("Unknown ID3v1 genre: '%s'.\n", nextArg);
1836                                    return -1;
1837                                }
1838                                else if (id3tag_mode == ID3TAG_MODE_V2_ONLY) {
1839                                    /* genre will be stored as-is in ID3v2 case, so no problem here */
1840                                }
1841                                else {
1842                                    if (global_ui_config.silent < 9) {
1843                                        error_printf("Unknown ID3v1 genre: '%s'.  Setting ID3v1 genre to 'Other'\n", nextArg);
1844                                    }
1845                                }
1846                            }
1847                            else {
1848                                if (global_ui_config.silent < 10)
1849                                    error_printf("Internal error.\n");
1850                                return -1;
1851                            }
1852                        }
1853                    }
1854
1855                T_ELIF("tv")
1856                    argUsed = 1;
1857                    if (id3_tag(gfp, 'v', id3_tenc, nextArg)) {
1858                        if (global_ui_config.silent < 9) {
1859                            error_printf("Invalid field value: '%s'. Ignored\n", nextArg);
1860                        }
1861                    }
1862
1863                T_ELIF("ti")
1864                    argUsed = 1;
1865                    if (set_id3_albumart(gfp, nextArg) != 0) {
1866                        if (! ignore_tag_errors) {
1867                            return -1;
1868                        }
1869                    }
1870
1871                T_ELIF("ignore-tag-errors")
1872                    ignore_tag_errors = 1;
1873
1874                T_ELIF("add-id3v2")
1875                    id3tag_add_v2(gfp);
1876
1877                T_ELIF("id3v1-only")
1878                    id3tag_v1_only(gfp);
1879                    id3tag_mode = ID3TAG_MODE_V1_ONLY;
1880
1881                T_ELIF("id3v2-only")
1882                    id3tag_v2_only(gfp);
1883                    id3tag_mode = ID3TAG_MODE_V2_ONLY;
1884
1885                T_ELIF("space-id3v1")
1886                    id3tag_space_v1(gfp);
1887
1888                T_ELIF("pad-id3v2")
1889                    id3tag_pad_v2(gfp);
1890
1891                T_ELIF("pad-id3v2-size")
1892                    argUsed = getIntValue(token, nextArg, &int_value);
1893                    if (argUsed) {
1894                        int_value = int_value <= 128000 ? int_value : 128000;
1895                        int_value = int_value >= 0      ? int_value : 0;
1896                        id3tag_set_pad(gfp, int_value);
1897                    }
1898
1899                T_ELIF("genre-list")
1900                    id3tag_genre_list(genre_list_handler, NULL);
1901                    return -2;
1902
1903
1904                T_ELIF("lowpass")
1905                    argUsed = getDoubleValue(token, nextArg, &double_value);
1906                    if (argUsed) {
1907                        if (double_value < 0) {
1908                            lame_set_lowpassfreq(gfp, -1);
1909                        }
1910                        else {
1911                            /* useful are 0.001 kHz...50 kHz, 50 Hz...50000 Hz */
1912                            if (double_value < 0.001 || double_value > 50000.) {
1913                                error_printf("Must specify lowpass with --lowpass freq, freq >= 0.001 kHz\n");
1914                                return -1;
1915                            }
1916                            lame_set_lowpassfreq(gfp, (int) (double_value * (double_value < 50. ? 1.e3 : 1.e0) + 0.5));
1917                        }
1918                    }
1919
1920                T_ELIF("lowpass-width")
1921                    argUsed = getDoubleValue(token, nextArg, &double_value);
1922                    if (argUsed) {
1923                        /* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
1924                        if (double_value < 0.001 || double_value > 50000.) {
1925                            error_printf
1926                                ("Must specify lowpass width with --lowpass-width freq, freq >= 0.001 kHz\n");
1927                            return -1;
1928                        }
1929                        lame_set_lowpasswidth(gfp, (int) (double_value * (double_value < 16. ? 1.e3 : 1.e0) + 0.5));
1930                    }
1931
1932                T_ELIF("highpass")
1933                    argUsed = getDoubleValue(token, nextArg, &double_value);
1934                    if (argUsed) {
1935                        if (double_value < 0.0) {
1936                            lame_set_highpassfreq(gfp, -1);
1937                        }
1938                        else {
1939                            /* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
1940                            if (double_value < 0.001 || double_value > 50000.) {
1941                                error_printf("Must specify highpass with --highpass freq, freq >= 0.001 kHz\n");
1942                                return -1;
1943                            }
1944                            lame_set_highpassfreq(gfp, (int) (double_value * (double_value < 16. ? 1.e3 : 1.e0) + 0.5));
1945                        }
1946                    }
1947
1948                T_ELIF("highpass-width")
1949                    argUsed = getDoubleValue(token, nextArg, &double_value);
1950                    if (argUsed) {
1951                        /* useful are 0.001 kHz...16 kHz, 16 Hz...50000 Hz */
1952                        if (double_value < 0.001 || double_value > 50000.) {
1953                            error_printf
1954                                ("Must specify highpass width with --highpass-width freq, freq >= 0.001 kHz\n");
1955                            return -1;
1956                        }
1957                        lame_set_highpasswidth(gfp, (int) double_value);
1958                    }
1959
1960                T_ELIF("comp")
1961                    argUsed = getDoubleValue(token, nextArg, &double_value);
1962                    if (argUsed) {
1963                        if (double_value < 1.0) {
1964                            error_printf("Must specify compression ratio >= 1.0\n");
1965                            return -1;
1966                        }
1967                        else {
1968                            lame_set_compression_ratio(gfp, (float) double_value);
1969                        }
1970                    }
1971
1972                /* some more GNU-ish options could be added
1973                 * brief         => few messages on screen (name, status report)
1974                 * o/output file => specifies output filename
1975                 * O             => stdout
1976                 * i/input file  => specifies input filename
1977                 * I             => stdin
1978                 */
1979                T_ELIF("quiet")
1980                    global_ui_config.silent = 10; /* on a scale from 1 to 10 be very silent */
1981
1982                T_ELIF("silent")
1983                    global_ui_config.silent = 9;
1984
1985                T_ELIF("brief")
1986                    global_ui_config.silent = -5; /* print few info on screen */
1987
1988                T_ELIF("verbose")
1989                    global_ui_config.silent = -10; /* print a lot on screen */
1990
1991                T_ELIF2("version", "license")
1992                    print_license(stdout);
1993                return -2;
1994
1995                T_ELIF2("help", "usage")
1996                    if (0 == local_strncasecmp(nextArg, "id3", 3)) {
1997                        help_id3tag(stdout);
1998                    }
1999                    else if (0 == local_strncasecmp(nextArg, "dev", 3)) {
2000                        help_developer_switches(stdout);
2001                    }
2002                    else {
2003                        short_help(gfp, stdout, ProgramName);
2004                    }
2005                return -2;
2006
2007                T_ELIF("longhelp")
2008                    long_help(gfp, stdout, ProgramName, 0 /* lessmode=NO */ );
2009                return -2;
2010
2011                T_ELIF("?")
2012#ifdef __unix__
2013                    FILE   *fp = popen("less -Mqc", "w");
2014                    long_help(gfp, fp, ProgramName, 0 /* lessmode=NO */ );
2015                    pclose(fp);
2016#else
2017                    long_help(gfp, stdout, ProgramName, 1 /* lessmode=YES */ );
2018#endif
2019                return -2;
2020
2021                T_ELIF2("preset", "alt-preset")
2022                    argUsed = 1;
2023                {
2024                    int     fast = 0, cbr = 0;
2025
2026                    while ((strcmp(nextArg, "fast") == 0) || (strcmp(nextArg, "cbr") == 0)) {
2027
2028                        if ((strcmp(nextArg, "fast") == 0) && (fast < 1))
2029                            fast = 1;
2030                        if ((strcmp(nextArg, "cbr") == 0) && (cbr < 1))
2031                            cbr = 1;
2032
2033                        argUsed++;
2034                        nextArg = i + argUsed < argc ? argv[i + argUsed] : "";
2035                    }
2036
2037                    if (presets_set(gfp, fast, cbr, nextArg, ProgramName) < 0)
2038                        return -1;
2039                }
2040
2041                T_ELIF("disptime")
2042                    argUsed = getDoubleValue(token, nextArg, &double_value);
2043                    if (argUsed)
2044                        global_ui_config.update_interval = (float) double_value;
2045
2046                T_ELIF("nogaptags")
2047                    nogap_tags = 1;
2048
2049                T_ELIF("nogapout")
2050                    int const arg_n = strnlen(nextArg, PATH_MAX);
2051                    if (arg_n >= PATH_MAX) {
2052                        error_printf("%s: %s argument length (%d) exceeds limit (%d)\n", ProgramName, token, arg_n, PATH_MAX);
2053                        return -1;
2054                    }
2055                    strncpy(outPath, nextArg, PATH_MAX);
2056                    outPath[PATH_MAX] = '\0';
2057                    argUsed = 1;
2058
2059                T_ELIF("out-dir")
2060                    int const arg_n = strnlen(nextArg, PATH_MAX);
2061                    if (arg_n >= PATH_MAX) {
2062                        error_printf("%s: %s argument length (%d) exceeds limit (%d)\n", ProgramName, token, arg_n, PATH_MAX);
2063                        return -1;
2064                    }
2065                    strncpy(outDir, nextArg, PATH_MAX);
2066                    outDir[PATH_MAX] = '\0';
2067                    argUsed = 1;
2068
2069                T_ELIF("nogap")
2070                    nogap = 1;
2071
2072                T_ELIF("swap-channel")
2073                    global_reader.swap_channel = 1;
2074
2075                T_ELIF("ignorelength")
2076                    global_reader.ignorewavlength = 1;
2077
2078                T_ELIF ("athaa-sensitivity")
2079                    argUsed = getDoubleValue(token, nextArg, &double_value);
2080                    if (argUsed)
2081                        lame_set_athaa_sensitivity(gfp, (float) double_value);
2082
2083                /* ---------------- lots of dead switches ---------------- */
2084
2085                T_ELIF_INTERNAL("noshort")
2086                    (void) lame_set_no_short_blocks(gfp, 1);
2087
2088                T_ELIF_INTERNAL("short")
2089                    (void) lame_set_no_short_blocks(gfp, 0);
2090
2091                T_ELIF_INTERNAL("allshort")
2092                    (void) lame_set_force_short_blocks(gfp, 1);
2093
2094                T_ELIF_INTERNAL("notemp")
2095                    (void) lame_set_useTemporal(gfp, 0);
2096
2097                T_ELIF_INTERNAL_WITH_ARG("interch")
2098                    argUsed = getDoubleValue(token, nextArg, &double_value);
2099                    if (argUsed)
2100                        (void) lame_set_interChRatio(gfp, (float) double_value);
2101
2102                T_ELIF_INTERNAL_WITH_ARG("temporal-masking")
2103                    argUsed = getIntValue(token, nextArg, &int_value);
2104                    if (argUsed)
2105                        (void) lame_set_useTemporal(gfp, int_value ? 1 : 0);
2106
2107                T_ELIF_INTERNAL("nspsytune")
2108                    ;
2109
2110                T_ELIF_INTERNAL("nssafejoint")
2111                    lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | 2);
2112
2113                T_ELIF_INTERNAL_WITH_ARG("nsmsfix")
2114                    argUsed = getDoubleValue(token, nextArg, &double_value);
2115                    if (argUsed)
2116                        (void) lame_set_msfix(gfp, double_value);
2117
2118                T_ELIF_INTERNAL_WITH_ARG("ns-bass")
2119                    argUsed = getDoubleValue(token, nextArg, &double_value);
2120                    if (argUsed) {
2121                        int     k = (int) (double_value * 4);
2122                        if (k < -32)
2123                            k = -32;
2124                        if (k > 31)
2125                            k = 31;
2126                        if (k < 0)
2127                            k += 64;
2128                        lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | (k << 2));
2129                    }
2130
2131                T_ELIF_INTERNAL_WITH_ARG("ns-alto")
2132                    argUsed = getDoubleValue(token, nextArg, &double_value);
2133                    if (argUsed) {
2134                        int     k = (int) (double_value * 4);
2135                        if (k < -32)
2136                            k = -32;
2137                        if (k > 31)
2138                            k = 31;
2139                        if (k < 0)
2140                            k += 64;
2141                        lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | (k << 8));
2142                    }
2143
2144                T_ELIF_INTERNAL_WITH_ARG("ns-treble")
2145                    argUsed = getDoubleValue(token, nextArg, &double_value);
2146                    if (argUsed) {
2147                        int     k = (int) (double_value * 4);
2148                        if (k < -32)
2149                            k = -32;
2150                        if (k > 31)
2151                            k = 31;
2152                        if (k < 0)
2153                            k += 64;
2154                        lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | (k << 14));
2155                    }
2156
2157                T_ELIF_INTERNAL_WITH_ARG("ns-sfb21")
2158                    /*  to be compatible with Naoki's original code,
2159                     *  ns-sfb21 specifies how to change ns-treble for sfb21 */
2160                    argUsed = getDoubleValue(token, nextArg, &double_value);
2161                    if (argUsed) {
2162                        int     k = (int) (double_value * 4);
2163                        if (k < -32)
2164                            k = -32;
2165                        if (k > 31)
2166                            k = 31;
2167                        if (k < 0)
2168                            k += 64;
2169                        lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | (k << 20));
2170                    }
2171
2172                T_ELIF_INTERNAL_WITH_ARG("tune") /*without helptext */
2173                    argUsed = getDoubleValue(token, nextArg, &double_value);
2174                    if (argUsed)
2175                        lame_set_tune(gfp, (float) double_value);
2176
2177                T_ELIF_INTERNAL_WITH_ARG("shortthreshold")
2178                {
2179                    float   x, y;
2180                    int     n = sscanf(nextArg, "%f,%f", &x, &y);
2181                    if (n == 1) {
2182                        y = x;
2183                    }
2184                    (void) lame_set_short_threshold(gfp, x, y);
2185                }
2186                T_ELIF_INTERNAL_WITH_ARG("maskingadjust") /*without helptext */
2187                    argUsed = getDoubleValue(token, nextArg, &double_value);
2188                    if (argUsed)
2189                        (void) lame_set_maskingadjust(gfp, (float) double_value);
2190
2191                T_ELIF_INTERNAL_WITH_ARG("maskingadjustshort") /*without helptext */
2192                    argUsed = getDoubleValue(token, nextArg, &double_value);
2193                    if (argUsed)
2194                        (void) lame_set_maskingadjust_short(gfp, (float) double_value);
2195
2196                T_ELIF_INTERNAL_WITH_ARG("athcurve") /*without helptext */
2197                    argUsed = getDoubleValue(token, nextArg, &double_value);
2198                    if (argUsed)
2199                        (void) lame_set_ATHcurve(gfp, (float) double_value);
2200
2201                T_ELIF_INTERNAL("no-preset-tune") /*without helptext */
2202                    (void) lame_set_preset_notune(gfp, 0);
2203
2204                T_ELIF_INTERNAL_WITH_ARG("substep")
2205                    argUsed = getIntValue(token, nextArg, &int_value);
2206                    if (argUsed)
2207                        (void) lame_set_substep(gfp, int_value);
2208
2209                T_ELIF_INTERNAL_WITH_ARG("sbgain") /*without helptext */
2210                    argUsed = getIntValue(token, nextArg, &int_value);
2211                    if (argUsed)
2212                        (void) lame_set_subblock_gain(gfp, int_value);
2213
2214                T_ELIF_INTERNAL("sfscale") /*without helptext */
2215                    (void) lame_set_sfscale(gfp, 1);
2216
2217                T_ELIF_INTERNAL("noath")
2218                    (void) lame_set_noATH(gfp, 1);
2219
2220                T_ELIF_INTERNAL("athonly")
2221                    (void) lame_set_ATHonly(gfp, 1);
2222
2223                T_ELIF_INTERNAL("athshort")
2224                    (void) lame_set_ATHshort(gfp, 1);
2225
2226                T_ELIF_INTERNAL_WITH_ARG("athlower")
2227                    argUsed = getDoubleValue(token, nextArg, &double_value);
2228                    if (argUsed)
2229                        (void) lame_set_ATHlower(gfp, (float) double_value);
2230
2231                T_ELIF_INTERNAL_WITH_ARG("athtype")
2232                    argUsed = getIntValue(token, nextArg, &int_value);
2233                    if (argUsed)
2234                        (void) lame_set_ATHtype(gfp, int_value);
2235
2236                T_ELIF_INTERNAL_WITH_ARG("athaa-type") /*  switch for developing, no DOCU */
2237                    /* once was 1:Gaby, 2:Robert, 3:Jon, else:off */
2238                    argUsed = getIntValue(token, nextArg, &int_value);
2239                    if (argUsed)
2240                        (void) lame_set_athaa_type(gfp, int_value); /* now: 0:off else:Jon */
2241
2242                T_ELIF_INTERNAL_WITH_ARG("debug-file") /* switch for developing, no DOCU */
2243                    /* file name to print debug info into */
2244                    set_debug_file(nextArg);
2245
2246                T_ELSE {
2247                    if (!argIgnored) {
2248                        error_printf("%s: unrecognized option --%s\n", ProgramName, token);
2249                        return -1;
2250                    }
2251                    argIgnored = 0;
2252                }
2253                T_END   i += argUsed;
2254
2255            }
2256            else {
2257                char    c;
2258                while ((c = *token++) != '\0') {
2259                    double double_value = 0;
2260                    int int_value = 0;
2261                    char const *arg = *token ? token : nextArg;
2262                    switch (c) {
2263                    case 'm':
2264                        argUsed = 1;
2265
2266                        switch (*arg) {
2267                        case 's':
2268                            (void) lame_set_mode(gfp, STEREO);
2269                            break;
2270                        case 'd':
2271                            (void) lame_set_mode(gfp, DUAL_CHANNEL);
2272                            break;
2273                        case 'f':
2274                            lame_set_force_ms(gfp, 1);
2275                            (void) lame_set_mode(gfp, JOINT_STEREO);
2276                            break;
2277                        case 'j':
2278                            lame_set_force_ms(gfp, 0);
2279                            (void) lame_set_mode(gfp, JOINT_STEREO);
2280                            break;
2281                        case 'm':
2282                            (void) lame_set_mode(gfp, MONO);
2283                            break;
2284                        case 'l':
2285                            (void) lame_set_mode(gfp, MONO);
2286                            (void) lame_set_scale_left(gfp, 2);
2287                            (void) lame_set_scale_right(gfp, 0);
2288                            break;
2289                        case 'r':
2290                            (void) lame_set_mode(gfp, MONO);
2291                            (void) lame_set_scale_left(gfp, 0);
2292                            (void) lame_set_scale_right(gfp, 2);
2293                            break;
2294                        case 'a': /* same as 'j' ??? */
2295                            lame_set_force_ms(gfp, 0);
2296                            (void) lame_set_mode(gfp, JOINT_STEREO);
2297                            break;
2298                        default:
2299                            error_printf("%s: -m mode must be s/d/f/j/m/l/r not %s\n", ProgramName,
2300                                         arg);
2301                            return -1;
2302                        }
2303                        break;
2304
2305                    case 'V':
2306                        argUsed = getDoubleValue("V", arg, &double_value);
2307                        if (argUsed) {
2308                            /* to change VBR default look in lame.h */
2309                            if (lame_get_VBR(gfp) == vbr_off)
2310                                lame_set_VBR(gfp, vbr_default);
2311                            lame_set_VBR_quality(gfp, (float) double_value);
2312                        }
2313                        break;
2314                    case 'v':
2315                        /* to change VBR default look in lame.h */
2316                        if (lame_get_VBR(gfp) == vbr_off)
2317                            lame_set_VBR(gfp, vbr_default);
2318                        break;
2319
2320                    case 'q':
2321                        argUsed = getIntValue("q", arg, &int_value);
2322                        if (argUsed)
2323                            (void) lame_set_quality(gfp, int_value);
2324                        break;
2325                    case 'f':
2326                        (void) lame_set_quality(gfp, 7);
2327                        break;
2328                    case 'h':
2329                        (void) lame_set_quality(gfp, 2);
2330                        break;
2331
2332                    case 's':
2333                        argUsed = getDoubleValue("s", arg, &double_value);
2334                        if (argUsed) {
2335                            double_value = (int) (double_value * (double_value <= 192 ? 1.e3 : 1.e0) + 0.5);
2336                            global_reader.input_samplerate = (int)double_value;
2337                            (void) lame_set_in_samplerate(gfp, (int)double_value);
2338                        }
2339                        break;
2340                    case 'b':
2341                        argUsed = getIntValue("b", arg, &int_value);
2342                        if (argUsed) {
2343                            lame_set_brate(gfp, int_value);
2344                            lame_set_VBR_min_bitrate_kbps(gfp, lame_get_brate(gfp));
2345                        }
2346                        break;
2347                    case 'B':
2348                        argUsed = getIntValue("B", arg, &int_value);
2349                        if (argUsed) {
2350                            lame_set_VBR_max_bitrate_kbps(gfp, int_value);
2351                        }
2352                        break;
2353                    case 'F':
2354                        lame_set_VBR_hard_min(gfp, 1);
2355                        break;
2356                    case 't': /* dont write VBR tag */
2357                        (void) lame_set_bWriteVbrTag(gfp, 0);
2358                        global_decoder.disable_wav_header = 1;
2359                        break;
2360                    case 'T': /* do write VBR tag */
2361                        (void) lame_set_bWriteVbrTag(gfp, 1);
2362                        nogap_tags = 1;
2363                        global_decoder.disable_wav_header = 0;
2364                        break;
2365                    case 'r': /* force raw pcm input file */
2366#if defined(LIBSNDFILE)
2367                        error_printf
2368                            ("WARNING: libsndfile may ignore -r and perform fseek's on the input.\n"
2369                             "Compile without libsndfile if this is a problem.\n");
2370#endif
2371                        global_reader.input_format = sf_raw;
2372                        break;
2373                    case 'x': /* force byte swapping */
2374                        global_reader.swapbytes = 1;
2375                        break;
2376                    case 'p': /* (jo) error_protection: add crc16 information to stream */
2377                        lame_set_error_protection(gfp, 1);
2378                        break;
2379                    case 'a': /* autoconvert input file from stereo to mono - for mono mp3 encoding */
2380                        autoconvert = 1;
2381                        (void) lame_set_mode(gfp, MONO);
2382                        break;
2383                    case 'd':   /*(void) lame_set_allow_diff_short( gfp, 1 ); */
2384                    case 'k':   /*lame_set_lowpassfreq(gfp, -1);
2385                                  lame_set_highpassfreq(gfp, -1); */
2386                        error_printf("WARNING: -%c is obsolete.\n", c);
2387                        break;
2388                    case 'S':
2389                        global_ui_config.silent = 5;
2390                        break;
2391                    case 'X':
2392                        /*  experimental switch -X:
2393                            the differnt types of quant compare are tough
2394                            to communicate to endusers, so they shouldn't
2395                            bother to toy around with them
2396                         */
2397                        {
2398                            int     x, y;
2399                            int     n = sscanf(arg, "%d,%d", &x, &y);
2400                            if (n == 1) {
2401                                y = x;
2402                            }
2403                            argUsed = 1;
2404                            if (internal_opts_enabled) {
2405                                lame_set_quant_comp(gfp, x);
2406                                lame_set_quant_comp_short(gfp, y);
2407                            }
2408                        }
2409                        break;
2410                    case 'Y':
2411                        lame_set_experimentalY(gfp, 1);
2412                        break;
2413                    case 'Z':
2414                        /*  experimental switch -Z:
2415                         */
2416                        {
2417                            int     n = 1;
2418                            argUsed = sscanf(arg, "%d", &n);
2419                            /*if (internal_opts_enabled)*/
2420                            {
2421                                lame_set_experimentalZ(gfp, n);
2422                            }
2423                        }
2424                        break;
2425                    case 'e':
2426                        argUsed = 1;
2427
2428                        switch (*arg) {
2429                        case 'n':
2430                            lame_set_emphasis(gfp, 0);
2431                            break;
2432                        case '5':
2433                            lame_set_emphasis(gfp, 1);
2434                            break;
2435                        case 'c':
2436                            lame_set_emphasis(gfp, 3);
2437                            break;
2438                        default:
2439                            error_printf("%s: -e emp must be n/5/c not %s\n", ProgramName, arg);
2440                            return -1;
2441                        }
2442                        break;
2443                    case 'c':
2444                        lame_set_copyright(gfp, 1);
2445                        break;
2446                    case 'o':
2447                        lame_set_original(gfp, 0);
2448                        break;
2449
2450                    case '?':
2451                        long_help(gfp, stdout, ProgramName, 0 /* LESSMODE=NO */ );
2452                        return -1;
2453
2454                    default:
2455                        error_printf("%s: unrecognized option -%c\n", ProgramName, c);
2456                        return -1;
2457                    }
2458                    if (argUsed) {
2459                        if (arg == token)
2460                            token = ""; /* no more from token */
2461                        else
2462                            ++i; /* skip arg we used */
2463                        arg = "";
2464                        argUsed = 0;
2465                    }
2466                }
2467            }
2468        }
2469        else {
2470            if (nogap) {
2471                if ((num_nogap != NULL) && (count_nogap < *num_nogap)) {
2472                    strncpy(nogap_inPath[count_nogap++], argv[i], PATH_MAX + 1);
2473                    input_file = 1;
2474                }
2475                else {
2476                    /* sorry, calling program did not allocate enough space */
2477                    error_printf
2478                        ("Error: 'nogap option'.  Calling program does not allow nogap option, or\n"
2479                         "you have exceeded maximum number of input files for the nogap option\n");
2480                    if (num_nogap) {
2481                        *num_nogap = -1;
2482                    }
2483                    return -1;
2484                }
2485            }
2486            else {
2487                /* normal options:   inputfile  [outputfile], and
2488                   either one can be a '-' for stdin/stdout */
2489                if (inPath[0] == '\0') {
2490                    strncpy(inPath, argv[i], PATH_MAX + 1);
2491                    input_file = 1;
2492                }
2493                else {
2494                    if (outPath[0] == '\0')
2495                        strncpy(outPath, argv[i], PATH_MAX + 1);
2496                    else {
2497                        error_printf("%s: excess arg %s\n", ProgramName, argv[i]);
2498                        return -1;
2499                    }
2500                }
2501            }
2502        }
2503    }                   /* loop over args */
2504
2505    if (!input_file) {
2506        usage(Console_IO.Console_fp, ProgramName);
2507        return -1;
2508    }
2509
2510    if (lame_get_decode_only(gfp) && count_nogap > 0) {
2511        error_printf("combination of nogap and decode not supported!\n");
2512        return -1;
2513    }
2514
2515    if (inPath[0] == '-') {
2516        if (global_ui_config.silent == 0) { /* user didn't overrule default behaviour */
2517            global_ui_config.silent = 1;
2518        }
2519    }
2520#ifdef WIN32
2521    else
2522        dosToLongFileName(inPath);
2523#endif
2524
2525    if (outPath[0] == '\0') { /* no explicit output dir or file */
2526        if (count_nogap > 0) { /* in case of nogap encode */
2527            strncpy(outPath, outDir, PATH_MAX);
2528            outPath[PATH_MAX] = '\0'; /* whatever someone set via --out-dir <path> argument */
2529        }
2530        else if (inPath[0] == '-') {
2531            /* if input is stdin, default output is stdout */
2532            strcpy(outPath, "-");
2533        }
2534        else {
2535            char const* s_ext = lame_get_decode_only(gfp) ? ".wav" : ".mp3";
2536            if (generateOutPath(inPath, outDir, s_ext, outPath) != 0) {
2537                return -1;
2538            }
2539        }
2540    }
2541
2542    /* RG is enabled by default */
2543    if (!noreplaygain)
2544        lame_set_findReplayGain(gfp, 1);
2545
2546    /* disable VBR tags with nogap unless the VBR tags are forced */
2547    if (nogap && lame_get_bWriteVbrTag(gfp) && nogap_tags == 0) {
2548        console_printf("Note: Disabling VBR Xing/Info tag since it interferes with --nogap\n");
2549        lame_set_bWriteVbrTag(gfp, 0);
2550    }
2551
2552    /* some file options not allowed with stdout */
2553    if (outPath[0] == '-') {
2554        (void) lame_set_bWriteVbrTag(gfp, 0); /* turn off VBR tag */
2555    }
2556
2557    /* if user did not explicitly specify input is mp3, check file name */
2558    if (global_reader.input_format == sf_unknown)
2559        global_reader.input_format = filename_to_type(inPath);
2560
2561#if !(defined HAVE_MPGLIB || defined AMIGA_MPEGA || HAVE_MPG123)
2562    if (is_mpeg_file_format(global_reader.input_format)) {
2563        error_printf("Error: libmp3lame not compiled with mpg123 *decoding* support \n");
2564        return -1;
2565    }
2566#endif
2567
2568    /* default guess for number of channels */
2569    if (autoconvert)
2570        (void) lame_set_num_channels(gfp, 2);
2571    else if (MONO == lame_get_mode(gfp))
2572        (void) lame_set_num_channels(gfp, 1);
2573    else
2574        (void) lame_set_num_channels(gfp, 2);
2575
2576    if (lame_get_free_format(gfp)) {
2577        if (lame_get_brate(gfp) < 8 || lame_get_brate(gfp) > 640) {
2578            error_printf("For free format, specify a bitrate between 8 and 640 kbps\n");
2579            error_printf("with the -b <bitrate> option\n");
2580            return -1;
2581        }
2582    }
2583    if (num_nogap != NULL)
2584        *num_nogap = count_nogap;
2585    return 0;
2586}
2587
2588static int
2589string_to_argv(char* str, char** argv, int N)
2590{
2591    int     argc = 0;
2592    if (str == 0) return argc;
2593    argv[argc++] = "lhama";
2594    for (;;) {
2595        int     quoted = 0;
2596        while (isspace(*str)) { /* skip blanks */
2597            ++str;
2598        }
2599        if (*str == '\"') { /* is quoted argument ? */
2600            quoted = 1;
2601            ++str;
2602        }
2603        if (*str == '\0') { /* end of string reached */
2604            break;
2605        }
2606        /* found beginning of some argument */
2607        if (argc < N) {
2608            argv[argc++] = str;
2609        }
2610        /* look out for end of argument, either end of string, blank or quote */
2611        for(; *str != '\0'; ++str) {
2612            if (quoted) {
2613                if (*str == '\"') { /* end of quotation reached */
2614                    *str++ = '\0';
2615                    break;
2616                }
2617            }
2618            else {
2619                if (isspace(*str)) { /* parameter separator reached */
2620                    *str++ = '\0';
2621                    break;
2622                }
2623            }
2624        }
2625    }
2626    return argc;
2627}
2628
2629static int
2630merge_argv(int argc, char** argv, int str_argc, char** str_argv, int N)
2631{
2632    int     i;
2633    if (argc > 0) {
2634        str_argv[0] = argv[0];
2635        if (str_argc < 1) str_argc = 1;
2636    }
2637    for (i = 1; i < argc; ++i) {
2638        int     j = str_argc + i - 1;
2639        if (j < N) {
2640            str_argv[j] = argv[i];
2641        }
2642    }
2643    return argc + str_argc - 1;
2644}
2645
2646#ifdef DEBUG
2647static void
2648dump_argv(int argc, char** argv)
2649{
2650    int     i;
2651    for (i = 0; i < argc; ++i) {
2652        printf("%d: \"%s\"\n",i,argv[i]);
2653    }
2654}
2655#endif
2656
2657
2658int parse_args(lame_t gfp, int argc, char **argv, char *const inPath, char *const outPath, char **nogap_inPath, int *num_nogap)
2659{
2660    char   *str_argv[512], *str;
2661    int     str_argc, ret;
2662    str = lame_getenv("LAMEOPT");
2663    str_argc = string_to_argv(str, str_argv, dimension_of(str_argv));
2664    str_argc = merge_argv(argc, argv, str_argc, str_argv, dimension_of(str_argv));
2665#ifdef DEBUG
2666    dump_argv(str_argc, str_argv);
2667#endif
2668    ret = parse_args_(gfp, str_argc, str_argv, inPath, outPath, nogap_inPath, num_nogap);
2669    free(str);
2670    return ret;
2671}
2672
2673/* end of parse.c */
2674