Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/cdcontrol/cdcontrol.c
101477 views
1
/*
2
* Compact Disc Control Utility by Serge V. Vakulenko <[email protected]>.
3
* Based on the non-X based CD player by Jean-Marc Zucconi and
4
* Andrey A. Chernov.
5
*
6
* Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <[email protected]>.
7
*
8
* 11-Sep-1995: Jukka A. Ukkonen <[email protected]>
9
* A couple of further fixes to my own earlier "fixes".
10
*
11
* 18-Sep-1995: Jukka A. Ukkonen <[email protected]>
12
* Added an ability to specify addresses relative to the
13
* beginning of a track. This is in fact a variation of
14
* doing the simple play_msf() call.
15
*
16
* 11-Oct-1995: Serge V.Vakulenko <[email protected]>
17
* New eject algorithm.
18
* Some code style reformatting.
19
*
20
* 13-Dec-1999: Knut A. Syed <[email protected]>
21
* Volume-command modified. If used with only one
22
* parameter it now sets both channels. If used without
23
* parameters it will print volume-info.
24
* Version 2.0.1
25
*
26
* 27-Jun-2008 Pietro Cerutti <[email protected]>
27
* Further enhancement to volume. Values not in range 0-255
28
* are now reduced to be in range. This prevents overflow in
29
* the uchar storing the volume (256 -> 0, -20 -> 236, ...).
30
* Version 2.0.2
31
*
32
*/
33
34
#include <sys/cdefs.h>
35
#include <sys/cdio.h>
36
#include <sys/cdrio.h>
37
#include <sys/file.h>
38
#include <sys/ioctl.h>
39
#include <sys/param.h>
40
#include <arpa/inet.h>
41
#include <ctype.h>
42
#include <err.h>
43
#include <errno.h>
44
#include <histedit.h>
45
#include <limits.h>
46
#include <paths.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <unistd.h>
51
#include <vis.h>
52
53
#define VERSION "2.0.2"
54
55
#define ASTS_INVALID 0x00 /* Audio status byte not valid */
56
#define ASTS_PLAYING 0x11 /* Audio play operation in progress */
57
#define ASTS_PAUSED 0x12 /* Audio play operation paused */
58
#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
59
#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
60
#define ASTS_VOID 0x15 /* No current audio status to return */
61
62
#ifdef DEFAULT_CD_DRIVE
63
# error "Setting DEFAULT_CD_DRIVE is no longer supported"
64
#endif
65
66
#define CMD_DEBUG 1
67
#define CMD_EJECT 2
68
#define CMD_HELP 3
69
#define CMD_INFO 4
70
#define CMD_PAUSE 5
71
#define CMD_PLAY 6
72
#define CMD_QUIT 7
73
#define CMD_RESUME 8
74
#define CMD_STOP 9
75
#define CMD_VOLUME 10
76
#define CMD_CLOSE 11
77
#define CMD_RESET 12
78
#define CMD_SET 13
79
#define CMD_STATUS 14
80
#define CMD_CDID 15
81
#define CMD_NEXT 16
82
#define CMD_PREVIOUS 17
83
#define CMD_SPEED 18
84
#define STATUS_AUDIO 0x1
85
#define STATUS_MEDIA 0x2
86
#define STATUS_VOLUME 0x4
87
88
static struct cmdtab {
89
int command;
90
const char *name;
91
unsigned min;
92
const char *args;
93
} cmdtab[] = {
94
{ CMD_CLOSE, "close", 1, "" },
95
{ CMD_DEBUG, "debug", 1, "on | off" },
96
{ CMD_EJECT, "eject", 1, "" },
97
{ CMD_HELP, "?", 1, 0 },
98
{ CMD_HELP, "help", 1, "" },
99
{ CMD_INFO, "info", 1, "" },
100
{ CMD_NEXT, "next", 1, "" },
101
{ CMD_PAUSE, "pause", 2, "" },
102
{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
103
{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
104
{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
105
{ CMD_PLAY, "play", 1, "[#block [len]]" },
106
{ CMD_PREVIOUS, "previous", 2, "" },
107
{ CMD_QUIT, "quit", 1, "" },
108
{ CMD_RESET, "reset", 4, "" },
109
{ CMD_RESUME, "resume", 1, "" },
110
{ CMD_SET, "set", 2, "msf | lba" },
111
{ CMD_STATUS, "status", 1, "[audio | media | volume]" },
112
{ CMD_STOP, "stop", 3, "" },
113
{ CMD_VOLUME, "volume", 1,
114
"<l&r> <l> <r> | left | right | mute | mono | stereo" },
115
{ CMD_CDID, "cdid", 2, "" },
116
{ CMD_SPEED, "speed", 2, "speed" },
117
{ 0, NULL, 0, NULL }
118
};
119
120
static struct cd_toc_entry toc_buffer[100];
121
122
static const char *cdname;
123
static int fd = -1;
124
static int verbose = 1;
125
static int msf = 1;
126
127
static int setvol(int, int);
128
static int read_toc_entrys(int);
129
static int play_msf(int, int, int, int, int, int);
130
static int play_track(int, int, int, int);
131
static int status(int *, int *, int *, int *);
132
static int open_cd(void);
133
static int next_prev(char *arg, int);
134
static int play(char *arg);
135
static int info(char *arg);
136
static int cdid(void);
137
static int pstatus(char *arg);
138
static char *input(int *);
139
static void prtrack(struct cd_toc_entry *e, int lastflag);
140
static void lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
141
static unsigned int msf2lba(u_char m, u_char s, u_char f);
142
static int play_blocks(int blk, int len);
143
static int run(int cmd, char *arg);
144
static char *parse(char *buf, int *cmd);
145
static void help(void);
146
static void usage(void);
147
static char *use_cdrom_instead(const char *);
148
static const char *strstatus(int);
149
static u_int dbprog_discid(void);
150
static const char *cdcontrol_prompt(void);
151
152
static void
153
help(void)
154
{
155
struct cmdtab *c;
156
const char *s;
157
char n;
158
int i;
159
160
for (c=cmdtab; c->name; ++c) {
161
if (! c->args)
162
continue;
163
printf("\t");
164
for (i = c->min, s = c->name; *s; s++, i--) {
165
if (i > 0)
166
n = toupper(*s);
167
else
168
n = *s;
169
putchar(n);
170
}
171
if (*c->args)
172
printf (" %s", c->args);
173
printf ("\n");
174
}
175
printf ("\n\tThe word \"play\" is not required for the play commands.\n");
176
printf ("\tThe plain target address is taken as a synonym for play.\n");
177
}
178
179
static void
180
usage(void)
181
{
182
fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
183
exit (1);
184
}
185
186
static char *
187
use_cdrom_instead(const char *old_envvar)
188
{
189
char *device;
190
191
device = getenv(old_envvar);
192
if (device)
193
warnx("%s environment variable deprecated, "
194
"please use CDROM in the future.", old_envvar);
195
return device;
196
}
197
198
199
int
200
main(int argc, char **argv)
201
{
202
int cmd;
203
char *arg;
204
205
for (;;) {
206
switch (getopt (argc, argv, "svhf:")) {
207
case -1:
208
break;
209
case 's':
210
verbose = 0;
211
continue;
212
case 'v':
213
verbose = 2;
214
continue;
215
case 'f':
216
cdname = optarg;
217
continue;
218
case 'h':
219
default:
220
usage ();
221
}
222
break;
223
}
224
argc -= optind;
225
argv += optind;
226
227
if (argc > 0 && ! strcasecmp (*argv, "help"))
228
usage ();
229
230
if (! cdname) {
231
cdname = getenv("CDROM");
232
}
233
234
if (! cdname)
235
cdname = use_cdrom_instead("MUSIC_CD");
236
if (! cdname)
237
cdname = use_cdrom_instead("CD_DRIVE");
238
if (! cdname)
239
cdname = use_cdrom_instead("DISC");
240
if (! cdname)
241
cdname = use_cdrom_instead("CDPLAY");
242
243
if (argc > 0) {
244
char buf[80], *p;
245
int len, rc;
246
247
for (p=buf; argc-->0; ++argv) {
248
len = strlen (*argv);
249
250
if (p + len >= buf + sizeof (buf) - 1)
251
usage ();
252
253
if (p > buf)
254
*p++ = ' ';
255
256
strcpy (p, *argv);
257
p += len;
258
}
259
*p = 0;
260
arg = parse (buf, &cmd);
261
rc = run (cmd, arg);
262
if (rc < 0 && verbose)
263
warn(NULL);
264
265
return (rc);
266
}
267
268
if (verbose == 1)
269
verbose = isatty (0);
270
271
if (verbose) {
272
printf ("Compact Disc Control utility, version %s\n", VERSION);
273
printf ("Type `?' for command list\n\n");
274
}
275
276
for (;;) {
277
arg = input (&cmd);
278
if (run (cmd, arg) < 0) {
279
if (verbose)
280
warn(NULL);
281
close (fd);
282
fd = -1;
283
}
284
fflush (stdout);
285
}
286
}
287
288
static int
289
run(int cmd, char *arg)
290
{
291
long speed;
292
int l, r, rc, count;
293
294
switch (cmd) {
295
296
case CMD_QUIT:
297
exit (0);
298
299
case CMD_INFO:
300
if (fd < 0 && ! open_cd ())
301
return (0);
302
303
return info (arg);
304
305
case CMD_CDID:
306
if (fd < 0 && ! open_cd ())
307
return (0);
308
309
return cdid ();
310
311
case CMD_STATUS:
312
if (fd < 0 && ! open_cd ())
313
return (0);
314
315
return pstatus (arg);
316
317
case CMD_NEXT:
318
case CMD_PREVIOUS:
319
if (fd < 0 && ! open_cd ())
320
return (0);
321
322
while (isspace (*arg))
323
arg++;
324
325
return next_prev (arg, cmd);
326
327
case CMD_PAUSE:
328
if (fd < 0 && ! open_cd ())
329
return (0);
330
331
return ioctl (fd, CDIOCPAUSE);
332
333
case CMD_RESUME:
334
if (fd < 0 && ! open_cd ())
335
return (0);
336
337
return ioctl (fd, CDIOCRESUME);
338
339
case CMD_STOP:
340
if (fd < 0 && ! open_cd ())
341
return (0);
342
343
rc = ioctl (fd, CDIOCSTOP);
344
345
(void) ioctl (fd, CDIOCALLOW);
346
347
return (rc);
348
349
case CMD_RESET:
350
if (fd < 0 && ! open_cd ())
351
return (0);
352
353
rc = ioctl (fd, CDIOCRESET);
354
if (rc < 0)
355
return rc;
356
close(fd);
357
fd = -1;
358
return (0);
359
360
case CMD_DEBUG:
361
if (fd < 0 && ! open_cd ())
362
return (0);
363
364
if (! strcasecmp (arg, "on"))
365
return ioctl (fd, CDIOCSETDEBUG);
366
367
if (! strcasecmp (arg, "off"))
368
return ioctl (fd, CDIOCCLRDEBUG);
369
370
warnx("invalid command arguments");
371
372
return (0);
373
374
case CMD_EJECT:
375
if (fd < 0 && ! open_cd ())
376
return (0);
377
378
(void) ioctl (fd, CDIOCALLOW);
379
rc = ioctl (fd, CDIOCEJECT);
380
if (rc < 0)
381
return (rc);
382
return (0);
383
384
case CMD_CLOSE:
385
if (fd < 0 && ! open_cd ())
386
return (0);
387
388
(void) ioctl (fd, CDIOCALLOW);
389
rc = ioctl (fd, CDIOCCLOSE);
390
if (rc < 0)
391
return (rc);
392
close(fd);
393
fd = -1;
394
return (0);
395
396
case CMD_PLAY:
397
if (fd < 0 && ! open_cd ())
398
return (0);
399
400
while (isspace (*arg))
401
arg++;
402
403
return play (arg);
404
405
case CMD_SET:
406
if (! strcasecmp (arg, "msf"))
407
msf = 1;
408
else if (! strcasecmp (arg, "lba"))
409
msf = 0;
410
else
411
warnx("invalid command arguments");
412
return (0);
413
414
case CMD_VOLUME:
415
if (fd < 0 && !open_cd ())
416
return (0);
417
418
if (! strlen (arg)) {
419
char volume[] = "volume";
420
421
return pstatus (volume);
422
}
423
424
if (! strncasecmp (arg, "left", strlen(arg)))
425
return ioctl (fd, CDIOCSETLEFT);
426
427
if (! strncasecmp (arg, "right", strlen(arg)))
428
return ioctl (fd, CDIOCSETRIGHT);
429
430
if (! strncasecmp (arg, "mono", strlen(arg)))
431
return ioctl (fd, CDIOCSETMONO);
432
433
if (! strncasecmp (arg, "stereo", strlen(arg)))
434
return ioctl (fd, CDIOCSETSTERIO);
435
436
if (! strncasecmp (arg, "mute", strlen(arg)))
437
return ioctl (fd, CDIOCSETMUTE);
438
439
count = sscanf (arg, "%d %d", &l, &r);
440
if (count == 1)
441
return setvol (l, l);
442
if (count == 2)
443
return setvol (l, r);
444
warnx("invalid command arguments");
445
return (0);
446
447
case CMD_SPEED:
448
if (fd < 0 && ! open_cd ())
449
return (0);
450
451
errno = 0;
452
if (strcasecmp("max", arg) == 0)
453
speed = CDR_MAX_SPEED;
454
else
455
speed = strtol(arg, NULL, 10) * 177;
456
if (speed <= 0 || speed > INT_MAX) {
457
warnx("invalid command arguments %s", arg);
458
return (0);
459
}
460
return ioctl(fd, CDRIOCREADSPEED, &speed);
461
462
default:
463
case CMD_HELP:
464
help ();
465
return (0);
466
467
}
468
}
469
470
static int
471
play(char *arg)
472
{
473
struct ioc_toc_header h;
474
unsigned int n;
475
int rc, start, end = 0, istart = 1, iend = 1;
476
477
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
478
479
if (rc < 0)
480
return (rc);
481
482
n = h.ending_track - h.starting_track + 1;
483
rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
484
485
if (rc < 0)
486
return (rc);
487
488
if (! arg || ! *arg) {
489
/* Play the whole disc */
490
if (msf)
491
return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
492
toc_buffer[n].addr.msf.second,
493
toc_buffer[n].addr.msf.frame));
494
else
495
return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
496
}
497
498
if (strchr (arg, '#')) {
499
/* Play block #blk [ len ] */
500
int blk, len = 0;
501
502
if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
503
1 != sscanf (arg, "#%d", &blk))
504
goto Clean_up;
505
506
if (len == 0) {
507
if (msf)
508
len = msf2lba (toc_buffer[n].addr.msf.minute,
509
toc_buffer[n].addr.msf.second,
510
toc_buffer[n].addr.msf.frame) - blk;
511
else
512
len = ntohl(toc_buffer[n].addr.lba) - blk;
513
}
514
return play_blocks (blk, len);
515
}
516
517
if (strchr (arg, ':')) {
518
/*
519
* Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
520
*
521
* Will now also understand timed addresses relative
522
* to the beginning of a track in the form...
523
*
524
* tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
525
*/
526
unsigned tr1, tr2;
527
unsigned m1, m2, s1, s2, f1, f2;
528
unsigned char tm, ts, tf;
529
530
tr2 = m2 = s2 = f2 = f1 = 0;
531
if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
532
&tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
533
goto Play_Relative_Addresses;
534
535
tr2 = m2 = s2 = f2 = f1 = 0;
536
if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
537
&tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
538
goto Play_Relative_Addresses;
539
540
tr2 = m2 = s2 = f2 = f1 = 0;
541
if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
542
&tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
543
goto Play_Relative_Addresses;
544
545
tr2 = m2 = s2 = f2 = f1 = 0;
546
if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
547
&tr1, &m1, &s1, &f1, &m2, &s2, &f2))
548
goto Play_Relative_Addresses;
549
550
tr2 = m2 = s2 = f2 = f1 = 0;
551
if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
552
&tr1, &m1, &s1, &f1, &m2, &s2))
553
goto Play_Relative_Addresses;
554
555
tr2 = m2 = s2 = f2 = f1 = 0;
556
if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
557
&tr1, &m1, &s1, &m2, &s2, &f2))
558
goto Play_Relative_Addresses;
559
560
tr2 = m2 = s2 = f2 = f1 = 0;
561
if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
562
&tr1, &m1, &s1, &f1, &tr2, &m2))
563
goto Play_Relative_Addresses;
564
565
tr2 = m2 = s2 = f2 = f1 = 0;
566
if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
567
goto Play_Relative_Addresses;
568
569
tr2 = m2 = s2 = f2 = f1 = 0;
570
if (5 == sscanf (arg, "%d %d:%d %d %d",
571
&tr1, &m1, &s1, &tr2, &m2))
572
goto Play_Relative_Addresses;
573
574
tr2 = m2 = s2 = f2 = f1 = 0;
575
if (5 == sscanf (arg, "%d %d:%d.%d %d",
576
&tr1, &m1, &s1, &f1, &tr2))
577
goto Play_Relative_Addresses;
578
579
tr2 = m2 = s2 = f2 = f1 = 0;
580
if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
581
goto Play_Relative_Addresses;
582
583
tr2 = m2 = s2 = f2 = f1 = 0;
584
if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
585
goto Play_Relative_Addresses;
586
587
tr2 = m2 = s2 = f2 = f1 = 0;
588
if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
589
goto Play_Relative_Addresses;
590
591
tr2 = m2 = s2 = f2 = f1 = 0;
592
goto Try_Absolute_Timed_Addresses;
593
594
Play_Relative_Addresses:
595
if (! tr1)
596
tr1 = 1;
597
else if (tr1 > n)
598
tr1 = n;
599
600
tr1--;
601
602
if (msf) {
603
tm = toc_buffer[tr1].addr.msf.minute;
604
ts = toc_buffer[tr1].addr.msf.second;
605
tf = toc_buffer[tr1].addr.msf.frame;
606
} else
607
lba2msf(ntohl(toc_buffer[tr1].addr.lba),
608
&tm, &ts, &tf);
609
if ((m1 > tm)
610
|| ((m1 == tm)
611
&& ((s1 > ts)
612
|| ((s1 == ts)
613
&& (f1 > tf))))) {
614
printf ("Track %d is not that long.\n", tr1);
615
return (0);
616
}
617
618
f1 += tf;
619
if (f1 >= 75) {
620
s1 += f1 / 75;
621
f1 %= 75;
622
}
623
624
s1 += ts;
625
if (s1 >= 60) {
626
m1 += s1 / 60;
627
s1 %= 60;
628
}
629
630
m1 += tm;
631
632
if (! tr2) {
633
if (m2 || s2 || f2) {
634
tr2 = tr1;
635
f2 += f1;
636
if (f2 >= 75) {
637
s2 += f2 / 75;
638
f2 %= 75;
639
}
640
641
s2 += s1;
642
if (s2 > 60) {
643
m2 += s2 / 60;
644
s2 %= 60;
645
}
646
647
m2 += m1;
648
} else {
649
tr2 = n;
650
if (msf) {
651
m2 = toc_buffer[n].addr.msf.minute;
652
s2 = toc_buffer[n].addr.msf.second;
653
f2 = toc_buffer[n].addr.msf.frame;
654
} else {
655
lba2msf(ntohl(toc_buffer[n].addr.lba),
656
&tm, &ts, &tf);
657
m2 = tm;
658
s2 = ts;
659
f2 = tf;
660
}
661
}
662
} else if (tr2 > n) {
663
tr2 = n;
664
m2 = s2 = f2 = 0;
665
} else {
666
if (m2 || s2 || f2)
667
tr2--;
668
if (msf) {
669
tm = toc_buffer[tr2].addr.msf.minute;
670
ts = toc_buffer[tr2].addr.msf.second;
671
tf = toc_buffer[tr2].addr.msf.frame;
672
} else
673
lba2msf(ntohl(toc_buffer[tr2].addr.lba),
674
&tm, &ts, &tf);
675
f2 += tf;
676
if (f2 >= 75) {
677
s2 += f2 / 75;
678
f2 %= 75;
679
}
680
681
s2 += ts;
682
if (s2 > 60) {
683
m2 += s2 / 60;
684
s2 %= 60;
685
}
686
687
m2 += tm;
688
}
689
690
if (msf) {
691
tm = toc_buffer[n].addr.msf.minute;
692
ts = toc_buffer[n].addr.msf.second;
693
tf = toc_buffer[n].addr.msf.frame;
694
} else
695
lba2msf(ntohl(toc_buffer[n].addr.lba),
696
&tm, &ts, &tf);
697
if ((tr2 < n)
698
&& ((m2 > tm)
699
|| ((m2 == tm)
700
&& ((s2 > ts)
701
|| ((s2 == ts)
702
&& (f2 > tf)))))) {
703
printf ("The playing time of the disc is not that long.\n");
704
return (0);
705
}
706
return (play_msf (m1, s1, f1, m2, s2, f2));
707
708
Try_Absolute_Timed_Addresses:
709
if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
710
&m1, &s1, &f1, &m2, &s2, &f2) &&
711
5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
712
5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
713
3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
714
4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
715
2 != sscanf (arg, "%d:%d", &m1, &s1))
716
goto Clean_up;
717
718
if (m2 == 0) {
719
if (msf) {
720
m2 = toc_buffer[n].addr.msf.minute;
721
s2 = toc_buffer[n].addr.msf.second;
722
f2 = toc_buffer[n].addr.msf.frame;
723
} else {
724
lba2msf(ntohl(toc_buffer[n].addr.lba),
725
&tm, &ts, &tf);
726
m2 = tm;
727
s2 = ts;
728
f2 = tf;
729
}
730
}
731
return play_msf (m1, s1, f1, m2, s2, f2);
732
}
733
734
/*
735
* Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
736
*/
737
if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
738
3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
739
3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
740
2 != sscanf (arg, "%d.%d", &start, &istart) &&
741
2 != sscanf (arg, "%d%d", &start, &end) &&
742
1 != sscanf (arg, "%d", &start))
743
goto Clean_up;
744
745
if (end == 0)
746
end = n;
747
return (play_track (start, istart, end, iend));
748
749
Clean_up:
750
warnx("invalid command arguments");
751
return (0);
752
}
753
754
static int
755
next_prev(char *arg, int cmd)
756
{
757
struct ioc_toc_header h;
758
int dir, junk, n, off, rc, trk;
759
760
dir = (cmd == CMD_NEXT) ? 1 : -1;
761
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
762
if (rc < 0)
763
return (rc);
764
765
n = h.ending_track - h.starting_track + 1;
766
rc = status (&trk, &junk, &junk, &junk);
767
if (rc < 0)
768
return (-1);
769
770
if (arg && *arg) {
771
if (sscanf (arg, "%u", &off) != 1) {
772
warnx("invalid command argument");
773
return (0);
774
} else
775
trk += off * dir;
776
} else
777
trk += dir;
778
779
if (trk > h.ending_track)
780
trk = 1;
781
782
return (play_track (trk, 1, n, 1));
783
}
784
785
static const char *
786
strstatus(int sts)
787
{
788
switch (sts) {
789
case ASTS_INVALID: return ("invalid");
790
case ASTS_PLAYING: return ("playing");
791
case ASTS_PAUSED: return ("paused");
792
case ASTS_COMPLETED: return ("completed");
793
case ASTS_ERROR: return ("error");
794
case ASTS_VOID: return ("void");
795
default: return ("??");
796
}
797
}
798
799
static int
800
pstatus(char *arg)
801
{
802
struct ioc_vol v;
803
struct ioc_read_subchannel ss;
804
struct cd_sub_channel_info data;
805
int rc, trk, m, s, f;
806
int what = 0;
807
char *p, vmcn[(4 * 15) + 1];
808
809
while ((p = strtok(arg, " \t"))) {
810
arg = 0;
811
if (!strncasecmp(p, "audio", strlen(p)))
812
what |= STATUS_AUDIO;
813
else if (!strncasecmp(p, "media", strlen(p)))
814
what |= STATUS_MEDIA;
815
else if (!strncasecmp(p, "volume", strlen(p)))
816
what |= STATUS_VOLUME;
817
else {
818
warnx("invalid command arguments");
819
return 0;
820
}
821
}
822
if (!what)
823
what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
824
if (what & STATUS_AUDIO) {
825
rc = status (&trk, &m, &s, &f);
826
if (rc >= 0)
827
if (verbose)
828
printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
829
rc, strstatus (rc), trk, m, s, f);
830
else
831
printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
832
else
833
printf ("No current status info available\n");
834
}
835
if (what & STATUS_MEDIA) {
836
bzero (&ss, sizeof (ss));
837
ss.data = &data;
838
ss.data_len = sizeof (data);
839
ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
840
ss.data_format = CD_MEDIA_CATALOG;
841
rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
842
if (rc >= 0) {
843
printf("Media catalog is %sactive",
844
ss.data->what.media_catalog.mc_valid ? "": "in");
845
if (ss.data->what.media_catalog.mc_valid &&
846
ss.data->what.media_catalog.mc_number[0])
847
{
848
strvisx (vmcn, ss.data->what.media_catalog.mc_number,
849
(sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
850
printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
851
}
852
putchar('\n');
853
} else
854
printf("No media catalog info available\n");
855
}
856
if (what & STATUS_VOLUME) {
857
rc = ioctl (fd, CDIOCGETVOL, &v);
858
if (rc >= 0)
859
if (verbose)
860
printf ("Left volume = %d, right volume = %d\n",
861
v.vol[0], v.vol[1]);
862
else
863
printf ("%d %d\n", v.vol[0], v.vol[1]);
864
else
865
printf ("No volume level info available\n");
866
}
867
return(0);
868
}
869
870
/*
871
* dbprog_sum
872
* Convert an integer to its text string representation, and
873
* compute its checksum. Used by dbprog_discid to derive the
874
* disc ID.
875
*
876
* Args:
877
* n - The integer value.
878
*
879
* Return:
880
* The integer checksum.
881
*/
882
static int
883
dbprog_sum(int n)
884
{
885
char buf[12],
886
*p;
887
int ret = 0;
888
889
/* For backward compatibility this algorithm must not change */
890
sprintf(buf, "%u", n);
891
for (p = buf; *p != '\0'; p++)
892
ret += (*p - '0');
893
894
return(ret);
895
}
896
897
898
/*
899
* dbprog_discid
900
* Compute a magic disc ID based on the number of tracks,
901
* the length of each track, and a checksum of the string
902
* that represents the offset of each track.
903
*
904
* Args:
905
* s - Pointer to the curstat_t structure.
906
*
907
* Return:
908
* The integer disc ID.
909
*/
910
static u_int
911
dbprog_discid(void)
912
{
913
struct ioc_toc_header h;
914
int rc;
915
int i, ntr,
916
t = 0,
917
n = 0;
918
919
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
920
if (rc < 0)
921
return 0;
922
ntr = h.ending_track - h.starting_track + 1;
923
i = msf;
924
msf = 1;
925
rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
926
msf = i;
927
if (rc < 0)
928
return 0;
929
/* For backward compatibility this algorithm must not change */
930
for (i = 0; i < ntr; i++) {
931
#define TC_MM(a) toc_buffer[a].addr.msf.minute
932
#define TC_SS(a) toc_buffer[a].addr.msf.second
933
n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
934
935
t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
936
((TC_MM(i) * 60) + TC_SS(i));
937
}
938
939
return((n % 0xff) << 24 | t << 8 | ntr);
940
}
941
942
static int
943
cdid(void)
944
{
945
u_int id;
946
947
id = dbprog_discid();
948
if (id)
949
{
950
if (verbose)
951
printf ("CDID=");
952
printf ("%08x\n",id);
953
}
954
return id ? 0 : 1;
955
}
956
957
static int
958
info(char *arg __unused)
959
{
960
struct ioc_toc_header h;
961
int rc, i, n;
962
963
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
964
if (rc >= 0) {
965
if (verbose)
966
printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
967
h.starting_track, h.ending_track, h.len);
968
else
969
printf ("%d %d %d\n", h.starting_track,
970
h.ending_track, h.len);
971
} else {
972
warn("getting toc header");
973
return (rc);
974
}
975
976
n = h.ending_track - h.starting_track + 1;
977
rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
978
if (rc < 0)
979
return (rc);
980
981
if (verbose) {
982
printf ("track start duration block length type\n");
983
printf ("-------------------------------------------------\n");
984
}
985
986
for (i = 0; i < n; i++) {
987
printf ("%5d ", toc_buffer[i].track);
988
prtrack (toc_buffer + i, 0);
989
}
990
printf ("%5d ", toc_buffer[n].track);
991
prtrack (toc_buffer + n, 1);
992
return (0);
993
}
994
995
static void
996
lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f)
997
{
998
lba += 150; /* block start offset */
999
lba &= 0xffffff; /* negative lbas use only 24 bits */
1000
*m = lba / (60 * 75);
1001
lba %= (60 * 75);
1002
*s = lba / 75;
1003
*f = lba % 75;
1004
}
1005
1006
static unsigned int
1007
msf2lba(u_char m, u_char s, u_char f)
1008
{
1009
return (((m * 60) + s) * 75 + f) - 150;
1010
}
1011
1012
static void
1013
prtrack(struct cd_toc_entry *e, int lastflag)
1014
{
1015
int block, next, len;
1016
u_char m, s, f;
1017
1018
if (msf) {
1019
/* Print track start */
1020
printf ("%2d:%02d.%02d ", e->addr.msf.minute,
1021
e->addr.msf.second, e->addr.msf.frame);
1022
1023
block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
1024
e->addr.msf.frame);
1025
} else {
1026
block = ntohl(e->addr.lba);
1027
lba2msf(block, &m, &s, &f);
1028
/* Print track start */
1029
printf ("%2d:%02d.%02d ", m, s, f);
1030
}
1031
if (lastflag) {
1032
/* Last track -- print block */
1033
printf (" - %6d - -\n", block);
1034
return;
1035
}
1036
1037
if (msf)
1038
next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
1039
e[1].addr.msf.frame);
1040
else
1041
next = ntohl(e[1].addr.lba);
1042
len = next - block;
1043
/* Take into account a start offset time. */
1044
lba2msf (len - 150, &m, &s, &f);
1045
1046
/* Print duration, block, length, type */
1047
printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
1048
(e->control & 4) ? "data" : "audio");
1049
}
1050
1051
static int
1052
play_track(int tstart, int istart, int tend, int iend)
1053
{
1054
struct ioc_play_track t;
1055
1056
t.start_track = tstart;
1057
t.start_index = istart;
1058
t.end_track = tend;
1059
t.end_index = iend;
1060
1061
return ioctl (fd, CDIOCPLAYTRACKS, &t);
1062
}
1063
1064
static int
1065
play_blocks(int blk, int len)
1066
{
1067
struct ioc_play_blocks t;
1068
1069
t.blk = blk;
1070
t.len = len;
1071
1072
return ioctl (fd, CDIOCPLAYBLOCKS, &t);
1073
}
1074
1075
static int
1076
setvol(int left, int right)
1077
{
1078
struct ioc_vol v;
1079
1080
left = left < 0 ? 0 : left > 255 ? 255 : left;
1081
right = right < 0 ? 0 : right > 255 ? 255 : right;
1082
1083
v.vol[0] = left;
1084
v.vol[1] = right;
1085
v.vol[2] = 0;
1086
v.vol[3] = 0;
1087
1088
return ioctl (fd, CDIOCSETVOL, &v);
1089
}
1090
1091
static int
1092
read_toc_entrys(int len)
1093
{
1094
struct ioc_read_toc_entry t;
1095
1096
t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1097
t.starting_track = 0;
1098
t.data_len = len;
1099
t.data = toc_buffer;
1100
1101
return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
1102
}
1103
1104
static int
1105
play_msf(int start_m, int start_s, int start_f,
1106
int end_m, int end_s, int end_f)
1107
{
1108
struct ioc_play_msf a;
1109
1110
a.start_m = start_m;
1111
a.start_s = start_s;
1112
a.start_f = start_f;
1113
a.end_m = end_m;
1114
a.end_s = end_s;
1115
a.end_f = end_f;
1116
1117
return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
1118
}
1119
1120
static int
1121
status(int *trk, int *min, int *sec, int *frame)
1122
{
1123
struct ioc_read_subchannel s;
1124
struct cd_sub_channel_info data;
1125
u_char mm, ss, ff;
1126
1127
bzero (&s, sizeof (s));
1128
s.data = &data;
1129
s.data_len = sizeof (data);
1130
s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1131
s.data_format = CD_CURRENT_POSITION;
1132
1133
if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
1134
return -1;
1135
1136
*trk = s.data->what.position.track_number;
1137
if (msf) {
1138
*min = s.data->what.position.reladdr.msf.minute;
1139
*sec = s.data->what.position.reladdr.msf.second;
1140
*frame = s.data->what.position.reladdr.msf.frame;
1141
} else {
1142
lba2msf(ntohl(s.data->what.position.reladdr.lba),
1143
&mm, &ss, &ff);
1144
*min = mm;
1145
*sec = ss;
1146
*frame = ff;
1147
}
1148
1149
return s.data->header.audio_status;
1150
}
1151
1152
static const char *
1153
cdcontrol_prompt(void)
1154
{
1155
return ("cdcontrol> ");
1156
}
1157
1158
static char *
1159
input(int *cmd)
1160
{
1161
#define MAXLINE 80
1162
static EditLine *el = NULL;
1163
static History *hist = NULL;
1164
HistEvent he;
1165
static char buf[MAXLINE];
1166
int num = 0;
1167
int len;
1168
const char *bp = NULL;
1169
char *p;
1170
1171
do {
1172
if (verbose) {
1173
if (!el) {
1174
el = el_init("cdcontrol", stdin, stdout,
1175
stderr);
1176
hist = history_init();
1177
history(hist, &he, H_SETSIZE, 100);
1178
el_set(el, EL_HIST, history, hist);
1179
el_set(el, EL_EDITOR, "emacs");
1180
el_set(el, EL_PROMPT, cdcontrol_prompt);
1181
el_set(el, EL_SIGNAL, 1);
1182
el_source(el, NULL);
1183
}
1184
if ((bp = el_gets(el, &num)) == NULL || num == 0) {
1185
*cmd = CMD_QUIT;
1186
fprintf (stderr, "\r\n");
1187
return (0);
1188
}
1189
1190
len = (num > MAXLINE) ? MAXLINE : num;
1191
memcpy(buf, bp, len);
1192
buf[len] = 0;
1193
history(hist, &he, H_ENTER, bp);
1194
#undef MAXLINE
1195
1196
} else {
1197
if (! fgets (buf, sizeof (buf), stdin)) {
1198
*cmd = CMD_QUIT;
1199
fprintf (stderr, "\r\n");
1200
return (0);
1201
}
1202
}
1203
p = parse (buf, cmd);
1204
} while (! p);
1205
return (p);
1206
}
1207
1208
static char *
1209
parse(char *buf, int *cmd)
1210
{
1211
struct cmdtab *c;
1212
char *p;
1213
unsigned int len;
1214
1215
for (p=buf; isspace (*p); p++)
1216
continue;
1217
1218
if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
1219
*cmd = CMD_PLAY;
1220
return (p);
1221
} else if (*p == '+') {
1222
*cmd = CMD_NEXT;
1223
return (p + 1);
1224
} else if (*p == '-') {
1225
*cmd = CMD_PREVIOUS;
1226
return (p + 1);
1227
}
1228
1229
for (buf = p; *p && ! isspace (*p); p++)
1230
continue;
1231
1232
len = p - buf;
1233
if (! len)
1234
return (0);
1235
1236
if (*p) { /* It must be a spacing character! */
1237
char *q;
1238
1239
*p++ = 0;
1240
for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1241
continue;
1242
*q = 0;
1243
}
1244
1245
*cmd = -1;
1246
for (c=cmdtab; c->name; ++c) {
1247
/* Is it an exact match? */
1248
if (! strcasecmp (buf, c->name)) {
1249
*cmd = c->command;
1250
break;
1251
}
1252
1253
/* Try short hand forms then... */
1254
if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
1255
if (*cmd != -1 && *cmd != c->command) {
1256
warnx("ambiguous command");
1257
return (0);
1258
}
1259
*cmd = c->command;
1260
}
1261
}
1262
1263
if (*cmd == -1) {
1264
warnx("invalid command, enter ``help'' for commands");
1265
return (0);
1266
}
1267
1268
while (isspace (*p))
1269
p++;
1270
return p;
1271
}
1272
1273
static int
1274
open_cd(void)
1275
{
1276
char devbuf[MAXPATHLEN];
1277
const char *dev;
1278
1279
if (fd > -1)
1280
return (1);
1281
1282
if (cdname) {
1283
if (*cdname == '/') {
1284
snprintf (devbuf, MAXPATHLEN, "%s", cdname);
1285
} else {
1286
snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
1287
}
1288
fd = open (dev = devbuf, O_RDONLY);
1289
} else {
1290
fd = open(dev = "/dev/cdrom", O_RDONLY);
1291
if (fd < 0 && errno == ENOENT)
1292
fd = open(dev = "/dev/cd0", O_RDONLY);
1293
}
1294
1295
if (fd < 0) {
1296
if (errno == ENXIO) {
1297
/* ENXIO has an overloaded meaning here.
1298
* The original "Device not configured" should
1299
* be interpreted as "No disc in drive %s". */
1300
warnx("no disc in drive %s", dev);
1301
return (0);
1302
}
1303
err(1, "%s", dev);
1304
}
1305
return (1);
1306
}
1307
1308