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