main.c 77.2 KB
Newer Older
Josh Coalson's avatar
Josh Coalson committed
1
/* flac - Command-line FLAC encoder/decoder
Josh Coalson's avatar
Josh Coalson committed
2
 * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
Josh Coalson's avatar
Josh Coalson committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <ctype.h>
Josh Coalson's avatar
Josh Coalson committed
20
#include <locale.h>
Josh Coalson's avatar
Josh Coalson committed
21
22
23
24
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
25
#include <time.h>
26
27
28
29
30

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

31
#if !defined _MSC_VER && !defined __MINGW32__
Josh Coalson's avatar
Josh Coalson committed
32
/* unlink is in stdio.h in VC++ */
Josh Coalson's avatar
Josh Coalson committed
33
#include <unistd.h> /* for unlink() */
34
35
#else
#define strcasecmp stricmp
Josh Coalson's avatar
Josh Coalson committed
36
#endif
Josh Coalson's avatar
Josh Coalson committed
37
#include "FLAC/all.h"
Josh Coalson's avatar
Josh Coalson committed
38
#include "share/grabbag.h"
Josh Coalson's avatar
Josh Coalson committed
39
#include "analyze.h"
Josh Coalson's avatar
Josh Coalson committed
40
41
#include "decode.h"
#include "encode.h"
42
#include "local_string_utils.h" /* for flac__strlcat() and flac__strlcpy() */
43
#include "utils.h"
Josh Coalson's avatar
Josh Coalson committed
44
#include "vorbiscomment.h"
Josh Coalson's avatar
Josh Coalson committed
45

46
47
48
49
50
51
52
53
#if 0
/*[JEC] was:#if HAVE_GETOPT_LONG*/
/*[JEC] see flac/include/share/getopt.h as to why the change */
#  include <getopt.h>
#else
#  include "share/getopt.h"
#endif

54
55
typedef enum { RAW, WAV, AIF } FileFormat;

56
57
static int do_it();

Josh Coalson's avatar
Josh Coalson committed
58
static FLAC__bool init_options();
59
60
61
62
63
64
static int parse_options(int argc, char *argv[]);
static int parse_option(int short_option, const char *long_option, const char *option_argument);
static void free_options();

static int usage_error(const char *message, ...);
static void short_usage();
Josh Coalson's avatar
Josh Coalson committed
65
static void show_version();
66
67
static void show_help();
static void show_explain();
68
69
static void format_mistake(const char *infilename, const char *wrong, const char *right);

70
71
72
73
74
static int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_last_file);
static int decode_file(const char *infilename);

static const char *get_encoded_outfilename(const char *infilename);
static const char *get_decoded_outfilename(const char *infilename);
75
static const char *get_outfilename(const char *infilename, const char *suffix);
76

77
78
79
80
81
static void die(const char *message);
static char *local_strdup(const char *source);


/*
Josh Coalson's avatar
Josh Coalson committed
82
 * share__getopt format struct; note that for long options with no
83
84
 * short option equivalent we just set the 'val' field to 0.
 */
Josh Coalson's avatar
Josh Coalson committed
85
static struct share__option long_options_[] = {
86
87
88
	/*
	 * general options
	 */
89
90
91
92
93
94
95
96
	{ "help"             , share__no_argument, 0, 'h' },
	{ "explain"          , share__no_argument, 0, 'H' },
	{ "version"          , share__no_argument, 0, 'v' },
	{ "decode"           , share__no_argument, 0, 'd' },
	{ "analyze"          , share__no_argument, 0, 'a' },
	{ "test"             , share__no_argument, 0, 't' },
	{ "stdout"           , share__no_argument, 0, 'c' },
	{ "silent"           , share__no_argument, 0, 's' },
97
	{ "totally-silent"   , share__no_argument, 0, 0 },
98
	{ "force"            , share__no_argument, 0, 'f' },
99
100
101
102
103
	{ "delete-input-file", share__no_argument, 0, 0 },
	{ "output-prefix"    , share__required_argument, 0, 0 },
	{ "output-name"      , share__required_argument, 0, 'o' },
	{ "skip"             , share__required_argument, 0, 0 },
	{ "until"            , share__required_argument, 0, 0 },
104
105
106
107

	/*
	 * decoding options
	 */
108
	{ "decode-through-errors", share__no_argument, 0, 'F' },
109
	{ "cue"                  , share__required_argument, 0, 0 },
110
	{ "apply-replaygain-which-is-not-lossless", share__optional_argument, 0, 0 }, /* undocumented */
111
112
113
114

	/*
	 * encoding options
	 */
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
	{ "cuesheet"                  , share__required_argument, 0, 0 },
	{ "no-cued-seekpoints"        , share__no_argument, 0, 0 },
	{ "tag"                       , share__required_argument, 0, 'T' },
	{ "compression-level-0"       , share__no_argument, 0, '0' },
	{ "compression-level-1"       , share__no_argument, 0, '1' },
	{ "compression-level-2"       , share__no_argument, 0, '2' },
	{ "compression-level-3"       , share__no_argument, 0, '3' },
	{ "compression-level-4"       , share__no_argument, 0, '4' },
	{ "compression-level-5"       , share__no_argument, 0, '5' },
	{ "compression-level-6"       , share__no_argument, 0, '6' },
	{ "compression-level-7"       , share__no_argument, 0, '7' },
	{ "compression-level-8"       , share__no_argument, 0, '8' },
	{ "compression-level-9"       , share__no_argument, 0, '9' },
	{ "best"                      , share__no_argument, 0, '8' },
	{ "fast"                      , share__no_argument, 0, '0' },
	{ "super-secret-totally-impractical-compression-level", share__no_argument, 0, 0 },
	{ "verify"                    , share__no_argument, 0, 'V' },
	{ "force-aiff-format"         , share__no_argument, 0, 0 },
	{ "force-raw-format"          , share__no_argument, 0, 0 },
	{ "lax"                       , share__no_argument, 0, 0 },
	{ "replay-gain"               , share__no_argument, 0, 0 },
	{ "sector-align"              , share__no_argument, 0, 0 },
	{ "seekpoint"                 , share__required_argument, 0, 'S' },
	{ "padding"                   , share__required_argument, 0, 'P' },
139
#ifdef FLAC__HAS_OGG
140
141
	{ "ogg"                       , share__no_argument, 0, 0 },
	{ "serial-number"             , share__required_argument, 0, 0 },
142
#endif
143
144
145
146
147
148
149
150
151
152
153
154
155
	{ "blocksize"                 , share__required_argument, 0, 'b' },
	{ "exhaustive-model-search"   , share__no_argument, 0, 'e' },
	{ "max-lpc-order"             , share__required_argument, 0, 'l' },
	{ "mid-side"                  , share__no_argument, 0, 'm' },
	{ "adaptive-mid-side"         , share__no_argument, 0, 'M' },
	{ "qlp-coeff-precision-search", share__no_argument, 0, 'p' },
	{ "qlp-coeff-precision"       , share__required_argument, 0, 'q' },
	{ "rice-partition-order"      , share__required_argument, 0, 'r' },
	{ "endian"                    , share__required_argument, 0, 0 },
	{ "channels"                  , share__required_argument, 0, 0 },
	{ "bps"                       , share__required_argument, 0, 0 },
	{ "sample-rate"               , share__required_argument, 0, 0 },
	{ "sign"                      , share__required_argument, 0, 0 },
Josh Coalson's avatar
Josh Coalson committed
156
	{ "input-size"                , share__required_argument, 0, 0 },
157
158
159
160

