xref: /openbsd-src/usr.bin/cdio/cdio.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: cdio.c,v 1.80 2021/01/18 00:44:00 mortimer 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>	/* isset */
56 #include <sys/cdio.h>
57 #include <sys/ioctl.h>
58 #include <sys/queue.h>
59 #include <sys/scsiio.h>
60 #include <sys/stat.h>
61 
62 #include <ctype.h>
63 #include <err.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <limits.h>
71 #include <histedit.h>
72 #include <util.h>
73 #include <vis.h>
74 
75 #include "extern.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 #ifndef DEFAULT_CD_DRIVE
85 #  define DEFAULT_CD_DRIVE  "cd0"
86 #endif
87 
88 #define CMD_DEBUG       1
89 #define CMD_DEVICE      2
90 #define CMD_EJECT       3
91 #define CMD_HELP        4
92 #define CMD_INFO        5
93 #define CMD_PAUSE       6
94 #define CMD_PLAY        7
95 #define CMD_QUIT        8
96 #define CMD_RESUME      9
97 #define CMD_STOP        10
98 #define CMD_VOLUME      11
99 #define CMD_CLOSE       12
100 #define CMD_RESET       13
101 #define CMD_SET         14
102 #define CMD_STATUS      15
103 #define CMD_NEXT	16
104 #define CMD_PREV	17
105 #define CMD_REPLAY	18
106 #define CMD_CDDB	19
107 #define CMD_CDID	20
108 #define CMD_BLANK	21
109 #define CMD_CDRIP	22
110 #define CMD_CDPLAY	23
111 
112 struct cmdtab {
113 	int command;
114 	char *name;
115 	int min;
116 	char *args;
117 } cmdtab[] = {
118 { CMD_BLANK,	"blank",	1, "" },
119 { CMD_CDDB,	"cddbinfo",     2, "[n]" },
120 { CMD_CDID,	"cdid",		3, "" },
121 { CMD_CDPLAY,	"cdplay",	3, "[track1-trackN ...]" },
122 { CMD_CDRIP,	"cdrip",	3, "[track1-trackN ...]" },
123 { CMD_CLOSE,    "close",        1, "" },
124 { CMD_DEBUG,    "debug",        3, "on | off" },
125 { CMD_DEVICE,   "device",       1, "devname" },
126 { CMD_EJECT,    "eject",        1, "" },
127 { CMD_QUIT,	"exit",		2, "" },
128 { CMD_HELP,     "?",            1, 0 },
129 { CMD_HELP,     "help",         1, "" },
130 { CMD_INFO,     "info",         1, "" },
131 { CMD_NEXT,	"next",		1, "" },
132 { CMD_PAUSE,    "pause",        2, "" },
133 { CMD_PLAY,     "play",         1, "[track1[.index1] [track2[.index2]]]" },
134 { CMD_PLAY,     "play",         1, "[[tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]]]" },
135 { CMD_PLAY,     "play",         1, "[#block [len]]" },
136 { CMD_PREV,	"previous",	2, "" },
137 { CMD_QUIT,     "quit",         1, "" },
138 { CMD_REPLAY,	"replay",	3, "" },
139 { CMD_RESET,    "reset",        4, "" },
140 { CMD_RESUME,   "resume",       1, "" },
141 { CMD_SET,      "set",          2, "lba | msf" },
142 { CMD_STATUS,   "status",       1, "" },
143 { CMD_STOP,     "stop",         3, "" },
144 { CMD_VOLUME,   "volume",       1, "left_channel right_channel" },
145 { CMD_VOLUME,   "volume",       1, "left | right | mono | stereo | mute" },
146 { 0, 0, 0, 0}
147 };
148 
149 struct cd_toc_entry *toc_buffer;
150 
151 struct track_head tracks;
152 
153 char		*cdname;
154 int		fd = -1;
155 int		writeperm = 0;
156 u_int8_t	mediacap[MMC_FEATURE_MAX / NBBY];
157 int		verbose = 1;
158 int		msf = 1;
159 const char	*cddb_host;
160 char		**track_names;
161 
162 EditLine	*el = NULL;	/* line-editing structure */
163 History		*hist = NULL;	/* line-editing history */
164 void		switch_el(void);
165 
166 extern char	*__progname;
167 
168 int		setvol(int, int);
169 int		read_toc_entrys(int);
170 int		play_msf(int, int, int, int, int, int);
171 int		play_track(int, int, int, int);
172 int		status(int *, int *, int *, int *);
173 int		is_wave(int);
174 __dead void	tao(int argc, char **argv);
175 int		play(char *arg);
176 int		info(char *arg);
177 int		cddbinfo(char *arg);
178 int		pstatus(char *arg);
179 int		play_next(char *arg);
180 int		play_prev(char *arg);
181 int		play_same(char *arg);
182 char		*input(int *);
183 char		*prompt(void);
184 void		prtrack(struct cd_toc_entry *e, int lastflag, char *name);
185 void		lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
186 unsigned int	msf2lba(u_char m, u_char s, u_char f);
187 int		play_blocks(int blk, int len);
188 int		run(int cmd, char *arg);
189 char		*parse(char *buf, int *cmd);
190 void		help(void);
191 void		usage(void);
192 char		*strstatus(int);
193 int		cdid(void);
194 void		addmsf(u_int *, u_int *, u_int *, u_char, u_char, u_char);
195 int		cmpmsf(u_char, u_char, u_char, u_char, u_char, u_char);
196 void		toc2msf(u_int, u_char *, u_char *, u_char *);
197 
198 void
199 help(void)
200 {
201 	struct cmdtab *c;
202 	char *s, n;
203 	int i;
204 
205 	for (c = cmdtab; c->name; ++c) {
206 		if (!c->args)
207 			continue;
208 		printf("\t");
209 		for (i = c->min, s = c->name; *s; s++, i--) {
210 			if (i > 0)
211 				n = toupper((unsigned char)*s);
212 			else
213 				n = *s;
214 			putchar(n);
215 		}
216 		if (*c->args)
217 			printf(" %s", c->args);
218 		printf("\n");
219 	}
220 	printf("\n\tThe word \"play\" is not required for the play commands.\n");
221 	printf("\tThe plain target address is taken as a synonym for play.\n");
222 }
223 
224 void
225 usage(void)
226 {
227 	fprintf(stderr, "usage: %s [-sv] [-d host:port] [-f device] [command args ...]\n",
228 	    __progname);
229 	exit(1);
230 }
231 
232 int
233 main(int argc, char **argv)
234 {
235 	int ch, cmd;
236 	char *arg;
237 
238 	cdname = getenv("DISC");
239 	if (!cdname)
240 		cdname = getenv("CDROM");
241 
242 	cddb_host = getenv("CDDB");
243 	if (!cddb_host)
244 		cddb_host = "gnudb.gnudb.org";
245 
246 	while ((ch = getopt(argc, argv, "svd:f:")) != -1)
247 		switch (ch) {
248 		case 's':
249 			verbose = 0;
250 			break;
251 		case 'v':
252 			verbose++;
253 			break;
254 		case 'f':
255 			cdname = optarg;
256 			break;
257 		case 'd':
258 			cddb_host = optarg;
259 			break;
260 		default:
261 			usage();
262 		}
263 
264 	argc -= optind;
265 	argv += optind;
266 
267 	if (argc > 0 && ! strcasecmp(*argv, "help"))
268 		usage();
269 
270 	if (!cdname) {
271 		cdname = DEFAULT_CD_DRIVE;
272 		if (verbose > 1)
273 			fprintf(stderr,
274 			    "No CD device name specified. Defaulting to %s.\n",
275 			    cdname);
276 	}
277 
278 	if (argc > 0 && !strcasecmp(*argv, "tao")) {
279 		tao(argc, argv);
280 		/* NOTREACHED */
281 	}
282 	if (argc > 0) {
283 		char buf[80], *p;
284 		int len;
285 
286 		for (p=buf; argc-->0; ++argv) {
287 			len = snprintf(p, buf + sizeof buf - p,
288 			   "%s%s", (p > buf) ? " " : "", *argv);
289 
290 			if (len < 0 || len >= buf + sizeof buf - p)
291 				errx(1, "argument list too long.");
292 
293 			p += len;
294 		}
295 		arg = parse(buf, &cmd);
296 		return (run(cmd, arg));
297 	}
298 
299 	if (verbose == 1)
300 		verbose = isatty(0);
301 
302 	if (verbose) {
303 		printf("Compact Disc Control utility, version %s\n", VERSION);
304 		printf("Type `?' for command list\n\n");
305 	}
306 
307 	switch_el();
308 
309 	for (;;) {
310 		arg = input(&cmd);
311 		if (run(cmd, arg) < 0) {
312 			if (verbose)
313 				warn(NULL);
314 			close(fd);
315 			fd = -1;
316 		}
317 		fflush(stdout);
318 	}
319 }
320 
321 int
322 run(int cmd, char *arg)
323 {
324 	int l, r, rc;
325 	static char newcdname[PATH_MAX];
326 
327 	switch (cmd) {
328 
329 	case CMD_QUIT:
330 		switch_el();
331 		exit(0);
332 
333 	case CMD_INFO:
334 		if (!open_cd(cdname, 0))
335 			return (0);
336 
337 		return info(arg);
338 
339 	case CMD_CDDB:
340 		if (!open_cd(cdname, 0))
341 			return (0);
342 
343 		return cddbinfo(arg);
344 
345 	case CMD_CDID:
346 		if (!open_cd(cdname, 0))
347 			return (0);
348 		return cdid();
349 
350 	case CMD_STATUS:
351 		if (!open_cd(cdname, 0))
352 			return (0);
353 
354 		return pstatus(arg);
355 
356 	case CMD_PAUSE:
357 		if (!open_cd(cdname, 0))
358 			return (0);
359 
360 		return ioctl(fd, CDIOCPAUSE);
361 
362 	case CMD_RESUME:
363 		if (!open_cd(cdname, 0))
364 			return (0);
365 
366 		return ioctl(fd, CDIOCRESUME);
367 
368 	case CMD_STOP:
369 		if (!open_cd(cdname, 0))
370 			return (0);
371 
372 		rc = ioctl(fd, CDIOCSTOP);
373 
374 		(void) ioctl(fd, CDIOCALLOW);
375 
376 		return (rc);
377 
378 	case CMD_RESET:
379 		if (!open_cd(cdname, 0))
380 			return (0);
381 
382 		rc = ioctl(fd, CDIOCRESET);
383 		if (rc == -1)
384 			return rc;
385 		close(fd);
386 		fd = -1;
387 		return (0);
388 
389 	case CMD_DEBUG:
390 		if (!open_cd(cdname, 0))
391 			return (0);
392 
393 		if (!strcasecmp(arg, "on"))
394 			return ioctl(fd, CDIOCSETDEBUG);
395 
396 		if (!strcasecmp(arg, "off"))
397 			return ioctl(fd, CDIOCCLRDEBUG);
398 
399 		printf("%s: Invalid command arguments\n", __progname);
400 
401 		return (0);
402 
403 	case CMD_DEVICE:
404 		/* close old device */
405 		if (fd > -1) {
406 			(void) ioctl(fd, CDIOCALLOW);
407 			close(fd);
408 			fd = -1;
409 		}
410 
411 		if (strlen(arg) == 0) {
412 			printf("%s: Invalid parameter\n", __progname);
413 			return (0);
414 		}
415 
416 		/* open new device */
417 		if (!open_cd(arg, 0))
418 			return (0);
419 		(void) strlcpy(newcdname, arg, sizeof(newcdname));
420 		cdname = newcdname;
421 		return (1);
422 
423 	case CMD_EJECT:
424 		if (!open_cd(cdname, 0))
425 			return (0);
426 
427 		(void) ioctl(fd, CDIOCALLOW);
428 		rc = ioctl(fd, CDIOCEJECT);
429 		if (rc == -1)
430 			return (rc);
431 #if defined(__OpenBSD__)
432 		close(fd);
433 		fd = -1;
434 #endif
435 		if (track_names)
436 			free_names(track_names);
437 		track_names = NULL;
438 		return (0);
439 
440 	case CMD_CLOSE:
441 #if defined(CDIOCCLOSE)
442 		if (!open_cd(cdname, 0))
443 			return (0);
444 
445 		(void) ioctl(fd, CDIOCALLOW);
446 		rc = ioctl(fd, CDIOCCLOSE);
447 		if (rc == -1)
448 			return (rc);
449 		close(fd);
450 		fd = -1;
451 		return (0);
452 #else
453 		printf("%s: Command not yet supported\n", __progname);
454 		return (0);
455 #endif
456 
457 	case CMD_PLAY:
458 		if (!open_cd(cdname, 0))
459 			return (0);
460 
461 		while (isspace((unsigned char)*arg))
462 			arg++;
463 
464 		return play(arg);
465 
466 	case CMD_SET:
467 		if (!strcasecmp(arg, "msf"))
468 			msf = 1;
469 		else if (!strcasecmp(arg, "lba"))
470 			msf = 0;
471 		else
472 			printf("%s: Invalid command arguments\n", __progname);
473 		return (0);
474 
475 	case CMD_VOLUME:
476 		if (!open_cd(cdname, 0))
477 			return (0);
478 
479 		if (!strncasecmp(arg, "left", strlen(arg)))
480 			return ioctl(fd, CDIOCSETLEFT);
481 
482 		if (!strncasecmp(arg, "right", strlen(arg)))
483 			return ioctl(fd, CDIOCSETRIGHT);
484 
485 		if (!strncasecmp(arg, "mono", strlen(arg)))
486 			return ioctl(fd, CDIOCSETMONO);
487 
488 		if (!strncasecmp(arg, "stereo", strlen(arg)))
489 			return ioctl(fd, CDIOCSETSTEREO);
490 
491 		if (!strncasecmp(arg, "mute", strlen(arg)))
492 			return ioctl(fd, CDIOCSETMUTE);
493 
494 		if (2 != sscanf(arg, "%d%d", &l, &r)) {
495 			printf("%s: Invalid command arguments\n", __progname);
496 			return (0);
497 		}
498 
499 		return setvol(l, r);
500 
501 	case CMD_NEXT:
502 		if (!open_cd(cdname, 0))
503 			return (0);
504 
505 		return play_next(arg);
506 
507 	case CMD_PREV:
508 		if (!open_cd(cdname, 0))
509 			return (0);
510 
511 		return play_prev(arg);
512 
513 	case CMD_REPLAY:
514 		if (!open_cd(cdname, 0))
515 			return 0;
516 
517 		return play_same(arg);
518 	case CMD_BLANK:
519 		if (!open_cd(cdname, 1))
520 			return 0;
521 
522 		if (get_media_capabilities(mediacap, 1) == -1) {
523 			warnx("Can't determine media type");
524 			return (0);
525 		}
526 		if (isset(mediacap, MMC_FEATURE_CDRW_WRITE) == 0 &&
527 		    get_media_type() != MEDIATYPE_CDRW) {
528 			warnx("The media doesn't support blanking");
529 			return (0);
530 		}
531 
532 		return blank();
533 	case CMD_CDRIP:
534 		if (!open_cd(cdname, 0))
535 			return (0);
536 
537 		while (isspace((unsigned char)*arg))
538 			arg++;
539 
540 		return cdrip(arg);
541 	case CMD_CDPLAY:
542 		if (!open_cd(cdname, 0))
543 			return (0);
544 
545 		while (isspace((unsigned char)*arg))
546 			arg++;
547 
548 		return cdplay(arg);
549 	default:
550 	case CMD_HELP:
551 		help();
552 		return (0);
553 
554 	}
555 }
556 
557 /*
558  * Check if audio file has RIFF WAVE format. If not, we assume it's just PCM.
559  */
560 int
561 is_wave(int fd)
562 {
563 	char buf[WAVHDRLEN];
564 	int rv;
565 
566 	rv = 0;
567 	if (read(fd, buf, sizeof(buf)) == sizeof(buf)) {
568 		if (memcmp(buf, "RIFF", 4) == 0 &&
569 		    memcmp(buf + 8, "WAVE", 4) == 0)
570 			rv = 1;
571 	}
572 
573 	return (rv);
574 }
575 
576 __dead void
577 tao(int argc, char **argv)
578 {
579 	struct stat sb;
580 	struct track_info *cur_track;
581 	struct track_info *tr;
582 	off_t availblk, needblk = 0;
583 	u_int blklen;
584 	u_int ntracks = 0;
585 	char type;
586 	int ch, speed;
587 	const char *errstr;
588 
589 	if (argc == 1)
590 		usage();
591 
592 	SLIST_INIT(&tracks);
593 	type = 'd';
594 	speed = DRIVE_SPEED_OPTIMAL;
595 	blklen = 2048;
596 	while (argc > 1) {
597 		tr = malloc(sizeof(struct track_info));
598 		if (tr == NULL)
599 			err(1, "tao");
600 
601 		optreset = 1;
602 		optind = 1;
603 		while ((ch = getopt(argc, argv, "ads:")) != -1) {
604 			switch (ch) {
605 			case 'a':
606 				type = 'a';
607 				blklen = 2352;
608 				break;
609 			case 'd':
610 				type = 'd';
611 				blklen = 2048;
612 				break;
613 			case 's':
614 				if (strcmp(optarg, "auto") == 0) {
615 					speed = DRIVE_SPEED_OPTIMAL;
616 				} else if (strcmp(optarg, "max") == 0) {
617 					speed = DRIVE_SPEED_MAX;
618 				} else {
619 					speed = (int)strtonum(optarg, 1,
620 					    CD_MAX_SPEED, &errstr);
621 					if (errstr != NULL) {
622 						errx(1,
623 						    "incorrect speed value");
624 					}
625 				}
626 				break;
627 			default:
628 				usage();
629 				/* NOTREACHED */
630 			}
631 		}
632 
633 		if (speed != DRIVE_SPEED_OPTIMAL && speed != DRIVE_SPEED_MAX)
634 			tr->speed = CD_SPEED_TO_KBPS(speed, blklen);
635 		else
636 			tr->speed = speed;
637 
638 		tr->type = type;
639 		tr->blklen = blklen;
640 		argc -= optind;
641 		argv += optind;
642 		if (argv[0] == NULL)
643 			usage();
644 		tr->file = argv[0];
645 		tr->fd = open(tr->file, O_RDONLY, 0640);
646 		if (tr->fd == -1)
647 			err(1, "cannot open file %s", tr->file);
648 		if (fstat(tr->fd, &sb) == -1)
649 			err(1, "cannot stat file %s", tr->file);
650 		tr->sz = sb.st_size;
651 		tr->off = 0;
652 		if (tr->type == 'a') {
653 			if (is_wave(tr->fd)) {
654 				tr->sz -= WAVHDRLEN;
655 				tr->off = WAVHDRLEN;
656 			}
657 		}
658 		if (SLIST_EMPTY(&tracks))
659 			SLIST_INSERT_HEAD(&tracks, tr, track_list);
660 		else
661 			SLIST_INSERT_AFTER(cur_track, tr, track_list);
662 		cur_track = tr;
663 	}
664 
665 	if (!open_cd(cdname, 1))
666 		exit(1);
667 	if (get_media_capabilities(mediacap, 1) == -1)
668 		errx(1, "Can't determine media type");
669 	if (isset(mediacap, MMC_FEATURE_CD_TAO) == 0)
670 		errx(1, "The media can't be written in TAO mode");
671 
672 	get_disc_size(&availblk);
673 	SLIST_FOREACH(tr, &tracks, track_list) {
674 		needblk += tr->sz/tr->blklen;
675 		ntracks++;
676 	}
677 	needblk += (ntracks - 1) * 150; /* transition area between tracks */
678 	if (needblk > availblk)
679 		errx(1, "Only %llu of the required %llu blocks available",
680 		    availblk, needblk);
681 	if (writetao(&tracks) != 0)
682 		exit(1);
683 	else
684 		exit(0);
685 }
686 
687 int
688 play(char *arg)
689 {
690 	struct ioc_toc_header h;
691 	unsigned char tm, ts, tf;
692 	unsigned int tr1, tr2, m1, m2, s1, s2, f1, f2, i1, i2;
693 	unsigned int blk, len, n;
694 	char c;
695 	int rc;
696 
697 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
698 
699 	if (rc == -1)
700 		return (rc);
701 
702 	if (h.starting_track > h.ending_track) {
703 		printf("TOC starting_track > TOC ending_track\n");
704 		return (0);
705 	}
706 
707 	n = h.ending_track - h.starting_track + 1;
708 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
709 
710 	if (rc < 0)
711 		return (rc);
712 
713 	/*
714 	 * Truncate trailing white space. Then by adding %c to the end of the
715 	 * sscanf() formats we catch any errant trailing characters.
716 	 */
717 	rc = strlen(arg) - 1;
718 	while (rc >= 0 && isspace((unsigned char)arg[rc])) {
719 		arg[rc] = '\0';
720 		rc--;
721 	}
722 
723 	if (!arg || ! *arg) {
724 		/* Play the whole disc */
725 		return (play_track(h.starting_track, 1, h.ending_track, 1));
726 	}
727 
728 	if (strchr(arg, '#')) {
729 		/* Play block #blk [ len ] */
730 		if (2 != sscanf(arg, "#%u%u%c", &blk, &len, &c) &&
731 		    1 != sscanf(arg, "#%u%c", &blk, &c)) {
732 			printf("%s: Invalid command arguments\n", __progname);
733 			return (0);
734 		}
735 
736 		if (len == 0) {
737 			if (msf)
738 				len = msf2lba(toc_buffer[n].addr.msf.minute,
739 				    toc_buffer[n].addr.msf.second,
740 				    toc_buffer[n].addr.msf.frame) - blk;
741 			else
742 				len = toc_buffer[n].addr.lba - blk;
743 		}
744 		return play_blocks(blk, len);
745 	}
746 
747 	if (strchr(arg, ':') == NULL) {
748 		/*
749 		 * Play track tr1[.i1] [tr2[.i2]]
750 		 */
751 		if (4 == sscanf(arg, "%u.%u%u.%u%c", &tr1, &i1, &tr2, &i2, &c))
752 			goto play_track;
753 
754 		i2 = 1;
755 		if (3 == sscanf(arg, "%u.%u%u%c", &tr1, &i1, &tr2, &c))
756 			goto play_track;
757 
758 		i1 = 1;
759 		if (3 == sscanf(arg, "%u%u.%u%c", &tr1, &tr2, &i2, &c))
760 			goto play_track;
761 
762 		tr2 = 0;
763 		i2 = 1;
764 		if (2 == sscanf(arg, "%u.%u%c", &tr1, &i1, &c))
765 			goto play_track;
766 
767 		i1 = i2 = 1;
768 		if (2 == sscanf(arg, "%u%u%c", &tr1, &tr2, &c))
769 			goto play_track;
770 
771 		i1 = i2 = 1;
772 		tr2 = 0;
773 		if (1 == sscanf(arg, "%u%c", &tr1, &c))
774 			goto play_track;
775 
776 		printf("%s: Invalid command arguments\n", __progname);
777 		return (0);
778 
779 play_track:
780 		if (tr1 > n || tr2 > n) {
781 			printf("Track number must be between 0 and %u\n", n);
782 			return (0);
783 		} else if (tr2 == 0)
784 			tr2 = h.ending_track;
785 
786 		if (tr1 > tr2) {
787 			printf("starting_track > ending_track\n");
788 			return (0);
789 		}
790 
791 		return (play_track(tr1, i1, tr2, i2));
792 	}
793 
794 	/*
795 	 * Play MSF [tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]]
796 	 *
797 	 * Start Time		End Time
798 	 * ----------		--------
799 	 * tr1 m1:s1.f1		tr2 m2:s2.f2
800 	 * tr1 m1:s1   		tr2 m2:s2.f2
801 	 * tr1 m1:s1.f1		tr2 m2:s2
802 	 * tr1 m1:s1   		tr2 m2:s2
803 	 *     m1:s1.f1		tr2 m2:s2.f2
804 	 *     m1:s1   		tr2 m2:s2.f2
805 	 *     m1:s1.f1		tr2 m2:s2
806 	 *     m1:s1   		tr2 m2:s2
807 	 * tr1 m1:s1.f1		    m2:s2.f2
808 	 * tr1 m1:s1   		    m2:s2.f2
809 	 * tr1 m1:s1.f1		    m2:s2
810 	 * tr1 m1:s1  		    m2:s2
811 	 *     m1:s1.f1		    m2:s2.f2
812 	 *     m1:s1       	    m2:s2.f2
813 	 *     m1:s1.f1  	    m2:s2
814 	 *     m1:s1     	    m2:s2
815 	 * tr1 m1:s1.f1		tr2
816 	 * tr1 m1:s1    	tr2
817 	 *     m1:s1.f1  	tr2
818 	 *     m1:s1      	tr2
819 	 * tr1 m1:s1.f1		<end of disc>
820 	 * tr1 m1:s1    	<end of disc>
821 	 *     m1:s1.f1  	<end of disc>
822 	 *     m1:s1      	<end of disc>
823 	 */
824 
825 	/* tr1 m1:s1.f1		tr2 m2:s2.f2 */
826 	if (8 == sscanf(arg, "%u%u:%u.%u%u%u:%u.%u%c",
827 	    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c))
828 		goto play_msf;
829 
830 	/* tr1 m1:s1   		tr2 m2:s2.f2 */
831 	f1 = 0;
832 	if (7 == sscanf(arg, "%u%u:%u%u%u:%u.%u%c",
833 	    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2, &c))
834 		goto play_msf;
835 
836 	/* tr1 m1:s1.f1		tr2 m2:s2 */
837 	f2 =0;
838 	if (7 == sscanf(arg, "%u%u:%u.%u%u%u:%u%c",
839 	    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &c))
840 		goto play_msf;
841 
842 	/*     m1:s1.f1		tr2 m2:s2.f2 */
843 	tr1 = 0;
844 	if (7 == sscanf(arg, "%u:%u.%u%u%u:%u.%u%c",
845 	    &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c))
846 		goto play_msf;
847 
848 	/* tr1 m1:s1.f1		    m2:s2.f2 */
849 	tr2 = 0;
850 	if (7 == sscanf(arg, "%u%u:%u.%u%u:%u.%u%c",
851 	    &tr1, &m1, &s1, &f1, &m2, &s2, &f2, &c))
852 		goto play_msf;
853 
854 	/*     m1:s1   		tr2 m2:s2.f2 */
855 	tr1 = f1 = 0;
856 	if (6 == sscanf(arg, "%u:%u%u%u:%u.%u%c",
857 	    &m1, &s1, &tr2, &m2, &s2, &f2, &c))
858 		goto play_msf;
859 
860 	/*     m1:s1.f1		tr2 m2:s2 */
861 	tr1 = f2 = 0;
862 	if (6 == sscanf(arg, "%u:%u.%u%u%u:%u%c",
863 	    &m1, &s1, &f1, &tr2, &m2, &s2, &c))
864 		goto play_msf;
865 
866 	/*     m1:s1.f1		    m2:s2.f2 */
867 	tr1 = tr2 = 0;
868 	if (6 == sscanf(arg, "%u:%u.%u%u:%u.%u%c",
869 	    &m1, &s1, &f1, &m2, &s2, &f2, &c))
870 		goto play_msf;
871 
872 	/* tr1 m1:s1.f1		    m2:s2 */
873 	tr2 = f2 = 0;
874 	if (6 == sscanf(arg, "%u%u:%u.%u%u:%u%c",
875 	    &tr1, &m1, &s1, &f1, &m2, &s2, &c))
876 		goto play_msf;
877 
878 	/* tr1 m1:s1   		    m2:s2.f2 */
879 	tr2 = f1 = 0;
880 	if (6 == sscanf(arg, "%u%u:%u%u:%u.%u%c",
881 	    &tr1, &m1, &s1, &m2, &s2, &f2, &c))
882 		goto play_msf;
883 
884 	/* tr1 m1:s1   		tr2 m2:s2 */
885 	f1 = f2 = 0;
886 	if (6 == sscanf(arg, "%u%u:%u%u%u:%u%c",
887 	    &tr1, &m1, &s1, &tr2, &m2, &s2, &c))
888 		goto play_msf;
889 
890 	/*     m1:s1   		tr2 m2:s2 */
891 	tr1 = f1 = f2 = 0;
892 	if (5 == sscanf(arg, "%u:%u%u%u:%u%c", &m1, &s1, &tr2, &m2, &s2, &c))
893 		goto play_msf;
894 
895 	/* tr1 m1:s1  		    m2:s2 */
896 	f1 = tr2 = f2 = 0;
897 	if (5 == sscanf(arg, "%u%u:%u%u:%u%c", &tr1, &m1, &s1, &m2, &s2, &c))
898 		goto play_msf;
899 
900 	/*     m1:s1       	    m2:s2.f2 */
901 	tr1 = f1 = tr2 = 0;
902 	if (5 == sscanf(arg, "%u:%u%u:%u.%u%c", &m1, &s1, &m2, &s2, &f2, &c))
903 		goto play_msf;
904 
905 	/*     m1:s1.f1  	    m2:s2 */
906 	tr1 = tr2 = f2 = 0;
907 	if (5 == sscanf(arg, "%u:%u.%u%u:%u%c", &m1, &s1, &f1, &m2, &s2, &c))
908 		goto play_msf;
909 
910 	/* tr1 m1:s1.f1		tr2 */
911 	m2 = s2 = f2 = 0;
912 	if (5 == sscanf(arg, "%u%u:%u.%u%u%c", &tr1, &m1, &s1, &f1, &tr2, &c))
913 		goto play_msf;
914 
915 	/*     m1:s1     	    m2:s2 */
916 	tr1 = f1 = tr2 = f2 = 0;
917 	if (4 == sscanf(arg, "%u:%u%u:%u%c", &m1, &s1, &m2, &s2, &c))
918 		goto play_msf;
919 
920 	/* tr1 m1:s1.f1		<end of disc> */
921 	tr2 = m2 = s2 = f2 = 0;
922 	if (4 == sscanf(arg, "%u%u:%u.%u%c", &tr1, &m1, &s1, &f1, &c))
923 		goto play_msf;
924 
925 	/* tr1 m1:s1    	tr2 */
926 	f1 = m2 = s2 = f2 = 0;
927 	if (4 == sscanf(arg, "%u%u:%u%u%c", &tr1, &m1, &s1, &tr2, &c))
928 		goto play_msf;
929 
930 	/*     m1:s1.f1  	tr2 */
931 	tr1 = m2 = s2 = f2 = 0;
932 	if (4 == sscanf(arg, "%u%u:%u%u%c", &m1, &s1, &f1, &tr2, &c))
933 		goto play_msf;
934 
935 	/*     m1:s1.f1  	<end of disc> */
936 	tr1 = tr2 = m2 = s2 = f2 = 0;
937 	if (3 == sscanf(arg, "%u:%u.%u%c", &m1, &s1, &f1, &c))
938 		goto play_msf;
939 
940 	/* tr1 m1:s1    	<end of disc> */
941 	f1 = tr2 = m2 = s2 = f2 = 0;
942 	if (3 == sscanf(arg, "%u%u:%u%c", &tr1, &m1, &s1, &c))
943 		goto play_msf;
944 
945 	/*     m1:s1      	tr2 */
946 	tr1 = f1 = m2 = s2 = f2 = 0;
947 	if (3 == sscanf(arg, "%u:%u%u%c", &m1, &s1, &tr2, &c))
948 		goto play_msf;
949 
950 	/*     m1:s1      	<end of disc> */
951 	tr1 = f1 = tr2 = m2 = s2 = f2 = 0;
952 	if (2 == sscanf(arg, "%u:%u%c", &m1, &s1, &c))
953 		goto play_msf;
954 
955 	printf("%s: Invalid command arguments\n", __progname);
956 	return (0);
957 
958 play_msf:
959 	if (tr1 > n || tr2 > n) {
960 		printf("Track number must be between 0 and %u\n", n);
961 		return (0);
962 	} else if (m1 > 99 || m2 > 99) {
963 		printf("Minutes must be between 0 and 99\n");
964 		return (0);
965 	} else if (s1 > 59 || s2 > 59) {
966 		printf("Seconds must be between 0 and 59\n");
967 		return (0);
968 	} else if (f1 > 74 || f2 > 74) {
969 		printf("Frames number must be between 0 and 74\n");
970 		return (0);
971 	}
972 
973 	if (tr1 > 0) {
974 		/*
975 		 * Start time is relative to tr1, Add start time of tr1
976 		 * to (m1,s1,f1) to yield absolute start time.
977 		 */
978 		toc2msf(tr1, &tm, &ts, &tf);
979 		addmsf(&m1, &s1, &f1, tm, ts, tf);
980 
981 		/* Compare (m1,s1,f1) to start time of next track. */
982 		toc2msf(tr1+1, &tm, &ts, &tf);
983 		if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) {
984 			printf("Track %u is not that long.\n", tr1);
985 			return (0);
986 		}
987 	}
988 
989 	toc2msf(n+1, &tm, &ts, &tf);
990 	if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) {
991 		printf("Start time is after end of disc.\n");
992 		return (0);
993 	}
994 
995 	if (tr2 > 0) {
996 		/*
997 		 * End time is relative to tr2, Add start time of tr2
998 		 * to (m2,s2,f2) to yield absolute end time.
999 		 */
1000 		toc2msf(tr2, &tm, &ts, &tf);
1001 		addmsf(&m2, &s2, &f2, tm, ts, tf);
1002 
1003 		/* Compare (m2,s2,f2) to start time of next track. */
1004 		toc2msf(tr2+1, &tm, &ts, &tf);
1005 		if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) {
1006 			printf("Track %u is not that long.\n", tr2);
1007 			return (0);
1008 		}
1009 	}
1010 
1011 	toc2msf(n+1, &tm, &ts, &tf);
1012 
1013 	if (!(tr2 || m2 || s2 || f2)) {
1014 		/* Play to end of disc. */
1015 		m2 = tm;
1016 		s2 = ts;
1017 		f2 = tf;
1018 	} else if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) {
1019 		printf("End time is after end of disc.\n");
1020 		return (0);
1021 	}
1022 
1023 	if (cmpmsf(m1, s1, f1, m2, s2, f2) == 1) {
1024 		printf("Start time is after end time.\n");
1025 		return (0);
1026 	}
1027 
1028 	return play_msf(m1, s1, f1, m2, s2, f2);
1029 }
1030 
1031 /* ARGSUSED */
1032 int
1033 play_prev(char *arg)
1034 {
1035 	int trk, min, sec, frm, rc;
1036 	struct ioc_toc_header h;
1037 
1038 	if (status(&trk, &min, &sec, &frm) >= 0) {
1039 		trk--;
1040 
1041 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1042 		if (rc == -1) {
1043 			warn("getting toc header");
1044 			return (rc);
1045 		}
1046 
1047 		if (trk < h.starting_track)
1048 			return play_track(h.starting_track, 1,
1049 			    h.ending_track + 1, 1);
1050 		return play_track(trk, 1, h.ending_track, 1);
1051 	}
1052 
1053 	return (0);
1054 }
1055 
1056 /* ARGSUSED */
1057 int
1058 play_same(char *arg)
1059 {
1060 	int trk, min, sec, frm, rc;
1061 	struct ioc_toc_header h;
1062 
1063 	if (status (&trk, &min, &sec, &frm) >= 0) {
1064 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1065 		if (rc == -1) {
1066 			warn("getting toc header");
1067 			return (rc);
1068 		}
1069 
1070 		return play_track(trk, 1, h.ending_track, 1);
1071 	}
1072 
1073 	return (0);
1074 }
1075 
1076 /* ARGSUSED */
1077 int
1078 play_next(char *arg)
1079 {
1080 	int trk, min, sec, frm, rc;
1081 	struct ioc_toc_header h;
1082 
1083 	if (status(&trk, &min, &sec, &frm) >= 0) {
1084 		trk++;
1085 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1086 		if (rc == -1) {
1087 			warn("getting toc header");
1088 			return (rc);
1089 		}
1090 
1091 		if (trk > h.ending_track) {
1092 			printf("%s: end of CD\n", __progname);
1093 
1094 			rc = ioctl(fd, CDIOCSTOP);
1095 
1096 			(void) ioctl(fd, CDIOCALLOW);
1097 
1098 			return (rc);
1099 		}
1100 
1101 		return play_track(trk, 1, h.ending_track, 1);
1102 	}
1103 
1104 	return (0);
1105 }
1106 
1107 char *
1108 strstatus(int sts)
1109 {
1110 	switch (sts) {
1111 	case ASTS_INVALID:
1112 		return ("invalid");
1113 	case ASTS_PLAYING:
1114 		return ("playing");
1115 	case ASTS_PAUSED:
1116 		return ("paused");
1117 	case ASTS_COMPLETED:
1118 		return ("completed");
1119 	case ASTS_ERROR:
1120 		return ("error");
1121 	case ASTS_VOID:
1122 		return ("void");
1123 	default:
1124 		return ("??");
1125 	}
1126 }
1127 
1128 /* ARGSUSED */
1129 int
1130 pstatus(char *arg)
1131 {
1132 	struct ioc_vol v;
1133 	struct ioc_read_subchannel ss;
1134 	struct cd_sub_channel_info data;
1135 	int rc, trk, m, s, f;
1136 	char vis_catalog[1 + 4 * 15];
1137 
1138 	rc = status(&trk, &m, &s, &f);
1139 	if (rc >= 0) {
1140 		if (verbose) {
1141 			if (track_names)
1142 				printf("Audio status = %d<%s>, "
1143 				    "current track = %d (%s)\n"
1144 				    "\tcurrent position = %d:%02d.%02d\n",
1145 				    rc, strstatus(rc), trk,
1146 				    trk ? track_names[trk-1] : "", m, s, f);
1147 			else
1148 				printf("Audio status = %d<%s>, "
1149 				    "current track = %d, "
1150 				    "current position = %d:%02d.%02d\n",
1151 				    rc, strstatus(rc), trk, m, s, f);
1152 		} else
1153 			printf("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
1154 	} else
1155 		printf("No current status info available\n");
1156 
1157 	bzero(&ss, sizeof (ss));
1158 	ss.data = &data;
1159 	ss.data_len = sizeof (data);
1160 	ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1161 	ss.data_format = CD_MEDIA_CATALOG;
1162 	rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss);
1163 	if (rc >= 0) {
1164 		printf("Media catalog is %sactive",
1165 		ss.data->what.media_catalog.mc_valid ? "": "in");
1166 		if (ss.data->what.media_catalog.mc_valid &&
1167 		    ss.data->what.media_catalog.mc_number[0]) {
1168 			strvisx(vis_catalog,
1169 			    (char *)ss.data->what.media_catalog.mc_number,
1170 			    15, VIS_SAFE);
1171 			printf(", number \"%.15s\"", vis_catalog);
1172 		}
1173 		putchar('\n');
1174 	} else
1175 		printf("No media catalog info available\n");
1176 
1177 	rc = ioctl(fd, CDIOCGETVOL, &v);
1178 	if (rc >= 0) {
1179 		if (verbose)
1180 			printf("Left volume = %d, right volume = %d\n",
1181 			    v.vol[0], v.vol[1]);
1182 		else
1183 			printf("%d %d\n", v.vol[0], v.vol[1]);
1184 	} else
1185 		printf("No volume level info available\n");
1186 	return(0);
1187 }
1188 
1189 int
1190 cdid(void)
1191 {
1192 	unsigned long id;
1193 	struct ioc_toc_header h;
1194 	int rc, n;
1195 
1196 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1197 	if (rc == -1) {
1198 		warn("getting toc header");
1199 		return (rc);
1200 	}
1201 
1202 	n = h.ending_track - h.starting_track + 1;
1203 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1204 	if (rc < 0)
1205 		return (rc);
1206 
1207 	id = cddb_discid(n, toc_buffer);
1208 	if (id) {
1209 		if (verbose)
1210 			printf("CDID=");
1211 		printf("%08lx\n", id);
1212 	}
1213 	return id ? 0 : 1;
1214 }
1215 
1216 /* ARGSUSED */
1217 int
1218 info(char *arg)
1219 {
1220 	struct ioc_toc_header h;
1221 	int rc, i, n;
1222 
1223 	if (get_media_capabilities(mediacap, 1) == -1)
1224 		errx(1, "Can't determine media type");
1225 
1226 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1227 	if (rc >= 0) {
1228 		if (verbose)
1229 			printf("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
1230 			    h.starting_track, h.ending_track, h.len);
1231 		else
1232 			printf("%d %d %d\n", h.starting_track,
1233 			    h.ending_track, h.len);
1234 	} else {
1235 		warn("getting toc header");
1236 		return (rc);
1237 	}
1238 
1239 	n = h.ending_track - h.starting_track + 1;
1240 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1241 	if (rc < 0)
1242 		return (rc);
1243 
1244 	if (verbose) {
1245 		printf("track     start  duration   block  length   type\n");
1246 		printf("-------------------------------------------------\n");
1247 	}
1248 
1249 	for (i = 0; i < n; i++) {
1250 		printf("%5d  ", toc_buffer[i].track);
1251 		prtrack(toc_buffer + i, 0, NULL);
1252 	}
1253 	printf("%5d  ", toc_buffer[n].track);
1254 	prtrack(toc_buffer + n, 1, NULL);
1255 	return (0);
1256 }
1257 
1258 int
1259 cddbinfo(char *arg)
1260 {
1261 	struct ioc_toc_header h;
1262 	int rc, i, n;
1263 
1264 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1265 	if (rc == -1) {
1266 		warn("getting toc header");
1267 		return (rc);
1268 	}
1269 
1270 	n = h.ending_track - h.starting_track + 1;
1271 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1272 	if (rc < 0)
1273 		return (rc);
1274 
1275 	if (track_names)
1276 		free_names(track_names);
1277 	track_names = NULL;
1278 
1279 	track_names = cddb(cddb_host, n, toc_buffer, arg);
1280 	if (!track_names)
1281 		return(0);
1282 
1283 	printf("-------------------------------------------------\n");
1284 
1285 	for (i = 0; i < n; i++) {
1286 		printf("%5d  ", toc_buffer[i].track);
1287 		prtrack(toc_buffer + i, 0, track_names[i]);
1288 	}
1289 	printf("%5d  ", toc_buffer[n].track);
1290 	prtrack(toc_buffer + n, 1, "");
1291 	return (0);
1292 }
1293 
1294 void
1295 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
1296 {
1297 	lba += 150;		/* block start offset */
1298 	lba &= 0xffffff;	/* negative lbas use only 24 bits */
1299 	*m = lba / (60 * 75);
1300 	lba %= (60 * 75);
1301 	*s = lba / 75;
1302 	*f = lba % 75;
1303 }
1304 
1305 unsigned int
1306 msf2lba(u_char m, u_char s, u_char f)
1307 {
1308 	return (((m * 60) + s) * 75 + f) - 150;
1309 }
1310 
1311 unsigned long
1312 entry2time(struct cd_toc_entry *e)
1313 {
1314 	int block;
1315 	u_char m, s, f;
1316 
1317 	if (msf) {
1318 		return (e->addr.msf.minute * 60 + e->addr.msf.second);
1319 	} else {
1320 		block = e->addr.lba;
1321 		lba2msf(block, &m, &s, &f);
1322 		return (m*60+s);
1323 	}
1324 }
1325 
1326 unsigned long
1327 entry2frames(struct cd_toc_entry *e)
1328 {
1329 	int block;
1330 	unsigned char m, s, f;
1331 
1332 	if (msf) {
1333 		return e->addr.msf.frame + e->addr.msf.second * 75 +
1334 		    e->addr.msf.minute * 60 * 75;
1335 	} else {
1336 		block = e->addr.lba;
1337 		lba2msf(block, &m, &s, &f);
1338 		return f + s * 75 + m * 60 * 75;
1339 	}
1340 }
1341 
1342 void
1343 prtrack(struct cd_toc_entry *e, int lastflag, char *name)
1344 {
1345 	int block, next, len;
1346 	u_char m, s, f;
1347 
1348 	if (msf) {
1349 		if (!name || lastflag)
1350 			/* Print track start */
1351 			printf("%2d:%02d.%02d  ", e->addr.msf.minute,
1352 			    e->addr.msf.second, e->addr.msf.frame);
1353 
1354 		block = msf2lba(e->addr.msf.minute, e->addr.msf.second,
1355 			e->addr.msf.frame);
1356 	} else {
1357 		block = e->addr.lba;
1358 		if (!name || lastflag) {
1359 			lba2msf(block, &m, &s, &f);
1360 			/* Print track start */
1361 			printf("%2d:%02d.%02d  ", m, s, f);
1362 		}
1363 	}
1364 	if (lastflag) {
1365 		if (!name)
1366 			/* Last track -- print block */
1367 			printf("       -  %6d       -      -\n", block);
1368 		else
1369 			printf("\n");
1370 		return;
1371 	}
1372 
1373 	if (msf)
1374 		next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second,
1375 			e[1].addr.msf.frame);
1376 	else
1377 		next = e[1].addr.lba;
1378 	len = next - block;
1379 	lba2msf(len - 150, &m, &s, &f);
1380 
1381 	if (name)
1382 		printf("%2d:%02d.%02d  %s\n", m, s, f, name);
1383 	/* Print duration, block, length, type */
1384 	else
1385 		printf("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
1386 		    (e->control & 4) ? "data" : "audio");
1387 }
1388 
1389 int
1390 play_track(int tstart, int istart, int tend, int iend)
1391 {
1392 	struct ioc_play_track t;
1393 
1394 	t.start_track = tstart;
1395 	t.start_index = istart;
1396 	t.end_track = tend;
1397 	t.end_index = iend;
1398 
1399 	return ioctl(fd, CDIOCPLAYTRACKS, &t);
1400 }
1401 
1402 int
1403 play_blocks(int blk, int len)
1404 {
1405 	struct ioc_play_blocks  t;
1406 
1407 	t.blk = blk;
1408 	t.len = len;
1409 
1410 	return ioctl(fd, CDIOCPLAYBLOCKS, &t);
1411 }
1412 
1413 int
1414 setvol(int left, int right)
1415 {
1416 	struct ioc_vol  v;
1417 
1418 	v.vol[0] = left;
1419 	v.vol[1] = right;
1420 	v.vol[2] = 0;
1421 	v.vol[3] = 0;
1422 
1423 	return ioctl(fd, CDIOCSETVOL, &v);
1424 }
1425 
1426 int
1427 read_toc_entrys(int len)
1428 {
1429 	struct ioc_read_toc_entry t;
1430 
1431 	if (toc_buffer) {
1432 		free(toc_buffer);
1433 		toc_buffer = 0;
1434 	}
1435 
1436 	toc_buffer = malloc(len);
1437 
1438 	if (!toc_buffer) {
1439 		errno = ENOMEM;
1440 		return (-1);
1441 	}
1442 
1443 	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1444 	t.starting_track = 0;
1445 	t.data_len = len;
1446 	t.data = toc_buffer;
1447 
1448 	return (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t));
1449 }
1450 
1451 int
1452 play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, int end_f)
1453 {
1454 	struct ioc_play_msf a;
1455 
1456 	a.start_m = start_m;
1457 	a.start_s = start_s;
1458 	a.start_f = start_f;
1459 	a.end_m = end_m;
1460 	a.end_s = end_s;
1461 	a.end_f = end_f;
1462 
1463 	return ioctl(fd, CDIOCPLAYMSF, (char *) &a);
1464 }
1465 
1466 int
1467 status(int *trk, int *min, int *sec, int *frame)
1468 {
1469 	struct ioc_read_subchannel s;
1470 	struct cd_sub_channel_info data;
1471 	u_char mm, ss, ff;
1472 
1473 	bzero(&s, sizeof (s));
1474 	s.data = &data;
1475 	s.data_len = sizeof (data);
1476 	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1477 	s.data_format = CD_CURRENT_POSITION;
1478 
1479 	if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) == -1)
1480 		return -1;
1481 
1482 	*trk = s.data->what.position.track_number;
1483 	if (msf) {
1484 		*min = s.data->what.position.reladdr.msf.minute;
1485 		*sec = s.data->what.position.reladdr.msf.second;
1486 		*frame = s.data->what.position.reladdr.msf.frame;
1487 	} else {
1488 		/*
1489 		 * NOTE: CDIOCREADSUBCHANNEL does not put the lba info into
1490 		 * host order like CDIOREADTOCENTRYS does.
1491 		 */
1492 		lba2msf(betoh32(s.data->what.position.reladdr.lba), &mm, &ss,
1493 		    &ff);
1494 		*min = mm;
1495 		*sec = ss;
1496 		*frame = ff;
1497 	}
1498 
1499 	return s.data->header.audio_status;
1500 }
1501 
1502 char *
1503 input(int *cmd)
1504 {
1505 	char *buf;
1506 	int siz = 0;
1507 	char *p;
1508 	HistEvent hev;
1509 
1510 	do {
1511 		if ((buf = (char *) el_gets(el, &siz)) == NULL || !siz) {
1512 			*cmd = CMD_QUIT;
1513 			fprintf(stderr, "\r\n");
1514 			return (0);
1515 		}
1516 		if (strlen(buf) > 1)
1517 			history(hist, &hev, H_ENTER, buf);
1518 		p = parse(buf, cmd);
1519 	} while (!p);
1520 	return (p);
1521 }
1522 
1523 char *
1524 parse(char *buf, int *cmd)
1525 {
1526 	struct cmdtab *c;
1527 	char *p;
1528 	size_t len;
1529 
1530 	for (p=buf; isspace((unsigned char)*p); p++)
1531 		continue;
1532 
1533 	if (isdigit((unsigned char)*p) ||
1534 	    (p[0] == '#' && isdigit((unsigned char)p[1]))) {
1535 		*cmd = CMD_PLAY;
1536 		return (p);
1537 	}
1538 
1539 	for (buf = p; *p && ! isspace((unsigned char)*p); p++)
1540 		continue;
1541 
1542 	len = p - buf;
1543 	if (!len)
1544 		return (0);
1545 
1546 	if (*p) {		/* It must be a spacing character! */
1547 		char *q;
1548 
1549 		*p++ = 0;
1550 		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1551 			continue;
1552 		*q = 0;
1553 	}
1554 
1555 	*cmd = -1;
1556 	for (c=cmdtab; c->name; ++c) {
1557 		/* Is it an exact match? */
1558 		if (!strcasecmp(buf, c->name)) {
1559 			*cmd = c->command;
1560 			break;
1561 		}
1562 
1563 		/* Try short hand forms then... */
1564 		if (len >= c->min && ! strncasecmp(buf, c->name, len)) {
1565 			if (*cmd != -1 && *cmd != c->command) {
1566 				fprintf(stderr, "Ambiguous command\n");
1567 				return (0);
1568 			}
1569 			*cmd = c->command;
1570 		}
1571 	}
1572 
1573 	if (*cmd == -1) {
1574 		fprintf(stderr, "%s: Invalid command, enter ``help'' for commands.\n",
1575 		    __progname);
1576 		return (0);
1577 	}
1578 
1579 	while (isspace((unsigned char)*p))
1580 		p++;
1581 	return p;
1582 }
1583 
1584 int
1585 open_cd(char *dev, int needwrite)
1586 {
1587 	char *realdev;
1588 	int tries;
1589 
1590 	if (fd > -1) {
1591 		if (needwrite && !writeperm) {
1592 			close(fd);
1593 			fd = -1;
1594 		} else
1595 			return (1);
1596 	}
1597 
1598 	for (tries = 0; fd < 0 && tries < 10; tries++) {
1599 		if (needwrite)
1600 			fd = opendev(dev, O_RDWR, OPENDEV_PART, &realdev);
1601 		else
1602 			fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev);
1603 		if (fd == -1) {
1604 			if (errno == ENXIO) {
1605 				/*  ENXIO has an overloaded meaning here.
1606 				 *  The original "Device not configured" should
1607 				 *  be interpreted as "No disc in drive %s". */
1608 				warnx("No disc in drive %s.", realdev);
1609 				return (0);
1610 			} else if (errno != EIO) {
1611 				/*  EIO may simply mean the device is not ready
1612 				 *  yet which is common with CD changers. */
1613 				warn("Can't open %s", realdev);
1614 				return (0);
1615 			}
1616 		}
1617 		sleep(1);
1618 	}
1619 	if (fd == -1) {
1620 		warn("Can't open %s", realdev);
1621 		return (0);
1622 	}
1623 	writeperm = needwrite;
1624 	return (1);
1625 }
1626 
1627 char *
1628 prompt(void)
1629 {
1630 	return (verbose ? "cdio> " : "");
1631 }
1632 
1633 void
1634 switch_el(void)
1635 {
1636 	HistEvent hev;
1637 
1638 	if (el == NULL && hist == NULL) {
1639 		el = el_init(__progname, stdin, stdout, stderr);
1640 		hist = history_init();
1641 		history(hist, &hev, H_SETSIZE, 100);
1642 		el_set(el, EL_HIST, history, hist);
1643 		el_set(el, EL_EDITOR, "emacs");
1644 		el_set(el, EL_PROMPT, prompt);
1645 		el_set(el, EL_SIGNAL, 1);
1646 		el_source(el, NULL);
1647 
1648 	} else {
1649 		if (hist != NULL) {
1650 			history_end(hist);
1651 			hist = NULL;
1652 		}
1653 		if (el != NULL) {
1654 			el_end(el);
1655 			el = NULL;
1656 		}
1657 	}
1658 }
1659 
1660 void
1661 addmsf(u_int *m, u_int *s, u_int *f, u_char m_inc, u_char s_inc, u_char f_inc)
1662 {
1663 	*f += f_inc;
1664 	if (*f > 75) {
1665 		*s += *f / 75;
1666 		*f %= 75;
1667 	}
1668 
1669 	*s += s_inc;
1670 	if (*s > 60) {
1671 		*m += *s / 60;
1672 		*s %= 60;
1673 	}
1674 
1675 	*m += m_inc;
1676 }
1677 
1678 int
1679 cmpmsf(u_char m1, u_char s1, u_char f1, u_char m2, u_char s2, u_char f2)
1680 {
1681 	if (m1 > m2)
1682 		return (1);
1683 	else if (m1 < m2)
1684 		return (-1);
1685 
1686 	if (s1 > s2)
1687 		return (1);
1688 	else if (s1 < s2)
1689 		return (-1);
1690 
1691 	if  (f1 > f2)
1692 		return (1);
1693 	else if (f1 < f2)
1694 		return (-1);
1695 
1696 	return (0);
1697 }
1698 
1699 void
1700 toc2msf(u_int track, u_char *m, u_char *s, u_char *f)
1701 {
1702 	struct cd_toc_entry *ctep;
1703 
1704 	ctep = &toc_buffer[track - 1];
1705 
1706 	if (msf) {
1707 		*m = ctep->addr.msf.minute;
1708 		*s = ctep->addr.msf.second;
1709 		*f = ctep->addr.msf.frame;
1710 	} else
1711 		lba2msf(ctep->addr.lba, m, s, f);
1712 }
1713