GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cdio/cdio.c Lines: 0 778 0.0 %
Date: 2017-11-07 Branches: 0 507 0.0 %

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