	/*
	 * analysis options
	 */
161
	{ "residual-gnuplot", share__no_argument, 0, 0 },
162
	{ "residual-text", share__no_argument, 0, 0 },
163
164
165
166

	/*
	 * negatives
	 */
167
168
	{ "no-decode-through-errors"  , share__no_argument, 0, 0 },
	{ "no-silent"                 , share__no_argument, 0, 0 },
169
	{ "no-force"                  , share__no_argument, 0, 0 },
170
171
172
173
174
	{ "no-seektable"              , share__no_argument, 0, 0 },
	{ "no-delete-input-file"      , share__no_argument, 0, 0 },
	{ "no-replay-gain"            , share__no_argument, 0, 0 },
	{ "no-sector-align"           , share__no_argument, 0, 0 },
	{ "no-lax"                    , share__no_argument, 0, 0 },
175
#ifdef FLAC__HAS_OGG
176
	{ "no-ogg"                    , share__no_argument, 0, 0 },
177
#endif
178
179
180
181
182
183
184
185
	{ "no-exhaustive-model-search", share__no_argument, 0, 0 },
	{ "no-mid-side"               , share__no_argument, 0, 0 },
	{ "no-adaptive-mid-side"      , share__no_argument, 0, 0 },
	{ "no-qlp-coeff-prec-search"  , share__no_argument, 0, 0 },
	{ "no-padding"                , share__no_argument, 0, 0 },
	{ "no-verify"                 , share__no_argument, 0, 0 },
	{ "no-residual-gnuplot"       , share__no_argument, 0, 0 },
	{ "no-residual-text"          , share__no_argument, 0, 0 },
186
187
188
	/*
	 * undocumented debugging options for the test suite
	 */
189
190
191
	{ "disable-constant-subframes", share__no_argument, 0, 0 },
	{ "disable-fixed-subframes"   , share__no_argument, 0, 0 },
	{ "disable-verbatim-subframes", share__no_argument, 0, 0 },
192

Josh Coalson's avatar
Josh Coalson committed
193
	{0, 0, 0, 0}
194
195
196
197
198
199
200
201
};


/*
 * global to hold command-line option values
 */

static struct {
202
203
	FLAC__bool show_help;
	FLAC__bool show_explain;
Josh Coalson's avatar
Josh Coalson committed
204
	FLAC__bool show_version;
205
206
	FLAC__bool mode_decode;
	FLAC__bool verify;
207
	FLAC__bool force_file_overwrite;
208
	FLAC__bool continue_through_decode_errors;
209
	replaygain_synthesis_spec_t replaygain_synthesis_spec;
210
211
212
213
	FLAC__bool lax;
	FLAC__bool test_only;
	FLAC__bool analyze;
	FLAC__bool use_ogg;
214
215
	FLAC__bool has_serial_number; /* true iff --serial-number was used */
	long serial_number; /* this is the Ogg serial number and is unused for native FLAC */
216
217
218
219
220
221
	FLAC__bool do_mid_side;
	FLAC__bool loose_mid_side;
	FLAC__bool do_exhaustive_model_search;
	FLAC__bool do_escape_coding;
	FLAC__bool do_qlp_coeff_prec_search;
	FLAC__bool force_to_stdout;
222
	FLAC__bool force_aiff_format;
223
224
	FLAC__bool force_raw_format;
	FLAC__bool delete_input;
225
	FLAC__bool replay_gain;
226
227
228
229
230
231
232
	FLAC__bool sector_align;
	const char *cmdline_forced_outfilename;
	const char *output_prefix;
	analysis_options aopts;
	int padding;
	unsigned max_lpc_order;
	unsigned qlp_coeff_precision;
233
	const char *skip_specification;
234
	const char *until_specification;
235
	const char *cue_specification;
236
237
238
239
240
	int format_is_big_endian;
	int format_is_unsigned_samples;
	int format_channels;
	int format_bps;
	int format_sample_rate;
Josh Coalson's avatar
Josh Coalson committed
241
	long format_input_size;
242
243
244
245
	int blocksize;
	int min_residual_partition_order;
	int max_residual_partition_order;
	int rice_parameter_search_dist;
246
	char requested_seek_points[50000]; /* bad MAGIC NUMBER but buffer overflow is checked */
247
	int num_requested_seek_points; /* -1 => no -S options were given, 0 => -S- was given */
248
249
	const char *cuesheet_filename;
	FLAC__bool cued_seekpoints;
250
251
252

	unsigned num_files;
	char **filenames;
Josh Coalson's avatar
Josh Coalson committed
253
254

	FLAC__StreamMetadata *vorbis_comment;
255
256
257
258
259
260

	struct {
		FLAC__bool disable_constant_subframes;
		FLAC__bool disable_fixed_subframes;
		FLAC__bool disable_verbatim_subframes;
	} debug;
261
262
263
264
265
266
267
268
269
270
} option_values;


/*
 * miscellaneous globals
 */

