Moving ongoing work into SVN; don't try to use this version.
[xiph/unicode.git] / cdparanoia / main.c
blobeb6c8b3510222f1ce08bb346fe7071fddc16a899
1 /*
2 * Copyright: GNU Public License 2 applies
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * cdparanoia (C) 2008 Monty <monty@xiph.org>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <errno.h>
30 #include <math.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
34 #include "interface/cdda_interface.h"
35 #include "paranoia/cdda_paranoia.h"
36 #include "utils.h"
37 #include "report.h"
38 #include "version.h"
39 #include "header.h"
40 #include "cachetest.h"
42 static long parse_offset(cdrom_drive *d, char *offset, int begin){
43 long track=-1;
44 long hours=-1;
45 long minutes=-1;
46 long seconds=-1;
47 long sectors=-1;
48 char *time=NULL,*temp=NULL;
49 long ret;
51 if(offset==NULL)return(-1);
53 /* seperate track from time offset */
54 temp=strchr(offset,']');
55 if(temp){
56 *temp='\0';
57 temp=strchr(offset,'[');
58 if(temp==NULL){
59 report("Error parsing span argument");
60 exit(1);
62 *temp='\0';
63 time=temp+1;
66 /* parse track */
68 int chars=strspn(offset,"0123456789");
69 if(chars>0){
70 offset[chars]='\0';
71 track=atoi(offset);
72 if(track<0 || track>d->tracks){ /*take track 0 as pre-gap of 1st track*/
73 char buffer[256];
74 sprintf(buffer,"Track #%ld does not exist.",track);
75 report(buffer);
76 exit(1);
81 while(time){
82 long val,chars;
83 char *sec=strrchr(time,'.');
84 if(!sec)sec=strrchr(time,':');
85 if(!sec)sec=time-1;
87 chars=strspn(sec+1,"0123456789");
88 if(chars)
89 val=atoi(sec+1);
90 else
91 val=0;
93 switch(*sec){
94 case '.':
95 if(sectors!=-1){
96 report("Error parsing span argument");
97 exit(1);
99 sectors=val;
100 break;
101 default:
102 if(seconds==-1)
103 seconds=val;
104 else
105 if(minutes==-1)
106 minutes=val;
107 else
108 if(hours==-1)
109 hours=val;
110 else{
111 report("Error parsing span argument");
112 exit(1);
114 break;
117 if(sec<=time)break;
118 *sec='\0';
121 if(track==-1){
122 if(seconds==-1 && sectors==-1)return(-1);
123 if(begin==-1)
124 ret=cdda_disc_firstsector(d);
125 else
126 ret=begin;
127 }else{
128 if(seconds==-1 && sectors==-1){
129 if(begin==-1){ /* first half of a span */
130 return(cdda_track_firstsector(d,track));
131 }else{
132 return(cdda_track_lastsector(d,track));
134 }else{
135 /* relative offset into a track */
136 ret=cdda_track_firstsector(d,track);
140 /* OK, we had some sort of offset into a track */
142 if(sectors!=-1)ret+=sectors;
143 if(seconds!=-1)ret+=seconds*75;
144 if(minutes!=-1)ret+=minutes*60*75;
145 if(hours!=-1)ret+=hours*60*60*75;
147 /* We don't want to outside of the track; if it's relative, that's OK... */
148 if(track!=-1){
149 if(cdda_sector_gettrack(d,ret)!=track){
150 report("Time/sector offset goes beyond end of specified track.");
151 exit(1);
155 /* Don't pass up end of session */
157 if(ret>cdda_disc_lastsector(d)){
158 report("Time/sector offset goes beyond end of disc.");
159 exit(1);
162 return(ret);
167 static void display_toc(cdrom_drive *d){
168 long audiolen=0;
169 int i;
170 report("\nTable of contents (audio tracks only):\n"
171 "track length begin copy pre ch\n"
172 "===========================================================");
174 for(i=1;i<=d->tracks;i++)
175 if(cdda_track_audiop(d,i)>0){
176 char buffer[256];
178 long sec=cdda_track_firstsector(d,i);
179 long off=cdda_track_lastsector(d,i)-sec+1;
181 sprintf(buffer,
182 "%3d. %7ld [%02d:%02d.%02d] %7ld [%02d:%02d.%02d] %s %s %s",
184 off,(int)(off/(60*75)),(int)((off/75)%60),(int)(off%75),
185 sec,(int)(sec/(60*75)),(int)((sec/75)%60),(int)(sec%75),
186 cdda_track_copyp(d,i)?" OK":" no",
187 cdda_track_preemp(d,i)?" yes":" no",
188 cdda_track_channels(d,i)==2?" 2":" 4");
189 report(buffer);
190 audiolen+=off;
193 char buffer[256];
194 sprintf(buffer, "TOTAL %7ld [%02d:%02d.%02d] (audio only)",
195 audiolen,(int)(audiolen/(60*75)),(int)((audiolen/75)%60),
196 (int)(audiolen%75));
197 report(buffer);
199 report("");
202 static void usage(FILE *f){
203 fprintf( f,
204 VERSION"\n"
207 "(C) 2008 Monty <monty@xiph.org> and Xiph.Org\n\n" \
208 "Report bugs to paranoia@xiph.org\n"\
209 "http://www.xiph.org/paranoia/\n"
211 "USAGE:\n"
212 " cdparanoia [options] <span> [outfile]\n\n"
214 "OPTIONS:\n"
215 " -v --verbose : extra verbose operation\n"
216 " -q --quiet : quiet operation\n"
217 " -e --stderr-progress : force output of progress information to\n"
218 " stderr (for wrapper scripts)\n"
219 " -l --log-summary <file> : save result summary to file\n"
220 " -V --version : print version info and quit\n"
221 " -Q --query : autosense drive, query disc and quit\n"
222 " -B --batch : 'batch' mode (saves each track to a\n"
223 " seperate file.\n"
224 " -s --search-for-drive : do an exhaustive search for drive\n"
225 " -h --help : print help\n\n"
227 " -p --output-raw : output raw 16 bit PCM in host byte \n"
228 " order\n"
229 " -r --output-raw-little-endian : output raw 16 bit little-endian PCM\n"
230 " -R --output-raw-big-endian : output raw 16 bit big-endian PCM\n"
231 " -w --output-wav : output as WAV file (default)\n"
232 " -f --output-aiff : output as AIFF file\n"
233 " -a --output-aifc : output as AIFF-C file\n\n"
235 " -c --force-cdrom-little-endian : force treating drive as little endian\n"
236 " -C --force-cdrom-big-endian : force treating drive as big endian\n"
237 " -n --force-default-sectors <n> : force default number of sectors in read\n"
238 " to n sectors\n"
239 " -o --force-search-overlap <n> : force minimum overlap search during\n"
240 " verification to n sectors\n"
241 " -d --force-cdrom-device <dev> : use specified device; disallow \n"
242 " autosense\n"
243 " -k --force-cooked-device <dev> : use specified cdrom device and force\n"
244 " use of the old 'cooked ioctl' kernel\n"
245 " interface. -k cannot be used with -d\n"
246 " or -g.\n"
247 " -g --force-generic-device <dev> : use specified generic scsi device and\n"
248 " force use of the old SG kernel\n"
249 " interface. -g cannot be used with -k.\n"
250 " -S --force-read-speed <n> : read from device at specified speed\n"
251 " -t --toc-offset <n> : Add <n> sectors to the values reported\n"
252 " when addressing tracks. May be negative\n"
253 " -T --toc-bias : Assume that the beginning offset of \n"
254 " track 1 as reported in the TOC will be\n"
255 " addressed as LBA 0. Necessary for some\n"
256 " Toshiba drives to get track boundaries\n"
257 " correct\n"
258 " -O --sample-offset <n> : Add <n> samples to the offset when\n"
259 " reading data. May be negative.\n"
260 " -z --never-skip[=n] : never accept any less than perfect\n"
261 " data reconstruction (don't allow 'V's)\n"
262 " but if [n] is given, skip after [n]\n"
263 " retries without progress.\n"
264 " -Z --disable-paranoia : disable all paranoia checking\n"
265 " -Y --disable-extra-paranoia : only do cdda2wav-style overlap checking\n"
266 " -X --abort-on-skip : abort on imperfect reads/skips\n"
267 " -U --cache-test : run a complete analysis of drive caching\n"
268 " behavior; verifies that cdparanoia is\n"
269 " correctly modelling a sprcific drive's\n"
270 " cache behavior.\n\n"
272 "OUTPUT SMILIES:\n"
273 " :-) Normal operation, low/no jitter\n"
274 " :-| Normal operation, considerable jitter\n"
275 " :-/ Read drift\n"
276 " :-P Unreported loss of streaming in atomic read operation\n"
277 " 8-| Finding read problems at same point during reread; hard to correct\n"
278 " :-0 SCSI/ATAPI transport error\n"
279 " :-( Scratch detected\n"
280 " ;-( Gave up trying to perform a correction\n"
281 " 8-X Aborted (as per -X) due to a scratch/skip\n"
282 " :^D Finished extracting\n\n"
284 "PROGRESS BAR SYMBOLS:\n"
285 "<space> No corrections needed\n"
286 " - Jitter correction required\n"
287 " + Unreported loss of streaming/other error in read\n"
288 " ! Errors are getting through stage 1 but corrected in stage2\n"
289 " e SCSI/ATAPI transport error (corrected)\n"
290 " V Uncorrected error/skip\n\n"
292 "SPAN ARGUMENT:\n"
293 "The span argument may be a simple track number or a offset/span\n"
294 "specification. The syntax of an offset/span takes the rough form:\n\n"
296 " 1[ww:xx:yy.zz]-2[aa:bb:cc.dd] \n\n"
298 "Here, 1 and 2 are track numbers; the numbers in brackets provide a\n"
299 "finer grained offset within a particular track. [aa:bb:cc.dd] is in\n"
300 "hours/minutes/seconds/sectors format. Zero fields need not be\n"
301 "specified: [::20], [:20], [20], [20.], etc, would be interpreted as\n"
302 "twenty seconds, [10:] would be ten minutes, [.30] would be thirty\n"
303 "sectors (75 sectors per second).\n\n"
305 "When only a single offset is supplied, it is interpreted as a starting\n"
306 "offset and ripping will continue to the end of he track. If a single\n"
307 "offset is preceeded or followed by a hyphen, the implicit missing\n"
308 "offset is taken to be the start or end of the disc, respectively. Thus:\n\n"
310 " 1:[20.35] Specifies ripping from track 1, second 20, sector 35 to \n"
311 " the end of track 1.\n\n"
313 " 1:[20.35]- Specifies ripping from 1[20.35] to the end of the disc\n\n"
315 " -2 Specifies ripping from the beginning of the disc up to\n"
316 " (and including) track 2\n\n"
318 " -2:[30.35] Specifies ripping from the beginning of the disc up to\n"
319 " 2:[30.35]\n\n"
321 " 2-4 Specifies ripping from the beginning of track two to the\n"
322 " end of track 4.\n\n"
324 "Don't forget to protect square brackets and preceeding hyphens from\n"
325 "the shell...\n\n"
326 "A few examples, protected from the shell:\n"
327 " A) query only with exhaustive search for a drive and full reporting\n"
328 " of autosense:\n"
329 " cdparanoia -vsQ\n\n"
330 " B) extract up to and including track 3, putting each track in a seperate\n"
331 " file:\n"
332 " cdparanoia -B -- \"-3\"\n\n"
333 " C) extract from track 1, time 0:30.12 to 1:10.00:\n"
334 " cdparanoia \"1[:30.12]-1[1:10]\"\n\n"
336 "Submit bug reports to paranoia@xiph.org\n\n");
339 long callbegin;
340 long callend;
341 long callscript=0;
343 static char *callback_strings[15]={"wrote",
344 "finished",
345 "read",
346 "verify",
347 "jitter",
348 "correction",
349 "scratch",
350 "scratch repair",
351 "skip",
352 "drift",
353 "backoff",
354 "overlap",
355 "dropped",
356 "duped",
357 "transport error"};
359 static int skipped_flag=0;
360 static int abort_on_skip=0;
361 FILE *logfile = NULL;
362 static void callback(long inpos, int function){
365 (== PROGRESS == [--+!---x--------------> | 007218 01 ] == :-) . ==)
369 int graph=30;
370 char buffer[256];
371 static long c_sector=0,v_sector=0;
372 static char dispcache[]=" ";
373 static int last=0;
374 static long lasttime=0;
375 long sector,osector=0;
376 struct timeval thistime;
377 static char heartbeat=' ';
378 int position=0,aheadposition=0;
379 static int overlap=0;
380 static int printit=-1;
382 static int slevel=0;
383 static int slast=0;
384 static int stimeout=0;
385 char *smilie="= :-)";
387 if(callscript)
388 fprintf(stderr,"##: %d [%s] @ %ld\n",
389 function,(function>=-2&&function<=13?callback_strings[function+2]:
390 ""),inpos);
392 if(!quiet){
393 long test;
394 osector=inpos;
395 sector=inpos/CD_FRAMEWORDS;
397 if(printit==-1){
398 if(isatty(STDERR_FILENO)){
399 printit=1;
400 }else{
401 printit=0;
405 if(printit==1){ /* else don't bother; it's probably being
406 redirected */
407 position=((float)(sector-callbegin)/
408 (callend-callbegin))*graph;
410 aheadposition=((float)(c_sector-callbegin)/
411 (callend-callbegin))*graph;
413 if(function==-2){
414 v_sector=sector;
415 return;
417 if(function==-1){
418 last=8;
419 heartbeat='*';
420 slevel=0;
421 v_sector=sector;
422 }else
423 if(position<graph && position>=0)
424 switch(function){
425 case PARANOIA_CB_VERIFY:
426 if(stimeout>=30){
427 if(overlap>CD_FRAMEWORDS)
428 slevel=2;
429 else
430 slevel=1;
432 break;
433 case PARANOIA_CB_READ:
434 if(sector>c_sector)c_sector=sector;
435 break;
437 case PARANOIA_CB_FIXUP_EDGE:
438 if(stimeout>=5){
439 if(overlap>CD_FRAMEWORDS)
440 slevel=2;
441 else
442 slevel=1;
444 if(dispcache[position]==' ')
445 dispcache[position]='-';
446 break;
447 case PARANOIA_CB_FIXUP_ATOM:
448 if(slevel<3 || stimeout>5)slevel=3;
449 if(dispcache[position]==' ' ||
450 dispcache[position]=='-')
451 dispcache[position]='+';
452 break;
453 case PARANOIA_CB_READERR:
454 slevel=6;
455 if(dispcache[position]!='V')
456 dispcache[position]='e';
457 break;
458 case PARANOIA_CB_SKIP:
459 slevel=8;
460 dispcache[position]='V';
461 break;
462 case PARANOIA_CB_OVERLAP:
463 overlap=osector;
464 break;
465 case PARANOIA_CB_SCRATCH:
466 slevel=7;
467 break;
468 case PARANOIA_CB_DRIFT:
469 if(slevel<4 || stimeout>5)slevel=4;
470 break;
471 case PARANOIA_CB_FIXUP_DROPPED:
472 case PARANOIA_CB_FIXUP_DUPED:
473 slevel=5;
474 if(dispcache[position]==' ' ||
475 dispcache[position]=='-' ||
476 dispcache[position]=='+')
477 dispcache[position]='!';
478 break;
481 switch(slevel){
482 case 0: /* finished, or no jitter */
483 if(skipped_flag)
484 smilie=" 8-X";
485 else
486 smilie=" :^D";
487 break;
488 case 1: /* normal. no atom, low jitter */
489 smilie=" :-)";
490 break;
491 case 2: /* normal, overlap > 1 */
492 smilie=" :-|";
493 break;
494 case 4: /* drift */
495 smilie=" :-/";
496 break;
497 case 3: /* unreported loss of streaming */
498 smilie=" :-P";
499 break;
500 case 5: /* dropped/duped bytes */
501 smilie=" 8-|";
502 break;
503 case 6: /* scsi error */
504 smilie=" :-0";
505 break;
506 case 7: /* scratch */
507 smilie=" :-(";
508 break;
509 case 8: /* skip */
510 smilie=" ;-(";
511 skipped_flag=1;
512 break;
516 gettimeofday(&thistime,NULL);
517 test=thistime.tv_sec*10+thistime.tv_usec/100000;
519 if(lasttime!=test || function==-1 || slast!=slevel){
520 if(lasttime!=test || function==-1){
521 last++;
522 lasttime=test;
523 if(last>7)last=0;
524 stimeout++;
525 switch(last){
526 case 0:
527 heartbeat=' ';
528 break;
529 case 1:case 7:
530 heartbeat='.';
531 break;
532 case 2:case 6:
533 heartbeat='o';
534 break;
535 case 3:case 5:
536 heartbeat='0';
537 break;
538 case 4:
539 heartbeat='O';
540 break;
542 if(function==-1)
543 heartbeat='*';
546 if(slast!=slevel){
547 stimeout=0;
549 slast=slevel;
551 if(abort_on_skip && skipped_flag && function !=-1){
552 sprintf(buffer,
553 "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ",
554 " ...aborting; please wait... ",
555 v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat);
556 }else{
557 if(v_sector==0)
558 sprintf(buffer,
559 "\r (== PROGRESS == [%s| ...... %02d ] ==%s %c ==) ",
560 dispcache,overlap/CD_FRAMEWORDS,smilie,heartbeat);
562 else
563 sprintf(buffer,
564 "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ",
565 dispcache,v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat);
567 if(aheadposition>=0 && aheadposition<graph && !(function==-1))
568 buffer[aheadposition+19]='>';
571 fprintf(stderr,buffer);
573 if (logfile != NULL && function==-1) {
574 fprintf(logfile,buffer+1);
575 fprintf(logfile,"\n\n");
576 fflush(logfile);
582 /* clear the indicator for next batch */
583 if(function==-1)
584 memset(dispcache,' ',graph);
587 const char *optstring = "escCn:o:O:d:g:k:S:prRwafvqVQhZz::YXWBi:Tt:l:U";
589 struct option options [] = {
590 {"stderr-progress",no_argument,NULL,'e'},
591 {"search-for-drive",no_argument,NULL,'s'},
592 {"force-cdrom-little-endian",no_argument,NULL,'c'},
593 {"force-cdrom-big-endian",no_argument,NULL,'C'},
594 {"force-default-sectors",required_argument,NULL,'n'},
595 {"force-search-overlap",required_argument,NULL,'o'},
596 {"force-cdrom-device",required_argument,NULL,'d'},
597 {"force-cooked-device",required_argument,NULL,'k'},
598 {"force-generic-device",required_argument,NULL,'g'},
599 {"force-read-speed",required_argument,NULL,'S'},
600 {"sample-offset",required_argument,NULL,'O'},
601 {"toc-offset",required_argument,NULL,'t'},
602 {"toc-bias",no_argument,NULL,'T'},
603 {"output-raw",no_argument,NULL,'p'},
604 {"output-raw-little-endian",no_argument,NULL,'r'},
605 {"output-raw-big-endian",no_argument,NULL,'R'},
606 {"output-wav",no_argument,NULL,'w'},
607 {"output-aiff",no_argument,NULL,'f'},
608 {"output-aifc",no_argument,NULL,'a'},
609 {"batch",no_argument,NULL,'B'},
610 {"verbose",no_argument,NULL,'v'},
611 {"quiet",no_argument,NULL,'q'},
612 {"version",no_argument,NULL,'V'},
613 {"query",no_argument,NULL,'Q'},
614 {"help",no_argument,NULL,'h'},
615 {"cache-test",no_argument,NULL,'U'},
616 {"disable-paranoia",no_argument,NULL,'Z'},
617 {"disable-extra-paranoia",no_argument,NULL,'Y'},
618 {"abort-on-skip",no_argument,NULL,'X'},
619 {"disable-fragmentation",no_argument,NULL,'F'},
620 {"output-info",required_argument,NULL,'i'},
621 {"never-skip",optional_argument,NULL,'z'},
622 {"log-summary",required_argument,NULL,'l'},
624 {NULL,0,NULL,0}
627 long blocking_write(int outf, char *buffer, long num){
628 long words=0,temp;
630 while(words<num){
631 temp=write(outf,buffer+words,num-words);
632 if(temp==-1){
633 if(errno!=EINTR && errno!=EAGAIN)
634 return(-1);
635 temp=0;
637 words+=temp;
639 return(0);
642 static cdrom_drive *d=NULL;
643 static cdrom_paranoia *p=NULL;
645 static void cleanup(void){
646 if(p)paranoia_free(p);
647 if(d)cdda_close(d);
650 int main(int argc,char *argv[]){
651 int toc_bias=0;
652 int toc_offset=0;
653 int sample_offset=0;
654 int force_cdrom_endian=-1;
655 int force_cdrom_sectors=-1;
656 int force_cdrom_overlap=-1;
657 char *force_cdrom_device=NULL;
658 char *force_generic_device=NULL;
659 char *force_cooked_device=NULL;
660 int force_cdrom_speed=0;
661 int max_retries=20;
662 char *span=NULL;
663 int output_type=1; /* 0=raw, 1=wav, 2=aifc */
664 int output_endian=0; /* -1=host, 0=little, 1=big */
665 int query_only=0;
666 int batch=0,i;
667 int run_cache_test=0;
669 /* full paranoia, but allow skipping */
670 int paranoia_mode=PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
672 char *info_file=NULL;
673 int out;
675 int search=0;
676 int c,long_option_index;
678 atexit(cleanup);
680 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
681 switch(c){
682 case 'B':
683 batch=1;
684 break;
685 case 'c':
686 force_cdrom_endian=0;
687 break;
688 case 'C':
689 force_cdrom_endian=1;
690 break;
691 case 'n':
692 force_cdrom_sectors=atoi(optarg);
693 break;
694 case 'o':
695 force_cdrom_overlap=atoi(optarg);
696 break;
697 case 'd':
698 if(force_cdrom_device)free(force_cdrom_device);
699 force_cdrom_device=copystring(optarg);
700 break;
701 case 'g':
702 if(force_cooked_device){
703 report("-g option incompatable with -k\n");
704 exit(1);
706 force_cooked_device=NULL;
707 if(force_generic_device)free(force_generic_device);
708 force_generic_device=copystring(optarg);
709 break;
710 case 'k':
711 if(force_generic_device || force_cdrom_device){
712 report("-k option incompatable with -d and -g\n");
713 exit(1);
715 if(force_cooked_device)free(force_cooked_device);
716 force_cooked_device=copystring(optarg);
717 break;
718 case 'S':
719 force_cdrom_speed=atoi(optarg);
720 break;
721 case 'p':
722 output_type=0;
723 output_endian=-1;
724 break;
725 case 'r':
726 output_type=0;
727 output_endian=0;
728 break;
729 case 'R':
730 output_type=0;
731 output_endian=1;
732 break;
733 case 'w':
734 output_type=1;
735 output_endian=0;
736 break;
737 case 'a':
738 output_type=2;
739 output_endian=1;
740 break;
741 case 'f':
742 output_type=3;
743 output_endian=1;
744 break;
745 case 'v':
746 verbose=CDDA_MESSAGE_PRINTIT;
747 quiet=0;
748 break;
749 case 's':
750 search=1;
751 break;
752 case 'q':
753 verbose=CDDA_MESSAGE_FORGETIT;
754 quiet=1;
755 break;
756 case 'e':
757 callscript=1;
758 fprintf(stderr,"Sending all callbacks to stderr for wrapper script\n");
759 break;
760 case 'V':
761 fprintf(stderr,VERSION);
762 fprintf(stderr,"\n");
763 exit(0);
764 break;
765 case 'Q':
766 query_only=1;
767 break;
768 case 'h':
769 usage(stdout);
770 exit(0);
771 case 'Z':
772 paranoia_mode=PARANOIA_MODE_DISABLE;
773 break;
774 case 'U':
775 run_cache_test=1;
776 query_only=1;
777 break;
778 case 'z':
779 if (optarg) {
780 max_retries = atoi (optarg);
781 paranoia_mode&=~PARANOIA_MODE_NEVERSKIP;
782 } else {
783 paranoia_mode|=PARANOIA_MODE_NEVERSKIP;
785 break;
786 case 'Y':
787 paranoia_mode|=PARANOIA_MODE_OVERLAP; /* cdda2wav style overlap
788 check only */
789 paranoia_mode&=~PARANOIA_MODE_VERIFY;
790 break;
791 case 'X':
792 /*paranoia_mode&=~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR);*/
793 abort_on_skip=1;
794 break;
795 case 'W':
796 paranoia_mode&=~PARANOIA_MODE_REPAIR;
797 break;
798 case 'F':
799 paranoia_mode&=~(PARANOIA_MODE_FRAGMENT);
800 break;
801 case 'i':
802 if(info_file)free(info_file);
803 info_file=copystring(info_file);
804 break;
805 case 'T':
806 toc_bias=-1;
807 break;
808 case 't':
809 toc_offset=atoi(optarg);
810 break;
811 case 'l':
812 if(logfile && logfile != stdout)fclose(logfile);
813 if(!strcmp(optarg,"-"))
814 logfile=stdout;
815 else{
816 logfile=fopen(optarg,"w");
817 if(logfile==NULL){
818 report("Cannot open log summary file %s: %s",(char*)optarg,
819 strerror(errno));
820 exit(1);
824 break;
825 case 'O':
826 sample_offset=atoi(optarg);
827 break;
828 default:
829 usage(stderr);
830 exit(1);
834 if(logfile){
835 /* log command line and version */
836 int i;
837 for (i = 0; i < argc; i++)
838 fprintf(logfile,"%s ",argv[i]);
839 fprintf(logfile,"\n");
841 fprintf(logfile,VERSION);
842 fprintf(logfile,"\n");
843 fflush(logfile);
846 if(optind>=argc && !query_only){
847 if(batch)
848 span=NULL;
849 else{
850 /* D'oh. No span. Fetch me a brain, Igor. */
851 usage(stderr);
852 exit(1);
854 }else
855 span=copystring(argv[optind]);
857 report(VERSION);
859 /* Query the cdrom/disc; we may need to override some settings */
861 if(force_cooked_device){
862 d=cdda_identify_cooked(force_cooked_device,verbose,NULL);
863 }else if(force_generic_device)
864 d=cdda_identify_scsi(force_generic_device,force_cdrom_device,verbose,NULL);
865 else
866 if(force_cdrom_device)
867 d=cdda_identify(force_cdrom_device,verbose,NULL);
868 else
869 if(search)
870 d=cdda_find_a_cdrom(verbose,NULL);
871 else{
872 /* does the /dev/cdrom link exist? */
873 struct stat s;
874 if(lstat("/dev/cdrom",&s)){
875 /* no link. Search anyway */
876 d=cdda_find_a_cdrom(verbose,NULL);
877 }else{
878 d=cdda_identify("/dev/cdrom",verbose,NULL);
879 if(d==NULL && !verbose){
880 verbose=1;
881 report("\n/dev/cdrom exists but isn't accessible. By default,\n"
882 "cdparanoia stops searching for an accessible drive here.\n"
883 "Consider using -sv to force a more complete autosense\n"
884 "of the machine.\n\nMore information about /dev/cdrom:");
886 d=cdda_identify("/dev/cdrom",CDDA_MESSAGE_PRINTIT,NULL);
887 report("\n");
888 exit(1);
889 }else
890 report("");
894 if(!d){
895 if(!verbose)
896 report("\nUnable to open cdrom drive; -v will give more information.");
897 exit(1);
900 if(verbose)
901 cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_PRINTIT);
902 else
903 cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_FORGETIT);
905 /* possibly force hand on endianness of drive, sector request size */
906 if(force_cdrom_endian!=-1){
907 d->bigendianp=force_cdrom_endian;
908 switch(force_cdrom_endian){
909 case 0:
910 report("Forcing CDROM sense to little-endian; ignoring preset and autosense");
911 break;
912 case 1:
913 report("Forcing CDROM sense to big-endian; ignoring preset and autosense");
914 break;
917 if(force_cdrom_sectors!=-1){
918 if(force_cdrom_sectors<0 || force_cdrom_sectors>100){
919 report("Default sector read size must be 1<= n <= 100\n");
920 cdda_close(d);
921 d=NULL;
922 exit(1);
925 char buffer[256];
926 sprintf(buffer,"Forcing default to read %d sectors; "
927 "ignoring preset and autosense",force_cdrom_sectors);
928 report(buffer);
929 d->nsectors=force_cdrom_sectors;
930 d->bigbuff=force_cdrom_sectors*CD_FRAMESIZE_RAW;
933 if(force_cdrom_overlap!=-1){
934 if(force_cdrom_overlap<0 || force_cdrom_overlap>75){
935 report("Search overlap sectors must be 0<= n <=75\n");
936 cdda_close(d);
937 d=NULL;
938 exit(1);
941 char buffer[256];
942 sprintf(buffer,"Forcing search overlap to %d sectors; "
943 "ignoring autosense",force_cdrom_overlap);
944 report(buffer);
948 switch(cdda_open(d)){
949 case -2:case -3:case -4:case -5:
950 report("\nUnable to open disc. Is there an audio CD in the drive?");
951 exit(1);
952 case -6:
953 report("\ncdparanoia could not find a way to read audio from this drive.");
954 exit(1);
955 case 0:
956 break;
957 default:
958 report("\nUnable to open disc.");
959 exit(1);
962 if(force_cdrom_speed!=0){
963 char buf[80];
964 sprintf(buf,"\nAttempting to set speed to %dx... ",force_cdrom_speed);
965 report(buf);
966 if(cdda_speed_set(d,force_cdrom_speed)){
967 report("\tFAILED.");
968 exit(1);
969 }else{
970 report("\tdrive returned OK.");
974 if(run_cache_test)
975 return analyze_timing_and_cache(d);
977 /* Dump the TOC */
978 if(query_only || verbose)display_toc(d);
979 if(query_only)exit(0);
981 /* bias the disc. A hack. Of course. */
982 /* we may need to read before or past user area; this is never
983 default, and we do it because the [allegedly informed] user told
984 us to */
985 if(sample_offset){
986 toc_offset+=sample_offset/588;
987 sample_offset%=588;
988 if(sample_offset<0){
989 sample_offset+=588;
990 toc_offset--;
994 if(toc_bias){
995 toc_offset=-cdda_track_firstsector(d,1);
997 for(i=0;i<d->tracks+1;i++)
998 d->disc_toc[i].dwStartSector+=toc_offset;
1001 if(d->nsectors==1){
1002 report("WARNING: The autosensed/selected sectors per read value is\n"
1003 " one sector, making it very unlikely Paranoia can \n"
1004 " work.\n\n"
1005 " Attempting to continue...\n\n");
1008 /* parse the span, set up begin and end sectors */
1011 long first_sector;
1012 long last_sector;
1013 long batch_first;
1014 long batch_last;
1015 int batch_track;
1017 if(span){
1018 /* look for the hyphen */
1019 char *span2=strchr(span,'-');
1020 if(strrchr(span,'-')!=span2){
1021 report("Error parsing span argument");
1022 cdda_close(d);
1023 d=NULL;
1024 exit(1);
1027 if(span2!=NULL){
1028 *span2='\0';
1029 span2++;
1032 first_sector=parse_offset(d,span,-1);
1033 if(first_sector==-1)
1034 last_sector=parse_offset(d,span2,cdda_disc_firstsector(d));
1035 else
1036 last_sector=parse_offset(d,span2,first_sector);
1038 if(first_sector==-1){
1039 if(last_sector==-1){
1040 report("Error parsing span argument");
1041 cdda_close(d);
1042 d=NULL;
1043 exit(1);
1044 }else{
1045 first_sector=cdda_disc_firstsector(d);
1047 }else{
1048 if(last_sector==-1){
1049 if(span2){ /* There was a hyphen */
1050 last_sector=cdda_disc_lastsector(d);
1051 }else{
1052 last_sector=
1053 cdda_track_lastsector(d,cdda_sector_gettrack(d,first_sector));
1057 }else{
1058 first_sector=cdda_disc_firstsector(d);
1059 last_sector=cdda_disc_lastsector(d);
1063 char buffer[250];
1064 int track1=cdda_sector_gettrack(d,first_sector);
1065 int track2=cdda_sector_gettrack(d,last_sector);
1066 long off1=first_sector-cdda_track_firstsector(d,track1);
1067 long off2=last_sector-cdda_track_firstsector(d,track2);
1068 int i;
1070 for(i=track1;i<=track2;i++)
1071 if(!cdda_track_audiop(d,i)){
1072 report("Selected span contains non audio tracks. Aborting.\n\n");
1073 exit(1);
1076 sprintf(buffer,"Ripping from sector %7ld (track %2d [%d:%02d.%02d])\n"
1077 "\t to sector %7ld (track %2d [%d:%02d.%02d])\n",first_sector,
1078 track1,(int)(off1/(60*75)),(int)((off1/75)%60),(int)(off1%75),
1079 last_sector,
1080 track2,(int)(off2/(60*75)),(int)((off2/75)%60),(int)(off2%75));
1081 report(buffer);
1086 long cursor;
1087 int16_t offset_buffer[1176];
1088 int offset_buffer_used=0;
1089 int offset_skip=sample_offset*4;
1091 p=paranoia_init(d);
1092 paranoia_modeset(p,paranoia_mode);
1093 if(force_cdrom_overlap!=-1)paranoia_overlapset(p,force_cdrom_overlap);
1095 if(verbose)
1096 cdda_verbose_set(d,CDDA_MESSAGE_LOGIT,CDDA_MESSAGE_LOGIT);
1097 else
1098 cdda_verbose_set(d,CDDA_MESSAGE_FORGETIT,CDDA_MESSAGE_FORGETIT);
1100 paranoia_seek(p,cursor=first_sector,SEEK_SET);
1102 /* this is probably a good idea in general */
1103 seteuid(getuid());
1104 setegid(getgid());
1106 /* we'll need to be able to read one sector past user data if we
1107 have a sample offset in order to pick up the last bytes. We
1108 need to set the disc length forward here so that the libs are
1109 willing to read past, assuming that works on the hardware, of
1110 course */
1111 if(sample_offset)
1112 d->disc_toc[d->tracks].dwStartSector++;
1114 while(cursor<=last_sector){
1115 char outfile_name[256];
1116 if(batch){
1117 batch_first=cursor;
1118 batch_last=
1119 cdda_track_lastsector(d,batch_track=
1120 cdda_sector_gettrack(d,cursor));
1121 if(batch_last>last_sector)batch_last=last_sector;
1122 }else{
1123 batch_first=first_sector;
1124 batch_last=last_sector;
1125 batch_track=-1;
1128 callbegin=batch_first;
1129 callend=batch_last;
1131 /* argv[optind] is the span, argv[optind+1] (if exists) is outfile */
1133 if(optind+1<argc){
1134 if(!strcmp(argv[optind+1],"-")){
1135 out=dup(fileno(stdout));
1136 if(batch)report("Are you sure you wanted 'batch' "
1137 "(-B) output with stdout?");
1138 report("outputting to stdout\n");
1139 if(logfile){
1140 fprintf(logfile,"outputting to stdout\n");
1141 fflush(logfile);
1143 outfile_name[0]='\0';
1144 }else{
1145 char path[256];
1147 char *post=strrchr(argv[optind+1],'/');
1148 int pos=(post?post-argv[optind+1]+1:0);
1149 char *file=argv[optind+1]+pos;
1151 path[0]='\0';
1153 if(pos)
1154 strncat(path,argv[optind+1],pos>256?256:pos);
1156 if(batch)
1157 snprintf(outfile_name,246,"%strack%02d.%s",path,batch_track,file);
1158 else
1159 snprintf(outfile_name,246,"%s%s",path,file);
1161 if(file[0]=='\0'){
1162 switch(output_type){
1163 case 0: /* raw */
1164 strcat(outfile_name,"cdda.raw");
1165 break;
1166 case 1:
1167 strcat(outfile_name,"cdda.wav");
1168 break;
1169 case 2:
1170 strcat(outfile_name,"cdda.aifc");
1171 break;
1172 case 3:
1173 strcat(outfile_name,"cdda.aiff");
1174 break;
1178 out=open(outfile_name,O_RDWR|O_CREAT|O_TRUNC,0666);
1179 if(out==-1){
1180 report("Cannot open specified output file %s: %s",outfile_name,
1181 strerror(errno));
1182 cdda_close(d);
1183 d=NULL;
1184 exit(1);
1186 report("outputting to %s\n",outfile_name);
1187 if(logfile){
1188 fprintf(logfile,"outputting to %s\n",outfile_name);
1189 fflush(logfile);
1192 }else{
1193 /* default */
1194 if(batch)
1195 sprintf(outfile_name,"track%02d.",batch_track);
1196 else
1197 outfile_name[0]='\0';
1199 switch(output_type){
1200 case 0: /* raw */
1201 strcat(outfile_name,"cdda.raw");
1202 break;
1203 case 1:
1204 strcat(outfile_name,"cdda.wav");
1205 break;
1206 case 2:
1207 strcat(outfile_name,"cdda.aifc");
1208 break;
1209 case 3:
1210 strcat(outfile_name,"cdda.aiff");
1211 break;
1214 out=open(outfile_name,O_RDWR|O_CREAT|O_TRUNC,0666);
1215 if(out==-1){
1216 report("Cannot open default output file %s: %s",outfile_name,
1217 strerror(errno));
1218 cdda_close(d);
1219 d=NULL;
1220 exit(1);
1222 report("outputting to %s\n",outfile_name);
1223 if(logfile){
1224 fprintf(logfile,"outputting to %s\n",outfile_name);
1225 fflush(logfile);
1229 switch(output_type){
1230 case 0: /* raw */
1231 break;
1232 case 1: /* wav */
1233 WriteWav(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1234 break;
1235 case 2: /* aifc */
1236 WriteAifc(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1237 break;
1238 case 3: /* aiff */
1239 WriteAiff(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1240 break;
1243 /* Off we go! */
1245 if(offset_buffer_used){
1246 /* partial sector from previous batch read */
1247 cursor++;
1248 if(buffering_write(out,
1249 ((char *)offset_buffer)+offset_buffer_used,
1250 CD_FRAMESIZE_RAW-offset_buffer_used)){
1251 report("Error writing output: %s",strerror(errno));
1252 exit(1);
1256 skipped_flag=0;
1257 while(cursor<=batch_last){
1258 /* read a sector */
1259 int16_t *readbuf=paranoia_read_limited(p,callback,max_retries);
1260 char *err=cdda_errors(d);
1261 char *mes=cdda_messages(d);
1263 if(mes || err)
1264 fprintf(stderr,"\r "
1265 " \r%s%s\n",
1266 mes?mes:"",err?err:"");
1268 if(err)free(err);
1269 if(mes)free(mes);
1270 if(readbuf==NULL){
1271 if(errno==EBADF || errno==ENOMEDIUM){
1272 report("\nparanoia_read: CDROM drive unavailable, bailing.\n");
1273 exit(1);
1275 skipped_flag=1;
1276 report("\nparanoia_read: Unrecoverable error, bailing.\n");
1277 break;
1279 if(skipped_flag && abort_on_skip){
1280 cursor=batch_last+1;
1281 break;
1284 skipped_flag=0;
1285 cursor++;
1287 if(output_endian!=bigendianp()){
1288 int i;
1289 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)readbuf[i]=swap16(readbuf[i]);
1292 callback(cursor*(CD_FRAMEWORDS)-1,-2);
1294 if(buffering_write(out,((char *)readbuf)+offset_skip,
1295 CD_FRAMESIZE_RAW-offset_skip)){
1296 report("Error writing output: %s",strerror(errno));
1297 exit(1);
1299 offset_skip=0;
1301 if(output_endian!=bigendianp()){
1302 int i;
1303 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)readbuf[i]=swap16(readbuf[i]);
1306 /* One last bit of silliness to deal with sample offsets */
1307 if(sample_offset && cursor>batch_last){
1308 int i;
1309 /* read a sector and output the partial offset. Save the
1310 rest for the next batch iteration */
1311 readbuf=paranoia_read_limited(p,callback,max_retries);
1312 err=cdda_errors(d);mes=cdda_messages(d);
1314 if(mes || err)
1315 fprintf(stderr,"\r "
1316 " \r%s%s\n",
1317 mes?mes:"",err?err:"");
1319 if(err)free(err);if(mes)free(mes);
1320 if(readbuf==NULL){
1321 skipped_flag=1;
1322 report("\nparanoia_read: Unrecoverable error reading through "
1323 "sample_offset shift\n\tat end of track, bailing.\n");
1324 break;
1326 if(skipped_flag && abort_on_skip)break;
1327 skipped_flag=0;
1328 /* do not move the cursor */
1330 if(output_endian!=bigendianp())
1331 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)
1332 offset_buffer[i]=swap16(readbuf[i]);
1333 else
1334 memcpy(offset_buffer,readbuf,CD_FRAMESIZE_RAW);
1335 offset_buffer_used=sample_offset*4;
1337 callback(cursor*(CD_FRAMEWORDS),-2);
1339 if(buffering_write(out,(char *)offset_buffer,
1340 offset_buffer_used)){
1341 report("Error writing output: %s",strerror(errno));
1342 exit(1);
1346 callback(cursor*(CD_FRAMESIZE_RAW/2)-1,-1);
1347 buffering_close(out);
1348 if(skipped_flag){
1349 /* remove the file */
1350 report("\nRemoving aborted file: %s",outfile_name);
1351 unlink(outfile_name);
1352 /* make the cursor correct if we have another track */
1353 if(batch_track!=-1){
1354 batch_track++;
1355 cursor=cdda_track_firstsector(d,batch_track);
1356 paranoia_seek(p,cursor,SEEK_SET);
1357 offset_skip=sample_offset*4;
1358 offset_buffer_used=0;
1361 report("\n");
1364 paranoia_free(p);
1365 p=NULL;
1369 report("Done.\n\n");
1371 cdda_close(d);
1372 d=NULL;
1373 if(logfile && logfile != stdout)
1374 fclose(logfile);
1375 return 0;