110099Sjkh/*
213884Sache * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
310099Sjkh * Based on the non-X based CD player by Jean-Marc Zucconi and
413832Sache * Andrey A. Chernov.
513884Sache *
613985Sache * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
713985Sache *
813985Sache * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
913985Sache *              A couple of further fixes to my own earlier "fixes".
1013985Sache *
1113985Sache * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
1213985Sache *              Added an ability to specify addresses relative to the
1313985Sache *              beginning of a track. This is in fact a variation of
1413985Sache *              doing the simple play_msf() call.
1513985Sache *
1613985Sache * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
1713985Sache *              New eject algorithm.
1813985Sache *              Some code style reformatting.
19180507Sgahr *
20180507Sgahr * 13-Dec-1999: Knut A. Syed <kas@kas.no>
21180507Sgahr * 		Volume-command modified.  If used with only one
22180507Sgahr * 		parameter it now sets both channels.  If used without
23180507Sgahr * 		parameters it will print volume-info.
24180507Sgahr * 		Version 2.0.1
25180507Sgahr *
26180507Sgahr * 27-Jun-2008  Pietro Cerutti <gahr@FreeBSD.org>
27180507Sgahr * 		Further enhancement to volume. Values not in range 0-255
28180507Sgahr * 		are now reduced to be in range. This prevents overflow in
29180507Sgahr * 		the uchar storing the volume (256 -> 0, -20 -> 236, ...).
30180507Sgahr * 		Version 2.0.2
31180507Sgahr *
3210099Sjkh */
3313985Sache
34114601Sobrien#include <sys/cdefs.h>
35114601Sobrien__FBSDID("$FreeBSD: releng/11.0/usr.sbin/cdcontrol/cdcontrol.c 227225 2011-11-06 16:52:26Z ed $");
3629103Scharnier
3777168Skris#include <sys/cdio.h>
3896213Smaxim#include <sys/cdrio.h>
3977168Skris#include <sys/file.h>
4077168Skris#include <sys/ioctl.h>
4177168Skris#include <sys/param.h>
4290868Smike#include <arpa/inet.h>
4313985Sache#include <ctype.h>
4429103Scharnier#include <err.h>
4529103Scharnier#include <errno.h>
4677168Skris#include <histedit.h>
4796213Smaxim#include <limits.h>
4869793Sobrien#include <paths.h>
4910099Sjkh#include <stdio.h>
5010099Sjkh#include <stdlib.h>
5110099Sjkh#include <string.h>
5210099Sjkh#include <unistd.h>
5377168Skris#include <vis.h>
5410099Sjkh
55180507Sgahr#define VERSION "2.0.2"
5610099Sjkh
5787573Smikeh#define ASTS_INVALID	0x00  /* Audio status byte not valid */
5887573Smikeh#define ASTS_PLAYING	0x11  /* Audio play operation in progress */
5987573Smikeh#define ASTS_PAUSED	0x12  /* Audio play operation paused */
6087573Smikeh#define ASTS_COMPLETED	0x13  /* Audio play operation successfully completed */
6187573Smikeh#define ASTS_ERROR	0x14  /* Audio play operation stopped due to error */
6287573Smikeh#define ASTS_VOID	0x15  /* No current audio status to return */
6310099Sjkh
64122855Seivind#ifdef DEFAULT_CD_DRIVE
65122855Seivind#  error "Setting DEFAULT_CD_DRIVE is no longer supported"
6613985Sache#endif
6713985Sache
6887573Smikeh#define CMD_DEBUG	1
6987573Smikeh#define CMD_EJECT	2
7087573Smikeh#define CMD_HELP	3
7187573Smikeh#define CMD_INFO	4
7287573Smikeh#define CMD_PAUSE	5
7387573Smikeh#define CMD_PLAY	6
7487573Smikeh#define CMD_QUIT	7
7587573Smikeh#define CMD_RESUME	8
7687573Smikeh#define CMD_STOP	9
7787573Smikeh#define CMD_VOLUME	10
7887573Smikeh#define CMD_CLOSE	11
7987573Smikeh#define CMD_RESET	12
8087573Smikeh#define CMD_SET		13
8187573Smikeh#define CMD_STATUS	14
8287573Smikeh#define CMD_CDID	15
8387573Smikeh#define CMD_NEXT	16
8487573Smikeh#define CMD_PREVIOUS	17
8596213Smaxim#define CMD_SPEED	18
8687573Smikeh#define STATUS_AUDIO	0x1
8787573Smikeh#define STATUS_MEDIA	0x2
8887573Smikeh#define STATUS_VOLUME	0x4
8913985Sache
90227225Sedstatic struct cmdtab {
9110099Sjkh	int command;
9287568Smikeh	const char *name;
9387568Smikeh	unsigned min;
9487568Smikeh	const char *args;
9510099Sjkh} cmdtab[] = {
9687573Smikeh{ CMD_CLOSE,	"close",	1, "" },
9787573Smikeh{ CMD_DEBUG,	"debug",	1, "on | off" },
9887573Smikeh{ CMD_EJECT,	"eject",	1, "" },
9987573Smikeh{ CMD_HELP,	"?",		1, 0 },
10087573Smikeh{ CMD_HELP,	"help",		1, "" },
10187573Smikeh{ CMD_INFO,	"info",		1, "" },
10287573Smikeh{ CMD_NEXT,	"next",		1, "" },
10387573Smikeh{ CMD_PAUSE,	"pause",	2, "" },
10487573Smikeh{ CMD_PLAY,	"play",		1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
10587573Smikeh{ CMD_PLAY,	"play",		1, "track1[.index1] [track2[.index2]]" },
10687573Smikeh{ CMD_PLAY,	"play",		1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
10787573Smikeh{ CMD_PLAY,	"play",		1, "[#block [len]]" },
10887573Smikeh{ CMD_PREVIOUS,	"previous",	2, "" },
10987573Smikeh{ CMD_QUIT,	"quit",		1, "" },
11087573Smikeh{ CMD_RESET,	"reset",	4, "" },
11187573Smikeh{ CMD_RESUME,	"resume",	1, "" },
11287573Smikeh{ CMD_SET,	"set",		2, "msf | lba" },
11387573Smikeh{ CMD_STATUS,	"status",	1, "[audio | media | volume]" },
11487573Smikeh{ CMD_STOP,	"stop",		3, "" },
11587573Smikeh{ CMD_VOLUME,	"volume",	1,
116180507Sgahr      "<l&r> <l> <r> | left | right | mute | mono | stereo" },
11787573Smikeh{ CMD_CDID,	"cdid",		2, "" },
11896213Smaxim{ CMD_SPEED,	"speed",	2, "speed" },
11987573Smikeh{ 0,		NULL,		0, NULL }
12010099Sjkh};
12110099Sjkh
122227225Sedstatic struct cd_toc_entry toc_buffer[100];
12310099Sjkh
124227225Sedstatic const char *cdname;
125227225Sedstatic int	fd = -1;
126227225Sedstatic int	verbose = 1;
127227225Sedstatic int	msf = 1;
12810099Sjkh
129227225Sedstatic int	 setvol(int, int);
130227225Sedstatic int	 read_toc_entrys(int);
131227225Sedstatic int	 play_msf(int, int, int, int, int, int);
132227225Sedstatic int	 play_track(int, int, int, int);
133227225Sedstatic int	 status(int *, int *, int *, int *);
134227225Sedstatic int	 open_cd(void);
135227225Sedstatic int	 next_prev(char *arg, int);
136227225Sedstatic int	 play(char *arg);
137227225Sedstatic int	 info(char *arg);
138227225Sedstatic int	 cdid(void);
139227225Sedstatic int	 pstatus(char *arg);
140227225Sedstatic char	*input(int *);
141227225Sedstatic void	 prtrack(struct cd_toc_entry *e, int lastflag);
142227225Sedstatic void	 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
143227225Sedstatic unsigned int msf2lba(u_char m, u_char s, u_char f);
144227225Sedstatic int	 play_blocks(int blk, int len);
145227225Sedstatic int	 run(int cmd, char *arg);
146227225Sedstatic char	*parse(char *buf, int *cmd);
147227225Sedstatic void	 help(void);
148227225Sedstatic void	 usage(void);
149227225Sedstatic char	*use_cdrom_instead(const char *);
150227225Sedstatic const char *strstatus(int);
15199800Salfredstatic u_int	 dbprog_discid(void);
152227225Sedstatic const char *cdcontrol_prompt(void);
15310099Sjkh
154227225Sedstatic void
155227225Sedhelp(void)
15610099Sjkh{
15710099Sjkh	struct cmdtab *c;
15887568Smikeh	const char *s;
15987568Smikeh	char n;
16013985Sache	int i;
16110099Sjkh
16210099Sjkh	for (c=cmdtab; c->name; ++c) {
16310099Sjkh		if (! c->args)
16410099Sjkh			continue;
16513985Sache		printf("\t");
16613985Sache		for (i = c->min, s = c->name; *s; s++, i--) {
16713985Sache			if (i > 0)
16813985Sache				n = toupper(*s);
16913985Sache			else
17013985Sache				n = *s;
17113985Sache			putchar(n);
17213985Sache		}
17310099Sjkh		if (*c->args)
17410099Sjkh			printf (" %s", c->args);
17510099Sjkh		printf ("\n");
17610099Sjkh	}
17713985Sache	printf ("\n\tThe word \"play\" is not required for the play commands.\n");
17813985Sache	printf ("\tThe plain target address is taken as a synonym for play.\n");
17910099Sjkh}
18010099Sjkh
181227225Sedstatic void
182227225Sedusage(void)
18310099Sjkh{
18443479Sbillf	fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
18510099Sjkh	exit (1);
18610099Sjkh}
18710099Sjkh
188227225Sedstatic char *
189227225Seduse_cdrom_instead(const char *old_envvar)
19071122Sjoe{
19171122Sjoe	char *device;
19271122Sjoe
19371122Sjoe	device = getenv(old_envvar);
19471122Sjoe	if (device)
19571122Sjoe		warnx("%s environment variable deprecated, "
19671122Sjoe		    "please use CDROM in the future.", old_envvar);
19771122Sjoe	return device;
19871122Sjoe}
19971122Sjoe
20071122Sjoe
201227225Sedint
202227225Sedmain(int argc, char **argv)
20310099Sjkh{
20410099Sjkh	int cmd;
20510099Sjkh	char *arg;
20610099Sjkh
20710099Sjkh	for (;;) {
20810099Sjkh		switch (getopt (argc, argv, "svhf:")) {
209176407Sru		case -1:
21010099Sjkh			break;
21110099Sjkh		case 's':
21210099Sjkh			verbose = 0;
21310099Sjkh			continue;
21410099Sjkh		case 'v':
21510099Sjkh			verbose = 2;
21610099Sjkh			continue;
21710099Sjkh		case 'f':
21810099Sjkh			cdname = optarg;
21910099Sjkh			continue;
22010099Sjkh		case 'h':
22110099Sjkh		default:
22210099Sjkh			usage ();
22310099Sjkh		}
22410099Sjkh		break;
22510099Sjkh	}
22610099Sjkh	argc -= optind;
22710099Sjkh	argv += optind;
22810099Sjkh
22913985Sache	if (argc > 0 && ! strcasecmp (*argv, "help"))
23010099Sjkh		usage ();
23110099Sjkh
23210099Sjkh	if (! cdname) {
23370149Sdes		cdname = getenv("CDROM");
23470149Sdes	}
23570149Sdes
23675324Sjoe	if (! cdname)
23775324Sjoe		cdname = use_cdrom_instead("MUSIC_CD");
23875324Sjoe	if (! cdname)
23975324Sjoe		cdname = use_cdrom_instead("CD_DRIVE");
24075324Sjoe	if (! cdname)
24175324Sjoe		cdname = use_cdrom_instead("DISC");
24275324Sjoe	if (! cdname)
24375324Sjoe		cdname = use_cdrom_instead("CDPLAY");
24475324Sjoe
24510099Sjkh	if (argc > 0) {
24610099Sjkh		char buf[80], *p;
247197833Sjh		int len, rc;
24810099Sjkh
24913985Sache		for (p=buf; argc-->0; ++argv) {
25010099Sjkh			len = strlen (*argv);
25113985Sache
25210099Sjkh			if (p + len >= buf + sizeof (buf) - 1)
25310099Sjkh				usage ();
25413985Sache
25510099Sjkh			if (p > buf)
25610099Sjkh				*p++ = ' ';
25713985Sache
25810099Sjkh			strcpy (p, *argv);
25910099Sjkh			p += len;
26010099Sjkh		}
26110099Sjkh		*p = 0;
26210099Sjkh		arg = parse (buf, &cmd);
263197833Sjh		rc = run (cmd, arg);
264197833Sjh		if (rc < 0 && verbose)
265197833Sjh			warn(NULL);
266197833Sjh
267197833Sjh		return (rc);
26810099Sjkh	}
26910099Sjkh
27010099Sjkh	if (verbose == 1)
27110099Sjkh		verbose = isatty (0);
27213985Sache
27310099Sjkh	if (verbose) {
27413985Sache		printf ("Compact Disc Control utility, version %s\n", VERSION);
27510099Sjkh		printf ("Type `?' for command list\n\n");
27610099Sjkh	}
27710099Sjkh
27810099Sjkh	for (;;) {
27910099Sjkh		arg = input (&cmd);
28010099Sjkh		if (run (cmd, arg) < 0) {
28110099Sjkh			if (verbose)
28229103Scharnier				warn(NULL);
28310099Sjkh			close (fd);
28410099Sjkh			fd = -1;
28510099Sjkh		}
28610099Sjkh		fflush (stdout);
28710099Sjkh	}
28810099Sjkh}
28910099Sjkh
290227225Sedstatic int
291227225Sedrun(int cmd, char *arg)
29210099Sjkh{
29396213Smaxim	long speed;
294180507Sgahr	int l, r, rc, count;
29510099Sjkh
29610099Sjkh	switch (cmd) {
29713985Sache
29810099Sjkh	case CMD_QUIT:
29910099Sjkh		exit (0);
30010099Sjkh
30113985Sache	case CMD_INFO:
30213985Sache		if (fd < 0 && ! open_cd ())
30313985Sache			return (0);
30410099Sjkh
30510099Sjkh		return info (arg);
30610099Sjkh
30763091Sjoe	case CMD_CDID:
30863091Sjoe		if (fd < 0 && ! open_cd ())
30963091Sjoe			return (0);
31063091Sjoe
31163091Sjoe		return cdid ();
31263091Sjoe
31313884Sache	case CMD_STATUS:
31413985Sache		if (fd < 0 && ! open_cd ())
31513985Sache			return (0);
31613985Sache
31713884Sache		return pstatus (arg);
31813884Sache
31977168Skris	case CMD_NEXT:
32077168Skris	case CMD_PREVIOUS:
32177168Skris		if (fd < 0 && ! open_cd ())
32277168Skris			return (0);
32377168Skris
32477168Skris		while (isspace (*arg))
32577168Skris			arg++;
32677168Skris
32777168Skris		return next_prev (arg, cmd);
32877168Skris
32910099Sjkh	case CMD_PAUSE:
33013985Sache		if (fd < 0 && ! open_cd ())
33113985Sache			return (0);
33213985Sache
33310099Sjkh		return ioctl (fd, CDIOCPAUSE);
33410099Sjkh
33510099Sjkh	case CMD_RESUME:
33613985Sache		if (fd < 0 && ! open_cd ())
33713985Sache			return (0);
33813985Sache
33910099Sjkh		return ioctl (fd, CDIOCRESUME);
34010099Sjkh
34110099Sjkh	case CMD_STOP:
34213985Sache		if (fd < 0 && ! open_cd ())
34313985Sache			return (0);
34410099Sjkh
34513985Sache		rc = ioctl (fd, CDIOCSTOP);
34613985Sache
34713985Sache		(void) ioctl (fd, CDIOCALLOW);
34813985Sache
34913985Sache		return (rc);
35013985Sache
35113884Sache	case CMD_RESET:
35213985Sache		if (fd < 0 && ! open_cd ())
35313985Sache			return (0);
35413985Sache
35513884Sache		rc = ioctl (fd, CDIOCRESET);
35613884Sache		if (rc < 0)
35713884Sache			return rc;
35813884Sache		close(fd);
35913884Sache		fd = -1;
36013884Sache		return (0);
36113884Sache
36210099Sjkh	case CMD_DEBUG:
36313985Sache		if (fd < 0 && ! open_cd ())
36413985Sache			return (0);
36513985Sache
36613985Sache		if (! strcasecmp (arg, "on"))
36710099Sjkh			return ioctl (fd, CDIOCSETDEBUG);
36813985Sache
36913985Sache		if (! strcasecmp (arg, "off"))
37010099Sjkh			return ioctl (fd, CDIOCCLRDEBUG);
37113985Sache
37229103Scharnier		warnx("invalid command arguments");
37313985Sache
37410099Sjkh		return (0);
37510099Sjkh
37610099Sjkh	case CMD_EJECT:
37713985Sache		if (fd < 0 && ! open_cd ())
37813985Sache			return (0);
37913985Sache
38010099Sjkh		(void) ioctl (fd, CDIOCALLOW);
38110099Sjkh		rc = ioctl (fd, CDIOCEJECT);
38210099Sjkh		if (rc < 0)
38310099Sjkh			return (rc);
38413865Sache		return (0);
38513865Sache
38613985Sache	case CMD_CLOSE:
38713985Sache		if (fd < 0 && ! open_cd ())
38813985Sache			return (0);
38913985Sache
39013985Sache		(void) ioctl (fd, CDIOCALLOW);
39113865Sache		rc = ioctl (fd, CDIOCCLOSE);
39213865Sache		if (rc < 0)
39313865Sache			return (rc);
39413865Sache		close(fd);
39510099Sjkh		fd = -1;
39610099Sjkh		return (0);
39710099Sjkh
39810099Sjkh	case CMD_PLAY:
39913985Sache		if (fd < 0 && ! open_cd ())
40013985Sache			return (0);
40113985Sache
40213985Sache		while (isspace (*arg))
40313985Sache			arg++;
40413985Sache
40510099Sjkh		return play (arg);
40610099Sjkh
40713884Sache	case CMD_SET:
40813985Sache		if (! strcasecmp (arg, "msf"))
40913884Sache			msf = 1;
41013985Sache		else if (! strcasecmp (arg, "lba"))
41113884Sache			msf = 0;
41213884Sache		else
41329103Scharnier			warnx("invalid command arguments");
41413884Sache		return (0);
41513884Sache
41610099Sjkh	case CMD_VOLUME:
41713985Sache		if (fd < 0 && !open_cd ())
41813985Sache			return (0);
41910099Sjkh
420201608Sdwmalone		if (! strlen (arg)) {
421201608Sdwmalone			char volume[] = "volume";
422180507Sgahr
423201608Sdwmalone		    	return pstatus (volume);
424201608Sdwmalone		}
425201608Sdwmalone
42613985Sache		if (! strncasecmp (arg, "left", strlen(arg)))
42710099Sjkh			return ioctl (fd, CDIOCSETLEFT);
42813985Sache
42913985Sache		if (! strncasecmp (arg, "right", strlen(arg)))
43010099Sjkh			return ioctl (fd, CDIOCSETRIGHT);
43113985Sache
43213985Sache		if (! strncasecmp (arg, "mono", strlen(arg)))
43310099Sjkh			return ioctl (fd, CDIOCSETMONO);
43413985Sache
43513985Sache		if (! strncasecmp (arg, "stereo", strlen(arg)))
43610099Sjkh			return ioctl (fd, CDIOCSETSTERIO);
43710099Sjkh
43813985Sache		if (! strncasecmp (arg, "mute", strlen(arg)))
43913985Sache			return ioctl (fd, CDIOCSETMUTE);
44013985Sache
441180507Sgahr		count = sscanf (arg, "%d %d", &l, &r);
442180507Sgahr		if (count == 1)
443180507Sgahr		    return setvol (l, l);
444180507Sgahr		if (count == 2)
445180507Sgahr		    return setvol (l, r);
446180507Sgahr		warnx("invalid command arguments");
447180507Sgahr		return (0);
44813985Sache
44996213Smaxim	case CMD_SPEED:
45096213Smaxim		if (fd < 0 && ! open_cd ())
45196213Smaxim			return (0);
45296213Smaxim
45396213Smaxim		errno = 0;
454105421Snjl		if (strcasecmp("max", arg) == 0)
455105421Snjl			speed = CDR_MAX_SPEED;
456105421Snjl		else
457105421Snjl			speed = strtol(arg, NULL, 10) * 177;
458105421Snjl		if (speed <= 0 || speed > INT_MAX) {
45996213Smaxim			warnx("invalid command arguments %s", arg);
46096213Smaxim			return (0);
46196213Smaxim		}
46296213Smaxim		return ioctl(fd, CDRIOCREADSPEED, &speed);
46396213Smaxim
46413985Sache	default:
46513985Sache	case CMD_HELP:
46613985Sache		help ();
46713985Sache		return (0);
46813985Sache
46910099Sjkh	}
47010099Sjkh}
47110099Sjkh
472227225Sedstatic int
473227225Sedplay(char *arg)
47410099Sjkh{
47510099Sjkh	struct ioc_toc_header h;
47687568Smikeh	unsigned int n;
47787568Smikeh	int rc, start, end = 0, istart = 1, iend = 1;
47810099Sjkh
47910099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
48013985Sache
48110099Sjkh	if (rc < 0)
48210099Sjkh		return (rc);
48310099Sjkh
48410099Sjkh	n = h.ending_track - h.starting_track + 1;
48510099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
48613985Sache
48710099Sjkh	if (rc < 0)
48810099Sjkh		return (rc);
48910099Sjkh
49013989Sache	if (! arg || ! *arg) {
49113985Sache		/* Play the whole disc */
49213989Sache		if (msf)
49313989Sache			return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
49413989Sache							toc_buffer[n].addr.msf.second,
49513989Sache							toc_buffer[n].addr.msf.frame));
49613989Sache		else
49713989Sache			return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
49813989Sache	}
49910099Sjkh
50010099Sjkh	if (strchr (arg, '#')) {
50113985Sache		/* Play block #blk [ len ] */
50210099Sjkh		int blk, len = 0;
50310099Sjkh
50410099Sjkh		if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
50513985Sache		    1 != sscanf (arg, "#%d", &blk))
50613985Sache			goto Clean_up;
50713985Sache
50813989Sache		if (len == 0) {
50913989Sache			if (msf)
51013989Sache				len = msf2lba (toc_buffer[n].addr.msf.minute,
51113989Sache					       toc_buffer[n].addr.msf.second,
51213989Sache					       toc_buffer[n].addr.msf.frame) - blk;
51313989Sache			else
51413989Sache				len = ntohl(toc_buffer[n].addr.lba) - blk;
51513989Sache		}
51610099Sjkh		return play_blocks (blk, len);
51710099Sjkh	}
51810099Sjkh
51910099Sjkh	if (strchr (arg, ':')) {
52010099Sjkh		/*
52110099Sjkh		 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
52213985Sache		 *
52313985Sache		 * Will now also undestand timed addresses relative
52413985Sache		 * to the beginning of a track in the form...
52513985Sache		 *
52613985Sache		 *      tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
52710099Sjkh		 */
52813985Sache		unsigned tr1, tr2;
52913985Sache		unsigned m1, m2, s1, s2, f1, f2;
53013989Sache		unsigned char tm, ts, tf;
53110099Sjkh
53213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53313985Sache		if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
53413985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
53513985Sache			goto Play_Relative_Addresses;
53613985Sache
53713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53813985Sache		if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
53913985Sache		    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
54013985Sache			goto Play_Relative_Addresses;
54113985Sache
54213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54313985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
54413985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
54513985Sache			goto Play_Relative_Addresses;
54613985Sache
54713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54813985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
54913985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
55013985Sache			goto Play_Relative_Addresses;
55113985Sache
55213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55313985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
55413985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2))
55513985Sache			goto Play_Relative_Addresses;
55613985Sache
55713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55813985Sache		if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
55913985Sache		    &tr1, &m1, &s1, &m2, &s2, &f2))
56013985Sache			goto Play_Relative_Addresses;
56113985Sache
56213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56313985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
56413985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2))
56513985Sache			goto Play_Relative_Addresses;
56613985Sache
56713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56813985Sache		if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
56913985Sache			goto Play_Relative_Addresses;
57013985Sache
57113985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57213985Sache		if (5 == sscanf (arg, "%d %d:%d %d %d",
57313985Sache		    &tr1, &m1, &s1, &tr2, &m2))
57413985Sache			goto Play_Relative_Addresses;
57513985Sache
57613985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57713985Sache		if (5 == sscanf (arg, "%d %d:%d.%d %d",
57813985Sache		    &tr1, &m1, &s1, &f1, &tr2))
57913985Sache			goto Play_Relative_Addresses;
58013985Sache
58113985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
58213985Sache		if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
58313985Sache			goto Play_Relative_Addresses;
58413985Sache
58513985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
58613985Sache		if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
58713985Sache			goto Play_Relative_Addresses;
58813985Sache
58913985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
59013985Sache		if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
59113985Sache			goto Play_Relative_Addresses;
59213985Sache
59313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
59413985Sache		goto Try_Absolute_Timed_Addresses;
59513985Sache
59613985SachePlay_Relative_Addresses:
59713985Sache		if (! tr1)
59813985Sache			tr1 = 1;
59913985Sache		else if (tr1 > n)
60013985Sache			tr1 = n;
60113985Sache
602112559Seivind		tr1--;
603112559Seivind
60413989Sache		if (msf) {
60513989Sache			tm = toc_buffer[tr1].addr.msf.minute;
60613989Sache			ts = toc_buffer[tr1].addr.msf.second;
60713989Sache			tf = toc_buffer[tr1].addr.msf.frame;
60813989Sache		} else
60913989Sache			lba2msf(ntohl(toc_buffer[tr1].addr.lba),
61013989Sache				&tm, &ts, &tf);
61113989Sache		if ((m1 > tm)
61213989Sache		    || ((m1 == tm)
61313989Sache		    && ((s1 > ts)
61413989Sache		    || ((s1 == ts)
61513989Sache		    && (f1 > tf))))) {
61613985Sache			printf ("Track %d is not that long.\n", tr1);
61713985Sache			return (0);
61813985Sache		}
61913985Sache
62013989Sache		f1 += tf;
62113985Sache		if (f1 >= 75) {
62213985Sache			s1 += f1 / 75;
62313985Sache			f1 %= 75;
62413985Sache		}
62513985Sache
62613989Sache		s1 += ts;
62713985Sache		if (s1 >= 60) {
62813985Sache			m1 += s1 / 60;
62913985Sache			s1 %= 60;
63013985Sache		}
63113985Sache
63213989Sache		m1 += tm;
63313985Sache
63413985Sache		if (! tr2) {
63513985Sache			if (m2 || s2 || f2) {
63613985Sache				tr2 = tr1;
63713985Sache				f2 += f1;
63813985Sache				if (f2 >= 75) {
63913985Sache					s2 += f2 / 75;
64013985Sache					f2 %= 75;
64113985Sache				}
64213985Sache
64313985Sache				s2 += s1;
64413985Sache				if (s2 > 60) {
64513985Sache					m2 += s2 / 60;
64613985Sache					s2 %= 60;
64713985Sache				}
64813985Sache
64913985Sache				m2 += m1;
65013985Sache			} else {
65113985Sache				tr2 = n;
65213989Sache				if (msf) {
65313989Sache					m2 = toc_buffer[n].addr.msf.minute;
65413989Sache					s2 = toc_buffer[n].addr.msf.second;
65513989Sache					f2 = toc_buffer[n].addr.msf.frame;
65613989Sache				} else {
65713989Sache					lba2msf(ntohl(toc_buffer[n].addr.lba),
65813989Sache						&tm, &ts, &tf);
65913989Sache					m2 = tm;
66013989Sache					s2 = ts;
66113989Sache					f2 = tf;
66213989Sache				}
66313985Sache			}
66413985Sache		} else if (tr2 > n) {
66513985Sache			tr2 = n;
66613985Sache			m2 = s2 = f2 = 0;
66713985Sache		} else {
66813985Sache			if (m2 || s2 || f2)
66913985Sache				tr2--;
67013989Sache			if (msf) {
67113989Sache				tm = toc_buffer[tr2].addr.msf.minute;
67213989Sache				ts = toc_buffer[tr2].addr.msf.second;
67313989Sache				tf = toc_buffer[tr2].addr.msf.frame;
67413989Sache			} else
67513989Sache				lba2msf(ntohl(toc_buffer[tr2].addr.lba),
67613989Sache					&tm, &ts, &tf);
67713989Sache			f2 += tf;
67813985Sache			if (f2 >= 75) {
67913985Sache				s2 += f2 / 75;
68013985Sache				f2 %= 75;
68113985Sache			}
68213985Sache
68313989Sache			s2 += ts;
68413985Sache			if (s2 > 60) {
68513985Sache				m2 += s2 / 60;
68613985Sache				s2 %= 60;
68713985Sache			}
68813985Sache
68913989Sache			m2 += tm;
69013985Sache		}
69113985Sache
69213989Sache		if (msf) {
69313989Sache			tm = toc_buffer[n].addr.msf.minute;
69413989Sache			ts = toc_buffer[n].addr.msf.second;
69513989Sache			tf = toc_buffer[n].addr.msf.frame;
69613989Sache		} else
69713989Sache			lba2msf(ntohl(toc_buffer[n].addr.lba),
69813989Sache				&tm, &ts, &tf);
69913985Sache		if ((tr2 < n)
70013989Sache		    && ((m2 > tm)
70113989Sache		    || ((m2 == tm)
70213989Sache		    && ((s2 > ts)
70313989Sache		    || ((s2 == ts)
70413989Sache		    && (f2 > tf)))))) {
70513985Sache			printf ("The playing time of the disc is not that long.\n");
70613985Sache			return (0);
70713985Sache		}
70813985Sache		return (play_msf (m1, s1, f1, m2, s2, f2));
70913985Sache
71013985SacheTry_Absolute_Timed_Addresses:
71113985Sache		if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
71213985Sache			&m1, &s1, &f1, &m2, &s2, &f2) &&
71310099Sjkh		    5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
71410099Sjkh		    5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
71510099Sjkh		    3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
71610099Sjkh		    4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
71710099Sjkh		    2 != sscanf (arg, "%d:%d", &m1, &s1))
71813985Sache			goto Clean_up;
71913985Sache
72010099Sjkh		if (m2 == 0) {
72113989Sache			if (msf) {
72213989Sache				m2 = toc_buffer[n].addr.msf.minute;
72313989Sache				s2 = toc_buffer[n].addr.msf.second;
72413989Sache				f2 = toc_buffer[n].addr.msf.frame;
72513989Sache			} else {
72613989Sache				lba2msf(ntohl(toc_buffer[n].addr.lba),
72713989Sache					&tm, &ts, &tf);
72813989Sache				m2 = tm;
72913989Sache				s2 = ts;
73013989Sache				f2 = tf;
73113989Sache			}
73210099Sjkh		}
73310099Sjkh		return play_msf (m1, s1, f1, m2, s2, f2);
73410099Sjkh	}
73510099Sjkh
73610099Sjkh	/*
73710099Sjkh	 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
73810099Sjkh	 */
73910099Sjkh	if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
74010099Sjkh	    3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
74110099Sjkh	    3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
74210099Sjkh	    2 != sscanf (arg, "%d.%d", &start, &istart) &&
74310099Sjkh	    2 != sscanf (arg, "%d%d", &start, &end) &&
74410099Sjkh	    1 != sscanf (arg, "%d", &start))
74513985Sache		goto Clean_up;
74613985Sache
74710099Sjkh	if (end == 0)
74810099Sjkh		end = n;
74913985Sache	return (play_track (start, istart, end, iend));
75013985Sache
75113985SacheClean_up:
75229103Scharnier	warnx("invalid command arguments");
75313985Sache	return (0);
75410099Sjkh}
75510099Sjkh
756227225Sedstatic int
757227225Sednext_prev(char *arg, int cmd)
75877168Skris{
75977168Skris	struct ioc_toc_header h;
76077168Skris	int dir, junk, n, off, rc, trk;
76177168Skris
76277168Skris	dir = (cmd == CMD_NEXT) ? 1 : -1;
76377168Skris	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
76477168Skris	if (rc < 0)
76577168Skris		return (rc);
76677168Skris
76777168Skris	n = h.ending_track - h.starting_track + 1;
76877168Skris	rc = status (&trk, &junk, &junk, &junk);
76977168Skris	if (rc < 0)
77077168Skris		return (-1);
77177168Skris
77277168Skris	if (arg && *arg) {
77377168Skris		if (sscanf (arg, "%u", &off) != 1) {
77477168Skris		    warnx("invalid command argument");
77577168Skris		    return (0);
77677168Skris		} else
77777168Skris		    trk += off * dir;
77877168Skris	} else
77977168Skris		trk += dir;
78077168Skris
78177168Skris	if (trk > h.ending_track)
78277168Skris		trk = 1;
78377168Skris
78477168Skris	return (play_track (trk, 1, n, 1));
78577168Skris}
78677168Skris
787227225Sedstatic const char *
788227225Sedstrstatus(int sts)
78910099Sjkh{
79010099Sjkh	switch (sts) {
79187573Smikeh	case ASTS_INVALID:	return ("invalid");
79287573Smikeh	case ASTS_PLAYING:	return ("playing");
79387573Smikeh	case ASTS_PAUSED:	return ("paused");
79487573Smikeh	case ASTS_COMPLETED:	return ("completed");
79587573Smikeh	case ASTS_ERROR:	return ("error");
79687573Smikeh	case ASTS_VOID:		return ("void");
79787573Smikeh	default:		return ("??");
79810099Sjkh	}
79910099Sjkh}
80010099Sjkh
801227225Sedstatic int
802227225Sedpstatus(char *arg)
80310099Sjkh{
80410099Sjkh	struct ioc_vol v;
80513888Sache	struct ioc_read_subchannel ss;
80613888Sache	struct cd_sub_channel_info data;
80713884Sache	int rc, trk, m, s, f;
80832782Sjmz	int what = 0;
80977168Skris	char *p, vmcn[(4 * 15) + 1];
81010099Sjkh
81132782Sjmz	while ((p = strtok(arg, " \t"))) {
81232782Sjmz	    arg = 0;
81332782Sjmz	    if (!strncasecmp(p, "audio", strlen(p)))
81432782Sjmz		what |= STATUS_AUDIO;
81532782Sjmz	    else if (!strncasecmp(p, "media", strlen(p)))
81632782Sjmz		what |= STATUS_MEDIA;
81732782Sjmz	    else if (!strncasecmp(p, "volume", strlen(p)))
81832782Sjmz		what |= STATUS_VOLUME;
81932782Sjmz	    else {
82032782Sjmz		warnx("invalid command arguments");
82132782Sjmz		return 0;
82232782Sjmz	    }
82332782Sjmz	}
82432782Sjmz	if (!what)
82532782Sjmz	    what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
82632782Sjmz	if (what & STATUS_AUDIO) {
82732782Sjmz	    rc = status (&trk, &m, &s, &f);
82832782Sjmz	    if (rc >= 0)
82910099Sjkh		if (verbose)
83032782Sjmz		    printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
83132782Sjmz			    rc, strstatus (rc), trk, m, s, f);
83210099Sjkh		else
83332782Sjmz		    printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
83432782Sjmz	    else
83513888Sache		printf ("No current status info available\n");
83632782Sjmz	}
83732782Sjmz	if (what & STATUS_MEDIA) {
83832782Sjmz	    bzero (&ss, sizeof (ss));
83932782Sjmz	    ss.data = &data;
84032782Sjmz	    ss.data_len = sizeof (data);
84132782Sjmz	    ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
84232782Sjmz	    ss.data_format = CD_MEDIA_CATALOG;
84332782Sjmz	    rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
84432782Sjmz	    if (rc >= 0) {
84513889Sache		printf("Media catalog is %sactive",
84687573Smikeh		    ss.data->what.media_catalog.mc_valid ? "": "in");
84716736Sache		if (ss.data->what.media_catalog.mc_valid &&
84816736Sache		    ss.data->what.media_catalog.mc_number[0])
84977168Skris		{
85077168Skris		    strvisx (vmcn, ss.data->what.media_catalog.mc_number,
85177168Skris			    (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
85277168Skris		    printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
85377168Skris		}
85413889Sache		putchar('\n');
85532782Sjmz	    } else
85613888Sache		printf("No media catalog info available\n");
85732782Sjmz	}
85832782Sjmz	if (what & STATUS_VOLUME) {
85932782Sjmz	    rc = ioctl (fd, CDIOCGETVOL, &v);
86032782Sjmz	    if (rc >= 0)
86110099Sjkh		if (verbose)
86232782Sjmz		    printf ("Left volume = %d, right volume = %d\n",
86332782Sjmz			    v.vol[0], v.vol[1]);
86410099Sjkh		else
86532782Sjmz		    printf ("%d %d\n", v.vol[0], v.vol[1]);
86632782Sjmz	    else
86713888Sache		printf ("No volume level info available\n");
86832782Sjmz	}
86913884Sache	return(0);
87013884Sache}
87110099Sjkh
87263091Sjoe/*
87363091Sjoe * dbprog_sum
87463091Sjoe *	Convert an integer to its text string representation, and
87563091Sjoe *	compute its checksum.  Used by dbprog_discid to derive the
87663091Sjoe *	disc ID.
87763091Sjoe *
87863091Sjoe * Args:
87963091Sjoe *	n - The integer value.
88063091Sjoe *
88163091Sjoe * Return:
88263091Sjoe *	The integer checksum.
88363091Sjoe */
88463091Sjoestatic int
88563091Sjoedbprog_sum(int n)
88663091Sjoe{
88763091Sjoe	char	buf[12],
88863091Sjoe		*p;
88963091Sjoe	int	ret = 0;
89063091Sjoe
89163091Sjoe	/* For backward compatibility this algorithm must not change */
89263091Sjoe	sprintf(buf, "%u", n);
89363091Sjoe	for (p = buf; *p != '\0'; p++)
89463091Sjoe		ret += (*p - '0');
89563091Sjoe
89663091Sjoe	return(ret);
89763091Sjoe}
89863091Sjoe
89963091Sjoe
90063091Sjoe/*
90163091Sjoe * dbprog_discid
90263091Sjoe *	Compute a magic disc ID based on the number of tracks,
90363091Sjoe *	the length of each track, and a checksum of the string
90463091Sjoe *	that represents the offset of each track.
90563091Sjoe *
90663091Sjoe * Args:
90763091Sjoe *	s - Pointer to the curstat_t structure.
90863091Sjoe *
90963091Sjoe * Return:
91063091Sjoe *	The integer disc ID.
91163091Sjoe */
91263091Sjoestatic u_int
913201608Sdwmalonedbprog_discid(void)
91463091Sjoe{
91563091Sjoe	struct	ioc_toc_header h;
91663091Sjoe	int	rc;
91763091Sjoe	int	i, ntr,
91863091Sjoe		t = 0,
91963091Sjoe		n = 0;
92063091Sjoe
92163091Sjoe	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
92263091Sjoe	if (rc < 0)
92363091Sjoe		return 0;
92463091Sjoe	ntr = h.ending_track - h.starting_track + 1;
92563091Sjoe	i = msf;
92663091Sjoe	msf = 1;
92763091Sjoe	rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
92863091Sjoe	msf = i;
92963091Sjoe	if (rc < 0)
93063091Sjoe		return 0;
93163091Sjoe	/* For backward compatibility this algorithm must not change */
93263091Sjoe	for (i = 0; i < ntr; i++) {
93363091Sjoe#define TC_MM(a) toc_buffer[a].addr.msf.minute
93463091Sjoe#define TC_SS(a) toc_buffer[a].addr.msf.second
93563091Sjoe		n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
93663091Sjoe
93763091Sjoe		t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
93887573Smikeh		    ((TC_MM(i) * 60) + TC_SS(i));
93963091Sjoe	}
94063091Sjoe
94163091Sjoe	return((n % 0xff) << 24 | t << 8 | ntr);
94263091Sjoe}
94363091Sjoe
944227225Sedstatic int
945227225Sedcdid(void)
94663091Sjoe{
94763091Sjoe	u_int	id;
94863091Sjoe
94963091Sjoe	id = dbprog_discid();
95063091Sjoe	if (id)
95163091Sjoe	{
95263091Sjoe		if (verbose)
95363091Sjoe			printf ("CDID=");
95463091Sjoe		printf ("%08x\n",id);
95563091Sjoe	}
95663091Sjoe	return id ? 0 : 1;
95763091Sjoe}
95863091Sjoe
959227225Sedstatic int
960227225Sedinfo(char *arg __unused)
96113884Sache{
96213884Sache	struct ioc_toc_header h;
96313884Sache	int rc, i, n;
96413884Sache
96510099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
96613888Sache	if (rc >= 0) {
96710099Sjkh		if (verbose)
96810099Sjkh			printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
96910099Sjkh				h.starting_track, h.ending_track, h.len);
97010099Sjkh		else
97110099Sjkh			printf ("%d %d %d\n", h.starting_track,
97210099Sjkh				h.ending_track, h.len);
97313888Sache	} else {
97429103Scharnier		warn("getting toc header");
97510099Sjkh		return (rc);
97610099Sjkh	}
97710099Sjkh
97810099Sjkh	n = h.ending_track - h.starting_track + 1;
97910099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
98010099Sjkh	if (rc < 0)
98110099Sjkh		return (rc);
98213985Sache
98310099Sjkh	if (verbose) {
98410099Sjkh		printf ("track     start  duration   block  length   type\n");
98510099Sjkh		printf ("-------------------------------------------------\n");
98610099Sjkh	}
98713985Sache
98810099Sjkh	for (i = 0; i < n; i++) {
98910099Sjkh		printf ("%5d  ", toc_buffer[i].track);
99010099Sjkh		prtrack (toc_buffer + i, 0);
99110099Sjkh	}
99213867Sache	printf ("%5d  ", toc_buffer[n].track);
99310099Sjkh	prtrack (toc_buffer + n, 1);
99410099Sjkh	return (0);
99510099Sjkh}
99610099Sjkh
997227225Sedstatic void
998227225Sedlba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
99910099Sjkh{
100087573Smikeh	lba += 150;			/* block start offset */
100187573Smikeh	lba &= 0xffffff;		/* negative lbas use only 24 bits */
100210099Sjkh	*m = lba / (60 * 75);
100310099Sjkh	lba %= (60 * 75);
100410099Sjkh	*s = lba / 75;
100510099Sjkh	*f = lba % 75;
100610099Sjkh}
100710099Sjkh
1008227225Sedstatic unsigned int
1009227225Sedmsf2lba(u_char m, u_char s, u_char f)
101010099Sjkh{
101110099Sjkh	return (((m * 60) + s) * 75 + f) - 150;
101210099Sjkh}
101310099Sjkh
1014227225Sedstatic void
1015227225Sedprtrack(struct cd_toc_entry *e, int lastflag)
101610099Sjkh{
101710099Sjkh	int block, next, len;
101810099Sjkh	u_char m, s, f;
101910099Sjkh
102013884Sache	if (msf) {
102113884Sache		/* Print track start */
102213884Sache		printf ("%2d:%02d.%02d  ", e->addr.msf.minute,
102313884Sache			e->addr.msf.second, e->addr.msf.frame);
102410099Sjkh
102513884Sache		block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
102613884Sache			e->addr.msf.frame);
102713884Sache	} else {
102813884Sache		block = ntohl(e->addr.lba);
102913884Sache		lba2msf(block, &m, &s, &f);
103013884Sache		/* Print track start */
103113884Sache		printf ("%2d:%02d.%02d  ", m, s, f);
103213884Sache	}
103310099Sjkh	if (lastflag) {
103410099Sjkh		/* Last track -- print block */
103510099Sjkh		printf ("       -  %6d       -      -\n", block);
103610099Sjkh		return;
103710099Sjkh	}
103810099Sjkh
103913884Sache	if (msf)
104013884Sache		next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
104113884Sache			e[1].addr.msf.frame);
104213884Sache	else
104313884Sache		next = ntohl(e[1].addr.lba);
104410099Sjkh	len = next - block;
1045103861Smaxim	/* Take into account a start offset time. */
1046103861Smaxim	lba2msf (len - 150, &m, &s, &f);
104710099Sjkh
104810099Sjkh	/* Print duration, block, length, type */
104910099Sjkh	printf ("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
105013985Sache		(e->control & 4) ? "data" : "audio");
105110099Sjkh}
105210099Sjkh
1053227225Sedstatic int
1054227225Sedplay_track(int tstart, int istart, int tend, int iend)
105510099Sjkh{
105610099Sjkh	struct ioc_play_track t;
105710099Sjkh
105810099Sjkh	t.start_track = tstart;
105910099Sjkh	t.start_index = istart;
106010099Sjkh	t.end_track = tend;
106110099Sjkh	t.end_index = iend;
106213985Sache
106310099Sjkh	return ioctl (fd, CDIOCPLAYTRACKS, &t);
106410099Sjkh}
106510099Sjkh
1066227225Sedstatic int
1067227225Sedplay_blocks(int blk, int len)
106810099Sjkh{
106913985Sache	struct ioc_play_blocks  t;
107010099Sjkh
107110099Sjkh	t.blk = blk;
107210099Sjkh	t.len = len;
107313985Sache
107410099Sjkh	return ioctl (fd, CDIOCPLAYBLOCKS, &t);
107510099Sjkh}
107610099Sjkh
1077227225Sedstatic int
1078227225Sedsetvol(int left, int right)
107910099Sjkh{
108013985Sache	struct ioc_vol  v;
108110099Sjkh
1082180507Sgahr	left  = left  < 0 ? 0 : left  > 255 ? 255 : left;
1083180507Sgahr	right = right < 0 ? 0 : right > 255 ? 255 : right;
1084180507Sgahr
108513985Sache	v.vol[0] = left;
108613985Sache	v.vol[1] = right;
108710099Sjkh	v.vol[2] = 0;
108810099Sjkh	v.vol[3] = 0;
108913985Sache
109010099Sjkh	return ioctl (fd, CDIOCSETVOL, &v);
109110099Sjkh}
109210099Sjkh
1093227225Sedstatic int
1094227225Sedread_toc_entrys(int len)
109510099Sjkh{
109610099Sjkh	struct ioc_read_toc_entry t;
109710099Sjkh
109813884Sache	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
109913737Sache	t.starting_track = 0;
110010099Sjkh	t.data_len = len;
110110099Sjkh	t.data = toc_buffer;
110213985Sache
110313985Sache	return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
110410099Sjkh}
110510099Sjkh
1106227225Sedstatic int
1107227225Sedplay_msf(int start_m, int start_s, int start_f,
110813985Sache	int end_m, int end_s, int end_f)
110910099Sjkh{
111087573Smikeh	struct ioc_play_msf a;
111110099Sjkh
111210099Sjkh	a.start_m = start_m;
111310099Sjkh	a.start_s = start_s;
111410099Sjkh	a.start_f = start_f;
111510099Sjkh	a.end_m = end_m;
111610099Sjkh	a.end_s = end_s;
111710099Sjkh	a.end_f = end_f;
111813985Sache
111910099Sjkh	return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
112010099Sjkh}
112110099Sjkh
1122227225Sedstatic int
1123227225Sedstatus(int *trk, int *min, int *sec, int *frame)
112410099Sjkh{
112510099Sjkh	struct ioc_read_subchannel s;
112610099Sjkh	struct cd_sub_channel_info data;
112713884Sache	u_char mm, ss, ff;
112810099Sjkh
112910099Sjkh	bzero (&s, sizeof (s));
113010099Sjkh	s.data = &data;
113110099Sjkh	s.data_len = sizeof (data);
113213884Sache	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
113310099Sjkh	s.data_format = CD_CURRENT_POSITION;
113413985Sache
113510099Sjkh	if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
113610099Sjkh		return -1;
113713985Sache
113810099Sjkh	*trk = s.data->what.position.track_number;
113913884Sache	if (msf) {
114013884Sache		*min = s.data->what.position.reladdr.msf.minute;
114113884Sache		*sec = s.data->what.position.reladdr.msf.second;
114213884Sache		*frame = s.data->what.position.reladdr.msf.frame;
114313884Sache	} else {
114413884Sache		lba2msf(ntohl(s.data->what.position.reladdr.lba),
114513884Sache			&mm, &ss, &ff);
114613884Sache		*min = mm;
114713884Sache		*sec = ss;
114813884Sache		*frame = ff;
114913884Sache	}
115013985Sache
115110099Sjkh	return s.data->header.audio_status;
115210099Sjkh}
115310099Sjkh
1154227225Sedstatic const char *
1155201608Sdwmalonecdcontrol_prompt(void)
115610099Sjkh{
115750039Smdodd	return ("cdcontrol> ");
115850039Smdodd}
115950039Smdodd
1160227225Sedstatic char *
1161227225Sedinput(int *cmd)
116250039Smdodd{
116350039Smdodd#define MAXLINE 80
116450039Smdodd	static EditLine *el = NULL;
116550039Smdodd	static History *hist = NULL;
116684261Sobrien	HistEvent he;
116750039Smdodd	static char buf[MAXLINE];
116850039Smdodd	int num = 0;
116950071Smdodd	int len;
117050039Smdodd	const char *bp = NULL;
117110099Sjkh	char *p;
117210099Sjkh
117310099Sjkh	do {
117450039Smdodd		if (verbose) {
117550039Smdodd			if (!el) {
117684261Sobrien				el = el_init("cdcontrol", stdin, stdout,
117784261Sobrien				    stderr);
117850039Smdodd				hist = history_init();
1179151471Sstefanf				history(hist, &he, H_SETSIZE, 100);
118050039Smdodd				el_set(el, EL_HIST, history, hist);
118150039Smdodd				el_set(el, EL_EDITOR, "emacs");
118250039Smdodd				el_set(el, EL_PROMPT, cdcontrol_prompt);
118350039Smdodd				el_set(el, EL_SIGNAL, 1);
118450042Smdodd				el_source(el, NULL);
118550039Smdodd			}
118663070Smckay			if ((bp = el_gets(el, &num)) == NULL || num == 0) {
118763070Smckay				*cmd = CMD_QUIT;
118863070Smckay				fprintf (stderr, "\r\n");
118950039Smdodd				return (0);
119063070Smckay			}
119150039Smdodd
119250071Smdodd			len = (num > MAXLINE) ? MAXLINE : num;
119350071Smdodd			memcpy(buf, bp, len);
119450071Smdodd			buf[len] = 0;
119584261Sobrien			history(hist, &he, H_ENTER, bp);
119650039Smdodd#undef MAXLINE
119750039Smdodd
119850039Smdodd		} else {
119950039Smdodd			if (! fgets (buf, sizeof (buf), stdin)) {
120050039Smdodd				*cmd = CMD_QUIT;
120150039Smdodd				fprintf (stderr, "\r\n");
120250039Smdodd				return (0);
120350039Smdodd			}
120410099Sjkh		}
120510099Sjkh		p = parse (buf, cmd);
120610099Sjkh	} while (! p);
120710099Sjkh	return (p);
120810099Sjkh}
120910099Sjkh
1210227225Sedstatic char *
1211227225Sedparse(char *buf, int *cmd)
121210099Sjkh{
121310099Sjkh	struct cmdtab *c;
121410099Sjkh	char *p;
121587568Smikeh	unsigned int len;
121610099Sjkh
121713985Sache	for (p=buf; isspace (*p); p++)
121813985Sache		continue;
121910099Sjkh
122013985Sache	if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
122113985Sache		*cmd = CMD_PLAY;
122213985Sache		return (p);
122377168Skris	} else if (*p == '+') {
122477168Skris		*cmd = CMD_NEXT;
122577168Skris		return (p + 1);
122677168Skris	} else if (*p == '-') {
122777168Skris		*cmd = CMD_PREVIOUS;
122877168Skris		return (p + 1);
122913985Sache	}
123010099Sjkh
123113985Sache	for (buf = p; *p && ! isspace (*p); p++)
123213985Sache		continue;
123396214Smaxim
123413985Sache	len = p - buf;
123510099Sjkh	if (! len)
123610099Sjkh		return (0);
123713985Sache
123887573Smikeh	if (*p) {		/* It must be a spacing character! */
123913985Sache		char *q;
124013985Sache
124113985Sache		*p++ = 0;
124213985Sache		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
124313985Sache			continue;
124413985Sache		*q = 0;
124513985Sache	}
124613985Sache
124710099Sjkh	*cmd = -1;
124810099Sjkh	for (c=cmdtab; c->name; ++c) {
124913985Sache		/* Is it an exact match? */
125013985Sache		if (! strcasecmp (buf, c->name)) {
125113985Sache  			*cmd = c->command;
125213985Sache  			break;
125313985Sache  		}
125413985Sache
125513985Sache		/* Try short hand forms then... */
125613985Sache		if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
125713985Sache			if (*cmd != -1 && *cmd != c->command) {
125829103Scharnier				warnx("ambiguous command");
125913985Sache				return (0);
126013985Sache			}
126110099Sjkh			*cmd = c->command;
126213985Sache  		}
126313985Sache	}
126410099Sjkh
126510099Sjkh	if (*cmd == -1) {
126629103Scharnier		warnx("invalid command, enter ``help'' for commands");
126710099Sjkh		return (0);
126810099Sjkh	}
126913985Sache
127013985Sache	while (isspace (*p))
127113985Sache		p++;
127210099Sjkh	return p;
127310099Sjkh}
127410099Sjkh
1275227225Sedstatic int
1276227225Sedopen_cd(void)
127710099Sjkh{
127854164Sjoe	char devbuf[MAXPATHLEN];
1279127714Sdwmalone	const char *dev;
128010099Sjkh
128110099Sjkh	if (fd > -1)
128210099Sjkh		return (1);
128313985Sache
1284122855Seivind	if (cdname) {
1285122855Seivind	    if (*cdname == '/') {
1286122855Seivind		    snprintf (devbuf, MAXPATHLEN, "%s", cdname);
1287122855Seivind	    } else {
1288122855Seivind		    snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
1289122855Seivind	    }
1290127714Sdwmalone	    fd = open (dev = devbuf, O_RDONLY);
129161105Smsmith	} else {
1292127714Sdwmalone	    fd = open(dev = "/dev/cdrom", O_RDONLY);
1293122855Seivind	    if (fd < 0 && errno == ENOENT)
1294127714Sdwmalone		fd = open(dev = "/dev/cd0", O_RDONLY);
1295122855Seivind	    if (fd < 0 && errno == ENOENT)
1296127714Sdwmalone		fd = open(dev = "/dev/acd0", O_RDONLY);
129754164Sjoe	}
129813985Sache
129910099Sjkh	if (fd < 0) {
130013985Sache		if (errno == ENXIO) {
130113985Sache			/*  ENXIO has an overloaded meaning here.
130213985Sache			 *  The original "Device not configured" should
130313985Sache			 *  be interpreted as "No disc in drive %s". */
1304127714Sdwmalone			warnx("no disc in drive %s", dev);
130513985Sache			return (0);
130610099Sjkh		}
1307127714Sdwmalone		err(1, "%s", dev);
130810099Sjkh	}
130910099Sjkh	return (1);
131010099Sjkh}
1311