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