Coverage Report

Created: 2023-11-19 06:20

/src/flac/src/metaflac/options.c
Line
Count
Source (jump to first uncovered line)
1
/* metaflac - Command-line FLAC metadata editor
2
 * Copyright (C) 2001-2009  Josh Coalson
3
 * Copyright (C) 2011-2023  Xiph.Org Foundation
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include <config.h>
22
#endif
23
24
#include "options.h"
25
#include "usage.h"
26
#include "utils.h"
27
#include "FLAC/assert.h"
28
#include "share/alloc.h"
29
#include "share/compat.h"
30
#include "share/grabbag/replaygain.h"
31
#include <ctype.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
36
/*
37
   share__getopt format struct; note we don't use short options so we just
38
   set the 'val' field to 0 everywhere to indicate a valid option.
39
*/
40
struct share__option long_options_[] = {
41
  /* global options */
42
  { "preserve-modtime", 0, 0, 0 },
43
  { "with-filename", 0, 0, 0 },
44
  { "no-filename", 0, 0, 0 },
45
  { "no-utf8-convert", 0, 0, 0 },
46
  { "dont-use-padding", 0, 0, 0 },
47
  { "no-cued-seekpoints", 0, 0, 0 },
48
  /* shorthand operations */
49
  { "show-md5sum", 0, 0, 0 },
50
  { "show-min-blocksize", 0, 0, 0 },
51
  { "show-max-blocksize", 0, 0, 0 },
52
  { "show-min-framesize", 0, 0, 0 },
53
  { "show-max-framesize", 0, 0, 0 },
54
  { "show-sample-rate", 0, 0, 0 },
55
  { "show-channels", 0, 0, 0 },
56
  { "show-bps", 0, 0, 0 },
57
  { "show-total-samples", 0, 0, 0 },
58
  { "set-md5sum", 1, 0, 0 }, /* undocumented */
59
  { "set-min-blocksize", 1, 0, 0 }, /* undocumented */
60
  { "set-max-blocksize", 1, 0, 0 }, /* undocumented */
61
  { "set-min-framesize", 1, 0, 0 }, /* undocumented */
62
  { "set-max-framesize", 1, 0, 0 }, /* undocumented */
63
  { "set-sample-rate", 1, 0, 0 }, /* undocumented */
64
  { "set-channels", 1, 0, 0 }, /* undocumented */
65
  { "set-bps", 1, 0, 0 }, /* undocumented */
66
  { "set-total-samples", 1, 0, 0 }, /* undocumented */ /* WATCHOUT: used by test/test_flac.sh on windows */
67
  { "show-vendor-tag", 0, 0, 0 },
68
  { "show-all-tags", 0, 0, 0 },
69
  { "show-tag", 1, 0, 0 },
70
  { "remove-all-tags", 0, 0, 0 },
71
  { "remove-all-tags-except", 1, 0, 0 },
72
  { "remove-tag", 1, 0, 0 },
73
  { "remove-first-tag", 1, 0, 0 },
74
  { "set-tag", 1, 0, 0 },
75
  { "set-tag-from-file", 1, 0, 0 },
76
  { "import-tags-from", 1, 0, 0 },
77
  { "export-tags-to", 1, 0, 0 },
78
  { "import-cuesheet-from", 1, 0, 0 },
79
  { "export-cuesheet-to", 1, 0, 0 },
80
  { "import-picture-from", 1, 0, 0 },
81
  { "export-picture-to", 1, 0, 0 },
82
  { "add-seekpoint", 1, 0, 0 },
83
  { "add-replay-gain", 0, 0, 0 },
84
  { "scan-replay-gain", 0, 0, 0 },
85
  { "remove-replay-gain", 0, 0, 0 },
86
  { "add-padding", 1, 0, 0 },
87
  /* major operations */
88
  { "help", 0, 0, 0 },
89
  { "version", 0, 0, 0 },
90
  { "list", 0, 0, 0 },
91
  { "append", 0, 0, 0 },
92
  { "remove", 0, 0, 0 },
93
  { "remove-all", 0, 0, 0 },
94
  { "merge-padding", 0, 0, 0 },
95
  { "sort-padding", 0, 0, 0 },
96
  /* major operation arguments */
97
  { "block-number", 1, 0, 0 },
98
  { "block-type", 1, 0, 0 },
99
  { "except-block-type", 1, 0, 0 },
100
  { "data-format", 1, 0, 0 },
101
  { "application-data-format", 1, 0, 0 },
102
  { "from-file", 1, 0, 0 },
103
  {0, 0, 0, 0}
104
};
105
106
static FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options);
107
static void append_new_operation(CommandLineOptions *options, Operation operation);
108
static void append_new_argument(CommandLineOptions *options, Argument argument);
109
static Operation *append_major_operation(CommandLineOptions *options, OperationType type);
110
static Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type);
111
static Argument *find_argument(CommandLineOptions *options, ArgumentType type);
112
static Operation *find_shorthand_operation(CommandLineOptions *options, OperationType type);
113
static Argument *append_argument(CommandLineOptions *options, ArgumentType type);
114
static FLAC__bool parse_md5(const char *src, FLAC__byte dest[16]);
115
static FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest);
116
static FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest);
117
static FLAC__bool parse_string(const char *src, char **dest);
118
static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation);
119
static FLAC__bool parse_vorbis_comment_field_names(const char *field_ref, char **names, const char **violation);
120
static FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation);
121
static FLAC__bool parse_add_padding(const char *in, unsigned *out);
122
static FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out);
123
static FLAC__bool parse_block_type(const char *in, Argument_BlockType *out);
124
static FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out);
125
static FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out);
126
static void undocumented_warning(const char *opt);
127
128
129
void init_options(CommandLineOptions *options)
130
13.7k
{
131
13.7k
  options->preserve_modtime = false;
132
133
  /* '2' is a hack to mean "use default if not forced on command line" */
134
13.7k
  FLAC__ASSERT(true != 2);
135
13.7k
  options->prefix_with_filename = 2;
136
137
13.7k
  options->utf8_convert = true;
138
13.7k
  options->use_padding = true;
139
13.7k
  options->cued_seekpoints = true;
140
13.7k
  options->show_long_help = false;
141
13.7k
  options->show_version = false;
142
13.7k
  options->data_format_is_binary = false;
143
13.7k
  options->data_format_is_binary_headerless = false;
144
13.7k
  options->application_data_format_is_hexdump = false;
145
146
13.7k
  options->ops.operations = 0;
147
13.7k
  options->ops.num_operations = 0;
148
13.7k
  options->ops.capacity = 0;
149
150
13.7k
  options->args.arguments = 0;
151
13.7k
  options->args.num_arguments = 0;
152
13.7k
  options->args.capacity = 0;
153
154
13.7k
  options->args.checks.num_shorthand_ops = 0;
155
13.7k
  options->args.checks.num_major_ops = 0;
156
13.7k
  options->args.checks.has_block_type = false;
157
13.7k
  options->args.checks.has_except_block_type = false;
158
159
13.7k
  options->num_files = 0;
160
13.7k
  options->filenames = 0;
161
13.7k
}
162
163
FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options)
164
13.7k
{
165
13.7k
  int ret;
166
13.7k
  int option_index = 1;
167
13.7k
  FLAC__bool had_error = false;
168
169
41.7k
  while ((ret = share__getopt_long(argc, argv, "", long_options_, &option_index)) != -1) {
170
27.9k
    switch (ret) {
171
26.9k
      case 0:
172
26.9k
        had_error |= !parse_option(option_index, share__optarg, options);
173
26.9k
        break;
174
968
      case '?':
175
968
      case ':':
176
968
        had_error = true;
177
968
        break;
178
0
      default:
179
0
        FLAC__ASSERT(0);
180
0
        break;
181
27.9k
    }
182
27.9k
  }
183
184
13.7k
  if(options->prefix_with_filename == 2)
185
13.6k
    options->prefix_with_filename = (argc - share__optind > 1);
186
187
13.7k
  if(share__optind >= argc && !options->show_long_help && !options->show_version) {
188
28
    flac_fprintf(stderr,"ERROR: you must specify at least one FLAC file;\n");
189
28
    flac_fprintf(stderr,"       metaflac cannot be used as a pipe\n");
190
28
    had_error = true;
191
28
  }
192
193
13.7k
  options->num_files = argc - share__optind;
194
195
13.7k
  if(options->num_files > 0) {
196
13.7k
    unsigned i = 0;
197
13.7k
    if(0 == (options->filenames = safe_malloc_mul_2op_(sizeof(char*), /*times*/options->num_files)))
198
0
      die("out of memory allocating space for file names list");
199
47.6k
    while(share__optind < argc)
200
33.9k
      options->filenames[i++] = local_strdup(argv[share__optind++]);
201
13.7k
  }
202
203
13.7k
  if(options->args.checks.num_major_ops > 0) {
204
2.55k
    if(options->args.checks.num_major_ops > 1) {
205
32
      flac_fprintf(stderr, "ERROR: you may only specify one major operation at a time\n");
206
32
      had_error = true;
207
32
    }
208
2.51k
    else if(options->args.checks.num_shorthand_ops > 0) {
209
7
      flac_fprintf(stderr, "ERROR: you may not mix shorthand and major operations\n");
210
7
      had_error = true;
211
7
    }
212
2.55k
  }
213
214
  /* check for only one FLAC file used with certain options */
215
13.7k
  if(!had_error && options->num_files > 1) {
216
8.88k
    if(0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM)) {
217
6
      flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--import-cuesheet-from'\n");
218
6
      had_error = true;
219
6
    }
220
8.88k
    if(0 != find_shorthand_operation(options, OP__EXPORT_CUESHEET_TO)) {
221
6
      flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--export-cuesheet-to'\n");
222
6
      had_error = true;
223
6
    }
224
8.88k
    if(0 != find_shorthand_operation(options, OP__EXPORT_PICTURE_TO)) {
225
4
      flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--export-picture-to'\n");
226
4
      had_error = true;
227
4
    }
228
8.88k
    if(
229
8.88k
      0 != find_shorthand_operation(options, OP__IMPORT_VC_FROM) &&
230
8.88k
      0 == strcmp(find_shorthand_operation(options, OP__IMPORT_VC_FROM)->argument.filename.value, "-")
231
8.88k
    ) {
232
1
      flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--import-tags-from=-'\n");
233
1
      had_error = true;
234
1
    }
235
8.88k
  }
