cdcontrol.c revision 99800
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.
1910099Sjkh */
2013985Sache
2129103Scharnier#ifndef lint
2229103Scharnierstatic const char rcsid[] =
2350479Speter  "$FreeBSD: head/usr.sbin/cdcontrol/cdcontrol.c 99800 2002-07-11 18:31:16Z alfred $";
2429103Scharnier#endif /* not lint */
2529103Scharnier
2677168Skris#include <sys/cdio.h>
2796213Smaxim#include <sys/cdrio.h>
2877168Skris#include <sys/file.h>
2977168Skris#include <sys/ioctl.h>
3077168Skris#include <sys/param.h>
3190868Smike#include <arpa/inet.h>
3213985Sache#include <ctype.h>
3329103Scharnier#include <err.h>
3429103Scharnier#include <errno.h>
3577168Skris#include <histedit.h>
3696213Smaxim#include <limits.h>
3769793Sobrien#include <paths.h>
3810099Sjkh#include <stdio.h>
3910099Sjkh#include <stdlib.h>
4010099Sjkh#include <string.h>
4110099Sjkh#include <unistd.h>
4277168Skris#include <vis.h>
4310099Sjkh
4413884Sache#define VERSION "2.0"
4510099Sjkh
4687573Smikeh#define ASTS_INVALID	0x00  /* Audio status byte not valid */
4787573Smikeh#define ASTS_PLAYING	0x11  /* Audio play operation in progress */
4887573Smikeh#define ASTS_PAUSED	0x12  /* Audio play operation paused */
4987573Smikeh#define ASTS_COMPLETED	0x13  /* Audio play operation successfully completed */
5087573Smikeh#define ASTS_ERROR	0x14  /* Audio play operation stopped due to error */
5187573Smikeh#define ASTS_VOID	0x15  /* No current audio status to return */
5210099Sjkh
5313985Sache#ifndef DEFAULT_CD_DRIVE
5413985Sache#  define DEFAULT_CD_DRIVE  "/dev/cd0c"
5513985Sache#endif
5613985Sache
5713985Sache#ifndef DEFAULT_CD_PARTITION
5813985Sache#  define DEFAULT_CD_PARTITION  "c"
5913985Sache#endif
6013985Sache
6187573Smikeh#define CMD_DEBUG	1
6287573Smikeh#define CMD_EJECT	2
6387573Smikeh#define CMD_HELP	3
6487573Smikeh#define CMD_INFO	4
6587573Smikeh#define CMD_PAUSE	5
6687573Smikeh#define CMD_PLAY	6
6787573Smikeh#define CMD_QUIT	7
6887573Smikeh#define CMD_RESUME	8
6987573Smikeh#define CMD_STOP	9
7087573Smikeh#define CMD_VOLUME	10
7187573Smikeh#define CMD_CLOSE	11
7287573Smikeh#define CMD_RESET	12
7387573Smikeh#define CMD_SET		13
7487573Smikeh#define CMD_STATUS	14
7587573Smikeh#define CMD_CDID	15
7687573Smikeh#define CMD_NEXT	16
7787573Smikeh#define CMD_PREVIOUS	17
7896213Smaxim#define CMD_SPEED	18
7987573Smikeh#define STATUS_AUDIO	0x1
8087573Smikeh#define STATUS_MEDIA	0x2
8187573Smikeh#define STATUS_VOLUME	0x4
8213985Sache
8310099Sjkhstruct cmdtab {
8410099Sjkh	int command;
8587568Smikeh	const char *name;
8687568Smikeh	unsigned min;
8787568Smikeh	const char *args;
8810099Sjkh} cmdtab[] = {
8987573Smikeh{ CMD_CLOSE,	"close",	1, "" },
9087573Smikeh{ CMD_DEBUG,	"debug",	1, "on | off" },
9187573Smikeh{ CMD_EJECT,	"eject",	1, "" },
9287573Smikeh{ CMD_HELP,	"?",		1, 0 },
9387573Smikeh{ CMD_HELP,	"help",		1, "" },
9487573Smikeh{ CMD_INFO,	"info",		1, "" },
9587573Smikeh{ CMD_NEXT,	"next",		1, "" },
9687573Smikeh{ CMD_PAUSE,	"pause",	2, "" },
9787573Smikeh{ CMD_PLAY,	"play",		1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
9887573Smikeh{ CMD_PLAY,	"play",		1, "track1[.index1] [track2[.index2]]" },
9987573Smikeh{ CMD_PLAY,	"play",		1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
10087573Smikeh{ CMD_PLAY,	"play",		1, "[#block [len]]" },
10187573Smikeh{ CMD_PREVIOUS,	"previous",	2, "" },
10287573Smikeh{ CMD_QUIT,	"quit",		1, "" },
10387573Smikeh{ CMD_RESET,	"reset",	4, "" },
10487573Smikeh{ CMD_RESUME,	"resume",	1, "" },
10587573Smikeh{ CMD_SET,	"set",		2, "msf | lba" },
10687573Smikeh{ CMD_STATUS,	"status",	1, "[audio | media | volume]" },
10787573Smikeh{ CMD_STOP,	"stop",		3, "" },
10887573Smikeh{ CMD_VOLUME,	"volume",	1,
10987573Smikeh      "<l> <r> | left | right | mute | mono | stereo" },
11087573Smikeh{ CMD_CDID,	"cdid",		2, "" },
11196213Smaxim{ CMD_SPEED,	"speed",	2, "speed" },
11287573Smikeh{ 0,		NULL,		0, NULL }
11310099Sjkh};
11410099Sjkh
11587573Smikehstruct cd_toc_entry	toc_buffer[100];
11610099Sjkh
11787573Smikehconst char	*cdname;
11887573Smikehint		fd = -1;
11987573Smikehint		verbose = 1;
12087573Smikehint		msf = 1;
12110099Sjkh
12299800Salfredint		 setvol(int, int);
12399800Salfredint		 read_toc_entrys(int);
12499800Salfredint		 play_msf(int, int, int, int, int, int);
12599800Salfredint		 play_track(int, int, int, int);
12699800Salfredint		 get_vol(int *, int *);
12799800Salfredint		 status(int *, int *, int *, int *);
12899800Salfredint		 open_cd(void);
12999800Salfredint		 next_prev(char *arg, int);
13099800Salfredint		 play(char *arg);
13199800Salfredint		 info(char *arg);
13299800Salfredint		 cdid(void);
13399800Salfredint		 pstatus(char *arg);
13499800Salfredchar		*input(int *);
13599800Salfredvoid		 prtrack(struct cd_toc_entry *e, int lastflag);
13699800Salfredvoid		 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
13799800Salfredunsigned int	 msf2lba(u_char m, u_char s, u_char f);
13899800Salfredint		 play_blocks(int blk, int len);
13999800Salfredint		 run(int cmd, char *arg);
14099800Salfredchar		*parse(char *buf, int *cmd);
14199800Salfredvoid		 help(void);
14299800Salfredvoid		 usage(void);
14399800Salfredchar		*use_cdrom_instead(const char *);
14499800Salfred__const char	*strstatus(int);
14599800Salfredstatic u_int	 dbprog_discid(void);
14699800Salfred__const char	*cdcontrol_prompt(void);
14710099Sjkh
14810099Sjkhvoid help ()
14910099Sjkh{
15010099Sjkh	struct cmdtab *c;
15187568Smikeh	const char *s;
15287568Smikeh	char n;
15313985Sache	int i;
15410099Sjkh
15510099Sjkh	for (c=cmdtab; c->name; ++c) {
15610099Sjkh		if (! c->args)
15710099Sjkh			continue;
15813985Sache		printf("\t");
15913985Sache		for (i = c->min, s = c->name; *s; s++, i--) {
16013985Sache			if (i > 0)
16113985Sache				n = toupper(*s);
16213985Sache			else
16313985Sache				n = *s;
16413985Sache			putchar(n);
16513985Sache		}
16610099Sjkh		if (*c->args)
16710099Sjkh			printf (" %s", c->args);
16810099Sjkh		printf ("\n");
16910099Sjkh	}
17013985Sache	printf ("\n\tThe word \"play\" is not required for the play commands.\n");
17113985Sache	printf ("\tThe plain target address is taken as a synonym for play.\n");
17210099Sjkh}
17310099Sjkh
17410099Sjkhvoid usage ()
17510099Sjkh{
17643479Sbillf	fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
17710099Sjkh	exit (1);
17810099Sjkh}
17910099Sjkh
18087568Smikehchar *use_cdrom_instead(const char *old_envvar)
18171122Sjoe{
18271122Sjoe	char *device;
18371122Sjoe
18471122Sjoe	device = getenv(old_envvar);
18571122Sjoe	if (device)
18671122Sjoe		warnx("%s environment variable deprecated, "
18771122Sjoe		    "please use CDROM in the future.", old_envvar);
18871122Sjoe	return device;
18971122Sjoe}
19071122Sjoe
19171122Sjoe
19210099Sjkhint main (int argc, char **argv)
19310099Sjkh{
19410099Sjkh	int cmd;
19510099Sjkh	char *arg;
19610099Sjkh
19710099Sjkh	for (;;) {
19810099Sjkh		switch (getopt (argc, argv, "svhf:")) {
19910099Sjkh		case EOF:
20010099Sjkh			break;
20110099Sjkh		case 's':
20210099Sjkh			verbose = 0;
20310099Sjkh			continue;
20410099Sjkh		case 'v':
20510099Sjkh			verbose = 2;
20610099Sjkh			continue;
20710099Sjkh		case 'f':
20810099Sjkh			cdname = optarg;
20910099Sjkh			continue;
21010099Sjkh		case 'h':
21110099Sjkh		default:
21210099Sjkh			usage ();
21310099Sjkh		}
21410099Sjkh		break;
21510099Sjkh	}
21610099Sjkh	argc -= optind;
21710099Sjkh	argv += optind;
21810099Sjkh
21913985Sache	if (argc > 0 && ! strcasecmp (*argv, "help"))
22010099Sjkh		usage ();
22110099Sjkh
22210099Sjkh	if (! cdname) {
22370149Sdes		cdname = getenv("CDROM");
22470149Sdes	}
22570149Sdes
22675324Sjoe	if (! cdname)
22775324Sjoe		cdname = use_cdrom_instead("MUSIC_CD");
22875324Sjoe	if (! cdname)
22975324Sjoe		cdname = use_cdrom_instead("CD_DRIVE");
23075324Sjoe	if (! cdname)
23175324Sjoe		cdname = use_cdrom_instead("DISC");
23275324Sjoe	if (! cdname)
23375324Sjoe		cdname = use_cdrom_instead("CDPLAY");
23475324Sjoe
23570149Sdes	if (! cdname) {
23613985Sache		cdname = DEFAULT_CD_DRIVE;
23729103Scharnier		warnx("no CD device name specified, defaulting to %s", cdname);
23810099Sjkh	}
23910099Sjkh
24010099Sjkh	if (argc > 0) {
24110099Sjkh		char buf[80], *p;
24210099Sjkh		int len;
24310099Sjkh
24413985Sache		for (p=buf; argc-->0; ++argv) {
24510099Sjkh			len = strlen (*argv);
24613985Sache
24710099Sjkh			if (p + len >= buf + sizeof (buf) - 1)
24810099Sjkh				usage ();
24913985Sache
25010099Sjkh			if (p > buf)
25110099Sjkh				*p++ = ' ';
25213985Sache
25310099Sjkh			strcpy (p, *argv);
25410099Sjkh			p += len;
25510099Sjkh		}
25610099Sjkh		*p = 0;
25710099Sjkh		arg = parse (buf, &cmd);
25813985Sache		return (run (cmd, arg));
25910099Sjkh	}
26010099Sjkh
26110099Sjkh	if (verbose == 1)
26210099Sjkh		verbose = isatty (0);
26313985Sache
26410099Sjkh	if (verbose) {
26513985Sache		printf ("Compact Disc Control utility, version %s\n", VERSION);
26610099Sjkh		printf ("Type `?' for command list\n\n");
26710099Sjkh	}
26810099Sjkh
26910099Sjkh	for (;;) {
27010099Sjkh		arg = input (&cmd);
27110099Sjkh		if (run (cmd, arg) < 0) {
27210099Sjkh			if (verbose)
27329103Scharnier				warn(NULL);
27410099Sjkh			close (fd);
27510099Sjkh			fd = -1;
27610099Sjkh		}
27710099Sjkh		fflush (stdout);
27810099Sjkh	}
27910099Sjkh}
28010099Sjkh
28110099Sjkhint run (int cmd, char *arg)
28210099Sjkh{
28396213Smaxim	long speed;
28410099Sjkh	int l, r, rc;
28596213Smaxim	char *ep;
28610099Sjkh
28710099Sjkh	switch (cmd) {
28813985Sache
28910099Sjkh	case CMD_QUIT:
29010099Sjkh		exit (0);
29110099Sjkh
29213985Sache	case CMD_INFO:
29313985Sache		if (fd < 0 && ! open_cd ())
29413985Sache			return (0);
29510099Sjkh
29610099Sjkh		return info (arg);
29710099Sjkh
29863091Sjoe	case CMD_CDID:
29963091Sjoe		if (fd < 0 && ! open_cd ())
30063091Sjoe			return (0);
30163091Sjoe
30263091Sjoe		return cdid ();
30363091Sjoe
30413884Sache	case CMD_STATUS:
30513985Sache		if (fd < 0 && ! open_cd ())
30613985Sache			return (0);
30713985Sache
30813884Sache		return pstatus (arg);
30913884Sache
31077168Skris	case CMD_NEXT:
31177168Skris	case CMD_PREVIOUS:
31277168Skris		if (fd < 0 && ! open_cd ())
31377168Skris			return (0);
31477168Skris
31577168Skris		while (isspace (*arg))
31677168Skris			arg++;
31777168Skris
31877168Skris		return next_prev (arg, cmd);
31977168Skris
32010099Sjkh	case CMD_PAUSE:
32113985Sache		if (fd < 0 && ! open_cd ())
32213985Sache			return (0);
32313985Sache
32410099Sjkh		return ioctl (fd, CDIOCPAUSE);
32510099Sjkh
32610099Sjkh	case CMD_RESUME:
32713985Sache		if (fd < 0 && ! open_cd ())
32813985Sache			return (0);
32913985Sache
33010099Sjkh		return ioctl (fd, CDIOCRESUME);
33110099Sjkh
33210099Sjkh	case CMD_STOP:
33313985Sache		if (fd < 0 && ! open_cd ())
33413985Sache			return (0);
33510099Sjkh
33613985Sache		rc = ioctl (fd, CDIOCSTOP);
33713985Sache
33813985Sache		(void) ioctl (fd, CDIOCALLOW);
33913985Sache
34013985Sache		return (rc);
34113985Sache
34213884Sache	case CMD_RESET:
34313985Sache		if (fd < 0 && ! open_cd ())
34413985Sache			return (0);
34513985Sache
34613884Sache		rc = ioctl (fd, CDIOCRESET);
34713884Sache		if (rc < 0)
34813884Sache			return rc;
34913884Sache		close(fd);
35013884Sache		fd = -1;
35113884Sache		return (0);
35213884Sache
35310099Sjkh	case CMD_DEBUG:
35413985Sache		if (fd < 0 && ! open_cd ())
35513985Sache			return (0);
35613985Sache
35713985Sache		if (! strcasecmp (arg, "on"))
35810099Sjkh			return ioctl (fd, CDIOCSETDEBUG);
35913985Sache
36013985Sache		if (! strcasecmp (arg, "off"))
36110099Sjkh			return ioctl (fd, CDIOCCLRDEBUG);
36213985Sache
36329103Scharnier		warnx("invalid command arguments");
36413985Sache
36510099Sjkh		return (0);
36610099Sjkh
36710099Sjkh	case CMD_EJECT:
36813985Sache		if (fd < 0 && ! open_cd ())
36913985Sache			return (0);
37013985Sache
37110099Sjkh		(void) ioctl (fd, CDIOCALLOW);
37210099Sjkh		rc = ioctl (fd, CDIOCEJECT);
37310099Sjkh		if (rc < 0)
37410099Sjkh			return (rc);
37513865Sache		return (0);
37613865Sache
37713985Sache	case CMD_CLOSE:
37813985Sache		if (fd < 0 && ! open_cd ())
37913985Sache			return (0);
38013985Sache
38113985Sache		(void) ioctl (fd, CDIOCALLOW);
38213865Sache		rc = ioctl (fd, CDIOCCLOSE);
38313865Sache		if (rc < 0)
38413865Sache			return (rc);
38513865Sache		close(fd);
38610099Sjkh		fd = -1;
38710099Sjkh		return (0);
38810099Sjkh
38910099Sjkh	case CMD_PLAY:
39013985Sache		if (fd < 0 && ! open_cd ())
39113985Sache			return (0);
39213985Sache
39313985Sache		while (isspace (*arg))
39413985Sache			arg++;
39513985Sache
39610099Sjkh		return play (arg);
39710099Sjkh
39813884Sache	case CMD_SET:
39913985Sache		if (! strcasecmp (arg, "msf"))
40013884Sache			msf = 1;
40113985Sache		else if (! strcasecmp (arg, "lba"))
40213884Sache			msf = 0;
40313884Sache		else
40429103Scharnier			warnx("invalid command arguments");
40513884Sache		return (0);
40613884Sache
40710099Sjkh	case CMD_VOLUME:
40813985Sache		if (fd < 0 && !open_cd ())
40913985Sache			return (0);
41010099Sjkh
41113985Sache		if (! strncasecmp (arg, "left", strlen(arg)))
41210099Sjkh			return ioctl (fd, CDIOCSETLEFT);
41313985Sache
41413985Sache		if (! strncasecmp (arg, "right", strlen(arg)))
41510099Sjkh			return ioctl (fd, CDIOCSETRIGHT);
41613985Sache
41713985Sache		if (! strncasecmp (arg, "mono", strlen(arg)))
41810099Sjkh			return ioctl (fd, CDIOCSETMONO);
41913985Sache
42013985Sache		if (! strncasecmp (arg, "stereo", strlen(arg)))
42110099Sjkh			return ioctl (fd, CDIOCSETSTERIO);
42210099Sjkh
42313985Sache		if (! strncasecmp (arg, "mute", strlen(arg)))
42413985Sache			return ioctl (fd, CDIOCSETMUTE);
42513985Sache
42610099Sjkh		if (2 != sscanf (arg, "%d %d", &l, &r)) {
42729103Scharnier			warnx("invalid command arguments");
42810099Sjkh			return (0);
42910099Sjkh		}
43013985Sache
43110099Sjkh		return setvol (l, r);
43213985Sache
43396213Smaxim	case CMD_SPEED:
43496213Smaxim		if (fd < 0 && ! open_cd ())
43596213Smaxim			return (0);
43696213Smaxim
43796213Smaxim		errno = 0;
43896213Smaxim		speed = strtol(arg, &ep, 10);
43996213Smaxim		if (*ep || ep == arg || speed <= 0 || speed > INT_MAX ||
44096213Smaxim		    errno != 0) {
44196213Smaxim			warnx("invalid command arguments %s", arg);
44296213Smaxim			return (0);
44396213Smaxim		}
44496213Smaxim		return ioctl(fd, CDRIOCREADSPEED, &speed);
44596213Smaxim
44613985Sache	default:
44713985Sache	case CMD_HELP:
44813985Sache		help ();
44913985Sache		return (0);
45013985Sache
45110099Sjkh	}
45210099Sjkh}
45310099Sjkh
45410099Sjkhint play (char *arg)
45510099Sjkh{
45610099Sjkh	struct ioc_toc_header h;
45787568Smikeh	unsigned int n;
45887568Smikeh	int rc, start, end = 0, istart = 1, iend = 1;
45910099Sjkh
46010099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
46113985Sache
46210099Sjkh	if (rc < 0)
46310099Sjkh		return (rc);
46410099Sjkh
46510099Sjkh	n = h.ending_track - h.starting_track + 1;
46610099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
46713985Sache
46810099Sjkh	if (rc < 0)
46910099Sjkh		return (rc);
47010099Sjkh
47113989Sache	if (! arg || ! *arg) {
47213985Sache		/* Play the whole disc */
47313989Sache		if (msf)
47413989Sache			return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
47513989Sache							toc_buffer[n].addr.msf.second,
47613989Sache							toc_buffer[n].addr.msf.frame));
47713989Sache		else
47813989Sache			return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
47913989Sache	}
48010099Sjkh
48110099Sjkh	if (strchr (arg, '#')) {
48213985Sache		/* Play block #blk [ len ] */
48310099Sjkh		int blk, len = 0;
48410099Sjkh
48510099Sjkh		if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
48613985Sache		    1 != sscanf (arg, "#%d", &blk))
48713985Sache			goto Clean_up;
48813985Sache
48913989Sache		if (len == 0) {
49013989Sache			if (msf)
49113989Sache				len = msf2lba (toc_buffer[n].addr.msf.minute,
49213989Sache					       toc_buffer[n].addr.msf.second,
49313989Sache					       toc_buffer[n].addr.msf.frame) - blk;
49413989Sache			else
49513989Sache				len = ntohl(toc_buffer[n].addr.lba) - blk;
49613989Sache		}
49710099Sjkh		return play_blocks (blk, len);
49810099Sjkh	}
49910099Sjkh
50010099Sjkh	if (strchr (arg, ':')) {
50110099Sjkh		/*
50210099Sjkh		 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
50313985Sache		 *
50413985Sache		 * Will now also undestand timed addresses relative
50513985Sache		 * to the beginning of a track in the form...
50613985Sache		 *
50713985Sache		 *      tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
50810099Sjkh		 */
50913985Sache		unsigned tr1, tr2;
51013985Sache		unsigned m1, m2, s1, s2, f1, f2;
51113989Sache		unsigned char tm, ts, tf;
51210099Sjkh
51313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
51413985Sache		if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
51513985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
51613985Sache			goto Play_Relative_Addresses;
51713985Sache
51813985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
51913985Sache		if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
52013985Sache		    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
52113985Sache			goto Play_Relative_Addresses;
52213985Sache
52313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
52413985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
52513985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
52613985Sache			goto Play_Relative_Addresses;
52713985Sache
52813985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
52913985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
53013985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
53113985Sache			goto Play_Relative_Addresses;
53213985Sache
53313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53413985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
53513985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2))
53613985Sache			goto Play_Relative_Addresses;
53713985Sache
53813985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53913985Sache		if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
54013985Sache		    &tr1, &m1, &s1, &m2, &s2, &f2))
54113985Sache			goto Play_Relative_Addresses;
54213985Sache
54313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54413985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
54513985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2))
54613985Sache			goto Play_Relative_Addresses;
54713985Sache
54813985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54913985Sache		if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
55013985Sache			goto Play_Relative_Addresses;
55113985Sache
55213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55313985Sache		if (5 == sscanf (arg, "%d %d:%d %d %d",
55413985Sache		    &tr1, &m1, &s1, &tr2, &m2))
55513985Sache			goto Play_Relative_Addresses;
55613985Sache
55713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55813985Sache		if (5 == sscanf (arg, "%d %d:%d.%d %d",
55913985Sache		    &tr1, &m1, &s1, &f1, &tr2))
56013985Sache			goto Play_Relative_Addresses;
56113985Sache
56213985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56313985Sache		if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
56413985Sache			goto Play_Relative_Addresses;
56513985Sache
56613985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56713985Sache		if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
56813985Sache			goto Play_Relative_Addresses;
56913985Sache
57013985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57113985Sache		if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
57213985Sache			goto Play_Relative_Addresses;
57313985Sache
57413985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57513985Sache		goto Try_Absolute_Timed_Addresses;
57613985Sache
57713985SachePlay_Relative_Addresses:
57813985Sache		if (! tr1)
57913985Sache			tr1 = 1;
58013985Sache		else if (tr1 > n)
58113985Sache			tr1 = n;
58213985Sache
58313989Sache		if (msf) {
58413989Sache			tm = toc_buffer[tr1].addr.msf.minute;
58513989Sache			ts = toc_buffer[tr1].addr.msf.second;
58613989Sache			tf = toc_buffer[tr1].addr.msf.frame;
58713989Sache		} else
58813989Sache			lba2msf(ntohl(toc_buffer[tr1].addr.lba),
58913989Sache				&tm, &ts, &tf);
59013989Sache		if ((m1 > tm)
59113989Sache		    || ((m1 == tm)
59213989Sache		    && ((s1 > ts)
59313989Sache		    || ((s1 == ts)
59413989Sache		    && (f1 > tf))))) {
59513985Sache			printf ("Track %d is not that long.\n", tr1);
59613985Sache			return (0);
59713985Sache		}
59813985Sache
59913985Sache		tr1--;
60013985Sache
60113989Sache		f1 += tf;
60213985Sache		if (f1 >= 75) {
60313985Sache			s1 += f1 / 75;
60413985Sache			f1 %= 75;
60513985Sache		}
60613985Sache
60713989Sache		s1 += ts;
60813985Sache		if (s1 >= 60) {
60913985Sache			m1 += s1 / 60;
61013985Sache			s1 %= 60;
61113985Sache		}
61213985Sache
61313989Sache		m1 += tm;
61413985Sache
61513985Sache		if (! tr2) {
61613985Sache			if (m2 || s2 || f2) {
61713985Sache				tr2 = tr1;
61813985Sache				f2 += f1;
61913985Sache				if (f2 >= 75) {
62013985Sache					s2 += f2 / 75;
62113985Sache					f2 %= 75;
62213985Sache				}
62313985Sache
62413985Sache				s2 += s1;
62513985Sache				if (s2 > 60) {
62613985Sache					m2 += s2 / 60;
62713985Sache					s2 %= 60;
62813985Sache				}
62913985Sache
63013985Sache				m2 += m1;
63113985Sache			} else {
63213985Sache				tr2 = n;
63313989Sache				if (msf) {
63413989Sache					m2 = toc_buffer[n].addr.msf.minute;
63513989Sache					s2 = toc_buffer[n].addr.msf.second;
63613989Sache					f2 = toc_buffer[n].addr.msf.frame;
63713989Sache				} else {
63813989Sache					lba2msf(ntohl(toc_buffer[n].addr.lba),
63913989Sache						&tm, &ts, &tf);
64013989Sache					m2 = tm;
64113989Sache					s2 = ts;
64213989Sache					f2 = tf;
64313989Sache				}
64413985Sache			}
64513985Sache		} else if (tr2 > n) {
64613985Sache			tr2 = n;
64713985Sache			m2 = s2 = f2 = 0;
64813985Sache		} else {
64913985Sache			if (m2 || s2 || f2)
65013985Sache				tr2--;
65113989Sache			if (msf) {
65213989Sache				tm = toc_buffer[tr2].addr.msf.minute;
65313989Sache				ts = toc_buffer[tr2].addr.msf.second;
65413989Sache				tf = toc_buffer[tr2].addr.msf.frame;
65513989Sache			} else
65613989Sache				lba2msf(ntohl(toc_buffer[tr2].addr.lba),
65713989Sache					&tm, &ts, &tf);
65813989Sache			f2 += tf;
65913985Sache			if (f2 >= 75) {
66013985Sache				s2 += f2 / 75;
66113985Sache				f2 %= 75;
66213985Sache			}
66313985Sache
66413989Sache			s2 += ts;
66513985Sache			if (s2 > 60) {
66613985Sache				m2 += s2 / 60;
66713985Sache				s2 %= 60;
66813985Sache			}
66913985Sache
67013989Sache			m2 += tm;
67113985Sache		}
67213985Sache
67313989Sache		if (msf) {
67413989Sache			tm = toc_buffer[n].addr.msf.minute;
67513989Sache			ts = toc_buffer[n].addr.msf.second;
67613989Sache			tf = toc_buffer[n].addr.msf.frame;
67713989Sache		} else
67813989Sache			lba2msf(ntohl(toc_buffer[n].addr.lba),
67913989Sache				&tm, &ts, &tf);
68013985Sache		if ((tr2 < n)
68113989Sache		    && ((m2 > tm)
68213989Sache		    || ((m2 == tm)
68313989Sache		    && ((s2 > ts)
68413989Sache		    || ((s2 == ts)
68513989Sache		    && (f2 > tf)))))) {
68613985Sache			printf ("The playing time of the disc is not that long.\n");
68713985Sache			return (0);
68813985Sache		}
68913985Sache		return (play_msf (m1, s1, f1, m2, s2, f2));
69013985Sache
69113985SacheTry_Absolute_Timed_Addresses:
69213985Sache		if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
69313985Sache			&m1, &s1, &f1, &m2, &s2, &f2) &&
69410099Sjkh		    5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
69510099Sjkh		    5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
69610099Sjkh		    3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
69710099Sjkh		    4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
69810099Sjkh		    2 != sscanf (arg, "%d:%d", &m1, &s1))
69913985Sache			goto Clean_up;
70013985Sache
70110099Sjkh		if (m2 == 0) {
70213989Sache			if (msf) {
70313989Sache				m2 = toc_buffer[n].addr.msf.minute;
70413989Sache				s2 = toc_buffer[n].addr.msf.second;
70513989Sache				f2 = toc_buffer[n].addr.msf.frame;
70613989Sache			} else {
70713989Sache				lba2msf(ntohl(toc_buffer[n].addr.lba),
70813989Sache					&tm, &ts, &tf);
70913989Sache				m2 = tm;
71013989Sache				s2 = ts;
71113989Sache				f2 = tf;
71213989Sache			}
71310099Sjkh		}
71410099Sjkh		return play_msf (m1, s1, f1, m2, s2, f2);
71510099Sjkh	}
71610099Sjkh
71710099Sjkh	/*
71810099Sjkh	 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
71910099Sjkh	 */
72010099Sjkh	if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
72110099Sjkh	    3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
72210099Sjkh	    3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
72310099Sjkh	    2 != sscanf (arg, "%d.%d", &start, &istart) &&
72410099Sjkh	    2 != sscanf (arg, "%d%d", &start, &end) &&
72510099Sjkh	    1 != sscanf (arg, "%d", &start))
72613985Sache		goto Clean_up;
72713985Sache
72810099Sjkh	if (end == 0)
72910099Sjkh		end = n;
73013985Sache	return (play_track (start, istart, end, iend));
73113985Sache
73213985SacheClean_up:
73329103Scharnier	warnx("invalid command arguments");
73413985Sache	return (0);
73510099Sjkh}
73610099Sjkh
73777168Skrisint next_prev (char *arg, int cmd)
73877168Skris{
73977168Skris	struct ioc_toc_header h;
74077168Skris	int dir, junk, n, off, rc, trk;
74177168Skris
74277168Skris	dir = (cmd == CMD_NEXT) ? 1 : -1;
74377168Skris	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
74477168Skris	if (rc < 0)
74577168Skris		return (rc);
74677168Skris
74777168Skris	n = h.ending_track - h.starting_track + 1;
74877168Skris	rc = status (&trk, &junk, &junk, &junk);
74977168Skris	if (rc < 0)
75077168Skris		return (-1);
75177168Skris
75277168Skris	if (arg && *arg) {
75377168Skris		if (sscanf (arg, "%u", &off) != 1) {
75477168Skris		    warnx("invalid command argument");
75577168Skris		    return (0);
75677168Skris		} else
75777168Skris		    trk += off * dir;
75877168Skris	} else
75977168Skris		trk += dir;
76077168Skris
76177168Skris	if (trk > h.ending_track)
76277168Skris		trk = 1;
76377168Skris
76477168Skris	return (play_track (trk, 1, n, 1));
76577168Skris}
76677168Skris
76787568Smikehconst char *strstatus (int sts)
76810099Sjkh{
76910099Sjkh	switch (sts) {
77087573Smikeh	case ASTS_INVALID:	return ("invalid");
77187573Smikeh	case ASTS_PLAYING:	return ("playing");
77287573Smikeh	case ASTS_PAUSED:	return ("paused");
77387573Smikeh	case ASTS_COMPLETED:	return ("completed");
77487573Smikeh	case ASTS_ERROR:	return ("error");
77587573Smikeh	case ASTS_VOID:		return ("void");
77687573Smikeh	default:		return ("??");
77710099Sjkh	}
77810099Sjkh}
77910099Sjkh
78013884Sacheint pstatus (char *arg)
78110099Sjkh{
78210099Sjkh	struct ioc_vol v;
78313888Sache	struct ioc_read_subchannel ss;
78413888Sache	struct cd_sub_channel_info data;
78513884Sache	int rc, trk, m, s, f;
78632782Sjmz	int what = 0;
78777168Skris	char *p, vmcn[(4 * 15) + 1];
78810099Sjkh
78932782Sjmz	while ((p = strtok(arg, " \t"))) {
79032782Sjmz	    arg = 0;
79132782Sjmz	    if (!strncasecmp(p, "audio", strlen(p)))
79232782Sjmz		what |= STATUS_AUDIO;
79332782Sjmz	    else if (!strncasecmp(p, "media", strlen(p)))
79432782Sjmz		what |= STATUS_MEDIA;
79532782Sjmz	    else if (!strncasecmp(p, "volume", strlen(p)))
79632782Sjmz		what |= STATUS_VOLUME;
79732782Sjmz	    else {
79832782Sjmz		warnx("invalid command arguments");
79932782Sjmz		return 0;
80032782Sjmz	    }
80132782Sjmz	}
80232782Sjmz	if (!what)
80332782Sjmz	    what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
80432782Sjmz	if (what & STATUS_AUDIO) {
80532782Sjmz	    rc = status (&trk, &m, &s, &f);
80632782Sjmz	    if (rc >= 0)
80710099Sjkh		if (verbose)
80832782Sjmz		    printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
80932782Sjmz			    rc, strstatus (rc), trk, m, s, f);
81010099Sjkh		else
81132782Sjmz		    printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
81232782Sjmz	    else
81313888Sache		printf ("No current status info available\n");
81432782Sjmz	}
81532782Sjmz	if (what & STATUS_MEDIA) {
81632782Sjmz	    bzero (&ss, sizeof (ss));
81732782Sjmz	    ss.data = &data;
81832782Sjmz	    ss.data_len = sizeof (data);
81932782Sjmz	    ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
82032782Sjmz	    ss.data_format = CD_MEDIA_CATALOG;
82132782Sjmz	    rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
82232782Sjmz	    if (rc >= 0) {
82313889Sache		printf("Media catalog is %sactive",
82487573Smikeh		    ss.data->what.media_catalog.mc_valid ? "": "in");
82516736Sache		if (ss.data->what.media_catalog.mc_valid &&
82616736Sache		    ss.data->what.media_catalog.mc_number[0])
82777168Skris		{
82877168Skris		    strvisx (vmcn, ss.data->what.media_catalog.mc_number,
82977168Skris			    (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
83077168Skris		    printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
83177168Skris		}
83213889Sache		putchar('\n');
83332782Sjmz	    } else
83413888Sache		printf("No media catalog info available\n");
83532782Sjmz	}
83632782Sjmz	if (what & STATUS_VOLUME) {
83732782Sjmz	    rc = ioctl (fd, CDIOCGETVOL, &v);
83832782Sjmz	    if (rc >= 0)
83910099Sjkh		if (verbose)
84032782Sjmz		    printf ("Left volume = %d, right volume = %d\n",
84132782Sjmz			    v.vol[0], v.vol[1]);
84210099Sjkh		else
84332782Sjmz		    printf ("%d %d\n", v.vol[0], v.vol[1]);
84432782Sjmz	    else
84513888Sache		printf ("No volume level info available\n");
84632782Sjmz	}
84713884Sache	return(0);
84813884Sache}
84910099Sjkh
85063091Sjoe/*
85163091Sjoe * dbprog_sum
85263091Sjoe *	Convert an integer to its text string representation, and
85363091Sjoe *	compute its checksum.  Used by dbprog_discid to derive the
85463091Sjoe *	disc ID.
85563091Sjoe *
85663091Sjoe * Args:
85763091Sjoe *	n - The integer value.
85863091Sjoe *
85963091Sjoe * Return:
86063091Sjoe *	The integer checksum.
86163091Sjoe */
86263091Sjoestatic int
86363091Sjoedbprog_sum(int n)
86463091Sjoe{
86563091Sjoe	char	buf[12],
86663091Sjoe		*p;
86763091Sjoe	int	ret = 0;
86863091Sjoe
86963091Sjoe	/* For backward compatibility this algorithm must not change */
87063091Sjoe	sprintf(buf, "%u", n);
87163091Sjoe	for (p = buf; *p != '\0'; p++)
87263091Sjoe		ret += (*p - '0');
87363091Sjoe
87463091Sjoe	return(ret);
87563091Sjoe}
87663091Sjoe
87763091Sjoe
87863091Sjoe/*
87963091Sjoe * dbprog_discid
88063091Sjoe *	Compute a magic disc ID based on the number of tracks,
88163091Sjoe *	the length of each track, and a checksum of the string
88263091Sjoe *	that represents the offset of each track.
88363091Sjoe *
88463091Sjoe * Args:
88563091Sjoe *	s - Pointer to the curstat_t structure.
88663091Sjoe *
88763091Sjoe * Return:
88863091Sjoe *	The integer disc ID.
88963091Sjoe */
89063091Sjoestatic u_int
89163091Sjoedbprog_discid()
89263091Sjoe{
89363091Sjoe	struct	ioc_toc_header h;
89463091Sjoe	int	rc;
89563091Sjoe	int	i, ntr,
89663091Sjoe		t = 0,
89763091Sjoe		n = 0;
89863091Sjoe
89963091Sjoe	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
90063091Sjoe	if (rc < 0)
90163091Sjoe		return 0;
90263091Sjoe	ntr = h.ending_track - h.starting_track + 1;
90363091Sjoe	i = msf;
90463091Sjoe	msf = 1;
90563091Sjoe	rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
90663091Sjoe	msf = i;
90763091Sjoe	if (rc < 0)
90863091Sjoe		return 0;
90963091Sjoe	/* For backward compatibility this algorithm must not change */
91063091Sjoe	for (i = 0; i < ntr; i++) {
91163091Sjoe#define TC_MM(a) toc_buffer[a].addr.msf.minute
91263091Sjoe#define TC_SS(a) toc_buffer[a].addr.msf.second
91363091Sjoe		n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
91463091Sjoe
91563091Sjoe		t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
91687573Smikeh		    ((TC_MM(i) * 60) + TC_SS(i));
91763091Sjoe	}
91863091Sjoe
91963091Sjoe	return((n % 0xff) << 24 | t << 8 | ntr);
92063091Sjoe}
92163091Sjoe
92263091Sjoeint cdid ()
92363091Sjoe{
92463091Sjoe	u_int	id;
92563091Sjoe
92663091Sjoe	id = dbprog_discid();
92763091Sjoe	if (id)
92863091Sjoe	{
92963091Sjoe		if (verbose)
93063091Sjoe			printf ("CDID=");
93163091Sjoe		printf ("%08x\n",id);
93263091Sjoe	}
93363091Sjoe	return id ? 0 : 1;
93463091Sjoe}
93563091Sjoe
93687568Smikehint info (char *arg __unused)
93713884Sache{
93813884Sache	struct ioc_toc_header h;
93913884Sache	int rc, i, n;
94013884Sache
94110099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
94213888Sache	if (rc >= 0) {
94310099Sjkh		if (verbose)
94410099Sjkh			printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
94510099Sjkh				h.starting_track, h.ending_track, h.len);
94610099Sjkh		else
94710099Sjkh			printf ("%d %d %d\n", h.starting_track,
94810099Sjkh				h.ending_track, h.len);
94913888Sache	} else {
95029103Scharnier		warn("getting toc header");
95110099Sjkh		return (rc);
95210099Sjkh	}
95310099Sjkh
95410099Sjkh	n = h.ending_track - h.starting_track + 1;
95510099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
95610099Sjkh	if (rc < 0)
95710099Sjkh		return (rc);
95813985Sache
95910099Sjkh	if (verbose) {
96010099Sjkh		printf ("track     start  duration   block  length   type\n");
96110099Sjkh		printf ("-------------------------------------------------\n");
96210099Sjkh	}
96313985Sache
96410099Sjkh	for (i = 0; i < n; i++) {
96510099Sjkh		printf ("%5d  ", toc_buffer[i].track);
96610099Sjkh		prtrack (toc_buffer + i, 0);
96710099Sjkh	}
96813867Sache	printf ("%5d  ", toc_buffer[n].track);
96910099Sjkh	prtrack (toc_buffer + n, 1);
97010099Sjkh	return (0);
97110099Sjkh}
97210099Sjkh
97313985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
97410099Sjkh{
97587573Smikeh	lba += 150;			/* block start offset */
97687573Smikeh	lba &= 0xffffff;		/* negative lbas use only 24 bits */
97710099Sjkh	*m = lba / (60 * 75);
97810099Sjkh	lba %= (60 * 75);
97910099Sjkh	*s = lba / 75;
98010099Sjkh	*f = lba % 75;
98110099Sjkh}
98210099Sjkh
98313985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f)
98410099Sjkh{
98510099Sjkh	return (((m * 60) + s) * 75 + f) - 150;
98610099Sjkh}
98710099Sjkh
98810099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag)
98910099Sjkh{
99010099Sjkh	int block, next, len;
99110099Sjkh	u_char m, s, f;
99210099Sjkh
99313884Sache	if (msf) {
99413884Sache		/* Print track start */
99513884Sache		printf ("%2d:%02d.%02d  ", e->addr.msf.minute,
99613884Sache			e->addr.msf.second, e->addr.msf.frame);
99710099Sjkh
99813884Sache		block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
99913884Sache			e->addr.msf.frame);
100013884Sache	} else {
100113884Sache		block = ntohl(e->addr.lba);
100213884Sache		lba2msf(block, &m, &s, &f);
100313884Sache		/* Print track start */
100413884Sache		printf ("%2d:%02d.%02d  ", m, s, f);
100513884Sache	}
100610099Sjkh	if (lastflag) {
100710099Sjkh		/* Last track -- print block */
100810099Sjkh		printf ("       -  %6d       -      -\n", block);
100910099Sjkh		return;
101010099Sjkh	}
101110099Sjkh
101213884Sache	if (msf)
101313884Sache		next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
101413884Sache			e[1].addr.msf.frame);
101513884Sache	else
101613884Sache		next = ntohl(e[1].addr.lba);
101710099Sjkh	len = next - block;
101810099Sjkh	lba2msf (len, &m, &s, &f);
101910099Sjkh
102010099Sjkh	/* Print duration, block, length, type */
102110099Sjkh	printf ("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
102213985Sache		(e->control & 4) ? "data" : "audio");
102310099Sjkh}
102410099Sjkh
102510099Sjkhint play_track (int tstart, int istart, int tend, int iend)
102610099Sjkh{
102710099Sjkh	struct ioc_play_track t;
102810099Sjkh
102910099Sjkh	t.start_track = tstart;
103010099Sjkh	t.start_index = istart;
103110099Sjkh	t.end_track = tend;
103210099Sjkh	t.end_index = iend;
103313985Sache
103410099Sjkh	return ioctl (fd, CDIOCPLAYTRACKS, &t);
103510099Sjkh}
103610099Sjkh
103710099Sjkhint play_blocks (int blk, int len)
103810099Sjkh{
103913985Sache	struct ioc_play_blocks  t;
104010099Sjkh
104110099Sjkh	t.blk = blk;
104210099Sjkh	t.len = len;
104313985Sache
104410099Sjkh	return ioctl (fd, CDIOCPLAYBLOCKS, &t);
104510099Sjkh}
104610099Sjkh
104713985Sacheint setvol (int left, int right)
104810099Sjkh{
104913985Sache	struct ioc_vol  v;
105010099Sjkh
105113985Sache	v.vol[0] = left;
105213985Sache	v.vol[1] = right;
105310099Sjkh	v.vol[2] = 0;
105410099Sjkh	v.vol[3] = 0;
105513985Sache
105610099Sjkh	return ioctl (fd, CDIOCSETVOL, &v);
105710099Sjkh}
105810099Sjkh
105910099Sjkhint read_toc_entrys (int len)
106010099Sjkh{
106110099Sjkh	struct ioc_read_toc_entry t;
106210099Sjkh
106313884Sache	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
106413737Sache	t.starting_track = 0;
106510099Sjkh	t.data_len = len;
106610099Sjkh	t.data = toc_buffer;
106713985Sache
106813985Sache	return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
106910099Sjkh}
107010099Sjkh
107110099Sjkhint play_msf (int start_m, int start_s, int start_f,
107213985Sache	int end_m, int end_s, int end_f)
107310099Sjkh{
107487573Smikeh	struct ioc_play_msf a;
107510099Sjkh
107610099Sjkh	a.start_m = start_m;
107710099Sjkh	a.start_s = start_s;
107810099Sjkh	a.start_f = start_f;
107910099Sjkh	a.end_m = end_m;
108010099Sjkh	a.end_s = end_s;
108110099Sjkh	a.end_f = end_f;
108213985Sache
108310099Sjkh	return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
108410099Sjkh}
108510099Sjkh
108610099Sjkhint status (int *trk, int *min, int *sec, int *frame)
108710099Sjkh{
108810099Sjkh	struct ioc_read_subchannel s;
108910099Sjkh	struct cd_sub_channel_info data;
109013884Sache	u_char mm, ss, ff;
109110099Sjkh
109210099Sjkh	bzero (&s, sizeof (s));
109310099Sjkh	s.data = &data;
109410099Sjkh	s.data_len = sizeof (data);
109513884Sache	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
109610099Sjkh	s.data_format = CD_CURRENT_POSITION;
109713985Sache
109810099Sjkh	if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
109910099Sjkh		return -1;
110013985Sache
110110099Sjkh	*trk = s.data->what.position.track_number;
110213884Sache	if (msf) {
110313884Sache		*min = s.data->what.position.reladdr.msf.minute;
110413884Sache		*sec = s.data->what.position.reladdr.msf.second;
110513884Sache		*frame = s.data->what.position.reladdr.msf.frame;
110613884Sache	} else {
110713884Sache		lba2msf(ntohl(s.data->what.position.reladdr.lba),
110813884Sache			&mm, &ss, &ff);
110913884Sache		*min = mm;
111013884Sache		*sec = ss;
111113884Sache		*frame = ff;
111213884Sache	}
111313985Sache
111410099Sjkh	return s.data->header.audio_status;
111510099Sjkh}
111610099Sjkh
111750039Smdoddconst char *
111850039Smdoddcdcontrol_prompt()
111910099Sjkh{
112050039Smdodd	return ("cdcontrol> ");
112150039Smdodd}
112250039Smdodd
112350039Smdoddchar *
112450039Smdoddinput (int *cmd)
112550039Smdodd{
112650039Smdodd#define MAXLINE 80
112750039Smdodd	static EditLine *el = NULL;
112850039Smdodd	static History *hist = NULL;
112984261Sobrien	HistEvent he;
113050039Smdodd	static char buf[MAXLINE];
113150039Smdodd	int num = 0;
113250071Smdodd	int len;
113350039Smdodd	const char *bp = NULL;
113410099Sjkh	char *p;
113510099Sjkh
113610099Sjkh	do {
113750039Smdodd		if (verbose) {
113850039Smdodd			if (!el) {
113984261Sobrien				el = el_init("cdcontrol", stdin, stdout,
114084261Sobrien				    stderr);
114150039Smdodd				hist = history_init();
114284261Sobrien				history(hist, &he, H_EVENT, 100);
114350039Smdodd				el_set(el, EL_HIST, history, hist);
114450039Smdodd				el_set(el, EL_EDITOR, "emacs");
114550039Smdodd				el_set(el, EL_PROMPT, cdcontrol_prompt);
114650039Smdodd				el_set(el, EL_SIGNAL, 1);
114750042Smdodd				el_source(el, NULL);
114850039Smdodd			}
114963070Smckay			if ((bp = el_gets(el, &num)) == NULL || num == 0) {
115063070Smckay				*cmd = CMD_QUIT;
115163070Smckay				fprintf (stderr, "\r\n");
115250039Smdodd				return (0);
115363070Smckay			}
115450039Smdodd
115550071Smdodd			len = (num > MAXLINE) ? MAXLINE : num;
115650071Smdodd			memcpy(buf, bp, len);
115750071Smdodd			buf[len] = 0;
115884261Sobrien			history(hist, &he, H_ENTER, bp);
115950039Smdodd#undef MAXLINE
116050039Smdodd
116150039Smdodd		} else {
116250039Smdodd			if (! fgets (buf, sizeof (buf), stdin)) {
116350039Smdodd				*cmd = CMD_QUIT;
116450039Smdodd				fprintf (stderr, "\r\n");
116550039Smdodd				return (0);
116650039Smdodd			}
116710099Sjkh		}
116810099Sjkh		p = parse (buf, cmd);
116910099Sjkh	} while (! p);
117010099Sjkh	return (p);
117110099Sjkh}
117210099Sjkh
117310099Sjkhchar *parse (char *buf, int *cmd)
117410099Sjkh{
117510099Sjkh	struct cmdtab *c;
117610099Sjkh	char *p;
117787568Smikeh	unsigned int len;
117810099Sjkh
117913985Sache	for (p=buf; isspace (*p); p++)
118013985Sache		continue;
118110099Sjkh
118213985Sache	if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
118313985Sache		*cmd = CMD_PLAY;
118413985Sache		return (p);
118577168Skris	} else if (*p == '+') {
118677168Skris		*cmd = CMD_NEXT;
118777168Skris		return (p + 1);
118877168Skris	} else if (*p == '-') {
118977168Skris		*cmd = CMD_PREVIOUS;
119077168Skris		return (p + 1);
119113985Sache	}
119210099Sjkh
119313985Sache	for (buf = p; *p && ! isspace (*p); p++)
119413985Sache		continue;
119596214Smaxim
119613985Sache	len = p - buf;
119710099Sjkh	if (! len)
119810099Sjkh		return (0);
119913985Sache
120087573Smikeh	if (*p) {		/* It must be a spacing character! */
120113985Sache		char *q;
120213985Sache
120313985Sache		*p++ = 0;
120413985Sache		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
120513985Sache			continue;
120613985Sache		*q = 0;
120713985Sache	}
120813985Sache
120910099Sjkh	*cmd = -1;
121010099Sjkh	for (c=cmdtab; c->name; ++c) {
121113985Sache		/* Is it an exact match? */
121213985Sache		if (! strcasecmp (buf, c->name)) {
121313985Sache  			*cmd = c->command;
121413985Sache  			break;
121513985Sache  		}
121613985Sache
121713985Sache		/* Try short hand forms then... */
121813985Sache		if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
121913985Sache			if (*cmd != -1 && *cmd != c->command) {
122029103Scharnier				warnx("ambiguous command");
122113985Sache				return (0);
122213985Sache			}
122310099Sjkh			*cmd = c->command;
122413985Sache  		}
122513985Sache	}
122610099Sjkh
122710099Sjkh	if (*cmd == -1) {
122829103Scharnier		warnx("invalid command, enter ``help'' for commands");
122910099Sjkh		return (0);
123010099Sjkh	}
123113985Sache
123213985Sache	while (isspace (*p))
123313985Sache		p++;
123410099Sjkh	return p;
123510099Sjkh}
123610099Sjkh
123710099Sjkhint open_cd ()
123810099Sjkh{
123954164Sjoe	char devbuf[MAXPATHLEN];
124010099Sjkh
124110099Sjkh	if (fd > -1)
124210099Sjkh		return (1);
124313985Sache
124454164Sjoe	if (*cdname == '/') {
124554164Sjoe		snprintf (devbuf, MAXPATHLEN, "%s", cdname);
124661105Smsmith	} else {
124769793Sobrien		snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
124854164Sjoe	}
124913985Sache
125010099Sjkh	fd = open (devbuf, O_RDONLY);
125113985Sache
125210099Sjkh	if (fd < 0 && errno == ENOENT) {
125313985Sache		strcat (devbuf, DEFAULT_CD_PARTITION);
125410099Sjkh		fd = open (devbuf, O_RDONLY);
125510099Sjkh	}
125613985Sache
125710099Sjkh	if (fd < 0) {
125813985Sache		if (errno == ENXIO) {
125913985Sache			/*  ENXIO has an overloaded meaning here.
126013985Sache			 *  The original "Device not configured" should
126113985Sache			 *  be interpreted as "No disc in drive %s". */
126229103Scharnier			warnx("no disc in drive %s", devbuf);
126313985Sache			return (0);
126410099Sjkh		}
126529103Scharnier		err(1, "%s", devbuf);
126610099Sjkh	}
126710099Sjkh	return (1);
126810099Sjkh}
1269