options.c
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:35k
源码类别:

Windows CE

开发平台:

C/C++

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