236
237
13.7k
  if(options->args.checks.has_block_type && options->args.checks.has_except_block_type) {
238
1
    flac_fprintf(stderr, "ERROR: you may not specify both '--block-type' and '--except-block-type'\n");
239
1
    had_error = true;
240
1
  }
241
242
13.7k
  if(had_error)
243
1.96k
    short_usage(0);
244
245
  /*
246
   * We need to create an OP__ADD_SEEKPOINT operation if there is
247
   * not one already, and --import-cuesheet-from was specified but
248
   * --no-cued-seekpoints was not:
249
   */
250
13.7k
  if(options->cued_seekpoints) {
251
13.7k
    Operation *op = find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM);
252
13.7k
    if(0 != op) {
253
2.20k
      Operation *op2 = find_shorthand_operation(options, OP__ADD_SEEKPOINT);
254
2.20k
      if(0 == op2)
255
2.20k
        op2 = append_shorthand_operation(options, OP__ADD_SEEKPOINT);
256
2.20k
      op->argument.import_cuesheet_from.add_seekpoint_link = &(op2->argument.add_seekpoint);
257
2.20k
    }
258
13.7k
  }
259
260
13.7k
  return had_error;
261
13.7k
}
262
263
void free_options(CommandLineOptions *options)
264
13.7k
{
265
13.7k
  unsigned i;
266
13.7k
  Operation *op;
267
13.7k
  Argument *arg;
268
269
13.7k
  FLAC__ASSERT(0 == options->ops.operations || options->ops.num_operations > 0);
270
13.7k
  FLAC__ASSERT(0 == options->args.arguments || options->args.num_arguments > 0);
271
272
40.3k
  for(i = 0, op = options->ops.operations; i < options->ops.num_operations; i++, op++) {
273
26.6k
    switch(op->type) {
274
165
      case OP__SHOW_VC_FIELD:
275
1.41k
      case OP__REMOVE_VC_FIELD:
276
1.62k
      case OP__REMOVE_VC_FIRSTFIELD:
277
1.91k
      case OP__REMOVE_VC_ALL_EXCEPT:
278
1.91k
        if(0 != op->argument.vc_field_name.value)
279
1.78k
          free(op->argument.vc_field_name.value);
280
1.91k
        break;
281
974
      case OP__SET_VC_FIELD:
282
974
        if(0 != op->argument.vc_field.field)
283
974
          free(op->argument.vc_field.field);
284
974
        if(0 != op->argument.vc_field.field_name)
285
906
          free(op->argument.vc_field.field_name);
286
974
        if(0 != op->argument.vc_field.field_value)
287
906
          free(op->argument.vc_field.field_value);
288
974
        break;
289
111
      case OP__IMPORT_VC_FROM:
290
292
      case OP__EXPORT_VC_TO:
291
393
      case OP__EXPORT_CUESHEET_TO:
292
393
        if(0 != op->argument.filename.value)
293
353
          free(op->argument.filename.value);
294
393
        break;
295
2.25k
      case OP__IMPORT_CUESHEET_FROM:
296
2.25k
        if(0 != op->argument.import_cuesheet_from.filename)
297
2.22k
          free(op->argument.import_cuesheet_from.filename);
298
2.25k
        break;
299
651
      case OP__IMPORT_PICTURE_FROM:
300
651
        if(0 != op->argument.specification.value)
301
640
          free(op->argument.specification.value);
302
651
        break;
303
182
      case OP__EXPORT_PICTURE_TO:
304
182
        if(0 != op->argument.export_picture_to.filename)
305
127
          free(op->argument.export_picture_to.filename);
306
182
        break;
307
3.42k
      case OP__ADD_SEEKPOINT:
308
3.42k
        if(0 != op->argument.add_seekpoint.specification)
309
1.56k
          free(op->argument.add_seekpoint.specification);
310
3.42k
        break;
311
16.8k
      default:
312
16.8k
        break;
313
26.6k
    }
314
26.6k
  }
315
316
16.5k
  for(i = 0, arg = options->args.arguments; i < options->args.num_arguments; i++, arg++) {
317
2.74k
    switch(arg->type) {
318
580
      case ARG__BLOCK_NUMBER:
319
580
        if(0 != arg->value.block_number.entries)
320
567
          free(arg->value.block_number.entries);
321
580
        break;
322
47
      case ARG__BLOCK_TYPE:
323
992
      case ARG__EXCEPT_BLOCK_TYPE:
324
992
        if(0 != arg->value.block_type.entries)
325
959
          free(arg->value.block_type.entries);
326
992
        break;
327
594
      case ARG__FROM_FILE:
328
594
        if(0 != arg->value.from_file.file_name)
329
594
          free(arg->value.from_file.file_name);
330
594
        break;
331
576
      default:
332
576
        break;
333
2.74k
    }
334
2.74k
  }
335
336
13.7k
  if(0 != options->ops.operations)
337
12.2k
    free(options->ops.operations);
338
339
13.7k
  if(0 != options->args.arguments)
340
2.30k
    free(options->args.arguments);
341
342
13.7k
  if(0 != options->filenames) {
343
47.6k
    for(i = 0; i < options->num_files; i++) {
344
33.9k
      if(0 != options->filenames[i])
345
33.9k
        free(options->filenames[i]);
346
33.9k
    }
347
13.7k
    free(options->filenames);
348
13.7k
  }
349
13.7k
}
350
351
/*
352
 * local routines
353
 */
