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