static FLAC__int32 align_reservoir_0[588], align_reservoir_1[588]; /* for carrying over samples from --sector-align */
static FLAC__int32 *align_reservoir[2] = { align_reservoir_0, align_reservoir_1 };
static unsigned align_reservoir_samples = 0; /* 0 .. 587 */
Josh Coalson's avatar
Josh Coalson committed
271

272

Josh Coalson's avatar
Josh Coalson committed
273
274
int main(int argc, char *argv[])
{
275
276
	int retval = 0;

Josh Coalson's avatar
Josh Coalson committed
277
278
	setlocale(LC_ALL, "");
	if(!init_options()) {
279
		flac__utils_printf(stderr, 1, "ERROR: allocating memory\n");
Josh Coalson's avatar
Josh Coalson committed
280
281
282
283
284
285
		retval = 1;
	}
	else {
		if((retval = parse_options(argc, argv)) == 0)
			retval = do_it();
	}
286
287
288
289
290
291
292
293
294
295

	free_options();

	return retval;
}

int do_it()
{
	int retval = 0;

Josh Coalson's avatar
Josh Coalson committed
296
297
298
299
300
	if(option_values.show_version) {
		show_version();
		return 0;
	}
	else if(option_values.show_explain) {
301
302
303
304
305
		show_explain();
		return 0;
	}
	else if(option_values.show_help) {
		show_help();
306
307
308
309
		return 0;
	}
	else {
		if(option_values.num_files == 0) {
310
311
			if(flac__utils_verbosity_ >= 1)
				short_usage();
312
313
314
315
316
317
318
319
320
321
322
323
			return 0;
		}

		/*
		 * tweak options; validate the values
		 */
		if(!option_values.mode_decode) {
			if(option_values.blocksize < 0) {
				if(option_values.max_lpc_order == 0)
					option_values.blocksize = 1152;
				else
					option_values.blocksize = 4608;
324
			}
325
326
327
328
329
330
331
332
333
334
335
336
337
			if(option_values.max_residual_partition_order < 0) {
				if(option_values.blocksize <= 1152)
					option_values.max_residual_partition_order = 2;
				else if(option_values.blocksize <= 2304)
					option_values.max_residual_partition_order = 3;
				else if(option_values.blocksize <= 4608)
					option_values.max_residual_partition_order = 3;
				else
					option_values.max_residual_partition_order = 4;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order;
			}
			if(option_values.rice_parameter_search_dist < 0) {
				option_values.rice_parameter_search_dist = 0;
338
			}
339
340
			if(0 != option_values.cue_specification)
				return usage_error("ERROR: --cue is not allowed in test mode\n");
Josh Coalson's avatar
Josh Coalson committed
341
342
		}
		else {
343
			if(option_values.test_only) {
344
				if(0 != option_values.skip_specification)
345
					return usage_error("ERROR: --skip is not allowed in test mode\n");
346
347
				if(0 != option_values.until_specification)
					return usage_error("ERROR: --until is not allowed in test mode\n");
348
349
				if(0 != option_values.cue_specification)
					return usage_error("ERROR: --cue is not allowed in test mode\n");
350
			}
Josh Coalson's avatar
Josh Coalson committed
351
352
		}

353
354
355
		if(0 != option_values.cue_specification && (0 != option_values.skip_specification || 0 != option_values.until_specification))
			return usage_error("ERROR: --cue may not be combined with --skip or --until\n");

356
357
358
359
360
		FLAC__ASSERT(option_values.blocksize >= 0 || option_values.mode_decode);

		if(option_values.format_channels >= 0) {
			if(option_values.format_channels == 0 || (unsigned)option_values.format_channels > FLAC__MAX_CHANNELS)
				return usage_error("ERROR: invalid number of channels '%u', must be > 0 and <= %u\n", option_values.format_channels, FLAC__MAX_CHANNELS);
361
		}
362
363
364
		if(option_values.format_bps >= 0) {
			if(option_values.format_bps != 8 && option_values.format_bps != 16 && option_values.format_bps != 24)
				return usage_error("ERROR: invalid bits per sample '%u' (must be 8/16/24)\n", option_values.format_bps);
Josh Coalson's avatar
Josh Coalson committed
365
		}
366
367
368
369
		if(option_values.format_sample_rate >= 0) {
			if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate))
				return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE);
		}
370
371
		if(option_values.force_raw_format && option_values.force_aiff_format)
			return usage_error("ERROR: only one of --force-raw-format and --force-aiff-format allowed\n");
372
373
374
375
376
377
378
379
380
381
382
383
384
385
		if(option_values.mode_decode) {
			if(!option_values.force_raw_format) {
				if(option_values.format_is_big_endian >= 0)
					return usage_error("ERROR: --endian only allowed with --force-raw-format\n");
				if(option_values.format_is_unsigned_samples >= 0)
					return usage_error("ERROR: --sign only allowed with --force-raw-format\n");
			}
			if(option_values.format_channels >= 0)
				return usage_error("ERROR: --channels not allowed with --decode\n");
			if(option_values.format_bps >= 0)
				return usage_error("ERROR: --bps not allowed with --decode\n");
			if(option_values.format_sample_rate >= 0)
				return usage_error("ERROR: --sample-rate not allowed with --decode\n");
		}
