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