sys/vfs/autofs: Cleanup autofs_mount()
[dragonfly.git] / usr.sbin / cdcontrol / cdcontrol.c
bloba419a44df20fd8f7b3f72faa9ee2027436626443
1 /*
2 * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
3 * Based on the non-X based CD player by Jean-Marc Zucconi and
4 * Andrey A. Chernov.
6 * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
8 * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
9 * A couple of further fixes to my own earlier "fixes".
11 * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
12 * Added an ability to specify addresses relative to the
13 * beginning of a track. This is in fact a variation of
14 * doing the simple play_msf() call.
16 * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
17 * New eject algorithm.
18 * Some code style reformatting.
20 * $FreeBSD: src/usr.sbin/cdcontrol/cdcontrol.c,v 1.24.2.11 2002/11/20 00:26:19 njl Exp $
23 #include <sys/cdio.h>
24 #include <sys/cdrio.h>
25 #include <sys/file.h>
26 #include <sys/ioctl.h>
27 #include <sys/param.h>
28 #include <ctype.h>
29 #include <err.h>
30 #include <errno.h>
31 #include <histedit.h>
32 #include <limits.h>
33 #include <paths.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <vis.h>
40 #define VERSION "2.0"
42 #define ASTS_INVALID 0x00 /* Audio status byte not valid */
43 #define ASTS_PLAYING 0x11 /* Audio play operation in progress */
44 #define ASTS_PAUSED 0x12 /* Audio play operation paused */
45 #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
46 #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
47 #define ASTS_VOID 0x15 /* No current audio status to return */
49 #ifndef DEFAULT_CD_DRIVE
50 # define DEFAULT_CD_DRIVE "/dev/cd0c"
51 #endif
53 #ifndef DEFAULT_CD_PARTITION
54 # define DEFAULT_CD_PARTITION "c"
55 #endif
57 #define CMD_DEBUG 1
58 #define CMD_EJECT 2
59 #define CMD_HELP 3
60 #define CMD_INFO 4
61 #define CMD_PAUSE 5
62 #define CMD_PLAY 6
63 #define CMD_QUIT 7
64 #define CMD_RESUME 8
65 #define CMD_STOP 9
66 #define CMD_VOLUME 10
67 #define CMD_CLOSE 11
68 #define CMD_RESET 12
69 #define CMD_SET 13
70 #define CMD_STATUS 14
71 #define CMD_CDID 15
72 #define CMD_NEXT 16
73 #define CMD_PREVIOUS 17
74 #define CMD_SPEED 18
75 #define STATUS_AUDIO 0x1
76 #define STATUS_MEDIA 0x2
77 #define STATUS_VOLUME 0x4
79 static struct cmdtab {
80 int command;
81 const char *name;
82 unsigned min;
83 const char *args;
84 } cmdtab[] = {
85 { CMD_CLOSE, "close", 1, "" },
86 { CMD_DEBUG, "debug", 1, "on | off" },
87 { CMD_EJECT, "eject", 1, "" },
88 { CMD_HELP, "?", 1, 0 },
89 { CMD_HELP, "help", 1, "" },
90 { CMD_INFO, "info", 1, "" },
91 { CMD_NEXT, "next", 1, "" },
92 { CMD_PAUSE, "pause", 2, "" },
93 { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
94 { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
95 { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
96 { CMD_PLAY, "play", 1, "[#block [len]]" },
97 { CMD_PREVIOUS, "previous", 2, "" },
98 { CMD_QUIT, "quit", 1, "" },
99 { CMD_QUIT, "exit", 1, "" },
100 { CMD_RESET, "reset", 4, "" },
101 { CMD_RESUME, "resume", 1, "" },
102 { CMD_SET, "set", 2, "msf | lba" },
103 { CMD_STATUS, "status", 1, "[audio | media | volume]" },
104 { CMD_STOP, "stop", 3, "" },
105 { CMD_VOLUME, "volume", 1,
106 "<l> <r> | left | right | mute | mono | stereo" },
107 { CMD_CDID, "cdid", 2, "" },
108 { CMD_SPEED, "speed", 2, "speed" },
109 { 0, NULL, 0, NULL }
112 static struct cd_toc_entry toc_buffer[100];
114 static const char *cdname;
115 static int fd = -1;
116 static int verbose = 1;
117 static int msf = 1;
119 static int setvol(int, int);
120 static int read_toc_entrys(int);
121 static int play_msf(int, int, int, int, int, int);
122 static int play_track(int, int, int, int);
123 static int status(int *, int *, int *, int *);
124 static int open_cd(void);
125 static int next_prev(char *arg, int);
126 static int play(char *arg);
127 static int info(char *arg);
128 static int cdid(void);
129 static int pstatus(char *arg);
130 static char *input(int *);
131 static void prtrack(struct cd_toc_entry *e, int lastflag);
132 static void lba2msf(unsigned long lba,
133 u_char *m, u_char *s, u_char *f);
134 static unsigned int msf2lba(u_char m, u_char s, u_char f);
135 static int play_blocks(int blk, int len);
136 static int run(int cmd, char *arg);
137 static char *parse(char *buf, int *cmd);
138 static void help(void);
139 static void usage(void);
140 static char *use_cdrom_instead(const char *);
141 static const char *strstatus(int);
142 static u_int dbprog_discid(void);
143 static const char *cdcontrol_prompt(void);
145 static void
146 help(void)
148 struct cmdtab *c;
149 const char *s;
150 char n;
151 int i;
153 for (c=cmdtab; c->name; ++c) {
154 if (! c->args)
155 continue;
156 printf("\t");
157 for (i = c->min, s = c->name; *s; s++, i--) {
158 if (i > 0)
159 n = toupper(*s);
160 else
161 n = *s;
162 putchar(n);
164 if (*c->args)
165 printf (" %s", c->args);
166 printf ("\n");
168 printf ("\n\tThe word \"play\" is not required for the play commands.\n");
169 printf ("\tThe plain target address is taken as a synonym for play.\n");
172 static void
173 usage(void)
175 fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
176 exit (1);
179 static char *
180 use_cdrom_instead(const char *old_envvar)
182 char *device;
184 device = getenv(old_envvar);
185 if (device)
186 warnx("%s environment variable deprecated, "
187 "please use CDROM in the future.", old_envvar);
188 return device;
193 main(int argc, char **argv)
195 int cmd;
196 char *arg;
198 for (;;) {
199 switch (getopt (argc, argv, "svhf:")) {
200 case EOF:
201 break;
202 case 's':
203 verbose = 0;
204 continue;
205 case 'v':
206 verbose = 2;
207 continue;
208 case 'f':
209 cdname = optarg;
210 continue;
211 case 'h':
212 default:
213 usage ();
215 break;
217 argc -= optind;
218 argv += optind;
220 if (argc > 0 && ! strcasecmp (*argv, "help"))
221 usage ();
223 if (! cdname) {
224 cdname = getenv("CDROM");
227 if (! cdname)
228 cdname = use_cdrom_instead("MUSIC_CD");
229 if (! cdname)
230 cdname = use_cdrom_instead("CD_DRIVE");
231 if (! cdname)
232 cdname = use_cdrom_instead("DISC");
233 if (! cdname)
234 cdname = use_cdrom_instead("CDPLAY");
236 if (! cdname) {
237 cdname = DEFAULT_CD_DRIVE;
238 warnx("no CD device name specified, defaulting to %s", cdname);
241 if (argc > 0) {
242 char buf[80], *p;
243 int len;
245 for (p=buf; argc-->0; ++argv) {
246 len = strlen (*argv);
248 if (p + len >= buf + sizeof (buf) - 1)
249 usage ();
251 if (p > buf)
252 *p++ = ' ';
254 strcpy (p, *argv);
255 p += len;
257 *p = 0;
258 arg = parse (buf, &cmd);
259 return (run (cmd, arg));
262 if (verbose == 1)
263 verbose = isatty (0);
265 if (verbose) {
266 printf ("Compact Disc Control utility, version %s\n", VERSION);
267 printf ("Type `?' for command list\n\n");
270 for (;;) {
271 arg = input (&cmd);
272 if (run (cmd, arg) < 0) {
273 if (verbose)
274 warn(NULL);
275 close (fd);
276 fd = -1;
278 fflush (stdout);
282 static int
283 run(int cmd, char *arg)
285 long speed;
286 int l, r, rc;
288 switch (cmd) {
290 case CMD_QUIT:
291 exit (0);
293 case CMD_INFO:
294 if (fd < 0 && ! open_cd ())
295 return (0);
297 return info (arg);
299 case CMD_CDID:
300 if (fd < 0 && ! open_cd ())
301 return (0);
303 return cdid ();
305 case CMD_STATUS:
306 if (fd < 0 && ! open_cd ())
307 return (0);
309 return pstatus (arg);
311 case CMD_NEXT:
312 case CMD_PREVIOUS:
313 if (fd < 0 && ! open_cd ())
314 return (0);
316 while (isspace (*arg))
317 arg++;
319 return next_prev (arg, cmd);
321 case CMD_PAUSE:
322 if (fd < 0 && ! open_cd ())
323 return (0);
325 return ioctl (fd, CDIOCPAUSE);
327 case CMD_RESUME:
328 if (fd < 0 && ! open_cd ())
329 return (0);
331 return ioctl (fd, CDIOCRESUME);
333 case CMD_STOP:
334 if (fd < 0 && ! open_cd ())
335 return (0);
337 rc = ioctl (fd, CDIOCSTOP);
339 ioctl (fd, CDIOCALLOW);
341 return (rc);
343 case CMD_RESET:
344 if (fd < 0 && ! open_cd ())
345 return (0);
347 rc = ioctl (fd, CDIOCRESET);
348 if (rc < 0)
349 return rc;
350 close(fd);
351 fd = -1;
352 return (0);
354 case CMD_DEBUG:
355 if (fd < 0 && ! open_cd ())
356 return (0);
358 if (! strcasecmp (arg, "on"))
359 return ioctl (fd, CDIOCSETDEBUG);
361 if (! strcasecmp (arg, "off"))
362 return ioctl (fd, CDIOCCLRDEBUG);
364 warnx("invalid command arguments");
366 return (0);
368 case CMD_EJECT:
369 if (fd < 0 && ! open_cd ())
370 return (0);
372 ioctl (fd, CDIOCALLOW);
373 rc = ioctl (fd, CDIOCEJECT);
374 if (rc < 0)
375 return (rc);
376 return (0);
378 case CMD_CLOSE:
379 if (fd < 0 && ! open_cd ())
380 return (0);
382 ioctl (fd, CDIOCALLOW);
383 rc = ioctl (fd, CDIOCCLOSE);
384 if (rc < 0)
385 return (rc);
386 close(fd);
387 fd = -1;
388 return (0);
390 case CMD_PLAY:
391 if (fd < 0 && ! open_cd ())
392 return (0);
394 while (isspace (*arg))
395 arg++;
397 return play (arg);
399 case CMD_SET:
400 if (! strcasecmp (arg, "msf"))
401 msf = 1;
402 else if (! strcasecmp (arg, "lba"))
403 msf = 0;
404 else
405 warnx("invalid command arguments");
406 return (0);
408 case CMD_VOLUME:
409 if (fd < 0 && !open_cd ())
410 return (0);
412 if (! strncasecmp (arg, "left", strlen(arg)))
413 return ioctl (fd, CDIOCSETLEFT);
415 if (! strncasecmp (arg, "right", strlen(arg)))
416 return ioctl (fd, CDIOCSETRIGHT);
418 if (! strncasecmp (arg, "mono", strlen(arg)))
419 return ioctl (fd, CDIOCSETMONO);
421 if (! strncasecmp (arg, "stereo", strlen(arg)))
422 return ioctl (fd, CDIOCSETSTERIO);
424 if (! strncasecmp (arg, "mute", strlen(arg)))
425 return ioctl (fd, CDIOCSETMUTE);
427 if (2 != sscanf (arg, "%d %d", &l, &r)) {
428 warnx("invalid command arguments");
429 return (0);
432 return setvol (l, r);
434 case CMD_SPEED:
435 if (fd < 0 && ! open_cd ())
436 return (0);
438 errno = 0;
439 if (strcasecmp("max", arg) == 0)
440 speed = CDR_MAX_SPEED;
441 else
442 speed = strtol(arg, NULL, 10) * 177;
443 if (speed <= 0 || speed > INT_MAX) {
444 warnx("invalid command arguments %s", arg);
445 return (0);
447 return ioctl(fd, CDRIOCREADSPEED, &speed);
449 default:
450 case CMD_HELP:
451 help ();
452 return (0);
457 static int
458 play(char *arg)
460 struct ioc_toc_header h;
461 unsigned int n;
462 int rc, start, end = 0, istart = 1, iend = 1;
464 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
466 if (rc < 0)
467 return (rc);
469 n = h.ending_track - h.starting_track + 1;
470 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
472 if (rc < 0)
473 return (rc);
475 if (! arg || ! *arg) {
476 /* Play the whole disc */
477 if (msf)
478 return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
479 toc_buffer[n].addr.msf.second,
480 toc_buffer[n].addr.msf.frame));
481 else
482 return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
485 if (strchr (arg, '#')) {
486 /* Play block #blk [ len ] */
487 int blk, len = 0;
489 if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
490 1 != sscanf (arg, "#%d", &blk))
491 goto Clean_up;
493 if (len == 0) {
494 if (msf)
495 len = msf2lba (toc_buffer[n].addr.msf.minute,
496 toc_buffer[n].addr.msf.second,
497 toc_buffer[n].addr.msf.frame) - blk;
498 else
499 len = ntohl(toc_buffer[n].addr.lba) - blk;
501 return play_blocks (blk, len);
504 if (strchr (arg, ':')) {
506 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
508 * Will now also undestand timed addresses relative
509 * to the beginning of a track in the form...
511 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
513 unsigned tr1, tr2;
514 unsigned m1, m2, s1, s2, f1, f2;
515 unsigned char tm, ts, tf;
517 tr2 = m2 = s2 = f2 = f1 = 0;
518 if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
519 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
520 goto Play_Relative_Addresses;
522 tr2 = m2 = s2 = f2 = f1 = 0;
523 if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
524 &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
525 goto Play_Relative_Addresses;
527 tr2 = m2 = s2 = f2 = f1 = 0;
528 if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
529 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
530 goto Play_Relative_Addresses;
532 tr2 = m2 = s2 = f2 = f1 = 0;
533 if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
534 &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
535 goto Play_Relative_Addresses;
537 tr2 = m2 = s2 = f2 = f1 = 0;
538 if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
539 &tr1, &m1, &s1, &f1, &m2, &s2))
540 goto Play_Relative_Addresses;
542 tr2 = m2 = s2 = f2 = f1 = 0;
543 if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
544 &tr1, &m1, &s1, &m2, &s2, &f2))
545 goto Play_Relative_Addresses;
547 tr2 = m2 = s2 = f2 = f1 = 0;
548 if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
549 &tr1, &m1, &s1, &f1, &tr2, &m2))
550 goto Play_Relative_Addresses;
552 tr2 = m2 = s2 = f2 = f1 = 0;
553 if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
554 goto Play_Relative_Addresses;
556 tr2 = m2 = s2 = f2 = f1 = 0;
557 if (5 == sscanf (arg, "%d %d:%d %d %d",
558 &tr1, &m1, &s1, &tr2, &m2))
559 goto Play_Relative_Addresses;
561 tr2 = m2 = s2 = f2 = f1 = 0;
562 if (5 == sscanf (arg, "%d %d:%d.%d %d",
563 &tr1, &m1, &s1, &f1, &tr2))
564 goto Play_Relative_Addresses;
566 tr2 = m2 = s2 = f2 = f1 = 0;
567 if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
568 goto Play_Relative_Addresses;
570 tr2 = m2 = s2 = f2 = f1 = 0;
571 if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
572 goto Play_Relative_Addresses;
574 tr2 = m2 = s2 = f2 = f1 = 0;
575 if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
576 goto Play_Relative_Addresses;
578 tr2 = m2 = s2 = f2 = f1 = 0;
579 goto Try_Absolute_Timed_Addresses;
581 Play_Relative_Addresses:
582 if (tr1 == 0)
583 tr1 = 1;
584 else if (tr1 > n)
585 tr1 = n;
587 tr1--;
589 if (msf) {
590 tm = toc_buffer[tr1].addr.msf.minute;
591 ts = toc_buffer[tr1].addr.msf.second;
592 tf = toc_buffer[tr1].addr.msf.frame;
593 } else
594 lba2msf(ntohl(toc_buffer[tr1].addr.lba),
595 &tm, &ts, &tf);
596 if ((m1 > tm)
597 || ((m1 == tm)
598 && ((s1 > ts)
599 || ((s1 == ts)
600 && (f1 > tf))))) {
601 printf ("Track %d is not that long.\n", tr1 + 1);
602 return (0);
605 f1 += tf;
606 if (f1 >= 75) {
607 s1 += f1 / 75;
608 f1 %= 75;
611 s1 += ts;
612 if (s1 >= 60) {
613 m1 += s1 / 60;
614 s1 %= 60;
617 m1 += tm;
619 if (! tr2) {
620 if (m2 || s2 || f2) {
621 tr2 = tr1;
622 f2 += f1;
623 if (f2 >= 75) {
624 s2 += f2 / 75;
625 f2 %= 75;
628 s2 += s1;
629 if (s2 > 60) {
630 m2 += s2 / 60;
631 s2 %= 60;
634 m2 += m1;
635 } else {
636 tr2 = n;
637 if (msf) {
638 m2 = toc_buffer[n].addr.msf.minute;
639 s2 = toc_buffer[n].addr.msf.second;
640 f2 = toc_buffer[n].addr.msf.frame;
641 } else {
642 lba2msf(ntohl(toc_buffer[n].addr.lba),
643 &tm, &ts, &tf);
644 m2 = tm;
645 s2 = ts;
646 f2 = tf;
649 } else if (tr2 > n) {
650 tr2 = n;
651 m2 = s2 = f2 = 0;
652 } else {
653 if (m2 || s2 || f2)
654 tr2--;
655 if (msf) {
656 tm = toc_buffer[tr2].addr.msf.minute;
657 ts = toc_buffer[tr2].addr.msf.second;
658 tf = toc_buffer[tr2].addr.msf.frame;
659 } else
660 lba2msf(ntohl(toc_buffer[tr2].addr.lba),
661 &tm, &ts, &tf);
662 f2 += tf;
663 if (f2 >= 75) {
664 s2 += f2 / 75;
665 f2 %= 75;
668 s2 += ts;
669 if (s2 > 60) {
670 m2 += s2 / 60;
671 s2 %= 60;
674 m2 += tm;
677 if (msf) {
678 tm = toc_buffer[n].addr.msf.minute;
679 ts = toc_buffer[n].addr.msf.second;
680 tf = toc_buffer[n].addr.msf.frame;
681 } else
682 lba2msf(ntohl(toc_buffer[n].addr.lba),
683 &tm, &ts, &tf);
684 if ((tr2 < n)
685 && ((m2 > tm)
686 || ((m2 == tm)
687 && ((s2 > ts)
688 || ((s2 == ts)
689 && (f2 > tf)))))) {
690 printf ("The playing time of the disc is not that long.\n");
691 return (0);
693 return (play_msf (m1, s1, f1, m2, s2, f2));
695 Try_Absolute_Timed_Addresses:
696 if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
697 &m1, &s1, &f1, &m2, &s2, &f2) &&
698 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
699 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
700 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
701 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
702 2 != sscanf (arg, "%d:%d", &m1, &s1))
703 goto Clean_up;
705 if (m2 == 0) {
706 if (msf) {
707 m2 = toc_buffer[n].addr.msf.minute;
708 s2 = toc_buffer[n].addr.msf.second;
709 f2 = toc_buffer[n].addr.msf.frame;
710 } else {
711 lba2msf(ntohl(toc_buffer[n].addr.lba),
712 &tm, &ts, &tf);
713 m2 = tm;
714 s2 = ts;
715 f2 = tf;
718 return play_msf (m1, s1, f1, m2, s2, f2);
722 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
724 if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
725 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
726 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
727 2 != sscanf (arg, "%d.%d", &start, &istart) &&
728 2 != sscanf (arg, "%d%d", &start, &end) &&
729 1 != sscanf (arg, "%d", &start))
730 goto Clean_up;
732 if (end == 0)
733 end = n;
734 return (play_track (start, istart, end, iend));
736 Clean_up:
737 warnx("invalid command arguments");
738 return (0);
741 static int
742 next_prev(char *arg, int cmd)
744 struct ioc_toc_header h;
745 int dir, junk, n, off, rc, trk;
747 dir = (cmd == CMD_NEXT) ? 1 : -1;
748 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
749 if (rc < 0)
750 return (rc);
752 n = h.ending_track - h.starting_track + 1;
753 rc = status (&trk, &junk, &junk, &junk);
754 if (rc < 0)
755 return (-1);
757 if (arg && *arg) {
758 if (sscanf (arg, "%u", &off) != 1) {
759 warnx("invalid command argument");
760 return (0);
761 } else
762 trk += off * dir;
763 } else
764 trk += dir;
766 if (trk > h.ending_track)
767 trk = 1;
769 return (play_track (trk, 1, n, 1));
772 static const char *
773 strstatus(int sts)
775 switch (sts) {
776 case ASTS_INVALID: return ("invalid");
777 case ASTS_PLAYING: return ("playing");
778 case ASTS_PAUSED: return ("paused");
779 case ASTS_COMPLETED: return ("completed");
780 case ASTS_ERROR: return ("error");
781 case ASTS_VOID: return ("void");
782 default: return ("??");
786 static int
787 pstatus(char *arg)
789 struct ioc_vol v;
790 struct ioc_read_subchannel ss;
791 struct cd_sub_channel_info data;
792 int rc, trk, m, s, f;
793 int what = 0;
794 char *p, vmcn[(4 * 15) + 1];
796 while ((p = strtok(arg, " \t"))) {
797 arg = NULL;
798 if (!strncasecmp(p, "audio", strlen(p)))
799 what |= STATUS_AUDIO;
800 else if (!strncasecmp(p, "media", strlen(p)))
801 what |= STATUS_MEDIA;
802 else if (!strncasecmp(p, "volume", strlen(p)))
803 what |= STATUS_VOLUME;
804 else {
805 warnx("invalid command arguments");
806 return 0;
809 if (!what)
810 what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
811 if (what & STATUS_AUDIO) {
812 rc = status (&trk, &m, &s, &f);
813 if (rc >= 0)
814 if (verbose)
815 printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
816 rc, strstatus (rc), trk, m, s, f);
817 else
818 printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
819 else
820 printf ("No current status info available\n");
822 if (what & STATUS_MEDIA) {
823 bzero (&ss, sizeof (ss));
824 ss.data = &data;
825 ss.data_len = sizeof (data);
826 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
827 ss.data_format = CD_MEDIA_CATALOG;
828 rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
829 if (rc >= 0) {
830 printf("Media catalog is %sactive",
831 ss.data->what.media_catalog.mc_valid ? "": "in");
832 if (ss.data->what.media_catalog.mc_valid &&
833 ss.data->what.media_catalog.mc_number[0])
835 strvisx (vmcn, ss.data->what.media_catalog.mc_number,
836 (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
837 printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
839 putchar('\n');
840 } else
841 printf("No media catalog info available\n");
843 if (what & STATUS_VOLUME) {
844 rc = ioctl (fd, CDIOCGETVOL, &v);
845 if (rc >= 0)
846 if (verbose)
847 printf ("Left volume = %d, right volume = %d\n",
848 v.vol[0], v.vol[1]);
849 else
850 printf ("%d %d\n", v.vol[0], v.vol[1]);
851 else
852 printf ("No volume level info available\n");
854 return(0);
858 * dbprog_sum
859 * Convert an integer to its text string representation, and
860 * compute its checksum. Used by dbprog_discid to derive the
861 * disc ID.
863 * Args:
864 * n - The integer value.
866 * Return:
867 * The integer checksum.
869 static int
870 dbprog_sum(int n)
872 char buf[12],
874 int ret = 0;
876 /* For backward compatibility this algorithm must not change */
877 sprintf(buf, "%u", n);
878 for (p = buf; *p != '\0'; p++)
879 ret += (*p - '0');
881 return(ret);
886 * dbprog_discid
887 * Compute a magic disc ID based on the number of tracks,
888 * the length of each track, and a checksum of the string
889 * that represents the offset of each track.
891 * Args:
892 * s - Pointer to the curstat_t structure.
894 * Return:
895 * The integer disc ID.
897 static u_int
898 dbprog_discid(void)
900 struct ioc_toc_header h;
901 int rc;
902 int i, ntr,
903 t = 0,
904 n = 0;
906 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
907 if (rc < 0)
908 return 0;
909 ntr = h.ending_track - h.starting_track + 1;
910 i = msf;
911 msf = 1;
912 rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
913 msf = i;
914 if (rc < 0)
915 return 0;
916 /* For backward compatibility this algorithm must not change */
917 for (i = 0; i < ntr; i++) {
918 #define TC_MM(a) toc_buffer[a].addr.msf.minute
919 #define TC_SS(a) toc_buffer[a].addr.msf.second
920 n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
922 t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
923 ((TC_MM(i) * 60) + TC_SS(i));
926 return((n % 0xff) << 24 | t << 8 | ntr);
929 static int
930 cdid(void)
932 u_int id;
934 id = dbprog_discid();
935 if (id)
937 if (verbose)
938 printf ("CDID=");
939 printf ("%08x\n",id);
941 return id ? 0 : 1;
944 static int
945 info(char *arg __unused)
947 struct ioc_toc_header h;
948 int rc, i, n;
950 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
951 if (rc >= 0) {
952 if (verbose)
953 printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
954 h.starting_track, h.ending_track, h.len);
955 else
956 printf ("%d %d %d\n", h.starting_track,
957 h.ending_track, h.len);
958 } else {
959 warn("getting toc header");
960 return (rc);
963 n = h.ending_track - h.starting_track + 1;
964 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
965 if (rc < 0)
966 return (rc);
968 if (verbose) {
969 printf ("track start duration block length type\n");
970 printf ("-------------------------------------------------\n");
973 for (i = 0; i < n; i++) {
974 printf ("%5d ", toc_buffer[i].track);
975 prtrack (toc_buffer + i, 0);
977 printf ("%5d ", toc_buffer[n].track);
978 prtrack (toc_buffer + n, 1);
979 return (0);
982 static void
983 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
985 lba += 150; /* block start offset */
986 lba &= 0xffffff; /* negative lbas use only 24 bits */
987 *m = lba / (60 * 75);
988 lba %= (60 * 75);
989 *s = lba / 75;
990 *f = lba % 75;
993 static unsigned int
994 msf2lba(u_char m, u_char s, u_char f)
996 return (((m * 60) + s) * 75 + f) - 150;
999 static void
1000 prtrack(struct cd_toc_entry *e, int lastflag)
1002 int block, next, len;
1003 u_char m, s, f;
1005 if (msf) {
1006 /* Print track start */
1007 printf ("%2d:%02d.%02d ", e->addr.msf.minute,
1008 e->addr.msf.second, e->addr.msf.frame);
1010 block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
1011 e->addr.msf.frame);
1012 } else {
1013 block = ntohl(e->addr.lba);
1014 lba2msf(block, &m, &s, &f);
1015 /* Print track start */
1016 printf ("%2d:%02d.%02d ", m, s, f);
1018 if (lastflag) {
1019 /* Last track -- print block */
1020 printf (" - %6d - -\n", block);
1021 return;
1024 if (msf)
1025 next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
1026 e[1].addr.msf.frame);
1027 else
1028 next = ntohl(e[1].addr.lba);
1029 len = next - block;
1030 /* Take into account a start offset time. */
1031 lba2msf (len - 150, &m, &s, &f);
1033 /* Print duration, block, length, type */
1034 printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
1035 (e->control & 4) ? "data" : "audio");
1038 static int
1039 play_track(int tstart, int istart, int tend, int iend)
1041 struct ioc_play_track t;
1043 t.start_track = tstart;
1044 t.start_index = istart;
1045 t.end_track = tend;
1046 t.end_index = iend;
1048 return ioctl (fd, CDIOCPLAYTRACKS, &t);
1051 static int
1052 play_blocks(int blk, int len)
1054 struct ioc_play_blocks t;
1056 t.blk = blk;
1057 t.len = len;
1059 return ioctl (fd, CDIOCPLAYBLOCKS, &t);
1062 static int
1063 setvol(int left, int right)
1065 struct ioc_vol v;
1067 v.vol[0] = left;
1068 v.vol[1] = right;
1069 v.vol[2] = 0;
1070 v.vol[3] = 0;
1072 return ioctl (fd, CDIOCSETVOL, &v);
1075 static int
1076 read_toc_entrys(int len)
1078 struct ioc_read_toc_entry t;
1080 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1081 t.starting_track = 0;
1082 t.data_len = len;
1083 t.data = toc_buffer;
1085 return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
1088 static int
1089 play_msf(int start_m, int start_s, int start_f,
1090 int end_m, int end_s, int end_f)
1092 struct ioc_play_msf a;
1094 a.start_m = start_m;
1095 a.start_s = start_s;
1096 a.start_f = start_f;
1097 a.end_m = end_m;
1098 a.end_s = end_s;
1099 a.end_f = end_f;
1101 return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
1104 static int
1105 status(int *trk, int *min, int *sec, int *frame)
1107 struct ioc_read_subchannel s;
1108 struct cd_sub_channel_info data;
1109 u_char mm, ss, ff;
1111 bzero (&s, sizeof (s));
1112 s.data = &data;
1113 s.data_len = sizeof (data);
1114 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1115 s.data_format = CD_CURRENT_POSITION;
1117 if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
1118 return -1;
1120 *trk = s.data->what.position.track_number;
1121 if (msf) {
1122 *min = s.data->what.position.reladdr.msf.minute;
1123 *sec = s.data->what.position.reladdr.msf.second;
1124 *frame = s.data->what.position.reladdr.msf.frame;
1125 } else {
1126 lba2msf(ntohl(s.data->what.position.reladdr.lba),
1127 &mm, &ss, &ff);
1128 *min = mm;
1129 *sec = ss;
1130 *frame = ff;
1133 return s.data->header.audio_status;
1136 static const char *
1137 cdcontrol_prompt(void)
1139 return ("cdcontrol> ");
1142 static char *
1143 input(int *cmd)
1145 #define MAXLINE 80
1146 static EditLine *el = NULL;
1147 static History *hist = NULL;
1148 static HistEvent he;
1149 static char buf[MAXLINE];
1150 int num = 0;
1151 int len;
1152 const char *bp = NULL;
1153 char *p;
1155 do {
1156 if (verbose) {
1157 if (!el) {
1158 el = el_init("cdcontrol", stdin, stdout, stderr);
1159 hist = history_init();
1160 history(hist, &he, H_SETSIZE, 100);
1161 el_set(el, EL_HIST, history, hist);
1162 el_set(el, EL_EDITOR, "emacs");
1163 el_set(el, EL_PROMPT, cdcontrol_prompt);
1164 el_set(el, EL_SIGNAL, 1);
1165 el_source(el, NULL);
1167 if ((bp = el_gets(el, &num)) == NULL || num == 0) {
1168 *cmd = CMD_QUIT;
1169 fprintf (stderr, "\r\n");
1170 return (0);
1173 len = (num >= MAXLINE) ? MAXLINE - 1 : num;
1174 memcpy(buf, bp, len);
1175 buf[len] = 0;
1176 history(hist, &he, H_ENTER, bp);
1177 #undef MAXLINE
1179 } else {
1180 if (! fgets (buf, sizeof (buf), stdin)) {
1181 *cmd = CMD_QUIT;
1182 fprintf (stderr, "\r\n");
1183 return (0);
1186 p = parse (buf, cmd);
1187 } while (! p);
1188 return (p);
1191 static char *
1192 parse(char *buf, int *cmd)
1194 struct cmdtab *c;
1195 char *p;
1196 unsigned int len;
1198 for (p=buf; isspace (*p); p++)
1199 continue;
1201 if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
1202 *cmd = CMD_PLAY;
1203 return (p);
1204 } else if (*p == '+') {
1205 *cmd = CMD_NEXT;
1206 return (p + 1);
1207 } else if (*p == '-') {
1208 *cmd = CMD_PREVIOUS;
1209 return (p + 1);
1212 for (buf = p; *p && ! isspace (*p); p++)
1213 continue;
1215 len = p - buf;
1216 if (! len)
1217 return (0);
1219 if (*p) { /* It must be a spacing character! */
1220 char *q;
1222 *p++ = 0;
1223 for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1224 continue;
1225 *q = 0;
1228 *cmd = -1;
1229 for (c=cmdtab; c->name; ++c) {
1230 /* Is it an exact match? */
1231 if (! strcasecmp (buf, c->name)) {
1232 *cmd = c->command;
1233 break;
1236 /* Try short hand forms then... */
1237 if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
1238 if (*cmd != -1 && *cmd != c->command) {
1239 warnx("ambiguous command");
1240 return (0);
1242 *cmd = c->command;
1246 if (*cmd == -1) {
1247 warnx("invalid command, enter ``help'' for commands");
1248 return (0);
1251 while (isspace (*p))
1252 p++;
1253 return p;
1256 static int
1257 open_cd(void)
1259 char devbuf[MAXPATHLEN];
1261 if (fd > -1)
1262 return (1);
1264 if (*cdname == '/') {
1265 snprintf (devbuf, MAXPATHLEN, "%s", cdname);
1266 } else {
1267 snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
1270 fd = open (devbuf, O_RDONLY);
1272 if (fd < 0 && errno == ENOENT) {
1273 strcat (devbuf, DEFAULT_CD_PARTITION);
1274 fd = open (devbuf, O_RDONLY);
1277 if (fd < 0) {
1278 if (errno == ENXIO) {
1279 /* ENXIO has an overloaded meaning here.
1280 * The original "Device not configured" should
1281 * be interpreted as "No disc in drive %s". */
1282 warnx("no disc in drive %s", devbuf);
1283 return (0);
1285 err(1, "%s", devbuf);
1287 return (1);