386
387
388
389
		if(!option_values.mode_decode && ((unsigned)option_values.blocksize < FLAC__MIN_BLOCK_SIZE || (unsigned)option_values.blocksize > FLAC__MAX_BLOCK_SIZE)) {
			return usage_error("ERROR: invalid blocksize '%u', must be >= %u and <= %u\n", (unsigned)option_values.blocksize, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
		}
		if(option_values.qlp_coeff_precision > 0 && option_values.qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION) {
390
			return usage_error("ERROR: invalid value '%u' for qlp coeff precision, must be 0 or >= %u\n", option_values.qlp_coeff_precision, FLAC__MIN_QLP_COEFF_PRECISION);
Josh Coalson's avatar
Josh Coalson committed
391
392
		}

393
394
395
		if(option_values.sector_align) {
			if(option_values.mode_decode)
				return usage_error("ERROR: --sector-align only allowed for encoding\n");
396
			if(0 != option_values.skip_specification)
397
				return usage_error("ERROR: --sector-align not allowed with --skip\n");
398
399
			if(0 != option_values.until_specification)
				return usage_error("ERROR: --sector-align not allowed with --until\n");
400
401
			if(0 != option_values.cue_specification)
				return usage_error("ERROR: --sector-align not allowed with --cue\n");
402
			if(option_values.format_channels >= 0 && option_values.format_channels != 2)
403
				return usage_error("ERROR: --sector-align can only be done with stereo input\n");
404
			if(option_values.format_bps >= 0 && option_values.format_bps != 16)
405
				return usage_error("ERROR: --sector-align can only be done with 16-bit samples\n");
406
			if(option_values.format_sample_rate >= 0 && option_values.format_sample_rate != 44100)
407
408
				return usage_error("ERROR: --sector-align can only be done with a sample rate of 44100\n");
		}
409
		if(option_values.replay_gain) {
410
411
			if(option_values.force_to_stdout)
				return usage_error("ERROR: --replay-gain not allowed with -c/--stdout\n");
412
413
414
415
			if(option_values.mode_decode)
				return usage_error("ERROR: --replay-gain only allowed for encoding\n");
			if(option_values.format_channels > 2)
				return usage_error("ERROR: --replay-gain can only be done with mono/stereo input\n");
Josh Coalson's avatar
Josh Coalson committed
416
			if(option_values.format_sample_rate >= 0 && !grabbag__replaygain_is_valid_sample_frequency(option_values.format_sample_rate))
417
				return usage_error("ERROR: invalid sample rate used with --replay-gain\n");
418
419
420
421
422
423
			/*
			 * We want to reserve padding space for the ReplayGain
			 * tags that we will set later, to avoid rewriting the
			 * whole file.
			 */
			if(option_values.padding < 0) {
424
				flac__utils_printf(stderr, 1, "NOTE: --replay-gain may leave a small PADDING block even with --no-padding\n");
Josh Coalson's avatar
Josh Coalson committed
425
				option_values.padding = GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED;
426
427
			}
			else {
Josh Coalson's avatar
Josh Coalson committed
428
				option_values.padding += GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED;
429
			}
430
		}
431
		if(option_values.num_files > 1 && option_values.cmdline_forced_outfilename) {
432
			return usage_error("ERROR: -o/--output-name cannot be used with multiple files\n");
433
434
		}
		if(option_values.cmdline_forced_outfilename && option_values.output_prefix) {
435
			return usage_error("ERROR: --output-prefix conflicts with -o/--output-name\n");
436
		}
437
438
439
		if(!option_values.mode_decode && 0 != option_values.cuesheet_filename && option_values.num_files > 1) {
			return usage_error("ERROR: --cuesheet cannot be used when encoding multiple files\n");
		}
Josh Coalson's avatar
Josh Coalson committed
440
	}
Josh Coalson's avatar
Josh Coalson committed
441

442
	flac__utils_printf(stderr, 2, "\n");
Josh Coalson's avatar
Josh Coalson committed
443
	flac__utils_printf(stderr, 2, "flac %s, Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson\n", FLAC__VERSION_STRING);
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
	flac__utils_printf(stderr, 2, "flac comes with ABSOLUTELY NO WARRANTY.  This is free software, and you are\n");
	flac__utils_printf(stderr, 2, "welcome to redistribute it under certain conditions.  Type `flac' for details.\n\n");

	if(!option_values.mode_decode) {
		char padopt[16];
		if(option_values.padding < 0)
			strcpy(padopt, "-");
		else
			sprintf(padopt, " %d", option_values.padding);
		flac__utils_printf(stderr, 2,
			"options:%s%s%s%s -P%s -b %u%s -l %u%s%s%s -q %u -r %u,%u%s\n",
			option_values.delete_input?" --delete-input-file":"",
			option_values.sector_align?" --sector-align":"",
			option_values.use_ogg?" --ogg":"",
			option_values.lax?" --lax":"",
			padopt,
			(unsigned)option_values.blocksize,
			option_values.loose_mid_side?" -M":option_values.do_mid_side?" -m":"",
			option_values.max_lpc_order,
			option_values.do_exhaustive_model_search?" -e":"",
			option_values.do_escape_coding?" -E":"",
			option_values.do_qlp_coeff_prec_search?" -p":"",
			option_values.qlp_coeff_precision,
			(unsigned)option_values.min_residual_partition_order,
			(unsigned)option_values.max_residual_partition_order,
			option_values.verify? " -V":""
		);
Josh Coalson's avatar
Josh Coalson committed
471
472
	}

473
	if(option_values.mode_decode) {
Josh Coalson's avatar
Josh Coalson committed
474
		FLAC__bool first = true;
Josh Coalson's avatar
Josh Coalson committed
475

476
		if(option_values.num_files == 0) {
477
			retval = decode_file("-");
478
		}
479
		else {
480
481
482
			unsigned i;
			if(option_values.num_files > 1)
				option_values.cmdline_forced_outfilename = 0;
483
			for(i = 0, retval = 0; i < option_values.num_files; i++) {
484
				if(0 == strcmp(option_values.filenames[i], "-") && !first)
485
					continue;
486
				retval |= decode_file(option_values.filenames[i]);
487
488
489
490
491
				first = false;
			}
		}
	}
	else { /* encode */
Josh Coalson's avatar
Josh Coalson committed
492
		FLAC__bool first = true;
493

494
		if(option_values.num_files == 0) {
495
			retval = encode_file("-", first, true);
496
		}
497
		else {
498
499
500
			unsigned i;
			if(option_values.num_files > 1)
				option_values.cmdline_forced_outfilename = 0;
501
			for(i = 0, retval = 0; i < option_values.num_files; i++) {
502
				if(0 == strcmp(option_values.filenames[i], "-") && !first)
503
					continue;
504
				retval |= encode_file(option_values.filenames[i], first, i == (option_values.num_files-1));
505
506
				first = false;
			}
507
508
			if(option_values.replay_gain && retval == 0) {
				float album_gain, album_peak;
Josh Coalson's avatar
Josh Coalson committed
509
				grabbag__replaygain_get_album(&album_gain, &album_peak);
510
511
				for(i = 0; i < option_values.num_files; i++) {
					const char *error, *outfilename = get_encoded_outfilename(option_values.filenames[i]);
512
					if(0 == outfilename) {
513
						flac__utils_printf(stderr, 1, "ERROR: filename too long: %s", option_values.filenames[i]);
514
515
						return 1;
					}
Josh Coalson's avatar
Josh Coalson committed
516
					if(0 == strcmp(option_values.filenames[i], "-")) {
517
						FLAC__ASSERT(0);
Josh Coalson's avatar
Josh Coalson committed
518
						/* double protection */
519
						flac__utils_printf(stderr, 1, "internal error\n");
Josh Coalson's avatar
Josh Coalson committed
520
521
						return 2;
					}
Josh Coalson's avatar
Josh Coalson committed
522
					if(0 != (error = grabbag__replaygain_store_to_file_album(outfilename, album_gain, album_peak, /*preserve_modtime=*/true))) {
523
						flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain album tags\n", outfilename);
524
525
526
527
						retval = 1;
					}
				}
			}
528
529
530
531
		}
	}

	return retval;
