/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2011 Rocky Bernstein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Program to debug read routines audio, auto, mode1, mode2 forms 1 & 2. */ #include "util.h" #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "getopt.h" #ifndef O_BINARY #define O_BINARY 0 #endif /* Configuration option codes */ enum { OP_HANDLED = 0, /* NOTE: libpopt version associated these with drivers. That appeared to be an unused historical artifact. */ OP_SOURCE_AUTO, OP_SOURCE_BIN, OP_SOURCE_CUE, OP_SOURCE_NRG, OP_SOURCE_CDRDAO, OP_SOURCE_DEVICE, OP_USAGE, /* These are the remaining configuration options */ OP_READ_MODE, OP_VERSION, }; typedef enum { READ_AUDIO = CDIO_READ_MODE_AUDIO, READ_M1F1 = CDIO_READ_MODE_M1F1, READ_M1F2 = CDIO_READ_MODE_M1F2, READ_M2F1 = CDIO_READ_MODE_M2F1, READ_M2F2 = CDIO_READ_MODE_M2F2, READ_MODE_UNINIT, READ_ANY } read_mode_t; /* Structure used so we can binary sort and set the --mode switch. */ typedef struct { char name[30]; read_mode_t read_mode; } subopt_entry_t; /* Sub-options for --mode. Note: entries must be sorted! */ static const subopt_entry_t modes_sublist[] = { {"any", READ_ANY}, {"audio", READ_AUDIO}, {"m1f1", READ_M1F1}, {"m1f2", READ_M1F2}, {"m2f1", READ_M2F1}, {"m2f2", READ_M2F2}, {"mode1form1", READ_M1F1}, {"mode1form2", READ_M1F2}, {"mode2form1", READ_M2F1}, {"mode2form2", READ_M2F2}, {"red", READ_AUDIO}, }; /* Used by `main' to communicate with `parse_opt'. And global options */ static struct arguments { char *access_mode; /* Access method driver should use for control */ char *output_file; /* file to output blocks if not NULL. */ int debug_level; int hexdump; /* Show output as a hexdump */ int nohexdump; /* Don't output as a hexdump. I don't know how to get popt to combine these as one variable. */ int just_hex; /* Don't try to print "printable" characters in hex dump. */ read_mode_t read_mode; int version_only; int no_header; int print_iso9660; source_image_t source_image; lsn_t start_lsn; lsn_t end_lsn; int num_sectors; } opts; static void hexdump (FILE *stream, uint8_t * buffer, unsigned int len, int just_hex) { unsigned int i; for (i = 0; i < len; i++, buffer++) { if (i % 16 == 0) fprintf (stream, "0x%04x: ", i); fprintf (stream, "%02x", *buffer); if (i % 2 == 1) fprintf (stream, " "); if (i % 16 == 15) { if (!just_hex) { uint8_t *p; fprintf (stream, " "); for (p=buffer-15; p <= buffer; p++) { fprintf(stream, "%c", isprint(*p) ? *p : '.'); } } fprintf (stream, "\n"); } } fprintf (stream, "\n"); fflush (stream); } /* Comparison function called by bearch() to find sub-option record. */ static int compare_subopts(const void *key1, const void *key2) { subopt_entry_t *a = (subopt_entry_t *) key1; subopt_entry_t *b = (subopt_entry_t *) key2; return (strncmp(a->name, b->name, 30)); } /* Do processing of a --mode sub option. Basically we find the option in the array, set it's corresponding flag variable to true as well as the "show.all" false. */ static void process_suboption(const char *subopt, const subopt_entry_t *sublist, const int num, const char *subopt_name) { subopt_entry_t *subopt_rec = bsearch(subopt, sublist, num, sizeof(subopt_entry_t), &compare_subopts); if (subopt_rec != NULL) { opts.read_mode = subopt_rec->read_mode; return; } else { unsigned int i; bool is_help=strcmp(subopt, "help")==0; if (is_help) { report( stderr, "The list of sub options for \"%s\" are:\n", subopt_name ); } else { report( stderr, "Invalid option following \"%s\": %s.\n", subopt_name, subopt ); report( stderr, "Should be one of: " ); } for (i=0; i= 0) switch (opt) { case 'a': opts.access_mode = strdup(optarg); break; case 'd': opts.debug_level = atoi(optarg); break; case 'x': opts.hexdump = 1; break; case 's': opts.start_lsn = atoi(optarg); break; case 'e': opts.end_lsn = atoi(optarg); break; case 'n': opts.num_sectors = atoi(optarg); break; case 'b': parse_source(OP_SOURCE_BIN); break; case 'c': parse_source(OP_SOURCE_CUE); break; case 'i': parse_source(OP_SOURCE_AUTO); break; case 'C': parse_source(OP_SOURCE_DEVICE); break; case 'N': parse_source(OP_SOURCE_NRG); break; case 't': parse_source(OP_SOURCE_CDRDAO); break; case 'o': opts.output_file = strdup(optarg); break; case 'm': process_suboption(optarg, modes_sublist, sizeof(modes_sublist) / sizeof(subopt_entry_t), "--mode"); break; case 'V': print_version(program_name, VERSION, 0, true); rc = EXIT_SUCCESS; goto error_exit; case '?': fprintf(stdout, helpText, program_name); rc = EXIT_INFO; goto error_exit; case OP_USAGE: fprintf(stderr, usageText, program_name); goto error_exit; case OP_HANDLED: break; } if (optind < argc) { const char *remaining_arg = argv[optind++]; /* NOTE: A bug in the libpopt version checked source_image, which rendered the subsequent source_image test useless. */ if (source_name != NULL) { report( stderr, "%s: Source specified in option %s and as %s\n", program_name, source_name, remaining_arg ); goto error_exit; } if (opts.source_image == INPUT_DEVICE) source_name = fillout_device_name(remaining_arg); else source_name = strdup(remaining_arg); if (optind < argc) { report( stderr, "%s: Source specified in previously %s and %s\n", program_name, source_name, remaining_arg ); goto error_exit; } } if (opts.debug_level == 3) { cdio_loglevel_default = CDIO_LOG_INFO; } else if (opts.debug_level >= 4) { cdio_loglevel_default = CDIO_LOG_DEBUG; } if (opts.read_mode == READ_MODE_UNINIT) { report( stderr, "%s: Need to give a read mode " "(audio, m1f1, m1f2, m2f1, m2f2, or auto)\n", program_name ); rc = 10; goto error_exit; } /* Check consistency between start_lsn, end_lsn and num_sectors. */ if (opts.nohexdump && opts.hexdump != 2) { report( stderr, "%s: don't give both --hexdump and --no-hexdump together\n", program_name ); rc = 11; goto error_exit; } if (opts.nohexdump) opts.hexdump = 0; if (opts.start_lsn == CDIO_INVALID_LSN) { /* Maybe we derive the start from the end and num sectors. */ if (opts.end_lsn == CDIO_INVALID_LSN) { /* No start or end LSN, so use 0 for the start */ opts.start_lsn = 0; if (opts.num_sectors == 0) opts.num_sectors = 1; } else if (opts.num_sectors != 0) { if (opts.end_lsn <= opts.num_sectors) { report( stderr, "%s: end LSN (%lu) needs to be greater than " " the sector to read (%lu)\n", program_name, (unsigned long) opts.end_lsn, (unsigned long) opts.num_sectors ); rc = 12; goto error_exit; } opts.start_lsn = opts.end_lsn - opts.num_sectors + 1; } } /* opts.start_lsn has been set somehow or we've aborted. */ if (opts.end_lsn == CDIO_INVALID_LSN) { if (0 == opts.num_sectors) opts.num_sectors = 1; opts.end_lsn = opts.start_lsn + opts.num_sectors - 1; } else { /* We were given an end lsn. */ if (opts.end_lsn < opts.start_lsn) { report( stderr, "%s: end LSN (%lu) needs to be grater than start LSN (%lu)\n", program_name, (unsigned long) opts.start_lsn, (unsigned long) opts.end_lsn ); rc = 13; goto error_exit; } if (opts.num_sectors != opts.end_lsn - opts.start_lsn + 1) if (opts.num_sectors != 0) { report( stderr, "%s: inconsistency between start LSN (%lu), end (%lu), " "and count (%d)\n", program_name, (unsigned long) opts.start_lsn, (unsigned long) opts.end_lsn, opts.num_sectors ); rc = 14; goto error_exit; } opts.num_sectors = opts.end_lsn - opts.start_lsn + 1; } return true; error_exit: free(program_name); exit(rc); } static void log_handler (cdio_log_level_t level, const char message[]) { if (level == CDIO_LOG_DEBUG && opts.debug_level < 2) return; if (level == CDIO_LOG_INFO && opts.debug_level < 1) return; if (level == CDIO_LOG_WARN && opts.debug_level < 0) return; gl_default_cdio_log_handler (level, message); } static void init(void) { opts.debug_level = 0; opts.start_lsn = CDIO_INVALID_LSN; opts.end_lsn = CDIO_INVALID_LSN; opts.num_sectors = 0; opts.read_mode = READ_MODE_UNINIT; opts.source_image = INPUT_UNKNOWN; opts.hexdump = 2; /* Not set. */ gl_default_cdio_log_handler = cdio_log_set_handler (log_handler); } int main(int argc, char *argv[]) { uint8_t buffer[CDIO_CD_FRAMESIZE_RAW] = { 0, }; unsigned int blocklen=CDIO_CD_FRAMESIZE_RAW; CdIo *p_cdio=NULL; int output_fd=-1; FILE *output_stream; init(); /* Parse our arguments; every option seen by `parse_opt' will be reflected in `arguments'. */ parse_options(argc, argv); print_version(program_name, VERSION, opts.no_header, opts.version_only); p_cdio = open_input(source_name, opts.source_image, opts.access_mode); if (opts.output_file!=NULL) { /* If hexdump not explicitly set, then don't produce hexdump when writing to a file. */ if (opts.hexdump == 2) opts.hexdump = 0; output_fd = open(opts.output_file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644); if (-1 == output_fd) { err_exit("Error opening output file %s: %s\n", opts.output_file, strerror(errno)); } } else /* If we are writing to stdout, then the default is to produce a hexdump. */ if (opts.hexdump == 2) opts.hexdump = 1; for ( ; opts.start_lsn <= opts.end_lsn; opts.start_lsn++ ) { switch (opts.read_mode) { case READ_AUDIO: case READ_M1F1: case READ_M1F2: case READ_M2F1: case READ_M2F2: if (DRIVER_OP_SUCCESS != cdio_read_sector(p_cdio, &buffer, opts.start_lsn, (cdio_read_mode_t) opts.read_mode)) { report( stderr, "error reading block %u\n", (unsigned int) opts.start_lsn ); blocklen = 0; } else { switch (opts.read_mode) { case READ_M1F1: blocklen=CDIO_CD_FRAMESIZE; break; case READ_M1F2: blocklen=M2RAW_SECTOR_SIZE; break; case READ_M2F1: blocklen=CDIO_CD_FRAMESIZE; break; case READ_M2F2: blocklen=M2F2_SECTOR_SIZE; break; default: ; } } break; case READ_ANY: { driver_id_t driver_id = cdio_get_driver_id(p_cdio); if (cdio_is_device(source_name, driver_id)) { if (DRIVER_OP_SUCCESS != mmc_read_sectors(p_cdio, &buffer, opts.start_lsn, CDIO_MMC_READ_TYPE_ANY, 1)) { report( stderr, "error reading block %u\n", (unsigned int) opts.start_lsn ); blocklen = 0; } } else { err_exit( "%s: mode 'any' must be used with a real CD-ROM, not an image file.\n", program_name); } } break; case READ_MODE_UNINIT: err_exit("%s: Reading mode not set\n", program_name); break; } if (!opts.output_file) { output_stream = stdout; } else { output_stream = fdopen(output_fd, "w"); } if (opts.hexdump) hexdump(output_stream, buffer, blocklen, opts.just_hex); else if (opts.output_file) { ssize_t bytes_ret; bytes_ret = write(output_fd, buffer, blocklen); (void)bytes_ret; /* Silence unused warnings */ } else { unsigned int i; for (i=0; i