Annotation of src/usr.bin/cdio/cdio.c, Revision 1.9
1.9 ! millert 1: /* $OpenBSD: cdio.c,v 1.8 1997/05/01 12:40:28 downsj Exp $ */
1.1 downsj 2: /*
3: * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
4: * Based on the non-X based CD player by Jean-Marc Zucconi and
5: * Andrey A. Chernov.
6: *
7: * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
8: *
9: * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
10: * A couple of further fixes to my own earlier "fixes".
11: *
12: * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
13: * Added an ability to specify addresses relative to the
14: * beginning of a track. This is in fact a variation of
15: * doing the simple play_msf() call.
16: *
17: * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
18: * New eject algorithm.
19: * Some code style reformatting.
20: *
21: * $FreeBSD: cdcontrol.c,v 1.13 1996/06/25 21:01:27 ache Exp $
22: */
23:
24: #include <ctype.h>
25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28: #include <unistd.h>
29: #include <util.h>
1.9 ! millert 30: #include <err.h>
1.1 downsj 31: #include <errno.h>
1.9 ! millert 32: #include <sys/param.h>
1.1 downsj 33: #include <sys/file.h>
34: #include <sys/cdio.h>
35: #include <sys/ioctl.h>
36:
37: #define VERSION "2.0"
38:
39: #define ASTS_INVALID 0x00 /* Audio status byte not valid */
40: #define ASTS_PLAYING 0x11 /* Audio play operation in progress */
41: #define ASTS_PAUSED 0x12 /* Audio play operation paused */
42: #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
43: #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
44: #define ASTS_VOID 0x15 /* No current audio status to return */
45:
46: #ifndef DEFAULT_CD_DRIVE
1.3 deraadt 47: # define DEFAULT_CD_DRIVE "cd0"
1.1 downsj 48: #endif
49:
50: #define CMD_DEBUG 1
1.9 ! millert 51: #define CMD_DEVICE 2
! 52: #define CMD_EJECT 3
! 53: #define CMD_HELP 4
! 54: #define CMD_INFO 5
! 55: #define CMD_PAUSE 6
! 56: #define CMD_PLAY 7
! 57: #define CMD_QUIT 8
! 58: #define CMD_RESUME 9
! 59: #define CMD_STOP 10
! 60: #define CMD_VOLUME 11
! 61: #define CMD_CLOSE 12
! 62: #define CMD_RESET 13
! 63: #define CMD_SET 14
! 64: #define CMD_STATUS 15
! 65: #define CMD_NEXT 16
! 66: #define CMD_PREV 17
! 67: #define CMD_REPLAY 18
1.1 downsj 68:
69: struct cmdtab {
70: int command;
71: char *name;
72: unsigned min;
73: char *args;
74: } cmdtab[] = {
75: { CMD_CLOSE, "close", 1, "" },
76: { CMD_DEBUG, "debug", 1, "on | off" },
1.9 ! millert 77: { CMD_DEVICE, "device", 1, "devname" },
1.1 downsj 78: { CMD_EJECT, "eject", 1, "" },
79: { CMD_HELP, "?", 1, 0 },
80: { CMD_HELP, "help", 1, "" },
81: { CMD_INFO, "info", 1, "" },
1.5 angelos 82: { CMD_NEXT, "next", 1, "" },
1.1 downsj 83: { CMD_PAUSE, "pause", 2, "" },
84: { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
85: { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
86: { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
87: { CMD_PLAY, "play", 1, "[#block [len]]" },
1.5 angelos 88: { CMD_PREV, "previous", 2, "" },
1.1 downsj 89: { CMD_QUIT, "quit", 1, "" },
90: { CMD_RESET, "reset", 4, "" },
91: { CMD_RESUME, "resume", 1, "" },
1.6 angelos 92: { CMD_REPLAY, "replay", 3, "" },
1.1 downsj 93: { CMD_SET, "set", 2, "msf | lba" },
94: { CMD_STATUS, "status", 1, "" },
95: { CMD_STOP, "stop", 3, "" },
96: { CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" },
97: { 0, }
98: };
99:
100: struct cd_toc_entry toc_buffer[100];
101:
102: char *cdname;
103: int fd = -1;
104: int verbose = 1;
105: int msf = 1;
106:
107: extern char *__progname;
108:
109: int setvol __P((int, int));
110: int read_toc_entrys __P((int));
111: int play_msf __P((int, int, int, int, int, int));
112: int play_track __P((int, int, int, int));
113: int get_vol __P((int *, int *));
114: int status __P((int *, int *, int *, int *));
1.9 ! millert 115: int open_cd __P((char *));
1.1 downsj 116: int play __P((char *arg));
117: int info __P((char *arg));
118: int pstatus __P((char *arg));
1.5 angelos 119: int play_next __P((char *arg));
120: int play_prev __P((char *arg));
1.6 angelos 121: int play_same __P((char *arg));
1.1 downsj 122: char *input __P((int *));
123: void prtrack __P((struct cd_toc_entry *e, int lastflag));
124: void lba2msf __P((unsigned long lba,
125: u_char *m, u_char *s, u_char *f));
126: unsigned int msf2lba __P((u_char m, u_char s, u_char f));
127: int play_blocks __P((int blk, int len));
128: int run __P((int cmd, char *arg));
129: char *parse __P((char *buf, int *cmd));
130:
131: void help ()
132: {
133: struct cmdtab *c;
134: char *s, n;
135: int i;
136:
137: for (c=cmdtab; c->name; ++c) {
138: if (! c->args)
139: continue;
140: printf("\t");
141: for (i = c->min, s = c->name; *s; s++, i--) {
142: if (i > 0)
143: n = toupper(*s);
144: else
145: n = *s;
146: putchar(n);
147: }
148: if (*c->args)
149: printf (" %s", c->args);
150: printf ("\n");
151: }
152: printf ("\n\tThe word \"play\" is not required for the play commands.\n");
153: printf ("\tThe plain target address is taken as a synonym for play.\n");
154: }
155:
156: void usage ()
157: {
158: printf ("Usage:\n\t%s [ -vs ] [ -f disc ] [ command args... ]\n", __progname);
159: printf ("Options:\n");
160: printf ("\t-v - verbose mode\n");
161: printf ("\t-s - silent mode\n");
162: printf ("\t-f disc - a block device name such as /dev/cd0c\n");
163: printf ("\tMUSIC_CD - shell variable with device name\n");
164: printf ("Commands:\n");
165: help ();
166: exit (1);
167: }
168:
169: int main (argc, argv)
170: int argc;
171: char **argv;
172: {
173: int cmd;
174: char *arg;
175:
176: cdname = getenv ("DISC");
177: if (! cdname)
178: cdname = getenv ("CDROM");
179:
180: for (;;) {
181: switch (getopt (argc, argv, "svhf:")) {
182: case EOF:
183: break;
184: case 's':
185: verbose = 0;
186: continue;
187: case 'v':
188: verbose = 2;
189: continue;
190: case 'f':
191: cdname = optarg;
192: continue;
193: case 'h':
194: default:
195: usage ();
196: }
197: break;
198: }
199: argc -= optind;
200: argv += optind;
201:
202: if (argc > 0 && ! strcasecmp (*argv, "help"))
203: usage ();
204:
205: if (! cdname) {
206: cdname = DEFAULT_CD_DRIVE;
207: fprintf (stderr,
208: "No CD device name specified. Defaulting to %s.\n", cdname);
209: }
210:
211: if (argc > 0) {
212: char buf[80], *p;
213: int len;
214:
215: for (p=buf; argc-->0; ++argv) {
216: len = strlen (*argv);
217:
218: if (p + len >= buf + sizeof (buf) - 1)
219: usage ();
220:
221: if (p > buf)
222: *p++ = ' ';
223:
224: strcpy (p, *argv);
225: p += len;
226: }
227: *p = 0;
228: arg = parse (buf, &cmd);
229: return (run (cmd, arg));
230: }
231:
232: if (verbose == 1)
233: verbose = isatty (0);
234:
235: if (verbose) {
236: printf ("Compact Disc Control utility, version %s\n", VERSION);
237: printf ("Type `?' for command list\n\n");
238: }
239:
240: for (;;) {
241: arg = input (&cmd);
242: if (run (cmd, arg) < 0) {
243: if (verbose)
1.9 ! millert 244: warn (NULL);
1.1 downsj 245: close (fd);
246: fd = -1;
247: }
248: fflush (stdout);
249: }
250: }
251:
252: int run (cmd, arg)
253: int cmd;
254: char *arg;
255: {
256: int l, r, rc;
1.9 ! millert 257: static char newcdname[MAXPATHLEN];
1.1 downsj 258:
259: switch (cmd) {
260:
261: case CMD_QUIT:
262: exit (0);
263:
264: case CMD_INFO:
1.9 ! millert 265: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 266: return (0);
267:
268: return info (arg);
269:
270: case CMD_STATUS:
1.9 ! millert 271: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 272: return (0);
273:
274: return pstatus (arg);
275:
276: case CMD_PAUSE:
1.9 ! millert 277: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 278: return (0);
279:
280: return ioctl (fd, CDIOCPAUSE);
281:
282: case CMD_RESUME:
1.9 ! millert 283: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 284: return (0);
285:
286: return ioctl (fd, CDIOCRESUME);
287:
288: case CMD_STOP:
1.9 ! millert 289: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 290: return (0);
291:
292: rc = ioctl (fd, CDIOCSTOP);
293:
294: (void) ioctl (fd, CDIOCALLOW);
295:
296: return (rc);
297:
298: case CMD_RESET:
1.9 ! millert 299: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 300: return (0);
301:
302: rc = ioctl (fd, CDIOCRESET);
303: if (rc < 0)
304: return rc;
305: close(fd);
306: fd = -1;
307: return (0);
308:
309: case CMD_DEBUG:
1.9 ! millert 310: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 311: return (0);
312:
313: if (! strcasecmp (arg, "on"))
314: return ioctl (fd, CDIOCSETDEBUG);
315:
316: if (! strcasecmp (arg, "off"))
317: return ioctl (fd, CDIOCCLRDEBUG);
318:
319: printf ("%s: Invalid command arguments\n", __progname);
320:
321: return (0);
322:
1.9 ! millert 323: case CMD_DEVICE:
! 324: /* close old device */
! 325: if (fd > -1) {
! 326: (void) ioctl (fd, CDIOCALLOW);
! 327: close(fd);
! 328: fd = -1;
! 329: }
! 330:
! 331: /* open new device */
! 332: if (!open_cd (arg))
! 333: return (0);
! 334: (void) strncpy(newcdname, arg, sizeof(newcdname) - 1);
! 335: newcdname[sizeof(newcdname) - 1] = '\0';
! 336: cdname = newcdname;
! 337: return (1);
! 338:
1.1 downsj 339: case CMD_EJECT:
1.9 ! millert 340: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 341: return (0);
342:
343: (void) ioctl (fd, CDIOCALLOW);
344: rc = ioctl (fd, CDIOCEJECT);
345: if (rc < 0)
346: return (rc);
347: #if defined(__OpenBSD__)
348: close(fd);
349: fd = -1;
350: #endif
351: return (0);
352:
353: case CMD_CLOSE:
354: #if defined(CDIOCCLOSE)
1.9 ! millert 355: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 356: return (0);
357:
358: (void) ioctl (fd, CDIOCALLOW);
359: rc = ioctl (fd, CDIOCCLOSE);
360: if (rc < 0)
361: return (rc);
362: close(fd);
363: fd = -1;
364: return (0);
365: #else
366: printf ("%s: Command not yet supported\n", __progname);
367: return (0);
368: #endif
369:
370: case CMD_PLAY:
1.9 ! millert 371: if (fd < 0 && ! open_cd (cdname))
1.1 downsj 372: return (0);
373:
374: while (isspace (*arg))
375: arg++;
376:
377: return play (arg);
378:
379: case CMD_SET:
380: if (! strcasecmp (arg, "msf"))
381: msf = 1;
382: else if (! strcasecmp (arg, "lba"))
383: msf = 0;
384: else
385: printf ("%s: Invalid command arguments\n", __progname);
386: return (0);
387:
388: case CMD_VOLUME:
1.9 ! millert 389: if (fd < 0 && !open_cd (cdname))
1.1 downsj 390: return (0);
391:
392: if (! strncasecmp (arg, "left", strlen(arg)))
393: return ioctl (fd, CDIOCSETLEFT);
394:
395: if (! strncasecmp (arg, "right", strlen(arg)))
396: return ioctl (fd, CDIOCSETRIGHT);
397:
398: if (! strncasecmp (arg, "mono", strlen(arg)))
399: return ioctl (fd, CDIOCSETMONO);
400:
401: if (! strncasecmp (arg, "stereo", strlen(arg)))
1.8 downsj 402: return ioctl (fd, CDIOCSETSTEREO);
1.1 downsj 403:
404: if (! strncasecmp (arg, "mute", strlen(arg)))
405: return ioctl (fd, CDIOCSETMUTE);
406:
407: if (2 != sscanf (arg, "%d %d", &l, &r)) {
408: printf ("%s: Invalid command arguments\n", __progname);
409: return (0);
410: }
411:
412: return setvol (l, r);
413:
1.5 angelos 414: case CMD_NEXT:
1.9 ! millert 415: if (fd < 0 && ! open_cd (cdname))
1.5 angelos 416: return (0);
417:
418: return play_next (arg);
419:
420: case CMD_PREV:
1.9 ! millert 421: if (fd < 0 && ! open_cd (cdname))
1.5 angelos 422: return (0);
423:
424: return play_prev (arg);
425:
1.6 angelos 426: case CMD_REPLAY:
1.9 ! millert 427: if (fd < 0 && ! open_cd (cdname))
1.6 angelos 428: return 0;
429:
430: return play_same (arg);
1.1 downsj 431: default:
432: case CMD_HELP:
433: help ();
434: return (0);
435:
436: }
437: }
438:
439: int play (arg)
440: char *arg;
441: {
442: struct ioc_toc_header h;
443: int rc, n, start, end = 0, istart = 1, iend = 1;
444:
445: rc = ioctl (fd, CDIOREADTOCHEADER, &h);
446:
447: if (rc < 0)
448: return (rc);
449:
450: n = h.ending_track - h.starting_track + 1;
451: rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
452:
453: if (rc < 0)
454: return (rc);
455:
456: if (! arg || ! *arg) {
457: /* Play the whole disc */
458: if (msf)
1.4 niklas 459: return
460: play_blocks (msf2lba (toc_buffer[0].addr.msf.minute,
461: toc_buffer[0].addr.msf.second,
462: toc_buffer[0].addr.msf.frame),
463: msf2lba (toc_buffer[n].addr.msf.minute,
464: toc_buffer[n].addr.msf.second,
465: toc_buffer[n].addr.msf.frame));
1.1 downsj 466: else
1.4 niklas 467: return play_blocks (ntohl (toc_buffer[0].addr.lba),
468: ntohl (toc_buffer[n].addr.lba));
1.1 downsj 469: }
470:
471: if (strchr (arg, '#')) {
472: /* Play block #blk [ len ] */
473: int blk, len = 0;
474:
475: if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
476: 1 != sscanf (arg, "#%d", &blk))
477: goto Clean_up;
478:
479: if (len == 0) {
480: if (msf)
481: len = msf2lba (toc_buffer[n].addr.msf.minute,
482: toc_buffer[n].addr.msf.second,
483: toc_buffer[n].addr.msf.frame) - blk;
484: else
485: len = ntohl(toc_buffer[n].addr.lba) - blk;
486: }
487: return play_blocks (blk, len);
488: }
489:
490: if (strchr (arg, ':')) {
491: /*
492: * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
493: *
494: * Will now also undestand timed addresses relative
495: * to the beginning of a track in the form...
496: *
497: * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
498: */
499: unsigned tr1, tr2;
500: unsigned m1, m2, s1, s2, f1, f2;
501: unsigned char tm, ts, tf;
502:
503: tr2 = m2 = s2 = f2 = f1 = 0;
504: if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
505: &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
506: goto Play_Relative_Addresses;
507:
508: tr2 = m2 = s2 = f2 = f1 = 0;
509: if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
510: &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
511: goto Play_Relative_Addresses;
512:
513: tr2 = m2 = s2 = f2 = f1 = 0;
514: if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
515: &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
516: goto Play_Relative_Addresses;
517:
518: tr2 = m2 = s2 = f2 = f1 = 0;
519: if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
520: &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
521: goto Play_Relative_Addresses;
522:
523: tr2 = m2 = s2 = f2 = f1 = 0;
524: if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
525: &tr1, &m1, &s1, &f1, &m2, &s2))
526: goto Play_Relative_Addresses;
527:
528: tr2 = m2 = s2 = f2 = f1 = 0;
529: if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
530: &tr1, &m1, &s1, &m2, &s2, &f2))
531: goto Play_Relative_Addresses;
532:
533: tr2 = m2 = s2 = f2 = f1 = 0;
534: if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
535: &tr1, &m1, &s1, &f1, &tr2, &m2))
536: goto Play_Relative_Addresses;
537:
538: tr2 = m2 = s2 = f2 = f1 = 0;
539: if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
540: goto Play_Relative_Addresses;
541:
542: tr2 = m2 = s2 = f2 = f1 = 0;
543: if (5 == sscanf (arg, "%d %d:%d %d %d",
544: &tr1, &m1, &s1, &tr2, &m2))
545: goto Play_Relative_Addresses;
546:
547: tr2 = m2 = s2 = f2 = f1 = 0;
548: if (5 == sscanf (arg, "%d %d:%d.%d %d",
549: &tr1, &m1, &s1, &f1, &tr2))
550: goto Play_Relative_Addresses;
551:
552: tr2 = m2 = s2 = f2 = f1 = 0;
553: if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
554: goto Play_Relative_Addresses;
555:
556: tr2 = m2 = s2 = f2 = f1 = 0;
557: if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
558: goto Play_Relative_Addresses;
559:
560: tr2 = m2 = s2 = f2 = f1 = 0;
561: if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
562: goto Play_Relative_Addresses;
563:
564: tr2 = m2 = s2 = f2 = f1 = 0;
565: goto Try_Absolute_Timed_Addresses;
566:
567: Play_Relative_Addresses:
568: if (! tr1)
569: tr1 = 1;
570: else if (tr1 > n)
571: tr1 = n;
572:
573: if (msf) {
574: tm = toc_buffer[tr1].addr.msf.minute;
575: ts = toc_buffer[tr1].addr.msf.second;
576: tf = toc_buffer[tr1].addr.msf.frame;
577: } else
578: lba2msf(ntohl(toc_buffer[tr1].addr.lba),
579: &tm, &ts, &tf);
580: if ((m1 > tm)
581: || ((m1 == tm)
582: && ((s1 > ts)
583: || ((s1 == ts)
584: && (f1 > tf))))) {
585: printf ("Track %d is not that long.\n", tr1);
586: return (0);
587: }
588:
589: tr1--;
590:
591: f1 += tf;
592: if (f1 >= 75) {
593: s1 += f1 / 75;
594: f1 %= 75;
595: }
596:
597: s1 += ts;
598: if (s1 >= 60) {
599: m1 += s1 / 60;
600: s1 %= 60;
601: }
602:
603: m1 += tm;
604:
605: if (! tr2) {
606: if (m2 || s2 || f2) {
607: tr2 = tr1;
608: f2 += f1;
609: if (f2 >= 75) {
610: s2 += f2 / 75;
611: f2 %= 75;
612: }
613:
614: s2 += s1;
615: if (s2 > 60) {
616: m2 += s2 / 60;
617: s2 %= 60;
618: }
619:
620: m2 += m1;
621: } else {
622: tr2 = n;
623: if (msf) {
624: m2 = toc_buffer[n].addr.msf.minute;
625: s2 = toc_buffer[n].addr.msf.second;
626: f2 = toc_buffer[n].addr.msf.frame;
627: } else {
628: lba2msf(ntohl(toc_buffer[n].addr.lba),
629: &tm, &ts, &tf);
630: m2 = tm;
631: s2 = ts;
632: f2 = tf;
633: }
634: }
635: } else if (tr2 > n) {
636: tr2 = n;
637: m2 = s2 = f2 = 0;
638: } else {
639: if (m2 || s2 || f2)
640: tr2--;
641: if (msf) {
642: tm = toc_buffer[tr2].addr.msf.minute;
643: ts = toc_buffer[tr2].addr.msf.second;
644: tf = toc_buffer[tr2].addr.msf.frame;
645: } else
646: lba2msf(ntohl(toc_buffer[tr2].addr.lba),
647: &tm, &ts, &tf);
648: f2 += tf;
649: if (f2 >= 75) {
650: s2 += f2 / 75;
651: f2 %= 75;
652: }
653:
654: s2 += ts;
655: if (s2 > 60) {
656: m2 += s2 / 60;
657: s2 %= 60;
658: }
659:
660: m2 += tm;
661: }
662:
663: if (msf) {
664: tm = toc_buffer[n].addr.msf.minute;
665: ts = toc_buffer[n].addr.msf.second;
666: tf = toc_buffer[n].addr.msf.frame;
667: } else
668: lba2msf(ntohl(toc_buffer[n].addr.lba),
669: &tm, &ts, &tf);
670: if ((tr2 < n)
671: && ((m2 > tm)
672: || ((m2 == tm)
673: && ((s2 > ts)
674: || ((s2 == ts)
675: && (f2 > tf)))))) {
676: printf ("The playing time of the disc is not that long.\n");
677: return (0);
678: }
679: return (play_msf (m1, s1, f1, m2, s2, f2));
680:
681: Try_Absolute_Timed_Addresses:
682: if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
683: &m1, &s1, &f1, &m2, &s2, &f2) &&
684: 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
685: 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
686: 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
687: 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
688: 2 != sscanf (arg, "%d:%d", &m1, &s1))
689: goto Clean_up;
690:
691: if (m2 == 0) {
692: if (msf) {
693: m2 = toc_buffer[n].addr.msf.minute;
694: s2 = toc_buffer[n].addr.msf.second;
695: f2 = toc_buffer[n].addr.msf.frame;
696: } else {
697: lba2msf(ntohl(toc_buffer[n].addr.lba),
698: &tm, &ts, &tf);
699: m2 = tm;
700: s2 = ts;
701: f2 = tf;
702: }
703: }
704: return play_msf (m1, s1, f1, m2, s2, f2);
705: }
706:
707: /*
708: * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
709: */
710: if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
711: 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
712: 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
713: 2 != sscanf (arg, "%d.%d", &start, &istart) &&
714: 2 != sscanf (arg, "%d%d", &start, &end) &&
715: 1 != sscanf (arg, "%d", &start))
716: goto Clean_up;
717:
718: if (end == 0)
719: end = n;
720: return (play_track (start, istart, end, iend));
721:
722: Clean_up:
723: printf ("%s: Invalid command arguments\n", __progname);
1.5 angelos 724: return (0);
725: }
726:
727: int play_prev (arg)
728: char *arg;
729: {
730: int trk, min, sec, frm, rc;
731: struct ioc_toc_header h;
732:
733: if (status (&trk, &min, &sec, &frm) >= 0)
734: {
735: trk--;
736:
737: rc = ioctl (fd, CDIOREADTOCHEADER, &h);
738: if (rc < 0)
739: {
1.9 ! millert 740: warn ("getting toc header");
1.5 angelos 741: return (rc);
742: }
743:
744: if (trk < h.starting_track)
745: return play_track (h.starting_track, 1,
1.7 angelos 746: h.ending_track + 1, 1);
1.6 angelos 747:
1.7 angelos 748: return play_track (trk, 1, h.ending_track, 1);
1.6 angelos 749: }
750:
751: return (0);
752: }
753:
754: int play_same (arg)
755: char *arg;
756: {
757: int trk, min, sec, frm, rc;
758: struct ioc_toc_header h;
759:
760: if (status (&trk, &min, &sec, &frm) >= 0)
761: {
762: rc = ioctl (fd, CDIOREADTOCHEADER, &h);
763: if (rc < 0)
764: {
1.9 ! millert 765: warn ("getting toc header");
1.6 angelos 766: return (rc);
767: }
1.5 angelos 768:
1.7 angelos 769: return play_track (trk, 1, h.ending_track, 1);
1.5 angelos 770: }
771:
772: return (0);
773: }
774:
775: int play_next (arg)
776: char *arg;
777: {
778: int trk, min, sec, frm, rc;
779: struct ioc_toc_header h;
780:
781: if (status (&trk, &min, &sec, &frm) >= 0)
782: {
783: trk++;
784: rc = ioctl (fd, CDIOREADTOCHEADER, &h);
785: if (rc < 0)
786: {
1.9 ! millert 787: warn ("getting toc header");
1.5 angelos 788: return (rc);
789: }
790:
791: if (trk > h.ending_track)
792: {
793: printf("%s: end of CD\n", __progname);
794:
795: rc = ioctl (fd, CDIOCSTOP);
796:
797: (void) ioctl (fd, CDIOCALLOW);
798:
799: return (rc);
800: }
801:
1.7 angelos 802: return play_track (trk, 1, h.ending_track, 1);
1.5 angelos 803: }
804:
1.1 downsj 805: return (0);
806: }
807:
808: char *strstatus (sts)
809: int sts;
810: {
811: switch (sts) {
812: case ASTS_INVALID: return ("invalid");
813: case ASTS_PLAYING: return ("playing");
814: case ASTS_PAUSED: return ("paused");
815: case ASTS_COMPLETED: return ("completed");
816: case ASTS_ERROR: return ("error");
817: case ASTS_VOID: return ("void");
818: default: return ("??");
819: }
820: }
821:
822: int pstatus (arg)
823: char *arg;
824: {
825: struct ioc_vol v;
826: struct ioc_read_subchannel ss;
827: struct cd_sub_channel_info data;
828: int rc, trk, m, s, f;
829:
830: rc = status (&trk, &m, &s, &f);
1.9 ! millert 831: if (rc >= 0) {
1.1 downsj 832: if (verbose)
833: printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
834: rc, strstatus (rc), trk, m, s, f);
835: else
836: printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
1.9 ! millert 837: } else
1.1 downsj 838: printf ("No current status info available\n");
839:
840: bzero (&ss, sizeof (ss));
841: ss.data = &data;
842: ss.data_len = sizeof (data);
843: ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
844: ss.data_format = CD_MEDIA_CATALOG;
845: rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
846: if (rc >= 0) {
847: printf("Media catalog is %sactive",
848: ss.data->what.media_catalog.mc_valid ? "": "in");
849: if (ss.data->what.media_catalog.mc_valid &&
850: ss.data->what.media_catalog.mc_number[0])
851: printf(", number \"%.15s\"",
852: ss.data->what.media_catalog.mc_number);
853: putchar('\n');
854: } else
855: printf("No media catalog info available\n");
856:
857: rc = ioctl (fd, CDIOCGETVOL, &v);
1.9 ! millert 858: if (rc >= 0) {
1.1 downsj 859: if (verbose)
860: printf ("Left volume = %d, right volume = %d\n",
861: v.vol[0], v.vol[1]);
862: else
863: printf ("%d %d\n", v.vol[0], v.vol[1]);
1.9 ! millert 864: } else
1.1 downsj 865: printf ("No volume level info available\n");
866: return(0);
867: }
868:
869: int info (arg)
870: char *arg;
871: {
872: struct ioc_toc_header h;
873: int rc, i, n;
874:
875: rc = ioctl (fd, CDIOREADTOCHEADER, &h);
876: if (rc >= 0) {
877: if (verbose)
878: printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
879: h.starting_track, h.ending_track, h.len);
880: else
881: printf ("%d %d %d\n", h.starting_track,
882: h.ending_track, h.len);
883: } else {
1.9 ! millert 884: warn ("getting toc header");
1.1 downsj 885: return (rc);
886: }
887:
888: n = h.ending_track - h.starting_track + 1;
889: rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
890: if (rc < 0)
891: return (rc);
892:
893: if (verbose) {
894: printf ("track start duration block length type\n");
895: printf ("-------------------------------------------------\n");
896: }
897:
898: for (i = 0; i < n; i++) {
899: printf ("%5d ", toc_buffer[i].track);
900: prtrack (toc_buffer + i, 0);
901: }
902: printf ("%5d ", toc_buffer[n].track);
903: prtrack (toc_buffer + n, 1);
904: return (0);
905: }
906:
907: void lba2msf (lba, m, s, f)
908: unsigned long lba;
909: u_char *m;
910: u_char *s;
911: u_char *f;
912: {
913: lba += 150; /* block start offset */
914: lba &= 0xffffff; /* negative lbas use only 24 bits */
915: *m = lba / (60 * 75);
916: lba %= (60 * 75);
917: *s = lba / 75;
918: *f = lba % 75;
919: }
920:
921: unsigned int msf2lba (m, s, f)
922: u_char m;
923: u_char s;
924: u_char f;
925: {
926: return (((m * 60) + s) * 75 + f) - 150;
927: }
928:
929: void prtrack (e, lastflag)
930: struct cd_toc_entry *e;
931: int lastflag;
932: {
933: int block, next, len;
934: u_char m, s, f;
935:
936: if (msf) {
937: /* Print track start */
938: printf ("%2d:%02d.%02d ", e->addr.msf.minute,
939: e->addr.msf.second, e->addr.msf.frame);
940:
941: block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
942: e->addr.msf.frame);
943: } else {
944: block = ntohl(e->addr.lba);
945: lba2msf(block, &m, &s, &f);
946: /* Print track start */
947: printf ("%2d:%02d.%02d ", m, s, f);
948: }
949: if (lastflag) {
950: /* Last track -- print block */
951: printf (" - %6d - -\n", block);
952: return;
953: }
954:
955: if (msf)
956: next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
957: e[1].addr.msf.frame);
958: else
959: next = ntohl(e[1].addr.lba);
960: len = next - block;
961: lba2msf (len, &m, &s, &f);
962:
963: /* Print duration, block, length, type */
964: printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
965: (e->control & 4) ? "data" : "audio");
966: }
967:
968: int play_track (tstart, istart, tend, iend)
969: int tstart;
970: int istart;
971: int tend;
972: int iend;
973: {
974: struct ioc_play_track t;
975:
976: t.start_track = tstart;
977: t.start_index = istart;
978: t.end_track = tend;
979: t.end_index = iend;
980:
981: return ioctl (fd, CDIOCPLAYTRACKS, &t);
982: }
983:
984: int play_blocks (blk, len)
985: int blk;
986: int len;
987: {
988: struct ioc_play_blocks t;
989:
990: t.blk = blk;
991: t.len = len;
992:
993: return ioctl (fd, CDIOCPLAYBLOCKS, &t);
994: }
995:
996: int setvol (left, right)
997: int left;
998: int right;
999: {
1000: struct ioc_vol v;
1001:
1002: v.vol[0] = left;
1003: v.vol[1] = right;
1004: v.vol[2] = 0;
1005: v.vol[3] = 0;
1006:
1007: return ioctl (fd, CDIOCSETVOL, &v);
1008: }
1009:
1010: int read_toc_entrys (len)
1011: int len;
1012: {
1013: struct ioc_read_toc_entry t;
1014:
1015: t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1016: t.starting_track = 0;
1017: t.data_len = len;
1018: t.data = toc_buffer;
1019:
1020: return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
1021: }
1022:
1023: int play_msf (start_m, start_s, start_f, end_m, end_s, end_f)
1024: int start_m;
1025: int start_s;
1026: int start_f;
1027: int end_m;
1028: int end_s;
1029: int end_f;
1030: {
1031: struct ioc_play_msf a;
1032:
1033: a.start_m = start_m;
1034: a.start_s = start_s;
1035: a.start_f = start_f;
1036: a.end_m = end_m;
1037: a.end_s = end_s;
1038: a.end_f = end_f;
1039:
1040: return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
1041: }
1042:
1043: int status (trk, min, sec, frame)
1044: int *trk;
1045: int *min;
1046: int *sec;
1047: int *frame;
1048: {
1049: struct ioc_read_subchannel s;
1050: struct cd_sub_channel_info data;
1051: u_char mm, ss, ff;
1052:
1053: bzero (&s, sizeof (s));
1054: s.data = &data;
1055: s.data_len = sizeof (data);
1056: s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1057: s.data_format = CD_CURRENT_POSITION;
1058:
1059: if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
1060: return -1;
1061:
1062: *trk = s.data->what.position.track_number;
1063: if (msf) {
1064: *min = s.data->what.position.reladdr.msf.minute;
1065: *sec = s.data->what.position.reladdr.msf.second;
1066: *frame = s.data->what.position.reladdr.msf.frame;
1067: } else {
1068: lba2msf(ntohl(s.data->what.position.reladdr.lba),
1069: &mm, &ss, &ff);
1070: *min = mm;
1071: *sec = ss;
1072: *frame = ff;
1073: }
1074:
1075: return s.data->header.audio_status;
1076: }
1077:
1078: char *input (cmd)
1079: int *cmd;
1080: {
1081: static char buf[80];
1082: char *p;
1083:
1084: do {
1085: if (verbose)
1086: fprintf (stderr, "%s> ", __progname);
1087: if (! fgets (buf, sizeof (buf), stdin)) {
1088: *cmd = CMD_QUIT;
1089: fprintf (stderr, "\r\n");
1090: return (0);
1091: }
1092: p = parse (buf, cmd);
1093: } while (! p);
1094: return (p);
1095: }
1096:
1097: char *parse (buf, cmd)
1098: char *buf;
1099: int *cmd;
1100: {
1101: struct cmdtab *c;
1102: char *p;
1103: int len;
1104:
1105: for (p=buf; isspace (*p); p++)
1106: continue;
1107:
1108: if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
1109: *cmd = CMD_PLAY;
1110: return (p);
1111: }
1112:
1113: for (buf = p; *p && ! isspace (*p); p++)
1114: continue;
1115:
1116: len = p - buf;
1117: if (! len)
1118: return (0);
1119:
1120: if (*p) { /* It must be a spacing character! */
1121: char *q;
1122:
1123: *p++ = 0;
1124: for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1125: continue;
1126: *q = 0;
1127: }
1128:
1129: *cmd = -1;
1130: for (c=cmdtab; c->name; ++c) {
1131: /* Is it an exact match? */
1132: if (! strcasecmp (buf, c->name)) {
1133: *cmd = c->command;
1134: break;
1135: }
1136:
1137: /* Try short hand forms then... */
1138: if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
1139: if (*cmd != -1 && *cmd != c->command) {
1140: fprintf (stderr, "Ambiguous command\n");
1141: return (0);
1142: }
1143: *cmd = c->command;
1144: }
1145: }
1146:
1147: if (*cmd == -1) {
1148: fprintf (stderr, "%s: Invalid command, enter ``help'' for commands.\n",
1149: __progname);
1150: return (0);
1151: }
1152:
1153: while (isspace (*p))
1154: p++;
1155: return p;
1156: }
1157:
1.9 ! millert 1158: int open_cd (dev)
! 1159: char *dev;
1.1 downsj 1160: {
1.9 ! millert 1161: char *realdev;
1.1 downsj 1162:
1163: if (fd > -1)
1164: return (1);
1165:
1.9 ! millert 1166: fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev);
1.1 downsj 1167: if (fd < 0) {
1168: if ((errno == ENXIO) || (errno == EIO)) {
1169: /* ENXIO has an overloaded meaning here.
1170: * The original "Device not configured" should
1171: * be interpreted as "No disc in drive %s". */
1.9 ! millert 1172: warnx ("No disc in drive %s.", realdev);
1.1 downsj 1173: return (0);
1174: }
1.9 ! millert 1175: warn ("Can't open %s", realdev);
! 1176: return (0);
1.1 downsj 1177: }
1178: return (1);
1179: }