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