Josh Coalson's avatar
Josh Coalson committed
532
533
}

Josh Coalson's avatar
Josh Coalson committed
534
FLAC__bool init_options()
Josh Coalson's avatar
Josh Coalson committed
535
{
536
537
	option_values.show_help = false;
	option_values.show_explain = false;
538
539
	option_values.mode_decode = false;
	option_values.verify = false;
540
	option_values.force_file_overwrite = false;
541
	option_values.continue_through_decode_errors = false;
542
543
544
545
546
	option_values.replaygain_synthesis_spec.apply = false;
	option_values.replaygain_synthesis_spec.use_album_gain = true;
	option_values.replaygain_synthesis_spec.limiter = RGSS_LIMIT__HARD;
	option_values.replaygain_synthesis_spec.noise_shaping = NOISE_SHAPING_LOW;
	option_values.replaygain_synthesis_spec.preamp = 0.0;
547
548
549
550
	option_values.lax = false;
	option_values.test_only = false;
	option_values.analyze = false;
	option_values.use_ogg = false;
551
552
	option_values.has_serial_number = false;
	option_values.serial_number = 0;
553
554
555
556
557
558
	option_values.do_mid_side = true;
	option_values.loose_mid_side = false;
	option_values.do_exhaustive_model_search = false;
	option_values.do_escape_coding = false;
	option_values.do_qlp_coeff_prec_search = false;
	option_values.force_to_stdout = false;
559
	option_values.force_aiff_format = false;
560
561
	option_values.force_raw_format = false;
	option_values.delete_input = false;
562
	option_values.replay_gain = false;
563
564
565
566
567
	option_values.sector_align = false;
	option_values.cmdline_forced_outfilename = 0;
	option_values.output_prefix = 0;
	option_values.aopts.do_residual_text = false;
	option_values.aopts.do_residual_gnuplot = false;
568
	option_values.padding = 4096;
569
570
	option_values.max_lpc_order = 8;
	option_values.qlp_coeff_precision = 0;
571
	option_values.skip_specification = 0;
572
	option_values.until_specification = 0;
573
	option_values.cue_specification = 0;
574
	option_values.format_is_big_endian = -1;
575
	option_values.format_is_unsigned_samples = -1;
576
577
578
	option_values.format_channels = -1;
	option_values.format_bps = -1;
	option_values.format_sample_rate = -1;
Josh Coalson's avatar
Josh Coalson committed
579
	option_values.format_input_size = -1;
580
581
582
583
584
585
	option_values.blocksize = -1;
	option_values.min_residual_partition_order = -1;
	option_values.max_residual_partition_order = -1;
	option_values.rice_parameter_search_dist = -1;
	option_values.requested_seek_points[0] = '\0';
	option_values.num_requested_seek_points = -1;
586
587
	option_values.cuesheet_filename = 0;
	option_values.cued_seekpoints = true;
588
589
590

	option_values.num_files = 0;
	option_values.filenames = 0;
Josh Coalson's avatar
Josh Coalson committed
591
592
593
594

	if(0 == (option_values.vorbis_comment = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)))
		return false;

595
596
597
598
	option_values.debug.disable_constant_subframes = false;
	option_values.debug.disable_fixed_subframes = false;
	option_values.debug.disable_verbatim_subframes = false;

Josh Coalson's avatar
Josh Coalson committed
599
	return true;
600
601
}

602
int parse_options(int argc, char *argv[])
603
{
Josh Coalson's avatar
Josh Coalson committed
604
605
	int short_option;
	int option_index = 1;
606
	FLAC__bool had_error = false;
607
	const char *short_opts = "0123456789ab:cdefFhHl:mMo:pP:q:r:sS:tT:vV";
608

Josh Coalson's avatar
Josh Coalson committed
609
	while ((short_option = share__getopt_long(argc, argv, short_opts, long_options_, &option_index)) != -1) {
Josh Coalson's avatar
Josh Coalson committed
610
611
		switch (short_option) {
			case 0: /* long option with no equivalent short option */
Josh Coalson's avatar
Josh Coalson committed
612
				had_error |= (parse_option(short_option, long_options_[option_index].name, share__optarg) != 0);
Josh Coalson's avatar
Josh Coalson committed
613
				break;
614
615
			case '?':
			case ':':
Josh Coalson's avatar
Josh Coalson committed
616
617
618
				had_error = true;
				break;
			default: /* short option */
Josh Coalson's avatar
Josh Coalson committed
619
				had_error |= (parse_option(short_option, 0, share__optarg) != 0);
Josh Coalson's avatar
Josh Coalson committed
620
621
622
				break;
		}
	}
623

624
625
626
	if(had_error) {
		return 1;
	}
627

Josh Coalson's avatar
Josh Coalson committed
628
	FLAC__ASSERT(share__optind <= argc);
629

Josh Coalson's avatar
Josh Coalson committed
630
	option_values.num_files = argc - share__optind;
631

632
633
	if(option_values.num_files > 0) {
		unsigned i = 0;
634
		if(0 == (option_values.filenames = (char**)malloc(sizeof(char*) * option_values.num_files)))
635
			die("out of memory allocating space for file names list");
Josh Coalson's avatar
Josh Coalson committed
636
637
		while(share__optind < argc)
			option_values.filenames[i++] = local_strdup(argv[share__optind++]);
638
	}
639
640
641
642
643
644
645
646
647
648

	return 0;
}