354
355
FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options)
356
26.9k
{
357
26.9k
  const char *opt = long_options_[option_index].name;
358
26.9k
  Operation *op;
359
26.9k
  Argument *arg;
360
26.9k
  FLAC__bool ok = true;
361
362
26.9k
  if(0 == strcmp(opt, "preserve-modtime")) {
363
25
    options->preserve_modtime = true;
364
25
  }
365
26.9k
  else if(0 == strcmp(opt, "with-filename")) {
366
20
    options->prefix_with_filename = true;
367
20
  }
368
26.9k
  else if(0 == strcmp(opt, "no-filename")) {
369
83
    options->prefix_with_filename = false;
370
83
  }
371
26.8k
  else if(0 == strcmp(opt, "no-utf8-convert")) {
372
104
    options->utf8_convert = false;
373
104
  }
374
26.7k
  else if(0 == strcmp(opt, "dont-use-padding")) {
375
41
    options->use_padding = false;
376
41
  }
377
26.7k
  else if(0 == strcmp(opt, "no-cued-seekpoints")) {
378
18
    options->cued_seekpoints = false;
379
18
  }
380
26.6k
  else if(0 == strcmp(opt, "show-md5sum")) {
381
46
    (void) append_shorthand_operation(options, OP__SHOW_MD5SUM);
382
46
  }
383
26.6k
  else if(0 == strcmp(opt, "show-min-blocksize")) {
384
37
    (void) append_shorthand_operation(options, OP__SHOW_MIN_BLOCKSIZE);
385
37
  }
386
26.6k
  else if(0 == strcmp(opt, "show-max-blocksize")) {
387
37
    (void) append_shorthand_operation(options, OP__SHOW_MAX_BLOCKSIZE);
388
37
  }
389
26.5k
  else if(0 == strcmp(opt, "show-min-framesize")) {
390
37
    (void) append_shorthand_operation(options, OP__SHOW_MIN_FRAMESIZE);
391
37
  }
392
26.5k
  else if(0 == strcmp(opt, "show-max-framesize")) {
393
37
    (void) append_shorthand_operation(options, OP__SHOW_MAX_FRAMESIZE);
394
37
  }
395
26.4k
  else if(0 == strcmp(opt, "show-sample-rate")) {
396
38
    (void) append_shorthand_operation(options, OP__SHOW_SAMPLE_RATE);
397
38
  }
398
26.4k
  else if(0 == strcmp(opt, "show-channels")) {
399
38
    (void) append_shorthand_operation(options, OP__SHOW_CHANNELS);
400
38
  }
401
26.4k
  else if(0 == strcmp(opt, "show-bps")) {
402
60
    (void) append_shorthand_operation(options, OP__SHOW_BPS);
403
60
  }
404
26.3k
  else if(0 == strcmp(opt, "show-total-samples")) {
405
39
    (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES);
406
39
  }
407
26.3k
  else if(0 == strcmp(opt, "set-md5sum")) {
408
107
    op = append_shorthand_operation(options, OP__SET_MD5SUM);
409
107
    FLAC__ASSERT(0 != option_argument);
410
107
    if(!parse_md5(option_argument, op->argument.streaminfo_md5.value)) {
411
86
      flac_fprintf(stderr, "ERROR (--%s): bad MD5 sum\n", opt);
412
86
      ok = false;
413
86
    }
414
21
    else
415
21
      undocumented_warning(opt);
416
107
  }
417
26.2k
  else if(0 == strcmp(opt, "set-min-blocksize")) {
418
112
    op = append_shorthand_operation(options, OP__SET_MIN_BLOCKSIZE);
419
112
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) {
420
69
      flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
421
69
      ok = false;
422
69
    }
423
43
    else
424
43
      undocumented_warning(opt);
425
112
  }
