"Fossies" - the Fresh Open Source Software Archive

Member "libcdio-2.1.0/src/cd-read.c" (13 Apr 2019, 18273 Bytes) of package /linux/privat/libcdio-2.1.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cd-read.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.0.0_vs_2.1.0.

    1 /*
    2   Copyright (C) 2003-2006, 2008, 2011, 2019 Rocky Bernstein
    3   <rocky@gnu.org>
    4 
    5   This program is free software: you can redistribute it and/or modify
    6   it under the terms of the GNU General Public License as published by
    7   the Free Software Foundation, either version 3 of the License, or
    8   (at your option) any later version.
    9 
   10   This program is distributed in the hope that it will be useful,
   11   but WITHOUT ANY WARRANTY; without even the implied warranty of
   12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13   GNU General Public License for more details.
   14 
   15   You should have received a copy of the GNU General Public License
   16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17 */
   18 
   19 /* Program to debug read routines audio, mode1, mode2 forms 1 & 2. */
   20 
   21 #include "util.h"
   22 #include <cdio/mmc.h>
   23 
   24 #ifdef HAVE_SYS_STAT_H
   25 #include <sys/stat.h>
   26 #endif
   27 #ifdef HAVE_FCNTL_H
   28 #include <fcntl.h>
   29 #endif
   30 #ifdef HAVE_ERRNO_H
   31 #include <errno.h>
   32 #endif
   33 
   34 #include "getopt.h"
   35 
   36 #ifndef O_BINARY
   37 #define O_BINARY 0
   38 #endif
   39 
   40 /* Configuration option codes */
   41 enum {
   42   OP_HANDLED = 0,
   43 
   44   /* NOTE: libpopt version associated these with drivers.  That
   45      appeared to be an unused historical artifact.
   46   */
   47   OP_SOURCE_AUTO,
   48   OP_SOURCE_BIN,
   49   OP_SOURCE_CUE,
   50   OP_SOURCE_NRG,
   51   OP_SOURCE_CDRDAO,
   52   OP_SOURCE_DEVICE,
   53 
   54   OP_USAGE,
   55 
   56   /* These are the remaining configuration options */
   57   OP_READ_MODE,
   58   OP_VERSION,
   59 
   60 };
   61 
   62 typedef enum
   63 {
   64   READ_AUDIO = CDIO_READ_MODE_AUDIO,
   65   READ_M1F1  = CDIO_READ_MODE_M1F1,
   66   READ_M1F2  = CDIO_READ_MODE_M1F2,
   67   READ_M2F1  = CDIO_READ_MODE_M2F1,
   68   READ_M2F2  = CDIO_READ_MODE_M2F2,
   69   READ_MODE_UNINIT,
   70   READ_ANY
   71 } read_mode_t;
   72 
   73 /* Structure used so we can binary sort and set the --mode switch. */
   74 typedef struct
   75 {
   76   char name[30];
   77   read_mode_t read_mode;
   78 } subopt_entry_t;
   79 
   80 
   81 /* Sub-options for --mode.  Note: entries must be sorted! */
   82 static const subopt_entry_t modes_sublist[] = {
   83   {"any",        READ_ANY},
   84   {"audio",      READ_AUDIO},
   85   {"m1f1",       READ_M1F1},
   86   {"m1f2",       READ_M1F2},
   87   {"m2f1",       READ_M2F1},
   88   {"m2f2",       READ_M2F2},
   89   {"mode1form1", READ_M1F1},
   90   {"mode1form2", READ_M1F2},
   91   {"mode2form1", READ_M2F1},
   92   {"mode2form2", READ_M2F2},
   93   {"red",        READ_AUDIO},
   94 };
   95 
   96 /* Used by `main' to communicate with `parse_opt'. And global options
   97  */
   98 static struct arguments
   99 {
  100   char          *access_mode; /* Access method driver should use for control */
  101   char          *output_file; /* file to output blocks if not NULL. */
  102   int            debug_level;
  103   int            hexdump;     /* Show output as a hexdump */
  104   int            nohexdump;   /* Don't output as a hexdump. I don't know
  105                  how to get popt to combine these as
  106                  one variable.
  107                    */
  108   int            just_hex;    /* Don't try to print "printable" characters
  109                  in hex dump.    */
  110   read_mode_t    read_mode;
  111   int            version_only;
  112   int            no_header;
  113   int            print_iso9660;
  114   source_image_t source_image;
  115   lsn_t          start_lsn;
  116   lsn_t          end_lsn;
  117   int            num_sectors;
  118 } opts;
  119 
  120 static void
  121 hexdump (FILE *stream,  uint8_t * buffer, unsigned int len,
  122      int just_hex)
  123 {
  124   unsigned int i;
  125   for (i = 0; i < len; i++, buffer++)
  126     {
  127       if (i % 16 == 0)
  128     fprintf (stream, "0x%04x: ", i);
  129       fprintf (stream, "%02x", *buffer);
  130       if (i % 2 == 1)
  131     fprintf (stream, " ");
  132       if (i % 16 == 15) {
  133     if (!just_hex) {
  134       uint8_t *p;
  135       fprintf (stream, "  ");
  136       for (p=buffer-15; p <= buffer; p++) {
  137         fprintf(stream, "%c", isprint(*p) ?  *p : '.');
  138       }
  139     }
  140     fprintf (stream, "\n");
  141       }
  142     }
  143   fprintf (stream, "\n");
  144   fflush (stream);
  145 }
  146 
  147 /* Comparison function called by bearch() to find sub-option record. */
  148 static int
  149 compare_subopts(const void *key1, const void *key2)
  150 {
  151   subopt_entry_t *a = (subopt_entry_t *) key1;
  152   subopt_entry_t *b = (subopt_entry_t *) key2;
  153   return (strncmp(a->name, b->name, 30));
  154 }
  155 
  156 /* Do processing of a --mode sub option.
  157    Basically we find the option in the array, set it's corresponding
  158    flag variable to true as well as the "show.all" false.
  159 */
  160 static void
  161 process_suboption(const char *subopt, const subopt_entry_t *sublist, const int num,
  162                   const char *subopt_name)
  163 {
  164   subopt_entry_t *subopt_rec =
  165     bsearch(subopt, sublist, num, sizeof(subopt_entry_t),
  166             &compare_subopts);
  167   if (subopt_rec != NULL) {
  168     opts.read_mode = subopt_rec->read_mode;
  169     return;
  170   } else {
  171     unsigned int i;
  172     bool is_help=strcmp(subopt, "help")==0;
  173     if (is_help) {
  174       report( stderr, "The list of sub options for \"%s\" are:\n",
  175           subopt_name );
  176     } else {
  177       report( stderr, "Invalid option following \"%s\": %s.\n",
  178           subopt_name, subopt );
  179       report( stderr, "Should be one of: " );
  180     }
  181     for (i=0; i<num-1; i++) {
  182       report( stderr, "%s, ", sublist[i].name );
  183     }
  184     report( stderr, "or %s.\n", sublist[num-1].name );
  185     exit (is_help ? EXIT_SUCCESS : EXIT_FAILURE);
  186   }
  187 }
  188 
  189 
  190 /* Parse source options. */
  191 static void
  192 parse_source(int opt)
  193 {
  194   /* NOTE: The libpopt version made use of an extra temporary
  195      variable (psz_my_source) for all sources _except_ devices.
  196      This distinction seemed to serve no purpose.
  197   */
  198   /* NOTE: The libpopt version had a bug which kept it from
  199      processing toc-file inputs
  200   */
  201 
  202   if (opts.source_image != INPUT_UNKNOWN) {
  203     report( stderr, "%s: another source type option given before.\n",
  204         program_name );
  205     report( stderr, "%s: give only one source type option.\n",
  206         program_name );
  207     return;
  208   }
  209 
  210   /* For all input sources which are not a DEVICE, we need to make
  211      a copy of the string; for a DEVICE the fill-out routine makes
  212      the copy.
  213   */
  214   if (OP_SOURCE_DEVICE != opt)
  215     if (optarg != NULL) source_name = strdup(optarg);
  216 
  217   switch (opt) {
  218   case OP_SOURCE_BIN:
  219     opts.source_image  = INPUT_BIN;
  220     break;
  221   case OP_SOURCE_CUE:
  222     opts.source_image  = INPUT_CUE;
  223     break;
  224   case OP_SOURCE_NRG:
  225     opts.source_image  = INPUT_NRG;
  226     break;
  227   case OP_SOURCE_AUTO:
  228     opts.source_image  = INPUT_AUTO;
  229     break;
  230   case OP_SOURCE_DEVICE:
  231     opts.source_image  = INPUT_DEVICE;
  232     if (optarg != NULL) source_name = fillout_device_name(optarg);
  233     break;
  234   }
  235 }
  236 
  237 
  238 /* Parse a options. */
  239 static bool
  240 parse_options (int argc, char *argv[])
  241 {
  242   int opt;
  243   int rc = EXIT_FAILURE;
  244 
  245   static const char helpText[] =
  246     "Usage: %s [OPTION...]\n"
  247     "  -a, --access-mode=STRING        Set CD control access mode\n"
  248     "  -m, --mode=MODE-TYPE            set CD-ROM read mode (audio, m1f1, m1f2,\n"
  249     "                                  m2mf1, m2f2)\n"
  250     "  -d, --debug=INT                 Set debugging to LEVEL\n"
  251     "  -x, --hexdump                   Show output as a hex dump. The default is a\n"
  252     "                                  hex dump when output goes to stdout and no\n"
  253     "                                  hex dump when output is to a file.\n"
  254     "  -j, --just-hex                  Don't display printable chars on hex\n"
  255     "                                  dump. The default is print chars too.\n"
  256     "  --no-header                     Don't display header and copyright (for\n"
  257     "                                  regression testing)\n"
  258     "  --no-hexdump                    Don't show output as a hex dump.\n"
  259     "  -s, --start=INT                 Set LBA to start reading from\n"
  260     "  -e, --end=INT                   Set LBA to end reading from\n"
  261     "  -n, --number=INT                Set number of sectors to read\n"
  262     "  -b, --bin-file[=FILE]           set \"bin\" CD-ROM disk image file as source\n"
  263     "  -c, --cue-file[=FILE]           set \"cue\" CD-ROM disk image file as source\n"
  264     "  -i, --input[=FILE]              set source and determine if \"bin\" image or\n"
  265     "                                  device\n"
  266     "  -C, --cdrom-device[=DEVICE]     set CD-ROM device as source\n"
  267     "  -N, --nrg-file[=FILE]           set Nero CD-ROM disk image file as source\n"
  268     "  -t, --toc-file[=FILE]           set \"TOC\" CD-ROM disk image file as source\n"
  269     "  -o, --output-file=FILE          Output blocks to file rather than give a\n"
  270     "                                  hexdump.\n"
  271     "  -V, --version                   display version and copyright information\n"
  272     "                                  and exit\n"
  273     "\n"
  274     "Help options:\n"
  275     "  -?, --help                      Show this help message\n"
  276     "  --usage                         Display brief usage message\n";
  277 
  278   static const char usageText[] =
  279     "Usage: %s [-a|--access-mode STRING] [-m|--mode MODE-TYPE]\n"
  280     "        [-d|--debug INT] [-x|--hexdump] [--no-header] [--no-hexdump]\n"
  281     "        [-s|--start INT] [-e|--end INT] [-n|--number INT] [-b|--bin-file FILE]\n"
  282     "        [-c|--cue-file FILE] [-i|--input FILE] [-C|--cdrom-device DEVICE]\n"
  283     "        [-N|--nrg-file FILE] [-t|--toc-file FILE] [-o|--output-file FILE]\n"
  284     "        [-V|--version] [-?|--help] [--usage]\n";
  285 
  286   /* Command-line options */
  287   static const char optionsString[] = "a:m:d:xjs:e:n:b::c::i::C::N::t::o:V?";
  288   static const struct option optionsTable[] = {
  289 
  290     {"access-mode", required_argument, NULL, 'a'},
  291     {"mode", required_argument, NULL, 'm'},
  292     {"debug", required_argument, NULL, 'd'},
  293     {"hexdump", no_argument, NULL, 'x'},
  294     {"no-header", no_argument, &opts.no_header, 1},
  295     {"no-hexdump", no_argument, &opts.nohexdump, 1},
  296     {"just-hex", no_argument, &opts.just_hex, 'j'},
  297     {"start", required_argument, NULL, 's'},
  298     {"end", required_argument, NULL, 'e'},
  299     {"number", required_argument, NULL, 'n'},
  300     {"bin-file", optional_argument, NULL, 'b'},
  301     {"cue-file", optional_argument, NULL, 'c'},
  302     {"input", optional_argument, NULL, 'i'},
  303     {"cdrom-device", optional_argument, NULL, 'C'},
  304     {"nrg-file", optional_argument, NULL, 'N'},
  305     {"toc-file", optional_argument, NULL, 't'},
  306     {"output-file", required_argument, NULL, 'o'},
  307     {"version", no_argument, NULL, 'V'},
  308 
  309     {"help", no_argument, NULL, '?' },
  310     {"usage", no_argument, NULL, OP_USAGE },
  311     { NULL, 0, NULL, 0 }
  312   };
  313 
  314   program_name = strrchr(argv[0],'/');
  315   program_name = program_name ? strdup(program_name+1) : strdup(argv[0]);
  316 
  317   while ((opt = getopt_long(argc, argv, optionsString, optionsTable, NULL)) >= 0)
  318     switch (opt)
  319       {
  320       case 'a': opts.access_mode = strdup(optarg); break;
  321       case 'd': opts.debug_level = atoi(optarg); break;
  322       case 'x': opts.hexdump = 1; break;
  323       case 's': opts.start_lsn = atoi(optarg); break;
  324       case 'e': opts.end_lsn = atoi(optarg); break;
  325       case 'n': opts.num_sectors = atoi(optarg); break;
  326       case 'b': parse_source(OP_SOURCE_BIN); break;
  327       case 'c': parse_source(OP_SOURCE_CUE); break;
  328       case 'i': parse_source(OP_SOURCE_AUTO); break;
  329       case 'C': parse_source(OP_SOURCE_DEVICE); break;
  330       case 'N': parse_source(OP_SOURCE_NRG); break;
  331       case 't': parse_source(OP_SOURCE_CDRDAO); break;
  332       case 'o': opts.output_file = strdup(optarg); break;
  333 
  334       case 'm':
  335     process_suboption(optarg, modes_sublist,
  336               sizeof(modes_sublist) / sizeof(subopt_entry_t),
  337                             "--mode");
  338         break;
  339 
  340       case 'V':
  341         print_version(program_name, VERSION, 0, true);
  342     rc = EXIT_SUCCESS;
  343     goto error_exit;
  344 
  345       case '?':
  346     fprintf(stdout, helpText, program_name);
  347     rc = EXIT_INFO;
  348     goto error_exit;
  349 
  350       case OP_USAGE:
  351     fprintf(stderr, usageText, program_name);
  352     goto error_exit;
  353 
  354       case OP_HANDLED:
  355     break;
  356       }
  357 
  358   if (optind < argc) {
  359     const char *remaining_arg = argv[optind++];
  360 
  361     /* NOTE: A bug in the libpopt version checked source_image, which
  362        rendered the subsequent source_image test useless.
  363     */
  364     if (source_name != NULL) {
  365       report( stderr, "%s: Source specified in option %s and as %s\n",
  366           program_name, source_name, remaining_arg );
  367       goto error_exit;
  368     }
  369 
  370     if (opts.source_image == INPUT_DEVICE)
  371       source_name = fillout_device_name(remaining_arg);
  372     else
  373       source_name = strdup(remaining_arg);
  374 
  375     if (optind < argc) {
  376       report( stderr, "%s: Source specified in previously %s and %s\n",
  377           program_name, source_name, remaining_arg );
  378       goto error_exit;
  379     }
  380   }
  381 
  382   if (opts.debug_level == 3) {
  383     cdio_loglevel_default = CDIO_LOG_INFO;
  384   } else if (opts.debug_level >= 4) {
  385     cdio_loglevel_default = CDIO_LOG_DEBUG;
  386   }
  387 
  388   if (opts.read_mode == READ_MODE_UNINIT) {
  389     report( stderr,
  390         "%s: Need to give a read mode "
  391         "(audio, m1f1, m1f2, m2f1, m2f2, or auto)\n",
  392         program_name );
  393     rc = 10;
  394     goto error_exit;
  395   }
  396 
  397   /* Check consistency between start_lsn, end_lsn and num_sectors. */
  398 
  399   if (opts.nohexdump && opts.hexdump != 2) {
  400     report( stderr,
  401         "%s: don't give both --hexdump and --no-hexdump together\n",
  402         program_name );
  403     rc = 11;
  404     goto error_exit;
  405   }
  406 
  407   if (opts.nohexdump) opts.hexdump = 0;
  408 
  409   if (opts.start_lsn == CDIO_INVALID_LSN) {
  410     /* Maybe we derive the start from the end and num sectors. */
  411     if (opts.end_lsn == CDIO_INVALID_LSN) {
  412       /* No start or end LSN, so use 0 for the start */
  413       opts.start_lsn = 0;
  414       if (opts.num_sectors == 0) opts.num_sectors = 1;
  415     } else if (opts.num_sectors != 0) {
  416       if (opts.end_lsn <= opts.num_sectors) {
  417     report( stderr, "%s: end LSN (%lu) needs to be greater than "
  418         " the sector to read (%lu)\n",
  419         program_name, (unsigned long) opts.end_lsn,
  420         (unsigned long) opts.num_sectors );
  421     rc = 12;
  422     goto error_exit;
  423       }
  424       opts.start_lsn = opts.end_lsn - opts.num_sectors + 1;
  425     }
  426   }
  427 
  428   /* opts.start_lsn has been set somehow or we've aborted. */
  429 
  430   if (opts.end_lsn == CDIO_INVALID_LSN) {
  431     if (0 == opts.num_sectors) opts.num_sectors = 1;
  432     opts.end_lsn = opts.start_lsn + opts.num_sectors - 1;
  433   } else {
  434     /* We were given an end lsn. */
  435     if (opts.end_lsn < opts.start_lsn) {
  436       report( stderr,
  437           "%s: end LSN (%lu) needs to be grater than start LSN (%lu)\n",
  438           program_name, (unsigned long) opts.start_lsn,
  439           (unsigned long) opts.end_lsn );
  440       rc = 13;
  441       goto error_exit;
  442     }
  443     if (opts.num_sectors != opts.end_lsn - opts.start_lsn + 1)
  444       if (opts.num_sectors != 0) {
  445      report( stderr,
  446          "%s: inconsistency between start LSN (%lu), end (%lu), "
  447          "and count (%d)\n",
  448          program_name, (unsigned long) opts.start_lsn,
  449          (unsigned long) opts.end_lsn, opts.num_sectors );
  450      rc = 14;
  451      goto error_exit;
  452     }
  453     opts.num_sectors = opts.end_lsn - opts.start_lsn + 1;
  454   }
  455 
  456   return true;
  457  error_exit:
  458   free(program_name);
  459   exit(rc);
  460 }
  461 
  462 static void
  463 log_handler (cdio_log_level_t level, const char message[])
  464 {
  465   if (level == CDIO_LOG_DEBUG && opts.debug_level < 2)
  466     return;
  467 
  468   if (level == CDIO_LOG_INFO  && opts.debug_level < 1)
  469     return;
  470 
  471   if (level == CDIO_LOG_WARN  && opts.debug_level < 0)
  472     return;
  473 
  474   gl_default_cdio_log_handler (level, message);
  475 }
  476 
  477 
  478 static void
  479 init(void)
  480 {
  481   opts.debug_level   = 0;
  482   opts.start_lsn     = CDIO_INVALID_LSN;
  483   opts.end_lsn       = CDIO_INVALID_LSN;
  484   opts.num_sectors   = 0;
  485   opts.read_mode     = READ_MODE_UNINIT;
  486   opts.source_image  = INPUT_UNKNOWN;
  487   opts.hexdump       = 2;         /* Not set. */
  488 
  489   gl_default_cdio_log_handler = cdio_log_set_handler (log_handler);
  490 }
  491 
  492 int
  493 main(int argc, char *argv[])
  494 {
  495   uint8_t buffer[CDIO_CD_FRAMESIZE_RAW] = { 0, };
  496   unsigned int blocklen=CDIO_CD_FRAMESIZE_RAW;
  497   CdIo *p_cdio=NULL;
  498   int output_fd=-1;
  499   FILE *output_stream;
  500 
  501   init();
  502 
  503   /* Parse our arguments; every option seen by `parse_opt' will
  504      be reflected in `arguments'. */
  505   parse_options(argc, argv);
  506 
  507   print_version(program_name, VERSION, opts.no_header, opts.version_only);
  508 
  509   p_cdio = open_input(source_name, opts.source_image, opts.access_mode);
  510 
  511   if (opts.output_file!=NULL) {
  512 
  513     /* If hexdump not explicitly set, then don't produce hexdump
  514        when writing to a file.
  515      */
  516     if (opts.hexdump == 2) opts.hexdump = 0;
  517 
  518     output_fd = open(opts.output_file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
  519     if (-1 == output_fd) {
  520       err_exit("Error opening output file %s: %s\n",
  521            opts.output_file, strerror(errno));
  522 
  523     }
  524   } else
  525     /* If we are writing to stdout, then the default is to produce
  526        a hexdump.
  527      */
  528     if (opts.hexdump == 2) opts.hexdump = 1;
  529 
  530 
  531   for ( ; opts.start_lsn <= opts.end_lsn; opts.start_lsn++ ) {
  532     switch (opts.read_mode) {
  533     case READ_AUDIO:
  534     case READ_M1F1:
  535     case READ_M1F2:
  536     case READ_M2F1:
  537     case READ_M2F2:
  538       if (DRIVER_OP_SUCCESS !=
  539       cdio_read_sector(p_cdio, &buffer,
  540                opts.start_lsn,
  541                (cdio_read_mode_t) opts.read_mode)) {
  542     report( stderr, "error reading block %u\n",
  543         (unsigned int) opts.start_lsn );
  544     blocklen = 0;
  545       } else {
  546     switch (opts.read_mode) {
  547     case READ_M1F1:
  548       blocklen=CDIO_CD_FRAMESIZE;
  549       break;
  550     case READ_M1F2:
  551       blocklen=M2RAW_SECTOR_SIZE;
  552       break;
  553     case READ_M2F1:
  554       blocklen=CDIO_CD_FRAMESIZE;
  555       break;
  556     case READ_M2F2:
  557       blocklen=M2F2_SECTOR_SIZE;
  558       break;
  559     default: ;
  560     }
  561       }
  562       break;
  563 
  564     case READ_ANY:
  565       {
  566     driver_id_t driver_id = cdio_get_driver_id(p_cdio);
  567     if (cdio_is_device(source_name, driver_id)) {
  568       if (DRIVER_OP_SUCCESS !=
  569           mmc_read_sectors(p_cdio, &buffer,
  570                    opts.start_lsn, CDIO_MMC_READ_TYPE_ANY, 1)) {
  571         report( stderr, "error reading block %u\n",
  572             (unsigned int) opts.start_lsn );
  573         blocklen = 0;
  574       }
  575     } else {
  576       err_exit(
  577            "%s: mode 'any' must be used with a real CD-ROM, not an image file.\n", program_name);
  578     }
  579       }
  580       break;
  581 
  582       case READ_MODE_UNINIT:
  583       err_exit("%s: Reading mode not set\n", program_name);
  584       break;
  585     }
  586 
  587     if (!opts.output_file) {
  588       output_stream = stdout;
  589     } else {
  590       output_stream = fdopen(output_fd, "w");
  591     }
  592 
  593     if (opts.hexdump)
  594       hexdump(output_stream, buffer, blocklen, opts.just_hex);
  595     else if (opts.output_file) {
  596       ssize_t bytes_ret;
  597       bytes_ret = write(output_fd, buffer, blocklen);
  598       (void)bytes_ret; /* Silence unused warnings */
  599     } else {
  600       unsigned int i;
  601       for (i=0; i<blocklen; i++) printf("%c", buffer[i]);
  602     }
  603 
  604   }
  605 
  606   if (opts.output_file) close(output_fd);
  607 
  608   myexit(p_cdio, EXIT_SUCCESS);
  609   /* Not reached:*/
  610   return(EXIT_SUCCESS);
  611 
  612 }