xref: /openbsd-src/usr.bin/cdio/cdio.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: cdio.c,v 1.30 2003/06/10 22:20:45 deraadt Exp $	*/
2 
3 /*  Copyright (c) 1995 Serge V. Vakulenko
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Serge V. Vakulenko.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
35  * Based on the non-X based CD player by Jean-Marc Zucconi and
36  * Andrey A. Chernov.
37  *
38  * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
39  *
40  * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
41  *              A couple of further fixes to my own earlier "fixes".
42  *
43  * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
44  *              Added an ability to specify addresses relative to the
45  *              beginning of a track. This is in fact a variation of
46  *              doing the simple play_msf() call.
47  *
48  * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
49  *              New eject algorithm.
50  *              Some code style reformatting.
51  *
52  * $FreeBSD: cdcontrol.c,v 1.13 1996/06/25 21:01:27 ache Exp $
53  */
54 
55 #include <sys/param.h>
56 #include <sys/file.h>
57 #include <sys/cdio.h>
58 #include <sys/ioctl.h>
59 
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 #include <histedit.h>
68 #include <util.h>
69 #include <vis.h>
70 
71 #include "extern.h"
72 
73 #define ASTS_INVALID    0x00  /* Audio status byte not valid */
74 #define ASTS_PLAYING    0x11  /* Audio play operation in progress */
75 #define ASTS_PAUSED     0x12  /* Audio play operation paused */
76 #define ASTS_COMPLETED  0x13  /* Audio play operation successfully completed */
77 #define ASTS_ERROR      0x14  /* Audio play operation stopped due to error */
78 #define ASTS_VOID       0x15  /* No current audio status to return */
79 
80 #ifndef DEFAULT_CD_DRIVE
81 #  define DEFAULT_CD_DRIVE  "cd0"
82 #endif
83 
84 #define CMD_DEBUG       1
85 #define CMD_DEVICE      2
86 #define CMD_EJECT       3
87 #define CMD_HELP        4
88 #define CMD_INFO        5
89 #define CMD_PAUSE       6
90 #define CMD_PLAY        7
91 #define CMD_QUIT        8
92 #define CMD_RESUME      9
93 #define CMD_STOP        10
94 #define CMD_VOLUME      11
95 #define CMD_CLOSE       12
96 #define CMD_RESET       13
97 #define CMD_SET         14
98 #define CMD_STATUS      15
99 #define CMD_NEXT	16
100 #define CMD_PREV	17
101 #define CMD_REPLAY	18
102 #define CMD_CDDB	19
103 #define CMD_CDID	20
104 
105 struct cmdtab {
106 	int command;
107 	char *name;
108 	unsigned int min;
109 	char *args;
110 } cmdtab[] = {
111 { CMD_CLOSE,    "close",        1, "" },
112 { CMD_DEBUG,    "debug",        1, "on | off" },
113 { CMD_DEVICE,   "device",       1, "devname" },
114 { CMD_EJECT,    "eject",        1, "" },
115 { CMD_HELP,     "?",            1, 0 },
116 { CMD_HELP,     "help",         1, "" },
117 { CMD_INFO,     "info",         1, "" },
118 { CMD_NEXT,	"next",		1, "" },
119 { CMD_PAUSE,    "pause",        2, "" },
120 { CMD_PLAY,     "play",         1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
121 { CMD_PLAY,     "play",         1, "track1[.index1] [track2[.index2]]" },
122 { CMD_PLAY,     "play",         1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
123 { CMD_PLAY,     "play",         1, "[#block [len]]" },
124 { CMD_PREV,	"previous",	2, "" },
125 { CMD_QUIT,     "quit",         1, "" },
126 { CMD_RESET,    "reset",        4, "" },
127 { CMD_RESUME,   "resume",       1, "" },
128 { CMD_REPLAY,	"replay",	3, "" },
129 { CMD_SET,      "set",          2, "msf | lba" },
130 { CMD_STATUS,   "status",       1, "" },
131 { CMD_STOP,     "stop",         3, "" },
132 { CMD_VOLUME,   "volume",       1, "<l> <r> | left | right | mute | mono | stereo" },
133 { CMD_CDDB,   	"cddbinfo",     2, "[n]" },
134 { CMD_CDID,	"cdid",		3, "" },
135 { 0, 0, 0, 0}
136 };
137 
138 struct cd_toc_entry     *toc_buffer;
139 
140 char		*cdname;
141 int             fd = -1;
142 int             verbose = 1;
143 int             msf = 1;
144 const char 	*cddb_host;
145 char		**track_names;
146 
147 EditLine       *el = NULL;	/* line-editing structure */
148 History	       *hist = NULL;	/* line-editing history */
149 void		switch_el(void);
150 
151 extern char     *__progname;
152 
153 int             setvol(int, int);
154 int             read_toc_entrys(int);
155 int             play_msf(int, int, int, int, int, int);
156 int             play_track(int, int, int, int);
157 int             get_vol(int *, int *);
158 int             status(int *, int *, int *, int *);
159 int             open_cd(char *);
160 int             play(char *arg);
161 int             info(char *arg);
162 int             cddbinfo(char *arg);
163 int             pstatus(char *arg);
164 int		play_next(char *arg);
165 int		play_prev(char *arg);
166 int		play_same(char *arg);
167 char            *input(int *);
168 char		*prompt(void);
169 void            prtrack(struct cd_toc_entry *e, int lastflag, char *name);
170 void            lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
171 unsigned int    msf2lba(u_char m, u_char s, u_char f);
172 int             play_blocks(int blk, int len);
173 int             run(int cmd, char *arg);
174 char            *parse(char *buf, int *cmd);
175 void 		help(void);
176 void		usage(void);
177 char 		*strstatus(int);
178 int		cdid(void);
179 
180 void
181 help(void)
182 {
183 	struct cmdtab *c;
184 	char *s, n;
185 	int i;
186 
187 	for (c=cmdtab; c->name; ++c) {
188 		if (! c->args)
189 			continue;
190 		printf("\t");
191 		for (i = c->min, s = c->name; *s; s++, i--) {
192 			if (i > 0)
193 				n = toupper(*s);
194 			else
195 				n = *s;
196 			putchar(n);
197 		}
198 		if (*c->args)
199 			printf(" %s", c->args);
200 		printf("\n");
201 	}
202 	printf("\n\tThe word \"play\" is not required for the play commands.\n");
203 	printf("\tThe plain target address is taken as a synonym for play.\n");
204 }
205 
206 void
207 usage(void)
208 {
209 	fprintf(stderr, "usage: %s [-sv] [-f device] [command args ...]\n",
210 	    __progname);
211 	exit(1);
212 }
213 
214 int
215 main(int argc, char **argv)
216 {
217 	int cmd;
218 	char *arg;
219 
220 	cdname = getenv("DISC");
221 	if (! cdname)
222 		cdname = getenv("CDROM");
223 
224 	cddb_host = getenv("CDDB");
225 	if (!cddb_host)
226 		cddb_host = "freedb.freedb.org";
227 
228 	for (;;) {
229 		switch (getopt(argc, argv, "svd:f:")) {
230 		case -1:
231 			break;
232 		case 's':
233 			verbose = 0;
234 			continue;
235 		case 'v':
236 			verbose = 2;
237 			continue;
238 		case 'f':
239 			cdname = optarg;
240 			continue;
241 	    	case 'd':
242 			cddb_host = optarg;
243 			continue;
244 		default:
245 			usage();
246 		}
247 		break;
248 	}
249 	argc -= optind;
250 	argv += optind;
251 
252 	if (argc > 0 && ! strcasecmp(*argv, "help"))
253 		usage();
254 
255 	if (! cdname) {
256 		cdname = DEFAULT_CD_DRIVE;
257 		fprintf(stderr,
258 		    "No CD device name specified. Defaulting to %s.\n", cdname);
259 	}
260 
261 	if (argc > 0) {
262 		char buf[80], *p;
263 		int len;
264 
265 		for (p=buf; argc-->0; ++argv) {
266 			len = snprintf(p, buf + sizeof buf - p,
267 			   "%s%s", (p > buf) ? " " : "", *argv);
268 
269 			if (len >= buf + sizeof buf - p)
270 				errx(1, "argument list too long.");
271 
272 			p += len;
273 		}
274 		arg = parse(buf, &cmd);
275 		return (run(cmd, arg));
276 	}
277 
278 	if (verbose == 1)
279 		verbose = isatty(0);
280 
281 	if (verbose) {
282 		printf("Compact Disc Control utility, version %s\n", VERSION);
283 		printf("Type `?' for command list\n\n");
284 	}
285 
286 	switch_el();
287 
288 	for (;;) {
289 		arg = input(&cmd);
290 		if (run(cmd, arg) < 0) {
291 			if (verbose)
292 				warn(NULL);
293 			close(fd);
294 			fd = -1;
295 		}
296 		fflush(stdout);
297 	}
298 }
299 
300 int
301 run(int cmd, char *arg)
302 {
303 	int l, r, rc;
304 	static char newcdname[MAXPATHLEN];
305 
306 	switch (cmd) {
307 
308 	case CMD_QUIT:
309 		switch_el();
310 		exit(0);
311 
312 	case CMD_INFO:
313 		if (fd < 0 && ! open_cd(cdname))
314 			return (0);
315 
316 		return info(arg);
317 
318 	case CMD_CDDB:
319 		if (fd < 0 && ! open_cd(cdname))
320 			return (0);
321 
322 		return cddbinfo(arg);
323 
324 	case CMD_CDID:
325 		if (fd < 0 && ! open_cd(cdname))
326 			return (0);
327 		return cdid();
328 
329 	case CMD_STATUS:
330 		if (fd < 0 && ! open_cd(cdname))
331 			return (0);
332 
333 		return pstatus(arg);
334 
335 	case CMD_PAUSE:
336 		if (fd < 0 && ! open_cd(cdname))
337 			return (0);
338 
339 		return ioctl(fd, CDIOCPAUSE);
340 
341 	case CMD_RESUME:
342 		if (fd < 0 && ! open_cd(cdname))
343 			return (0);
344 
345 		return ioctl(fd, CDIOCRESUME);
346 
347 	case CMD_STOP:
348 		if (fd < 0 && ! open_cd(cdname))
349 			return (0);
350 
351 		rc = ioctl(fd, CDIOCSTOP);
352 
353 		(void) ioctl(fd, CDIOCALLOW);
354 
355 		return (rc);
356 
357 	case CMD_RESET:
358 		if (fd < 0 && ! open_cd(cdname))
359 			return (0);
360 
361 		rc = ioctl(fd, CDIOCRESET);
362 		if (rc < 0)
363 			return rc;
364 		close(fd);
365 		fd = -1;
366 		return (0);
367 
368 	case CMD_DEBUG:
369 		if (fd < 0 && ! open_cd(cdname))
370 			return (0);
371 
372 		if (! strcasecmp(arg, "on"))
373 			return ioctl(fd, CDIOCSETDEBUG);
374 
375 		if (! strcasecmp(arg, "off"))
376 			return ioctl(fd, CDIOCCLRDEBUG);
377 
378 		printf("%s: Invalid command arguments\n", __progname);
379 
380 		return (0);
381 
382 	case CMD_DEVICE:
383 		/* close old device */
384 		if (fd > -1) {
385 			(void) ioctl(fd, CDIOCALLOW);
386 			close(fd);
387 			fd = -1;
388 		}
389 
390 		/* open new device */
391 		if (!open_cd(arg))
392 			return (0);
393 		(void) strlcpy(newcdname, arg, sizeof(newcdname));
394 		cdname = newcdname;
395 		return (1);
396 
397 	case CMD_EJECT:
398 		if (fd < 0 && ! open_cd(cdname))
399 			return (0);
400 
401 		(void) ioctl(fd, CDIOCALLOW);
402 		rc = ioctl(fd, CDIOCEJECT);
403 		if (rc < 0)
404 			return (rc);
405 #if defined(__OpenBSD__)
406 		close(fd);
407 		fd = -1;
408 #endif
409 		if (track_names)
410 			free_names(track_names);
411 		track_names = NULL;
412 		return (0);
413 
414 	case CMD_CLOSE:
415 #if defined(CDIOCCLOSE)
416 		if (fd < 0 && ! open_cd(cdname))
417 			return (0);
418 
419 		(void) ioctl(fd, CDIOCALLOW);
420 		rc = ioctl(fd, CDIOCCLOSE);
421 		if (rc < 0)
422 			return (rc);
423 		close(fd);
424 		fd = -1;
425 		return (0);
426 #else
427 		printf("%s: Command not yet supported\n", __progname);
428 		return (0);
429 #endif
430 
431 	case CMD_PLAY:
432 		if (fd < 0 && ! open_cd(cdname))
433 			return (0);
434 
435 		while (isspace(*arg))
436 			arg++;
437 
438 		return play(arg);
439 
440 	case CMD_SET:
441 		if (!strcasecmp(arg, "msf"))
442 			msf = 1;
443 		else if (!strcasecmp(arg, "lba"))
444 			msf = 0;
445 		else
446 			printf("%s: Invalid command arguments\n", __progname);
447 		return (0);
448 
449 	case CMD_VOLUME:
450 		if (fd < 0 && !open_cd(cdname))
451 			return (0);
452 
453 		if (!strncasecmp(arg, "left", strlen(arg)))
454 			return ioctl(fd, CDIOCSETLEFT);
455 
456 		if (!strncasecmp(arg, "right", strlen(arg)))
457 			return ioctl(fd, CDIOCSETRIGHT);
458 
459 		if (!strncasecmp(arg, "mono", strlen(arg)))
460 			return ioctl(fd, CDIOCSETMONO);
461 
462 		if (!strncasecmp(arg, "stereo", strlen(arg)))
463 			return ioctl(fd, CDIOCSETSTEREO);
464 
465 		if (!strncasecmp(arg, "mute", strlen(arg)))
466 			return ioctl(fd, CDIOCSETMUTE);
467 
468 		if (2 != sscanf(arg, "%d %d", &l, &r)) {
469 			printf("%s: Invalid command arguments\n", __progname);
470 			return (0);
471 		}
472 
473 		return setvol(l, r);
474 
475 	case CMD_NEXT:
476 		if (fd < 0 && ! open_cd(cdname))
477 			return (0);
478 
479 		return play_next(arg);
480 
481 	case CMD_PREV:
482 		if (fd < 0 && ! open_cd(cdname))
483 			return (0);
484 
485 		return play_prev(arg);
486 
487 	case CMD_REPLAY:
488 		if (fd < 0 && ! open_cd(cdname))
489 			return 0;
490 
491 		return play_same(arg);
492 	default:
493 	case CMD_HELP:
494 		help();
495 		return (0);
496 
497 	}
498 }
499 
500 int
501 play(char *arg)
502 {
503 	struct ioc_toc_header h;
504 	int rc, n, start, end = 0, istart = 1, iend = 1;
505 
506 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
507 
508 	if (rc < 0)
509 		return (rc);
510 
511 	n = h.ending_track - h.starting_track + 1;
512 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
513 
514 	if (rc < 0)
515 		return (rc);
516 
517 	if (! arg || ! *arg) {
518 		/* Play the whole disc */
519 
520 		return (play_track(h.starting_track, 1,
521 		    h.ending_track, 1));
522 	}
523 
524 	if (strchr(arg, '#')) {
525 		/* Play block #blk [ len ] */
526 		int blk, len = 0;
527 
528 		if (2 != sscanf(arg, "#%d%d", &blk, &len) &&
529 		    1 != sscanf(arg, "#%d", &blk))
530 			goto Clean_up;
531 
532 		if (len == 0) {
533 			if (msf)
534 				len = msf2lba(toc_buffer[n].addr.msf.minute,
535 				    toc_buffer[n].addr.msf.second,
536 				    toc_buffer[n].addr.msf.frame) - blk;
537 			else
538 				len = ntohl(toc_buffer[n].addr.lba) - blk;
539 		}
540 		return play_blocks(blk, len);
541 	}
542 
543 	if (strchr(arg, ':')) {
544 		/*
545 		 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
546 		 *
547 		 * Will now also undestand timed addresses relative
548 		 * to the beginning of a track in the form...
549 		 *
550 		 *      tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
551 		 */
552 		int tr1, tr2;
553 		unsigned int m1, m2, s1, s2, f1, f2;
554 		unsigned char tm, ts, tf;
555 
556 		tr2 = m2 = s2 = f2 = f1 = 0;
557 		if (8 == sscanf(arg, "%d %d:%d.%d %d %d:%d.%d",
558 		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
559 			goto Play_Relative_Addresses;
560 
561 		tr2 = m2 = s2 = f2 = f1 = 0;
562 		if (7 == sscanf(arg, "%d %d:%d %d %d:%d.%d",
563 		    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
564 			goto Play_Relative_Addresses;
565 
566 		tr2 = m2 = s2 = f2 = f1 = 0;
567 		if (7 == sscanf(arg, "%d %d:%d.%d %d %d:%d",
568 		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
569 			goto Play_Relative_Addresses;
570 
571 		tr2 = m2 = s2 = f2 = f1 = 0;
572 		if (7 == sscanf(arg, "%d %d:%d.%d %d:%d.%d",
573 		    &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
574 			goto Play_Relative_Addresses;
575 
576 		tr2 = m2 = s2 = f2 = f1 = 0;
577 		if (6 == sscanf(arg, "%d %d:%d.%d %d:%d",
578 		    &tr1, &m1, &s1, &f1, &m2, &s2))
579 			goto Play_Relative_Addresses;
580 
581 		tr2 = m2 = s2 = f2 = f1 = 0;
582 		if (6 == sscanf(arg, "%d %d:%d %d:%d.%d",
583 		    &tr1, &m1, &s1, &m2, &s2, &f2))
584 			goto Play_Relative_Addresses;
585 
586 		tr2 = m2 = s2 = f2 = f1 = 0;
587 		if (6 == sscanf(arg, "%d %d:%d.%d %d %d",
588 		    &tr1, &m1, &s1, &f1, &tr2, &m2))
589 			goto Play_Relative_Addresses;
590 
591 		tr2 = m2 = s2 = f2 = f1 = 0;
592 		if (5 == sscanf(arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
593 			goto Play_Relative_Addresses;
594 
595 		tr2 = m2 = s2 = f2 = f1 = 0;
596 		if (5 == sscanf(arg, "%d %d:%d %d %d",
597 		    &tr1, &m1, &s1, &tr2, &m2))
598 			goto Play_Relative_Addresses;
599 
600 		tr2 = m2 = s2 = f2 = f1 = 0;
601 		if (5 == sscanf(arg, "%d %d:%d.%d %d",
602 		    &tr1, &m1, &s1, &f1, &tr2))
603 			goto Play_Relative_Addresses;
604 
605 		tr2 = m2 = s2 = f2 = f1 = 0;
606 		if (4 == sscanf(arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
607 			goto Play_Relative_Addresses;
608 
609 		tr2 = m2 = s2 = f2 = f1 = 0;
610 		if (4 == sscanf(arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
611 			goto Play_Relative_Addresses;
612 
613 		tr2 = m2 = s2 = f2 = f1 = 0;
614 		if (3 == sscanf(arg, "%d %d:%d", &tr1, &m1, &s1))
615 			goto Play_Relative_Addresses;
616 
617 		tr2 = m2 = s2 = f2 = f1 = 0;
618 		goto Try_Absolute_Timed_Addresses;
619 
620 Play_Relative_Addresses:
621 		if (tr1 <= 0)
622 			tr1 = 1;
623 		else if (tr1 > n)
624 			tr1 = n;
625 
626 		if (msf) {
627 			tm = toc_buffer[tr1].addr.msf.minute;
628 			ts = toc_buffer[tr1].addr.msf.second;
629 			tf = toc_buffer[tr1].addr.msf.frame;
630 		} else
631 			lba2msf(ntohl(toc_buffer[tr1].addr.lba),
632 				&tm, &ts, &tf);
633 		if ((m1 > tm)
634 		    || ((m1 == tm)
635 		    && ((s1 > ts)
636 		    || ((s1 == ts)
637 		    && (f1 > tf))))) {
638 			printf("Track %d is not that long.\n", tr1);
639 			return (0);
640 		}
641 
642 		tr1--;
643 
644 		f1 += tf;
645 		if (f1 >= 75) {
646 			s1 += f1 / 75;
647 			f1 %= 75;
648 		}
649 
650 		s1 += ts;
651 		if (s1 >= 60) {
652 			m1 += s1 / 60;
653 			s1 %= 60;
654 		}
655 
656 		m1 += tm;
657 
658 		if (tr2 <= 0) {
659 			if (m2 || s2 || f2) {
660 				tr2 = tr1;
661 				f2 += f1;
662 				if (f2 >= 75) {
663 					s2 += f2 / 75;
664 					f2 %= 75;
665 				}
666 
667 				s2 += s1;
668 				if (s2 > 60) {
669 					m2 += s2 / 60;
670 					s2 %= 60;
671 				}
672 
673 				m2 += m1;
674 			} else {
675 				tr2 = n;
676 				if (msf) {
677 					m2 = toc_buffer[n].addr.msf.minute;
678 					s2 = toc_buffer[n].addr.msf.second;
679 					f2 = toc_buffer[n].addr.msf.frame;
680 				} else {
681 					lba2msf(ntohl(toc_buffer[n].addr.lba),
682 						&tm, &ts, &tf);
683 					m2 = tm;
684 					s2 = ts;
685 					f2 = tf;
686 				}
687 			}
688 		} else if (tr2 > n) {
689 			tr2 = n;
690 			m2 = s2 = f2 = 0;
691 		} else {
692 			if (m2 || s2 || f2)
693 				tr2--;
694 			if (msf) {
695 				tm = toc_buffer[tr2].addr.msf.minute;
696 				ts = toc_buffer[tr2].addr.msf.second;
697 				tf = toc_buffer[tr2].addr.msf.frame;
698 			} else
699 				lba2msf(ntohl(toc_buffer[tr2].addr.lba),
700 					&tm, &ts, &tf);
701 			f2 += tf;
702 			if (f2 >= 75) {
703 				s2 += f2 / 75;
704 				f2 %= 75;
705 			}
706 
707 			s2 += ts;
708 			if (s2 > 60) {
709 				m2 += s2 / 60;
710 				s2 %= 60;
711 			}
712 
713 			m2 += tm;
714 		}
715 
716 		if (msf) {
717 			tm = toc_buffer[n].addr.msf.minute;
718 			ts = toc_buffer[n].addr.msf.second;
719 			tf = toc_buffer[n].addr.msf.frame;
720 		} else
721 			lba2msf(ntohl(toc_buffer[n].addr.lba),
722 				&tm, &ts, &tf);
723 		if ((tr2 < n)
724 		    && ((m2 > tm)
725 		    || ((m2 == tm)
726 		    && ((s2 > ts)
727 		    || ((s2 == ts)
728 		    && (f2 > tf)))))) {
729 			printf("The playing time of the disc is not that long.\n");
730 			return (0);
731 		}
732 		return (play_msf(m1, s1, f1, m2, s2, f2));
733 
734 Try_Absolute_Timed_Addresses:
735 		if (6 != sscanf(arg, "%d:%d.%d%d:%d.%d",
736 		    &m1, &s1, &f1, &m2, &s2, &f2) &&
737 		    5 != sscanf(arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
738 		    5 != sscanf(arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
739 		    3 != sscanf(arg, "%d:%d.%d", &m1, &s1, &f1) &&
740 		    4 != sscanf(arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
741 		    2 != sscanf(arg, "%d:%d", &m1, &s1))
742 			goto Clean_up;
743 
744 		if (m2 == 0) {
745 			if (msf) {
746 				m2 = toc_buffer[n].addr.msf.minute;
747 				s2 = toc_buffer[n].addr.msf.second;
748 				f2 = toc_buffer[n].addr.msf.frame;
749 			} else {
750 				lba2msf(ntohl(toc_buffer[n].addr.lba),
751 					&tm, &ts, &tf);
752 				m2 = tm;
753 				s2 = ts;
754 				f2 = tf;
755 			}
756 		}
757 		return play_msf(m1, s1, f1, m2, s2, f2);
758 	}
759 
760 	/*
761 	 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
762 	 */
763 	if (4 != sscanf(arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
764 	    3 != sscanf(arg, "%d.%d%d", &start, &istart, &end) &&
765 	    3 != sscanf(arg, "%d%d.%d", &start, &end, &iend) &&
766 	    2 != sscanf(arg, "%d.%d", &start, &istart) &&
767 	    2 != sscanf(arg, "%d%d", &start, &end) &&
768 	    1 != sscanf(arg, "%d", &start))
769 		goto Clean_up;
770 
771 	if (end == 0)
772 		end = h.ending_track;
773 	return (play_track(start, istart, end, iend));
774 
775 Clean_up:
776 	printf("%s: Invalid command arguments\n", __progname);
777 	return (0);
778 }
779 
780 int
781 play_prev(char *arg)
782 {
783 	int trk, min, sec, frm, rc;
784 	struct ioc_toc_header h;
785 
786 	if (status(&trk, &min, &sec, &frm) >= 0) {
787 		trk--;
788 
789 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
790 		if (rc < 0) {
791 			warn("getting toc header");
792 			return (rc);
793 		}
794 
795 		if (trk < h.starting_track)
796 			return play_track(h.starting_track, 1,
797 			    h.ending_track + 1, 1);
798 		return play_track(trk, 1, h.ending_track, 1);
799 	}
800 
801 	return (0);
802 }
803 
804 int
805 play_same(char *arg)
806 {
807 	int trk, min, sec, frm, rc;
808 	struct ioc_toc_header h;
809 
810 	if (status (&trk, &min, &sec, &frm) >= 0) {
811 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
812 		if (rc < 0) {
813 			warn("getting toc header");
814 			return (rc);
815 		}
816 
817 		return play_track(trk, 1, h.ending_track, 1);
818 	}
819 
820 	return (0);
821 }
822 
823 int
824 play_next(char *arg)
825 {
826 	int trk, min, sec, frm, rc;
827 	struct ioc_toc_header h;
828 
829 	if (status(&trk, &min, &sec, &frm) >= 0) {
830 		trk++;
831 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
832 		if (rc < 0) {
833 			warn("getting toc header");
834 			return (rc);
835 		}
836 
837 		if (trk > h.ending_track) {
838 			printf("%s: end of CD\n", __progname);
839 
840 			rc = ioctl(fd, CDIOCSTOP);
841 
842 			(void) ioctl(fd, CDIOCALLOW);
843 
844 			return (rc);
845 		}
846 
847 		return play_track(trk, 1, h.ending_track, 1);
848 	}
849 
850 	return (0);
851 }
852 
853 char *
854 strstatus(int sts)
855 {
856 	switch (sts) {
857 	case ASTS_INVALID:
858 		return ("invalid");
859 	case ASTS_PLAYING:
860 		return ("playing");
861 	case ASTS_PAUSED:
862 		return ("paused");
863 	case ASTS_COMPLETED:
864 		return ("completed");
865 	case ASTS_ERROR:
866 		return ("error");
867 	case ASTS_VOID:
868 		return ("void");
869 	default:
870 		return ("??");
871 	}
872 }
873 
874 int
875 pstatus(char *arg)
876 {
877 	struct ioc_vol v;
878 	struct ioc_read_subchannel ss;
879 	struct cd_sub_channel_info data;
880 	int rc, trk, m, s, f;
881 	char vis_catalog[1 + 4 * 15];
882 
883 	rc = status(&trk, &m, &s, &f);
884 	if (rc >= 0) {
885 		if (verbose) {
886 			if (track_names)
887 				printf("Audio status = %d<%s>, "
888 				    "current track = %d (%s)\n"
889 				    "\tcurrent position = %d:%02d.%02d\n",
890 				    rc, strstatus(rc), trk,
891 				    trk ? track_names[trk-1] : "", m, s, f);
892 			else
893 				printf("Audio status = %d<%s>, "
894 				    "current track = %d, "
895 				    "current position = %d:%02d.%02d\n",
896 				    rc, strstatus(rc), trk, m, s, f);
897 		} else
898 			printf("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
899 	} else
900 		printf("No current status info available\n");
901 
902 	bzero(&ss, sizeof (ss));
903 	ss.data = &data;
904 	ss.data_len = sizeof (data);
905 	ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
906 	ss.data_format = CD_MEDIA_CATALOG;
907 	rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss);
908 	if (rc >= 0) {
909 		printf("Media catalog is %sactive",
910 		ss.data->what.media_catalog.mc_valid ? "": "in");
911 		if (ss.data->what.media_catalog.mc_valid &&
912 		    ss.data->what.media_catalog.mc_number[0]) {
913 		    	strvisx(vis_catalog,
914 			   ss.data->what.media_catalog.mc_number,
915 			   15, VIS_SAFE);
916 			printf(", number \"%.15s\"", vis_catalog);
917 		}
918 		putchar('\n');
919 	} else
920 		printf("No media catalog info available\n");
921 
922 	rc = ioctl(fd, CDIOCGETVOL, &v);
923 	if (rc >= 0) {
924 		if (verbose)
925 			printf("Left volume = %d, right volume = %d\n",
926 				v.vol[0], v.vol[1]);
927 		else
928 			printf("%d %d\n", v.vol[0], v.vol[1]);
929 	} else
930 		printf("No volume level info available\n");
931 	return(0);
932 }
933 
934 int
935 cdid(void)
936 {
937 	unsigned long id;
938 	struct ioc_toc_header h;
939 	int rc, n;
940 
941 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
942 	if (rc == -1) {
943 		warn("getting toc header");
944 		return (rc);
945 	}
946 
947 	n = h.ending_track - h.starting_track + 1;
948 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
949 	if (rc < 0)
950 		return (rc);
951 
952 	id = cddb_discid(n, toc_buffer);
953 	if (id) {
954 		if (verbose)
955 			printf("CDID=");
956 		printf("%08lx\n", id);
957 	}
958 	return id ? 0 : 1;
959 }
960 
961 int
962 info(char *arg)
963 {
964 	struct ioc_toc_header h;
965 	int rc, i, n;
966 
967 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
968 	if (rc >= 0) {
969 		if (verbose)
970 			printf("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
971 			    h.starting_track, h.ending_track, h.len);
972 		else
973 			printf("%d %d %d\n", h.starting_track,
974 			    h.ending_track, h.len);
975 	} else {
976 		warn("getting toc header");
977 		return (rc);
978 	}
979 
980 	n = h.ending_track - h.starting_track + 1;
981 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
982 	if (rc < 0)
983 		return (rc);
984 
985 	if (verbose) {
986 		printf("track     start  duration   block  length   type\n");
987 		printf("-------------------------------------------------\n");
988 	}
989 
990 	for (i = 0; i < n; i++) {
991 		printf("%5d  ", toc_buffer[i].track);
992 		prtrack(toc_buffer + i, 0, NULL);
993 	}
994 	printf("%5d  ", toc_buffer[n].track);
995 	prtrack(toc_buffer + n, 1, NULL);
996 	return (0);
997 }
998 
999 int
1000 cddbinfo(char *arg)
1001 {
1002 	struct ioc_toc_header h;
1003 	int rc, i, n;
1004 
1005 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1006 	if (rc == -1) {
1007 		warn("getting toc header");
1008 		return (rc);
1009 	}
1010 
1011 	n = h.ending_track - h.starting_track + 1;
1012 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1013 	if (rc < 0)
1014 		return (rc);
1015 
1016 	if (track_names)
1017 		free_names(track_names);
1018 	track_names = NULL;
1019 
1020 	track_names = cddb(cddb_host, n, toc_buffer, arg);
1021 	if (!track_names)
1022 		return(0);
1023 
1024 	printf("-------------------------------------------------\n");
1025 
1026 	for (i = 0; i < n; i++) {
1027 		printf("%5d  ", toc_buffer[i].track);
1028 		prtrack(toc_buffer + i, 0, track_names[i]);
1029 	}
1030 	printf("%5d  ", toc_buffer[n].track);
1031 	prtrack(toc_buffer + n, 1, "");
1032 	return (0);
1033 }
1034 
1035 void
1036 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
1037 {
1038 	lba += 150;		/* block start offset */
1039 	lba &= 0xffffff;	/* negative lbas use only 24 bits */
1040 	*m = lba / (60 * 75);
1041 	lba %= (60 * 75);
1042 	*s = lba / 75;
1043 	*f = lba % 75;
1044 }
1045 
1046 unsigned int
1047 msf2lba(u_char m, u_char s, u_char f)
1048 {
1049 	return (((m * 60) + s) * 75 + f) - 150;
1050 }
1051 
1052 unsigned long
1053 entry2time(struct cd_toc_entry *e)
1054 {
1055 	int block;
1056 	u_char m, s, f;
1057 
1058 	if (msf) {
1059 		return (e->addr.msf.minute * 60 + e->addr.msf.second);
1060 	} else {
1061 		block = ntohl(e->addr.lba);
1062 		lba2msf(block, &m, &s, &f);
1063 		return (m*60+s);
1064 	}
1065 }
1066 
1067 unsigned long
1068 entry2frames(struct cd_toc_entry *e)
1069 {
1070 	int block;
1071 	unsigned char m, s, f;
1072 
1073 	if (msf) {
1074 		return e->addr.msf.frame + e->addr.msf.second * 75 +
1075 		    e->addr.msf.minute * 60 * 75;
1076 	} else {
1077 		block = ntohl(e->addr.lba);
1078 		lba2msf(block, &m, &s, &f);
1079 		return f + s * 75 + m * 60 * 75;
1080 	}
1081 }
1082 
1083 void
1084 prtrack(struct cd_toc_entry *e, int lastflag, char *name)
1085 {
1086 	int block, next, len;
1087 	u_char m, s, f;
1088 
1089 	if (msf) {
1090 		if (!name || lastflag)
1091 			/* Print track start */
1092 			printf("%2d:%02d.%02d  ", e->addr.msf.minute,
1093 			    e->addr.msf.second, e->addr.msf.frame);
1094 
1095 		block = msf2lba(e->addr.msf.minute, e->addr.msf.second,
1096 			e->addr.msf.frame);
1097 	} else {
1098 		block = ntohl(e->addr.lba);
1099 		if (!name || lastflag) {
1100 			lba2msf(block, &m, &s, &f);
1101 			/* Print track start */
1102 			printf("%2d:%02d.%02d  ", m, s, f);
1103 	    	}
1104 	}
1105 	if (lastflag) {
1106 		if (!name)
1107 			/* Last track -- print block */
1108 			printf("       -  %6d       -      -\n", block);
1109 		else
1110 			printf("\n");
1111 		return;
1112 	}
1113 
1114 	if (msf)
1115 		next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second,
1116 			e[1].addr.msf.frame);
1117 	else
1118 		next = ntohl(e[1].addr.lba);
1119 	len = next - block;
1120 	lba2msf(len, &m, &s, &f);
1121 
1122 	if (name)
1123 		printf("%2d:%02d.%02d  %s\n", m, s, f, name);
1124 	/* Print duration, block, length, type */
1125 	else
1126 		printf("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
1127 		    (e->control & 4) ? "data" : "audio");
1128 }
1129 
1130 int
1131 play_track(int tstart, int istart, int tend, int iend)
1132 {
1133 	struct ioc_play_track t;
1134 
1135 	t.start_track = tstart;
1136 	t.start_index = istart;
1137 	t.end_track = tend;
1138 	t.end_index = iend;
1139 
1140 	return ioctl(fd, CDIOCPLAYTRACKS, &t);
1141 }
1142 
1143 int
1144 play_blocks(int blk, int len)
1145 {
1146 	struct ioc_play_blocks  t;
1147 
1148 	t.blk = blk;
1149 	t.len = len;
1150 
1151 	return ioctl(fd, CDIOCPLAYBLOCKS, &t);
1152 }
1153 
1154 int
1155 setvol(int left, int right)
1156 {
1157 	struct ioc_vol  v;
1158 
1159 	v.vol[0] = left;
1160 	v.vol[1] = right;
1161 	v.vol[2] = 0;
1162 	v.vol[3] = 0;
1163 
1164 	return ioctl(fd, CDIOCSETVOL, &v);
1165 }
1166 
1167 int
1168 read_toc_entrys(int len)
1169 {
1170 	struct ioc_read_toc_entry t;
1171 
1172 	if (toc_buffer) {
1173 		free(toc_buffer);
1174 		toc_buffer = 0;
1175 	}
1176 
1177 	toc_buffer = malloc(len);
1178 
1179 	if (!toc_buffer) {
1180 		errno = ENOMEM;
1181 		return (-1);
1182 	}
1183 
1184 	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1185 	t.starting_track = 0;
1186 	t.data_len = len;
1187 	t.data = toc_buffer;
1188 
1189 	return (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t));
1190 }
1191 
1192 int
1193 play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, int end_f)
1194 {
1195 	struct ioc_play_msf a;
1196 
1197 	a.start_m = start_m;
1198 	a.start_s = start_s;
1199 	a.start_f = start_f;
1200 	a.end_m = end_m;
1201 	a.end_s = end_s;
1202 	a.end_f = end_f;
1203 
1204 	return ioctl(fd, CDIOCPLAYMSF, (char *) &a);
1205 }
1206 
1207 int
1208 status(int *trk, int *min, int *sec, int *frame)
1209 {
1210 	struct ioc_read_subchannel s;
1211 	struct cd_sub_channel_info data;
1212 	u_char mm, ss, ff;
1213 
1214 	bzero(&s, sizeof (s));
1215 	s.data = &data;
1216 	s.data_len = sizeof (data);
1217 	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1218 	s.data_format = CD_CURRENT_POSITION;
1219 
1220 	if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
1221 		return -1;
1222 
1223 	*trk = s.data->what.position.track_number;
1224 	if (msf) {
1225 		*min = s.data->what.position.reladdr.msf.minute;
1226 		*sec = s.data->what.position.reladdr.msf.second;
1227 		*frame = s.data->what.position.reladdr.msf.frame;
1228 	} else {
1229 		lba2msf(ntohl(s.data->what.position.reladdr.lba),
1230 		    &mm, &ss, &ff);
1231 		*min = mm;
1232 		*sec = ss;
1233 		*frame = ff;
1234 	}
1235 
1236 	return s.data->header.audio_status;
1237 }
1238 
1239 char *
1240 input(int *cmd)
1241 {
1242 	char *buf;
1243 	int siz = 0;
1244 	char *p;
1245 
1246 	do {
1247 		if ((buf = (char *) el_gets(el, &siz)) == NULL || !siz) {
1248 			*cmd = CMD_QUIT;
1249 			fprintf(stderr, "\r\n");
1250 			return (0);
1251 		}
1252 		if (strlen(buf) > 1)
1253 			history(hist, H_ENTER, buf);
1254 		p = parse(buf, cmd);
1255 	} while (!p);
1256 	return (p);
1257 }
1258 
1259 char *
1260 parse(char *buf, int *cmd)
1261 {
1262 	struct cmdtab *c;
1263 	char *p;
1264 	int len;
1265 
1266 	for (p=buf; isspace(*p); p++)
1267 		continue;
1268 
1269 	if (isdigit(*p) || (p[0] == '#' && isdigit(p[1]))) {
1270 		*cmd = CMD_PLAY;
1271 		return (p);
1272 	}
1273 
1274 	for (buf = p; *p && ! isspace(*p); p++)
1275 		continue;
1276 
1277 	len = p - buf;
1278 	if (! len)
1279 		return (0);
1280 
1281 	if (*p) {		/* It must be a spacing character! */
1282 		char *q;
1283 
1284 		*p++ = 0;
1285 		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1286 			continue;
1287 		*q = 0;
1288 	}
1289 
1290 	*cmd = -1;
1291 	for (c=cmdtab; c->name; ++c) {
1292 		/* Is it an exact match? */
1293 		if (! strcasecmp(buf, c->name)) {
1294   			*cmd = c->command;
1295   			break;
1296   		}
1297 
1298 		/* Try short hand forms then... */
1299 		if (len >= c->min && ! strncasecmp(buf, c->name, len)) {
1300 			if (*cmd != -1 && *cmd != c->command) {
1301 				fprintf(stderr, "Ambiguous command\n");
1302 				return (0);
1303 			}
1304 			*cmd = c->command;
1305   		}
1306 	}
1307 
1308 	if (*cmd == -1) {
1309 		fprintf(stderr, "%s: Invalid command, enter ``help'' for commands.\n",
1310 		    __progname);
1311 		return (0);
1312 	}
1313 
1314 	while (isspace(*p))
1315 		p++;
1316 	return p;
1317 }
1318 
1319 int
1320 open_cd(char *dev)
1321 {
1322 	char *realdev;
1323 	int tries;
1324 
1325 	if (fd > -1)
1326 		return (1);
1327 
1328 	for (tries = 0; fd < 0 && tries < 10; tries++) {
1329 		fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev);
1330 		if (fd < 0) {
1331 			if (errno == ENXIO) {
1332 				/*  ENXIO has an overloaded meaning here.
1333 				 *  The original "Device not configured" should
1334 				 *  be interpreted as "No disc in drive %s". */
1335 				warnx("No disc in drive %s.", realdev);
1336 				return (0);
1337 			} else if (errno != EIO) {
1338 				/*  EIO may simply mean the device is not ready
1339 				 *  yet which is common with CD changers. */
1340 				warn("Can't open %s", realdev);
1341 				return (0);
1342 			}
1343 		}
1344 		sleep(1);
1345 	}
1346 	if (fd < 0) {
1347 		warn("Can't open %s", realdev);
1348 		return (0);
1349 	}
1350 	return (1);
1351 }
1352 
1353 char *
1354 prompt(void)
1355 {
1356 	return (verbose ? "cdio> " : "");
1357 }
1358 
1359 void
1360 switch_el(void)
1361 {
1362 	if (el == NULL && hist == NULL) {
1363 		el = el_init(__progname, stdin, stdout);
1364 		hist = history_init();
1365 		history(hist, H_EVENT, 100);
1366 		el_set(el, EL_HIST, history, hist);
1367 		el_set(el, EL_EDITOR, "emacs");
1368 		el_set(el, EL_PROMPT, prompt);
1369 		el_set(el, EL_SIGNAL, 1);
1370 		el_source(el, NULL);
1371 
1372 	} else {
1373 		if (hist != NULL) {
1374 			history_end(hist);
1375 			hist = NULL;
1376 		}
1377 		if (el != NULL) {
1378 			el_end(el);
1379 			el = NULL;
1380 		}
1381 	}
1382 }
1383