FLAC__bool export_pic_to(const char *filename, const FLAC__StreamMetadata *picture, const char *pic_filename) { FILE *f; const FLAC__uint32 len = picture->data.picture.data_length; if(0 == pic_filename || strlen(pic_filename) == 0) { flac_fprintf(stderr, "%s: ERROR: empty export file name\n", filename); return false; } if(0 == strcmp(pic_filename, "-")) f = grabbag__file_get_binary_stdout(); else f = flac_fopen(pic_filename, "wb"); if(0 == f) { flac_fprintf(stderr, "%s: ERROR: can't open export file %s: %s\n", filename, pic_filename, strerror(errno)); return false; } if(fwrite(picture->data.picture.data, 1, len, f) != len) { flac_fprintf(stderr, "%s: ERROR: writing PICTURE data to file\n", filename); if(f != stdout) fclose(f); return false; } if(f != stdout) fclose(f); return true; }
FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options) { FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); FLAC__StreamMetadata *block; FLAC__bool ok = true; unsigned block_number; if(0 == iterator) die("out of memory allocating iterator"); FLAC__metadata_iterator_init(iterator, chain); block_number = 0; do { block = FLAC__metadata_iterator_get_block(iterator); ok &= (0 != block); if(!ok) flac_fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename); else if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) write_metadata(filename, block, block_number, !options->utf8_convert, options->application_data_format_is_hexdump); block_number++; } while(ok && FLAC__metadata_iterator_next(iterator)); FLAC__metadata_iterator_delete(iterator); return ok; }
void print_error_with_chain_status(FLAC__Metadata_Chain *chain, const char *format, ...) { const FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status(chain); va_list args; FLAC__ASSERT(0 != format); va_start(args, format); (void) flac_vfprintf(stderr, format, args); va_end(args); flac_fprintf(stderr, ", status = \"%s\"\n", FLAC__Metadata_ChainStatusString[status]); if(status == FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE) { flac_fprintf(stderr, "\n" "The FLAC file could not be opened. Most likely the file does not exist\n" "or is not readable.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE) { flac_fprintf(stderr, "\n" "The file does not appear to be a FLAC file.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE) { flac_fprintf(stderr, "\n" "The FLAC file does not have write permissions.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_BAD_METADATA) { flac_fprintf(stderr, "\n" "The metadata to be written does not conform to the FLAC metadata\n" "specifications.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_READ_ERROR) { flac_fprintf(stderr, "\n" "There was an error while reading the FLAC file.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR) { flac_fprintf(stderr, "\n" "There was an error while writing FLAC file; most probably the disk is\n" "full.\n" ); } else if(status == FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR) { flac_fprintf(stderr, "\n" "There was an error removing the temporary FLAC file.\n" ); } }
int short_usage(const char *message, ...) { va_list args; if(message) { va_start(args, message); (void) vfprintf(stderr, message, args); va_end(args); } usage_header(stderr); flac_fprintf(stderr, "\n"); flac_fprintf(stderr, "This is the short help; for full help use 'metaflac --help'\n"); flac_fprintf(stderr, "\n"); usage_summary(stderr); return message? 1 : 0; }
FLAC__bool export_cs_to(const char *filename, const FLAC__StreamMetadata *cuesheet, const char *cs_filename) { FILE *f; char *ref = 0; size_t reflen; if(0 == cs_filename || strlen(cs_filename) == 0) { flac_fprintf(stderr, "%s: ERROR: empty export file name\n", filename); return false; } if(0 == strcmp(cs_filename, "-")) f = stdout; else f = flac_fopen(cs_filename, "w"); if(0 == f) { flac_fprintf(stderr, "%s: ERROR: can't open export file %s: %s\n", filename, cs_filename, strerror(errno)); return false; } reflen = strlen(filename) + 7 + 1; if(0 == (ref = malloc(reflen))) { flac_fprintf(stderr, "%s: ERROR: allocating memory\n", filename); if(f != stdout) fclose(f); return false; } flac_snprintf(ref, reflen, "\"%s\" FLAC", filename); grabbag__cuesheet_emit(f, cuesheet, ref); free(ref); if(f != stdout) fclose(f); return true; }
void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f) { if(0 != entry->entry) { if(filename) flac_fprintf(f, "%s:", filename); if(!raw) { /* * WATCHOUT: comments that contain an embedded null will * be truncated by utf_decode(). */ #ifdef _WIN32 /* if we are outputting to console, we need to use proper print functions to show unicode characters */ if (f == stdout || f == stderr) { flac_fprintf(f, "%s", entry->entry); } else { #endif char *converted; if(utf8_decode((const char *)entry->entry, &converted) >= 0) { (void) local_fwrite(converted, 1, strlen(converted), f); free(converted); } else { (void) local_fwrite(entry->entry, 1, entry->length, f); } #ifdef _WIN32 } #endif } else { (void) local_fwrite(entry->entry, 1, entry->length, f); } } putc('\n', f); }
FLAC__bool import_pic_from(const char *filename, FLAC__StreamMetadata **picture, const char *specification, FLAC__bool *needs_write) { const char *error_message; if(0 == specification || strlen(specification) == 0) { flac_fprintf(stderr, "%s: ERROR: empty picture specification\n", filename); return false; } *picture = grabbag__picture_parse_specification(specification, &error_message); if(0 == *picture) { flac_fprintf(stderr, "%s: ERROR: while parsing picture specification \"%s\": %s\n", filename, specification, error_message); return false; } if(!FLAC__format_picture_is_legal(&(*picture)->data.picture, &error_message)) { flac_fprintf(stderr, "%s: ERROR: new PICTURE block for \"%s\" is illegal: %s\n", filename, specification, error_message); return false; } *needs_write = true; return true; }
FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options) { const char *opt = long_options_[option_index].name; Operation *op; Argument *arg; FLAC__bool ok = true; if(0 == strcmp(opt, "preserve-modtime")) { options->preserve_modtime = true; } else if(0 == strcmp(opt, "with-filename")) { options->prefix_with_filename = true; } else if(0 == strcmp(opt, "no-filename")) { options->prefix_with_filename = false; } else if(0 == strcmp(opt, "no-utf8-convert")) { options->utf8_convert = false; } else if(0 == strcmp(opt, "dont-use-padding")) { options->use_padding = false; } else if(0 == strcmp(opt, "no-cued-seekpoints")) { options->cued_seekpoints = false; } else if(0 == strcmp(opt, "show-md5sum")) { (void) append_shorthand_operation(options, OP__SHOW_MD5SUM); } else if(0 == strcmp(opt, "show-min-blocksize")) { (void) append_shorthand_operation(options, OP__SHOW_MIN_BLOCKSIZE); } else if(0 == strcmp(opt, "show-max-blocksize")) { (void) append_shorthand_operation(options, OP__SHOW_MAX_BLOCKSIZE); } else if(0 == strcmp(opt, "show-min-framesize")) { (void) append_shorthand_operation(options, OP__SHOW_MIN_FRAMESIZE); } else if(0 == strcmp(opt, "show-max-framesize")) { (void) append_shorthand_operation(options, OP__SHOW_MAX_FRAMESIZE); } else if(0 == strcmp(opt, "show-sample-rate")) { (void) append_shorthand_operation(options, OP__SHOW_SAMPLE_RATE); } else if(0 == strcmp(opt, "show-channels")) { (void) append_shorthand_operation(options, OP__SHOW_CHANNELS); } else if(0 == strcmp(opt, "show-bps")) { (void) append_shorthand_operation(options, OP__SHOW_BPS); } else if(0 == strcmp(opt, "show-total-samples")) { (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES); } else if(0 == strcmp(opt, "set-md5sum")) { op = append_shorthand_operation(options, OP__SET_MD5SUM); FLAC__ASSERT(0 != option_argument); if(!parse_md5(option_argument, op->argument.streaminfo_md5.value)) { flac_fprintf(stderr, "ERROR (--%s): bad MD5 sum\n", opt); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-min-blocksize")) { op = append_shorthand_operation(options, OP__SET_MIN_BLOCKSIZE); 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) { flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-max-blocksize")) { op = append_shorthand_operation(options, OP__SET_MAX_BLOCKSIZE); 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) { flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-min-framesize")) { op = append_shorthand_operation(options, OP__SET_MIN_FRAMESIZE); if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) { flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-max-framesize")) { op = append_shorthand_operation(options, OP__SET_MAX_FRAMESIZE); if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) { flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-sample-rate")) { op = append_shorthand_operation(options, OP__SET_SAMPLE_RATE); if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || !FLAC__format_sample_rate_is_valid(op->argument.streaminfo_uint32.value)) { flac_fprintf(stderr, "ERROR (--%s): invalid sample rate\n", opt); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-channels")) { op = append_shorthand_operation(options, OP__SET_CHANNELS); if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value > FLAC__MAX_CHANNELS) { flac_fprintf(stderr, "ERROR (--%s): value must be > 0 and <= %u\n", opt, FLAC__MAX_CHANNELS); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-bps")) { op = append_shorthand_operation(options, OP__SET_BPS); 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) { flac_fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "set-total-samples")) { op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES); if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (((FLAC__uint64)1)<<FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) { flac_fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN); ok = false; } else undocumented_warning(opt); } else if(0 == strcmp(opt, "show-vendor-tag")) { (void) append_shorthand_operation(options, OP__SHOW_VC_VENDOR); } else if(0 == strcmp(opt, "show-tag")) { const char *violation; op = append_shorthand_operation(options, OP__SHOW_VC_FIELD); FLAC__ASSERT(0 != option_argument); if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "remove-all-tags")) { (void) append_shorthand_operation(options, OP__REMOVE_VC_ALL); } else if(0 == strcmp(opt, "remove-tag")) { const char *violation; op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD); FLAC__ASSERT(0 != option_argument); if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "remove-first-tag")) { const char *violation; op = append_shorthand_operation(options, OP__REMOVE_VC_FIRSTFIELD); FLAC__ASSERT(0 != option_argument); if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "set-tag")) { const char *violation; op = append_shorthand_operation(options, OP__SET_VC_FIELD); FLAC__ASSERT(0 != option_argument); op->argument.vc_field.field_value_from_file = false; 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)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "set-tag-from-file")) { const char *violation; op = append_shorthand_operation(options, OP__SET_VC_FIELD); FLAC__ASSERT(0 != option_argument); op->argument.vc_field.field_value_from_file = true; 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)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "import-tags-from")) { op = append_shorthand_operation(options, OP__IMPORT_VC_FROM); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.filename.value))) { flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } } else if(0 == strcmp(opt, "export-tags-to")) { op = append_shorthand_operation(options, OP__EXPORT_VC_TO); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.filename.value))) { flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } } else if(0 == strcmp(opt, "import-cuesheet-from")) { if(0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM)) { flac_fprintf(stderr, "ERROR (--%s): may be specified only once\n", opt); ok = false; } op = append_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.import_cuesheet_from.filename))) { flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } } else if(0 == strcmp(opt, "export-cuesheet-to")) { op = append_shorthand_operation(options, OP__EXPORT_CUESHEET_TO); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.filename.value))) { flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } } else if(0 == strcmp(opt, "import-picture-from")) { op = append_shorthand_operation(options, OP__IMPORT_PICTURE_FROM); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.specification.value))) { flac_fprintf(stderr, "ERROR (--%s): missing specification\n", opt); ok = false; } } else if(0 == strcmp(opt, "export-picture-to")) { arg = find_argument(options, ARG__BLOCK_NUMBER); op = append_shorthand_operation(options, OP__EXPORT_PICTURE_TO); FLAC__ASSERT(0 != option_argument); if(!parse_string(option_argument, &(op->argument.export_picture_to.filename))) { flac_fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } op->argument.export_picture_to.block_number_link = arg? &(arg->value.block_number) : 0; } else if(0 == strcmp(opt, "add-seekpoint")) { const char *violation; char *spec; FLAC__ASSERT(0 != option_argument); if(!parse_add_seekpoint(option_argument, &spec, &violation)) { FLAC__ASSERT(0 != violation); flac_fprintf(stderr, "ERROR (--%s): malformed seekpoint specification \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } else { op = find_shorthand_operation(options, OP__ADD_SEEKPOINT); if(0 == op) op = append_shorthand_operation(options, OP__ADD_SEEKPOINT); local_strcat(&(op->argument.add_seekpoint.specification), spec); local_strcat(&(op->argument.add_seekpoint.specification), ";"); free(spec); } } else if(0 == strcmp(opt, "add-replay-gain")) { (void) append_shorthand_operation(options, OP__ADD_REPLAY_GAIN); } else if(0 == strcmp(opt, "remove-replay-gain")) { const FLAC__byte * const tags[5] = { GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS, GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN, GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK, GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN, GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK }; size_t i; for(i = 0; i < sizeof(tags)/sizeof(tags[0]); i++) { op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD); op->argument.vc_field_name.value = local_strdup((const char *)tags[i]); } } else if(0 == strcmp(opt, "add-padding")) { op = append_shorthand_operation(options, OP__ADD_PADDING); FLAC__ASSERT(0 != option_argument); if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) { flac_fprintf(stderr, "ERROR (--%s): illegal length \"%s\", length must be >= 0 and < 2^%u\n", opt, option_argument, FLAC__STREAM_METADATA_LENGTH_LEN); ok = false; } } else if(0 == strcmp(opt, "help")) { options->show_long_help = true; } else if(0 == strcmp(opt, "version")) { options->show_version = true; } else if(0 == strcmp(opt, "list")) { (void) append_major_operation(options, OP__LIST); } else if(0 == strcmp(opt, "append")) { (void) append_major_operation(options, OP__APPEND); } else if(0 == strcmp(opt, "remove")) { (void) append_major_operation(options, OP__REMOVE); } else if(0 == strcmp(opt, "remove-all")) { (void) append_major_operation(options, OP__REMOVE_ALL); } else if(0 == strcmp(opt, "merge-padding")) { (void) append_major_operation(options, OP__MERGE_PADDING); } else if(0 == strcmp(opt, "sort-padding")) { (void) append_major_operation(options, OP__SORT_PADDING); } else if(0 == strcmp(opt, "block-number")) { arg = append_argument(options, ARG__BLOCK_NUMBER); FLAC__ASSERT(0 != option_argument); if(!parse_block_number(option_argument, &(arg->value.block_number))) { flac_fprintf(stderr, "ERROR: malformed block number specification \"%s\"\n", option_argument); ok = false; } } else if(0 == strcmp(opt, "block-type")) { arg = append_argument(options, ARG__BLOCK_TYPE); FLAC__ASSERT(0 != option_argument); if(!parse_block_type(option_argument, &(arg->value.block_type))) { flac_fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument); ok = false; } options->args.checks.has_block_type = true; } else if(0 == strcmp(opt, "except-block-type")) { arg = append_argument(options, ARG__EXCEPT_BLOCK_TYPE); FLAC__ASSERT(0 != option_argument); if(!parse_block_type(option_argument, &(arg->value.block_type))) { flac_fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument); ok = false; } options->args.checks.has_except_block_type = true; } else if(0 == strcmp(opt, "data-format")) { arg = append_argument(options, ARG__DATA_FORMAT); FLAC__ASSERT(0 != option_argument); if(!parse_data_format(option_argument, &(arg->value.data_format))) { flac_fprintf(stderr, "ERROR (--%s): illegal data format \"%s\"\n", opt, option_argument); ok = false; } } else if(0 == strcmp(opt, "application-data-format")) { FLAC__ASSERT(0 != option_argument); if(!parse_application_data_format(option_argument, &(options->application_data_format_is_hexdump))) { flac_fprintf(stderr, "ERROR (--%s): illegal application data format \"%s\"\n", opt, option_argument); ok = false; } } else if(0 == strcmp(opt, "from-file")) { arg = append_argument(options, ARG__FROM_FILE); FLAC__ASSERT(0 != option_argument); arg->value.from_file.file_name = local_strdup(option_argument); } else { FLAC__ASSERT(0); } return ok; }
FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options) { int ret; int option_index = 1; FLAC__bool had_error = false; while ((ret = share__getopt_long(argc, argv, "", long_options_, &option_index)) != -1) { switch (ret) { case 0: had_error |= !parse_option(option_index, share__optarg, options); break; case '?': case ':': had_error = true; break; default: FLAC__ASSERT(0); break; } } if(options->prefix_with_filename == 2) options->prefix_with_filename = (argc - share__optind > 1); if(share__optind >= argc && !options->show_long_help && !options->show_version) { flac_fprintf(stderr,"ERROR: you must specify at least one FLAC file;\n"); flac_fprintf(stderr," metaflac cannot be used as a pipe\n"); had_error = true; } options->num_files = argc - share__optind; if(options->num_files > 0) { unsigned i = 0; if(0 == (options->filenames = safe_malloc_mul_2op_(sizeof(char*), /*times*/options->num_files))) die("out of memory allocating space for file names list"); while(share__optind < argc) options->filenames[i++] = local_strdup(argv[share__optind++]); } if(options->args.checks.num_major_ops > 0) { if(options->args.checks.num_major_ops > 1) { flac_fprintf(stderr, "ERROR: you may only specify one major operation at a time\n"); had_error = true; } else if(options->args.checks.num_shorthand_ops > 0) { flac_fprintf(stderr, "ERROR: you may not mix shorthand and major operations\n"); had_error = true; } } /* check for only one FLAC file used with certain options */ if(options->num_files > 1) { if(0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM)) { flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--import-cuesheet-from'\n"); had_error = true; } if(0 != find_shorthand_operation(options, OP__EXPORT_CUESHEET_TO)) { flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--export-cuesheet-to'\n"); had_error = true; } if(0 != find_shorthand_operation(options, OP__EXPORT_PICTURE_TO)) { flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--export-picture-to'\n"); had_error = true; } if( 0 != find_shorthand_operation(options, OP__IMPORT_VC_FROM) && 0 == strcmp(find_shorthand_operation(options, OP__IMPORT_VC_FROM)->argument.filename.value, "-") ) { flac_fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--import-tags-from=-'\n"); had_error = true; } } if(options->args.checks.has_block_type && options->args.checks.has_except_block_type) { flac_fprintf(stderr, "ERROR: you may not specify both '--block-type' and '--except-block-type'\n"); had_error = true; } if(had_error) short_usage(0); /* * We need to create an OP__ADD_SEEKPOINT operation if there is * not one already, and --import-cuesheet-from was specified but * --no-cued-seekpoints was not: */ if(options->cued_seekpoints) { Operation *op = find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM); if(0 != op) { Operation *op2 = find_shorthand_operation(options, OP__ADD_SEEKPOINT); if(0 == op2) op2 = append_shorthand_operation(options, OP__ADD_SEEKPOINT); op->argument.import_cuesheet_from.add_seekpoint_link = &(op2->argument.add_seekpoint); } } return had_error; }
void die(const char *message) { FLAC__ASSERT(0 != message); flac_fprintf(stderr, "ERROR: %s\n", message); exit(1); }
FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write) { FLAC__bool ok = true; FLAC__StreamMetadata *cuesheet = 0; FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); FLAC__uint64 lead_out_offset = 0; FLAC__bool is_cdda = false; unsigned sample_rate = 0; if(0 == iterator) die("out of memory allocating iterator"); FLAC__metadata_iterator_init(iterator, chain); do { FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator); if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { lead_out_offset = block->data.stream_info.total_samples; if(lead_out_offset == 0) { flac_fprintf(stderr, "%s: ERROR: FLAC file must have total_samples set in STREAMINFO in order to import/export cuesheet\n", filename); FLAC__metadata_iterator_delete(iterator); return false; } sample_rate = block->data.stream_info.sample_rate; is_cdda = (block->data.stream_info.channels == 1 || block->data.stream_info.channels == 2) && (block->data.stream_info.bits_per_sample == 16) && (sample_rate == 44100); } else if(block->type == FLAC__METADATA_TYPE_CUESHEET) cuesheet = block; } while(FLAC__metadata_iterator_next(iterator)); if(lead_out_offset == 0) { flac_fprintf(stderr, "%s: ERROR: FLAC stream has no STREAMINFO block\n", filename); FLAC__metadata_iterator_delete(iterator); return false; } switch(operation->type) { case OP__IMPORT_CUESHEET_FROM: if(0 != cuesheet) { flac_fprintf(stderr, "%s: ERROR: FLAC file already has CUESHEET block\n", filename); ok = false; } else { ok = import_cs_from(filename, &cuesheet, operation->argument.import_cuesheet_from.filename, needs_write, lead_out_offset, sample_rate, is_cdda, operation->argument.import_cuesheet_from.add_seekpoint_link); if(ok) { /* append CUESHEET block */ while(FLAC__metadata_iterator_next(iterator)) ; if(!FLAC__metadata_iterator_insert_block_after(iterator, cuesheet)) { print_error_with_chain_status(chain, "%s: ERROR: adding new CUESHEET block to metadata", filename); FLAC__metadata_object_delete(cuesheet); ok = false; } } } break; case OP__EXPORT_CUESHEET_TO: if(0 == cuesheet) { flac_fprintf(stderr, "%s: ERROR: FLAC file has no CUESHEET block\n", filename); ok = false; } else ok = export_cs_to(filename, cuesheet, operation->argument.filename.value); break; default: ok = false; FLAC__ASSERT(0); break; }; FLAC__metadata_iterator_delete(iterator); return ok; }
FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, unsigned sample_rate, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link) { FILE *f; const char *error_message; char **seekpoint_specification = add_seekpoint_link? &(add_seekpoint_link->specification) : 0; unsigned last_line_read; if(0 == cs_filename || strlen(cs_filename) == 0) { flac_fprintf(stderr, "%s: ERROR: empty import file name\n", filename); return false; } if(0 == strcmp(cs_filename, "-")) f = stdin; else f = flac_fopen(cs_filename, "r"); if(0 == f) { flac_fprintf(stderr, "%s: ERROR: can't open import file %s: %s\n", filename, cs_filename, strerror(errno)); return false; } *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset); if(f != stdin) fclose(f); if(0 == *cuesheet) { flac_fprintf(stderr, "%s: ERROR: while parsing cuesheet \"%s\" on line %u: %s\n", filename, cs_filename, last_line_read, error_message); return false; } if(!FLAC__format_cuesheet_is_legal(&(*cuesheet)->data.cue_sheet, /*check_cd_da_subset=*/false, &error_message)) { flac_fprintf(stderr, "%s: ERROR parsing cuesheet \"%s\": %s\n", filename, cs_filename, error_message); return false; } /* if we're expecting CDDA, warn about non-compliance */ if(is_cdda && !FLAC__format_cuesheet_is_legal(&(*cuesheet)->data.cue_sheet, /*check_cd_da_subset=*/true, &error_message)) { flac_fprintf(stderr, "%s: WARNING cuesheet \"%s\" is not audio CD compliant: %s\n", filename, cs_filename, error_message); (*cuesheet)->data.cue_sheet.is_cd = false; } /* add seekpoints for each index point if required */ if(0 != seekpoint_specification) { char spec[128]; unsigned track, indx; const FLAC__StreamMetadata_CueSheet *cs = &(*cuesheet)->data.cue_sheet; if(0 == *seekpoint_specification) *seekpoint_specification = local_strdup(""); for(track = 0; track < cs->num_tracks; track++) { const FLAC__StreamMetadata_CueSheet_Track *tr = cs->tracks+track; for(indx = 0; indx < tr->num_indices; indx++) { flac_snprintf(spec, sizeof (spec), "%" PRIu64 ";", (tr->offset + tr->indices[indx].offset)); local_strcat(seekpoint_specification, spec); } } } *needs_write = true; return true; }
FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write) { FLAC__bool ok = true, has_type1 = false, has_type2 = false; FLAC__StreamMetadata *picture = 0; FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); if(0 == iterator) die("out of memory allocating iterator"); FLAC__metadata_iterator_init(iterator, chain); switch(operation->type) { case OP__IMPORT_PICTURE_FROM: ok = import_pic_from(filename, &picture, operation->argument.specification.value, needs_write); if(ok) { /* append PICTURE block */ while(FLAC__metadata_iterator_next(iterator)) ; if(!FLAC__metadata_iterator_insert_block_after(iterator, picture)) { print_error_with_chain_status(chain, "%s: ERROR: adding new PICTURE block to metadata", filename); FLAC__metadata_object_delete(picture); ok = false; } } if(ok) { /* check global PICTURE constraints (max 1 block each of type=1 and type=2) */ while(FLAC__metadata_iterator_prev(iterator)) ; do { FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator); if(block->type == FLAC__METADATA_TYPE_PICTURE) { if(block->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD) { if(has_type1) { print_error_with_chain_status(chain, "%s: ERROR: FLAC stream can only have one 32x32 standard icon (type=1) PICTURE block", filename); ok = false; } has_type1 = true; } else if(block->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON) { if(has_type2) { print_error_with_chain_status(chain, "%s: ERROR: FLAC stream can only have one icon (type=2) PICTURE block", filename); ok = false; } has_type2 = true; } } } while(FLAC__metadata_iterator_next(iterator)); } break; case OP__EXPORT_PICTURE_TO: { const Argument_BlockNumber *a = operation->argument.export_picture_to.block_number_link; int block_number = (a && a->num_entries > 0)? (int)a->entries[0] : -1; unsigned i = 0; do { FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator); if(block->type == FLAC__METADATA_TYPE_PICTURE && (block_number < 0 || i == (unsigned)block_number)) picture = block; i++; } while(FLAC__metadata_iterator_next(iterator) && 0 == picture); if(0 == picture) { if(block_number < 0) flac_fprintf(stderr, "%s: ERROR: FLAC file has no PICTURE block\n", filename); else flac_fprintf(stderr, "%s: ERROR: FLAC file has no PICTURE block at block #%d\n", filename, block_number); ok = false; } else ok = export_pic_to(filename, picture, operation->argument.filename.value); } break; default: ok = false; FLAC__ASSERT(0); break; }; FLAC__metadata_iterator_delete(iterator); return ok; }
FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime, FLAC__bool scan) { FLAC__StreamMetadata streaminfo; float *title_gains = 0, *title_peaks = 0; float album_gain, album_peak; unsigned sample_rate = 0; unsigned bits_per_sample = 0; unsigned channels = 0; unsigned i; const char *error; FLAC__bool first = true; FLAC__ASSERT(num_files > 0); for(i = 0; i < num_files; i++) { FLAC__ASSERT(0 != filenames[i]); if(!FLAC__metadata_get_streaminfo(filenames[i], &streaminfo)) { flac_fprintf(stderr, "%s: ERROR: can't open file or get STREAMINFO block\n", filenames[i]); return false; } if(first) { first = false; sample_rate = streaminfo.data.stream_info.sample_rate; bits_per_sample = streaminfo.data.stream_info.bits_per_sample; channels = streaminfo.data.stream_info.channels; } else { if(sample_rate != streaminfo.data.stream_info.sample_rate) { flac_fprintf(stderr, "%s: ERROR: sample rate of %u Hz does not match previous files' %u Hz\n", filenames[i], streaminfo.data.stream_info.sample_rate, sample_rate); return false; } if(bits_per_sample != streaminfo.data.stream_info.bits_per_sample) { flac_fprintf(stderr, "%s: ERROR: resolution of %u bps does not match previous files' %u bps\n", filenames[i], streaminfo.data.stream_info.bits_per_sample, bits_per_sample); return false; } if(channels != streaminfo.data.stream_info.channels) { flac_fprintf(stderr, "%s: ERROR: # channels (%u) does not match previous files' (%u)\n", filenames[i], streaminfo.data.stream_info.channels, channels); return false; } } if(!grabbag__replaygain_is_valid_sample_frequency(sample_rate)) { flac_fprintf(stderr, "%s: ERROR: sample rate of %u Hz is not supported\n", filenames[i], sample_rate); return false; } if(channels != 1 && channels != 2) { flac_fprintf(stderr, "%s: ERROR: # of channels (%u) is not supported, must be 1 or 2\n", filenames[i], channels); return false; } } FLAC__ASSERT(bits_per_sample >= FLAC__MIN_BITS_PER_SAMPLE && bits_per_sample <= FLAC__MAX_BITS_PER_SAMPLE); if(!grabbag__replaygain_init(sample_rate)) { FLAC__ASSERT(0); /* double protection */ flac_fprintf(stderr, "internal error\n"); return false; } if( 0 == (title_gains = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files)) || 0 == (title_peaks = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files)) ) die("out of memory allocating space for title gains/peaks"); for(i = 0; i < num_files; i++) { if(0 != (error = grabbag__replaygain_analyze_file(filenames[i], title_gains+i, title_peaks+i))) { flac_fprintf(stderr, "%s: ERROR: during analysis (%s)\n", filenames[i], error); free(title_gains); free(title_peaks); return false; } } grabbag__replaygain_get_album(&album_gain, &album_peak); for(i = 0; i < num_files; i++) { if(!scan) { if(0 != (error = grabbag__replaygain_store_to_file(filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i], preserve_modtime))) { flac_fprintf(stderr, "%s: ERROR: writing tags (%s)\n", filenames[i], error); free(title_gains); free(title_peaks); return false; } } else { flac_fprintf(stdout, "%s: %f %f %f %f\n", filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i]); } } free(title_gains); free(title_peaks); return true; }
FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options) { (void) chain, (void) options; flac_fprintf(stderr, "ERROR: --append not implemented yet\n"); return false; }
int main(int argc, char *argv[]) { FILE *f; char buf[36]; foreign_metadata_t *fm; const char *fn, *error; size_t i; FLAC__uint32 size; #ifdef _WIN32 if (get_utf8_argv(&argc, &argv) != 0) { fprintf(stderr, "ERROR: failed to convert command line parameters to UTF-8\n"); return 1; } #endif if(argc != 2) { flac_fprintf(stderr, "usage: %s { file.wav | file.aif }\n", argv[0]); return 1; } fn = argv[1]; if(0 == (f = flac_fopen(fn, "rb")) || fread(buf, 1, 4, f) != 4) { flac_fprintf(stderr, "ERROR opening %s for reading\n", fn); return 1; } fclose(f); if(0 == (fm = flac__foreign_metadata_new(memcmp(buf, "RIFF", 4) && memcmp(buf, "RF64", 4)? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) { flac_fprintf(stderr, "ERROR: out of memory\n"); return 1; } if(fm->type == FOREIGN_BLOCK_TYPE__AIFF) { if(!flac__foreign_metadata_read_from_aiff(fm, fn, &error)) { flac_fprintf(stderr, "ERROR reading chunks from %s: %s\n", fn, error); return 1; } } else { if(!flac__foreign_metadata_read_from_wave(fm, fn, &error)) { flac_fprintf(stderr, "ERROR reading chunks from %s: %s\n", fn, error); return 1; } } if(0 == (f = flac_fopen(fn, "rb"))) { flac_fprintf(stderr, "ERROR opening %s for reading\n", fn); return 1; } for(i = 0; i < fm->num_blocks; i++) { if(fseeko(f, fm->blocks[i].offset, SEEK_SET) < 0) { flac_fprintf(stderr, "ERROR seeking in %s\n", fn); return 1; } if(fread(buf, 1, i==0?12:8, f) != (i==0?12:8)) { flac_fprintf(stderr, "ERROR reading %s\n", fn); return 1; } size = unpack32_((FLAC__byte*)buf+4, fm->type); printf("block:[%c%c%c%c] size=%08x=(%10u)", buf[0], buf[1], buf[2], buf[3], size, size); if(i == 0) printf(" type:[%c%c%c%c]", buf[8], buf[9], buf[10], buf[11]); else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF && i == fm->audio_block) printf(" offset size=%08x=(%10u)", fm->ssnd_offset_size, fm->ssnd_offset_size); else if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && i == 1 && !memcmp(buf, "ds64", 4)) { if(fread(buf+8, 1, 36-8, f) != 36-8) { flac_fprintf(stderr, "ERROR reading %s\n", fn); return 1; } printf("\n RIFF size=%016" PRIx64 "=(%20" PRIu64 ")", unpack64le_((FLAC__byte*)buf+8), unpack64le_((FLAC__byte*)buf+8)); printf("\n data size=%016" PRIx64 "=(%20" PRIu64 ")", unpack64le_((FLAC__byte*)buf+16), unpack64le_((FLAC__byte*)buf+16)); printf("\n sample count=%016" PRIx64 "=(%20" PRIu64 ")", unpack64le_((FLAC__byte*)buf+24), unpack64le_((FLAC__byte*)buf+24)); printf("\n table size=%08x=(%10u)", unpack32le_((FLAC__byte*)buf+32), unpack32le_((FLAC__byte*)buf+32)); } printf("\n"); } fclose(f); flac__foreign_metadata_delete(fm); return 0; }