int parse_option(int short_option, const char *long_option, const char *option_argument)
{
	char *p;

	if(short_option == 0) {
		FLAC__ASSERT(0 != long_option);
649
		if(0 == strcmp(long_option, "totally-silent")) {
650
			flac__utils_verbosity_ = 0;
651
652
		}
		else if(0 == strcmp(long_option, "delete-input-file")) {
653
654
			option_values.delete_input = true;
		}
Josh Coalson's avatar
Josh Coalson committed
655
		else if(0 == strcmp(long_option, "output-prefix")) {
656
657
658
			FLAC__ASSERT(0 != option_argument);
			option_values.output_prefix = option_argument;
		}
Josh Coalson's avatar
Josh Coalson committed
659
		else if(0 == strcmp(long_option, "skip")) {
660
			FLAC__ASSERT(0 != option_argument);
661
			option_values.skip_specification = option_argument;
662
		}
663
664
665
666
		else if(0 == strcmp(long_option, "until")) {
			FLAC__ASSERT(0 != option_argument);
			option_values.until_specification = option_argument;
		}
Josh Coalson's avatar
Josh Coalson committed
667
668
669
670
		else if(0 == strcmp(long_option, "input-size")) {
			FLAC__ASSERT(0 != option_argument);
			option_values.format_input_size = atol(option_argument);
		}
671
672
673
674
		else if(0 == strcmp(long_option, "cue")) {
			FLAC__ASSERT(0 != option_argument);
			option_values.cue_specification = option_argument;
		}
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
		else if(0 == strcmp(long_option, "apply-replaygain-which-is-not-lossless")) {
			option_values.replaygain_synthesis_spec.apply = true;
			if (0 != option_argument) {
				char *p;
				option_values.replaygain_synthesis_spec.limiter = RGSS_LIMIT__NONE;
				option_values.replaygain_synthesis_spec.noise_shaping = NOISE_SHAPING_NONE;
				option_values.replaygain_synthesis_spec.preamp = strtod(option_argument, &p);
				for ( ; *p; p++) {
					if (*p == 'a')
						option_values.replaygain_synthesis_spec.use_album_gain = true;
					else if (*p == 't')
						option_values.replaygain_synthesis_spec.use_album_gain = false;
					else if (*p == 'l')
						option_values.replaygain_synthesis_spec.limiter = RGSS_LIMIT__PEAK;
					else if (*p == 'L')
						option_values.replaygain_synthesis_spec.limiter = RGSS_LIMIT__HARD;
					else if (*p == 'n' && p[1] >= '0' && p[1] <= '3') {
						option_values.replaygain_synthesis_spec.noise_shaping = p[1] - '0';
						p++;
					}
					else
						return usage_error("ERROR: bad specification string \"%s\" for --%s\n", option_argument, long_option);
				}
			}
		}
700
701
702
703
704
705
706
707
708
		else if(0 == strcmp(long_option, "cuesheet")) {
			FLAC__ASSERT(0 != option_argument);
			option_values.cuesheet_filename = option_argument;
		}
		else if(0 == strcmp(long_option, "no-cued-seekpoints")) {
			option_values.cued_seekpoints = false;
		}
		else if(0 == strcmp(long_option, "super-secret-totally-impractical-compression-level")) {
			option_values.lax = true;
709
710
711
712
713
714
715
716
717
718
			option_values.do_exhaustive_model_search = true;
			option_values.do_escape_coding = true;
			option_values.do_mid_side = true;
			option_values.loose_mid_side = false;
			option_values.do_qlp_coeff_prec_search = true;
			option_values.min_residual_partition_order = 0;
			option_values.max_residual_partition_order = 16;
			option_values.rice_parameter_search_dist = 0;
			option_values.max_lpc_order = 32;
		}
719
720
721
		else if(0 == strcmp(long_option, "force-aiff-format")) {
			option_values.force_aiff_format = true;
		}
722
		else if(0 == strcmp(long_option, "force-raw-format")) {
723
724
			option_values.force_raw_format = true;
		}
Josh Coalson's avatar
Josh Coalson committed
725
		else if(0 == strcmp(long_option, "lax")) {
726
727
			option_values.lax = true;
		}
728
729
730
		else if(0 == strcmp(long_option, "replay-gain")) {
			option_values.replay_gain = true;
		}
Josh Coalson's avatar
Josh Coalson committed
731
		else if(0 == strcmp(long_option, "sector-align")) {
732
733
734
			option_values.sector_align = true;
		}
#ifdef FLAC__HAS_OGG
Josh Coalson's avatar
Josh Coalson committed
735
		else if(0 == strcmp(long_option, "ogg")) {
736
737
			option_values.use_ogg = true;
		}
738
739
740
741
		else if(0 == strcmp(long_option, "serial-number")) {
			option_values.has_serial_number = true;
			option_values.serial_number = atol(option_argument);
		}
742
#endif
Josh Coalson's avatar
Josh Coalson committed
743
		else if(0 == strcmp(long_option, "endian")) {
744
745
746
747
748
			FLAC__ASSERT(0 != option_argument);
			if(0 == strncmp(option_argument, "big", strlen(option_argument)))
				option_values.format_is_big_endian = true;
			else if(0 == strncmp(option_argument, "little", strlen(option_argument)))
				option_values.format_is_big_endian = false;
Josh Coalson's avatar
Josh Coalson committed
749
			else
750
751
				return usage_error("ERROR: argument to --endian must be \"big\" or \"little\"\n");
		}
Josh Coalson's avatar
Josh Coalson committed
752
		else if(0 == strcmp(long_option, "channels")) {
753
754
755
			FLAC__ASSERT(0 != option_argument);
			option_values.format_channels = atoi(option_argument);
		}
Josh Coalson's avatar
Josh Coalson committed
756
		else if(0 == strcmp(long_option, "bps")) {
757
758
759
			FLAC__ASSERT(0 != option_argument);
			option_values.format_bps = atoi(option_argument);
		}
Josh Coalson's avatar
Josh Coalson committed
760
		else if(0 == strcmp(long_option, "sample-rate")) {
761
762
763
			FLAC__ASSERT(0 != option_argument);
			option_values.format_sample_rate = atoi(option_argument);
		}
Josh Coalson's avatar
Josh Coalson committed
764
		else if(0 == strcmp(long_option, "sign")) {
765
766
767
768
769
			FLAC__ASSERT(0 != option_argument);
			if(0 == strncmp(option_argument, "signed", strlen(option_argument)))
				option_values.format_is_unsigned_samples = false;
			else if(0 == strncmp(option_argument, "unsigned", strlen(option_argument)))
				option_values.format_is_unsigned_samples = true;
Josh Coalson's avatar
Josh Coalson committed
770
			else
771
772
				return usage_error("ERROR: argument to --sign must be \"signed\" or \"unsigned\"\n");
		}
773
		else if(0 == strcmp(long_option, "residual-gnuplot")) {
774
775
			option_values.aopts.do_residual_gnuplot = true;
		}
Josh Coalson's avatar
Josh Coalson committed
776
		else if(0 == strcmp(long_option, "residual-text")) {
777
778
779
780
781
			option_values.aopts.do_residual_text = true;
		}
		/*
		 * negatives
		 */
Josh Coalson's avatar
Josh Coalson committed
782
		else if(0 == strcmp(long_option, "no-decode-through-errors")) {
783
784
			option_values.continue_through_decode_errors = false;
		}
Josh Coalson's avatar
Josh Coalson committed
785
		else if(0 == strcmp(long_option, "no-silent")) {
786
			flac__utils_verbosity_ = 2;
787
		}
788
789
790
		else if(0 == strcmp(long_option, "no-force")) {
			option_values.force_file_overwrite = false;
		}
Josh Coalson's avatar
Josh Coalson committed
791
		else if(0 == strcmp(long_option, "no-seektable")) {
792
793
794
			option_values.num_requested_seek_points = 0;
			option_values.requested_seek_points[0] = '\0';
		}
Josh Coalson's avatar
Josh Coalson committed
795
		else if(0 == strcmp(long_option, "no-delete-input-file")) {
796
797
			option_values.delete_input = false;
		}
798
799
800
		else if(0 == strcmp(long_option, "no-replay-gain")) {
			option_values.replay_gain = false;
		}
Josh Coalson's avatar
Josh Coalson committed
801
		else if(0 == strcmp(long_option, "no-sector-align")) {
802
803
			option_values.sector_align = false;
		}
Josh Coalson's avatar
Josh Coalson committed
804
		else if(0 == strcmp(long_option, "no-lax")) {
805
806
807
			option_values.lax = false;
		}
#ifdef FLAC__HAS_OGG
Josh Coalson's avatar
Josh Coalson committed
808
		else if(0 == strcmp(long_option, "no-ogg")) {
809
810
811
			option_values.use_ogg = false;
		}
#endif
Josh Coalson's avatar
Josh Coalson committed
812
		else if(0 == strcmp(long_option, "no-exhaustive-model-search")) {
813
814
			option_values.do_exhaustive_model_search = false;
		}
Josh Coalson's avatar
Josh Coalson committed
815
		else if(0 == strcmp(long_option, "no-mid-side")) {
816
817
			option_values.do_mid_side = option_values.loose_mid_side = false;
		}
Josh Coalson's avatar
Josh Coalson committed
818
		else if(0 == strcmp(long_option, "no-adaptive-mid-side")) {
819
820
			option_values.loose_mid_side = option_values.do_mid_side = false;
		}
Josh Coalson's avatar
Josh Coalson committed
821
		else if(0 == strcmp(long_option, "no-qlp-coeff-prec-search")) {
822
823
			option_values.do_qlp_coeff_prec_search = false;
		}
Josh Coalson's avatar
Josh Coalson committed
824
		else if(0 == strcmp(long_option, "no-padding")) {
825
826
			option_values.padding = -1;
		}
Josh Coalson's avatar
Josh Coalson committed
827
		else if(0 == strcmp(long_option, "no-verify")) {
828
829
			option_values.verify = false;
		}
Josh Coalson's avatar
Josh Coalson committed
830
		else if(0 == strcmp(long_option, "no-residual-gnuplot")) {
831
832
			option_values.aopts.do_residual_gnuplot = false;
		}
Josh Coalson's avatar
Josh Coalson committed
833
		else if(0 == strcmp(long_option, "no-residual-text")) {
834
835
			option_values.aopts.do_residual_text = false;
		}
836
837
838
839
840
841
842
843
844
		else if(0 == strcmp(long_option, "disable-constant-subframes")) {
			option_values.debug.disable_constant_subframes = true;
		}
		else if(0 == strcmp(long_option, "disable-fixed-subframes")) {
			option_values.debug.disable_fixed_subframes = true;
		}
		else if(0 == strcmp(long_option, "disable-verbatim-subframes")) {
			option_values.debug.disable_verbatim_subframes = true;
		}
845
846
	}
	else {
Josh Coalson's avatar
Josh Coalson committed
847
		const char *violation;
848
		switch(short_option) {
849
850
851
			case 'h':
				option_values.show_help = true;
				break;
852
			case 'H':
853
				option_values.show_explain = true;
854
				break;
Josh Coalson's avatar
Josh Coalson committed
855
856
857
			case 'v':
				option_values.show_version = true;
				break;
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
			case 'd':
				option_values.mode_decode = true;
				break;
			case 'a':
				option_values.mode_decode = true;
				option_values.analyze = true;
				break;
			case 't':
				option_values.mode_decode = true;
				option_values.test_only = true;
				break;
			case 'c':
				option_values.force_to_stdout = true;
				break;
			case 's':
873
				flac__utils_verbosity_ = 1;
874
				break;
875
876
877
			case 'f':
				option_values.force_file_overwrite = true;
				break;
878
879
880
881
882
883
884
			case 'o':
				FLAC__ASSERT(0 != option_argument);
				option_values.cmdline_forced_outfilename = option_argument;
				break;
			case 'F':
				option_values.continue_through_decode_errors = true;
				break;
Josh Coalson's avatar
Josh Coalson committed
885
886
887
888
889
			case 'T':
				FLAC__ASSERT(0 != option_argument);
				if(!flac__vorbiscomment_add(option_values.vorbis_comment, option_argument, &violation))
					return usage_error("ERROR: (-T/--tag) %s\n", violation);
				break;
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
			case '0':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = false;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order = 2;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 0;
				break;
			case '1':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = true;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order = 2;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 0;
				break;
			case '2':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = 0;
				option_values.max_residual_partition_order = 3;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 0;
				break;
			case '3':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = false;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 6;
				break;
			case '4':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = true;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 8;
				break;
			case '5':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 8;
				break;
			case '6':
				option_values.do_exhaustive_model_search = false;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = 0;
				option_values.max_residual_partition_order = 4;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 8;
				break;
			case '7':
				option_values.do_exhaustive_model_search = true;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = 0;
				option_values.max_residual_partition_order = 6;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 8;
				break;
			case '8':
				option_values.do_exhaustive_model_search = true;
				option_values.do_escape_coding = false;
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				option_values.qlp_coeff_precision = 0;
				option_values.min_residual_partition_order = 0;
				option_values.max_residual_partition_order = 6;
				option_values.rice_parameter_search_dist = 0;
				option_values.max_lpc_order = 12;
				break;
			case '9':
				return usage_error("ERROR: compression level '9' is reserved\n");
			case 'V':
				option_values.verify = true;
				break;
			case 'S':
				FLAC__ASSERT(0 != option_argument);
				if(option_values.num_requested_seek_points < 0)
					option_values.num_requested_seek_points = 0;
				option_values.num_requested_seek_points++;
994
995
996
997
998
999
1000
				if(strlen(option_values.requested_seek_points)+strlen(option_argument)+2 >= sizeof(option_values.requested_seek_points)) {
					return usage_error("ERROR: too many seekpoints requested\n");
				}
				else {
					strcat(option_values.requested_seek_points, option_argument);
					strcat(option_values.requested_seek_points, ";");
				}
1001
1002
1003
1004
				break;
			case 'P':
				FLAC__ASSERT(0 != option_argument);
				option_values.padding = atoi(option_argument);
Josh Coalson's avatar
Josh Coalson committed
1005
				if(option_values.padding < 0)
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
					return usage_error("ERROR: argument to -P must be >= 0\n");
				break;
			case 'b':
				FLAC__ASSERT(0 != option_argument);
				option_values.blocksize = atoi(option_argument);
				break;
			case 'e':
				option_values.do_exhaustive_model_search = true;
				break;
			case 'E':
				option_values.do_escape_coding = true;
				break;
			case 'l':
				FLAC__ASSERT(0 != option_argument);
				option_values.max_lpc_order = atoi(option_argument);
				break;
			case 'm':
				option_values.do_mid_side = true;
				option_values.loose_mid_side = false;
				break;
			case 'M':
				option_values.loose_mid_side = option_values.do_mid_side = true;
				break;
			case 'p':
				option_values.do_qlp_coeff_prec_search = true;
				break;
			case 'q':
				FLAC__ASSERT(0 != option_argument);
				option_values.qlp_coeff_precision = atoi(option_argument);
				break;
			case 'r':
				FLAC__ASSERT(0 != option_argument);
				p = strchr(option_argument, ',');
				if(0 == p) {
					option_values.min_residual_partition_order = 0;
					option_values.max_residual_partition_order = atoi(option_argument);
				}
				else {
					option_values.min_residual_partition_order = atoi(option_argument);
					option_values.max_residual_partition_order = atoi(++p);
				}
				break;
			case 'R':
				FLAC__ASSERT(0 != option_argument);
				option_values.rice_parameter_search_dist = atoi(option_argument);
				break;
			default:
				FLAC__ASSERT(0);
		}
	}

Josh Coalson's avatar
Josh Coalson committed
1057
	return 0;
1058
1059
}

