xref: /openbsd/usr.bin/cdio/cdio.c (revision 5b133f3f)
1*5b133f3fSguenther /*	$OpenBSD: cdio.c,v 1.86 2023/03/08 04:43:10 guenther Exp $	*/
2de23af84Sjmc 
3de23af84Sjmc /*  Copyright (c) 1995 Serge V. Vakulenko
4de23af84Sjmc  * All rights reserved.
5de23af84Sjmc  *
6de23af84Sjmc  * Redistribution and use in source and binary forms, with or without
7de23af84Sjmc  * modification, are permitted provided that the following conditions
8de23af84Sjmc  * are met:
9de23af84Sjmc  *
10de23af84Sjmc  * 1. Redistributions of source code must retain the above copyright
11de23af84Sjmc  *    notice, this list of conditions and the following disclaimer.
12de23af84Sjmc  * 2. Redistributions in binary form must reproduce the above copyright
13de23af84Sjmc  *    notice, this list of conditions and the following disclaimer in the
14de23af84Sjmc  *    documentation and/or other materials provided with the distribution.
15de23af84Sjmc  * 3. All advertising materials mentioning features or use of this software
16de23af84Sjmc  *    must display the following acknowledgement:
17de23af84Sjmc  *	This product includes software developed by Serge V. Vakulenko.
18de23af84Sjmc  * 4. The name of the author may not be used to endorse or promote products
19de23af84Sjmc  *    derived from this software without specific prior written permission.
20de23af84Sjmc  *
21de23af84Sjmc  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22de23af84Sjmc  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23de23af84Sjmc  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24de23af84Sjmc  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25de23af84Sjmc  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26de23af84Sjmc  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27de23af84Sjmc  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28de23af84Sjmc  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29de23af84Sjmc  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30de23af84Sjmc  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31de23af84Sjmc  */
32de23af84Sjmc 
33c92011ceSdownsj /*
34c92011ceSdownsj  * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
35c92011ceSdownsj  * Based on the non-X based CD player by Jean-Marc Zucconi and
36c92011ceSdownsj  * Andrey A. Chernov.
37c92011ceSdownsj  *
38c92011ceSdownsj  * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
39c92011ceSdownsj  *
40c92011ceSdownsj  * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
41c92011ceSdownsj  *              A couple of further fixes to my own earlier "fixes".
42c92011ceSdownsj  *
43c92011ceSdownsj  * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
44c92011ceSdownsj  *              Added an ability to specify addresses relative to the
45c92011ceSdownsj  *              beginning of a track. This is in fact a variation of
46c92011ceSdownsj  *              doing the simple play_msf() call.
47c92011ceSdownsj  *
48c92011ceSdownsj  * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
49c92011ceSdownsj  *              New eject algorithm.
50c92011ceSdownsj  *              Some code style reformatting.
51c92011ceSdownsj  *
52c92011ceSdownsj  * $FreeBSD: cdcontrol.c,v 1.13 1996/06/25 21:01:27 ache Exp $
53c92011ceSdownsj  */
54c92011ceSdownsj 
551a0afcdeSderaadt #include <sys/types.h>
56ec6762c7Sfgsch #include <sys/cdio.h>
57ec6762c7Sfgsch #include <sys/ioctl.h>
58cf22b6b5Smjc #include <sys/queue.h>
597993cdedSmjc #include <sys/scsiio.h>
60cf22b6b5Smjc #include <sys/stat.h>
61ec6762c7Sfgsch 
62c92011ceSdownsj #include <ctype.h>
63dfcdb41bSespie #include <err.h>
64dfcdb41bSespie #include <errno.h>
65f4147939Sguenther #include <fcntl.h>
66a93de8edSschwarze #include <signal.h>
67c92011ceSdownsj #include <stdio.h>
68c92011ceSdownsj #include <stdlib.h>
69c92011ceSdownsj #include <string.h>
70c92011ceSdownsj #include <unistd.h>
71b9fc9a72Sderaadt #include <limits.h>
72ec6762c7Sfgsch #include <histedit.h>
73c92011ceSdownsj #include <util.h>
74dfcdb41bSespie #include <vis.h>
75c92011ceSdownsj 
76ec6762c7Sfgsch #include "extern.h"
77c92011ceSdownsj 
78c92011ceSdownsj #define ASTS_INVALID    0x00  /* Audio status byte not valid */
79c92011ceSdownsj #define ASTS_PLAYING    0x11  /* Audio play operation in progress */
80c92011ceSdownsj #define ASTS_PAUSED     0x12  /* Audio play operation paused */
81c92011ceSdownsj #define ASTS_COMPLETED  0x13  /* Audio play operation successfully completed */
82c92011ceSdownsj #define ASTS_ERROR      0x14  /* Audio play operation stopped due to error */
83c92011ceSdownsj #define ASTS_VOID       0x15  /* No current audio status to return */
84c92011ceSdownsj 
85c92011ceSdownsj #ifndef DEFAULT_CD_DRIVE
8633d336edSderaadt #  define DEFAULT_CD_DRIVE  "cd0"
87c92011ceSdownsj #endif
88c92011ceSdownsj 
89c92011ceSdownsj #define CMD_DEBUG       1
90f87f2499Smillert #define CMD_DEVICE      2
91f87f2499Smillert #define CMD_EJECT       3
92f87f2499Smillert #define CMD_HELP        4
93f87f2499Smillert #define CMD_INFO        5
94f87f2499Smillert #define CMD_PAUSE       6
95f87f2499Smillert #define CMD_PLAY        7
96f87f2499Smillert #define CMD_QUIT        8
97f87f2499Smillert #define CMD_RESUME      9
98f87f2499Smillert #define CMD_STOP        10
99f87f2499Smillert #define CMD_VOLUME      11
100f87f2499Smillert #define CMD_CLOSE       12
101f87f2499Smillert #define CMD_RESET       13
102f87f2499Smillert #define CMD_SET         14
103f87f2499Smillert #define CMD_STATUS      15
104f87f2499Smillert #define CMD_NEXT	16
105f87f2499Smillert #define CMD_PREV	17
106f87f2499Smillert #define CMD_REPLAY	18
1079ed7639aSespie #define CMD_CDDB	19
108ae359c1cSespie #define CMD_CDID	20
1097993cdedSmjc #define CMD_BLANK	21
110a076f0c4Smjc #define CMD_CDRIP	22
111a076f0c4Smjc #define CMD_CDPLAY	23
112c92011ceSdownsj 
113c92011ceSdownsj struct cmdtab {
114c92011ceSdownsj 	int command;
115c92011ceSdownsj 	char *name;
116a248831fSnaddy 	int min;
117c92011ceSdownsj 	char *args;
118c92011ceSdownsj } cmdtab[] = {
119c4c97271Snaddy { CMD_BLANK,	"blank",	1, "" },
120c4c97271Snaddy { CMD_CDDB,	"cddbinfo",     2, "[n]" },
121c4c97271Snaddy { CMD_CDID,	"cdid",		3, "" },
122c4c97271Snaddy { CMD_CDPLAY,	"cdplay",	3, "[track1-trackN ...]" },
123c4c97271Snaddy { CMD_CDRIP,	"cdrip",	3, "[track1-trackN ...]" },
124c92011ceSdownsj { CMD_CLOSE,    "close",        1, "" },
125c934c333Salek { CMD_DEBUG,    "debug",        3, "on | off" },
126f87f2499Smillert { CMD_DEVICE,   "device",       1, "devname" },
127c92011ceSdownsj { CMD_EJECT,    "eject",        1, "" },
128c4c97271Snaddy { CMD_QUIT,	"exit",		2, "" },
129c92011ceSdownsj { CMD_HELP,     "?",            1, 0 },
130c92011ceSdownsj { CMD_HELP,     "help",         1, "" },
131c92011ceSdownsj { CMD_INFO,     "info",         1, "" },
1323301428eSangelos { CMD_NEXT,	"next",		1, "" },
133c92011ceSdownsj { CMD_PAUSE,    "pause",        2, "" },
13474fdbf9dSjmc { CMD_PLAY,     "play",         1, "[track1[.index1] [track2[.index2]]]" },
13574fdbf9dSjmc { CMD_PLAY,     "play",         1, "[[tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]]]" },
136c92011ceSdownsj { CMD_PLAY,     "play",         1, "[#block [len]]" },
1373301428eSangelos { CMD_PREV,	"previous",	2, "" },
138c92011ceSdownsj { CMD_QUIT,     "quit",         1, "" },
139c4c97271Snaddy { CMD_REPLAY,	"replay",	3, "" },
140c92011ceSdownsj { CMD_RESET,    "reset",        4, "" },
141c92011ceSdownsj { CMD_RESUME,   "resume",       1, "" },
142c4c97271Snaddy { CMD_SET,      "set",          2, "lba | msf" },
143c92011ceSdownsj { CMD_STATUS,   "status",       1, "" },
144c92011ceSdownsj { CMD_STOP,     "stop",         3, "" },
145c4c97271Snaddy { CMD_VOLUME,   "volume",       1, "left_channel right_channel" },
146c4c97271Snaddy { CMD_VOLUME,   "volume",       1, "left | right | mono | stereo | mute" },
1479ed7639aSespie { 0, 0, 0, 0}
148c92011ceSdownsj };
149c92011ceSdownsj 
1503a87bfaeScsapuntz struct cd_toc_entry *toc_buffer;
151c92011ceSdownsj 
152c52ec6b0Smortimer struct track_head tracks;
153c52ec6b0Smortimer 
154c92011ceSdownsj char		*cdname;
155c92011ceSdownsj int		fd = -1;
156c7ed6047Sav int		writeperm = 0;
157dc4cd921Skrw u_int8_t	mediacap[MMC_FEATURE_MAX / NBBY];
158c92011ceSdownsj int		verbose = 1;
159c92011ceSdownsj int		msf = 1;
1609ed7639aSespie const char	*cddb_host;
1619ed7639aSespie char		**track_names;
162a93de8edSschwarze volatile sig_atomic_t signo;
163c92011ceSdownsj 
164ec6762c7Sfgsch EditLine	*el = NULL;	/* line-editing structure */
165ec6762c7Sfgsch History		*hist = NULL;	/* line-editing history */
166ec6762c7Sfgsch void		switch_el(void);
167ec6762c7Sfgsch 
168c92011ceSdownsj extern char	*__progname;
169c92011ceSdownsj 
170c72b5b24Smillert int		setvol(int, int);
171c72b5b24Smillert int		read_toc_entrys(int);
172c72b5b24Smillert int		play_msf(int, int, int, int, int, int);
173c72b5b24Smillert int		play_track(int, int, int, int);
174c72b5b24Smillert int		status(int *, int *, int *, int *);
1757b34dc5fSav int		is_wave(int);
1769dfd8015Sav __dead void	tao(int argc, char **argv);
177c72b5b24Smillert int		play(char *arg);
178c72b5b24Smillert int		info(char *arg);
1799ed7639aSespie int		cddbinfo(char *arg);
180c72b5b24Smillert int		pstatus(char *arg);
181c72b5b24Smillert int		play_next(char *arg);
182c72b5b24Smillert int		play_prev(char *arg);
183c72b5b24Smillert int		play_same(char *arg);
184a93de8edSschwarze void		sigint_handler(int);
185c72b5b24Smillert char		*input(int *);
186ec6762c7Sfgsch char		*prompt(void);
1879ed7639aSespie void		prtrack(struct cd_toc_entry *e, int lastflag, char *name);
188f3c3a9c6Smillert void		lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
189c72b5b24Smillert unsigned int	msf2lba(u_char m, u_char s, u_char f);
190c72b5b24Smillert int		play_blocks(int blk, int len);
191c72b5b24Smillert int		run(int cmd, char *arg);
192c72b5b24Smillert char		*parse(char *buf, int *cmd);
193da50f41aSespie void		help(void);
194da50f41aSespie void		usage(void);
195da50f41aSespie char		*strstatus(int);
196ae359c1cSespie int		cdid(void);
1978dcc8943Skrw void		addmsf(u_int *, u_int *, u_int *, u_char, u_char, u_char);
198b8a783aeSkrw int		cmpmsf(u_char, u_char, u_char, u_char, u_char, u_char);
1993438b82bSkrw void		toc2msf(u_int, u_char *, u_char *, u_char *);
200c92011ceSdownsj 
201f18d0a50Sderaadt void
help(void)2021837a5caSderaadt help(void)
203c92011ceSdownsj {
204c92011ceSdownsj 	struct cmdtab *c;
205c92011ceSdownsj 	char *s, n;
206a248831fSnaddy 	int i;
207c92011ceSdownsj 
208c92011ceSdownsj 	for (c = cmdtab; c->name; ++c) {
209c92011ceSdownsj 		if (!c->args)
210c92011ceSdownsj 			continue;
211c92011ceSdownsj 		printf("\t");
212c92011ceSdownsj 		for (i = c->min, s = c->name; *s; s++, i--) {
213c92011ceSdownsj 			if (i > 0)
21420ae3208Sderaadt 				n = toupper((unsigned char)*s);
215c92011ceSdownsj 			else
216c92011ceSdownsj 				n = *s;
217c92011ceSdownsj 			putchar(n);
218c92011ceSdownsj 		}
219c92011ceSdownsj 		if (*c->args)
220c92011ceSdownsj 			printf(" %s", c->args);
221c92011ceSdownsj 		printf("\n");
222c92011ceSdownsj 	}
223c92011ceSdownsj 	printf("\n\tThe word \"play\" is not required for the play commands.\n");
224c92011ceSdownsj 	printf("\tThe plain target address is taken as a synonym for play.\n");
225c92011ceSdownsj }
226c92011ceSdownsj 
227f18d0a50Sderaadt void
usage(void)2281837a5caSderaadt usage(void)
229c92011ceSdownsj {
230881f6c5fSkn 	fprintf(stderr, "usage: %s [-sv] [-d host:port] [-f device] "
231881f6c5fSkn 	    "[command [arg ...]]\n",
232f18d0a50Sderaadt 	    __progname);
233c92011ceSdownsj 	exit(1);
234c92011ceSdownsj }
235c92011ceSdownsj 
236f18d0a50Sderaadt int
main(int argc,char ** argv)2379ed7639aSespie main(int argc, char **argv)
238c92011ceSdownsj {
239c283657cSmiod 	int ch, cmd;
240c92011ceSdownsj 	char *arg;
241c92011ceSdownsj 
242c92011ceSdownsj 	cdname = getenv("DISC");
243c92011ceSdownsj 	if (!cdname)
244c92011ceSdownsj 		cdname = getenv("CDROM");
245c92011ceSdownsj 
2469ed7639aSespie 	cddb_host = getenv("CDDB");
2479ed7639aSespie 	if (!cddb_host)
248143054faSnaddy 		cddb_host = "gnudb.gnudb.org";
2499ed7639aSespie 
250c283657cSmiod 	while ((ch = getopt(argc, argv, "svd:f:")) != -1)
251c283657cSmiod 		switch (ch) {
252c92011ceSdownsj 		case 's':
253c92011ceSdownsj 			verbose = 0;
254c283657cSmiod 			break;
255c92011ceSdownsj 		case 'v':
256dc4cd921Skrw 			verbose++;
257c283657cSmiod 			break;
258c92011ceSdownsj 		case 'f':
259c92011ceSdownsj 			cdname = optarg;
260c283657cSmiod 			break;
2619ed7639aSespie 		case 'd':
2629ed7639aSespie 			cddb_host = optarg;
263c283657cSmiod 			break;
264c92011ceSdownsj 		default:
265c92011ceSdownsj 			usage();
266c92011ceSdownsj 		}
267c283657cSmiod 
268c92011ceSdownsj 	argc -= optind;
269c92011ceSdownsj 	argv += optind;
270c92011ceSdownsj 
271c92011ceSdownsj 	if (argc > 0 && ! strcasecmp(*argv, "help"))
272c92011ceSdownsj 		usage();
273c92011ceSdownsj 
274c92011ceSdownsj 	if (!cdname) {
275c92011ceSdownsj 		cdname = DEFAULT_CD_DRIVE;
276dc4cd921Skrw 		if (verbose > 1)
277c92011ceSdownsj 			fprintf(stderr,
27880cdf6b4Sderaadt 			    "No CD device name specified. Defaulting to %s.\n",
27980cdf6b4Sderaadt 			    cdname);
280c92011ceSdownsj 	}
281c92011ceSdownsj 
2827993cdedSmjc 	if (argc > 0 && !strcasecmp(*argv, "tao")) {
2839dfd8015Sav 		tao(argc, argv);
2849dfd8015Sav 		/* NOTREACHED */
2857993cdedSmjc 	}
286c92011ceSdownsj 	if (argc > 0) {
287c92011ceSdownsj 		char buf[80], *p;
288c92011ceSdownsj 		int len;
289c92011ceSdownsj 
290c92011ceSdownsj 		for (p=buf; argc-->0; ++argv) {
2910144018cSkrw 			len = snprintf(p, buf + sizeof buf - p,
2920144018cSkrw 			   "%s%s", (p > buf) ? " " : "", *argv);
293c92011ceSdownsj 
294515e489cSderaadt 			if (len < 0 || len >= buf + sizeof buf - p)
2950144018cSkrw 				errx(1, "argument list too long.");
296c92011ceSdownsj 
297c92011ceSdownsj 			p += len;
298c92011ceSdownsj 		}
299c92011ceSdownsj 		arg = parse(buf, &cmd);
300c92011ceSdownsj 		return (run(cmd, arg));
301c92011ceSdownsj 	}
302c92011ceSdownsj 
303c92011ceSdownsj 	if (verbose == 1)
304c92011ceSdownsj 		verbose = isatty(0);
305c92011ceSdownsj 
306c92011ceSdownsj 	if (verbose) {
307c92011ceSdownsj 		printf("Compact Disc Control utility, version %s\n", VERSION);
308c92011ceSdownsj 		printf("Type `?' for command list\n\n");
309c92011ceSdownsj 	}
310c92011ceSdownsj 
311ec6762c7Sfgsch 	switch_el();
312ec6762c7Sfgsch 
313c92011ceSdownsj 	for (;;) {
314c92011ceSdownsj 		arg = input(&cmd);
315c92011ceSdownsj 		if (run(cmd, arg) < 0) {
316c92011ceSdownsj 			if (verbose)
317f87f2499Smillert 				warn(NULL);
318c92011ceSdownsj 			close(fd);
319c92011ceSdownsj 			fd = -1;
320c92011ceSdownsj 		}
321c92011ceSdownsj 		fflush(stdout);
322c92011ceSdownsj 	}
323c92011ceSdownsj }
324c92011ceSdownsj 
325f18d0a50Sderaadt int
run(int cmd,char * arg)3269ed7639aSespie run(int cmd, char *arg)
327c92011ceSdownsj {
3287b34dc5fSav 	int l, r, rc;
329b9fc9a72Sderaadt 	static char newcdname[PATH_MAX];
330c92011ceSdownsj 
331c92011ceSdownsj 	switch (cmd) {
332c92011ceSdownsj 
333c92011ceSdownsj 	case CMD_QUIT:
334ec6762c7Sfgsch 		switch_el();
335c92011ceSdownsj 		exit(0);
336c92011ceSdownsj 
337c92011ceSdownsj 	case CMD_INFO:
338c7ed6047Sav 		if (!open_cd(cdname, 0))
339c92011ceSdownsj 			return (0);
340c92011ceSdownsj 
341c92011ceSdownsj 		return info(arg);
342c92011ceSdownsj 
3439ed7639aSespie 	case CMD_CDDB:
344c7ed6047Sav 		if (!open_cd(cdname, 0))
3459ed7639aSespie 			return (0);
3469ed7639aSespie 
3479ed7639aSespie 		return cddbinfo(arg);
3489ed7639aSespie 
349ae359c1cSespie 	case CMD_CDID:
350c7ed6047Sav 		if (!open_cd(cdname, 0))
351ae359c1cSespie 			return (0);
352ae359c1cSespie 		return cdid();
353ae359c1cSespie 
354c92011ceSdownsj 	case CMD_STATUS:
355c7ed6047Sav 		if (!open_cd(cdname, 0))
356c92011ceSdownsj 			return (0);
357c92011ceSdownsj 
358c92011ceSdownsj 		return pstatus(arg);
359c92011ceSdownsj 
360c92011ceSdownsj 	case CMD_PAUSE:
361c7ed6047Sav 		if (!open_cd(cdname, 0))
362c92011ceSdownsj 			return (0);
363c92011ceSdownsj 
364c92011ceSdownsj 		return ioctl(fd, CDIOCPAUSE);
365c92011ceSdownsj 
366c92011ceSdownsj 	case CMD_RESUME:
367c7ed6047Sav 		if (!open_cd(cdname, 0))
368c92011ceSdownsj 			return (0);
369c92011ceSdownsj 
370c92011ceSdownsj 		return ioctl(fd, CDIOCRESUME);
371c92011ceSdownsj 
372c92011ceSdownsj 	case CMD_STOP:
373c7ed6047Sav 		if (!open_cd(cdname, 0))
374c92011ceSdownsj 			return (0);
375c92011ceSdownsj 
376c92011ceSdownsj 		rc = ioctl(fd, CDIOCSTOP);
377c92011ceSdownsj 
378c92011ceSdownsj 		(void) ioctl(fd, CDIOCALLOW);
379c92011ceSdownsj 
380c92011ceSdownsj 		return (rc);
381c92011ceSdownsj 
382c92011ceSdownsj 	case CMD_RESET:
383c7ed6047Sav 		if (!open_cd(cdname, 0))
384c92011ceSdownsj 			return (0);
385c92011ceSdownsj 
386c92011ceSdownsj 		rc = ioctl(fd, CDIOCRESET);
3873aaa63ebSderaadt 		if (rc == -1)
388c92011ceSdownsj 			return rc;
389c92011ceSdownsj 		close(fd);
390c92011ceSdownsj 		fd = -1;
391c92011ceSdownsj 		return (0);
392c92011ceSdownsj 
393c92011ceSdownsj 	case CMD_DEBUG:
394c7ed6047Sav 		if (!open_cd(cdname, 0))
395c92011ceSdownsj 			return (0);
396c92011ceSdownsj 
397c92011ceSdownsj 		if (!strcasecmp(arg, "on"))
398c92011ceSdownsj 			return ioctl(fd, CDIOCSETDEBUG);
399c92011ceSdownsj 
400c92011ceSdownsj 		if (!strcasecmp(arg, "off"))
401c92011ceSdownsj 			return ioctl(fd, CDIOCCLRDEBUG);
402c92011ceSdownsj 
403c92011ceSdownsj 		printf("%s: Invalid command arguments\n", __progname);
404c92011ceSdownsj 
405c92011ceSdownsj 		return (0);
406c92011ceSdownsj 
407f87f2499Smillert 	case CMD_DEVICE:
408f87f2499Smillert 		/* close old device */
409f87f2499Smillert 		if (fd > -1) {
410f87f2499Smillert 			(void) ioctl(fd, CDIOCALLOW);
411f87f2499Smillert 			close(fd);
412f87f2499Smillert 			fd = -1;
413f87f2499Smillert 		}
414f87f2499Smillert 
415d8a9cdc1Salek 		if (strlen(arg) == 0) {
416d8a9cdc1Salek 			printf("%s: Invalid parameter\n", __progname);
417d8a9cdc1Salek 			return (0);
418d8a9cdc1Salek 		}
419d8a9cdc1Salek 
420f87f2499Smillert 		/* open new device */
4217993cdedSmjc 		if (!open_cd(arg, 0))
422f87f2499Smillert 			return (0);
4233cbec610Slebel 		(void) strlcpy(newcdname, arg, sizeof(newcdname));
424f87f2499Smillert 		cdname = newcdname;
425f87f2499Smillert 		return (1);
426f87f2499Smillert 
427c92011ceSdownsj 	case CMD_EJECT:
428c7ed6047Sav 		if (!open_cd(cdname, 0))
429c92011ceSdownsj 			return (0);
430c92011ceSdownsj 
431c92011ceSdownsj 		(void) ioctl(fd, CDIOCALLOW);
432c92011ceSdownsj 		rc = ioctl(fd, CDIOCEJECT);
4333aaa63ebSderaadt 		if (rc == -1)
434c92011ceSdownsj 			return (rc);
435c92011ceSdownsj 		close(fd);
436c92011ceSdownsj 		fd = -1;
4379ed7639aSespie 		if (track_names)
4389ed7639aSespie 			free_names(track_names);
4399ed7639aSespie 		track_names = NULL;
440c92011ceSdownsj 		return (0);
441c92011ceSdownsj 
442c92011ceSdownsj 	case CMD_CLOSE:
443c7ed6047Sav 		if (!open_cd(cdname, 0))
444c92011ceSdownsj 			return (0);
445c92011ceSdownsj 
446c92011ceSdownsj 		(void) ioctl(fd, CDIOCALLOW);
447c92011ceSdownsj 		rc = ioctl(fd, CDIOCCLOSE);
4483aaa63ebSderaadt 		if (rc == -1)
449c92011ceSdownsj 			return (rc);
450c92011ceSdownsj 		close(fd);
451c92011ceSdownsj 		fd = -1;
452c92011ceSdownsj 		return (0);
453c92011ceSdownsj 
454c92011ceSdownsj 	case CMD_PLAY:
455c7ed6047Sav 		if (!open_cd(cdname, 0))
456c92011ceSdownsj 			return (0);
457c92011ceSdownsj 
45820ae3208Sderaadt 		while (isspace((unsigned char)*arg))
459c92011ceSdownsj 			arg++;
460c92011ceSdownsj 
461c92011ceSdownsj 		return play(arg);
462c92011ceSdownsj 
463c92011ceSdownsj 	case CMD_SET:
464c92011ceSdownsj 		if (!strcasecmp(arg, "msf"))
465c92011ceSdownsj 			msf = 1;
466c92011ceSdownsj 		else if (!strcasecmp(arg, "lba"))
467c92011ceSdownsj 			msf = 0;
468c92011ceSdownsj 		else
469c92011ceSdownsj 			printf("%s: Invalid command arguments\n", __progname);
470c92011ceSdownsj 		return (0);
471c92011ceSdownsj 
472c92011ceSdownsj 	case CMD_VOLUME:
473c7ed6047Sav 		if (!open_cd(cdname, 0))
474c92011ceSdownsj 			return (0);
475c92011ceSdownsj 
476c92011ceSdownsj 		if (!strncasecmp(arg, "left", strlen(arg)))
477c92011ceSdownsj 			return ioctl(fd, CDIOCSETLEFT);
478c92011ceSdownsj 
479c92011ceSdownsj 		if (!strncasecmp(arg, "right", strlen(arg)))
480c92011ceSdownsj 			return ioctl(fd, CDIOCSETRIGHT);
481c92011ceSdownsj 
482c92011ceSdownsj 		if (!strncasecmp(arg, "mono", strlen(arg)))
483c92011ceSdownsj 			return ioctl(fd, CDIOCSETMONO);
484c92011ceSdownsj 
485c92011ceSdownsj 		if (!strncasecmp(arg, "stereo", strlen(arg)))
486ac9e9f6dSdownsj 			return ioctl(fd, CDIOCSETSTEREO);
487c92011ceSdownsj 
488c92011ceSdownsj 		if (!strncasecmp(arg, "mute", strlen(arg)))
489c92011ceSdownsj 			return ioctl(fd, CDIOCSETMUTE);
490c92011ceSdownsj 
491c92011ceSdownsj 		if (2 != sscanf(arg, "%d%d", &l, &r)) {
492c92011ceSdownsj 			printf("%s: Invalid command arguments\n", __progname);
493c92011ceSdownsj 			return (0);
494c92011ceSdownsj 		}
495c92011ceSdownsj 
496c92011ceSdownsj 		return setvol(l, r);
497c92011ceSdownsj 
4983301428eSangelos 	case CMD_NEXT:
499c7ed6047Sav 		if (!open_cd(cdname, 0))
5003301428eSangelos 			return (0);
5013301428eSangelos 
5023301428eSangelos 		return play_next(arg);
5033301428eSangelos 
5043301428eSangelos 	case CMD_PREV:
505c7ed6047Sav 		if (!open_cd(cdname, 0))
5063301428eSangelos 			return (0);
5073301428eSangelos 
5083301428eSangelos 		return play_prev(arg);
5093301428eSangelos 
5104efcb1a6Sangelos 	case CMD_REPLAY:
511c7ed6047Sav 		if (!open_cd(cdname, 0))
5124efcb1a6Sangelos 			return 0;
5134efcb1a6Sangelos 
5144efcb1a6Sangelos 		return play_same(arg);
5157993cdedSmjc 	case CMD_BLANK:
516c7ed6047Sav 		if (!open_cd(cdname, 1))
5177993cdedSmjc 			return 0;
5187993cdedSmjc 
5197299f7a2Sfgsch 		if (get_media_capabilities(mediacap, 1) == -1) {
5205c0dfce9Sav 			warnx("Can't determine media type");
5215c0dfce9Sav 			return (0);
5225c0dfce9Sav 		}
5231a0afcdeSderaadt 		if (cdio_isset(mediacap, MMC_FEATURE_CDRW_WRITE) == 0 &&
524f560e737Sav 		    get_media_type() != MEDIATYPE_CDRW) {
5255c0dfce9Sav 			warnx("The media doesn't support blanking");
5265c0dfce9Sav 			return (0);
5275c0dfce9Sav 		}
5285c0dfce9Sav 
5297993cdedSmjc 		return blank();
530a076f0c4Smjc 	case CMD_CDRIP:
531c7ed6047Sav 		if (!open_cd(cdname, 0))
532a076f0c4Smjc 			return (0);
533a076f0c4Smjc 
53420ae3208Sderaadt 		while (isspace((unsigned char)*arg))
535a076f0c4Smjc 			arg++;
536a076f0c4Smjc 
537a076f0c4Smjc 		return cdrip(arg);
538a076f0c4Smjc 	case CMD_CDPLAY:
539c7ed6047Sav 		if (!open_cd(cdname, 0))
540a076f0c4Smjc 			return (0);
541a076f0c4Smjc 
54220ae3208Sderaadt 		while (isspace((unsigned char)*arg))
543a076f0c4Smjc 			arg++;
544a076f0c4Smjc 
545a076f0c4Smjc 		return cdplay(arg);
546c92011ceSdownsj 	default:
547c92011ceSdownsj 	case CMD_HELP:
548c92011ceSdownsj 		help();
549c92011ceSdownsj 		return (0);
550c92011ceSdownsj 
551c92011ceSdownsj 	}
552c92011ceSdownsj }
553c92011ceSdownsj 
554f7dfecd4Sav /*
555f7dfecd4Sav  * Check if audio file has RIFF WAVE format. If not, we assume it's just PCM.
556f7dfecd4Sav  */
557f7dfecd4Sav int
is_wave(int fd)558f7dfecd4Sav is_wave(int fd)
559f7dfecd4Sav {
560f7dfecd4Sav 	char buf[WAVHDRLEN];
561f7dfecd4Sav 	int rv;
562f7dfecd4Sav 
563f7dfecd4Sav 	rv = 0;
564f7dfecd4Sav 	if (read(fd, buf, sizeof(buf)) == sizeof(buf)) {
565f7dfecd4Sav 		if (memcmp(buf, "RIFF", 4) == 0 &&
566f7dfecd4Sav 		    memcmp(buf + 8, "WAVE", 4) == 0)
567f7dfecd4Sav 			rv = 1;
568f7dfecd4Sav 	}
569f7dfecd4Sav 
570f7dfecd4Sav 	return (rv);
571f7dfecd4Sav }
572f7dfecd4Sav 
5739dfd8015Sav __dead void
tao(int argc,char ** argv)5749dfd8015Sav tao(int argc, char **argv)
5759dfd8015Sav {
5769dfd8015Sav 	struct stat sb;
5779dfd8015Sav 	struct track_info *cur_track;
5789dfd8015Sav 	struct track_info *tr;
5799dfd8015Sav 	off_t availblk, needblk = 0;
5809dfd8015Sav 	u_int blklen;
5819dfd8015Sav 	u_int ntracks = 0;
5829dfd8015Sav 	char type;
5837b34dc5fSav 	int ch, speed;
5847b34dc5fSav 	const char *errstr;
5859dfd8015Sav 
5869dfd8015Sav 	if (argc == 1)
5879dfd8015Sav 		usage();
5889dfd8015Sav 
5899dfd8015Sav 	SLIST_INIT(&tracks);
5909dfd8015Sav 	type = 'd';
5917b34dc5fSav 	speed = DRIVE_SPEED_OPTIMAL;
5929dfd8015Sav 	blklen = 2048;
5939dfd8015Sav 	while (argc > 1) {
5949dfd8015Sav 		tr = malloc(sizeof(struct track_info));
5959dfd8015Sav 		if (tr == NULL)
5969dfd8015Sav 			err(1, "tao");
5979dfd8015Sav 
5989dfd8015Sav 		optreset = 1;
5999dfd8015Sav 		optind = 1;
6007b34dc5fSav 		while ((ch = getopt(argc, argv, "ads:")) != -1) {
6019dfd8015Sav 			switch (ch) {
6029dfd8015Sav 			case 'a':
6039dfd8015Sav 				type = 'a';
6049dfd8015Sav 				blklen = 2352;
6059dfd8015Sav 				break;
6069dfd8015Sav 			case 'd':
6079dfd8015Sav 				type = 'd';
6089dfd8015Sav 				blklen = 2048;
6099dfd8015Sav 				break;
6107b34dc5fSav 			case 's':
6117b34dc5fSav 				if (strcmp(optarg, "auto") == 0) {
6127b34dc5fSav 					speed = DRIVE_SPEED_OPTIMAL;
6137b34dc5fSav 				} else if (strcmp(optarg, "max") == 0) {
6147b34dc5fSav 					speed = DRIVE_SPEED_MAX;
6157b34dc5fSav 				} else {
6167b34dc5fSav 					speed = (int)strtonum(optarg, 1,
6177b34dc5fSav 					    CD_MAX_SPEED, &errstr);
6187b34dc5fSav 					if (errstr != NULL) {
6197b34dc5fSav 						errx(1,
6207b34dc5fSav 						    "incorrect speed value");
6217b34dc5fSav 					}
6227b34dc5fSav 				}
6237b34dc5fSav 				break;
6249dfd8015Sav 			default:
6259dfd8015Sav 				usage();
6269dfd8015Sav 				/* NOTREACHED */
6279dfd8015Sav 			}
6289dfd8015Sav 		}
6299dfd8015Sav 
6307b34dc5fSav 		if (speed != DRIVE_SPEED_OPTIMAL && speed != DRIVE_SPEED_MAX)
6317b34dc5fSav 			tr->speed = CD_SPEED_TO_KBPS(speed, blklen);
6327b34dc5fSav 		else
6337b34dc5fSav 			tr->speed = speed;
6347b34dc5fSav 
6359dfd8015Sav 		tr->type = type;
6369dfd8015Sav 		tr->blklen = blklen;
6379dfd8015Sav 		argc -= optind;
6389dfd8015Sav 		argv += optind;
6399dfd8015Sav 		if (argv[0] == NULL)
6409dfd8015Sav 			usage();
6419dfd8015Sav 		tr->file = argv[0];
642b7041c07Sderaadt 		tr->fd = open(tr->file, O_RDONLY);
6439dfd8015Sav 		if (tr->fd == -1)
6449dfd8015Sav 			err(1, "cannot open file %s", tr->file);
6459dfd8015Sav 		if (fstat(tr->fd, &sb) == -1)
6469dfd8015Sav 			err(1, "cannot stat file %s", tr->file);
6479dfd8015Sav 		tr->sz = sb.st_size;
648f7dfecd4Sav 		tr->off = 0;
649f7dfecd4Sav 		if (tr->type == 'a') {
650f7dfecd4Sav 			if (is_wave(tr->fd)) {
6519dfd8015Sav 				tr->sz -= WAVHDRLEN;
652f7dfecd4Sav 				tr->off = WAVHDRLEN;
653f7dfecd4Sav 			}
654f7dfecd4Sav 		}
6559dfd8015Sav 		if (SLIST_EMPTY(&tracks))
6569dfd8015Sav 			SLIST_INSERT_HEAD(&tracks, tr, track_list);
6579dfd8015Sav 		else
6589dfd8015Sav 			SLIST_INSERT_AFTER(cur_track, tr, track_list);
6599dfd8015Sav 		cur_track = tr;
6609dfd8015Sav 	}
6619dfd8015Sav 
6629dfd8015Sav 	if (!open_cd(cdname, 1))
6639dfd8015Sav 		exit(1);
6647299f7a2Sfgsch 	if (get_media_capabilities(mediacap, 1) == -1)
6659dfd8015Sav 		errx(1, "Can't determine media type");
6661a0afcdeSderaadt 	if (cdio_isset(mediacap, MMC_FEATURE_CD_TAO) == 0)
6679dfd8015Sav 		errx(1, "The media can't be written in TAO mode");
6689dfd8015Sav 
6699dfd8015Sav 	get_disc_size(&availblk);
6709dfd8015Sav 	SLIST_FOREACH(tr, &tracks, track_list) {
6719dfd8015Sav 		needblk += tr->sz/tr->blklen;
6729dfd8015Sav 		ntracks++;
6739dfd8015Sav 	}
6749dfd8015Sav 	needblk += (ntracks - 1) * 150; /* transition area between tracks */
6759dfd8015Sav 	if (needblk > availblk)
6769dfd8015Sav 		errx(1, "Only %llu of the required %llu blocks available",
6779dfd8015Sav 		    availblk, needblk);
6789dfd8015Sav 	if (writetao(&tracks) != 0)
6799dfd8015Sav 		exit(1);
6809dfd8015Sav 	else
6819dfd8015Sav 		exit(0);
6829dfd8015Sav }
6839dfd8015Sav 
684f18d0a50Sderaadt int
play(char * arg)6859ed7639aSespie play(char *arg)
686c92011ceSdownsj {
687c92011ceSdownsj 	struct ioc_toc_header h;
6888bdf91ceSkrw 	unsigned char tm, ts, tf;
6898bdf91ceSkrw 	unsigned int tr1, tr2, m1, m2, s1, s2, f1, f2, i1, i2;
6908bdf91ceSkrw 	unsigned int blk, len, n;
6918bdf91ceSkrw 	char c;
6928bdf91ceSkrw 	int rc;
693c92011ceSdownsj 
694c92011ceSdownsj 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
695c92011ceSdownsj 
6963aaa63ebSderaadt 	if (rc == -1)
697c92011ceSdownsj 		return (rc);
698c92011ceSdownsj 
6998bdf91ceSkrw 	if (h.starting_track > h.ending_track) {
7008bdf91ceSkrw 		printf("TOC starting_track > TOC ending_track\n");
7018bdf91ceSkrw 		return (0);
7028bdf91ceSkrw 	}
7038bdf91ceSkrw 
704c92011ceSdownsj 	n = h.ending_track - h.starting_track + 1;
705c92011ceSdownsj 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
706c92011ceSdownsj 
707c92011ceSdownsj 	if (rc < 0)
708c92011ceSdownsj 		return (rc);
709c92011ceSdownsj 
7108bdf91ceSkrw 	/*
7118bdf91ceSkrw 	 * Truncate trailing white space. Then by adding %c to the end of the
7128bdf91ceSkrw 	 * sscanf() formats we catch any errant trailing characters.
7138bdf91ceSkrw 	 */
7148bdf91ceSkrw 	rc = strlen(arg) - 1;
71520ae3208Sderaadt 	while (rc >= 0 && isspace((unsigned char)arg[rc])) {
7168bdf91ceSkrw 		arg[rc] = '\0';
7178bdf91ceSkrw 		rc--;
7188bdf91ceSkrw 	}
7198bdf91ceSkrw 
720c92011ceSdownsj 	if (!arg || ! *arg) {
721c92011ceSdownsj 		/* Play the whole disc */
7228bdf91ceSkrw 		return (play_track(h.starting_track, 1, h.ending_track, 1));
723c92011ceSdownsj 	}
724c92011ceSdownsj 
725c92011ceSdownsj 	if (strchr(arg, '#')) {
726c92011ceSdownsj 		/* Play block #blk [ len ] */
7278bdf91ceSkrw 		if (2 != sscanf(arg, "#%u%u%c", &blk, &len, &c) &&
7288bdf91ceSkrw 		    1 != sscanf(arg, "#%u%c", &blk, &c)) {
7298bdf91ceSkrw 			printf("%s: Invalid command arguments\n", __progname);
7308bdf91ceSkrw 			return (0);
7318bdf91ceSkrw 		}
732c92011ceSdownsj 
733c92011ceSdownsj 		if (len == 0) {
734c92011ceSdownsj 			if (msf)
735c92011ceSdownsj 				len = msf2lba(toc_buffer[n].addr.msf.minute,
736c92011ceSdownsj 				    toc_buffer[n].addr.msf.second,
737c92011ceSdownsj 				    toc_buffer[n].addr.msf.frame) - blk;
738c92011ceSdownsj 			else
73943844f6cSkrw 				len = toc_buffer[n].addr.lba - blk;
740c92011ceSdownsj 		}
741c92011ceSdownsj 		return play_blocks(blk, len);
742c92011ceSdownsj 	}
743c92011ceSdownsj 
7448bdf91ceSkrw 	if (strchr(arg, ':') == NULL) {
745c92011ceSdownsj 		/*
7468bdf91ceSkrw 		 * Play track tr1[.i1] [tr2[.i2]]
747c92011ceSdownsj 		 */
7488bdf91ceSkrw 		if (4 == sscanf(arg, "%u.%u%u.%u%c", &tr1, &i1, &tr2, &i2, &c))
7498bdf91ceSkrw 			goto play_track;
750c92011ceSdownsj 
7518bdf91ceSkrw 		i2 = 1;
7528bdf91ceSkrw 		if (3 == sscanf(arg, "%u.%u%u%c", &tr1, &i1, &tr2, &c))
7538bdf91ceSkrw 			goto play_track;
754c92011ceSdownsj 
7558bdf91ceSkrw 		i1 = 1;
7568bdf91ceSkrw 		if (3 == sscanf(arg, "%u%u.%u%c", &tr1, &tr2, &i2, &c))
7578bdf91ceSkrw 			goto play_track;
758c92011ceSdownsj 
7598bdf91ceSkrw 		tr2 = 0;
7608bdf91ceSkrw 		i2 = 1;
7618bdf91ceSkrw 		if (2 == sscanf(arg, "%u.%u%c", &tr1, &i1, &c))
7628bdf91ceSkrw 			goto play_track;
763c92011ceSdownsj 
7648bdf91ceSkrw 		i1 = i2 = 1;
7658bdf91ceSkrw 		if (2 == sscanf(arg, "%u%u%c", &tr1, &tr2, &c))
7668bdf91ceSkrw 			goto play_track;
767c92011ceSdownsj 
7688bdf91ceSkrw 		i1 = i2 = 1;
7698bdf91ceSkrw 		tr2 = 0;
7708bdf91ceSkrw 		if (1 == sscanf(arg, "%u%c", &tr1, &c))
7718bdf91ceSkrw 			goto play_track;
772c92011ceSdownsj 
7738bdf91ceSkrw 		printf("%s: Invalid command arguments\n", __progname);
7747a498d98Skrw 		return (0);
7758bdf91ceSkrw 
7768bdf91ceSkrw play_track:
7778bdf91ceSkrw 		if (tr1 > n || tr2 > n) {
7788bdf91ceSkrw 			printf("Track number must be between 0 and %u\n", n);
7798bdf91ceSkrw 			return (0);
7808bdf91ceSkrw 		} else if (tr2 == 0)
7818bdf91ceSkrw 			tr2 = h.ending_track;
7828bdf91ceSkrw 
7838bdf91ceSkrw 		if (tr1 > tr2) {
7848bdf91ceSkrw 			printf("starting_track > ending_track\n");
7857a498d98Skrw 			return (0);
7867a498d98Skrw 		}
787c92011ceSdownsj 
7888bdf91ceSkrw 		return (play_track(tr1, i1, tr2, i2));
7898bdf91ceSkrw 	}
7908bdf91ceSkrw 
7918bdf91ceSkrw 	/*
7928bdf91ceSkrw 	 * Play MSF [tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]]
7938bdf91ceSkrw 	 *
7948bdf91ceSkrw 	 * Start Time		End Time
7958bdf91ceSkrw 	 * ----------		--------
7968bdf91ceSkrw 	 * tr1 m1:s1.f1		tr2 m2:s2.f2
7978bdf91ceSkrw 	 * tr1 m1:s1   		tr2 m2:s2.f2
7988bdf91ceSkrw 	 * tr1 m1:s1.f1		tr2 m2:s2
7998bdf91ceSkrw 	 * tr1 m1:s1   		tr2 m2:s2
8008bdf91ceSkrw 	 *     m1:s1.f1		tr2 m2:s2.f2
8018bdf91ceSkrw 	 *     m1:s1   		tr2 m2:s2.f2
8028bdf91ceSkrw 	 *     m1:s1.f1		tr2 m2:s2
8038bdf91ceSkrw 	 *     m1:s1   		tr2 m2:s2
8048bdf91ceSkrw 	 * tr1 m1:s1.f1		    m2:s2.f2
8058bdf91ceSkrw 	 * tr1 m1:s1   		    m2:s2.f2
8068bdf91ceSkrw 	 * tr1 m1:s1.f1		    m2:s2
8078bdf91ceSkrw 	 * tr1 m1:s1  		    m2:s2
8088bdf91ceSkrw 	 *     m1:s1.f1		    m2:s2.f2
8098bdf91ceSkrw 	 *     m1:s1       	    m2:s2.f2
8108bdf91ceSkrw 	 *     m1:s1.f1  	    m2:s2
8118bdf91ceSkrw 	 *     m1:s1     	    m2:s2
8128bdf91ceSkrw 	 * tr1 m1:s1.f1		tr2
8138bdf91ceSkrw 	 * tr1 m1:s1    	tr2
8148bdf91ceSkrw 	 *     m1:s1.f1  	tr2
8158bdf91ceSkrw 	 *     m1:s1      	tr2
8168bdf91ceSkrw 	 * tr1 m1:s1.f1		<end of disc>
8178bdf91ceSkrw 	 * tr1 m1:s1    	<end of disc>
8188bdf91ceSkrw 	 *     m1:s1.f1  	<end of disc>
8198bdf91ceSkrw 	 *     m1:s1      	<end of disc>
8208bdf91ceSkrw 	 */
8218bdf91ceSkrw 
8228bdf91ceSkrw 	/* tr1 m1:s1.f1		tr2 m2:s2.f2 */
8238bdf91ceSkrw 	if (8 == sscanf(arg, "%u%u:%u.%u%u%u:%u.%u%c",
8248bdf91ceSkrw 	    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c))
8258bdf91ceSkrw 		goto play_msf;
8268bdf91ceSkrw 
8278bdf91ceSkrw 	/* tr1 m1:s1   		tr2 m2:s2.f2 */
8288bdf91ceSkrw 	f1 = 0;
8298bdf91ceSkrw 	if (7 == sscanf(arg, "%u%u:%u%u%u:%u.%u%c",
8308bdf91ceSkrw 	    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2, &c))
8318bdf91ceSkrw 		goto play_msf;
8328bdf91ceSkrw 
8338bdf91ceSkrw 	/* tr1 m1:s1.f1		tr2 m2:s2 */
8348bdf91ceSkrw 	f2 =0;
8358bdf91ceSkrw 	if (7 == sscanf(arg, "%u%u:%u.%u%u%u:%u%c",
8368bdf91ceSkrw 	    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &c))
8378bdf91ceSkrw 		goto play_msf;
8388bdf91ceSkrw 
8398bdf91ceSkrw 	/*     m1:s1.f1		tr2 m2:s2.f2 */
8408bdf91ceSkrw 	tr1 = 0;
8418bdf91ceSkrw 	if (7 == sscanf(arg, "%u:%u.%u%u%u:%u.%u%c",
8428bdf91ceSkrw 	    &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c))
8438bdf91ceSkrw 		goto play_msf;
8448bdf91ceSkrw 
8458bdf91ceSkrw 	/* tr1 m1:s1.f1		    m2:s2.f2 */
8468bdf91ceSkrw 	tr2 = 0;
8478bdf91ceSkrw 	if (7 == sscanf(arg, "%u%u:%u.%u%u:%u.%u%c",
8488bdf91ceSkrw 	    &tr1, &m1, &s1, &f1, &m2, &s2, &f2, &c))
8498bdf91ceSkrw 		goto play_msf;
8508bdf91ceSkrw 
8518bdf91ceSkrw 	/*     m1:s1   		tr2 m2:s2.f2 */
8528bdf91ceSkrw 	tr1 = f1 = 0;
8538bdf91ceSkrw 	if (6 == sscanf(arg, "%u:%u%u%u:%u.%u%c",
8548bdf91ceSkrw 	    &m1, &s1, &tr2, &m2, &s2, &f2, &c))
8558bdf91ceSkrw 		goto play_msf;
8568bdf91ceSkrw 
8578bdf91ceSkrw 	/*     m1:s1.f1		tr2 m2:s2 */
8588bdf91ceSkrw 	tr1 = f2 = 0;
8598bdf91ceSkrw 	if (6 == sscanf(arg, "%u:%u.%u%u%u:%u%c",
8608bdf91ceSkrw 	    &m1, &s1, &f1, &tr2, &m2, &s2, &c))
8618bdf91ceSkrw 		goto play_msf;
8628bdf91ceSkrw 
8638bdf91ceSkrw 	/*     m1:s1.f1		    m2:s2.f2 */
8648bdf91ceSkrw 	tr1 = tr2 = 0;
8658bdf91ceSkrw 	if (6 == sscanf(arg, "%u:%u.%u%u:%u.%u%c",
8668bdf91ceSkrw 	    &m1, &s1, &f1, &m2, &s2, &f2, &c))
8678bdf91ceSkrw 		goto play_msf;
8688bdf91ceSkrw 
8698bdf91ceSkrw 	/* tr1 m1:s1.f1		    m2:s2 */
8708bdf91ceSkrw 	tr2 = f2 = 0;
8718bdf91ceSkrw 	if (6 == sscanf(arg, "%u%u:%u.%u%u:%u%c",
8728bdf91ceSkrw 	    &tr1, &m1, &s1, &f1, &m2, &s2, &c))
8738bdf91ceSkrw 		goto play_msf;
8748bdf91ceSkrw 
8758bdf91ceSkrw 	/* tr1 m1:s1   		    m2:s2.f2 */
8768bdf91ceSkrw 	tr2 = f1 = 0;
8778bdf91ceSkrw 	if (6 == sscanf(arg, "%u%u:%u%u:%u.%u%c",
8788bdf91ceSkrw 	    &tr1, &m1, &s1, &m2, &s2, &f2, &c))
8798bdf91ceSkrw 		goto play_msf;
8808bdf91ceSkrw 
8818bdf91ceSkrw 	/* tr1 m1:s1   		tr2 m2:s2 */
8828bdf91ceSkrw 	f1 = f2 = 0;
8838bdf91ceSkrw 	if (6 == sscanf(arg, "%u%u:%u%u%u:%u%c",
8848bdf91ceSkrw 	    &tr1, &m1, &s1, &tr2, &m2, &s2, &c))
8858bdf91ceSkrw 		goto play_msf;
8868bdf91ceSkrw 
8878bdf91ceSkrw 	/*     m1:s1   		tr2 m2:s2 */
8888bdf91ceSkrw 	tr1 = f1 = f2 = 0;
8898bdf91ceSkrw 	if (5 == sscanf(arg, "%u:%u%u%u:%u%c", &m1, &s1, &tr2, &m2, &s2, &c))
8908bdf91ceSkrw 		goto play_msf;
8918bdf91ceSkrw 
8928bdf91ceSkrw 	/* tr1 m1:s1  		    m2:s2 */
8938bdf91ceSkrw 	f1 = tr2 = f2 = 0;
8948bdf91ceSkrw 	if (5 == sscanf(arg, "%u%u:%u%u:%u%c", &tr1, &m1, &s1, &m2, &s2, &c))
8958bdf91ceSkrw 		goto play_msf;
8968bdf91ceSkrw 
8978bdf91ceSkrw 	/*     m1:s1       	    m2:s2.f2 */
8988bdf91ceSkrw 	tr1 = f1 = tr2 = 0;
8998bdf91ceSkrw 	if (5 == sscanf(arg, "%u:%u%u:%u.%u%c", &m1, &s1, &m2, &s2, &f2, &c))
9008bdf91ceSkrw 		goto play_msf;
9018bdf91ceSkrw 
9028bdf91ceSkrw 	/*     m1:s1.f1  	    m2:s2 */
9038bdf91ceSkrw 	tr1 = tr2 = f2 = 0;
9048bdf91ceSkrw 	if (5 == sscanf(arg, "%u:%u.%u%u:%u%c", &m1, &s1, &f1, &m2, &s2, &c))
9058bdf91ceSkrw 		goto play_msf;
9068bdf91ceSkrw 
9078bdf91ceSkrw 	/* tr1 m1:s1.f1		tr2 */
9088bdf91ceSkrw 	m2 = s2 = f2 = 0;
9098bdf91ceSkrw 	if (5 == sscanf(arg, "%u%u:%u.%u%u%c", &tr1, &m1, &s1, &f1, &tr2, &c))
9108bdf91ceSkrw 		goto play_msf;
9118bdf91ceSkrw 
9128bdf91ceSkrw 	/*     m1:s1     	    m2:s2 */
9138bdf91ceSkrw 	tr1 = f1 = tr2 = f2 = 0;
9148bdf91ceSkrw 	if (4 == sscanf(arg, "%u:%u%u:%u%c", &m1, &s1, &m2, &s2, &c))
9158bdf91ceSkrw 		goto play_msf;
9168bdf91ceSkrw 
9178bdf91ceSkrw 	/* tr1 m1:s1.f1		<end of disc> */
9188bdf91ceSkrw 	tr2 = m2 = s2 = f2 = 0;
9198bdf91ceSkrw 	if (4 == sscanf(arg, "%u%u:%u.%u%c", &tr1, &m1, &s1, &f1, &c))
9208bdf91ceSkrw 		goto play_msf;
9218bdf91ceSkrw 
9228bdf91ceSkrw 	/* tr1 m1:s1    	tr2 */
9238bdf91ceSkrw 	f1 = m2 = s2 = f2 = 0;
9248bdf91ceSkrw 	if (4 == sscanf(arg, "%u%u:%u%u%c", &tr1, &m1, &s1, &tr2, &c))
9258bdf91ceSkrw 		goto play_msf;
9268bdf91ceSkrw 
9278bdf91ceSkrw 	/*     m1:s1.f1  	tr2 */
9288bdf91ceSkrw 	tr1 = m2 = s2 = f2 = 0;
9298bdf91ceSkrw 	if (4 == sscanf(arg, "%u%u:%u%u%c", &m1, &s1, &f1, &tr2, &c))
9308bdf91ceSkrw 		goto play_msf;
9318bdf91ceSkrw 
9328bdf91ceSkrw 	/*     m1:s1.f1  	<end of disc> */
9338bdf91ceSkrw 	tr1 = tr2 = m2 = s2 = f2 = 0;
9348bdf91ceSkrw 	if (3 == sscanf(arg, "%u:%u.%u%c", &m1, &s1, &f1, &c))
9358bdf91ceSkrw 		goto play_msf;
9368bdf91ceSkrw 
9378bdf91ceSkrw 	/* tr1 m1:s1    	<end of disc> */
9388bdf91ceSkrw 	f1 = tr2 = m2 = s2 = f2 = 0;
9398bdf91ceSkrw 	if (3 == sscanf(arg, "%u%u:%u%c", &tr1, &m1, &s1, &c))
9408bdf91ceSkrw 		goto play_msf;
9418bdf91ceSkrw 
9428bdf91ceSkrw 	/*     m1:s1      	tr2 */
9438bdf91ceSkrw 	tr1 = f1 = m2 = s2 = f2 = 0;
9448bdf91ceSkrw 	if (3 == sscanf(arg, "%u:%u%u%c", &m1, &s1, &tr2, &c))
9458bdf91ceSkrw 		goto play_msf;
9468bdf91ceSkrw 
9478bdf91ceSkrw 	/*     m1:s1      	<end of disc> */
9488bdf91ceSkrw 	tr1 = f1 = tr2 = m2 = s2 = f2 = 0;
9498bdf91ceSkrw 	if (2 == sscanf(arg, "%u:%u%c", &m1, &s1, &c))
9508bdf91ceSkrw 		goto play_msf;
9518bdf91ceSkrw 
9528bdf91ceSkrw 	printf("%s: Invalid command arguments\n", __progname);
9538bdf91ceSkrw 	return (0);
9548bdf91ceSkrw 
9558bdf91ceSkrw play_msf:
9568bdf91ceSkrw 	if (tr1 > n || tr2 > n) {
9578bdf91ceSkrw 		printf("Track number must be between 0 and %u\n", n);
9588bdf91ceSkrw 		return (0);
9598bdf91ceSkrw 	} else if (m1 > 99 || m2 > 99) {
9608bdf91ceSkrw 		printf("Minutes must be between 0 and 99\n");
9618bdf91ceSkrw 		return (0);
9628bdf91ceSkrw 	} else if (s1 > 59 || s2 > 59) {
9638bdf91ceSkrw 		printf("Seconds must be between 0 and 59\n");
9648bdf91ceSkrw 		return (0);
9652ccade87Snaddy 	} else if (f1 > 74 || f2 > 74) {
9668bdf91ceSkrw 		printf("Frames number must be between 0 and 74\n");
9678bdf91ceSkrw 		return (0);
9688bdf91ceSkrw 	}
9698bdf91ceSkrw 
9708bdf91ceSkrw 	if (tr1 > 0) {
9718bdf91ceSkrw 		/*
9728bdf91ceSkrw 		 * Start time is relative to tr1, Add start time of tr1
9738bdf91ceSkrw 		 * to (m1,s1,f1) to yield absolute start time.
9748bdf91ceSkrw 		 */
9757a498d98Skrw 		toc2msf(tr1, &tm, &ts, &tf);
9767a498d98Skrw 		addmsf(&m1, &s1, &f1, tm, ts, tf);
9777a498d98Skrw 
9787a498d98Skrw 		/* Compare (m1,s1,f1) to start time of next track. */
9793438b82bSkrw 		toc2msf(tr1+1, &tm, &ts, &tf);
980b8a783aeSkrw 		if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) {
9818bdf91ceSkrw 			printf("Track %u is not that long.\n", tr1);
9828bdf91ceSkrw 			return (0);
9838bdf91ceSkrw 		}
9848bdf91ceSkrw 	}
9858bdf91ceSkrw 
9868bdf91ceSkrw 	toc2msf(n+1, &tm, &ts, &tf);
9878bdf91ceSkrw 	if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) {
9888bdf91ceSkrw 		printf("Start time is after end of disc.\n");
989c92011ceSdownsj 		return (0);
990c92011ceSdownsj 	}
991c92011ceSdownsj 
9928bdf91ceSkrw 	if (tr2 > 0) {
9937a498d98Skrw 		/*
9948bdf91ceSkrw 		 * End time is relative to tr2, Add start time of tr2
9958bdf91ceSkrw 		 * to (m2,s2,f2) to yield absolute end time.
9967a498d98Skrw 		 */
9977a498d98Skrw 		toc2msf(tr2, &tm, &ts, &tf);
9988dcc8943Skrw 		addmsf(&m2, &s2, &f2, tm, ts, tf);
9998bdf91ceSkrw 
10007a498d98Skrw 		/* Compare (m2,s2,f2) to start time of next track. */
10017a498d98Skrw 		toc2msf(tr2+1, &tm, &ts, &tf);
10027a498d98Skrw 		if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) {
10038bdf91ceSkrw 			printf("Track %u is not that long.\n", tr2);
10047a498d98Skrw 			return (0);
10057a498d98Skrw 		}
10067a498d98Skrw 	}
10077a498d98Skrw 
10083438b82bSkrw 	toc2msf(n+1, &tm, &ts, &tf);
10098bdf91ceSkrw 
10108bdf91ceSkrw 	if (!(tr2 || m2 || s2 || f2)) {
10118bdf91ceSkrw 		/* Play to end of disc. */
1012c92011ceSdownsj 		m2 = tm;
1013c92011ceSdownsj 		s2 = ts;
1014c92011ceSdownsj 		f2 = tf;
10158bdf91ceSkrw 	} else if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) {
10168bdf91ceSkrw 		printf("End time is after end of disc.\n");
10178bdf91ceSkrw 		return (0);
10188bdf91ceSkrw 	}
10198bdf91ceSkrw 
10208bdf91ceSkrw 	if (cmpmsf(m1, s1, f1, m2, s2, f2) == 1) {
10218bdf91ceSkrw 		printf("Start time is after end time.\n");
10228bdf91ceSkrw 		return (0);
1023c92011ceSdownsj 	}
10243438b82bSkrw 
1025c92011ceSdownsj 	return play_msf(m1, s1, f1, m2, s2, f2);
1026c92011ceSdownsj }
1027c92011ceSdownsj 
1028f18d0a50Sderaadt int
play_prev(char * arg)10299ed7639aSespie play_prev(char *arg)
10303301428eSangelos {
10313301428eSangelos 	int trk, min, sec, frm, rc;
10323301428eSangelos 	struct ioc_toc_header h;
10333301428eSangelos 
1034f18d0a50Sderaadt 	if (status(&trk, &min, &sec, &frm) >= 0) {
10353301428eSangelos 		trk--;
10363301428eSangelos 
10373301428eSangelos 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
10383aaa63ebSderaadt 		if (rc == -1) {
1039f87f2499Smillert 			warn("getting toc header");
10403301428eSangelos 			return (rc);
10413301428eSangelos 		}
10423301428eSangelos 
10433301428eSangelos 		if (trk < h.starting_track)
10443301428eSangelos 			return play_track(h.starting_track, 1,
1045ada687dbSangelos 			    h.ending_track + 1, 1);
1046ada687dbSangelos 		return play_track(trk, 1, h.ending_track, 1);
10473301428eSangelos 	}
10483301428eSangelos 
10493301428eSangelos 	return (0);
10503301428eSangelos }
10513301428eSangelos 
1052f18d0a50Sderaadt int
play_same(char * arg)10539ed7639aSespie play_same(char *arg)
10544efcb1a6Sangelos {
10554efcb1a6Sangelos 	int trk, min, sec, frm, rc;
10564efcb1a6Sangelos 	struct ioc_toc_header h;
10574efcb1a6Sangelos 
1058f18d0a50Sderaadt 	if (status (&trk, &min, &sec, &frm) >= 0) {
10594efcb1a6Sangelos 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
10603aaa63ebSderaadt 		if (rc == -1) {
1061f87f2499Smillert 			warn("getting toc header");
10624efcb1a6Sangelos 			return (rc);
10634efcb1a6Sangelos 		}
10644efcb1a6Sangelos 
1065ada687dbSangelos 		return play_track(trk, 1, h.ending_track, 1);
10664efcb1a6Sangelos 	}
10674efcb1a6Sangelos 
10684efcb1a6Sangelos 	return (0);
10694efcb1a6Sangelos }
10704efcb1a6Sangelos 
1071f18d0a50Sderaadt int
play_next(char * arg)10729ed7639aSespie play_next(char *arg)
10733301428eSangelos {
10743301428eSangelos 	int trk, min, sec, frm, rc;
10753301428eSangelos 	struct ioc_toc_header h;
10763301428eSangelos 
1077f18d0a50Sderaadt 	if (status(&trk, &min, &sec, &frm) >= 0) {
10783301428eSangelos 		trk++;
10793301428eSangelos 		rc = ioctl(fd, CDIOREADTOCHEADER, &h);
10803aaa63ebSderaadt 		if (rc == -1) {
1081f87f2499Smillert 			warn("getting toc header");
10823301428eSangelos 			return (rc);
10833301428eSangelos 		}
10843301428eSangelos 
1085f18d0a50Sderaadt 		if (trk > h.ending_track) {
10863301428eSangelos 			printf("%s: end of CD\n", __progname);
10873301428eSangelos 
10883301428eSangelos 			rc = ioctl(fd, CDIOCSTOP);
10893301428eSangelos 
10903301428eSangelos 			(void) ioctl(fd, CDIOCALLOW);
10913301428eSangelos 
10923301428eSangelos 			return (rc);
10933301428eSangelos 		}
10943301428eSangelos 
1095ada687dbSangelos 		return play_track(trk, 1, h.ending_track, 1);
10963301428eSangelos 	}
10973301428eSangelos 
10983301428eSangelos 	return (0);
10993301428eSangelos }
11003301428eSangelos 
1101f18d0a50Sderaadt char *
strstatus(int sts)11029ed7639aSespie strstatus(int sts)
1103c92011ceSdownsj {
1104c92011ceSdownsj 	switch (sts) {
1105f18d0a50Sderaadt 	case ASTS_INVALID:
1106f18d0a50Sderaadt 		return ("invalid");
1107f18d0a50Sderaadt 	case ASTS_PLAYING:
1108f18d0a50Sderaadt 		return ("playing");
1109f18d0a50Sderaadt 	case ASTS_PAUSED:
1110f18d0a50Sderaadt 		return ("paused");
1111f18d0a50Sderaadt 	case ASTS_COMPLETED:
1112f18d0a50Sderaadt 		return ("completed");
1113f18d0a50Sderaadt 	case ASTS_ERROR:
1114f18d0a50Sderaadt 		return ("error");
1115f18d0a50Sderaadt 	case ASTS_VOID:
1116f18d0a50Sderaadt 		return ("void");
1117f18d0a50Sderaadt 	default:
1118f18d0a50Sderaadt 		return ("??");
1119c92011ceSdownsj 	}
1120c92011ceSdownsj }
1121c92011ceSdownsj 
1122f18d0a50Sderaadt int
pstatus(char * arg)11239ed7639aSespie pstatus(char *arg)
1124c92011ceSdownsj {
1125c92011ceSdownsj 	struct ioc_vol v;
1126c92011ceSdownsj 	struct ioc_read_subchannel ss;
1127c92011ceSdownsj 	struct cd_sub_channel_info data;
1128c92011ceSdownsj 	int rc, trk, m, s, f;
1129dfcdb41bSespie 	char vis_catalog[1 + 4 * 15];
1130c92011ceSdownsj 
1131c92011ceSdownsj 	rc = status(&trk, &m, &s, &f);
1132f87f2499Smillert 	if (rc >= 0) {
11339ed7639aSespie 		if (verbose) {
11349ed7639aSespie 			if (track_names)
11359ed7639aSespie 				printf("Audio status = %d<%s>, "
11369ed7639aSespie 				    "current track = %d (%s)\n"
11379ed7639aSespie 				    "\tcurrent position = %d:%02d.%02d\n",
11389ed7639aSespie 				    rc, strstatus(rc), trk,
11399ed7639aSespie 				    trk ? track_names[trk-1] : "", m, s, f);
1140c92011ceSdownsj 			else
11419ed7639aSespie 				printf("Audio status = %d<%s>, "
11429ed7639aSespie 				    "current track = %d, "
11439ed7639aSespie 				    "current position = %d:%02d.%02d\n",
11449ed7639aSespie 				    rc, strstatus(rc), trk, m, s, f);
11459ed7639aSespie 		} else
1146c92011ceSdownsj 			printf("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
1147f87f2499Smillert 	} else
1148c92011ceSdownsj 		printf("No current status info available\n");
1149c92011ceSdownsj 
1150c92011ceSdownsj 	bzero(&ss, sizeof (ss));
1151c92011ceSdownsj 	ss.data = &data;
1152c92011ceSdownsj 	ss.data_len = sizeof (data);
1153c92011ceSdownsj 	ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1154c92011ceSdownsj 	ss.data_format = CD_MEDIA_CATALOG;
1155c92011ceSdownsj 	rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss);
1156c92011ceSdownsj 	if (rc >= 0) {
1157c92011ceSdownsj 		printf("Media catalog is %sactive",
1158c92011ceSdownsj 		ss.data->what.media_catalog.mc_valid ? "": "in");
1159c92011ceSdownsj 		if (ss.data->what.media_catalog.mc_valid &&
1160dfcdb41bSespie 		    ss.data->what.media_catalog.mc_number[0]) {
1161dfcdb41bSespie 			strvisx(vis_catalog,
1162c2a796bdSderaadt 			    (char *)ss.data->what.media_catalog.mc_number,
1163dfcdb41bSespie 			    15, VIS_SAFE);
1164dfcdb41bSespie 			printf(", number \"%.15s\"", vis_catalog);
1165dfcdb41bSespie 		}
1166c92011ceSdownsj 		putchar('\n');
1167c92011ceSdownsj 	} else
1168c92011ceSdownsj 		printf("No media catalog info available\n");
1169c92011ceSdownsj 
1170c92011ceSdownsj 	rc = ioctl(fd, CDIOCGETVOL, &v);
1171f87f2499Smillert 	if (rc >= 0) {
1172c92011ceSdownsj 		if (verbose)
1173c92011ceSdownsj 			printf("Left volume = %d, right volume = %d\n",
1174c92011ceSdownsj 			    v.vol[0], v.vol[1]);
1175c92011ceSdownsj 		else
1176c92011ceSdownsj 			printf("%d %d\n", v.vol[0], v.vol[1]);
1177f87f2499Smillert 	} else
1178c92011ceSdownsj 		printf("No volume level info available\n");
1179c92011ceSdownsj 	return(0);
1180c92011ceSdownsj }
1181c92011ceSdownsj 
1182f18d0a50Sderaadt int
cdid(void)11831837a5caSderaadt cdid(void)
1184ae359c1cSespie {
1185ae359c1cSespie 	unsigned long id;
1186ae359c1cSespie 	struct ioc_toc_header h;
1187ae359c1cSespie 	int rc, n;
1188ae359c1cSespie 
1189ae359c1cSespie 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1190ae359c1cSespie 	if (rc == -1) {
1191ae359c1cSespie 		warn("getting toc header");
1192ae359c1cSespie 		return (rc);
1193ae359c1cSespie 	}
1194ae359c1cSespie 
1195ae359c1cSespie 	n = h.ending_track - h.starting_track + 1;
1196ae359c1cSespie 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1197ae359c1cSespie 	if (rc < 0)
1198ae359c1cSespie 		return (rc);
1199ae359c1cSespie 
1200ae359c1cSespie 	id = cddb_discid(n, toc_buffer);
1201ae359c1cSespie 	if (id) {
1202ae359c1cSespie 		if (verbose)
1203ae359c1cSespie 			printf("CDID=");
1204ae359c1cSespie 		printf("%08lx\n", id);
1205ae359c1cSespie 	}
1206ae359c1cSespie 	return id ? 0 : 1;
1207ae359c1cSespie }
1208ae359c1cSespie 
1209ae359c1cSespie int
info(char * arg)12109ed7639aSespie info(char *arg)
1211c92011ceSdownsj {
1212c92011ceSdownsj 	struct ioc_toc_header h;
1213c92011ceSdownsj 	int rc, i, n;
1214c92011ceSdownsj 
1215dc4cd921Skrw 	if (get_media_capabilities(mediacap, 1) == -1)
1216dc4cd921Skrw 		errx(1, "Can't determine media type");
1217dc4cd921Skrw 
1218c92011ceSdownsj 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1219c92011ceSdownsj 	if (rc >= 0) {
1220c92011ceSdownsj 		if (verbose)
1221c92011ceSdownsj 			printf("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
1222c92011ceSdownsj 			    h.starting_track, h.ending_track, h.len);
1223c92011ceSdownsj 		else
1224c92011ceSdownsj 			printf("%d %d %d\n", h.starting_track,
1225c92011ceSdownsj 			    h.ending_track, h.len);
1226c92011ceSdownsj 	} else {
1227f87f2499Smillert 		warn("getting toc header");
1228c92011ceSdownsj 		return (rc);
1229c92011ceSdownsj 	}
1230c92011ceSdownsj 
1231c92011ceSdownsj 	n = h.ending_track - h.starting_track + 1;
1232c92011ceSdownsj 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
1233c92011ceSdownsj 	if (rc < 0)
1234c92011ceSdownsj 		return (rc);
1235c92011ceSdownsj 
1236c92011ceSdownsj 	if (verbose) {
1237c92011ceSdownsj 		printf("track     start  duration   block  length   type\n");
1238c92011ceSdownsj 		printf("-------------------------------------------------\n");
1239c92011ceSdownsj 	}
1240c92011ceSdownsj 
1241c92011ceSdownsj 	for (i = 0; i < n; i++) {
1242c92011ceSdownsj 		printf("%5d  ", toc_buffer[i].track);
12439ed7639aSespie 		prtrack(toc_buffer + i, 0, NULL);
1244c92011ceSdownsj 	}
1245c92011ceSdownsj 	printf("%5d  ", toc_buffer[n].track);
12469ed7639aSespie 	prtrack(toc_buffer + n, 1, NULL);
12479ed7639aSespie 	return (0);
12489ed7639aSespie }
12499ed7639aSespie 
12509ed7639aSespie int
cddbinfo(char * arg)12519ed7639aSespie cddbinfo(char *arg)
12529ed7639aSespie {
12539ed7639aSespie 	struct ioc_toc_header h;
12549ed7639aSespie 	int rc, i, n;
12559ed7639aSespie 
12569ed7639aSespie 	rc = ioctl(fd, CDIOREADTOCHEADER, &h);
12579ed7639aSespie 	if (rc == -1) {
12589ed7639aSespie 		warn("getting toc header");
12599ed7639aSespie 		return (rc);
12609ed7639aSespie 	}
12619ed7639aSespie 
12629ed7639aSespie 	n = h.ending_track - h.starting_track + 1;
12639ed7639aSespie 	rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry));
12649ed7639aSespie 	if (rc < 0)
12659ed7639aSespie 		return (rc);
12669ed7639aSespie 
12679ed7639aSespie 	if (track_names)
12689ed7639aSespie 		free_names(track_names);
12699ed7639aSespie 	track_names = NULL;
12709ed7639aSespie 
12719ed7639aSespie 	track_names = cddb(cddb_host, n, toc_buffer, arg);
12729ed7639aSespie 	if (!track_names)
12739ed7639aSespie 		return(0);
12749ed7639aSespie 
12759ed7639aSespie 	printf("-------------------------------------------------\n");
12769ed7639aSespie 
12779ed7639aSespie 	for (i = 0; i < n; i++) {
12789ed7639aSespie 		printf("%5d  ", toc_buffer[i].track);
12799ed7639aSespie 		prtrack(toc_buffer + i, 0, track_names[i]);
12809ed7639aSespie 	}
12819ed7639aSespie 	printf("%5d  ", toc_buffer[n].track);
12829ed7639aSespie 	prtrack(toc_buffer + n, 1, "");
1283c92011ceSdownsj 	return (0);
1284c92011ceSdownsj }
1285c92011ceSdownsj 
1286f18d0a50Sderaadt void
lba2msf(unsigned long lba,u_char * m,u_char * s,u_char * f)12879ed7639aSespie lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
1288c92011ceSdownsj {
1289c92011ceSdownsj 	lba += 150;		/* block start offset */
1290c92011ceSdownsj 	lba &= 0xffffff;	/* negative lbas use only 24 bits */
1291c92011ceSdownsj 	*m = lba / (60 * 75);
1292c92011ceSdownsj 	lba %= (60 * 75);
1293c92011ceSdownsj 	*s = lba / 75;
1294c92011ceSdownsj 	*f = lba % 75;
1295c92011ceSdownsj }
1296c92011ceSdownsj 
1297f18d0a50Sderaadt unsigned int
msf2lba(u_char m,u_char s,u_char f)1298da50f41aSespie msf2lba(u_char m, u_char s, u_char f)
1299c92011ceSdownsj {
1300c92011ceSdownsj 	return (((m * 60) + s) * 75 + f) - 150;
1301c92011ceSdownsj }
1302c92011ceSdownsj 
13039ed7639aSespie unsigned long
entry2time(struct cd_toc_entry * e)13049ed7639aSespie entry2time(struct cd_toc_entry *e)
13059ed7639aSespie {
13069ed7639aSespie 	int block;
13079ed7639aSespie 	u_char m, s, f;
13089ed7639aSespie 
13099ed7639aSespie 	if (msf) {
13109ed7639aSespie 		return (e->addr.msf.minute * 60 + e->addr.msf.second);
13119ed7639aSespie 	} else {
131243844f6cSkrw 		block = e->addr.lba;
13139ed7639aSespie 		lba2msf(block, &m, &s, &f);
13149ed7639aSespie 		return (m*60+s);
13159ed7639aSespie 	}
13169ed7639aSespie }
13179ed7639aSespie 
13189ed7639aSespie unsigned long
entry2frames(struct cd_toc_entry * e)13199ed7639aSespie entry2frames(struct cd_toc_entry *e)
13209ed7639aSespie {
13219ed7639aSespie 	int block;
13229ed7639aSespie 	unsigned char m, s, f;
13239ed7639aSespie 
13249ed7639aSespie 	if (msf) {
13259ed7639aSespie 		return e->addr.msf.frame + e->addr.msf.second * 75 +
13269ed7639aSespie 		    e->addr.msf.minute * 60 * 75;
13279ed7639aSespie 	} else {
132843844f6cSkrw 		block = e->addr.lba;
13299ed7639aSespie 		lba2msf(block, &m, &s, &f);
13309ed7639aSespie 		return f + s * 75 + m * 60 * 75;
13319ed7639aSespie 	}
13329ed7639aSespie }
13339ed7639aSespie 
1334f18d0a50Sderaadt void
prtrack(struct cd_toc_entry * e,int lastflag,char * name)13359ed7639aSespie prtrack(struct cd_toc_entry *e, int lastflag, char *name)
1336c92011ceSdownsj {
1337c92011ceSdownsj 	int block, next, len;
1338c92011ceSdownsj 	u_char m, s, f;
1339c92011ceSdownsj 
1340c92011ceSdownsj 	if (msf) {
13419ed7639aSespie 		if (!name || lastflag)
1342c92011ceSdownsj 			/* Print track start */
1343c92011ceSdownsj 			printf("%2d:%02d.%02d  ", e->addr.msf.minute,
1344c92011ceSdownsj 			    e->addr.msf.second, e->addr.msf.frame);
1345c92011ceSdownsj 
1346c92011ceSdownsj 		block = msf2lba(e->addr.msf.minute, e->addr.msf.second,
1347c92011ceSdownsj 			e->addr.msf.frame);
1348c92011ceSdownsj 	} else {
134943844f6cSkrw 		block = e->addr.lba;
13509ed7639aSespie 		if (!name || lastflag) {
1351c92011ceSdownsj 			lba2msf(block, &m, &s, &f);
1352c92011ceSdownsj 			/* Print track start */
1353c92011ceSdownsj 			printf("%2d:%02d.%02d  ", m, s, f);
1354c92011ceSdownsj 		}
13559ed7639aSespie 	}
1356c92011ceSdownsj 	if (lastflag) {
13579ed7639aSespie 		if (!name)
1358c92011ceSdownsj 			/* Last track -- print block */
1359c92011ceSdownsj 			printf("       -  %6d       -      -\n", block);
13609ed7639aSespie 		else
13619ed7639aSespie 			printf("\n");
1362c92011ceSdownsj 		return;
1363c92011ceSdownsj 	}
1364c92011ceSdownsj 
1365c92011ceSdownsj 	if (msf)
1366c92011ceSdownsj 		next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second,
1367c92011ceSdownsj 			e[1].addr.msf.frame);
1368c92011ceSdownsj 	else
136943844f6cSkrw 		next = e[1].addr.lba;
1370c92011ceSdownsj 	len = next - block;
1371fbd1c299Sfgsch 	lba2msf(len - 150, &m, &s, &f);
1372c92011ceSdownsj 
13739ed7639aSespie 	if (name)
13749ed7639aSespie 		printf("%2d:%02d.%02d  %s\n", m, s, f, name);
1375c92011ceSdownsj 	/* Print duration, block, length, type */
13769ed7639aSespie 	else
1377c92011ceSdownsj 		printf("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
1378c92011ceSdownsj 		    (e->control & 4) ? "data" : "audio");
1379c92011ceSdownsj }
1380c92011ceSdownsj 
1381f18d0a50Sderaadt int
play_track(int tstart,int istart,int tend,int iend)13829ed7639aSespie play_track(int tstart, int istart, int tend, int iend)
1383c92011ceSdownsj {
1384c92011ceSdownsj 	struct ioc_play_track t;
1385c92011ceSdownsj 
1386c92011ceSdownsj 	t.start_track = tstart;
1387c92011ceSdownsj 	t.start_index = istart;
1388c92011ceSdownsj 	t.end_track = tend;
1389c92011ceSdownsj 	t.end_index = iend;
1390c92011ceSdownsj 
1391c92011ceSdownsj 	return ioctl(fd, CDIOCPLAYTRACKS, &t);
1392c92011ceSdownsj }
1393c92011ceSdownsj 
1394f18d0a50Sderaadt int
play_blocks(int blk,int len)13959ed7639aSespie play_blocks(int blk, int len)
1396c92011ceSdownsj {
1397c92011ceSdownsj 	struct ioc_play_blocks  t;
1398c92011ceSdownsj 
1399c92011ceSdownsj 	t.blk = blk;
1400c92011ceSdownsj 	t.len = len;
1401c92011ceSdownsj 
1402c92011ceSdownsj 	return ioctl(fd, CDIOCPLAYBLOCKS, &t);
1403c92011ceSdownsj }
1404c92011ceSdownsj 
1405f18d0a50Sderaadt int
setvol(int left,int right)14069ed7639aSespie setvol(int left, int right)
1407c92011ceSdownsj {
1408c92011ceSdownsj 	struct ioc_vol  v;
1409c92011ceSdownsj 
1410c92011ceSdownsj 	v.vol[0] = left;
1411c92011ceSdownsj 	v.vol[1] = right;
1412c92011ceSdownsj 	v.vol[2] = 0;
1413c92011ceSdownsj 	v.vol[3] = 0;
1414c92011ceSdownsj 
1415c92011ceSdownsj 	return ioctl(fd, CDIOCSETVOL, &v);
1416c92011ceSdownsj }
1417c92011ceSdownsj 
1418f18d0a50Sderaadt int
read_toc_entrys(int len)14199ed7639aSespie read_toc_entrys(int len)
1420c92011ceSdownsj {
1421c92011ceSdownsj 	struct ioc_read_toc_entry t;
1422c92011ceSdownsj 
14233a87bfaeScsapuntz 	if (toc_buffer) {
14243a87bfaeScsapuntz 		free(toc_buffer);
14253a87bfaeScsapuntz 		toc_buffer = 0;
14263a87bfaeScsapuntz 	}
14273a87bfaeScsapuntz 
14283a87bfaeScsapuntz 	toc_buffer = malloc(len);
14293a87bfaeScsapuntz 
14303a87bfaeScsapuntz 	if (!toc_buffer) {
14313a87bfaeScsapuntz 		errno = ENOMEM;
14323a87bfaeScsapuntz 		return (-1);
14333a87bfaeScsapuntz 	}
14343a87bfaeScsapuntz 
1435c92011ceSdownsj 	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1436c92011ceSdownsj 	t.starting_track = 0;
1437c92011ceSdownsj 	t.data_len = len;
1438c92011ceSdownsj 	t.data = toc_buffer;
1439c92011ceSdownsj 
1440c92011ceSdownsj 	return (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t));
1441c92011ceSdownsj }
1442c92011ceSdownsj 
1443f18d0a50Sderaadt int
play_msf(int start_m,int start_s,int start_f,int end_m,int end_s,int end_f)14449ed7639aSespie play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, int end_f)
1445c92011ceSdownsj {
1446c92011ceSdownsj 	struct ioc_play_msf a;
1447c92011ceSdownsj 
1448c92011ceSdownsj 	a.start_m = start_m;
1449c92011ceSdownsj 	a.start_s = start_s;
1450c92011ceSdownsj 	a.start_f = start_f;
1451c92011ceSdownsj 	a.end_m = end_m;
1452c92011ceSdownsj 	a.end_s = end_s;
1453c92011ceSdownsj 	a.end_f = end_f;
1454c92011ceSdownsj 
1455c92011ceSdownsj 	return ioctl(fd, CDIOCPLAYMSF, (char *) &a);
1456c92011ceSdownsj }
1457c92011ceSdownsj 
145867831131Sespie int
status(int * trk,int * min,int * sec,int * frame)14599ed7639aSespie status(int *trk, int *min, int *sec, int *frame)
1460c92011ceSdownsj {
1461c92011ceSdownsj 	struct ioc_read_subchannel s;
1462c92011ceSdownsj 	struct cd_sub_channel_info data;
1463c92011ceSdownsj 	u_char mm, ss, ff;
1464c92011ceSdownsj 
1465c92011ceSdownsj 	bzero(&s, sizeof (s));
1466c92011ceSdownsj 	s.data = &data;
1467c92011ceSdownsj 	s.data_len = sizeof (data);
1468c92011ceSdownsj 	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1469c92011ceSdownsj 	s.data_format = CD_CURRENT_POSITION;
1470c92011ceSdownsj 
14713aaa63ebSderaadt 	if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) == -1)
1472c92011ceSdownsj 		return -1;
1473c92011ceSdownsj 
1474c92011ceSdownsj 	*trk = s.data->what.position.track_number;
1475c92011ceSdownsj 	if (msf) {
1476c92011ceSdownsj 		*min = s.data->what.position.reladdr.msf.minute;
1477c92011ceSdownsj 		*sec = s.data->what.position.reladdr.msf.second;
1478c92011ceSdownsj 		*frame = s.data->what.position.reladdr.msf.frame;
1479c92011ceSdownsj 	} else {
148043844f6cSkrw 		/*
148143844f6cSkrw 		 * NOTE: CDIOCREADSUBCHANNEL does not put the lba info into
148243844f6cSkrw 		 * host order like CDIOREADTOCENTRYS does.
148343844f6cSkrw 		 */
148443844f6cSkrw 		lba2msf(betoh32(s.data->what.position.reladdr.lba), &mm, &ss,
148543844f6cSkrw 		    &ff);
1486c92011ceSdownsj 		*min = mm;
1487c92011ceSdownsj 		*sec = ss;
1488c92011ceSdownsj 		*frame = ff;
1489c92011ceSdownsj 	}
1490c92011ceSdownsj 
1491c92011ceSdownsj 	return s.data->header.audio_status;
1492c92011ceSdownsj }
1493c92011ceSdownsj 
1494a93de8edSschwarze void
sigint_handler(int signo_arg)1495a93de8edSschwarze sigint_handler(int signo_arg)
1496a93de8edSschwarze {
1497a93de8edSschwarze 	signo = signo_arg;
1498a93de8edSschwarze }
1499a93de8edSschwarze 
1500f18d0a50Sderaadt char *
input(int * cmd)15019ed7639aSespie input(int *cmd)
1502c92011ceSdownsj {
1503a93de8edSschwarze 	struct sigaction sa;
1504ec6762c7Sfgsch 	char *buf;
1505ec6762c7Sfgsch 	int siz = 0;
1506c92011ceSdownsj 	char *p;
15078b59c5f0Sotto 	HistEvent hev;
1508c92011ceSdownsj 
1509a93de8edSschwarze 	memset(&sa, 0, sizeof(sa));
1510c92011ceSdownsj 	do {
1511a93de8edSschwarze 		signo = 0;
1512a93de8edSschwarze 		sa.sa_handler = sigint_handler;
1513a93de8edSschwarze 		if (sigaction(SIGINT, &sa, NULL) == -1)
1514a93de8edSschwarze 			err(1, "sigaction");
1515a93de8edSschwarze 		buf = (char *)el_gets(el, &siz);
1516a93de8edSschwarze 		sa.sa_handler = SIG_DFL;
1517a93de8edSschwarze 		if (sigaction(SIGINT, &sa, NULL) == -1)
1518a93de8edSschwarze 			err(1, "sigaction");
1519a93de8edSschwarze 		if (buf == NULL || siz <= 0) {
1520c92011ceSdownsj 			fprintf(stderr, "\r\n");
1521a93de8edSschwarze 			if (siz < 0 && errno == EINTR && signo == SIGINT)
1522a93de8edSschwarze 				continue;
1523a93de8edSschwarze 			*cmd = CMD_QUIT;
1524c92011ceSdownsj 			return (0);
1525c92011ceSdownsj 		}
1526ec6762c7Sfgsch 		if (strlen(buf) > 1)
15278b59c5f0Sotto 			history(hist, &hev, H_ENTER, buf);
1528c92011ceSdownsj 		p = parse(buf, cmd);
1529c92011ceSdownsj 	} while (!p);
1530c92011ceSdownsj 	return (p);
1531c92011ceSdownsj }
1532c92011ceSdownsj 
1533f18d0a50Sderaadt char *
parse(char * buf,int * cmd)15349ed7639aSespie parse(char *buf, int *cmd)
1535c92011ceSdownsj {
1536c92011ceSdownsj 	struct cmdtab *c;
1537c92011ceSdownsj 	char *p;
1538fa3e22b8Sav 	size_t len;
1539c92011ceSdownsj 
154020ae3208Sderaadt 	for (p=buf; isspace((unsigned char)*p); p++)
1541c92011ceSdownsj 		continue;
1542c92011ceSdownsj 
154320ae3208Sderaadt 	if (isdigit((unsigned char)*p) ||
154420ae3208Sderaadt 	    (p[0] == '#' && isdigit((unsigned char)p[1]))) {
1545c92011ceSdownsj 		*cmd = CMD_PLAY;
1546c92011ceSdownsj 		return (p);
1547c92011ceSdownsj 	}
1548c92011ceSdownsj 
154920ae3208Sderaadt 	for (buf = p; *p && ! isspace((unsigned char)*p); p++)
1550c92011ceSdownsj 		continue;
1551c92011ceSdownsj 
1552c92011ceSdownsj 	len = p - buf;
1553c92011ceSdownsj 	if (!len)
1554c92011ceSdownsj 		return (0);
1555c92011ceSdownsj 
1556c92011ceSdownsj 	if (*p) {		/* It must be a spacing character! */
1557c92011ceSdownsj 		char *q;
1558c92011ceSdownsj 
1559c92011ceSdownsj 		*p++ = 0;
1560c92011ceSdownsj 		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1561c92011ceSdownsj 			continue;
1562c92011ceSdownsj 		*q = 0;
1563c92011ceSdownsj 	}
1564c92011ceSdownsj 
1565c92011ceSdownsj 	*cmd = -1;
1566c92011ceSdownsj 	for (c=cmdtab; c->name; ++c) {
1567c92011ceSdownsj 		/* Is it an exact match? */
1568c92011ceSdownsj 		if (!strcasecmp(buf, c->name)) {
1569c92011ceSdownsj 			*cmd = c->command;
1570c92011ceSdownsj 			break;
1571c92011ceSdownsj 		}
1572c92011ceSdownsj 
1573c92011ceSdownsj 		/* Try short hand forms then... */
1574c92011ceSdownsj 		if (len >= c->min && ! strncasecmp(buf, c->name, len)) {
1575c92011ceSdownsj 			if (*cmd != -1 && *cmd != c->command) {
1576c92011ceSdownsj 				fprintf(stderr, "Ambiguous command\n");
1577c92011ceSdownsj 				return (0);
1578c92011ceSdownsj 			}
1579c92011ceSdownsj 			*cmd = c->command;
1580c92011ceSdownsj 		}
1581c92011ceSdownsj 	}
1582c92011ceSdownsj 
1583c92011ceSdownsj 	if (*cmd == -1) {
1584c92011ceSdownsj 		fprintf(stderr, "%s: Invalid command, enter ``help'' for commands.\n",
1585c92011ceSdownsj 		    __progname);
1586c92011ceSdownsj 		return (0);
1587c92011ceSdownsj 	}
1588c92011ceSdownsj 
158920ae3208Sderaadt 	while (isspace((unsigned char)*p))
1590c92011ceSdownsj 		p++;
1591c92011ceSdownsj 	return p;
1592c92011ceSdownsj }
1593c92011ceSdownsj 
1594f18d0a50Sderaadt int
open_cd(char * dev,int needwrite)15957993cdedSmjc open_cd(char *dev, int needwrite)
1596f87f2499Smillert {
1597f87f2499Smillert 	char *realdev;
159895cc45e3Smillert 	int tries;
1599c92011ceSdownsj 
1600c7ed6047Sav 	if (fd > -1) {
1601c7ed6047Sav 		if (needwrite && !writeperm) {
1602c7ed6047Sav 			close(fd);
1603c7ed6047Sav 			fd = -1;
1604c7ed6047Sav 		} else
1605c92011ceSdownsj 			return (1);
1606c7ed6047Sav 	}
1607c92011ceSdownsj 
160895cc45e3Smillert 	for (tries = 0; fd < 0 && tries < 10; tries++) {
16097993cdedSmjc 		if (needwrite)
16107993cdedSmjc 			fd = opendev(dev, O_RDWR, OPENDEV_PART, &realdev);
16117993cdedSmjc 		else
1612f87f2499Smillert 			fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev);
16133aaa63ebSderaadt 		if (fd == -1) {
161495cc45e3Smillert 			if (errno == ENXIO) {
1615c92011ceSdownsj 				/*  ENXIO has an overloaded meaning here.
1616c92011ceSdownsj 				 *  The original "Device not configured" should
1617c92011ceSdownsj 				 *  be interpreted as "No disc in drive %s". */
1618f87f2499Smillert 				warnx("No disc in drive %s.", realdev);
1619c92011ceSdownsj 				return (0);
162095cc45e3Smillert 			} else if (errno != EIO) {
162195cc45e3Smillert 				/*  EIO may simply mean the device is not ready
162295cc45e3Smillert 				 *  yet which is common with CD changers. */
162395cc45e3Smillert 				warn("Can't open %s", realdev);
162495cc45e3Smillert 				return (0);
1625c92011ceSdownsj 			}
162695cc45e3Smillert 		}
162795cc45e3Smillert 		sleep(1);
162895cc45e3Smillert 	}
16293aaa63ebSderaadt 	if (fd == -1) {
1630f87f2499Smillert 		warn("Can't open %s", realdev);
1631f87f2499Smillert 		return (0);
1632c92011ceSdownsj 	}
1633c7ed6047Sav 	writeperm = needwrite;
1634c92011ceSdownsj 	return (1);
1635c92011ceSdownsj }
1636ec6762c7Sfgsch 
1637ec6762c7Sfgsch char *
prompt(void)1638ec6762c7Sfgsch prompt(void)
1639ec6762c7Sfgsch {
1640ec6762c7Sfgsch 	return (verbose ? "cdio> " : "");
1641ec6762c7Sfgsch }
1642ec6762c7Sfgsch 
1643ec6762c7Sfgsch void
switch_el(void)1644ec6762c7Sfgsch switch_el(void)
1645ec6762c7Sfgsch {
16468b59c5f0Sotto 	HistEvent hev;
16478b59c5f0Sotto 
1648ec6762c7Sfgsch 	if (el == NULL && hist == NULL) {
16498b59c5f0Sotto 		el = el_init(__progname, stdin, stdout, stderr);
1650ec6762c7Sfgsch 		hist = history_init();
16518b59c5f0Sotto 		history(hist, &hev, H_SETSIZE, 100);
1652ec6762c7Sfgsch 		el_set(el, EL_HIST, history, hist);
1653ec6762c7Sfgsch 		el_set(el, EL_EDITOR, "emacs");
1654ec6762c7Sfgsch 		el_set(el, EL_PROMPT, prompt);
1655ec6762c7Sfgsch 		el_set(el, EL_SIGNAL, 1);
1656ec6762c7Sfgsch 		el_source(el, NULL);
1657ec6762c7Sfgsch 
1658ec6762c7Sfgsch 	} else {
1659ec6762c7Sfgsch 		if (hist != NULL) {
1660ec6762c7Sfgsch 			history_end(hist);
1661ec6762c7Sfgsch 			hist = NULL;
1662ec6762c7Sfgsch 		}
1663ec6762c7Sfgsch 		if (el != NULL) {
1664ec6762c7Sfgsch 			el_end(el);
1665ec6762c7Sfgsch 			el = NULL;
1666ec6762c7Sfgsch 		}
1667ec6762c7Sfgsch 	}
1668ec6762c7Sfgsch }
16698dcc8943Skrw 
16708dcc8943Skrw void
addmsf(u_int * m,u_int * s,u_int * f,u_char m_inc,u_char s_inc,u_char f_inc)16718dcc8943Skrw addmsf(u_int *m, u_int *s, u_int *f, u_char m_inc, u_char s_inc, u_char f_inc)
16728dcc8943Skrw {
16738dcc8943Skrw 	*f += f_inc;
16748dcc8943Skrw 	if (*f > 75) {
16758dcc8943Skrw 		*s += *f / 75;
16768dcc8943Skrw 		*f %= 75;
16778dcc8943Skrw 	}
16788dcc8943Skrw 
16798dcc8943Skrw 	*s += s_inc;
16808dcc8943Skrw 	if (*s > 60) {
16818dcc8943Skrw 		*m += *s / 60;
16828dcc8943Skrw 		*s %= 60;
16838dcc8943Skrw 	}
16848dcc8943Skrw 
16858dcc8943Skrw 	*m += m_inc;
16868dcc8943Skrw }
16873438b82bSkrw 
1688b8a783aeSkrw int
cmpmsf(u_char m1,u_char s1,u_char f1,u_char m2,u_char s2,u_char f2)1689b8a783aeSkrw cmpmsf(u_char m1, u_char s1, u_char f1, u_char m2, u_char s2, u_char f2)
1690b8a783aeSkrw {
1691b8a783aeSkrw 	if (m1 > m2)
1692b8a783aeSkrw 		return (1);
1693b8a783aeSkrw 	else if (m1 < m2)
1694b8a783aeSkrw 		return (-1);
1695b8a783aeSkrw 
1696b8a783aeSkrw 	if (s1 > s2)
1697b8a783aeSkrw 		return (1);
1698b8a783aeSkrw 	else if (s1 < s2)
1699b8a783aeSkrw 		return (-1);
1700b8a783aeSkrw 
1701b8a783aeSkrw 	if  (f1 > f2)
1702b8a783aeSkrw 		return (1);
1703b8a783aeSkrw 	else if (f1 < f2)
1704b8a783aeSkrw 		return (-1);
1705b8a783aeSkrw 
1706b8a783aeSkrw 	return (0);
1707b8a783aeSkrw }
1708b8a783aeSkrw 
17093438b82bSkrw void
toc2msf(u_int track,u_char * m,u_char * s,u_char * f)17103438b82bSkrw toc2msf(u_int track, u_char *m, u_char *s, u_char *f)
17113438b82bSkrw {
17123438b82bSkrw 	struct cd_toc_entry *ctep;
17133438b82bSkrw 
17143438b82bSkrw 	ctep = &toc_buffer[track - 1];
17153438b82bSkrw 
17163438b82bSkrw 	if (msf) {
17173438b82bSkrw 		*m = ctep->addr.msf.minute;
17183438b82bSkrw 		*s = ctep->addr.msf.second;
17193438b82bSkrw 		*f = ctep->addr.msf.frame;
17203438b82bSkrw 	} else
17213438b82bSkrw 		lba2msf(ctep->addr.lba, m, s, f);
17223438b82bSkrw }
1723