Bring legend handling in line with the 1d panels, fixing a few update bugs along...
[xiph/unicode.git] / cdparanoia / main.c
blob4ad0947fe88e350d6f1dcc50f153e22d65434f43
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) 2006 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"
41 extern int verbose;
42 extern int quiet;
44 static long parse_offset(cdrom_drive *d, char *offset, int begin){
45 long track=-1;
46 long hours=-1;
47 long minutes=-1;
48 long seconds=-1;
49 long sectors=-1;
50 char *time=NULL,*temp=NULL;
51 long ret;
53 if(offset==NULL)return(-1);
55 /* seperate track from time offset */
56 temp=strchr(offset,']');
57 if(temp){
58 *temp='\0';
59 temp=strchr(offset,'[');
60 if(temp==NULL){
61 report("Error parsing span argument");
62 exit(1);
64 *temp='\0';
65 time=temp+1;
68 /* parse track */
70 int chars=strspn(offset,"0123456789");
71 if(chars>0){
72 offset[chars]='\0';
73 track=atoi(offset);
74 if(track<0 || track>d->tracks){ /*take track 0 as pre-gap of 1st track*/
75 char buffer[256];
76 sprintf(buffer,"Track #%ld does not exist.",track);
77 report(buffer);
78 exit(1);
83 while(time){
84 long val,chars;
85 char *sec=strrchr(time,'.');
86 if(!sec)sec=strrchr(time,':');
87 if(!sec)sec=time-1;
89 chars=strspn(sec+1,"0123456789");
90 if(chars)
91 val=atoi(sec+1);
92 else
93 val=0;
95 switch(*sec){
96 case '.':
97 if(sectors!=-1){
98 report("Error parsing span argument");
99 exit(1);
101 sectors=val;
102 break;
103 default:
104 if(seconds==-1)
105 seconds=val;
106 else
107 if(minutes==-1)
108 minutes=val;
109 else
110 if(hours==-1)
111 hours=val;
112 else{
113 report("Error parsing span argument");
114 exit(1);
116 break;
119 if(sec<=time)break;
120 *sec='\0';
123 if(track==-1){
124 if(seconds==-1 && sectors==-1)return(-1);
125 if(begin==-1)
126 ret=cdda_disc_firstsector(d);
127 else
128 ret=begin;
129 }else{
130 if(seconds==-1 && sectors==-1){
131 if(begin==-1){ /* first half of a span */
132 return(cdda_track_firstsector(d,track));
133 }else{
134 return(cdda_track_lastsector(d,track));
136 }else{
137 /* relative offset into a track */
138 ret=cdda_track_firstsector(d,track);
142 /* OK, we had some sort of offset into a track */
144 if(sectors!=-1)ret+=sectors;
145 if(seconds!=-1)ret+=seconds*75;
146 if(minutes!=-1)ret+=minutes*60*75;
147 if(hours!=-1)ret+=hours*60*60*75;
149 /* We don't want to outside of the track; if it's relative, that's OK... */
150 if(track!=-1){
151 if(cdda_sector_gettrack(d,ret)!=track){
152 report("Time/sector offset goes beyond end of specified track.");
153 exit(1);
157 /* Don't pass up end of session */
159 if(ret>cdda_disc_lastsector(d)){
160 report("Time/sector offset goes beyond end of disc.");
161 exit(1);
164 return(ret);
169 static void display_toc(cdrom_drive *d){
170 long audiolen=0;
171 int i;
172 report("\nTable of contents (audio tracks only):\n"
173 "track length begin copy pre ch\n"
174 "===========================================================");
176 for(i=1;i<=d->tracks;i++)
177 if(cdda_track_audiop(d,i)){
178 char buffer[256];
180 long sec=cdda_track_firstsector(d,i);
181 long off=cdda_track_lastsector(d,i)-sec+1;
183 sprintf(buffer,
184 "%3d. %7ld [%02d:%02d.%02d] %7ld [%02d:%02d.%02d] %s %s %s",
186 off,(int)(off/(60*75)),(int)((off/75)%60),(int)(off%75),
187 sec,(int)(sec/(60*75)),(int)((sec/75)%60),(int)(sec%75),
188 cdda_track_copyp(d,i)?" OK":" no",
189 cdda_track_preemp(d,i)?" yes":" no",
190 cdda_track_channels(d,i)==2?" 2":" 4");
191 report(buffer);
192 audiolen+=off;
195 char buffer[256];
196 sprintf(buffer, "TOTAL %7ld [%02d:%02d.%02d] (audio only)",
197 audiolen,(int)(audiolen/(60*75)),(int)((audiolen/75)%60),
198 (int)(audiolen%75));
199 report(buffer);
201 report("");
204 static void usage(FILE *f){
205 fprintf( f,
206 VERSION"\n"
209 "(C) 2006 Monty <monty@xiph.org> and Xiph.Org\n\n" \
210 "Report bugs to paranoia@xiph.org\n"\
211 "http://www.xiph.org/paranoia/\n"
213 "USAGE:\n"
214 " cdparanoia [options] <span> [outfile]\n\n"
216 "OPTIONS:\n"
217 " -v --verbose : extra verbose operation\n"
218 " -q --quiet : quiet operation\n"
219 " -e --stderr-progress : force output of progress information to\n"
220 " stderr (for wrapper scripts)\n"
221 " -l --log-summary <file> : save result summary to file\n"
222 " -V --version : print version info and quit\n"
223 " -Q --query : autosense drive, query disc and quit\n"
224 " -B --batch : 'batch' mode (saves each track to a\n"
225 " seperate file.\n"
226 " -s --search-for-drive : do an exhaustive search for drive\n"
227 " -h --help : print help\n\n"
229 " -p --output-raw : output raw 16 bit PCM in host byte \n"
230 " order\n"
231 " -r --output-raw-little-endian : output raw 16 bit little-endian PCM\n"
232 " -R --output-raw-big-endian : output raw 16 bit big-endian PCM\n"
233 " -w --output-wav : output as WAV file (default)\n"
234 " -f --output-aiff : output as AIFF file\n"
235 " -a --output-aifc : output as AIFF-C file\n\n"
237 " -c --force-cdrom-little-endian : force treating drive as little endian\n"
238 " -C --force-cdrom-big-endian : force treating drive as big endian\n"
239 " -n --force-default-sectors <n> : force default number of sectors in read\n"
240 " to n sectors\n"
241 " -o --force-search-overlap <n> : force minimum overlap search during\n"
242 " verification to n sectors\n"
243 " -d --force-cdrom-device <dev> : use specified device; disallow \n"
244 " autosense\n"
245 " -g --force-generic-device <dev> : use specified generic scsi device\n"
246 " -S --force-read-speed <n> : read from device at specified speed\n"
247 " -t --toc-offset <n> : Add <n> sectors to the values reported\n"
248 " when addressing tracks. May be negative\n"
249 " -T --toc-bias : Assume that the beginning offset of \n"
250 " track 1 as reported in the TOC will be\n"
251 " addressed as LBA 0. Necessary for some\n"
252 " Toshiba drives to get track boundaries\n"
253 " correct\n"
254 " -O --sample-offset <n> : Add <n> samples to the offset when\n"
255 " reading data. May be negative.\n"
256 " -z --never-skip[=n] : never accept any less than perfect\n"
257 " data reconstruction (don't allow 'V's)\n"
258 " but if [n] is given, skip after [n]\n"
259 " retries without progress.\n"
260 " -Z --disable-paranoia : disable all paranoia checking\n"
261 " -Y --disable-extra-paranoia : only do cdda2wav-style overlap checking\n"
262 " -X --abort-on-skip : abort on imperfect reads/skips\n\n"
264 "OUTPUT SMILIES:\n"
265 " :-) Normal operation, low/no jitter\n"
266 " :-| Normal operation, considerable jitter\n"
267 " :-/ Read drift\n"
268 " :-P Unreported loss of streaming in atomic read operation\n"
269 " 8-| Finding read problems at same point during reread; hard to correct\n"
270 " :-0 SCSI/ATAPI transport error\n"
271 " :-( Scratch detected\n"
272 " ;-( Gave up trying to perform a correction\n"
273 " 8-X Aborted (as per -X) due to a scratch/skip\n"
274 " :^D Finished extracting\n\n"
276 "PROGRESS BAR SYMBOLS:\n"
277 "<space> No corrections needed\n"
278 " - Jitter correction required\n"
279 " + Unreported loss of streaming/other error in read\n"
280 " ! Errors are getting through stage 1 but corrected in stage2\n"
281 " e SCSI/ATAPI transport error (corrected)\n"
282 " V Uncorrected error/skip\n\n"
284 "SPAN ARGUMENT:\n"
285 "The span argument may be a simple track number or a offset/span\n"
286 "specification. The syntax of an offset/span takes the rough form:\n\n"
288 " 1[ww:xx:yy.zz]-2[aa:bb:cc.dd] \n\n"
290 "Here, 1 and 2 are track numbers; the numbers in brackets provide a\n"
291 "finer grained offset within a particular track. [aa:bb:cc.dd] is in\n"
292 "hours/minutes/seconds/sectors format. Zero fields need not be\n"
293 "specified: [::20], [:20], [20], [20.], etc, would be interpreted as\n"
294 "twenty seconds, [10:] would be ten minutes, [.30] would be thirty\n"
295 "sectors (75 sectors per second).\n\n"
297 "When only a single offset is supplied, it is interpreted as a starting\n"
298 "offset and ripping will continue to the end of he track. If a single\n"
299 "offset is preceeded or followed by a hyphen, the implicit missing\n"
300 "offset is taken to be the start or end of the disc, respectively. Thus:\n\n"
302 " 1:[20.35] Specifies ripping from track 1, second 20, sector 35 to \n"
303 " the end of track 1.\n\n"
305 " 1:[20.35]- Specifies ripping from 1[20.35] to the end of the disc\n\n"
307 " -2 Specifies ripping from the beginning of the disc up to\n"
308 " (and including) track 2\n\n"
310 " -2:[30.35] Specifies ripping from the beginning of the disc up to\n"
311 " 2:[30.35]\n\n"
313 " 2-4 Specifies ripping from the beginning of track two to the\n"
314 " end of track 4.\n\n"
316 "Don't forget to protect square brackets and preceeding hyphens from\n"
317 "the shell...\n\n"
318 "A few examples, protected from the shell:\n"
319 " A) query only with exhaustive search for a drive and full reporting\n"
320 " of autosense:\n"
321 " cdparanoia -vsQ\n\n"
322 " B) extract up to and including track 3, putting each track in a seperate\n"
323 " file:\n"
324 " cdparanoia -B -- \"-3\"\n\n"
325 " C) extract from track 1, time 0:30.12 to 1:10.00:\n"
326 " cdparanoia \"1[:30.12]-1[1:10]\"\n\n"
328 "Submit bug reports to paranoia@xiph.org\n\n");
331 long callbegin;
332 long callend;
333 long callscript=0;
335 static char *callback_strings[15]={"wrote",
336 "finished",
337 "read",
338 "verify",
339 "jitter",
340 "correction",
341 "scratch",
342 "scratch repair",
343 "skip",
344 "drift",
345 "backoff",
346 "overlap",
347 "dropped",
348 "duped",
349 "transport error"};
351 static int skipped_flag=0;
352 static int abort_on_skip=0;
353 FILE *logfile = NULL;
354 static void callback(long inpos, int function){
357 (== PROGRESS == [--+!---x--------------> | 007218 01 ] == :-) . ==)
361 int graph=30;
362 char buffer[256];
363 static long c_sector=0,v_sector=0;
364 static char dispcache[]=" ";
365 static int last=0;
366 static long lasttime=0;
367 long sector,osector=0;
368 struct timeval thistime;
369 static char heartbeat=' ';
370 int position=0,aheadposition=0;
371 static int overlap=0;
372 static int printit=-1;
374 static int slevel=0;
375 static int slast=0;
376 static int stimeout=0;
377 char *smilie="= :-)";
379 if(callscript)
380 fprintf(stderr,"##: %d [%s] @ %ld\n",
381 function,(function>=-2&&function<=13?callback_strings[function+2]:
382 ""),inpos);
384 if(!quiet){
385 long test;
386 osector=inpos;
387 sector=inpos/CD_FRAMEWORDS;
389 if(printit==-1){
390 if(isatty(STDERR_FILENO)){
391 printit=1;
392 }else{
393 printit=0;
397 if(printit==1){ /* else don't bother; it's probably being
398 redirected */
399 position=((float)(sector-callbegin)/
400 (callend-callbegin))*graph;
402 aheadposition=((float)(c_sector-callbegin)/
403 (callend-callbegin))*graph;
405 if(function==-2){
406 v_sector=sector;
407 return;
409 if(function==-1){
410 last=8;
411 heartbeat='*';
412 slevel=0;
413 v_sector=sector;
414 }else
415 if(position<graph && position>=0)
416 switch(function){
417 case PARANOIA_CB_VERIFY:
418 if(stimeout>=30){
419 if(overlap>CD_FRAMEWORDS)
420 slevel=2;
421 else
422 slevel=1;
424 break;
425 case PARANOIA_CB_READ:
426 if(sector>c_sector)c_sector=sector;
427 break;
429 case PARANOIA_CB_FIXUP_EDGE:
430 if(stimeout>=5){
431 if(overlap>CD_FRAMEWORDS)
432 slevel=2;
433 else
434 slevel=1;
436 if(dispcache[position]==' ')
437 dispcache[position]='-';
438 break;
439 case PARANOIA_CB_FIXUP_ATOM:
440 if(slevel<3 || stimeout>5)slevel=3;
441 if(dispcache[position]==' ' ||
442 dispcache[position]=='-')
443 dispcache[position]='+';
444 break;
445 case PARANOIA_CB_READERR:
446 slevel=6;
447 if(dispcache[position]!='V')
448 dispcache[position]='e';
449 break;
450 case PARANOIA_CB_SKIP:
451 slevel=8;
452 dispcache[position]='V';
453 break;
454 case PARANOIA_CB_OVERLAP:
455 overlap=osector;
456 break;
457 case PARANOIA_CB_SCRATCH:
458 slevel=7;
459 break;
460 case PARANOIA_CB_DRIFT:
461 if(slevel<4 || stimeout>5)slevel=4;
462 break;
463 case PARANOIA_CB_FIXUP_DROPPED:
464 case PARANOIA_CB_FIXUP_DUPED:
465 slevel=5;
466 if(dispcache[position]==' ' ||
467 dispcache[position]=='-' ||
468 dispcache[position]=='+')
469 dispcache[position]='!';
470 break;
473 switch(slevel){
474 case 0: /* finished, or no jitter */
475 if(skipped_flag)
476 smilie=" 8-X";
477 else
478 smilie=" :^D";
479 break;
480 case 1: /* normal. no atom, low jitter */
481 smilie=" :-)";
482 break;
483 case 2: /* normal, overlap > 1 */
484 smilie=" :-|";
485 break;
486 case 4: /* drift */
487 smilie=" :-/";
488 break;
489 case 3: /* unreported loss of streaming */
490 smilie=" :-P";
491 break;
492 case 5: /* dropped/duped bytes */
493 smilie=" 8-|";
494 break;
495 case 6: /* scsi error */
496 smilie=" :-0";
497 break;
498 case 7: /* scratch */
499 smilie=" :-(";
500 break;
501 case 8: /* skip */
502 smilie=" ;-(";
503 skipped_flag=1;
504 break;
508 gettimeofday(&thistime,NULL);
509 test=thistime.tv_sec*10+thistime.tv_usec/100000;
511 if(lasttime!=test || function==-1 || slast!=slevel){
512 if(lasttime!=test || function==-1){
513 last++;
514 lasttime=test;
515 if(last>7)last=0;
516 stimeout++;
517 switch(last){
518 case 0:
519 heartbeat=' ';
520 break;
521 case 1:case 7:
522 heartbeat='.';
523 break;
524 case 2:case 6:
525 heartbeat='o';
526 break;
527 case 3:case 5:
528 heartbeat='0';
529 break;
530 case 4:
531 heartbeat='O';
532 break;
534 if(function==-1)
535 heartbeat='*';
538 if(slast!=slevel){
539 stimeout=0;
541 slast=slevel;
543 if(abort_on_skip && skipped_flag && function !=-1){
544 sprintf(buffer,
545 "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ",
546 " ...aborting; please wait... ",
547 v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat);
548 }else{
549 if(v_sector==0)
550 sprintf(buffer,
551 "\r (== PROGRESS == [%s| ...... %02d ] ==%s %c ==) ",
552 dispcache,overlap/CD_FRAMEWORDS,smilie,heartbeat);
554 else
555 sprintf(buffer,
556 "\r (== PROGRESS == [%s| %06ld %02d ] ==%s %c ==) ",
557 dispcache,v_sector,overlap/CD_FRAMEWORDS,smilie,heartbeat);
559 if(aheadposition>=0 && aheadposition<graph && !(function==-1))
560 buffer[aheadposition+19]='>';
563 fprintf(stderr,buffer);
565 if (logfile != NULL && function==-1) {
566 fprintf(logfile,buffer+1);
567 fprintf(logfile,"\n\n");
568 fflush(logfile);
574 /* clear the indicator for next batch */
575 if(function==-1)
576 memset(dispcache,' ',graph);
579 const char *optstring = "escCn:o:O:d:g:S:prRwafvqVQhZz::YXWBi:Tt:l:";
581 struct option options [] = {
582 {"stderr-progress",no_argument,NULL,'e'},
583 {"search-for-drive",no_argument,NULL,'s'},
584 {"force-cdrom-little-endian",no_argument,NULL,'c'},
585 {"force-cdrom-big-endian",no_argument,NULL,'C'},
586 {"force-default-sectors",required_argument,NULL,'n'},
587 {"force-search-overlap",required_argument,NULL,'o'},
588 {"force-cdrom-device",required_argument,NULL,'d'},
589 {"force-generic-device",required_argument,NULL,'g'},
590 {"force-read-speed",required_argument,NULL,'S'},
591 {"sample-offset",required_argument,NULL,'O'},
592 {"toc-offset",required_argument,NULL,'t'},
593 {"toc-bias",no_argument,NULL,'T'},
594 {"output-raw",no_argument,NULL,'p'},
595 {"output-raw-little-endian",no_argument,NULL,'r'},
596 {"output-raw-big-endian",no_argument,NULL,'R'},
597 {"output-wav",no_argument,NULL,'w'},
598 {"output-aiff",no_argument,NULL,'f'},
599 {"output-aifc",no_argument,NULL,'a'},
600 {"batch",no_argument,NULL,'B'},
601 {"verbose",no_argument,NULL,'v'},
602 {"quiet",no_argument,NULL,'q'},
603 {"version",no_argument,NULL,'V'},
604 {"query",no_argument,NULL,'Q'},
605 {"help",no_argument,NULL,'h'},
606 {"disable-paranoia",no_argument,NULL,'Z'},
607 {"disable-extra-paranoia",no_argument,NULL,'Y'},
608 {"abort-on-skip",no_argument,NULL,'X'},
609 {"disable-fragmentation",no_argument,NULL,'F'},
610 {"output-info",required_argument,NULL,'i'},
611 {"never-skip",optional_argument,NULL,'z'},
612 {"log-summary",required_argument,NULL,'l'},
614 {NULL,0,NULL,0}
617 long blocking_write(int outf, char *buffer, long num){
618 long words=0,temp;
620 while(words<num){
621 temp=write(outf,buffer+words,num-words);
622 if(temp==-1){
623 if(errno!=EINTR && errno!=EAGAIN)
624 return(-1);
625 temp=0;
627 words+=temp;
629 return(0);
632 static cdrom_drive *d=NULL;
633 static cdrom_paranoia *p=NULL;
635 static void cleanup(void){
636 if(p)paranoia_free(p);
637 if(d)cdda_close(d);
640 int main(int argc,char *argv[]){
641 int toc_bias=0;
642 int toc_offset=0;
643 int sample_offset=0;
644 int force_cdrom_endian=-1;
645 int force_cdrom_sectors=-1;
646 int force_cdrom_overlap=-1;
647 char *force_cdrom_device=NULL;
648 char *force_generic_device=NULL;
649 int force_cdrom_speed=-1;
650 int max_retries=20;
651 char *span=NULL;
652 int output_type=1; /* 0=raw, 1=wav, 2=aifc */
653 int output_endian=0; /* -1=host, 0=little, 1=big */
654 int query_only=0;
655 int batch=0,i;
657 /* full paranoia, but allow skipping */
658 int paranoia_mode=PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
660 char *info_file=NULL;
661 int out;
663 int search=0;
664 int c,long_option_index;
666 atexit(cleanup);
668 while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
669 switch(c){
670 case 'B':
671 batch=1;
672 break;
673 case 'c':
674 force_cdrom_endian=0;
675 break;
676 case 'C':
677 force_cdrom_endian=1;
678 break;
679 case 'n':
680 force_cdrom_sectors=atoi(optarg);
681 break;
682 case 'o':
683 force_cdrom_overlap=atoi(optarg);
684 break;
685 case 'd':
686 if(force_cdrom_device)free(force_cdrom_device);
687 force_cdrom_device=copystring(optarg);
688 break;
689 case 'g':
690 if(force_generic_device)free(force_generic_device);
691 force_generic_device=copystring(optarg);
692 break;
693 case 'S':
694 force_cdrom_speed=atoi(optarg);
695 break;
696 case 'p':
697 output_type=0;
698 output_endian=-1;
699 break;
700 case 'r':
701 output_type=0;
702 output_endian=0;
703 break;
704 case 'R':
705 output_type=0;
706 output_endian=1;
707 break;
708 case 'w':
709 output_type=1;
710 output_endian=0;
711 break;
712 case 'a':
713 output_type=2;
714 output_endian=1;
715 break;
716 case 'f':
717 output_type=3;
718 output_endian=1;
719 break;
720 case 'v':
721 verbose=CDDA_MESSAGE_PRINTIT;
722 quiet=0;
723 break;
724 case 's':
725 search=1;
726 break;
727 case 'q':
728 verbose=CDDA_MESSAGE_FORGETIT;
729 quiet=1;
730 break;
731 case 'e':
732 callscript=1;
733 fprintf(stderr,"Sending all callcaks to stderr for wrapper script\n");
734 break;
735 case 'V':
736 fprintf(stderr,VERSION);
737 fprintf(stderr,"\n");
738 exit(0);
739 break;
740 case 'Q':
741 query_only=1;
742 break;
743 case 'h':
744 usage(stdout);
745 exit(0);
746 case 'Z':
747 paranoia_mode=PARANOIA_MODE_DISABLE;
748 break;
749 case 'z':
750 if (optarg) {
751 max_retries = atoi (optarg);
752 paranoia_mode&=~PARANOIA_MODE_NEVERSKIP;
753 } else {
754 paranoia_mode|=PARANOIA_MODE_NEVERSKIP;
756 break;
757 case 'Y':
758 paranoia_mode|=PARANOIA_MODE_OVERLAP; /* cdda2wav style overlap
759 check only */
760 paranoia_mode&=~PARANOIA_MODE_VERIFY;
761 break;
762 case 'X':
763 /*paranoia_mode&=~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR);*/
764 abort_on_skip=1;
765 break;
766 case 'W':
767 paranoia_mode&=~PARANOIA_MODE_REPAIR;
768 break;
769 case 'F':
770 paranoia_mode&=~(PARANOIA_MODE_FRAGMENT);
771 break;
772 case 'i':
773 if(info_file)free(info_file);
774 info_file=copystring(info_file);
775 break;
776 case 'T':
777 toc_bias=-1;
778 break;
779 case 't':
780 toc_offset=atoi(optarg);
781 break;
782 case 'l':
783 if(logfile && logfile != stdout)fclose(logfile);
784 if(!strcmp(optarg,"-"))
785 logfile=stdout;
786 else{
787 logfile=fopen(optarg,"w");
788 if(logfile==NULL){
789 report3("Cannot open log summary file %s: %s",(char*)optarg,
790 strerror(errno));
791 exit(1);
795 break;
796 case 'O':
797 sample_offset=atoi(optarg);
798 break;
799 default:
800 usage(stderr);
801 exit(1);
805 if(logfile){
806 /* log command line and version */
807 int i;
808 for (i = 0; i < argc; i++)
809 fprintf(logfile,"%s ",argv[i]);
810 fprintf(logfile,"\n",argv[i]);
812 fprintf(logfile,VERSION);
813 fprintf(logfile,"\n");
814 fflush(logfile);
817 if(optind>=argc && !query_only){
818 if(batch)
819 span=NULL;
820 else{
821 /* D'oh. No span. Fetch me a brain, Igor. */
822 usage(stderr);
823 exit(1);
825 }else
826 span=copystring(argv[optind]);
828 report(VERSION);
830 /* Query the cdrom/disc; we may need to override some settings */
832 if(force_generic_device)
833 d=cdda_identify_scsi(force_generic_device,force_cdrom_device,verbose,NULL);
834 else
835 if(force_cdrom_device)
836 d=cdda_identify(force_cdrom_device,verbose,NULL);
837 else
838 if(search)
839 d=cdda_find_a_cdrom(verbose,NULL);
840 else{
841 /* does the /dev/cdrom link exist? */
842 struct stat s;
843 if(lstat("/dev/cdrom",&s)){
844 /* no link. Search anyway */
845 d=cdda_find_a_cdrom(verbose,NULL);
846 }else{
847 d=cdda_identify("/dev/cdrom",verbose,NULL);
848 if(d==NULL && !verbose){
849 verbose=1;
850 report("\n/dev/cdrom exists but isn't accessible. By default,\n"
851 "cdparanoia stops searching for an accessible drive here.\n"
852 "Consider using -sv to force a more complete autosense\n"
853 "of the machine.\n\nMore information about /dev/cdrom:");
855 d=cdda_identify("/dev/cdrom",CDDA_MESSAGE_PRINTIT,NULL);
856 report("\n");
857 exit(1);
858 }else
859 report("");
863 if(!d){
864 if(!verbose)
865 report("\nUnable to open cdrom drive; -v will give more information.");
866 exit(1);
869 if(verbose)
870 cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_PRINTIT);
871 else
872 cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_FORGETIT);
874 /* possibly force hand on endianness of drive, sector request size */
875 if(force_cdrom_endian!=-1){
876 d->bigendianp=force_cdrom_endian;
877 switch(force_cdrom_endian){
878 case 0:
879 report("Forcing CDROM sense to little-endian; ignoring preset and autosense");
880 break;
881 case 1:
882 report("Forcing CDROM sense to big-endian; ignoring preset and autosense");
883 break;
886 if(force_cdrom_sectors!=-1){
887 if(force_cdrom_sectors<0 || force_cdrom_sectors>100){
888 report("Default sector read size must be 1<= n <= 100\n");
889 cdda_close(d);
890 d=NULL;
891 exit(1);
894 char buffer[256];
895 sprintf(buffer,"Forcing default to read %d sectors; "
896 "ignoring preset and autosense",force_cdrom_sectors);
897 report(buffer);
898 d->nsectors=force_cdrom_sectors;
899 d->bigbuff=force_cdrom_sectors*CD_FRAMESIZE_RAW;
902 if(force_cdrom_overlap!=-1){
903 if(force_cdrom_overlap<0 || force_cdrom_overlap>75){
904 report("Search overlap sectors must be 0<= n <=75\n");
905 cdda_close(d);
906 d=NULL;
907 exit(1);
910 char buffer[256];
911 sprintf(buffer,"Forcing search overlap to %d sectors; "
912 "ignoring autosense",force_cdrom_overlap);
913 report(buffer);
917 switch(cdda_open(d)){
918 case -2:case -3:case -4:case -5:
919 report("\nUnable to open disc. Is there an audio CD in the drive?");
920 exit(1);
921 case -6:
922 report("\ncdparanoia could not find a way to read audio from this drive.");
923 exit(1);
924 case 0:
925 break;
926 default:
927 report("\nUnable to open disc.");
928 exit(1);
931 /* Dump the TOC */
932 if(query_only || verbose)display_toc(d);
933 if(query_only)exit(0);
935 /* bias the disc. A hack. Of course. */
936 /* we may need to read before or past user area; this is never
937 default, and we do it because the [allegedly informed] user told
938 us to */
939 if(sample_offset){
940 toc_offset+=sample_offset/588;
941 sample_offset%=588;
942 if(sample_offset<0){
943 sample_offset+=588;
944 toc_offset--;
948 if(toc_bias){
949 toc_offset=-cdda_track_firstsector(d,1);
951 for(i=0;i<d->tracks+1;i++)
952 d->disc_toc[i].dwStartSector+=toc_offset;
955 if(force_cdrom_speed!=-1){
956 cdda_speed_set(d,force_cdrom_speed);
959 if(d->nsectors==1){
960 report("WARNING: The autosensed/selected sectors per read value is\n"
961 " one sector, making it very unlikely Paranoia can \n"
962 " work.\n\n"
963 " Attempting to continue...\n\n");
966 /* parse the span, set up begin and end sectors */
969 long first_sector;
970 long last_sector;
971 long batch_first;
972 long batch_last;
973 int batch_track;
975 if(span){
976 /* look for the hyphen */
977 char *span2=strchr(span,'-');
978 if(strrchr(span,'-')!=span2){
979 report("Error parsing span argument");
980 cdda_close(d);
981 d=NULL;
982 exit(1);
985 if(span2!=NULL){
986 *span2='\0';
987 span2++;
990 first_sector=parse_offset(d,span,-1);
991 if(first_sector==-1)
992 last_sector=parse_offset(d,span2,cdda_disc_firstsector(d));
993 else
994 last_sector=parse_offset(d,span2,first_sector);
996 if(first_sector==-1){
997 if(last_sector==-1){
998 report("Error parsing span argument");
999 cdda_close(d);
1000 d=NULL;
1001 exit(1);
1002 }else{
1003 first_sector=cdda_disc_firstsector(d);
1005 }else{
1006 if(last_sector==-1){
1007 if(span2){ /* There was a hyphen */
1008 last_sector=cdda_disc_lastsector(d);
1009 }else{
1010 last_sector=
1011 cdda_track_lastsector(d,cdda_sector_gettrack(d,first_sector));
1015 }else{
1016 first_sector=cdda_disc_firstsector(d);
1017 last_sector=cdda_disc_lastsector(d);
1021 char buffer[250];
1022 int track1=cdda_sector_gettrack(d,first_sector);
1023 int track2=cdda_sector_gettrack(d,last_sector);
1024 long off1=first_sector-cdda_track_firstsector(d,track1);
1025 long off2=last_sector-cdda_track_firstsector(d,track2);
1026 int i;
1028 for(i=track1;i<=track2;i++)
1029 if(!cdda_track_audiop(d,i)){
1030 report("Selected span contains non audio tracks. Aborting.\n\n");
1031 exit(1);
1034 sprintf(buffer,"Ripping from sector %7ld (track %2d [%d:%02d.%02d])\n"
1035 "\t to sector %7ld (track %2d [%d:%02d.%02d])\n",first_sector,
1036 track1,(int)(off1/(60*75)),(int)((off1/75)%60),(int)(off1%75),
1037 last_sector,
1038 track2,(int)(off2/(60*75)),(int)((off2/75)%60),(int)(off2%75));
1039 report(buffer);
1044 long cursor;
1045 int16_t offset_buffer[1176];
1046 int offset_buffer_used=0;
1047 int offset_skip=sample_offset*4;
1049 p=paranoia_init(d);
1050 paranoia_modeset(p,paranoia_mode);
1051 if(force_cdrom_overlap!=-1)paranoia_overlapset(p,force_cdrom_overlap);
1053 if(verbose)
1054 cdda_verbose_set(d,CDDA_MESSAGE_LOGIT,CDDA_MESSAGE_LOGIT);
1055 else
1056 cdda_verbose_set(d,CDDA_MESSAGE_FORGETIT,CDDA_MESSAGE_FORGETIT);
1058 paranoia_seek(p,cursor=first_sector,SEEK_SET);
1060 /* this is probably a good idea in general */
1061 seteuid(getuid());
1062 setegid(getgid());
1064 /* we'll need to be able to read one sector past user data if we
1065 have a sample offset in order to pick up the last bytes. We
1066 need to set the disc length forward here so that the libs are
1067 willing to read past, assuming that works on the hardware, of
1068 course */
1069 if(sample_offset)
1070 d->disc_toc[d->tracks].dwStartSector++;
1072 while(cursor<=last_sector){
1073 char outfile_name[256];
1074 if(batch){
1075 batch_first=cursor;
1076 batch_last=
1077 cdda_track_lastsector(d,batch_track=
1078 cdda_sector_gettrack(d,cursor));
1079 if(batch_last>last_sector)batch_last=last_sector;
1080 }else{
1081 batch_first=first_sector;
1082 batch_last=last_sector;
1083 batch_track=-1;
1086 callbegin=batch_first;
1087 callend=batch_last;
1089 /* argv[optind] is the span, argv[optind+1] (if exists) is outfile */
1091 if(optind+1<argc){
1092 if(!strcmp(argv[optind+1],"-")){
1093 out=dup(fileno(stdout));
1094 if(batch)report("Are you sure you wanted 'batch' "
1095 "(-B) output with stdout?");
1096 report("outputting to stdout\n");
1097 if(logfile){
1098 fprintf(logfile,"outputting to stdout\n");
1099 fflush(logfile);
1101 outfile_name[0]='\0';
1102 }else{
1103 char path[256];
1105 char *post=strrchr(argv[optind+1],'/');
1106 int pos=(post?post-argv[optind+1]+1:0);
1107 char *file=argv[optind+1]+pos;
1109 path[0]='\0';
1111 if(pos)
1112 strncat(path,argv[optind+1],pos>256?256:pos);
1114 if(batch)
1115 snprintf(outfile_name,246,"%strack%02d.%s",path,batch_track,file);
1116 else
1117 snprintf(outfile_name,246,"%s%s",path,file);
1119 if(file[0]=='\0'){
1120 switch(output_type){
1121 case 0: /* raw */
1122 strcat(outfile_name,"cdda.raw");
1123 break;
1124 case 1:
1125 strcat(outfile_name,"cdda.wav");
1126 break;
1127 case 2:
1128 strcat(outfile_name,"cdda.aifc");
1129 break;
1130 case 3:
1131 strcat(outfile_name,"cdda.aiff");
1132 break;
1136 out=open(outfile_name,O_RDWR|O_CREAT|O_TRUNC,0666);
1137 if(out==-1){
1138 report3("Cannot open specified output file %s: %s",outfile_name,
1139 strerror(errno));
1140 cdda_close(d);
1141 d=NULL;
1142 exit(1);
1144 report2("outputting to %s\n",outfile_name);
1145 if(logfile){
1146 fprintf(logfile,"outputting to %s\n",outfile_name);
1147 fflush(logfile);
1150 }else{
1151 /* default */
1152 if(batch)
1153 sprintf(outfile_name,"track%02d.",batch_track);
1154 else
1155 outfile_name[0]='\0';
1157 switch(output_type){
1158 case 0: /* raw */
1159 strcat(outfile_name,"cdda.raw");
1160 break;
1161 case 1:
1162 strcat(outfile_name,"cdda.wav");
1163 break;
1164 case 2:
1165 strcat(outfile_name,"cdda.aifc");
1166 break;
1167 case 3:
1168 strcat(outfile_name,"cdda.aiff");
1169 break;
1172 out=open(outfile_name,O_RDWR|O_CREAT|O_TRUNC,0666);
1173 if(out==-1){
1174 report3("Cannot open default output file %s: %s",outfile_name,
1175 strerror(errno));
1176 cdda_close(d);
1177 d=NULL;
1178 exit(1);
1180 report2("outputting to %s\n",outfile_name);
1181 if(logfile){
1182 fprintf(logfile,"outputting to %s\n",outfile_name);
1183 fflush(logfile);
1187 switch(output_type){
1188 case 0: /* raw */
1189 break;
1190 case 1: /* wav */
1191 WriteWav(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1192 break;
1193 case 2: /* aifc */
1194 WriteAifc(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1195 break;
1196 case 3: /* aiff */
1197 WriteAiff(out,(batch_last-batch_first+1)*CD_FRAMESIZE_RAW);
1198 break;
1201 /* Off we go! */
1203 if(offset_buffer_used){
1204 /* partial sector from previous batch read */
1205 cursor++;
1206 if(buffering_write(out,
1207 ((char *)offset_buffer)+offset_buffer_used,
1208 CD_FRAMESIZE_RAW-offset_buffer_used)){
1209 report2("Error writing output: %s",strerror(errno));
1210 exit(1);
1214 skipped_flag=0;
1215 while(cursor<=batch_last){
1216 /* read a sector */
1217 int16_t *readbuf=paranoia_read_limited(p,callback,max_retries);
1218 char *err=cdda_errors(d);
1219 char *mes=cdda_messages(d);
1221 if(mes || err)
1222 fprintf(stderr,"\r "
1223 " \r%s%s\n",
1224 mes?mes:"",err?err:"");
1226 if(err)free(err);
1227 if(mes)free(mes);
1228 if(readbuf==NULL){
1229 skipped_flag=1;
1230 report("\nparanoia_read: Unrecoverable error, bailing.\n");
1231 break;
1233 if(skipped_flag && abort_on_skip){
1234 cursor=batch_last+1;
1235 break;
1238 skipped_flag=0;
1239 cursor++;
1241 if(output_endian!=bigendianp()){
1242 int i;
1243 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)readbuf[i]=swap16(readbuf[i]);
1246 callback(cursor*(CD_FRAMEWORDS)-1,-2);
1248 if(buffering_write(out,((char *)readbuf)+offset_skip,
1249 CD_FRAMESIZE_RAW-offset_skip)){
1250 report2("Error writing output: %s",strerror(errno));
1251 exit(1);
1253 offset_skip=0;
1255 if(output_endian!=bigendianp()){
1256 int i;
1257 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)readbuf[i]=swap16(readbuf[i]);
1260 /* One last bit of silliness to deal with sample offsets */
1261 if(sample_offset && cursor>batch_last){
1262 int i;
1263 /* read a sector and output the partial offset. Save the
1264 rest for the next batch iteration */
1265 readbuf=paranoia_read_limited(p,callback,max_retries);
1266 err=cdda_errors(d);mes=cdda_messages(d);
1268 if(mes || err)
1269 fprintf(stderr,"\r "
1270 " \r%s%s\n",
1271 mes?mes:"",err?err:"");
1273 if(err)free(err);if(mes)free(mes);
1274 if(readbuf==NULL){
1275 skipped_flag=1;
1276 report("\nparanoia_read: Unrecoverable error reading through "
1277 "sample_offset shift\n\tat end of track, bailing.\n");
1278 break;
1280 if(skipped_flag && abort_on_skip)break;
1281 skipped_flag=0;
1282 /* do not move the cursor */
1284 if(output_endian!=bigendianp())
1285 for(i=0;i<CD_FRAMESIZE_RAW/2;i++)
1286 offset_buffer[i]=swap16(readbuf[i]);
1287 else
1288 memcpy(offset_buffer,readbuf,CD_FRAMESIZE_RAW);
1289 offset_buffer_used=sample_offset*4;
1291 callback(cursor*(CD_FRAMEWORDS),-2);
1293 if(buffering_write(out,(char *)offset_buffer,
1294 offset_buffer_used)){
1295 report2("Error writing output: %s",strerror(errno));
1296 exit(1);
1300 callback(cursor*(CD_FRAMESIZE_RAW/2)-1,-1);
1301 buffering_close(out);
1302 if(skipped_flag){
1303 /* remove the file */
1304 report2("\nRemoving aborted file: %s",outfile_name);
1305 unlink(outfile_name);
1306 /* make the cursor correct if we have another track */
1307 if(batch_track!=-1){
1308 batch_track++;
1309 cursor=cdda_track_firstsector(d,batch_track);
1310 paranoia_seek(p,cursor,SEEK_SET);
1311 offset_skip=sample_offset*4;
1312 offset_buffer_used=0;
1315 report("\n");
1318 paranoia_free(p);
1319 p=NULL;
1323 report("Done.\n\n");
1325 cdda_close(d);
1326 d=NULL;
1327 if(logfile && logfile != stdout)
1328 fclose(logfile);
1329 return 0;