426
26.1k
  else if(0 == strcmp(opt, "set-max-blocksize")) {
427
164
    op = append_shorthand_operation(options, OP__SET_MAX_BLOCKSIZE);
428
164
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) {
429
101
      flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
430
101
      ok = false;
431
101
    }
432
63
    else
433
63
      undocumented_warning(opt);
434
164
  }
435
25.9k
  else if(0 == strcmp(opt, "set-min-framesize")) {
436
45
    op = append_shorthand_operation(options, OP__SET_MIN_FRAMESIZE);
437
45
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) {
438
24
      flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN);
439
24
      ok = false;
440
24
    }
441
21
    else
442
21
      undocumented_warning(opt);
443
45
  }
444
25.8k
  else if(0 == strcmp(opt, "set-max-framesize")) {
445
80
    op = append_shorthand_operation(options, OP__SET_MAX_FRAMESIZE);
446
80
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) {
447
43
      flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN);
448
43
      ok = false;
449
43
    }
450
37
    else
451
37
      undocumented_warning(opt);
452
80
  }
453
25.8k
  else if(0 == strcmp(opt, "set-sample-rate")) {
454
107
    op = append_shorthand_operation(options, OP__SET_SAMPLE_RATE);
455
107
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || !FLAC__format_sample_rate_is_valid(op->argument.streaminfo_uint32.value)) {
456
58
      flac_fprintf(stderr, "ERROR (--%s): invalid sample rate\n", opt);
457
58
      ok = false;
458
58
    }
459
49
    else
460
49
      undocumented_warning(opt);
461
107
  }
462
25.7k
  else if(0 == strcmp(opt, "set-channels")) {
463
114
    op = append_shorthand_operation(options, OP__SET_CHANNELS);
464
114
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value > FLAC__MAX_CHANNELS) {
465
72
      flac_fprintf(stderr, "ERROR (--%s): value must be > 0 and <= %u\n", opt, FLAC__MAX_CHANNELS);
466
72
      ok = false;
467
72
    }
468
42
    else
469
42
      undocumented_warning(opt);
470
114
  }
471
25.5k
  else if(0 == strcmp(opt, "set-bps")) {
472
150
    op = append_shorthand_operation(options, OP__SET_BPS);
473
150
    if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BITS_PER_SAMPLE || op->argument.streaminfo_uint32.value > FLAC__MAX_BITS_PER_SAMPLE) {
474
120
      flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE);
475
120
      ok = false;
476
120
    }
477
30
    else
478
30
      undocumented_warning(opt);
479
150
  }
480
25.4k
  else if(0 == strcmp(opt, "set-total-samples")) {
481
140
    op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES);
482
140
    if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (((FLAC__uint64)1)<<FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) {
483
102
      flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN);
484
102
      ok = false;
485
102
    }
486
38
    else
487
38
      undocumented_warning(opt);
488
140
  }
489
25.3k
  else if(0 == strcmp(opt, "show-vendor-tag")) {
490
73
    (void) append_shorthand_operation(options, OP__SHOW_VC_VENDOR);
491
73
  }
492
25.2k
  else if(0 == strcmp(opt, "show-tag")) {
493
165
    const char *violation;
494
165
    op = append_shorthand_operation(options, OP__SHOW_VC_FIELD);
495
165
    FLAC__ASSERT(0 != option_argument);
496
165
    if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
497
46
      FLAC__ASSERT(0 != violation);
498
46
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
499
46
      ok = false;
500
46
    }
501
165
  }
502
25.0k
  else if(0 == strcmp(opt, "show-all-tags")) {
503
112
    op = append_shorthand_operation(options, OP__EXPORT_VC_TO);
504
112
    parse_string("-",&op->argument.filename.value);
505
112
  }
506
24.9k
  else if(0 == strcmp(opt, "remove-all-tags")) {
507
80
    (void) append_shorthand_operation(options, OP__REMOVE_VC_ALL);
508
80
  }
509
24.8k
  else if(0 == strcmp(opt, "remove-all-tags-except")) {
510
290
    const char *violation;
511
290
    op = append_shorthand_operation(options, OP__REMOVE_VC_ALL_EXCEPT);
512
290
    FLAC__ASSERT(0 != option_argument);
513
290
    if(!parse_vorbis_comment_field_names(option_argument, &(op->argument.vc_field_name.value), &violation)) {
514
45
      FLAC__ASSERT(0 != violation);
515
45
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
516
45
      ok = false;
517
45
    }
518
290
  }
519
24.5k
  else if(0 == strcmp(opt, "remove-tag")) {
520
82
    const char *violation;
521
82
    op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
522
82
    FLAC__ASSERT(0 != option_argument);
523
82
    if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
524
19
      FLAC__ASSERT(0 != violation);
525
19
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
526
19
      ok = false;
527
19
    }
528
82
  }
529
24.5k
  else if(0 == strcmp(opt, "remove-first-tag")) {
530
210
    const char *violation;
531
210
    op = append_shorthand_operation(options, OP__REMOVE_VC_FIRSTFIELD);
532
210
    FLAC__ASSERT(0 != option_argument);
533
210
    if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
534
18
      FLAC__ASSERT(0 != violation);
535
18
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
536
18
      ok = false;
537
18
    }
538
210
  }
539
24.2k
  else if(0 == strcmp(opt, "set-tag")) {
540
932
    const char *violation;
541
932
    op = append_shorthand_operation(options, OP__SET_VC_FIELD);
542
932
    FLAC__ASSERT(0 != option_argument);
543
932
    op->argument.vc_field.field_value_from_file = false;
544
932
    if(!parse_vorbis_comment_field(option_argument, &(op->argument.vc_field.field), &(op->argument.vc_field.field_name), &(op->argument.vc_field.field_value), &(op->argument.vc_field.field_value_length), &violation)) {
545
50
      FLAC__ASSERT(0 != violation);
546
50
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n       %s\n", opt, option_argument, violation);
547
50
      ok = false;
548
50
    }
549
932
  }
550
23.3k
  else if(0 == strcmp(opt, "set-tag-from-file")) {
551
42
    const char *violation;
552
42
    op = append_shorthand_operation(options, OP__SET_VC_FIELD);
553
42
    FLAC__ASSERT(0 != option_argument);
554
42
    op->argument.vc_field.field_value_from_file = true;
555
42
    if(!parse_vorbis_comment_field(option_argument, &(op->argument.vc_field.field), &(op->argument.vc_field.field_name), &(op->argument.vc_field.field_value), &(op->argument.vc_field.field_value_length), &violation)) {
556
18
      FLAC__ASSERT(0 != violation);
557
18
      flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n       %s\n", opt, option_argument, violation);
558
18
      ok = false;
559
18
    }
560
42
  }
