cdplay.c revision 1.11 1 /* $NetBSD: cdplay.c,v 1.11 2000/12/16 01:36:00 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1999 Andrew Doran.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 /*
31 * Compact Disc Control Utility by Serge V. Vakulenko <vak (at) cronyx.ru>.
32 * Based on the non-X based CD player by Jean-Marc Zucconi and
33 * Andrey A. Chernov.
34 *
35 * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau (at) funet.fi>.
36 *
37 * 11-Sep-1995: Jukka A. Ukkonen <jau (at) funet.fi>
38 * A couple of further fixes to my own earlier "fixes".
39 *
40 * 18-Sep-1995: Jukka A. Ukkonen <jau (at) funet.fi>
41 * Added an ability to specify addresses relative to the
42 * beginning of a track. This is in fact a variation of
43 * doing the simple play_msf() call.
44 *
45 * 11-Oct-1995: Serge V.Vakulenko <vak (at) cronyx.ru>
46 * New eject algorithm.
47 * Some code style reformatting.
48 *
49 * from FreeBSD: cdcontrol.c,v 1.17.2.1 1999/01/31 15:36:01 billf Exp
50 */
51
52 /*
53 * XXX there are too many opportunities to trash the stack from the command
54 * line - ad
55 */
56
57 #include <sys/cdefs.h>
58 #ifndef lint
59 __RCSID("$NetBSD: cdplay.c,v 1.11 2000/12/16 01:36:00 lukem Exp $");
60 #endif /* not lint */
61
62 #include <sys/endian.h>
63 #include <sys/ioctl.h>
64 #include <sys/file.h>
65 #include <sys/cdio.h>
66
67 #include <ctype.h>
68 #include <err.h>
69 #include <errno.h>
70 #include <histedit.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
75 #include <util.h>
76
77 #include <machine/disklabel.h>
78
79 #define ASTS_INVALID 0x00 /* Audio status byte not valid */
80 #define ASTS_PLAYING 0x11 /* Audio play operation in progress */
81 #define ASTS_PAUSED 0x12 /* Audio play operation paused */
82 #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
83 #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
84 #define ASTS_VOID 0x15 /* No current audio status to return */
85
86 #define CMD_DEBUG 1
87 #define CMD_EJECT 2
88 #define CMD_HELP 3
89 #define CMD_INFO 4
90 #define CMD_PAUSE 5
91 #define CMD_PLAY 6
92 #define CMD_QUIT 7
93 #define CMD_RESUME 8
94 #define CMD_STOP 9
95 #define CMD_VOLUME 10
96 #define CMD_CLOSE 11
97 #define CMD_RESET 12
98 #define CMD_SET 13
99 #define CMD_STATUS 14
100 #define STATUS_AUDIO 0x1
101 #define STATUS_MEDIA 0x2
102 #define STATUS_VOLUME 0x4
103
104 struct cmdtab {
105 int command;
106 char *name;
107 unsigned min;
108 char *args;
109 } cmdtab[] = {
110 { CMD_CLOSE, "close", 1, "" },
111 { CMD_EJECT, "eject", 1, "" },
112 { CMD_HELP, "?", 1, 0 },
113 { CMD_HELP, "help", 1, "" },
114 { CMD_INFO, "info", 1, "" },
115 { CMD_PAUSE, "pause", 2, "" },
116 { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
117 { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
118 { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
119 { CMD_PLAY, "play", 1, "[#block [len]]" },
120 { CMD_QUIT, "quit", 1, "" },
121 { CMD_RESET, "reset", 4, "" },
122 { CMD_RESUME, "resume", 1, "" },
123 { CMD_SET, "set", 2, "msf | lba" },
124 { CMD_STATUS, "status", 1, "[audio | media | volume]" },
125 { CMD_STOP, "stop", 3, "" },
126 { CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" },
127 { 0, }
128 };
129
130 struct cd_toc_entry toc_buffer[100];
131
132 const char *cdname;
133 int fd = -1;
134 int msf = 1;
135
136 /* for histedit */
137 extern char *__progname; /* from crt0.o */
138 History *hist;
139 HistEvent he;
140 EditLine *elptr;
141
142 int setvol __P((int, int));
143 int read_toc_entrys __P((int));
144 int play_msf __P((int, int, int, int, int, int));
145 int play_track __P((int, int, int, int));
146 int get_vol __P((int *, int *));
147 int status __P((int *, int *, int *, int *));
148 int opencd __P((void));
149 int play __P((char *));
150 int info __P((char *));
151 int pstatus __P((char *));
152 char *prompt __P((void));
153 void prtrack __P((struct cd_toc_entry *, int));
154 void lba2msf __P((u_long, u_char *, u_char *, u_char *));
155 u_int msf2lba __P((u_char, u_char, u_char));
156 int play_blocks __P((int, int));
157 int run __P((int, char *));
158 char *parse __P((char *, int *));
159 void help __P((void));
160 void usage __P((void));
161 char *strstatus __P((int));
162 int main __P((int, char **));
163
164 void
165 help()
166 {
167 struct cmdtab *c;
168 char *s, n;
169 int i;
170
171 for (c = cmdtab; c->name; ++c) {
172 if (!c->args)
173 continue;
174 printf("\t");
175 for (i = c->min, s = c->name; *s; s++, i--) {
176 if (i > 0)
177 n = toupper(*s);
178 else
179 n = *s;
180 putchar(n);
181 }
182 if (*c->args)
183 printf(" %s", c->args);
184 printf("\n");
185 }
186 printf("\n\tThe word \"play\" is not required for the play commands.\n");
187 printf("\tThe plain target address is taken as a synonym for play.\n");
188 }
189
190 void
191 usage()
192 {
193
194 fprintf(stderr, "usage: cdplay [-f device] [command ...]\n");
195 exit(1);
196 }
197
198 int
199 main(argc, argv)
200 int argc;
201 char **argv;
202 {
203 char *arg, *p, buf[80];
204 static char defdev[16];
205 int cmd, len;
206 char *line;
207 const char *elline;
208 int scratch;
209
210 cdname = getenv("MUSIC_CD");
211 if (!cdname)
212 cdname = getenv("CD_DRIVE");
213 if (!cdname)
214 cdname = getenv("DISC");
215 if (!cdname)
216 cdname = getenv("CDPLAY");
217
218 for (;;) {
219 switch (getopt(argc, argv, "svhf:")) {
220 case EOF:
221 break;
222 case 'f':
223 cdname = optarg;
224 continue;
225 case 'h':
226 default:
227 usage();
228 }
229 break;
230 }
231 argc -= optind;
232 argv += optind;
233
234 if (argc > 0 && !strcasecmp(*argv, "help"))
235 usage();
236
237 if (!cdname) {
238 sprintf(defdev, "cd0%c", 'a' + RAW_PART);
239 cdname = defdev;
240 }
241
242 opencd();
243
244 if (argc > 0) {
245 for (p = buf; argc-- > 0; ++argv) {
246 len = strlen(*argv);
247
248 if (p + len >= buf + sizeof(buf) - 1)
249 usage();
250
251 if (p > buf)
252 *p++ = ' ';
253
254 strcpy(p, *argv);
255 p += len;
256 }
257 *p = 0;
258 arg = parse(buf, &cmd);
259 return (run(cmd, arg));
260 }
261
262 printf("Type `?' for command list\n\n");
263
264 hist = history_init();
265 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */
266 elptr = el_init(__progname, stdin, stdout, stderr);
267 el_set(elptr, EL_EDITOR, "emacs");
268 el_set(elptr, EL_PROMPT, prompt);
269 el_set(elptr, EL_HIST, history, hist);
270 el_source(elptr, NULL);
271
272 for (;;) {
273 line = NULL;
274 do {
275 if (((elline = el_gets(elptr, &scratch)) != NULL)
276 && (scratch != 0)){
277 history(hist, &he, H_ENTER, elline);
278 line = strdup(elline);
279 arg = parse(line, &cmd);
280 } else {
281 cmd = CMD_QUIT;
282 fprintf(stderr, "\r\n");
283 arg = 0;
284 break;
285 }
286 } while (!arg);
287
288 if (run(cmd, arg) < 0) {
289 warn(NULL);
290 close(fd);
291 fd = -1;
292 }
293 fflush(stdout);
294 if (line != NULL)
295 free(line);
296 }
297 el_end(elptr);
298 history_end(hist);
299 }
300
301 int
302 run(cmd, arg)
303 int cmd;
304 char *arg;
305 {
306 int l, r, rc;
307
308 switch (cmd) {
309 case CMD_QUIT:
310 exit(0);
311
312 case CMD_CLOSE:
313 if (fd >= 0) {
314 ioctl(fd, CDIOCALLOW);
315 if ((rc = ioctl(fd, CDIOCCLOSE)) < 0)
316 return (rc);
317 close(fd);
318 fd = -1;
319 return (0);
320 }
321 break;
322 }
323
324 if (fd < 0 && !opencd())
325 return (0);
326
327 switch (cmd) {
328 case CMD_INFO:
329 return (info(arg));
330
331 case CMD_STATUS:
332 return (pstatus(arg));
333
334 case CMD_PAUSE:
335 return (ioctl(fd, CDIOCPAUSE));
336
337 case CMD_RESUME:
338 return (ioctl(fd, CDIOCRESUME));
339
340 case CMD_STOP:
341 rc = ioctl(fd, CDIOCSTOP);
342 ioctl(fd, CDIOCALLOW);
343 return (rc);
344
345 case CMD_RESET:
346 if ((rc = ioctl(fd, CDIOCRESET)) < 0)
347 return (rc);
348 close(fd);
349 fd = -1;
350 return (0);
351
352 case CMD_EJECT:
353 ioctl(fd, CDIOCALLOW);
354 if ((rc = ioctl(fd, CDIOCEJECT)) < 0)
355 return (rc);
356 return (0);
357
358 case CMD_CLOSE:
359 ioctl(fd, CDIOCALLOW);
360 if ((rc = ioctl(fd, CDIOCCLOSE)) < 0)
361 return (rc);
362 close(fd);
363 fd = -1;
364 return (0);
365
366 case CMD_PLAY:
367 while (isspace(*arg))
368 arg++;
369 return (play(arg));
370
371 case CMD_SET:
372 if (!strcasecmp(arg, "msf"))
373 msf = 1;
374 else if (!strcasecmp(arg, "lba"))
375 msf = 0;
376 else
377 warnx("invalid command arguments");
378 return (0);
379
380 case CMD_VOLUME:
381 if (!strncasecmp(arg, "left", strlen(arg)))
382 return (ioctl(fd, CDIOCSETLEFT));
383
384 if (!strncasecmp(arg, "right", strlen(arg)))
385 return (ioctl(fd, CDIOCSETRIGHT));
386
387 if (!strncasecmp(arg, "mono", strlen(arg)))
388 return (ioctl(fd, CDIOCSETMONO));
389
390 if (!strncasecmp(arg, "stereo", strlen(arg)))
391 return (ioctl(fd, CDIOCSETSTEREO));
392
393 if (!strncasecmp(arg, "mute", strlen(arg)))
394 return (ioctl(fd, CDIOCSETMUTE));
395
396 if (sscanf(arg, "%d %d", &l, &r) != 2) {
397 if (sscanf(arg, "%d", &l) == 1) {
398 r = l;
399 } else {
400 warnx("invalid command arguments");
401 return (0);
402 }
403 }
404 return (setvol(l, r));
405
406 case CMD_HELP:
407 default:
408 help();
409 return (0);
410
411 }
412 }
413
414 int
415 play(arg)
416 char *arg;
417 {
418 int rc, n, start, end, istart, iend, blk, len;
419 struct ioc_toc_header h;
420
421 if ((rc = ioctl(fd, CDIOREADTOCHEADER, &h)) < 0)
422 return (rc);
423
424 end = 0;
425 istart = iend = 1;
426 n = h.ending_track - h.starting_track + 1;
427 rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
428
429 if (rc < 0)
430 return (rc);
431
432 if (!arg || !*arg) {
433 /* Play the whole disc */
434 return (play_track(h.starting_track, 1, h.ending_track, 1));
435 }
436
437 if (strchr(arg, '#')) {
438 /* Play block #blk [ len ] */
439 len = 0;
440
441 if (2 != sscanf(arg, "#%d%d", &blk, &len) &&
442 1 != sscanf(arg, "#%d", &blk))
443 goto Clean_up;
444
445 if (len == 0) {
446 if (msf)
447 len = msf2lba(toc_buffer[n].addr.msf.minute,
448 toc_buffer[n].addr.msf.second,
449 toc_buffer[n].addr.msf.frame) - blk;
450 else
451 len = be32toh(toc_buffer[n].addr.lba) - blk;
452 }
453 return (play_blocks(blk, len));
454 }
455
456 if (strchr(arg, ':')) {
457 /*
458 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
459 *
460 * Will now also undestand timed addresses relative
461 * to the beginning of a track in the form...
462 *
463 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
464 */
465 unsigned tr1, tr2;
466 unsigned m1, m2, s1, s2, f1, f2;
467 unsigned char tm, ts, tf;
468
469 tr2 = m2 = s2 = f2 = f1 = 0;
470 if (8 == sscanf(arg, "%d %d:%d.%d %d %d:%d.%d", &tr1, &m1,
471 &s1, &f1, &tr2, &m2, &s2, &f2))
472 goto Play_Relative_Addresses;
473
474 tr2 = m2 = s2 = f2 = f1 = 0;
475 if (7 == sscanf(arg, "%d %d:%d %d %d:%d.%d", &tr1, &m1, &s1,
476 &tr2, &m2, &s2, &f2))
477 goto Play_Relative_Addresses;
478
479 tr2 = m2 = s2 = f2 = f1 = 0;
480 if (7 == sscanf(arg, "%d %d:%d.%d %d %d:%d", &tr1, &m1, &s1,
481 &f1, &tr2, &m2, &s2))
482 goto Play_Relative_Addresses;
483
484 tr2 = m2 = s2 = f2 = f1 = 0;
485 if (7 == sscanf(arg, "%d %d:%d.%d %d:%d.%d", &tr1, &m1, &s1,
486 &f1, &m2, &s2, &f2))
487 goto Play_Relative_Addresses;
488
489 tr2 = m2 = s2 = f2 = f1 = 0;
490 if (6 == sscanf(arg, "%d %d:%d.%d %d:%d", &tr1, &m1, &s1, &f1,
491 &m2, &s2))
492 goto Play_Relative_Addresses;
493
494 tr2 = m2 = s2 = f2 = f1 = 0;
495 if (6 == sscanf(arg, "%d %d:%d %d:%d.%d", &tr1, &m1, &s1, &m2,
496 &s2, &f2))
497 goto Play_Relative_Addresses;
498
499 tr2 = m2 = s2 = f2 = f1 = 0;
500 if (6 == sscanf(arg, "%d %d:%d.%d %d %d", &tr1, &m1, &s1, &f1,
501 &tr2, &m2))
502 goto Play_Relative_Addresses;
503
504 tr2 = m2 = s2 = f2 = f1 = 0;
505 if (5 == sscanf(arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2,
506 &s2))
507 goto Play_Relative_Addresses;
508
509 tr2 = m2 = s2 = f2 = f1 = 0;
510 if (5 == sscanf(arg, "%d %d:%d %d %d", &tr1, &m1, &s1, &tr2,
511 &m2))
512 goto Play_Relative_Addresses;
513
514 tr2 = m2 = s2 = f2 = f1 = 0;
515 if (5 == sscanf(arg, "%d %d:%d.%d %d", &tr1, &m1, &s1, &f1,
516 &tr2))
517 goto Play_Relative_Addresses;
518
519 tr2 = m2 = s2 = f2 = f1 = 0;
520 if (4 == sscanf(arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
521 goto Play_Relative_Addresses;
522
523 tr2 = m2 = s2 = f2 = f1 = 0;
524 if (4 == sscanf(arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
525 goto Play_Relative_Addresses;
526
527 tr2 = m2 = s2 = f2 = f1 = 0;
528 if (3 == sscanf(arg, "%d %d:%d", &tr1, &m1, &s1))
529 goto Play_Relative_Addresses;
530
531 tr2 = m2 = s2 = f2 = f1 = 0;
532 goto Try_Absolute_Timed_Addresses;
533
534 Play_Relative_Addresses:
535 if (!tr1)
536 tr1 = 1;
537 else if (tr1 > n)
538 tr1 = n;
539
540 if (msf) {
541 tm = toc_buffer[tr1].addr.msf.minute;
542 ts = toc_buffer[tr1].addr.msf.second;
543 tf = toc_buffer[tr1].addr.msf.frame;
544 } else
545 lba2msf(be32toh(toc_buffer[tr1].addr.lba), &tm, &ts, &tf);
546 if ((m1 > tm) || ((m1 == tm) && ((s1 > ts) || ((s1 == ts) &&
547 (f1 > tf))))) {
548 printf("Track %d is not that long.\n", tr1);
549 return (0);
550 }
551 tr1--;
552
553 f1 += tf;
554 if (f1 >= 75) {
555 s1 += f1 / 75;
556 f1 %= 75;
557 }
558 s1 += ts;
559 if (s1 >= 60) {
560 m1 += s1 / 60;
561 s1 %= 60;
562 }
563 m1 += tm;
564
565 if (!tr2) {
566 if (m2 || s2 || f2) {
567 tr2 = tr1;
568 f2 += f1;
569 if (f2 >= 75) {
570 s2 += f2 / 75;
571 f2 %= 75;
572 }
573 s2 += s1;
574 if (s2 > 60) {
575 m2 += s2 / 60;
576 s2 %= 60;
577 }
578 m2 += m1;
579 } else {
580 tr2 = n;
581 if (msf) {
582 m2 = toc_buffer[n].addr.msf.minute;
583 s2 = toc_buffer[n].addr.msf.second;
584 f2 = toc_buffer[n].addr.msf.frame;
585 } else {
586 lba2msf(be32toh(toc_buffer[n].addr.lba),
587 &tm, &ts, &tf);
588 m2 = tm;
589 s2 = ts;
590 f2 = tf;
591 }
592 }
593 } else {
594 if (tr2 > n) {
595 tr2 = n;
596 m2 = s2 = f2 = 0;
597 } else {
598 if (m2 || s2 || f2)
599 tr2--;
600 if (msf) {
601 tm = toc_buffer[tr2].addr.msf.minute;
602 ts = toc_buffer[tr2].addr.msf.second;
603 tf = toc_buffer[tr2].addr.msf.frame;
604 } else
605 lba2msf(be32toh(toc_buffer[tr2].addr.lba),
606 &tm, &ts, &tf);
607 f2 += tf;
608 if (f2 >= 75) {
609 s2 += f2 / 75;
610 f2 %= 75;
611 }
612 s2 += ts;
613 if (s2 > 60) {
614 m2 += s2 / 60;
615 s2 %= 60;
616 }
617 m2 += tm;
618 }
619 }
620
621 if (msf) {
622 tm = toc_buffer[n].addr.msf.minute;
623 ts = toc_buffer[n].addr.msf.second;
624 tf = toc_buffer[n].addr.msf.frame;
625 } else
626 lba2msf(be32toh(toc_buffer[n].addr.lba), &tm, &ts, &tf);
627
628 if ((tr2 < n) && ((m2 > tm) || ((m2 == tm) && ((s2 > ts) ||
629 ((s2 == ts) && (f2 > tf)))))) {
630 printf("The playing time of the disc is not that long.\n");
631 return (0);
632 }
633
634 return (play_msf(m1, s1, f1, m2, s2, f2));
635
636 Try_Absolute_Timed_Addresses:
637 if (6 != sscanf(arg, "%d:%d.%d%d:%d.%d",
638 &m1, &s1, &f1, &m2, &s2, &f2) &&
639 5 != sscanf(arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
640 5 != sscanf(arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
641 3 != sscanf(arg, "%d:%d.%d", &m1, &s1, &f1) &&
642 4 != sscanf(arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
643 2 != sscanf(arg, "%d:%d", &m1, &s1))
644 goto Clean_up;
645
646 if (m2 == 0) {
647 if (msf) {
648 m2 = toc_buffer[n].addr.msf.minute;
649 s2 = toc_buffer[n].addr.msf.second;
650 f2 = toc_buffer[n].addr.msf.frame;
651 } else {
652 lba2msf(be32toh(toc_buffer[n].addr.lba),
653 &tm, &ts, &tf);
654 m2 = tm;
655 s2 = ts;
656 f2 = tf;
657 }
658 }
659 return (play_msf(m1, s1, f1, m2, s2, f2));
660 }
661 /*
662 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
663 */
664 if (4 != sscanf(arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
665 3 != sscanf(arg, "%d.%d%d", &start, &istart, &end) &&
666 3 != sscanf(arg, "%d%d.%d", &start, &end, &iend) &&
667 2 != sscanf(arg, "%d.%d", &start, &istart) &&
668 2 != sscanf(arg, "%d%d", &start, &end) &&
669 1 != sscanf(arg, "%d", &start))
670 goto Clean_up;
671
672 if (end == 0)
673 end = n;
674 return (play_track(start, istart, end, iend));
675
676 Clean_up:
677 warnx("invalid command arguments");
678 return (0);
679 }
680
681 char *
682 strstatus(sts)
683 int sts;
684 {
685
686 switch (sts) {
687 case ASTS_INVALID:
688 return ("invalid");
689 case ASTS_PLAYING:
690 return ("playing");
691 case ASTS_PAUSED:
692 return ("paused");
693 case ASTS_COMPLETED:
694 return ("completed");
695 case ASTS_ERROR:
696 return ("error");
697 case ASTS_VOID:
698 return ("not playing");
699 default:
700 return ("??");
701 }
702 }
703
704 int
705 pstatus(arg)
706 char *arg;
707 {
708 struct cd_sub_channel_info data;
709 struct ioc_read_subchannel ss;
710 int rc, trk, m, s, f;
711 struct ioc_vol v;
712 int what = 0;
713 char *p;
714
715 while ((p = strtok(arg, " \t"))) {
716 arg = 0;
717 if (!strncasecmp(p, "audio", strlen(p)))
718 what |= STATUS_AUDIO;
719 else {
720 if (!strncasecmp(p, "media", strlen(p)))
721 what |= STATUS_MEDIA;
722 else {
723 if (!strncasecmp(p, "volume", strlen(p)))
724 what |= STATUS_VOLUME;
725 else {
726 warnx("invalid command arguments");
727 return 0;
728 }
729 }
730 }
731 }
732 if (!what)
733 what = STATUS_AUDIO | STATUS_MEDIA | STATUS_VOLUME;
734 if (what & STATUS_AUDIO) {
735 rc = status(&trk, &m, &s, &f);
736 if (rc >= 0) {
737 printf("Audio status:\t%s\n", strstatus(rc));
738 printf("Current track:\t%d\n", trk);
739 printf("Position:\t%d:%02d.%02d\n", m, s, f);
740 } else
741 printf("Audio status:\tno info available\n");
742 }
743 if (what & STATUS_MEDIA) {
744 bzero(&ss, sizeof(ss));
745 ss.data = &data;
746 ss.data_len = sizeof(data);
747 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
748 ss.data_format = CD_MEDIA_CATALOG;
749 rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss);
750 if (rc >= 0) {
751 printf("Media catalog:\t%sactive",
752 ss.data->what.media_catalog.mc_valid ? "" : "in");
753 if (ss.data->what.media_catalog.mc_valid &&
754 ss.data->what.media_catalog.mc_number[0])
755 printf(" (%.15s)",
756 ss.data->what.media_catalog.mc_number);
757 putchar('\n');
758 } else
759 printf("Media catalog:\tnone\n");
760 }
761 if (what & STATUS_VOLUME) {
762 rc = ioctl(fd, CDIOCGETVOL, &v);
763 if (rc >= 0) {
764 printf("Left volume:\t%d\n", v.vol[0]);
765 printf("Right volume:\t%d\n", v.vol[1]);
766 } else {
767 printf("Left volume:\tnot available\n");
768 printf("Right volume:\tnot available\n");
769 }
770 }
771 return (0);
772 }
773
774 int
775 info(arg)
776 char *arg;
777 {
778 struct ioc_toc_header h;
779 int rc, i, n;
780
781 if ((rc = ioctl(fd, CDIOREADTOCHEADER, &h)) < 0) {
782 warn("getting toc header");
783 return (rc);
784 }
785
786 n = h.ending_track - h.starting_track + 1;
787 rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
788 if (rc < 0)
789 return (rc);
790
791 printf("track start duration block length type\n");
792 printf("-------------------------------------------------\n");
793
794 for (i = 0; i < n; i++) {
795 printf("%5d ", toc_buffer[i].track);
796 prtrack(toc_buffer + i, 0);
797 }
798 printf("%5d ", toc_buffer[n].track);
799 prtrack(toc_buffer + n, 1);
800 return (0);
801 }
802
803 void
804 lba2msf(lba, m, s, f)
805 u_long lba;
806 u_char *m, *s, *f;
807 {
808
809 lba += 150; /* block start offset */
810 lba &= 0xffffff; /* negative lbas use only 24 bits */
811 *m = lba / (60 * 75);
812 lba %= (60 * 75);
813 *s = lba / 75;
814 *f = lba % 75;
815 }
816
817 u_int
818 msf2lba(m, s, f)
819 u_char m, s, f;
820 {
821
822 return (((m * 60) + s) * 75 + f) - 150;
823 }
824
825 void
826 prtrack(e, lastflag)
827 struct cd_toc_entry *e;
828 int lastflag;
829 {
830 int block, next, len;
831 u_char m, s, f;
832
833 if (msf) {
834 /* Print track start */
835 printf("%2d:%02d.%02d ", e->addr.msf.minute,
836 e->addr.msf.second, e->addr.msf.frame);
837
838 block = msf2lba(e->addr.msf.minute, e->addr.msf.second,
839 e->addr.msf.frame);
840 } else {
841 block = e->addr.lba;
842 lba2msf(block, &m, &s, &f);
843 /* Print track start */
844 printf("%2d:%02d.%02d ", m, s, f);
845 }
846 if (lastflag) {
847 /* Last track -- print block */
848 printf(" - %6d - -\n", block);
849 return;
850 }
851 if (msf)
852 next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second,
853 e[1].addr.msf.frame);
854 else
855 next = e[1].addr.lba;
856 len = next - block;
857 lba2msf(len, &m, &s, &f);
858
859 /* Print duration, block, length, type */
860 printf("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
861 (e->control & 4) ? "data" : "audio");
862 }
863
864 int
865 play_track(tstart, istart, tend, iend)
866 int tstart, istart, tend, iend;
867 {
868 struct ioc_play_track t;
869
870 t.start_track = tstart;
871 t.start_index = istart;
872 t.end_track = tend;
873 t.end_index = iend;
874
875 return (ioctl(fd, CDIOCPLAYTRACKS, &t));
876 }
877
878 int
879 play_blocks(blk, len)
880 int blk, len;
881 {
882 struct ioc_play_blocks t;
883
884 t.blk = blk;
885 t.len = len;
886
887 return (ioctl(fd, CDIOCPLAYBLOCKS, &t));
888 }
889
890 int
891 setvol(left, right)
892 int left, right;
893 {
894 struct ioc_vol v;
895
896 v.vol[0] = left;
897 v.vol[1] = right;
898 v.vol[2] = 0;
899 v.vol[3] = 0;
900
901 return (ioctl(fd, CDIOCSETVOL, &v));
902 }
903
904 int
905 read_toc_entrys(len)
906 int len;
907 {
908 struct ioc_read_toc_entry t;
909
910 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
911 t.starting_track = 0;
912 t.data_len = len;
913 t.data = toc_buffer;
914
915 return (ioctl(fd, CDIOREADTOCENTRYS, (char *)&t));
916 }
917
918 int
919 play_msf(start_m, start_s, start_f, end_m, end_s, end_f)
920 int start_m, start_s, start_f, end_m, end_s, end_f;
921 {
922 struct ioc_play_msf a;
923
924 a.start_m = start_m;
925 a.start_s = start_s;
926 a.start_f = start_f;
927 a.end_m = end_m;
928 a.end_s = end_s;
929 a.end_f = end_f;
930
931 return (ioctl(fd, CDIOCPLAYMSF, (char *) &a));
932 }
933
934 int
935 status(trk, min, sec, frame)
936 int *trk, *min, *sec, *frame;
937 {
938 struct ioc_read_subchannel s;
939 struct cd_sub_channel_info data;
940 u_char mm, ss, ff;
941
942 bzero(&s, sizeof(s));
943 s.data = &data;
944 s.data_len = sizeof(data);
945 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
946 s.data_format = CD_CURRENT_POSITION;
947
948 if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
949 return -1;
950
951 *trk = s.data->what.position.track_number;
952 if (msf) {
953 *min = s.data->what.position.reladdr.msf.minute;
954 *sec = s.data->what.position.reladdr.msf.second;
955 *frame = s.data->what.position.reladdr.msf.frame;
956 } else {
957 lba2msf(be32toh(s.data->what.position.reladdr.lba), &mm, &ss, &ff);
958 *min = mm;
959 *sec = ss;
960 *frame = ff;
961 }
962
963 return (s.data->header.audio_status);
964 }
965
966 char *
967 prompt()
968 {
969 return ("cdplay> ");
970 }
971
972 char *
973 parse(buf, cmd)
974 char *buf;
975 int *cmd;
976 {
977 struct cmdtab *c;
978 char *p, *q;
979 int len;
980
981 for (p = buf; isspace(*p); p++)
982 continue;
983
984 if (isdigit(*p) || (p[0] == '#' && isdigit(p[1]))) {
985 *cmd = CMD_PLAY;
986 return (p);
987 }
988
989 for (buf = p; *p && !isspace(*p); p++)
990 continue;
991
992 len = p - buf;
993 if (!len)
994 return (0);
995
996 if (*p) { /* It must be a spacing character! */
997 *p++ = 0;
998 for (q = p; *q && *q != '\n' && *q != '\r'; q++)
999 continue;
1000 *q = 0;
1001 }
1002
1003 *cmd = -1;
1004
1005 for (c = cmdtab; c->name; ++c) {
1006 /* Is it an exact match? */
1007 if (!strcasecmp(buf, c->name)) {
1008 *cmd = c->command;
1009 break;
1010 }
1011 /* Try short hand forms then... */
1012 if (len >= c->min && !strncasecmp(buf, c->name, len)) {
1013 if (*cmd != -1 && *cmd != c->command) {
1014 warnx("ambiguous command");
1015 return (0);
1016 }
1017 *cmd = c->command;
1018 }
1019 }
1020
1021 if (*cmd == -1) {
1022 warnx("invalid command, enter ``help'' for commands");
1023 return (0);
1024 }
1025
1026 while (isspace(*p))
1027 p++;
1028
1029 return (p);
1030 }
1031
1032 int
1033 opencd()
1034 {
1035 char devbuf[80];
1036
1037 if (fd > -1)
1038 return (1);
1039
1040 fd = opendisk(cdname, O_RDONLY, devbuf, sizeof(devbuf), 0);
1041
1042 if (fd < 0) {
1043 if (errno == ENXIO) {
1044 /* ENXIO has an overloaded meaning here. The original
1045 * "Device not configured" should be interpreted as
1046 * "No disc in drive %s". */
1047 warnx("no disc in drive %s", devbuf);
1048 return (0);
1049 }
1050 err(1, "%s", devbuf);
1051 }
1052
1053 return (1);
1054 }
1055