1060
1061
void free_options()
{
1062
1063
1064
1065
1066
1067
	unsigned i;
	if(0 != option_values.filenames) {
		for(i = 0; i < option_values.num_files; i++) {
			if(0 != option_values.filenames[i])
				free(option_values.filenames[i]);
		}
1068
		free(option_values.filenames);
1069
	}
Josh Coalson's avatar
Josh Coalson committed
1070
1071
	if(0 != option_values.vorbis_comment)
		FLAC__metadata_object_delete(option_values.vorbis_comment);
1072
1073
1074
}

int usage_error(const char *message, ...)
1075
{
1076
	if(flac__utils_verbosity_ >= 1) {
1077
		va_list args;
1078

1079
		FLAC__ASSERT(0 != message);
1080

1081
		va_start(args, message);
1082

1083
		(void) vfprintf(stderr, message, args);
1084

1085
		va_end(args);
1086

1087
1088
		printf("Type \"flac\" for a usage summary or \"flac --help\" for all options\n");
	}
1089
1090
1091
1092

	return 1;
}

Josh Coalson's avatar
Josh Coalson committed
1093
1094
1095
1096
1097
void show_version()
{
	printf("flac %s\n", FLAC__VERSION_STRING);
}

1098
1099
1100
1101
static void usage_header()
{
	printf("===============================================================================\n");
	printf("flac - Command-line FLAC encoder/decoder version %s\n", FLAC__VERSION_STRING);
Josh Coalson's avatar
Josh Coalson committed
1102
	printf("Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson\n");
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
	printf("\n");
	printf("This program is free software; you can redistribute it and/or\n");
	printf("modify it under the terms of the GNU General Public License\n");
	printf("as published by the Free Software Foundation; either version 2\n");
	printf("of the License, or (at your option) any later version.\n");
	printf("\n");
	printf("This program is distributed in the hope that it will be useful,\n");
	printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
	printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
	printf("GNU General Public License for more details.\n");
	printf("\n");
	printf("You should have received a copy of the GNU General Public License\n");
	printf("along with this program; if not, write to the Free Software\n");
	printf("Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n");
	printf("===============================================================================\n");
}

1120
1121
1122
1123
static void usage_summary()
{
	printf("Usage:\n");
	printf("\n");
Josh Coalson's avatar
Josh Coalson committed
1124
1125
1126
1127
	printf(" Encoding: flac [<general-options>] [<encoding/format-options>] [INPUTFILE [...]]\n");
	printf(" Decoding: flac -d [<general-options>] [<format-options>] [FLACFILE [...]]\n");
	printf("  Testing: flac -t [<general-options>] [FLACFILE [...]]\n");
	printf("Analyzing: flac -a [<general-options>] [<analysis-options>] [FLACFILE [...]]\n");
1128
1129
1130
	printf("\n");
}

1131
1132
void short_usage()
{
1133
	usage_header();
1134
	printf("\n");
1135
1136
	printf("This is the short help; for all options use 'flac --help'; for even more\n");
	printf("instructions use 'flac --explain'\n");
1137
1138
	printf("\n");
	printf("To encode:\n");