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
6 * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
8 * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
9 * A couple of further fixes to my own earlier "fixes".
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.
16 * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
17 * New eject algorithm.
18 * Some code style reformatting.
22 static const char rcsid[] =
28 #include <sys/ioctl.h>
29 #include <sys/param.h>
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 */
50 #ifndef DEFAULT_CD_DRIVE
51 # define DEFAULT_CD_DRIVE "/dev/cd0c"
54 #ifndef DEFAULT_CD_PARTITION
55 # define DEFAULT_CD_PARTITION "c"
74 #define CMD_PREVIOUS 17
75 #define STATUS_AUDIO 0x1
76 #define STATUS_MEDIA 0x2
77 #define STATUS_VOLUME 0x4
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, "" },
110 struct cd_toc_entry toc_buffer[100];
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));
151 for (c=cmdtab; c->name; ++c) {
155 for (i = c->min, s = c->name; *s; s++, i--) {
163 printf (" %s", c->args);
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");
172 fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
176 char *use_cdrom_instead(const char *old_envvar)
180 device = getenv(old_envvar);
182 warnx("%s environment variable deprecated, "
183 "please use CDROM in the future.", old_envvar);
188 int main (int argc, char **argv)
194 switch (getopt (argc, argv, "svhf:")) {
215 if (argc > 0 && ! strcasecmp (*argv, "help"))
219 cdname = getenv("CDROM");
223 cdname = use_cdrom_instead("MUSIC_CD");
225 cdname = use_cdrom_instead("CD_DRIVE");
227 cdname = use_cdrom_instead("DISC");
229 cdname = use_cdrom_instead("CDPLAY");
232 cdname = DEFAULT_CD_DRIVE;
233 warnx("no CD device name specified, defaulting to %s", cdname);
240 for (p=buf; argc-->0; ++argv) {
241 len = strlen (*argv);
243 if (p + len >= buf + sizeof (buf) - 1)
253 arg = parse (buf, &cmd);
254 return (run (cmd, arg));
258 verbose = isatty (0);
261 printf ("Compact Disc Control utility, version %s\n", VERSION);
262 printf ("Type `?' for command list\n\n");
267 if (run (cmd, arg) < 0) {
277 int run (int cmd, char *arg)
287 if (fd < 0 && ! open_cd ())
293 if (fd < 0 && ! open_cd ())
299 if (fd < 0 && ! open_cd ())
302 return pstatus (arg);
306 if (fd < 0 && ! open_cd ())
309 while (isspace (*arg))
312 return next_prev (arg, cmd);
315 if (fd < 0 && ! open_cd ())
318 return ioctl (fd, CDIOCPAUSE);
321 if (fd < 0 && ! open_cd ())
324 return ioctl (fd, CDIOCRESUME);
327 if (fd < 0 && ! open_cd ())
330 rc = ioctl (fd, CDIOCSTOP);
332 (void) ioctl (fd, CDIOCALLOW);
337 if (fd < 0 && ! open_cd ())
340 rc = ioctl (fd, CDIOCRESET);
348 if (fd < 0 && ! open_cd ())
351 if (! strcasecmp (arg, "on"))
352 return ioctl (fd, CDIOCSETDEBUG);
354 if (! strcasecmp (arg, "off"))
355 return ioctl (fd, CDIOCCLRDEBUG);
357 warnx("invalid command arguments");
362 if (fd < 0 && ! open_cd ())
365 (void) ioctl (fd, CDIOCALLOW);
366 rc = ioctl (fd, CDIOCEJECT);
372 if (fd < 0 && ! open_cd ())
375 (void) ioctl (fd, CDIOCALLOW);
376 rc = ioctl (fd, CDIOCCLOSE);
384 if (fd < 0 && ! open_cd ())
387 while (isspace (*arg))
393 if (! strcasecmp (arg, "msf"))
395 else if (! strcasecmp (arg, "lba"))
398 warnx("invalid command arguments");
402 if (fd < 0 && !open_cd ())
405 if (! strncasecmp (arg, "left", strlen(arg)))
406 return ioctl (fd, CDIOCSETLEFT);
408 if (! strncasecmp (arg, "right", strlen(arg)))
409 return ioctl (fd, CDIOCSETRIGHT);
411 if (! strncasecmp (arg, "mono", strlen(arg)))
412 return ioctl (fd, CDIOCSETMONO);
414 if (! strncasecmp (arg, "stereo", strlen(arg)))
415 return ioctl (fd, CDIOCSETSTERIO);
417 if (! strncasecmp (arg, "mute", strlen(arg)))
418 return ioctl (fd, CDIOCSETMUTE);
420 if (2 != sscanf (arg, "%d %d", &l, &r)) {
421 warnx("invalid command arguments");
425 return setvol (l, r);
437 struct ioc_toc_header h;
439 int rc, start, end = 0, istart = 1, iend = 1;
441 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
446 n = h.ending_track - h.starting_track + 1;
447 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
452 if (! arg || ! *arg) {
453 /* Play the whole disc */
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));
459 return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
462 if (strchr (arg, '#')) {
463 /* Play block #blk [ len ] */
466 if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
467 1 != sscanf (arg, "#%d", &blk))
472 len = msf2lba (toc_buffer[n].addr.msf.minute,
473 toc_buffer[n].addr.msf.second,
474 toc_buffer[n].addr.msf.frame) - blk;
476 len = ntohl(toc_buffer[n].addr.lba) - blk;
478 return play_blocks (blk, len);
481 if (strchr (arg, ':')) {
483 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
485 * Will now also undestand timed addresses relative
486 * to the beginning of a track in the form...
488 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
491 unsigned m1, m2, s1, s2, f1, f2;
492 unsigned char tm, ts, tf;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
551 tr2 = m2 = s2 = f2 = f1 = 0;
552 if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
553 goto Play_Relative_Addresses;
555 tr2 = m2 = s2 = f2 = f1 = 0;
556 goto Try_Absolute_Timed_Addresses;
558 Play_Relative_Addresses:
565 tm = toc_buffer[tr1].addr.msf.minute;
566 ts = toc_buffer[tr1].addr.msf.second;
567 tf = toc_buffer[tr1].addr.msf.frame;
569 lba2msf(ntohl(toc_buffer[tr1].addr.lba),
576 printf ("Track %d is not that long.\n", tr1);
597 if (m2 || s2 || f2) {
615 m2 = toc_buffer[n].addr.msf.minute;
616 s2 = toc_buffer[n].addr.msf.second;
617 f2 = toc_buffer[n].addr.msf.frame;
619 lba2msf(ntohl(toc_buffer[n].addr.lba),
626 } else if (tr2 > n) {
633 tm = toc_buffer[tr2].addr.msf.minute;
634 ts = toc_buffer[tr2].addr.msf.second;
635 tf = toc_buffer[tr2].addr.msf.frame;
637 lba2msf(ntohl(toc_buffer[tr2].addr.lba),
655 tm = toc_buffer[n].addr.msf.minute;
656 ts = toc_buffer[n].addr.msf.second;
657 tf = toc_buffer[n].addr.msf.frame;
659 lba2msf(ntohl(toc_buffer[n].addr.lba),
667 printf ("The playing time of the disc is not that long.\n");
670 return (play_msf (m1, s1, f1, m2, s2, f2));
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))
684 m2 = toc_buffer[n].addr.msf.minute;
685 s2 = toc_buffer[n].addr.msf.second;
686 f2 = toc_buffer[n].addr.msf.frame;
688 lba2msf(ntohl(toc_buffer[n].addr.lba),
695 return play_msf (m1, s1, f1, m2, s2, f2);
699 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
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))
711 return (play_track (start, istart, end, iend));
714 warnx("invalid command arguments");
718 int next_prev (char *arg, int cmd)
720 struct ioc_toc_header h;
721 int dir, junk, n, off, rc, trk;
723 dir = (cmd == CMD_NEXT) ? 1 : -1;
724 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
728 n = h.ending_track - h.starting_track + 1;
729 rc = status (&trk, &junk, &junk, &junk);
734 if (sscanf (arg, "%u", &off) != 1) {
735 warnx("invalid command argument");
742 if (trk > h.ending_track)
745 return (play_track (trk, 1, n, 1));
748 const char *strstatus (int 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 ("??");
761 int pstatus (char *arg)
764 struct ioc_read_subchannel ss;
765 struct cd_sub_channel_info data;
766 int rc, trk, m, s, f;
768 char *p, vmcn[(4 * 15) + 1];
770 while ((p = strtok(arg, " \t"))) {
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;
779 warnx("invalid command arguments");
784 what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
785 if (what & STATUS_AUDIO) {
786 rc = status (&trk, &m, &s, &f);
789 printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
790 rc, strstatus (rc), trk, m, s, f);
792 printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
794 printf ("No current status info available\n");
796 if (what & STATUS_MEDIA) {
797 bzero (&ss, sizeof (ss));
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);
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])
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);
815 printf("No media catalog info available\n");
817 if (what & STATUS_VOLUME) {
818 rc = ioctl (fd, CDIOCGETVOL, &v);
821 printf ("Left volume = %d, right volume = %d\n",
824 printf ("%d %d\n", v.vol[0], v.vol[1]);
826 printf ("No volume level info available\n");
833 * Convert an integer to its text string representation, and
834 * compute its checksum. Used by dbprog_discid to derive the
838 * n - The integer value.
841 * The integer checksum.
850 /* For backward compatibility this algorithm must not change */
851 sprintf(buf, "%u", n);
852 for (p = buf; *p != '\0'; p++)
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.
866 * s - Pointer to the curstat_t structure.
869 * The integer disc ID.
874 struct ioc_toc_header h;
880 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
883 ntr = h.ending_track - h.starting_track + 1;
886 rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
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));
896 t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
897 ((TC_MM(i) * 60) + TC_SS(i));
900 return((n % 0xff) << 24 | t << 8 | ntr);
907 id = dbprog_discid();
912 printf ("%08x\n",id);
917 int info (char *arg __unused)
919 struct ioc_toc_header h;
922 rc = ioctl (fd, CDIOREADTOCHEADER, &h);
925 printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
926 h.starting_track, h.ending_track, h.len);
928 printf ("%d %d %d\n", h.starting_track,
929 h.ending_track, h.len);
931 warn("getting toc header");
935 n = h.ending_track - h.starting_track + 1;
936 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
941 printf ("track start duration block length type\n");
942 printf ("-------------------------------------------------\n");
945 for (i = 0; i < n; i++) {
946 printf ("%5d ", toc_buffer[i].track);
947 prtrack (toc_buffer + i, 0);
949 printf ("%5d ", toc_buffer[n].track);
950 prtrack (toc_buffer + n, 1);
954 void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
956 lba += 150; /* block start offset */
957 lba &= 0xffffff; /* negative lbas use only 24 bits */
958 *m = lba / (60 * 75);
964 unsigned int msf2lba (u_char m, u_char s, u_char f)
966 return (((m * 60) + s) * 75 + f) - 150;
969 void prtrack (struct cd_toc_entry *e, int lastflag)
971 int block, next, len;
975 /* Print track start */
976 printf ("%2d:%02d.%02d ", e->addr.msf.minute,
977 e->addr.msf.second, e->addr.msf.frame);
979 block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
982 block = ntohl(e->addr.lba);
983 lba2msf(block, &m, &s, &f);
984 /* Print track start */
985 printf ("%2d:%02d.%02d ", m, s, f);
988 /* Last track -- print block */
989 printf (" - %6d - -\n", block);
994 next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
995 e[1].addr.msf.frame);
997 next = ntohl(e[1].addr.lba);
999 lba2msf (len, &m, &s, &f);
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");
1006 int play_track (int tstart, int istart, int tend, int iend)
1008 struct ioc_play_track t;
1010 t.start_track = tstart;
1011 t.start_index = istart;
1015 return ioctl (fd, CDIOCPLAYTRACKS, &t);
1018 int play_blocks (int blk, int len)
1020 struct ioc_play_blocks t;
1025 return ioctl (fd, CDIOCPLAYBLOCKS, &t);
1028 int setvol (int left, int right)
1037 return ioctl (fd, CDIOCSETVOL, &v);
1040 int read_toc_entrys (int len)
1042 struct ioc_read_toc_entry t;
1044 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1045 t.starting_track = 0;
1047 t.data = toc_buffer;
1049 return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
1052 int play_msf (int start_m, int start_s, int start_f,
1053 int end_m, int end_s, int end_f)
1055 struct ioc_play_msf a;
1057 a.start_m = start_m;
1058 a.start_s = start_s;
1059 a.start_f = start_f;
1064 return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
1067 int status (int *trk, int *min, int *sec, int *frame)
1069 struct ioc_read_subchannel s;
1070 struct cd_sub_channel_info data;
1073 bzero (&s, sizeof (s));
1075 s.data_len = sizeof (data);
1076 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
1077 s.data_format = CD_CURRENT_POSITION;
1079 if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
1082 *trk = s.data->what.position.track_number;
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;
1088 lba2msf(ntohl(s.data->what.position.reladdr.lba),
1095 return s.data->header.audio_status;
1101 return ("cdcontrol> ");
1108 static EditLine *el = NULL;
1109 static History *hist = NULL;
1111 static char buf[MAXLINE];
1114 const char *bp = NULL;
1120 el = el_init("cdcontrol", stdin, stdout,
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);
1130 if ((bp = el_gets(el, &num)) == NULL || num == 0) {
1132 fprintf (stderr, "\r\n");
1136 len = (num > MAXLINE) ? MAXLINE : num;
1137 memcpy(buf, bp, len);
1139 history(hist, &he, H_ENTER, bp);
1143 if (! fgets (buf, sizeof (buf), stdin)) {
1145 fprintf (stderr, "\r\n");
1149 p = parse (buf, cmd);
1154 char *parse (char *buf, int *cmd)
1160 for (p=buf; isspace (*p); p++)
1163 if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
1166 } else if (*p == '+') {
1169 } else if (*p == '-') {
1170 *cmd = CMD_PREVIOUS;
1174 for (buf = p; *p && ! isspace (*p); p++)
1181 if (*p) { /* It must be a spacing character! */
1185 for (q=p; *q && *q != '\n' && *q != '\r'; q++)
1191 for (c=cmdtab; c->name; ++c) {
1192 /* Is it an exact match? */
1193 if (! strcasecmp (buf, c->name)) {
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");
1209 warnx("invalid command, enter ``help'' for commands");
1213 while (isspace (*p))
1220 char devbuf[MAXPATHLEN];
1225 if (*cdname == '/') {
1226 snprintf (devbuf, MAXPATHLEN, "%s", cdname);
1228 snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
1231 fd = open (devbuf, O_RDONLY);
1233 if (fd < 0 && errno == ENOENT) {
1234 strcat (devbuf, DEFAULT_CD_PARTITION);
1235 fd = open (devbuf, O_RDONLY);
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);
1246 err(1, "%s", devbuf);