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