561
23.3k
  else if(0 == strcmp(opt, "import-tags-from")) {
562
111
    op = append_shorthand_operation(options, OP__IMPORT_VC_FROM);
563
111
    FLAC__ASSERT(0 != option_argument);
564
111
    if(!parse_string(option_argument, &(op->argument.filename.value))) {
565
12
      flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
566
12
      ok = false;
567
12
    }
568
111
  }
569
23.2k
  else if(0 == strcmp(opt, "export-tags-to")) {
570
69
    op = append_shorthand_operation(options, OP__EXPORT_VC_TO);
571
69
    FLAC__ASSERT(0 != option_argument);
572
69
    if(!parse_string(option_argument, &(op->argument.filename.value))) {
573
18
      flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
574
18
      ok = false;
575
18
    }
576
69
  }
577
23.1k
  else if(0 == strcmp(opt, "import-cuesheet-from")) {
578
2.25k
    if(0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM)) {
579
49
      flac_fprintf(stderr, "ERROR (--%s): may be specified only once\n", opt);
580
49
      ok = false;
581
49
    }
582
2.25k
    op = append_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM);
583
2.25k
    FLAC__ASSERT(0 != option_argument);
584
2.25k
    if(!parse_string(option_argument, &(op->argument.import_cuesheet_from.filename))) {
585
34
      flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
586
34
      ok = false;
587
34
    }
588
2.25k
  }
589
20.8k
  else if(0 == strcmp(opt, "export-cuesheet-to")) {
590
101
    op = append_shorthand_operation(options, OP__EXPORT_CUESHEET_TO);
591
101
    FLAC__ASSERT(0 != option_argument);
592
101
    if(!parse_string(option_argument, &(op->argument.filename.value))) {
593
10
      flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
594
10
      ok = false;
595
10
    }
596
101
  }
597
20.7k
  else if(0 == strcmp(opt, "import-picture-from")) {
598
651
    op = append_shorthand_operation(options, OP__IMPORT_PICTURE_FROM);
599
651
    FLAC__ASSERT(0 != option_argument);
600
651
    if(!parse_string(option_argument, &(op->argument.specification.value))) {
601
11
      flac_fprintf(stderr, "ERROR (--%s): missing specification\n", opt);
602
11
      ok = false;
603
11
    }
604
651
  }
605
20.1k
  else if(0 == strcmp(opt, "export-picture-to")) {
606
182
    arg = find_argument(options, ARG__BLOCK_NUMBER);
607
182
    op = append_shorthand_operation(options, OP__EXPORT_PICTURE_TO);
608
182
    FLAC__ASSERT(0 != option_argument);
609
182
    if(!parse_string(option_argument, &(op->argument.export_picture_to.filename))) {
610
55
      flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
611
55
      ok = false;
612
55
    }
613
182
    op->argument.export_picture_to.block_number_link = arg? &(arg->value.block_number) : 0;
614
182
  }
615
19.9k
  else if(0 == strcmp(opt, "add-seekpoint")) {
616
1.53k
    const char *violation;
617
1.53k
    char *spec;
618
1.53k
    FLAC__ASSERT(0 != option_argument);
619
1.53k
    if(!parse_add_seekpoint(option_argument, &spec, &violation)) {
620
142
      FLAC__ASSERT(0 != violation);
621
142
      flac_fprintf(stderr, "ERROR (--%s): malformed seekpoint specification \"%s\",\n       %s\n", opt, option_argument, violation);
622
142
      ok = false;
623
142
    }
624
1.39k
    else {
625
1.39k
      op = find_shorthand_operation(options, OP__ADD_SEEKPOINT);
626
1.39k
      if(0 == op)
627
1.22k
        op = append_shorthand_operation(options, OP__ADD_SEEKPOINT);
628
1.39k
      local_strcat(&(op->argument.add_seekpoint.specification), spec);
629
1.39k
      local_strcat(&(op->argument.add_seekpoint.specification), ";");
630
1.39k
      free(spec);
631
1.39k
    }
632
1.53k
  }
633
18.4k
  else if(0 == strcmp(opt, "add-replay-gain")) {
634
54
    (void) append_shorthand_operation(options, OP__ADD_REPLAY_GAIN);
635
54
  }
636
18.3k
  else if(0 == strcmp(opt, "scan-replay-gain")) {
637
12.3k
    (void) append_shorthand_operation(options, OP__SCAN_REPLAY_GAIN);
638
12.3k
  }
639
5.99k
  else if(0 == strcmp(opt, "remove-replay-gain")) {
640
234
    const FLAC__byte * const tags[5] = {
641
234
      GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS,
642
234
      GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN,
643
234
      GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK,
644
234
      GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN,
645
234
      GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK
646
234
    };
647
234
    size_t i;
648
1.40k
    for(i = 0; i < sizeof(tags)/sizeof(tags[0]); i++) {
649
1.17k
      op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
650
1.17k
      op->argument.vc_field_name.value = local_strdup((const char *)tags[i]);
651
1.17k
    }
652
234
  }
653
5.75k
  else if(0 == strcmp(opt, "add-padding")) {
654
191
    op = append_shorthand_operation(options, OP__ADD_PADDING);
655
191
    FLAC__ASSERT(0 != option_argument);
656
191
    if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) {
657
16
      flac_fprintf(stderr, "ERROR (--%s): illegal length \"%s\", length must be >= 0 and < 2^%u\n", opt, option_argument, FLAC__STREAM_METADATA_LENGTH_LEN);
658
16
      ok = false;
659
16
    }
660
191
  }
661
5.56k
  else if(0 == strcmp(opt, "help")) {
662
19
    options->show_long_help = true;
663
19
  }
664
5.54k
  else if(0 == strcmp(opt, "version")) {
665
20
    options->show_version = true;
666
20
  }
667
5.52k
  else if(0 == strcmp(opt, "list")) {
668
1.74k
    (void) append_major_operation(options, OP__LIST);
669
1.74k
  }
670
3.78k
  else if(0 == strcmp(opt, "append")) {
671
577
    (void) append_major_operation(options, OP__APPEND);
672
577
  }
673
3.21k
  else if(0 == strcmp(opt, "remove")) {
674
75
    (void) append_major_operation(options, OP__REMOVE);
675
75
  }
676
3.13k
  else if(0 == strcmp(opt, "remove-all")) {
677
42
    (void) append_major_operation(options, OP__REMOVE_ALL);
678
42
  }
679
3.09k
  else if(0 == strcmp(opt, "merge-padding")) {
680
185
    (void) append_major_operation(options, OP__MERGE_PADDING);
681
185
  }
