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