cdcontrol.c revision 50039
110099Sjkh/* 213884Sache * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>. 310099Sjkh * Based on the non-X based CD player by Jean-Marc Zucconi and 413832Sache * Andrey A. Chernov. 513884Sache * 613985Sache * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>. 713985Sache * 813985Sache * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 913985Sache * A couple of further fixes to my own earlier "fixes". 1013985Sache * 1113985Sache * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 1213985Sache * Added an ability to specify addresses relative to the 1313985Sache * beginning of a track. This is in fact a variation of 1413985Sache * doing the simple play_msf() call. 1513985Sache * 1613985Sache * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru> 1713985Sache * New eject algorithm. 1813985Sache * Some code style reformatting. 1910099Sjkh */ 2013985Sache 2129103Scharnier#ifndef lint 2229103Scharnierstatic const char rcsid[] = 2350039Smdodd "$Id: cdcontrol.c,v 1.18 1999/01/31 15:30:21 billf Exp $"; 2429103Scharnier#endif /* not lint */ 2529103Scharnier 2613985Sache#include <ctype.h> 2729103Scharnier#include <err.h> 2829103Scharnier#include <errno.h> 2910099Sjkh#include <stdio.h> 3010099Sjkh#include <stdlib.h> 3110099Sjkh#include <string.h> 3210099Sjkh#include <unistd.h> 3310099Sjkh#include <sys/file.h> 3410099Sjkh#include <sys/cdio.h> 3510099Sjkh#include <sys/ioctl.h> 3650039Smdodd#include <histedit.h> 3710099Sjkh 3813884Sache#define VERSION "2.0" 3910099Sjkh 4013985Sache#define ASTS_INVALID 0x00 /* Audio status byte not valid */ 4113985Sache#define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 4213985Sache#define ASTS_PAUSED 0x12 /* Audio play operation paused */ 4313985Sache#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 4413985Sache#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 4513985Sache#define ASTS_VOID 0x15 /* No current audio status to return */ 4610099Sjkh 4713985Sache#ifndef DEFAULT_CD_DRIVE 4813985Sache# define DEFAULT_CD_DRIVE "/dev/cd0c" 4913985Sache#endif 5013985Sache 5113985Sache#ifndef DEFAULT_CD_PARTITION 5213985Sache# define DEFAULT_CD_PARTITION "c" 5313985Sache#endif 5413985Sache 5513985Sache#define CMD_DEBUG 1 5613985Sache#define CMD_EJECT 2 5713985Sache#define CMD_HELP 3 5813985Sache#define CMD_INFO 4 5913985Sache#define CMD_PAUSE 5 6013985Sache#define CMD_PLAY 6 6113985Sache#define CMD_QUIT 7 6213985Sache#define CMD_RESUME 8 6313985Sache#define CMD_STOP 9 6413985Sache#define CMD_VOLUME 10 6513985Sache#define CMD_CLOSE 11 6613985Sache#define CMD_RESET 12 6713985Sache#define CMD_SET 13 6813985Sache#define CMD_STATUS 14 6932782Sjmz#define STATUS_AUDIO 0x1 7032782Sjmz#define STATUS_MEDIA 0x2 7132782Sjmz#define STATUS_VOLUME 0x4 7213985Sache 7310099Sjkhstruct cmdtab { 7410099Sjkh int command; 7510099Sjkh char *name; 7613985Sache unsigned min; 7710099Sjkh char *args; 7810099Sjkh} cmdtab[] = { 7913985Sache{ CMD_CLOSE, "close", 1, "" }, 8013985Sache{ CMD_DEBUG, "debug", 1, "on | off" }, 8113985Sache{ CMD_EJECT, "eject", 1, "" }, 8213985Sache{ CMD_HELP, "?", 1, 0 }, 8313985Sache{ CMD_HELP, "help", 1, "" }, 8413985Sache{ CMD_INFO, "info", 1, "" }, 8513985Sache{ CMD_PAUSE, "pause", 2, "" }, 8613985Sache{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 8713985Sache{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 8813985Sache{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 8913985Sache{ CMD_PLAY, "play", 1, "[#block [len]]" }, 9013985Sache{ CMD_QUIT, "quit", 1, "" }, 9113985Sache{ CMD_RESET, "reset", 4, "" }, 9213985Sache{ CMD_RESUME, "resume", 1, "" }, 9313985Sache{ CMD_SET, "set", 2, "msf | lba" }, 9432782Sjmz{ CMD_STATUS, "status", 1, "[audio | media | volume]" }, 9513985Sache{ CMD_STOP, "stop", 3, "" }, 9613985Sache{ CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" }, 9713985Sache{ 0, } 9810099Sjkh}; 9910099Sjkh 10013985Sachestruct cd_toc_entry toc_buffer[100]; 10110099Sjkh 10213985Sacheconst char *cdname; 10313985Sacheint fd = -1; 10413985Sacheint verbose = 1; 10513985Sacheint msf = 1; 10610099Sjkh 10713985Sacheint setvol __P((int, int)); 10813985Sacheint read_toc_entrys __P((int)); 10913985Sacheint play_msf __P((int, int, int, int, int, int)); 11013985Sacheint play_track __P((int, int, int, int)); 11113985Sacheint get_vol __P((int *, int *)); 11213985Sacheint status __P((int *, int *, int *, int *)); 11313985Sacheint open_cd __P((void)); 11413985Sacheint play __P((char *arg)); 11513985Sacheint info __P((char *arg)); 11613985Sacheint pstatus __P((char *arg)); 11713985Sachechar *input __P((int *)); 11813985Sachevoid prtrack __P((struct cd_toc_entry *e, int lastflag)); 11913985Sachevoid lba2msf __P((unsigned long lba, 12013985Sache u_char *m, u_char *s, u_char *f)); 12113985Sacheunsigned int msf2lba __P((u_char m, u_char s, u_char f)); 12213985Sacheint play_blocks __P((int blk, int len)); 12313985Sacheint run __P((int cmd, char *arg)); 12413985Sachechar *parse __P((char *buf, int *cmd)); 12510099Sjkh 12610099Sjkhvoid help () 12710099Sjkh{ 12810099Sjkh struct cmdtab *c; 12913985Sache char *s, n; 13013985Sache int i; 13110099Sjkh 13210099Sjkh for (c=cmdtab; c->name; ++c) { 13310099Sjkh if (! c->args) 13410099Sjkh continue; 13513985Sache printf("\t"); 13613985Sache for (i = c->min, s = c->name; *s; s++, i--) { 13713985Sache if (i > 0) 13813985Sache n = toupper(*s); 13913985Sache else 14013985Sache n = *s; 14113985Sache putchar(n); 14213985Sache } 14310099Sjkh if (*c->args) 14410099Sjkh printf (" %s", c->args); 14510099Sjkh printf ("\n"); 14610099Sjkh } 14713985Sache printf ("\n\tThe word \"play\" is not required for the play commands.\n"); 14813985Sache printf ("\tThe plain target address is taken as a synonym for play.\n"); 14910099Sjkh} 15010099Sjkh 15110099Sjkhvoid usage () 15210099Sjkh{ 15343479Sbillf fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n"); 15410099Sjkh exit (1); 15510099Sjkh} 15610099Sjkh 15710099Sjkhint main (int argc, char **argv) 15810099Sjkh{ 15910099Sjkh int cmd; 16010099Sjkh char *arg; 16110099Sjkh 16213985Sache cdname = getenv ("MUSIC_CD"); 16310099Sjkh if (! cdname) 16413985Sache cdname = getenv ("CD_DRIVE"); 16513985Sache if (! cdname) 16613985Sache cdname = getenv ("DISC"); 16713985Sache if (! cdname) 16810099Sjkh cdname = getenv ("CDPLAY"); 16910099Sjkh 17010099Sjkh for (;;) { 17110099Sjkh switch (getopt (argc, argv, "svhf:")) { 17210099Sjkh case EOF: 17310099Sjkh break; 17410099Sjkh case 's': 17510099Sjkh verbose = 0; 17610099Sjkh continue; 17710099Sjkh case 'v': 17810099Sjkh verbose = 2; 17910099Sjkh continue; 18010099Sjkh case 'f': 18110099Sjkh cdname = optarg; 18210099Sjkh continue; 18310099Sjkh case 'h': 18410099Sjkh default: 18510099Sjkh usage (); 18610099Sjkh } 18710099Sjkh break; 18810099Sjkh } 18910099Sjkh argc -= optind; 19010099Sjkh argv += optind; 19110099Sjkh 19213985Sache if (argc > 0 && ! strcasecmp (*argv, "help")) 19310099Sjkh usage (); 19410099Sjkh 19510099Sjkh if (! cdname) { 19613985Sache cdname = DEFAULT_CD_DRIVE; 19729103Scharnier warnx("no CD device name specified, defaulting to %s", cdname); 19810099Sjkh } 19910099Sjkh 20010099Sjkh if (argc > 0) { 20110099Sjkh char buf[80], *p; 20210099Sjkh int len; 20310099Sjkh 20413985Sache for (p=buf; argc-->0; ++argv) { 20510099Sjkh len = strlen (*argv); 20613985Sache 20710099Sjkh if (p + len >= buf + sizeof (buf) - 1) 20810099Sjkh usage (); 20913985Sache 21010099Sjkh if (p > buf) 21110099Sjkh *p++ = ' '; 21213985Sache 21310099Sjkh strcpy (p, *argv); 21410099Sjkh p += len; 21510099Sjkh } 21610099Sjkh *p = 0; 21710099Sjkh arg = parse (buf, &cmd); 21813985Sache return (run (cmd, arg)); 21910099Sjkh } 22010099Sjkh 22110099Sjkh if (verbose == 1) 22210099Sjkh verbose = isatty (0); 22313985Sache 22410099Sjkh if (verbose) { 22513985Sache printf ("Compact Disc Control utility, version %s\n", VERSION); 22610099Sjkh printf ("Type `?' for command list\n\n"); 22710099Sjkh } 22810099Sjkh 22910099Sjkh for (;;) { 23010099Sjkh arg = input (&cmd); 23110099Sjkh if (run (cmd, arg) < 0) { 23210099Sjkh if (verbose) 23329103Scharnier warn(NULL); 23410099Sjkh close (fd); 23510099Sjkh fd = -1; 23610099Sjkh } 23710099Sjkh fflush (stdout); 23810099Sjkh } 23910099Sjkh} 24010099Sjkh 24110099Sjkhint run (int cmd, char *arg) 24210099Sjkh{ 24310099Sjkh int l, r, rc; 24410099Sjkh 24510099Sjkh switch (cmd) { 24613985Sache 24710099Sjkh case CMD_QUIT: 24810099Sjkh exit (0); 24910099Sjkh 25013985Sache case CMD_INFO: 25113985Sache if (fd < 0 && ! open_cd ()) 25213985Sache return (0); 25310099Sjkh 25410099Sjkh return info (arg); 25510099Sjkh 25613884Sache case CMD_STATUS: 25713985Sache if (fd < 0 && ! open_cd ()) 25813985Sache return (0); 25913985Sache 26013884Sache return pstatus (arg); 26113884Sache 26210099Sjkh case CMD_PAUSE: 26313985Sache if (fd < 0 && ! open_cd ()) 26413985Sache return (0); 26513985Sache 26610099Sjkh return ioctl (fd, CDIOCPAUSE); 26710099Sjkh 26810099Sjkh case CMD_RESUME: 26913985Sache if (fd < 0 && ! open_cd ()) 27013985Sache return (0); 27113985Sache 27210099Sjkh return ioctl (fd, CDIOCRESUME); 27310099Sjkh 27410099Sjkh case CMD_STOP: 27513985Sache if (fd < 0 && ! open_cd ()) 27613985Sache return (0); 27710099Sjkh 27813985Sache rc = ioctl (fd, CDIOCSTOP); 27913985Sache 28013985Sache (void) ioctl (fd, CDIOCALLOW); 28113985Sache 28213985Sache return (rc); 28313985Sache 28413884Sache case CMD_RESET: 28513985Sache if (fd < 0 && ! open_cd ()) 28613985Sache return (0); 28713985Sache 28813884Sache rc = ioctl (fd, CDIOCRESET); 28913884Sache if (rc < 0) 29013884Sache return rc; 29113884Sache close(fd); 29213884Sache fd = -1; 29313884Sache return (0); 29413884Sache 29510099Sjkh case CMD_DEBUG: 29613985Sache if (fd < 0 && ! open_cd ()) 29713985Sache return (0); 29813985Sache 29913985Sache if (! strcasecmp (arg, "on")) 30010099Sjkh return ioctl (fd, CDIOCSETDEBUG); 30113985Sache 30213985Sache if (! strcasecmp (arg, "off")) 30310099Sjkh return ioctl (fd, CDIOCCLRDEBUG); 30413985Sache 30529103Scharnier warnx("invalid command arguments"); 30613985Sache 30710099Sjkh return (0); 30810099Sjkh 30910099Sjkh case CMD_EJECT: 31013985Sache if (fd < 0 && ! open_cd ()) 31113985Sache return (0); 31213985Sache 31310099Sjkh (void) ioctl (fd, CDIOCALLOW); 31410099Sjkh rc = ioctl (fd, CDIOCEJECT); 31510099Sjkh if (rc < 0) 31610099Sjkh return (rc); 31713865Sache return (0); 31813865Sache 31913985Sache case CMD_CLOSE: 32013985Sache if (fd < 0 && ! open_cd ()) 32113985Sache return (0); 32213985Sache 32313985Sache (void) ioctl (fd, CDIOCALLOW); 32413865Sache rc = ioctl (fd, CDIOCCLOSE); 32513865Sache if (rc < 0) 32613865Sache return (rc); 32713865Sache close(fd); 32810099Sjkh fd = -1; 32910099Sjkh return (0); 33010099Sjkh 33110099Sjkh case CMD_PLAY: 33213985Sache if (fd < 0 && ! open_cd ()) 33313985Sache return (0); 33413985Sache 33513985Sache while (isspace (*arg)) 33613985Sache arg++; 33713985Sache 33810099Sjkh return play (arg); 33910099Sjkh 34013884Sache case CMD_SET: 34113985Sache if (! strcasecmp (arg, "msf")) 34213884Sache msf = 1; 34313985Sache else if (! strcasecmp (arg, "lba")) 34413884Sache msf = 0; 34513884Sache else 34629103Scharnier warnx("invalid command arguments"); 34713884Sache return (0); 34813884Sache 34910099Sjkh case CMD_VOLUME: 35013985Sache if (fd < 0 && !open_cd ()) 35113985Sache return (0); 35210099Sjkh 35313985Sache if (! strncasecmp (arg, "left", strlen(arg))) 35410099Sjkh return ioctl (fd, CDIOCSETLEFT); 35513985Sache 35613985Sache if (! strncasecmp (arg, "right", strlen(arg))) 35710099Sjkh return ioctl (fd, CDIOCSETRIGHT); 35813985Sache 35913985Sache if (! strncasecmp (arg, "mono", strlen(arg))) 36010099Sjkh return ioctl (fd, CDIOCSETMONO); 36113985Sache 36213985Sache if (! strncasecmp (arg, "stereo", strlen(arg))) 36310099Sjkh return ioctl (fd, CDIOCSETSTERIO); 36410099Sjkh 36513985Sache if (! strncasecmp (arg, "mute", strlen(arg))) 36613985Sache return ioctl (fd, CDIOCSETMUTE); 36713985Sache 36810099Sjkh if (2 != sscanf (arg, "%d %d", &l, &r)) { 36929103Scharnier warnx("invalid command arguments"); 37010099Sjkh return (0); 37110099Sjkh } 37213985Sache 37310099Sjkh return setvol (l, r); 37413985Sache 37513985Sache default: 37613985Sache case CMD_HELP: 37713985Sache help (); 37813985Sache return (0); 37913985Sache 38010099Sjkh } 38110099Sjkh} 38210099Sjkh 38310099Sjkhint play (char *arg) 38410099Sjkh{ 38510099Sjkh struct ioc_toc_header h; 38610099Sjkh int rc, n, start, end = 0, istart = 1, iend = 1; 38710099Sjkh 38810099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 38913985Sache 39010099Sjkh if (rc < 0) 39110099Sjkh return (rc); 39210099Sjkh 39310099Sjkh n = h.ending_track - h.starting_track + 1; 39410099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 39513985Sache 39610099Sjkh if (rc < 0) 39710099Sjkh return (rc); 39810099Sjkh 39913989Sache if (! arg || ! *arg) { 40013985Sache /* Play the whole disc */ 40113989Sache if (msf) 40213989Sache return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, 40313989Sache toc_buffer[n].addr.msf.second, 40413989Sache toc_buffer[n].addr.msf.frame)); 40513989Sache else 40613989Sache return play_blocks (0, ntohl(toc_buffer[n].addr.lba)); 40713989Sache } 40810099Sjkh 40910099Sjkh if (strchr (arg, '#')) { 41013985Sache /* Play block #blk [ len ] */ 41110099Sjkh int blk, len = 0; 41210099Sjkh 41310099Sjkh if (2 != sscanf (arg, "#%d%d", &blk, &len) && 41413985Sache 1 != sscanf (arg, "#%d", &blk)) 41513985Sache goto Clean_up; 41613985Sache 41713989Sache if (len == 0) { 41813989Sache if (msf) 41913989Sache len = msf2lba (toc_buffer[n].addr.msf.minute, 42013989Sache toc_buffer[n].addr.msf.second, 42113989Sache toc_buffer[n].addr.msf.frame) - blk; 42213989Sache else 42313989Sache len = ntohl(toc_buffer[n].addr.lba) - blk; 42413989Sache } 42510099Sjkh return play_blocks (blk, len); 42610099Sjkh } 42710099Sjkh 42810099Sjkh if (strchr (arg, ':')) { 42910099Sjkh /* 43010099Sjkh * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 43113985Sache * 43213985Sache * Will now also undestand timed addresses relative 43313985Sache * to the beginning of a track in the form... 43413985Sache * 43513985Sache * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 43610099Sjkh */ 43713985Sache unsigned tr1, tr2; 43813985Sache unsigned m1, m2, s1, s2, f1, f2; 43913989Sache unsigned char tm, ts, tf; 44010099Sjkh 44113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44213985Sache if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", 44313985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 44413985Sache goto Play_Relative_Addresses; 44513985Sache 44613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44713985Sache if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", 44813985Sache &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 44913985Sache goto Play_Relative_Addresses; 45013985Sache 45113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45213985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", 45313985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 45413985Sache goto Play_Relative_Addresses; 45513985Sache 45613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45713985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", 45813985Sache &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 45913985Sache goto Play_Relative_Addresses; 46013985Sache 46113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46213985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", 46313985Sache &tr1, &m1, &s1, &f1, &m2, &s2)) 46413985Sache goto Play_Relative_Addresses; 46513985Sache 46613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46713985Sache if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", 46813985Sache &tr1, &m1, &s1, &m2, &s2, &f2)) 46913985Sache goto Play_Relative_Addresses; 47013985Sache 47113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47213985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d %d", 47313985Sache &tr1, &m1, &s1, &f1, &tr2, &m2)) 47413985Sache goto Play_Relative_Addresses; 47513985Sache 47613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47713985Sache if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 47813985Sache goto Play_Relative_Addresses; 47913985Sache 48013985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48113985Sache if (5 == sscanf (arg, "%d %d:%d %d %d", 48213985Sache &tr1, &m1, &s1, &tr2, &m2)) 48313985Sache goto Play_Relative_Addresses; 48413985Sache 48513985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48613985Sache if (5 == sscanf (arg, "%d %d:%d.%d %d", 48713985Sache &tr1, &m1, &s1, &f1, &tr2)) 48813985Sache goto Play_Relative_Addresses; 48913985Sache 49013985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49113985Sache if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 49213985Sache goto Play_Relative_Addresses; 49313985Sache 49413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49513985Sache if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 49613985Sache goto Play_Relative_Addresses; 49713985Sache 49813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49913985Sache if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) 50013985Sache goto Play_Relative_Addresses; 50113985Sache 50213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 50313985Sache goto Try_Absolute_Timed_Addresses; 50413985Sache 50513985SachePlay_Relative_Addresses: 50613985Sache if (! tr1) 50713985Sache tr1 = 1; 50813985Sache else if (tr1 > n) 50913985Sache tr1 = n; 51013985Sache 51113989Sache if (msf) { 51213989Sache tm = toc_buffer[tr1].addr.msf.minute; 51313989Sache ts = toc_buffer[tr1].addr.msf.second; 51413989Sache tf = toc_buffer[tr1].addr.msf.frame; 51513989Sache } else 51613989Sache lba2msf(ntohl(toc_buffer[tr1].addr.lba), 51713989Sache &tm, &ts, &tf); 51813989Sache if ((m1 > tm) 51913989Sache || ((m1 == tm) 52013989Sache && ((s1 > ts) 52113989Sache || ((s1 == ts) 52213989Sache && (f1 > tf))))) { 52313985Sache printf ("Track %d is not that long.\n", tr1); 52413985Sache return (0); 52513985Sache } 52613985Sache 52713985Sache tr1--; 52813985Sache 52913989Sache f1 += tf; 53013985Sache if (f1 >= 75) { 53113985Sache s1 += f1 / 75; 53213985Sache f1 %= 75; 53313985Sache } 53413985Sache 53513989Sache s1 += ts; 53613985Sache if (s1 >= 60) { 53713985Sache m1 += s1 / 60; 53813985Sache s1 %= 60; 53913985Sache } 54013985Sache 54113989Sache m1 += tm; 54213985Sache 54313985Sache if (! tr2) { 54413985Sache if (m2 || s2 || f2) { 54513985Sache tr2 = tr1; 54613985Sache f2 += f1; 54713985Sache if (f2 >= 75) { 54813985Sache s2 += f2 / 75; 54913985Sache f2 %= 75; 55013985Sache } 55113985Sache 55213985Sache s2 += s1; 55313985Sache if (s2 > 60) { 55413985Sache m2 += s2 / 60; 55513985Sache s2 %= 60; 55613985Sache } 55713985Sache 55813985Sache m2 += m1; 55913985Sache } else { 56013985Sache tr2 = n; 56113989Sache if (msf) { 56213989Sache m2 = toc_buffer[n].addr.msf.minute; 56313989Sache s2 = toc_buffer[n].addr.msf.second; 56413989Sache f2 = toc_buffer[n].addr.msf.frame; 56513989Sache } else { 56613989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 56713989Sache &tm, &ts, &tf); 56813989Sache m2 = tm; 56913989Sache s2 = ts; 57013989Sache f2 = tf; 57113989Sache } 57213985Sache } 57313985Sache } else if (tr2 > n) { 57413985Sache tr2 = n; 57513985Sache m2 = s2 = f2 = 0; 57613985Sache } else { 57713985Sache if (m2 || s2 || f2) 57813985Sache tr2--; 57913989Sache if (msf) { 58013989Sache tm = toc_buffer[tr2].addr.msf.minute; 58113989Sache ts = toc_buffer[tr2].addr.msf.second; 58213989Sache tf = toc_buffer[tr2].addr.msf.frame; 58313989Sache } else 58413989Sache lba2msf(ntohl(toc_buffer[tr2].addr.lba), 58513989Sache &tm, &ts, &tf); 58613989Sache f2 += tf; 58713985Sache if (f2 >= 75) { 58813985Sache s2 += f2 / 75; 58913985Sache f2 %= 75; 59013985Sache } 59113985Sache 59213989Sache s2 += ts; 59313985Sache if (s2 > 60) { 59413985Sache m2 += s2 / 60; 59513985Sache s2 %= 60; 59613985Sache } 59713985Sache 59813989Sache m2 += tm; 59913985Sache } 60013985Sache 60113989Sache if (msf) { 60213989Sache tm = toc_buffer[n].addr.msf.minute; 60313989Sache ts = toc_buffer[n].addr.msf.second; 60413989Sache tf = toc_buffer[n].addr.msf.frame; 60513989Sache } else 60613989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 60713989Sache &tm, &ts, &tf); 60813985Sache if ((tr2 < n) 60913989Sache && ((m2 > tm) 61013989Sache || ((m2 == tm) 61113989Sache && ((s2 > ts) 61213989Sache || ((s2 == ts) 61313989Sache && (f2 > tf)))))) { 61413985Sache printf ("The playing time of the disc is not that long.\n"); 61513985Sache return (0); 61613985Sache } 61713985Sache return (play_msf (m1, s1, f1, m2, s2, f2)); 61813985Sache 61913985SacheTry_Absolute_Timed_Addresses: 62013985Sache if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", 62113985Sache &m1, &s1, &f1, &m2, &s2, &f2) && 62210099Sjkh 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 62310099Sjkh 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 62410099Sjkh 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 62510099Sjkh 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 62610099Sjkh 2 != sscanf (arg, "%d:%d", &m1, &s1)) 62713985Sache goto Clean_up; 62813985Sache 62910099Sjkh if (m2 == 0) { 63013989Sache if (msf) { 63113989Sache m2 = toc_buffer[n].addr.msf.minute; 63213989Sache s2 = toc_buffer[n].addr.msf.second; 63313989Sache f2 = toc_buffer[n].addr.msf.frame; 63413989Sache } else { 63513989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 63613989Sache &tm, &ts, &tf); 63713989Sache m2 = tm; 63813989Sache s2 = ts; 63913989Sache f2 = tf; 64013989Sache } 64110099Sjkh } 64210099Sjkh return play_msf (m1, s1, f1, m2, s2, f2); 64310099Sjkh } 64410099Sjkh 64510099Sjkh /* 64610099Sjkh * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 64710099Sjkh */ 64810099Sjkh if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 64910099Sjkh 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 65010099Sjkh 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 65110099Sjkh 2 != sscanf (arg, "%d.%d", &start, &istart) && 65210099Sjkh 2 != sscanf (arg, "%d%d", &start, &end) && 65310099Sjkh 1 != sscanf (arg, "%d", &start)) 65413985Sache goto Clean_up; 65513985Sache 65610099Sjkh if (end == 0) 65710099Sjkh end = n; 65813985Sache return (play_track (start, istart, end, iend)); 65913985Sache 66013985SacheClean_up: 66129103Scharnier warnx("invalid command arguments"); 66213985Sache return (0); 66310099Sjkh} 66410099Sjkh 66510099Sjkhchar *strstatus (int sts) 66610099Sjkh{ 66710099Sjkh switch (sts) { 66810099Sjkh case ASTS_INVALID: return ("invalid"); 66910099Sjkh case ASTS_PLAYING: return ("playing"); 67010099Sjkh case ASTS_PAUSED: return ("paused"); 67110099Sjkh case ASTS_COMPLETED: return ("completed"); 67210099Sjkh case ASTS_ERROR: return ("error"); 67310099Sjkh case ASTS_VOID: return ("void"); 67410099Sjkh default: return ("??"); 67510099Sjkh } 67610099Sjkh} 67710099Sjkh 67813884Sacheint pstatus (char *arg) 67910099Sjkh{ 68010099Sjkh struct ioc_vol v; 68113888Sache struct ioc_read_subchannel ss; 68213888Sache struct cd_sub_channel_info data; 68313884Sache int rc, trk, m, s, f; 68432782Sjmz int what = 0; 68532782Sjmz char *p; 68610099Sjkh 68732782Sjmz while ((p = strtok(arg, " \t"))) { 68832782Sjmz arg = 0; 68932782Sjmz if (!strncasecmp(p, "audio", strlen(p))) 69032782Sjmz what |= STATUS_AUDIO; 69132782Sjmz else if (!strncasecmp(p, "media", strlen(p))) 69232782Sjmz what |= STATUS_MEDIA; 69332782Sjmz else if (!strncasecmp(p, "volume", strlen(p))) 69432782Sjmz what |= STATUS_VOLUME; 69532782Sjmz else { 69632782Sjmz warnx("invalid command arguments"); 69732782Sjmz return 0; 69832782Sjmz } 69932782Sjmz } 70032782Sjmz if (!what) 70132782Sjmz what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME; 70232782Sjmz if (what & STATUS_AUDIO) { 70332782Sjmz rc = status (&trk, &m, &s, &f); 70432782Sjmz if (rc >= 0) 70510099Sjkh if (verbose) 70632782Sjmz printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", 70732782Sjmz rc, strstatus (rc), trk, m, s, f); 70810099Sjkh else 70932782Sjmz printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 71032782Sjmz else 71113888Sache printf ("No current status info available\n"); 71232782Sjmz } 71332782Sjmz if (what & STATUS_MEDIA) { 71432782Sjmz bzero (&ss, sizeof (ss)); 71532782Sjmz ss.data = &data; 71632782Sjmz ss.data_len = sizeof (data); 71732782Sjmz ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 71832782Sjmz ss.data_format = CD_MEDIA_CATALOG; 71932782Sjmz rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); 72032782Sjmz if (rc >= 0) { 72113889Sache printf("Media catalog is %sactive", 72232782Sjmz ss.data->what.media_catalog.mc_valid ? "": "in"); 72316736Sache if (ss.data->what.media_catalog.mc_valid && 72416736Sache ss.data->what.media_catalog.mc_number[0]) 72532782Sjmz printf(", number \"%.15s\"", 72632782Sjmz ss.data->what.media_catalog.mc_number); 72713889Sache putchar('\n'); 72832782Sjmz } else 72913888Sache printf("No media catalog info available\n"); 73032782Sjmz } 73132782Sjmz if (what & STATUS_VOLUME) { 73232782Sjmz rc = ioctl (fd, CDIOCGETVOL, &v); 73332782Sjmz if (rc >= 0) 73410099Sjkh if (verbose) 73532782Sjmz printf ("Left volume = %d, right volume = %d\n", 73632782Sjmz v.vol[0], v.vol[1]); 73710099Sjkh else 73832782Sjmz printf ("%d %d\n", v.vol[0], v.vol[1]); 73932782Sjmz else 74013888Sache printf ("No volume level info available\n"); 74132782Sjmz } 74213884Sache return(0); 74313884Sache} 74410099Sjkh 74513884Sacheint info (char *arg) 74613884Sache{ 74713884Sache struct ioc_toc_header h; 74813884Sache int rc, i, n; 74913884Sache 75010099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 75113888Sache if (rc >= 0) { 75210099Sjkh if (verbose) 75310099Sjkh printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 75410099Sjkh h.starting_track, h.ending_track, h.len); 75510099Sjkh else 75610099Sjkh printf ("%d %d %d\n", h.starting_track, 75710099Sjkh h.ending_track, h.len); 75813888Sache } else { 75929103Scharnier warn("getting toc header"); 76010099Sjkh return (rc); 76110099Sjkh } 76210099Sjkh 76310099Sjkh n = h.ending_track - h.starting_track + 1; 76410099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 76510099Sjkh if (rc < 0) 76610099Sjkh return (rc); 76713985Sache 76810099Sjkh if (verbose) { 76910099Sjkh printf ("track start duration block length type\n"); 77010099Sjkh printf ("-------------------------------------------------\n"); 77110099Sjkh } 77213985Sache 77310099Sjkh for (i = 0; i < n; i++) { 77410099Sjkh printf ("%5d ", toc_buffer[i].track); 77510099Sjkh prtrack (toc_buffer + i, 0); 77610099Sjkh } 77713867Sache printf ("%5d ", toc_buffer[n].track); 77810099Sjkh prtrack (toc_buffer + n, 1); 77910099Sjkh return (0); 78010099Sjkh} 78110099Sjkh 78213985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) 78310099Sjkh{ 78413985Sache lba += 150; /* block start offset */ 78513985Sache lba &= 0xffffff; /* negative lbas use only 24 bits */ 78610099Sjkh *m = lba / (60 * 75); 78710099Sjkh lba %= (60 * 75); 78810099Sjkh *s = lba / 75; 78910099Sjkh *f = lba % 75; 79010099Sjkh} 79110099Sjkh 79213985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f) 79310099Sjkh{ 79410099Sjkh return (((m * 60) + s) * 75 + f) - 150; 79510099Sjkh} 79610099Sjkh 79710099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag) 79810099Sjkh{ 79910099Sjkh int block, next, len; 80010099Sjkh u_char m, s, f; 80110099Sjkh 80213884Sache if (msf) { 80313884Sache /* Print track start */ 80413884Sache printf ("%2d:%02d.%02d ", e->addr.msf.minute, 80513884Sache e->addr.msf.second, e->addr.msf.frame); 80610099Sjkh 80713884Sache block = msf2lba (e->addr.msf.minute, e->addr.msf.second, 80813884Sache e->addr.msf.frame); 80913884Sache } else { 81013884Sache block = ntohl(e->addr.lba); 81113884Sache lba2msf(block, &m, &s, &f); 81213884Sache /* Print track start */ 81313884Sache printf ("%2d:%02d.%02d ", m, s, f); 81413884Sache } 81510099Sjkh if (lastflag) { 81610099Sjkh /* Last track -- print block */ 81710099Sjkh printf (" - %6d - -\n", block); 81810099Sjkh return; 81910099Sjkh } 82010099Sjkh 82113884Sache if (msf) 82213884Sache next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, 82313884Sache e[1].addr.msf.frame); 82413884Sache else 82513884Sache next = ntohl(e[1].addr.lba); 82610099Sjkh len = next - block; 82710099Sjkh lba2msf (len, &m, &s, &f); 82810099Sjkh 82910099Sjkh /* Print duration, block, length, type */ 83010099Sjkh printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 83113985Sache (e->control & 4) ? "data" : "audio"); 83210099Sjkh} 83310099Sjkh 83410099Sjkhint play_track (int tstart, int istart, int tend, int iend) 83510099Sjkh{ 83610099Sjkh struct ioc_play_track t; 83710099Sjkh 83810099Sjkh t.start_track = tstart; 83910099Sjkh t.start_index = istart; 84010099Sjkh t.end_track = tend; 84110099Sjkh t.end_index = iend; 84213985Sache 84310099Sjkh return ioctl (fd, CDIOCPLAYTRACKS, &t); 84410099Sjkh} 84510099Sjkh 84610099Sjkhint play_blocks (int blk, int len) 84710099Sjkh{ 84813985Sache struct ioc_play_blocks t; 84910099Sjkh 85010099Sjkh t.blk = blk; 85110099Sjkh t.len = len; 85213985Sache 85310099Sjkh return ioctl (fd, CDIOCPLAYBLOCKS, &t); 85410099Sjkh} 85510099Sjkh 85613985Sacheint setvol (int left, int right) 85710099Sjkh{ 85813985Sache struct ioc_vol v; 85910099Sjkh 86013985Sache v.vol[0] = left; 86113985Sache v.vol[1] = right; 86210099Sjkh v.vol[2] = 0; 86310099Sjkh v.vol[3] = 0; 86413985Sache 86510099Sjkh return ioctl (fd, CDIOCSETVOL, &v); 86610099Sjkh} 86710099Sjkh 86810099Sjkhint read_toc_entrys (int len) 86910099Sjkh{ 87010099Sjkh struct ioc_read_toc_entry t; 87110099Sjkh 87213884Sache t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 87313737Sache t.starting_track = 0; 87410099Sjkh t.data_len = len; 87510099Sjkh t.data = toc_buffer; 87613985Sache 87713985Sache return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); 87810099Sjkh} 87910099Sjkh 88010099Sjkhint play_msf (int start_m, int start_s, int start_f, 88113985Sache int end_m, int end_s, int end_f) 88210099Sjkh{ 88313985Sache struct ioc_play_msf a; 88410099Sjkh 88510099Sjkh a.start_m = start_m; 88610099Sjkh a.start_s = start_s; 88710099Sjkh a.start_f = start_f; 88810099Sjkh a.end_m = end_m; 88910099Sjkh a.end_s = end_s; 89010099Sjkh a.end_f = end_f; 89113985Sache 89210099Sjkh return ioctl (fd, CDIOCPLAYMSF, (char *) &a); 89310099Sjkh} 89410099Sjkh 89510099Sjkhint status (int *trk, int *min, int *sec, int *frame) 89610099Sjkh{ 89710099Sjkh struct ioc_read_subchannel s; 89810099Sjkh struct cd_sub_channel_info data; 89913884Sache u_char mm, ss, ff; 90010099Sjkh 90110099Sjkh bzero (&s, sizeof (s)); 90210099Sjkh s.data = &data; 90310099Sjkh s.data_len = sizeof (data); 90413884Sache s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 90510099Sjkh s.data_format = CD_CURRENT_POSITION; 90613985Sache 90710099Sjkh if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 90810099Sjkh return -1; 90913985Sache 91010099Sjkh *trk = s.data->what.position.track_number; 91113884Sache if (msf) { 91213884Sache *min = s.data->what.position.reladdr.msf.minute; 91313884Sache *sec = s.data->what.position.reladdr.msf.second; 91413884Sache *frame = s.data->what.position.reladdr.msf.frame; 91513884Sache } else { 91613884Sache lba2msf(ntohl(s.data->what.position.reladdr.lba), 91713884Sache &mm, &ss, &ff); 91813884Sache *min = mm; 91913884Sache *sec = ss; 92013884Sache *frame = ff; 92113884Sache } 92213985Sache 92310099Sjkh return s.data->header.audio_status; 92410099Sjkh} 92510099Sjkh 92650039Smdoddconst char * 92750039Smdoddcdcontrol_prompt() 92810099Sjkh{ 92950039Smdodd return ("cdcontrol> "); 93050039Smdodd} 93150039Smdodd 93250039Smdoddchar * 93350039Smdoddinput (int *cmd) 93450039Smdodd{ 93550039Smdodd#define MAXLINE 80 93650039Smdodd static EditLine *el = NULL; 93750039Smdodd static History *hist = NULL; 93850039Smdodd static char buf[MAXLINE]; 93950039Smdodd int num = 0; 94050039Smdodd const char *bp = NULL; 94110099Sjkh char *p; 94210099Sjkh 94310099Sjkh do { 94450039Smdodd if (verbose) { 94550039Smdodd if (!el) { 94650039Smdodd el = el_init("cdcontrol", stdin, stdout); 94750039Smdodd hist = history_init(); 94850039Smdodd history(hist, H_EVENT, 100); 94950039Smdodd el_set(el, EL_HIST, history, hist); 95050039Smdodd el_set(el, EL_EDITOR, "emacs"); 95150039Smdodd el_set(el, EL_PROMPT, cdcontrol_prompt); 95250039Smdodd el_set(el, EL_SIGNAL, 1); 95350039Smdodd } 95450039Smdodd if ((bp = el_gets(el, &num)) == NULL || num == 0) 95550039Smdodd return (0); 95650039Smdodd 95750039Smdodd memcpy(buf, bp, (MAXLINE > num ? MAXLINE : num)); 95850039Smdodd buf[num] = 0; 95950039Smdodd history(hist, H_ENTER, bp); 96050039Smdodd#undef MAXLINE 96150039Smdodd 96250039Smdodd } else { 96350039Smdodd if (! fgets (buf, sizeof (buf), stdin)) { 96450039Smdodd *cmd = CMD_QUIT; 96550039Smdodd fprintf (stderr, "\r\n"); 96650039Smdodd return (0); 96750039Smdodd } 96810099Sjkh } 96910099Sjkh p = parse (buf, cmd); 97010099Sjkh } while (! p); 97110099Sjkh return (p); 97210099Sjkh} 97310099Sjkh 97410099Sjkhchar *parse (char *buf, int *cmd) 97510099Sjkh{ 97610099Sjkh struct cmdtab *c; 97710099Sjkh char *p; 97810099Sjkh int len; 97910099Sjkh 98013985Sache for (p=buf; isspace (*p); p++) 98113985Sache continue; 98210099Sjkh 98313985Sache if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { 98413985Sache *cmd = CMD_PLAY; 98513985Sache return (p); 98613985Sache } 98710099Sjkh 98813985Sache for (buf = p; *p && ! isspace (*p); p++) 98913985Sache continue; 99013985Sache 99113985Sache len = p - buf; 99210099Sjkh if (! len) 99310099Sjkh return (0); 99413985Sache 99513985Sache if (*p) { /* It must be a spacing character! */ 99613985Sache char *q; 99713985Sache 99813985Sache *p++ = 0; 99913985Sache for (q=p; *q && *q != '\n' && *q != '\r'; q++) 100013985Sache continue; 100113985Sache *q = 0; 100213985Sache } 100313985Sache 100410099Sjkh *cmd = -1; 100510099Sjkh for (c=cmdtab; c->name; ++c) { 100613985Sache /* Is it an exact match? */ 100713985Sache if (! strcasecmp (buf, c->name)) { 100813985Sache *cmd = c->command; 100913985Sache break; 101013985Sache } 101113985Sache 101213985Sache /* Try short hand forms then... */ 101313985Sache if (len >= c->min && ! strncasecmp (buf, c->name, len)) { 101413985Sache if (*cmd != -1 && *cmd != c->command) { 101529103Scharnier warnx("ambiguous command"); 101613985Sache return (0); 101713985Sache } 101810099Sjkh *cmd = c->command; 101913985Sache } 102013985Sache } 102110099Sjkh 102210099Sjkh if (*cmd == -1) { 102329103Scharnier warnx("invalid command, enter ``help'' for commands"); 102410099Sjkh return (0); 102510099Sjkh } 102613985Sache 102713985Sache while (isspace (*p)) 102813985Sache p++; 102910099Sjkh return p; 103010099Sjkh} 103110099Sjkh 103210099Sjkhint open_cd () 103310099Sjkh{ 103410099Sjkh char devbuf[80]; 103510099Sjkh 103610099Sjkh if (fd > -1) 103710099Sjkh return (1); 103813985Sache 103910099Sjkh if (*cdname == '/') 104010099Sjkh strcpy (devbuf, cdname); 104110099Sjkh else if (*cdname == 'r') 104210099Sjkh sprintf (devbuf, "/dev/%s", cdname); 104310099Sjkh else 104410099Sjkh sprintf (devbuf, "/dev/r%s", cdname); 104513985Sache 104610099Sjkh fd = open (devbuf, O_RDONLY); 104713985Sache 104810099Sjkh if (fd < 0 && errno == ENOENT) { 104913985Sache strcat (devbuf, DEFAULT_CD_PARTITION); 105010099Sjkh fd = open (devbuf, O_RDONLY); 105110099Sjkh } 105213985Sache 105310099Sjkh if (fd < 0) { 105413985Sache if (errno == ENXIO) { 105513985Sache /* ENXIO has an overloaded meaning here. 105613985Sache * The original "Device not configured" should 105713985Sache * be interpreted as "No disc in drive %s". */ 105829103Scharnier warnx("no disc in drive %s", devbuf); 105913985Sache return (0); 106010099Sjkh } 106129103Scharnier err(1, "%s", devbuf); 106210099Sjkh } 106310099Sjkh return (1); 106410099Sjkh} 1065