682
2.90k
  else if(0 == strcmp(opt, "sort-padding")) {
683
31
    (void) append_major_operation(options, OP__SORT_PADDING);
684
31
  }
685
2.87k
  else if(0 == strcmp(opt, "block-number")) {
686
580
    arg = append_argument(options, ARG__BLOCK_NUMBER);
687
580
    FLAC__ASSERT(0 != option_argument);
688
580
    if(!parse_block_number(option_argument, &(arg->value.block_number))) {
689
112
      flac_fprintf(stderr, "ERROR: malformed block number specification \"%s\"\n", option_argument);
690
112
      ok = false;
691
112
    }
692
580
  }
693
2.29k
  else if(0 == strcmp(opt, "block-type")) {
694
47
    arg = append_argument(options, ARG__BLOCK_TYPE);
695
47
    FLAC__ASSERT(0 != option_argument);
696
47
    if(!parse_block_type(option_argument, &(arg->value.block_type))) {
697
21
      flac_fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
698
21
      ok = false;
699
21
    }
700
47
    options->args.checks.has_block_type = true;
701
47
  }
702
2.25k
  else if(0 == strcmp(opt, "except-block-type")) {
703
945
    arg = append_argument(options, ARG__EXCEPT_BLOCK_TYPE);
704
945
    FLAC__ASSERT(0 != option_argument);
705
945
    if(!parse_block_type(option_argument, &(arg->value.block_type))) {
706
825
      flac_fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
707
825
      ok = false;
708
825
    }
709
945
    options->args.checks.has_except_block_type = true;
710
945
  }
711
1.30k
  else if(0 == strcmp(opt, "data-format")) {
712
576
    arg = append_argument(options, ARG__DATA_FORMAT);
713
576
    FLAC__ASSERT(0 != option_argument);
714
576
    if(!parse_data_format(option_argument, &(arg->value.data_format))) {
715
192
      flac_fprintf(stderr, "ERROR (--%s): illegal data format \"%s\"\n", opt, option_argument);
716
192
      ok = false;
717
192
    }
718
576
    options->data_format_is_binary = arg->value.data_format.is_binary;
719
576
    options->data_format_is_binary_headerless = arg->value.data_format.is_headerless;
720
576
  }
721
729
  else if(0 == strcmp(opt, "application-data-format")) {
722
135
    FLAC__ASSERT(0 != option_argument);
723
135
    if(!parse_application_data_format(option_argument, &(options->application_data_format_is_hexdump))) {
724
99
      flac_fprintf(stderr, "ERROR (--%s): illegal application data format \"%s\"\n", opt, option_argument);
725
99
      ok = false;
726
99
    }
727
135
  }
728
594
  else if(0 == strcmp(opt, "from-file")) {
729
594
    arg = append_argument(options, ARG__FROM_FILE);
730
594
    FLAC__ASSERT(0 != option_argument);
731
594
    arg->value.from_file.file_name = local_strdup(option_argument);
732
594
  }
733
0
  else {
734
0
    FLAC__ASSERT(0);
735
0
  }
736
737
26.9k
  return ok;
