cdcontrol.c revision 105421
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 105421 2002-10-18 22:03:39Z njl $";
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;
28510099Sjkh
28610099Sjkh	switch (cmd) {
28713985Sache
28810099Sjkh	case CMD_QUIT:
28910099Sjkh		exit (0);
29010099Sjkh
29113985Sache	case CMD_INFO:
29213985Sache		if (fd < 0 && ! open_cd ())
29313985Sache			return (0);
29410099Sjkh
29510099Sjkh		return info (arg);
29610099Sjkh
29763091Sjoe	case CMD_CDID:
29863091Sjoe		if (fd < 0 && ! open_cd ())
29963091Sjoe			return (0);
30063091Sjoe
30163091Sjoe		return cdid ();
30263091Sjoe
30313884Sache	case CMD_STATUS:
30413985Sache		if (fd < 0 && ! open_cd ())
30513985Sache			return (0);
30613985Sache
30713884Sache		return pstatus (arg);
30813884Sache
30977168Skris	case CMD_NEXT:
31077168Skris	case CMD_PREVIOUS:
31177168Skris		if (fd < 0 && ! open_cd ())
31277168Skris			return (0);
31377168Skris
31477168Skris		while (isspace (*arg))
31577168Skris			arg++;
31677168Skris
31777168Skris		return next_prev (arg, cmd);
31877168Skris
31910099Sjkh	case CMD_PAUSE:
32013985Sache		if (fd < 0 && ! open_cd ())
32113985Sache			return (0);
32213985Sache
32310099Sjkh		return ioctl (fd, CDIOCPAUSE);
32410099Sjkh
32510099Sjkh	case CMD_RESUME:
32613985Sache		if (fd < 0 && ! open_cd ())
32713985Sache			return (0);
32813985Sache
32910099Sjkh		return ioctl (fd, CDIOCRESUME);
33010099Sjkh
33110099Sjkh	case CMD_STOP:
33213985Sache		if (fd < 0 && ! open_cd ())
33313985Sache			return (0);
33410099Sjkh
33513985Sache		rc = ioctl (fd, CDIOCSTOP);
33613985Sache
33713985Sache		(void) ioctl (fd, CDIOCALLOW);
33813985Sache
33913985Sache		return (rc);
34013985Sache
34113884Sache	case CMD_RESET:
34213985Sache		if (fd < 0 && ! open_cd ())
34313985Sache			return (0);
34413985Sache
34513884Sache		rc = ioctl (fd, CDIOCRESET);
34613884Sache		if (rc < 0)
34713884Sache			return rc;
34813884Sache		close(fd);
34913884Sache		fd = -1;
35013884Sache		return (0);
35113884Sache
35210099Sjkh	case CMD_DEBUG:
35313985Sache		if (fd < 0 && ! open_cd ())
35413985Sache			return (0);
35513985Sache
35613985Sache		if (! strcasecmp (arg, "on"))
35710099Sjkh			return ioctl (fd, CDIOCSETDEBUG);
35813985Sache
35913985Sache		if (! strcasecmp (arg, "off"))
36010099Sjkh			return ioctl (fd, CDIOCCLRDEBUG);
36113985Sache
36229103Scharnier		warnx("invalid command arguments");
36313985Sache
36410099Sjkh		return (0);
36510099Sjkh
36610099Sjkh	case CMD_EJECT:
36713985Sache		if (fd < 0 && ! open_cd ())
36813985Sache			return (0);
36913985Sache
37010099Sjkh		(void) ioctl (fd, CDIOCALLOW);
37110099Sjkh		rc = ioctl (fd, CDIOCEJECT);
37210099Sjkh		if (rc < 0)
37310099Sjkh			return (rc);
37413865Sache		return (0);
37513865Sache
37613985Sache	case CMD_CLOSE:
37713985Sache		if (fd < 0 && ! open_cd ())
37813985Sache			return (0);
37913985Sache
38013985Sache		(void) ioctl (fd, CDIOCALLOW);
38113865Sache		rc = ioctl (fd, CDIOCCLOSE);
38213865Sache		if (rc < 0)
38313865Sache			return (rc);
38413865Sache		close(fd);
38510099Sjkh		fd = -1;
38610099Sjkh		return (0);
38710099Sjkh
38810099Sjkh	case CMD_PLAY:
38913985Sache		if (fd < 0 && ! open_cd ())
39013985Sache			return (0);
39113985Sache
39213985Sache		while (isspace (*arg))
39313985Sache			arg++;
39413985Sache
39510099Sjkh		return play (arg);
39610099Sjkh
39713884Sache	case CMD_SET:
39813985Sache		if (! strcasecmp (arg, "msf"))
39913884Sache			msf = 1;
40013985Sache		else if (! strcasecmp (arg, "lba"))
40113884Sache			msf = 0;
40213884Sache		else
40329103Scharnier			warnx("invalid command arguments");
40413884Sache		return (0);
40513884Sache
40610099Sjkh	case CMD_VOLUME:
40713985Sache		if (fd < 0 && !open_cd ())
40813985Sache			return (0);
40910099Sjkh
41013985Sache		if (! strncasecmp (arg, "left", strlen(arg)))
41110099Sjkh			return ioctl (fd, CDIOCSETLEFT);
41213985Sache
41313985Sache		if (! strncasecmp (arg, "right", strlen(arg)))
41410099Sjkh			return ioctl (fd, CDIOCSETRIGHT);
41513985Sache
41613985Sache		if (! strncasecmp (arg, "mono", strlen(arg)))
41710099Sjkh			return ioctl (fd, CDIOCSETMONO);
41813985Sache
41913985Sache		if (! strncasecmp (arg, "stereo", strlen(arg)))
42010099Sjkh			return ioctl (fd, CDIOCSETSTERIO);
42110099Sjkh
42213985Sache		if (! strncasecmp (arg, "mute", strlen(arg)))
42313985Sache			return ioctl (fd, CDIOCSETMUTE);
42413985Sache
42510099Sjkh		if (2 != sscanf (arg, "%d %d", &l, &r)) {
42629103Scharnier			warnx("invalid command arguments");
42710099Sjkh			return (0);
42810099Sjkh		}
42913985Sache
43010099Sjkh		return setvol (l, r);
43113985Sache
43296213Smaxim	case CMD_SPEED:
43396213Smaxim		if (fd < 0 && ! open_cd ())
43496213Smaxim			return (0);
43596213Smaxim
43696213Smaxim		errno = 0;
437105421Snjl		if (strcasecmp("max", arg) == 0)
438105421Snjl			speed = CDR_MAX_SPEED;
439105421Snjl		else
440105421Snjl			speed = strtol(arg, NULL, 10) * 177;
441105421Snjl		if (speed <= 0 || speed > INT_MAX) {
44296213Smaxim			warnx("invalid command arguments %s", arg);
44396213Smaxim			return (0);
44496213Smaxim		}
44596213Smaxim		return ioctl(fd, CDRIOCREADSPEED, &speed);
44696213Smaxim
44713985Sache	default:
44813985Sache	case CMD_HELP:
44913985Sache		help ();
45013985Sache		return (0);
45113985Sache
45210099Sjkh	}
45310099Sjkh}
45410099Sjkh
45510099Sjkhint play (char *arg)
45610099Sjkh{
45710099Sjkh	struct ioc_toc_header h;
45887568Smikeh	unsigned int n;
45987568Smikeh	int rc, start, end = 0, istart = 1, iend = 1;
46010099Sjkh
46110099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
46213985Sache
46310099Sjkh	if (rc < 0)
46410099Sjkh		return (rc);
46510099Sjkh
46610099Sjkh	n = h.ending_track - h.starting_track + 1;
46710099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
46813985Sache
46910099Sjkh	if (rc < 0)
47010099Sjkh		return (rc);
47110099Sjkh
47213989Sache	if (! arg || ! *arg) {
47313985Sache		/* Play the whole disc */
47413989Sache		if (msf)
47513989Sache			return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
47613989Sache							toc_buffer[n].addr.msf.second,
47713989Sache							toc_buffer[n].addr.msf.frame));
47813989Sache		else
47913989Sache			return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
48013989Sache	}
48110099Sjkh
48210099Sjkh	if (strchr (arg, '#')) {
48313985Sache		/* Play block #blk [ len ] */
48410099Sjkh		int blk, len = 0;
48510099Sjkh
48610099Sjkh		if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
48713985Sache		    1 != sscanf (arg, "#%d", &blk))
48813985Sache			goto Clean_up;
48913985Sache
49013989Sache		if (len == 0) {
49113989Sache			if (msf)
49213989Sache				len = msf2lba (toc_buffer[n].addr.msf.minute,
49313989Sache					       toc_buffer[n].addr.msf.second,
49413989Sache					       toc_buffer[n].addr.msf.frame) - blk;
49513989Sache			else
49613989Sache				len = ntohl(toc_buffer[n].addr.lba) - blk;
49713989Sache		}
49810099Sjkh		return play_blocks (blk, len);
49910099Sjkh	}
50010099Sjkh
50110099Sjkh	if (strchr (arg, ':')) {
50210099Sjkh		/*
50310099Sjkh		 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
50413985Sache		 *
50513985Sache		 * Will now also undestand timed addresses relative
50613985Sache		 * to the beginning of a track in the form...
50713985Sache		 *
50813985Sache		 *      tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
50910099Sjkh		 */
51013985Sache		unsigned tr1, tr2;
51113985Sache		unsigned m1, m2, s1, s2, f1, f2;
51213989Sache		unsigned char tm, ts, tf;
51310099Sjkh
51413985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
51513985Sache		if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
51613985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
51713985Sache			goto Play_Relative_Addresses;
51813985Sache
51913985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
52013985Sache		if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
52113985Sache		    &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
52213985Sache			goto Play_Relative_Addresses;
52313985Sache
52413985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
52513985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
52613985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
52713985Sache			goto Play_Relative_Addresses;
52813985Sache
52913985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53013985Sache		if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
53113985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
53213985Sache			goto Play_Relative_Addresses;
53313985Sache
53413985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
53513985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
53613985Sache		    &tr1, &m1, &s1, &f1, &m2, &s2))
53713985Sache			goto Play_Relative_Addresses;
53813985Sache
53913985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54013985Sache		if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
54113985Sache		    &tr1, &m1, &s1, &m2, &s2, &f2))
54213985Sache			goto Play_Relative_Addresses;
54313985Sache
54413985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
54513985Sache		if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
54613985Sache		    &tr1, &m1, &s1, &f1, &tr2, &m2))
54713985Sache			goto Play_Relative_Addresses;
54813985Sache
54913985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55013985Sache		if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
55113985Sache			goto Play_Relative_Addresses;
55213985Sache
55313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55413985Sache		if (5 == sscanf (arg, "%d %d:%d %d %d",
55513985Sache		    &tr1, &m1, &s1, &tr2, &m2))
55613985Sache			goto Play_Relative_Addresses;
55713985Sache
55813985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
55913985Sache		if (5 == sscanf (arg, "%d %d:%d.%d %d",
56013985Sache		    &tr1, &m1, &s1, &f1, &tr2))
56113985Sache			goto Play_Relative_Addresses;
56213985Sache
56313985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56413985Sache		if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
56513985Sache			goto Play_Relative_Addresses;
56613985Sache
56713985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
56813985Sache		if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
56913985Sache			goto Play_Relative_Addresses;
57013985Sache
57113985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57213985Sache		if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
57313985Sache			goto Play_Relative_Addresses;
57413985Sache
57513985Sache		tr2 = m2 = s2 = f2 = f1 = 0;
57613985Sache		goto Try_Absolute_Timed_Addresses;
57713985Sache
57813985SachePlay_Relative_Addresses:
57913985Sache		if (! tr1)
58013985Sache			tr1 = 1;
58113985Sache		else if (tr1 > n)
58213985Sache			tr1 = n;
58313985Sache
58413989Sache		if (msf) {
58513989Sache			tm = toc_buffer[tr1].addr.msf.minute;
58613989Sache			ts = toc_buffer[tr1].addr.msf.second;
58713989Sache			tf = toc_buffer[tr1].addr.msf.frame;
58813989Sache		} else
58913989Sache			lba2msf(ntohl(toc_buffer[tr1].addr.lba),
59013989Sache				&tm, &ts, &tf);
59113989Sache		if ((m1 > tm)
59213989Sache		    || ((m1 == tm)
59313989Sache		    && ((s1 > ts)
59413989Sache		    || ((s1 == ts)
59513989Sache		    && (f1 > tf))))) {
59613985Sache			printf ("Track %d is not that long.\n", tr1);
59713985Sache			return (0);
59813985Sache		}
59913985Sache
60013985Sache		tr1--;
60113985Sache
60213989Sache		f1 += tf;
60313985Sache		if (f1 >= 75) {
60413985Sache			s1 += f1 / 75;
60513985Sache			f1 %= 75;
60613985Sache		}
60713985Sache
60813989Sache		s1 += ts;
60913985Sache		if (s1 >= 60) {
61013985Sache			m1 += s1 / 60;
61113985Sache			s1 %= 60;
61213985Sache		}
61313985Sache
61413989Sache		m1 += tm;
61513985Sache
61613985Sache		if (! tr2) {
61713985Sache			if (m2 || s2 || f2) {
61813985Sache				tr2 = tr1;
61913985Sache				f2 += f1;
62013985Sache				if (f2 >= 75) {
62113985Sache					s2 += f2 / 75;
62213985Sache					f2 %= 75;
62313985Sache				}
62413985Sache
62513985Sache				s2 += s1;
62613985Sache				if (s2 > 60) {
62713985Sache					m2 += s2 / 60;
62813985Sache					s2 %= 60;
62913985Sache				}
63013985Sache
63113985Sache				m2 += m1;
63213985Sache			} else {
63313985Sache				tr2 = n;
63413989Sache				if (msf) {
63513989Sache					m2 = toc_buffer[n].addr.msf.minute;
63613989Sache					s2 = toc_buffer[n].addr.msf.second;
63713989Sache					f2 = toc_buffer[n].addr.msf.frame;
63813989Sache				} else {
63913989Sache					lba2msf(ntohl(toc_buffer[n].addr.lba),
64013989Sache						&tm, &ts, &tf);
64113989Sache					m2 = tm;
64213989Sache					s2 = ts;
64313989Sache					f2 = tf;
64413989Sache				}
64513985Sache			}
64613985Sache		} else if (tr2 > n) {
64713985Sache			tr2 = n;
64813985Sache			m2 = s2 = f2 = 0;
64913985Sache		} else {
65013985Sache			if (m2 || s2 || f2)
65113985Sache				tr2--;
65213989Sache			if (msf) {
65313989Sache				tm = toc_buffer[tr2].addr.msf.minute;
65413989Sache				ts = toc_buffer[tr2].addr.msf.second;
65513989Sache				tf = toc_buffer[tr2].addr.msf.frame;
65613989Sache			} else
65713989Sache				lba2msf(ntohl(toc_buffer[tr2].addr.lba),
65813989Sache					&tm, &ts, &tf);
65913989Sache			f2 += tf;
66013985Sache			if (f2 >= 75) {
66113985Sache				s2 += f2 / 75;
66213985Sache				f2 %= 75;
66313985Sache			}
66413985Sache
66513989Sache			s2 += ts;
66613985Sache			if (s2 > 60) {
66713985Sache				m2 += s2 / 60;
66813985Sache				s2 %= 60;
66913985Sache			}
67013985Sache
67113989Sache			m2 += tm;
67213985Sache		}
67313985Sache
67413989Sache		if (msf) {
67513989Sache			tm = toc_buffer[n].addr.msf.minute;
67613989Sache			ts = toc_buffer[n].addr.msf.second;
67713989Sache			tf = toc_buffer[n].addr.msf.frame;
67813989Sache		} else
67913989Sache			lba2msf(ntohl(toc_buffer[n].addr.lba),
68013989Sache				&tm, &ts, &tf);
68113985Sache		if ((tr2 < n)
68213989Sache		    && ((m2 > tm)
68313989Sache		    || ((m2 == tm)
68413989Sache		    && ((s2 > ts)
68513989Sache		    || ((s2 == ts)
68613989Sache		    && (f2 > tf)))))) {
68713985Sache			printf ("The playing time of the disc is not that long.\n");
68813985Sache			return (0);
68913985Sache		}
69013985Sache		return (play_msf (m1, s1, f1, m2, s2, f2));
69113985Sache
69213985SacheTry_Absolute_Timed_Addresses:
69313985Sache		if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
69413985Sache			&m1, &s1, &f1, &m2, &s2, &f2) &&
69510099Sjkh		    5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
69610099Sjkh		    5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
69710099Sjkh		    3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
69810099Sjkh		    4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
69910099Sjkh		    2 != sscanf (arg, "%d:%d", &m1, &s1))
70013985Sache			goto Clean_up;
70113985Sache
70210099Sjkh		if (m2 == 0) {
70313989Sache			if (msf) {
70413989Sache				m2 = toc_buffer[n].addr.msf.minute;
70513989Sache				s2 = toc_buffer[n].addr.msf.second;
70613989Sache				f2 = toc_buffer[n].addr.msf.frame;
70713989Sache			} else {
70813989Sache				lba2msf(ntohl(toc_buffer[n].addr.lba),
70913989Sache					&tm, &ts, &tf);
71013989Sache				m2 = tm;
71113989Sache				s2 = ts;
71213989Sache				f2 = tf;
71313989Sache			}
71410099Sjkh		}
71510099Sjkh		return play_msf (m1, s1, f1, m2, s2, f2);
71610099Sjkh	}
71710099Sjkh
71810099Sjkh	/*
71910099Sjkh	 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
72010099Sjkh	 */
72110099Sjkh	if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
72210099Sjkh	    3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
72310099Sjkh	    3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
72410099Sjkh	    2 != sscanf (arg, "%d.%d", &start, &istart) &&
72510099Sjkh	    2 != sscanf (arg, "%d%d", &start, &end) &&
72610099Sjkh	    1 != sscanf (arg, "%d", &start))
72713985Sache		goto Clean_up;
72813985Sache
72910099Sjkh	if (end == 0)
73010099Sjkh		end = n;
73113985Sache	return (play_track (start, istart, end, iend));
73213985Sache
73313985SacheClean_up:
73429103Scharnier	warnx("invalid command arguments");
73513985Sache	return (0);
73610099Sjkh}
73710099Sjkh
73877168Skrisint next_prev (char *arg, int cmd)
73977168Skris{
74077168Skris	struct ioc_toc_header h;
74177168Skris	int dir, junk, n, off, rc, trk;
74277168Skris
74377168Skris	dir = (cmd == CMD_NEXT) ? 1 : -1;
74477168Skris	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
74577168Skris	if (rc < 0)
74677168Skris		return (rc);
74777168Skris
74877168Skris	n = h.ending_track - h.starting_track + 1;
74977168Skris	rc = status (&trk, &junk, &junk, &junk);
75077168Skris	if (rc < 0)
75177168Skris		return (-1);
75277168Skris
75377168Skris	if (arg && *arg) {
75477168Skris		if (sscanf (arg, "%u", &off) != 1) {
75577168Skris		    warnx("invalid command argument");
75677168Skris		    return (0);
75777168Skris		} else
75877168Skris		    trk += off * dir;
75977168Skris	} else
76077168Skris		trk += dir;
76177168Skris
76277168Skris	if (trk > h.ending_track)
76377168Skris		trk = 1;
76477168Skris
76577168Skris	return (play_track (trk, 1, n, 1));
76677168Skris}
76777168Skris
76887568Smikehconst char *strstatus (int sts)
76910099Sjkh{
77010099Sjkh	switch (sts) {
77187573Smikeh	case ASTS_INVALID:	return ("invalid");
77287573Smikeh	case ASTS_PLAYING:	return ("playing");
77387573Smikeh	case ASTS_PAUSED:	return ("paused");
77487573Smikeh	case ASTS_COMPLETED:	return ("completed");
77587573Smikeh	case ASTS_ERROR:	return ("error");
77687573Smikeh	case ASTS_VOID:		return ("void");
77787573Smikeh	default:		return ("??");
77810099Sjkh	}
77910099Sjkh}
78010099Sjkh
78113884Sacheint pstatus (char *arg)
78210099Sjkh{
78310099Sjkh	struct ioc_vol v;
78413888Sache	struct ioc_read_subchannel ss;
78513888Sache	struct cd_sub_channel_info data;
78613884Sache	int rc, trk, m, s, f;
78732782Sjmz	int what = 0;
78877168Skris	char *p, vmcn[(4 * 15) + 1];
78910099Sjkh
79032782Sjmz	while ((p = strtok(arg, " \t"))) {
79132782Sjmz	    arg = 0;
79232782Sjmz	    if (!strncasecmp(p, "audio", strlen(p)))
79332782Sjmz		what |= STATUS_AUDIO;
79432782Sjmz	    else if (!strncasecmp(p, "media", strlen(p)))
79532782Sjmz		what |= STATUS_MEDIA;
79632782Sjmz	    else if (!strncasecmp(p, "volume", strlen(p)))
79732782Sjmz		what |= STATUS_VOLUME;
79832782Sjmz	    else {
79932782Sjmz		warnx("invalid command arguments");
80032782Sjmz		return 0;
80132782Sjmz	    }
80232782Sjmz	}
80332782Sjmz	if (!what)
80432782Sjmz	    what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
80532782Sjmz	if (what & STATUS_AUDIO) {
80632782Sjmz	    rc = status (&trk, &m, &s, &f);
80732782Sjmz	    if (rc >= 0)
80810099Sjkh		if (verbose)
80932782Sjmz		    printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
81032782Sjmz			    rc, strstatus (rc), trk, m, s, f);
81110099Sjkh		else
81232782Sjmz		    printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
81332782Sjmz	    else
81413888Sache		printf ("No current status info available\n");
81532782Sjmz	}
81632782Sjmz	if (what & STATUS_MEDIA) {
81732782Sjmz	    bzero (&ss, sizeof (ss));
81832782Sjmz	    ss.data = &data;
81932782Sjmz	    ss.data_len = sizeof (data);
82032782Sjmz	    ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
82132782Sjmz	    ss.data_format = CD_MEDIA_CATALOG;
82232782Sjmz	    rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
82332782Sjmz	    if (rc >= 0) {
82413889Sache		printf("Media catalog is %sactive",
82587573Smikeh		    ss.data->what.media_catalog.mc_valid ? "": "in");
82616736Sache		if (ss.data->what.media_catalog.mc_valid &&
82716736Sache		    ss.data->what.media_catalog.mc_number[0])
82877168Skris		{
82977168Skris		    strvisx (vmcn, ss.data->what.media_catalog.mc_number,
83077168Skris			    (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
83177168Skris		    printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
83277168Skris		}
83313889Sache		putchar('\n');
83432782Sjmz	    } else
83513888Sache		printf("No media catalog info available\n");
83632782Sjmz	}
83732782Sjmz	if (what & STATUS_VOLUME) {
83832782Sjmz	    rc = ioctl (fd, CDIOCGETVOL, &v);
83932782Sjmz	    if (rc >= 0)
84010099Sjkh		if (verbose)
84132782Sjmz		    printf ("Left volume = %d, right volume = %d\n",
84232782Sjmz			    v.vol[0], v.vol[1]);
84310099Sjkh		else
84432782Sjmz		    printf ("%d %d\n", v.vol[0], v.vol[1]);
84532782Sjmz	    else
84613888Sache		printf ("No volume level info available\n");
84732782Sjmz	}
84813884Sache	return(0);
84913884Sache}
85010099Sjkh
85163091Sjoe/*
85263091Sjoe * dbprog_sum
85363091Sjoe *	Convert an integer to its text string representation, and
85463091Sjoe *	compute its checksum.  Used by dbprog_discid to derive the
85563091Sjoe *	disc ID.
85663091Sjoe *
85763091Sjoe * Args:
85863091Sjoe *	n - The integer value.
85963091Sjoe *
86063091Sjoe * Return:
86163091Sjoe *	The integer checksum.
86263091Sjoe */
86363091Sjoestatic int
86463091Sjoedbprog_sum(int n)
86563091Sjoe{
86663091Sjoe	char	buf[12],
86763091Sjoe		*p;
86863091Sjoe	int	ret = 0;
86963091Sjoe
87063091Sjoe	/* For backward compatibility this algorithm must not change */
87163091Sjoe	sprintf(buf, "%u", n);
87263091Sjoe	for (p = buf; *p != '\0'; p++)
87363091Sjoe		ret += (*p - '0');
87463091Sjoe
87563091Sjoe	return(ret);
87663091Sjoe}
87763091Sjoe
87863091Sjoe
87963091Sjoe/*
88063091Sjoe * dbprog_discid
88163091Sjoe *	Compute a magic disc ID based on the number of tracks,
88263091Sjoe *	the length of each track, and a checksum of the string
88363091Sjoe *	that represents the offset of each track.
88463091Sjoe *
88563091Sjoe * Args:
88663091Sjoe *	s - Pointer to the curstat_t structure.
88763091Sjoe *
88863091Sjoe * Return:
88963091Sjoe *	The integer disc ID.
89063091Sjoe */
89163091Sjoestatic u_int
89263091Sjoedbprog_discid()
89363091Sjoe{
89463091Sjoe	struct	ioc_toc_header h;
89563091Sjoe	int	rc;
89663091Sjoe	int	i, ntr,
89763091Sjoe		t = 0,
89863091Sjoe		n = 0;
89963091Sjoe
90063091Sjoe	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
90163091Sjoe	if (rc < 0)
90263091Sjoe		return 0;
90363091Sjoe	ntr = h.ending_track - h.starting_track + 1;
90463091Sjoe	i = msf;
90563091Sjoe	msf = 1;
90663091Sjoe	rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
90763091Sjoe	msf = i;
90863091Sjoe	if (rc < 0)
90963091Sjoe		return 0;
91063091Sjoe	/* For backward compatibility this algorithm must not change */
91163091Sjoe	for (i = 0; i < ntr; i++) {
91263091Sjoe#define TC_MM(a) toc_buffer[a].addr.msf.minute
91363091Sjoe#define TC_SS(a) toc_buffer[a].addr.msf.second
91463091Sjoe		n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
91563091Sjoe
91663091Sjoe		t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
91787573Smikeh		    ((TC_MM(i) * 60) + TC_SS(i));
91863091Sjoe	}
91963091Sjoe
92063091Sjoe	return((n % 0xff) << 24 | t << 8 | ntr);
92163091Sjoe}
92263091Sjoe
92363091Sjoeint cdid ()
92463091Sjoe{
92563091Sjoe	u_int	id;
92663091Sjoe
92763091Sjoe	id = dbprog_discid();
92863091Sjoe	if (id)
92963091Sjoe	{
93063091Sjoe		if (verbose)
93163091Sjoe			printf ("CDID=");
93263091Sjoe		printf ("%08x\n",id);
93363091Sjoe	}
93463091Sjoe	return id ? 0 : 1;
93563091Sjoe}
93663091Sjoe
93787568Smikehint info (char *arg __unused)
93813884Sache{
93913884Sache	struct ioc_toc_header h;
94013884Sache	int rc, i, n;
94113884Sache
94210099Sjkh	rc = ioctl (fd, CDIOREADTOCHEADER, &h);
94313888Sache	if (rc >= 0) {
94410099Sjkh		if (verbose)
94510099Sjkh			printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
94610099Sjkh				h.starting_track, h.ending_track, h.len);
94710099Sjkh		else
94810099Sjkh			printf ("%d %d %d\n", h.starting_track,
94910099Sjkh				h.ending_track, h.len);
95013888Sache	} else {
95129103Scharnier		warn("getting toc header");
95210099Sjkh		return (rc);
95310099Sjkh	}
95410099Sjkh
95510099Sjkh	n = h.ending_track - h.starting_track + 1;
95610099Sjkh	rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
95710099Sjkh	if (rc < 0)
95810099Sjkh		return (rc);
95913985Sache
96010099Sjkh	if (verbose) {
96110099Sjkh		printf ("track     start  duration   block  length   type\n");
96210099Sjkh		printf ("-------------------------------------------------\n");
96310099Sjkh	}
96413985Sache
96510099Sjkh	for (i = 0; i < n; i++) {
96610099Sjkh		printf ("%5d  ", toc_buffer[i].track);
96710099Sjkh		prtrack (toc_buffer + i, 0);
96810099Sjkh	}
96913867Sache	printf ("%5d  ", toc_buffer[n].track);
97010099Sjkh	prtrack (toc_buffer + n, 1);
97110099Sjkh	return (0);
97210099Sjkh}
97310099Sjkh
97413985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
97510099Sjkh{
97687573Smikeh	lba += 150;			/* block start offset */
97787573Smikeh	lba &= 0xffffff;		/* negative lbas use only 24 bits */
97810099Sjkh	*m = lba / (60 * 75);
97910099Sjkh	lba %= (60 * 75);
98010099Sjkh	*s = lba / 75;
98110099Sjkh	*f = lba % 75;
98210099Sjkh}
98310099Sjkh
98413985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f)
98510099Sjkh{
98610099Sjkh	return (((m * 60) + s) * 75 + f) - 150;
98710099Sjkh}
98810099Sjkh
98910099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag)
99010099Sjkh{
99110099Sjkh	int block, next, len;
99210099Sjkh	u_char m, s, f;
99310099Sjkh
99413884Sache	if (msf) {
99513884Sache		/* Print track start */
99613884Sache		printf ("%2d:%02d.%02d  ", e->addr.msf.minute,
99713884Sache			e->addr.msf.second, e->addr.msf.frame);
99810099Sjkh
99913884Sache		block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
100013884Sache			e->addr.msf.frame);
100113884Sache	} else {
100213884Sache		block = ntohl(e->addr.lba);
100313884Sache		lba2msf(block, &m, &s, &f);
100413884Sache		/* Print track start */
100513884Sache		printf ("%2d:%02d.%02d  ", m, s, f);
100613884Sache	}
100710099Sjkh	if (lastflag) {
100810099Sjkh		/* Last track -- print block */
100910099Sjkh		printf ("       -  %6d       -      -\n", block);
101010099Sjkh		return;
101110099Sjkh	}
101210099Sjkh
101313884Sache	if (msf)
101413884Sache		next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
101513884Sache			e[1].addr.msf.frame);
101613884Sache	else
101713884Sache		next = ntohl(e[1].addr.lba);
101810099Sjkh	len = next - block;
1019103861Smaxim	/* Take into account a start offset time. */
1020103861Smaxim	lba2msf (len - 150, &m, &s, &f);
102110099Sjkh
102210099Sjkh	/* Print duration, block, length, type */
102310099Sjkh	printf ("%2d:%02d.%02d  %6d  %6d  %5s\n", m, s, f, block, len,
102413985Sache		(e->control & 4) ? "data" : "audio");
102510099Sjkh}
102610099Sjkh
102710099Sjkhint play_track (int tstart, int istart, int tend, int iend)
102810099Sjkh{
102910099Sjkh	struct ioc_play_track t;
103010099Sjkh
103110099Sjkh	t.start_track = tstart;
103210099Sjkh	t.start_index = istart;
103310099Sjkh	t.end_track = tend;
103410099Sjkh	t.end_index = iend;
103513985Sache
103610099Sjkh	return ioctl (fd, CDIOCPLAYTRACKS, &t);
103710099Sjkh}
103810099Sjkh
103910099Sjkhint play_blocks (int blk, int len)
104010099Sjkh{
104113985Sache	struct ioc_play_blocks  t;
104210099Sjkh
104310099Sjkh	t.blk = blk;
104410099Sjkh	t.len = len;
104513985Sache
104610099Sjkh	return ioctl (fd, CDIOCPLAYBLOCKS, &t);
104710099Sjkh}
104810099Sjkh
104913985Sacheint setvol (int left, int right)
105010099Sjkh{
105113985Sache	struct ioc_vol  v;
105210099Sjkh
105313985Sache	v.vol[0] = left;
105413985Sache	v.vol[1] = right;
105510099Sjkh	v.vol[2] = 0;
105610099Sjkh	v.vol[3] = 0;
105713985Sache
105810099Sjkh	return ioctl (fd, CDIOCSETVOL, &v);
105910099Sjkh}
106010099Sjkh
106110099Sjkhint read_toc_entrys (int len)
106210099Sjkh{
106310099Sjkh	struct ioc_read_toc_entry t;
106410099Sjkh
106513884Sache	t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
106613737Sache	t.starting_track = 0;
106710099Sjkh	t.data_len = len;
106810099Sjkh	t.data = toc_buffer;
106913985Sache
107013985Sache	return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
107110099Sjkh}
107210099Sjkh
107310099Sjkhint play_msf (int start_m, int start_s, int start_f,
107413985Sache	int end_m, int end_s, int end_f)
107510099Sjkh{
107687573Smikeh	struct ioc_play_msf a;
107710099Sjkh
107810099Sjkh	a.start_m = start_m;
107910099Sjkh	a.start_s = start_s;
108010099Sjkh	a.start_f = start_f;
108110099Sjkh	a.end_m = end_m;
108210099Sjkh	a.end_s = end_s;
108310099Sjkh	a.end_f = end_f;
108413985Sache
108510099Sjkh	return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
108610099Sjkh}
108710099Sjkh
108810099Sjkhint status (int *trk, int *min, int *sec, int *frame)
108910099Sjkh{
109010099Sjkh	struct ioc_read_subchannel s;
109110099Sjkh	struct cd_sub_channel_info data;
109213884Sache	u_char mm, ss, ff;
109310099Sjkh
109410099Sjkh	bzero (&s, sizeof (s));
109510099Sjkh	s.data = &data;
109610099Sjkh	s.data_len = sizeof (data);
109713884Sache	s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
109810099Sjkh	s.data_format = CD_CURRENT_POSITION;
109913985Sache
110010099Sjkh	if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
110110099Sjkh		return -1;
110213985Sache
110310099Sjkh	*trk = s.data->what.position.track_number;
110413884Sache	if (msf) {
110513884Sache		*min = s.data->what.position.reladdr.msf.minute;
110613884Sache		*sec = s.data->what.position.reladdr.msf.second;
110713884Sache		*frame = s.data->what.position.reladdr.msf.frame;
110813884Sache	} else {
110913884Sache		lba2msf(ntohl(s.data->what.position.reladdr.lba),
111013884Sache			&mm, &ss, &ff);
111113884Sache		*min = mm;
111213884Sache		*sec = ss;
111313884Sache		*frame = ff;
111413884Sache	}
111513985Sache
111610099Sjkh	return s.data->header.audio_status;
111710099Sjkh}
111810099Sjkh
111950039Smdoddconst char *
112050039Smdoddcdcontrol_prompt()
112110099Sjkh{
112250039Smdodd	return ("cdcontrol> ");
112350039Smdodd}
112450039Smdodd
112550039Smdoddchar *
112650039Smdoddinput (int *cmd)
112750039Smdodd{
112850039Smdodd#define MAXLINE 80
112950039Smdodd	static EditLine *el = NULL;
113050039Smdodd	static History *hist = NULL;
113184261Sobrien	HistEvent he;
113250039Smdodd	static char buf[MAXLINE];
113350039Smdodd	int num = 0;
113450071Smdodd	int len;
113550039Smdodd	const char *bp = NULL;
113610099Sjkh	char *p;
113710099Sjkh
113810099Sjkh	do {
113950039Smdodd		if (verbose) {
114050039Smdodd			if (!el) {
114184261Sobrien				el = el_init("cdcontrol", stdin, stdout,
114284261Sobrien				    stderr);
114350039Smdodd				hist = history_init();
114484261Sobrien				history(hist, &he, H_EVENT, 100);
114550039Smdodd				el_set(el, EL_HIST, history, hist);
114650039Smdodd				el_set(el, EL_EDITOR, "emacs");
114750039Smdodd				el_set(el, EL_PROMPT, cdcontrol_prompt);
114850039Smdodd				el_set(el, EL_SIGNAL, 1);
114950042Smdodd				el_source(el, NULL);
115050039Smdodd			}
115163070Smckay			if ((bp = el_gets(el, &num)) == NULL || num == 0) {
115263070Smckay				*cmd = CMD_QUIT;
115363070Smckay				fprintf (stderr, "\r\n");
115450039Smdodd				return (0);
115563070Smckay			}
115650039Smdodd
115750071Smdodd			len = (num > MAXLINE) ? MAXLINE : num;
115850071Smdodd			memcpy(buf, bp, len);
115950071Smdodd			buf[len] = 0;
116084261Sobrien			history(hist, &he, H_ENTER, bp);
116150039Smdodd#undef MAXLINE
116250039Smdodd
116350039Smdodd		} else {
116450039Smdodd			if (! fgets (buf, sizeof (buf), stdin)) {
116550039Smdodd				*cmd = CMD_QUIT;
116650039Smdodd				fprintf (stderr, "\r\n");
116750039Smdodd				return (0);
116850039Smdodd			}
116910099Sjkh		}
117010099Sjkh		p = parse (buf, cmd);
117110099Sjkh	} while (! p);
117210099Sjkh	return (p);
117310099Sjkh}
117410099Sjkh
117510099Sjkhchar *parse (char *buf, int *cmd)
117610099Sjkh{
117710099Sjkh	struct cmdtab *c;
117810099Sjkh	char *p;
117987568Smikeh	unsigned int len;
118010099Sjkh
118113985Sache	for (p=buf; isspace (*p); p++)
118213985Sache		continue;
118310099Sjkh
118413985Sache	if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
118513985Sache		*cmd = CMD_PLAY;
118613985Sache		return (p);
118777168Skris	} else if (*p == '+') {
118877168Skris		*cmd = CMD_NEXT;
118977168Skris		return (p + 1);
119077168Skris	} else if (*p == '-') {
119177168Skris		*cmd = CMD_PREVIOUS;
119277168Skris		return (p + 1);
119313985Sache	}
119410099Sjkh
119513985Sache	for (buf = p; *p && ! isspace (*p); p++)
119613985Sache		continue;
119796214Smaxim
119813985Sache	len = p - buf;
119910099Sjkh	if (! len)
120010099Sjkh		return (0);
120113985Sache
120287573Smikeh	if (*p) {		/* It must be a spacing character! */
120313985Sache		char *q;
120413985Sache
120513985Sache		*p++ = 0;
120613985Sache		for (q=p; *q && *q != '\n' && *q != '\r'; q++)
120713985Sache			continue;
120813985Sache		*q = 0;
120913985Sache	}
121013985Sache
121110099Sjkh	*cmd = -1;
121210099Sjkh	for (c=cmdtab; c->name; ++c) {
121313985Sache		/* Is it an exact match? */
121413985Sache		if (! strcasecmp (buf, c->name)) {
121513985Sache  			*cmd = c->command;
121613985Sache  			break;
121713985Sache  		}
121813985Sache
121913985Sache		/* Try short hand forms then... */
122013985Sache		if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
122113985Sache			if (*cmd != -1 && *cmd != c->command) {
122229103Scharnier				warnx("ambiguous command");
122313985Sache				return (0);
122413985Sache			}
122510099Sjkh			*cmd = c->command;
122613985Sache  		}
122713985Sache	}
122810099Sjkh
122910099Sjkh	if (*cmd == -1) {
123029103Scharnier		warnx("invalid command, enter ``help'' for commands");
123110099Sjkh		return (0);
123210099Sjkh	}
123313985Sache
123413985Sache	while (isspace (*p))
123513985Sache		p++;
123610099Sjkh	return p;
123710099Sjkh}
123810099Sjkh
123910099Sjkhint open_cd ()
124010099Sjkh{
124154164Sjoe	char devbuf[MAXPATHLEN];
124210099Sjkh
124310099Sjkh	if (fd > -1)
124410099Sjkh		return (1);
124513985Sache
124654164Sjoe	if (*cdname == '/') {
124754164Sjoe		snprintf (devbuf, MAXPATHLEN, "%s", cdname);
124861105Smsmith	} else {
124969793Sobrien		snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
125054164Sjoe	}
125113985Sache
125210099Sjkh	fd = open (devbuf, O_RDONLY);
125313985Sache
125410099Sjkh	if (fd < 0 && errno == ENOENT) {
125513985Sache		strcat (devbuf, DEFAULT_CD_PARTITION);
125610099Sjkh		fd = open (devbuf, O_RDONLY);
125710099Sjkh	}
125813985Sache
125910099Sjkh	if (fd < 0) {
126013985Sache		if (errno == ENXIO) {
126113985Sache			/*  ENXIO has an overloaded meaning here.
126213985Sache			 *  The original "Device not configured" should
126313985Sache			 *  be interpreted as "No disc in drive %s". */
126429103Scharnier			warnx("no disc in drive %s", devbuf);
126513985Sache			return (0);
126610099Sjkh		}
126729103Scharnier		err(1, "%s", devbuf);
126810099Sjkh	}
126910099Sjkh	return (1);
127010099Sjkh}
1271