738
26.9k
}
739
740
void append_new_operation(CommandLineOptions *options, Operation operation)
741
26.6k
{
742
26.6k
  if(options->ops.capacity == 0) {
743
12.2k
    options->ops.capacity = 50;
744
12.2k
    if(0 == (options->ops.operations = malloc(sizeof(Operation) * options->ops.capacity)))
745
0
      die("out of memory allocating space for option list");
746
12.2k
    memset(options->ops.operations, 0, sizeof(Operation) * options->ops.capacity);
747
12.2k
  }
748
26.6k
  if(options->ops.capacity <= options->ops.num_operations) {
749
2
    unsigned original_capacity = options->ops.capacity;
750
2
    if(options->ops.capacity > UINT32_MAX / 2) /* overflow check */
751
0
      die("out of memory allocating space for option list");
752
2
    options->ops.capacity *= 2;
753
2
    if(0 == (options->ops.operations = safe_realloc_mul_2op_(options->ops.operations, sizeof(Operation), /*times*/options->ops.capacity)))
754
0
      die("out of memory allocating space for option list");
755
2
    memset(options->ops.operations + original_capacity, 0, sizeof(Operation) * (options->ops.capacity - original_capacity));
756
2
  }
757
758
26.6k
  options->ops.operations[options->ops.num_operations++] = operation;
759
26.6k
}
760
761
void append_new_argument(CommandLineOptions *options, Argument argument)
762
2.74k
{
763
2.74k
  if(options->args.capacity == 0) {
764
2.30k
    options->args.capacity = 50;
765
2.30k
    if(0 == (options->args.arguments = malloc(sizeof(Argument) * options->args.capacity)))
766
0
      die("out of memory allocating space for option list");
767
2.30k
    memset(options->args.arguments, 0, sizeof(Argument) * options->args.capacity);
768
2.30k
  }
769
2.74k
  if(options->args.capacity <= options->args.num_arguments) {
770
0
    unsigned original_capacity = options->args.capacity;
771
0
    if(options->args.capacity > UINT32_MAX / 2) /* overflow check */
772
0
      die("out of memory allocating space for option list");
773
0
    options->args.capacity *= 2;
774
0
    if(0 == (options->args.arguments = safe_realloc_mul_2op_(options->args.arguments, sizeof(Argument), /*times*/options->args.capacity)))
775
0
      die("out of memory allocating space for option list");
776
0
    memset(options->args.arguments + original_capacity, 0, sizeof(Argument) * (options->args.capacity - original_capacity));
777
0
  }
778
779
2.74k
  options->args.arguments[options->args.num_arguments++] = argument;
780
2.74k
}
781
782
Operation *append_major_operation(CommandLineOptions *options, OperationType type)
783
2.65k
{
784
2.65k
  Operation op;
785
2.65k
  memset(&op, 0, sizeof(op));
786
2.65k
  op.type = type;
787
2.65k
  append_new_operation(options, op);
788
2.65k
  options->args.checks.num_major_ops++;
789
2.65k
  return options->ops.operations + (options->ops.num_operations - 1);
790
2.65k
}
791
792
Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type)
793
23.9k
{
794
23.9k
  Operation op;
795
23.9k
  memset(&op, 0, sizeof(op));
796
23.9k
  op.type = type;
797
23.9k
  append_new_operation(options, op);
798
23.9k
  options->args.checks.num_shorthand_ops++;
799
23.9k
  return options->ops.operations + (options->ops.num_operations - 1);
800
23.9k
}
801
802
Argument *find_argument(CommandLineOptions *options, ArgumentType type)
803
182
{
804
182
  unsigned i;
805
278
  for(i = 0; i < options->args.num_arguments; i++)
806
164
    if(options->args.arguments[i].type == type)
807
68
      return &options->args.arguments[i];
808
114
  return 0;
809
182
}
810
811
Operation *find_shorthand_operation(CommandLineOptions *options, OperationType type)
812
55.1k
{
813
55.1k
  unsigned i;
814
158k
  for(i = 0; i < options->ops.num_operations; i++)
815
106k
    if(options->ops.operations[i].type == type)
816
2.48k
      return &options->ops.operations[i];
817
52.6k
  return 0;
818
55.1k
}
819
820
Argument *append_argument(CommandLineOptions *options, ArgumentType type)
821
2.74k
{
822
2.74k
  Argument arg;
823
2.74k
  memset(&arg, 0, sizeof(arg));
824
2.74k
  arg.type = type;
825
2.74k
  append_new_argument(options, arg);
826
2.74k
  return options->args.arguments + (options->args.num_arguments - 1);
827
2.74k
}
828
829
FLAC__bool parse_md5(const char *src, FLAC__byte dest[16])
830
107
{
831
107
  unsigned i, d;
832
107
  int c;
833
107
  FLAC__ASSERT(0 != src);
834
107
  if(strlen(src) != 32)
835
34
    return false;
836
  /* strtoul() accepts negative numbers which we do not want, so we do it the hard way */
837
574
  for(i = 0; i < 16; i++) {
838
553
    c = (int)(*src++);
839
553
    if(isdigit(c))
840
343
      d = (unsigned)(c - '0');
841
210
    else if(c >= 'a' && c <= 'f')
842
101
      d = (unsigned)(c - 'a') + 10u;
843
109
    else if(c >= 'A' && c <= 'F')
844
77
      d = (unsigned)(c - 'A') + 10u;
845
32
    else
846
32
      return false;
847
521
    d <<= 4;
848
521
    c = (int)(*src++);
849
521
    if(isdigit(c))
850
336
      d |= (unsigned)(c - '0');
851
185
    else if(c >= 'a' && c <= 'f')
852
90
      d |= (unsigned)(c - 'a') + 10u;
853
95
    else if(c >= 'A' && c <= 'F')
854
75
      d |= (unsigned)(c - 'A') + 10u;
855
20
    else
856
20
      return false;
857
501
    dest[i] = (FLAC__byte)d;
858
501
  }
859
21
  return true;
860
73
}
861
862
FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest)
863
772
{
864
772
  FLAC__ASSERT(0 != src);
865
772
  if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
866
143
    return false;
867
629
  *dest = strtoul(src, 0, 10);
868
629
  return true;
869
772
}
870
871
FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest)
872
140
{
873
140
  FLAC__ASSERT(0 != src);
874
140
  if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
875
64
    return false;
876
76
  *dest = strtoull(src, 0, 10);
877
76
  return true;
878
140
}
879
880
FLAC__bool parse_string(const char *src, char **dest)
881
3.48k
{
882
3.48k
  if(0 == src || strlen(src) == 0)
883
140
    return false;
884
3.34k
  *dest = strdup(src);
885
3.34k
  return true;
886
3.48k
}
887
888
FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation)
889
457
{
890
457
  static const char * const violations[] = {
891
457
    "field name contains invalid character"
892
457
  };
893
894
457
  char *q, *s;
895
896
457
  s = local_strdup(field_ref);
897
898
1.81k
  for(q = s; *q; q++) {
899
1.44k
    if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
900
83
      free(s);
901
83
      *violation = violations[0];
902
83
      return false;
903
83
    }
904
1.44k
  }
905
906
374
  *name = s;
907
908
374
  return true;
909
457
}
910
911
FLAC__bool parse_vorbis_comment_field_names(const char *field_ref, char **names, const char **violation)
912
290
{
913
290
  static const char * const violations[] = {
914
290
    "field name contains invalid character"
915
290
  };
916
917
290
  char *q, *s;
918
919
290
  s = local_strdup(field_ref);
920
921
7.97k
  for(q = s; *q; q++) {
922
7.73k
    if(*q < 0x20 || *q > 0x7d) {
923
45
      free(s);
924
45
      *violation = violations[0];
925
45
      return false;
926
45
    }
927
7.73k
  }
928
929
245
  *names = s;
930
931
245
  return true;
932
290
}
933
934
FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation)
935
1.53k
{
936
1.53k
  static const char *garbled_ = "garbled specification";
937
1.53k
  const unsigned n = strlen(in);
938
939
1.53k
  FLAC__ASSERT(0 != in);
940
1.53k
  FLAC__ASSERT(0 != out);
941
942
1.53k
  if(n == 0) {
943
11
    *violation = "specification is empty";
944
11
    return false;
945
11
  }
946
947
1.52k
  if(n > strspn(in, "0123456789.Xsx")) {
948
55
    *violation = "specification contains invalid character";
949
55
    return false;
950
55
  }
951
952
1.46k
  if(in[n-1] == 'X') {
953
50
    if(n > 1) {
954
10
      *violation = garbled_;
955
10
      return false;
956
10
    }
957
50
  }
958
1.41k
  else if(in[n-1] == 's') {
959
536
    if(n-1 > strspn(in, "0123456789.")) {
960
12
      *violation = garbled_;
961
12
      return false;
962
12
    }
963
536
  }
964
883
  else if(in[n-1] == 'x') {
965
351
    if(n-1 > strspn(in, "0123456789")) {
966
35
      *violation = garbled_;
967
35
      return false;
968
35
    }
969
351
  }
970
532
  else {
971
532
    if(n > strspn(in, "0123456789")) {
972
19
      *violation = garbled_;
973
19
      return false;
974
19
    }
975
532
  }
976
977
1.39k
  *out = local_strdup(in);
978
1.39k
  return true;
979
1.46k
}
980
981
FLAC__bool parse_add_padding(const char *in, unsigned *out)
982
191
{
983
191
  FLAC__ASSERT(0 != in);
984
191
  FLAC__ASSERT(0 != out);
985
191
  *out = (unsigned)strtoul(in, 0, 10);
986
191
  return *out < (1u << FLAC__STREAM_METADATA_LENGTH_LEN);
987
191
}
988
989
FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out)
990
580
{
991
580
  char *p, *q, *s, *end;
992
580
  long i;
993
580
  unsigned entry;
994
995
580
  if(*in == '\0')
996
13
    return false;
997
998
567
  s = local_strdup(in);
999
1000
  /* first count the entries */
1001
2.74k
  for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
1002
2.17k
    ;
1003
1004
  /* make space */
1005
567
  FLAC__ASSERT(out->num_entries > 0);
1006
567
  if(0 == (out->entries = safe_malloc_mul_2op_(sizeof(unsigned), /*times*/out->num_entries)))
1007
0
    die("out of memory allocating space for option list");
1008
1009
  /* load 'em up */
1010
567
  entry = 0;
1011
567
  q = s;
1012
2.98k
  while(q) {
1013
2.51k
    FLAC__ASSERT(entry < out->num_entries);
1014
2.51k
    if(0 != (p = strchr(q, ',')))
1015
1.98k
      *p++ = '\0';
1016
2.51k
    if(!isdigit((int)(*q)) || (i = strtol(q, &end, 10)) < 0 || *end) {
1017
99
      free(s);
1018
99
      return false;
1019
99
    }
1020
2.41k
    out->entries[entry++] = (unsigned)i;
1021
2.41k
    q = p;
1022
2.41k
  }
1023
468
  FLAC__ASSERT(entry == out->num_entries);
1024
1025
468
  free(s);
1026
468
  return true;
1027
468
}
1028
1029
FLAC__bool parse_block_type(const char *in, Argument_BlockType *out)
1030
992
{
1031
992
  char *p, *q, *r, *s;
1032
992
  unsigned entry;
1033
1034
992
  if(*in == '\0')
1035
33
    return false;
1036
1037
959
  s = local_strdup(in);
1038
1039
  /* first count the entries */
1040
1.71k
  for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
1041
752
    ;
1042
1043
  /* make space */
1044
959
  FLAC__ASSERT(out->num_entries > 0);
1045
959
  if(0 == (out->entries = safe_malloc_mul_2op_(sizeof(Argument_BlockTypeEntry), /*times*/out->num_entries)))
1046
0
    die("out of memory allocating space for option list");
1047
1048
  /* load 'em up */
1049
959
  entry = 0;
1050
959
  q = s;
1051
1.52k
  while(q) {
1052
1.37k
    FLAC__ASSERT(entry < out->num_entries);
1053
1.37k
    if(0 != (p = strchr(q, ',')))
1054
475
      *p++ = 0;
1055
1.37k
    r = strchr(q, ':');
1056
1.37k
    if(r)
1057
283
      *r++ = '\0';
1058
1.37k
    if(0 != r && 0 != strcmp(q, "APPLICATION")) {
1059
120
      free(s);
1060
120
      return false;
1061
120
    }
1062
1.25k
    if(0 == strcmp(q, "STREAMINFO")) {
1063
21
      out->entries[entry++].type = FLAC__METADATA_TYPE_STREAMINFO;
1064
21
    }
1065
1.23k
    else if(0 == strcmp(q, "PADDING")) {
1066
81
      out->entries[entry++].type = FLAC__METADATA_TYPE_PADDING;
1067
81
    }
1068
1.15k
    else if(0 == strcmp(q, "APPLICATION")) {
1069
181
      out->entries[entry].type = FLAC__METADATA_TYPE_APPLICATION;
1070
181
      out->entries[entry].filter_application_by_id = (0 != r);
1071
181
      if(0 != r) {
1072
163
        if(strlen(r) == sizeof (out->entries[entry].application_id)) {
1073
73
          memcpy(out->entries[entry].application_id, r, sizeof (out->entries[entry].application_id));
1074
73
        }
1075
90
        else if(strlen(r) == 10 && FLAC__STRNCASECMP(r, "0x", 2) == 0 && strspn(r+2, "0123456789ABCDEFabcdef") == 8) {
1076
12
          FLAC__uint32 x = strtoul(r+2, 0, 16);
1077
12
          out->entries[entry].application_id[3] = (FLAC__byte)(x & 0xff);
1078
12
          out->entries[entry].application_id[2] = (FLAC__byte)((x>>=8) & 0xff);
1079
12
          out->entries[entry].application_id[1] = (FLAC__byte)((x>>=8) & 0xff);
1080
12
          out->entries[entry].application_id[0] = (FLAC__byte)((x>>=8) & 0xff);
1081
12
        }
1082
78
        else {
1083
78
          free(s);
1084
78
          return false;
1085
78
        }
1086
163
      }
1087
103
      entry++;
1088
103
    }
1089
975
    else if(0 == strcmp(q, "SEEKTABLE")) {
1090
34
      out->entries[entry++].type = FLAC__METADATA_TYPE_SEEKTABLE;
1091
34
    }
1092
941
    else if(0 == strcmp(q, "VORBIS_COMMENT")) {
1093
10
      out->entries[entry++].type = FLAC__METADATA_TYPE_VORBIS_COMMENT;
1094
10
    }
1095
931
    else if(0 == strcmp(q, "CUESHEET")) {
1096
245
      out->entries[entry++].type = FLAC__METADATA_TYPE_CUESHEET;
1097
245
    }
1098
686
    else if(0 == strcmp(q, "PICTURE")) {
1099
71
      out->entries[entry++].type = FLAC__METADATA_TYPE_PICTURE;
1100
71
    }
1101
615
    else {
1102
615
      free(s);
1103
615
      return false;
1104
615
    }
1105
565
    q = p;
1106
565
  }
1107
146
  FLAC__ASSERT(entry == out->num_entries);
1108
1109
146
  free(s);
1110
146
  return true;
1111
146
}
1112
1113
FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out)
1114
576
{
1115
576
  if(0 == strcmp(in, "binary-headerless")) {
1116
25
    out->is_binary = false;
1117
25
    out->is_headerless = true;
1118
25
  }
1119
551
  else if(0 == strcmp(in, "binary")) {
1120
349
    out->is_binary = true;
1121
349
    out->is_headerless = false;
1122
349
  }
1123
202
  else if(0 == strcmp(in, "text")) {
1124
10
    out->is_binary = false;
1125
10
    out->is_headerless = false;
1126
10
  }
1127
192
  else
1128
192
    return false;
1129
384
  return true;
1130
576
}
1131
1132
FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out)
1133
135
{
1134
135
  if(0 == strcmp(in, "hexdump"))
1135
18
    *out = true;
1136
117
  else if(0 == strcmp(in, "text"))
1137
18
    *out = false;
1138
99
  else
1139
99
    return false;
1140
36
  return true;
1141
135
}
1142
1143
void undocumented_warning(const char *opt)
1144
344
{
1145
344
  flac_fprintf(stderr, "WARNING: undocumented option --%s should be used with caution,\n         only for repairing a damaged STREAMINFO block\n", opt);
1146
344
}