cdcontrol.c revision 13989
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. 1913985Sache * 2013989Sache * $Id: cdcontrol.c,v 1.11 1996/02/09 00:22:17 ache Exp $ 2110099Sjkh */ 2213985Sache 2313985Sache#include <ctype.h> 2410099Sjkh#include <stdio.h> 2510099Sjkh#include <stdlib.h> 2610099Sjkh#include <string.h> 2710099Sjkh#include <unistd.h> 2810099Sjkh#include <errno.h> 2910099Sjkh#include <sys/file.h> 3010099Sjkh#include <sys/cdio.h> 3110099Sjkh#include <sys/ioctl.h> 3210099Sjkh 3313884Sache#define VERSION "2.0" 3410099Sjkh 3513985Sache#define ASTS_INVALID 0x00 /* Audio status byte not valid */ 3613985Sache#define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 3713985Sache#define ASTS_PAUSED 0x12 /* Audio play operation paused */ 3813985Sache#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 3913985Sache#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 4013985Sache#define ASTS_VOID 0x15 /* No current audio status to return */ 4110099Sjkh 4213985Sache#ifndef DEFAULT_CD_DRIVE 4313985Sache# define DEFAULT_CD_DRIVE "/dev/cd0c" 4413985Sache#endif 4513985Sache 4613985Sache#ifndef DEFAULT_CD_PARTITION 4713985Sache# define DEFAULT_CD_PARTITION "c" 4813985Sache#endif 4913985Sache 5013985Sache#define CMD_DEBUG 1 5113985Sache#define CMD_EJECT 2 5213985Sache#define CMD_HELP 3 5313985Sache#define CMD_INFO 4 5413985Sache#define CMD_PAUSE 5 5513985Sache#define CMD_PLAY 6 5613985Sache#define CMD_QUIT 7 5713985Sache#define CMD_RESUME 8 5813985Sache#define CMD_STOP 9 5913985Sache#define CMD_VOLUME 10 6013985Sache#define CMD_CLOSE 11 6113985Sache#define CMD_RESET 12 6213985Sache#define CMD_SET 13 6313985Sache#define CMD_STATUS 14 6413985Sache 6510099Sjkhstruct cmdtab { 6610099Sjkh int command; 6710099Sjkh char *name; 6813985Sache unsigned min; 6910099Sjkh char *args; 7010099Sjkh} cmdtab[] = { 7113985Sache{ CMD_CLOSE, "close", 1, "" }, 7213985Sache{ CMD_DEBUG, "debug", 1, "on | off" }, 7313985Sache{ CMD_EJECT, "eject", 1, "" }, 7413985Sache{ CMD_HELP, "?", 1, 0 }, 7513985Sache{ CMD_HELP, "help", 1, "" }, 7613985Sache{ CMD_INFO, "info", 1, "" }, 7713985Sache{ CMD_PAUSE, "pause", 2, "" }, 7813985Sache{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 7913985Sache{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 8013985Sache{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 8113985Sache{ CMD_PLAY, "play", 1, "[#block [len]]" }, 8213985Sache{ CMD_QUIT, "quit", 1, "" }, 8313985Sache{ CMD_RESET, "reset", 4, "" }, 8413985Sache{ CMD_RESUME, "resume", 1, "" }, 8513985Sache{ CMD_SET, "set", 2, "msf | lba" }, 8613985Sache{ CMD_STATUS, "status", 1, "" }, 8713985Sache{ CMD_STOP, "stop", 3, "" }, 8813985Sache{ CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" }, 8913985Sache{ 0, } 9010099Sjkh}; 9110099Sjkh 9213985Sachestruct cd_toc_entry toc_buffer[100]; 9310099Sjkh 9413985Sacheconst char *cdname; 9513985Sacheint fd = -1; 9613985Sacheint verbose = 1; 9713985Sacheint msf = 1; 9810099Sjkh 9913985Sacheextern char *__progname; 10010099Sjkh 10113985Sacheint setvol __P((int, int)); 10213985Sacheint read_toc_entrys __P((int)); 10313985Sacheint play_msf __P((int, int, int, int, int, int)); 10413985Sacheint play_track __P((int, int, int, int)); 10513985Sacheint get_vol __P((int *, int *)); 10613985Sacheint status __P((int *, int *, int *, int *)); 10713985Sacheint open_cd __P((void)); 10813985Sacheint play __P((char *arg)); 10913985Sacheint info __P((char *arg)); 11013985Sacheint pstatus __P((char *arg)); 11113985Sachechar *input __P((int *)); 11213985Sachevoid prtrack __P((struct cd_toc_entry *e, int lastflag)); 11313985Sachevoid lba2msf __P((unsigned long lba, 11413985Sache u_char *m, u_char *s, u_char *f)); 11513985Sacheunsigned int msf2lba __P((u_char m, u_char s, u_char f)); 11613985Sacheint play_blocks __P((int blk, int len)); 11713985Sacheint run __P((int cmd, char *arg)); 11813985Sachechar *parse __P((char *buf, int *cmd)); 11910099Sjkh 12010099Sjkhvoid help () 12110099Sjkh{ 12210099Sjkh struct cmdtab *c; 12313985Sache char *s, n; 12413985Sache int i; 12510099Sjkh 12610099Sjkh for (c=cmdtab; c->name; ++c) { 12710099Sjkh if (! c->args) 12810099Sjkh continue; 12913985Sache printf("\t"); 13013985Sache for (i = c->min, s = c->name; *s; s++, i--) { 13113985Sache if (i > 0) 13213985Sache n = toupper(*s); 13313985Sache else 13413985Sache n = *s; 13513985Sache putchar(n); 13613985Sache } 13710099Sjkh if (*c->args) 13810099Sjkh printf (" %s", c->args); 13910099Sjkh printf ("\n"); 14010099Sjkh } 14113985Sache printf ("\n\tThe word \"play\" is not required for the play commands.\n"); 14213985Sache printf ("\tThe plain target address is taken as a synonym for play.\n"); 14310099Sjkh} 14410099Sjkh 14510099Sjkhvoid usage () 14610099Sjkh{ 14713985Sache printf ("Usage:\n\t%s [ -vs ] [ -f disc ] [ command args... ]\n", __progname); 14810099Sjkh printf ("Options:\n"); 14910099Sjkh printf ("\t-v - verbose mode\n"); 15010099Sjkh printf ("\t-s - silent mode\n"); 15113985Sache printf ("\t-f disc - a block device name such as /dev/cd0c\n"); 15213985Sache printf ("\tMUSIC_CD - shell variable with device name\n"); 15310099Sjkh printf ("Commands:\n"); 15410099Sjkh help (); 15510099Sjkh exit (1); 15610099Sjkh} 15710099Sjkh 15810099Sjkhint main (int argc, char **argv) 15910099Sjkh{ 16010099Sjkh int cmd; 16110099Sjkh char *arg; 16210099Sjkh 16313985Sache cdname = getenv ("MUSIC_CD"); 16410099Sjkh if (! cdname) 16513985Sache cdname = getenv ("CD_DRIVE"); 16613985Sache if (! cdname) 16713985Sache cdname = getenv ("DISC"); 16813985Sache if (! cdname) 16910099Sjkh cdname = getenv ("CDPLAY"); 17010099Sjkh 17110099Sjkh for (;;) { 17210099Sjkh switch (getopt (argc, argv, "svhf:")) { 17310099Sjkh case EOF: 17410099Sjkh break; 17510099Sjkh case 's': 17610099Sjkh verbose = 0; 17710099Sjkh continue; 17810099Sjkh case 'v': 17910099Sjkh verbose = 2; 18010099Sjkh continue; 18110099Sjkh case 'f': 18210099Sjkh cdname = optarg; 18310099Sjkh continue; 18410099Sjkh case 'h': 18510099Sjkh default: 18610099Sjkh usage (); 18710099Sjkh } 18810099Sjkh break; 18910099Sjkh } 19010099Sjkh argc -= optind; 19110099Sjkh argv += optind; 19210099Sjkh 19313985Sache if (argc > 0 && ! strcasecmp (*argv, "help")) 19410099Sjkh usage (); 19510099Sjkh 19610099Sjkh if (! cdname) { 19713985Sache cdname = DEFAULT_CD_DRIVE; 19813985Sache fprintf (stderr, 19913985Sache "No CD device name specified. Defaulting to %s.\n", cdname); 20010099Sjkh } 20110099Sjkh 20210099Sjkh if (argc > 0) { 20310099Sjkh char buf[80], *p; 20410099Sjkh int len; 20510099Sjkh 20613985Sache for (p=buf; argc-->0; ++argv) { 20710099Sjkh len = strlen (*argv); 20813985Sache 20910099Sjkh if (p + len >= buf + sizeof (buf) - 1) 21010099Sjkh usage (); 21113985Sache 21210099Sjkh if (p > buf) 21310099Sjkh *p++ = ' '; 21413985Sache 21510099Sjkh strcpy (p, *argv); 21610099Sjkh p += len; 21710099Sjkh } 21810099Sjkh *p = 0; 21910099Sjkh arg = parse (buf, &cmd); 22013985Sache return (run (cmd, arg)); 22110099Sjkh } 22210099Sjkh 22310099Sjkh if (verbose == 1) 22410099Sjkh verbose = isatty (0); 22513985Sache 22610099Sjkh if (verbose) { 22713985Sache printf ("Compact Disc Control utility, version %s\n", VERSION); 22810099Sjkh printf ("Type `?' for command list\n\n"); 22910099Sjkh } 23010099Sjkh 23110099Sjkh for (;;) { 23210099Sjkh arg = input (&cmd); 23310099Sjkh if (run (cmd, arg) < 0) { 23410099Sjkh if (verbose) 23513985Sache perror (__progname); 23610099Sjkh close (fd); 23710099Sjkh fd = -1; 23810099Sjkh } 23910099Sjkh fflush (stdout); 24010099Sjkh } 24110099Sjkh} 24210099Sjkh 24310099Sjkhint run (int cmd, char *arg) 24410099Sjkh{ 24510099Sjkh int l, r, rc; 24610099Sjkh 24710099Sjkh switch (cmd) { 24813985Sache 24910099Sjkh case CMD_QUIT: 25010099Sjkh exit (0); 25110099Sjkh 25213985Sache case CMD_INFO: 25313985Sache if (fd < 0 && ! open_cd ()) 25413985Sache return (0); 25510099Sjkh 25610099Sjkh return info (arg); 25710099Sjkh 25813884Sache case CMD_STATUS: 25913985Sache if (fd < 0 && ! open_cd ()) 26013985Sache return (0); 26113985Sache 26213884Sache return pstatus (arg); 26313884Sache 26410099Sjkh case CMD_PAUSE: 26513985Sache if (fd < 0 && ! open_cd ()) 26613985Sache return (0); 26713985Sache 26810099Sjkh return ioctl (fd, CDIOCPAUSE); 26910099Sjkh 27010099Sjkh case CMD_RESUME: 27113985Sache if (fd < 0 && ! open_cd ()) 27213985Sache return (0); 27313985Sache 27410099Sjkh return ioctl (fd, CDIOCRESUME); 27510099Sjkh 27610099Sjkh case CMD_STOP: 27713985Sache if (fd < 0 && ! open_cd ()) 27813985Sache return (0); 27910099Sjkh 28013985Sache rc = ioctl (fd, CDIOCSTOP); 28113985Sache 28213985Sache (void) ioctl (fd, CDIOCALLOW); 28313985Sache 28413985Sache return (rc); 28513985Sache 28613884Sache case CMD_RESET: 28713985Sache if (fd < 0 && ! open_cd ()) 28813985Sache return (0); 28913985Sache 29013884Sache rc = ioctl (fd, CDIOCRESET); 29113884Sache if (rc < 0) 29213884Sache return rc; 29313884Sache close(fd); 29413884Sache fd = -1; 29513884Sache return (0); 29613884Sache 29710099Sjkh case CMD_DEBUG: 29813985Sache if (fd < 0 && ! open_cd ()) 29913985Sache return (0); 30013985Sache 30113985Sache if (! strcasecmp (arg, "on")) 30210099Sjkh return ioctl (fd, CDIOCSETDEBUG); 30313985Sache 30413985Sache if (! strcasecmp (arg, "off")) 30510099Sjkh return ioctl (fd, CDIOCCLRDEBUG); 30613985Sache 30713985Sache printf ("%s: Invalid command arguments\n", __progname); 30813985Sache 30910099Sjkh return (0); 31010099Sjkh 31110099Sjkh case CMD_EJECT: 31213985Sache if (fd < 0 && ! open_cd ()) 31313985Sache return (0); 31413985Sache 31510099Sjkh (void) ioctl (fd, CDIOCALLOW); 31610099Sjkh rc = ioctl (fd, CDIOCEJECT); 31710099Sjkh if (rc < 0) 31810099Sjkh return (rc); 31913865Sache return (0); 32013865Sache 32113985Sache case CMD_CLOSE: 32213985Sache if (fd < 0 && ! open_cd ()) 32313985Sache return (0); 32413985Sache 32513985Sache (void) ioctl (fd, CDIOCALLOW); 32613865Sache rc = ioctl (fd, CDIOCCLOSE); 32713865Sache if (rc < 0) 32813865Sache return (rc); 32913865Sache close(fd); 33010099Sjkh fd = -1; 33110099Sjkh return (0); 33210099Sjkh 33310099Sjkh case CMD_PLAY: 33413985Sache if (fd < 0 && ! open_cd ()) 33513985Sache return (0); 33613985Sache 33713985Sache while (isspace (*arg)) 33813985Sache arg++; 33913985Sache 34010099Sjkh return play (arg); 34110099Sjkh 34213884Sache case CMD_SET: 34313985Sache if (! strcasecmp (arg, "msf")) 34413884Sache msf = 1; 34513985Sache else if (! strcasecmp (arg, "lba")) 34613884Sache msf = 0; 34713884Sache else 34813985Sache printf ("%s: Invalid command arguments\n", __progname); 34913884Sache return (0); 35013884Sache 35110099Sjkh case CMD_VOLUME: 35213985Sache if (fd < 0 && !open_cd ()) 35313985Sache return (0); 35410099Sjkh 35513985Sache if (! strncasecmp (arg, "left", strlen(arg))) 35610099Sjkh return ioctl (fd, CDIOCSETLEFT); 35713985Sache 35813985Sache if (! strncasecmp (arg, "right", strlen(arg))) 35910099Sjkh return ioctl (fd, CDIOCSETRIGHT); 36013985Sache 36113985Sache if (! strncasecmp (arg, "mono", strlen(arg))) 36210099Sjkh return ioctl (fd, CDIOCSETMONO); 36313985Sache 36413985Sache if (! strncasecmp (arg, "stereo", strlen(arg))) 36510099Sjkh return ioctl (fd, CDIOCSETSTERIO); 36610099Sjkh 36713985Sache if (! strncasecmp (arg, "mute", strlen(arg))) 36813985Sache return ioctl (fd, CDIOCSETMUTE); 36913985Sache 37010099Sjkh if (2 != sscanf (arg, "%d %d", &l, &r)) { 37113985Sache printf ("%s: Invalid command arguments\n", __progname); 37210099Sjkh return (0); 37310099Sjkh } 37413985Sache 37510099Sjkh return setvol (l, r); 37613985Sache 37713985Sache default: 37813985Sache case CMD_HELP: 37913985Sache help (); 38013985Sache return (0); 38113985Sache 38210099Sjkh } 38310099Sjkh} 38410099Sjkh 38510099Sjkhint play (char *arg) 38610099Sjkh{ 38710099Sjkh struct ioc_toc_header h; 38810099Sjkh int rc, n, start, end = 0, istart = 1, iend = 1; 38910099Sjkh 39010099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 39113985Sache 39210099Sjkh if (rc < 0) 39310099Sjkh return (rc); 39410099Sjkh 39510099Sjkh n = h.ending_track - h.starting_track + 1; 39610099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 39713985Sache 39810099Sjkh if (rc < 0) 39910099Sjkh return (rc); 40010099Sjkh 40113989Sache if (! arg || ! *arg) { 40213985Sache /* Play the whole disc */ 40313989Sache if (msf) 40413989Sache return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, 40513989Sache toc_buffer[n].addr.msf.second, 40613989Sache toc_buffer[n].addr.msf.frame)); 40713989Sache else 40813989Sache return play_blocks (0, ntohl(toc_buffer[n].addr.lba)); 40913989Sache } 41010099Sjkh 41110099Sjkh if (strchr (arg, '#')) { 41213985Sache /* Play block #blk [ len ] */ 41310099Sjkh int blk, len = 0; 41410099Sjkh 41510099Sjkh if (2 != sscanf (arg, "#%d%d", &blk, &len) && 41613985Sache 1 != sscanf (arg, "#%d", &blk)) 41713985Sache goto Clean_up; 41813985Sache 41913989Sache if (len == 0) { 42013989Sache if (msf) 42113989Sache len = msf2lba (toc_buffer[n].addr.msf.minute, 42213989Sache toc_buffer[n].addr.msf.second, 42313989Sache toc_buffer[n].addr.msf.frame) - blk; 42413989Sache else 42513989Sache len = ntohl(toc_buffer[n].addr.lba) - blk; 42613989Sache } 42710099Sjkh return play_blocks (blk, len); 42810099Sjkh } 42910099Sjkh 43010099Sjkh if (strchr (arg, ':')) { 43110099Sjkh /* 43210099Sjkh * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 43313985Sache * 43413985Sache * Will now also undestand timed addresses relative 43513985Sache * to the beginning of a track in the form... 43613985Sache * 43713985Sache * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 43810099Sjkh */ 43913985Sache unsigned tr1, tr2; 44013985Sache unsigned m1, m2, s1, s2, f1, f2; 44113989Sache unsigned char tm, ts, tf; 44210099Sjkh 44313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44413985Sache if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", 44513985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 44613985Sache goto Play_Relative_Addresses; 44713985Sache 44813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44913985Sache if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", 45013985Sache &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 45113985Sache goto Play_Relative_Addresses; 45213985Sache 45313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45413985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", 45513985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 45613985Sache goto Play_Relative_Addresses; 45713985Sache 45813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45913985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", 46013985Sache &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 46113985Sache goto Play_Relative_Addresses; 46213985Sache 46313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46413985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", 46513985Sache &tr1, &m1, &s1, &f1, &m2, &s2)) 46613985Sache goto Play_Relative_Addresses; 46713985Sache 46813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46913985Sache if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", 47013985Sache &tr1, &m1, &s1, &m2, &s2, &f2)) 47113985Sache goto Play_Relative_Addresses; 47213985Sache 47313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47413985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d %d", 47513985Sache &tr1, &m1, &s1, &f1, &tr2, &m2)) 47613985Sache goto Play_Relative_Addresses; 47713985Sache 47813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47913985Sache if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 48013985Sache goto Play_Relative_Addresses; 48113985Sache 48213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48313985Sache if (5 == sscanf (arg, "%d %d:%d %d %d", 48413985Sache &tr1, &m1, &s1, &tr2, &m2)) 48513985Sache goto Play_Relative_Addresses; 48613985Sache 48713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48813985Sache if (5 == sscanf (arg, "%d %d:%d.%d %d", 48913985Sache &tr1, &m1, &s1, &f1, &tr2)) 49013985Sache goto Play_Relative_Addresses; 49113985Sache 49213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49313985Sache if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 49413985Sache goto Play_Relative_Addresses; 49513985Sache 49613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49713985Sache if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 49813985Sache goto Play_Relative_Addresses; 49913985Sache 50013985Sache tr2 = m2 = s2 = f2 = f1 = 0; 50113985Sache if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) 50213985Sache goto Play_Relative_Addresses; 50313985Sache 50413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 50513985Sache goto Try_Absolute_Timed_Addresses; 50613985Sache 50713985SachePlay_Relative_Addresses: 50813985Sache if (! tr1) 50913985Sache tr1 = 1; 51013985Sache else if (tr1 > n) 51113985Sache tr1 = n; 51213985Sache 51313989Sache if (msf) { 51413989Sache tm = toc_buffer[tr1].addr.msf.minute; 51513989Sache ts = toc_buffer[tr1].addr.msf.second; 51613989Sache tf = toc_buffer[tr1].addr.msf.frame; 51713989Sache } else 51813989Sache lba2msf(ntohl(toc_buffer[tr1].addr.lba), 51913989Sache &tm, &ts, &tf); 52013989Sache if ((m1 > tm) 52113989Sache || ((m1 == tm) 52213989Sache && ((s1 > ts) 52313989Sache || ((s1 == ts) 52413989Sache && (f1 > tf))))) { 52513985Sache printf ("Track %d is not that long.\n", tr1); 52613985Sache return (0); 52713985Sache } 52813985Sache 52913985Sache tr1--; 53013985Sache 53113989Sache f1 += tf; 53213985Sache if (f1 >= 75) { 53313985Sache s1 += f1 / 75; 53413985Sache f1 %= 75; 53513985Sache } 53613985Sache 53713989Sache s1 += ts; 53813985Sache if (s1 >= 60) { 53913985Sache m1 += s1 / 60; 54013985Sache s1 %= 60; 54113985Sache } 54213985Sache 54313989Sache m1 += tm; 54413985Sache 54513985Sache if (! tr2) { 54613985Sache if (m2 || s2 || f2) { 54713985Sache tr2 = tr1; 54813985Sache f2 += f1; 54913985Sache if (f2 >= 75) { 55013985Sache s2 += f2 / 75; 55113985Sache f2 %= 75; 55213985Sache } 55313985Sache 55413985Sache s2 += s1; 55513985Sache if (s2 > 60) { 55613985Sache m2 += s2 / 60; 55713985Sache s2 %= 60; 55813985Sache } 55913985Sache 56013985Sache m2 += m1; 56113985Sache } else { 56213985Sache tr2 = n; 56313989Sache if (msf) { 56413989Sache m2 = toc_buffer[n].addr.msf.minute; 56513989Sache s2 = toc_buffer[n].addr.msf.second; 56613989Sache f2 = toc_buffer[n].addr.msf.frame; 56713989Sache } else { 56813989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 56913989Sache &tm, &ts, &tf); 57013989Sache m2 = tm; 57113989Sache s2 = ts; 57213989Sache f2 = tf; 57313989Sache } 57413985Sache } 57513985Sache } else if (tr2 > n) { 57613985Sache tr2 = n; 57713985Sache m2 = s2 = f2 = 0; 57813985Sache } else { 57913985Sache if (m2 || s2 || f2) 58013985Sache tr2--; 58113989Sache if (msf) { 58213989Sache tm = toc_buffer[tr2].addr.msf.minute; 58313989Sache ts = toc_buffer[tr2].addr.msf.second; 58413989Sache tf = toc_buffer[tr2].addr.msf.frame; 58513989Sache } else 58613989Sache lba2msf(ntohl(toc_buffer[tr2].addr.lba), 58713989Sache &tm, &ts, &tf); 58813989Sache f2 += tf; 58913985Sache if (f2 >= 75) { 59013985Sache s2 += f2 / 75; 59113985Sache f2 %= 75; 59213985Sache } 59313985Sache 59413989Sache s2 += ts; 59513985Sache if (s2 > 60) { 59613985Sache m2 += s2 / 60; 59713985Sache s2 %= 60; 59813985Sache } 59913985Sache 60013989Sache m2 += tm; 60113985Sache } 60213985Sache 60313989Sache if (msf) { 60413989Sache tm = toc_buffer[n].addr.msf.minute; 60513989Sache ts = toc_buffer[n].addr.msf.second; 60613989Sache tf = toc_buffer[n].addr.msf.frame; 60713989Sache } else 60813989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 60913989Sache &tm, &ts, &tf); 61013985Sache if ((tr2 < n) 61113989Sache && ((m2 > tm) 61213989Sache || ((m2 == tm) 61313989Sache && ((s2 > ts) 61413989Sache || ((s2 == ts) 61513989Sache && (f2 > tf)))))) { 61613985Sache printf ("The playing time of the disc is not that long.\n"); 61713985Sache return (0); 61813985Sache } 61913985Sache return (play_msf (m1, s1, f1, m2, s2, f2)); 62013985Sache 62113985SacheTry_Absolute_Timed_Addresses: 62213985Sache if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", 62313985Sache &m1, &s1, &f1, &m2, &s2, &f2) && 62410099Sjkh 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 62510099Sjkh 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 62610099Sjkh 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 62710099Sjkh 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 62810099Sjkh 2 != sscanf (arg, "%d:%d", &m1, &s1)) 62913985Sache goto Clean_up; 63013985Sache 63110099Sjkh if (m2 == 0) { 63213989Sache if (msf) { 63313989Sache m2 = toc_buffer[n].addr.msf.minute; 63413989Sache s2 = toc_buffer[n].addr.msf.second; 63513989Sache f2 = toc_buffer[n].addr.msf.frame; 63613989Sache } else { 63713989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 63813989Sache &tm, &ts, &tf); 63913989Sache m2 = tm; 64013989Sache s2 = ts; 64113989Sache f2 = tf; 64213989Sache } 64310099Sjkh } 64410099Sjkh return play_msf (m1, s1, f1, m2, s2, f2); 64510099Sjkh } 64610099Sjkh 64710099Sjkh /* 64810099Sjkh * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 64910099Sjkh */ 65010099Sjkh if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 65110099Sjkh 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 65210099Sjkh 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 65310099Sjkh 2 != sscanf (arg, "%d.%d", &start, &istart) && 65410099Sjkh 2 != sscanf (arg, "%d%d", &start, &end) && 65510099Sjkh 1 != sscanf (arg, "%d", &start)) 65613985Sache goto Clean_up; 65713985Sache 65810099Sjkh if (end == 0) 65910099Sjkh end = n; 66013985Sache return (play_track (start, istart, end, iend)); 66113985Sache 66213985SacheClean_up: 66313985Sache printf ("%s: Invalid command arguments\n", __progname); 66413985Sache return (0); 66510099Sjkh} 66610099Sjkh 66710099Sjkhchar *strstatus (int sts) 66810099Sjkh{ 66910099Sjkh switch (sts) { 67010099Sjkh case ASTS_INVALID: return ("invalid"); 67110099Sjkh case ASTS_PLAYING: return ("playing"); 67210099Sjkh case ASTS_PAUSED: return ("paused"); 67310099Sjkh case ASTS_COMPLETED: return ("completed"); 67410099Sjkh case ASTS_ERROR: return ("error"); 67510099Sjkh case ASTS_VOID: return ("void"); 67610099Sjkh default: return ("??"); 67710099Sjkh } 67810099Sjkh} 67910099Sjkh 68013884Sacheint pstatus (char *arg) 68110099Sjkh{ 68210099Sjkh struct ioc_vol v; 68313888Sache struct ioc_read_subchannel ss; 68413888Sache struct cd_sub_channel_info data; 68513884Sache int rc, trk, m, s, f; 68610099Sjkh 68710099Sjkh rc = status (&trk, &m, &s, &f); 68810099Sjkh if (rc >= 0) 68910099Sjkh if (verbose) 69010099Sjkh printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", 69110099Sjkh rc, strstatus (rc), trk, m, s, f); 69210099Sjkh else 69310099Sjkh printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 69410099Sjkh else 69513888Sache printf ("No current status info available\n"); 69610099Sjkh 69713888Sache bzero (&ss, sizeof (ss)); 69813888Sache ss.data = &data; 69913888Sache ss.data_len = sizeof (data); 70013888Sache ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 70113888Sache ss.data_format = CD_MEDIA_CATALOG; 70213888Sache rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); 70313888Sache if (rc >= 0) { 70413889Sache printf("Media catalog is %sactive", 70513888Sache ss.data->what.media_catalog.mc_valid ? "": "in"); 70613889Sache if (ss.data->what.media_catalog.mc_number[0]) 70713889Sache printf(", number \"%.15s\"", 70813889Sache ss.data->what.media_catalog.mc_number); 70913889Sache putchar('\n'); 71013888Sache } else 71113888Sache printf("No media catalog info available\n"); 71213888Sache 71310099Sjkh rc = ioctl (fd, CDIOCGETVOL, &v); 71410099Sjkh if (rc >= 0) 71510099Sjkh if (verbose) 71610099Sjkh printf ("Left volume = %d, right volume = %d\n", 71710099Sjkh v.vol[0], v.vol[1]); 71810099Sjkh else 71910099Sjkh printf ("%d %d\n", v.vol[0], v.vol[1]); 72010099Sjkh else 72113888Sache printf ("No volume level info available\n"); 72213884Sache return(0); 72313884Sache} 72410099Sjkh 72513884Sacheint info (char *arg) 72613884Sache{ 72713884Sache struct ioc_toc_header h; 72813884Sache int rc, i, n; 72913884Sache 73010099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 73113888Sache if (rc >= 0) { 73210099Sjkh if (verbose) 73310099Sjkh printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 73410099Sjkh h.starting_track, h.ending_track, h.len); 73510099Sjkh else 73610099Sjkh printf ("%d %d %d\n", h.starting_track, 73710099Sjkh h.ending_track, h.len); 73813888Sache } else { 73910099Sjkh perror ("getting toc header"); 74010099Sjkh return (rc); 74110099Sjkh } 74210099Sjkh 74310099Sjkh n = h.ending_track - h.starting_track + 1; 74410099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 74510099Sjkh if (rc < 0) 74610099Sjkh return (rc); 74713985Sache 74810099Sjkh if (verbose) { 74910099Sjkh printf ("track start duration block length type\n"); 75010099Sjkh printf ("-------------------------------------------------\n"); 75110099Sjkh } 75213985Sache 75310099Sjkh for (i = 0; i < n; i++) { 75410099Sjkh printf ("%5d ", toc_buffer[i].track); 75510099Sjkh prtrack (toc_buffer + i, 0); 75610099Sjkh } 75713867Sache printf ("%5d ", toc_buffer[n].track); 75810099Sjkh prtrack (toc_buffer + n, 1); 75910099Sjkh return (0); 76010099Sjkh} 76110099Sjkh 76213985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) 76310099Sjkh{ 76413985Sache lba += 150; /* block start offset */ 76513985Sache lba &= 0xffffff; /* negative lbas use only 24 bits */ 76610099Sjkh *m = lba / (60 * 75); 76710099Sjkh lba %= (60 * 75); 76810099Sjkh *s = lba / 75; 76910099Sjkh *f = lba % 75; 77010099Sjkh} 77110099Sjkh 77213985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f) 77310099Sjkh{ 77410099Sjkh return (((m * 60) + s) * 75 + f) - 150; 77510099Sjkh} 77610099Sjkh 77710099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag) 77810099Sjkh{ 77910099Sjkh int block, next, len; 78010099Sjkh u_char m, s, f; 78110099Sjkh 78213884Sache if (msf) { 78313884Sache /* Print track start */ 78413884Sache printf ("%2d:%02d.%02d ", e->addr.msf.minute, 78513884Sache e->addr.msf.second, e->addr.msf.frame); 78610099Sjkh 78713884Sache block = msf2lba (e->addr.msf.minute, e->addr.msf.second, 78813884Sache e->addr.msf.frame); 78913884Sache } else { 79013884Sache block = ntohl(e->addr.lba); 79113884Sache lba2msf(block, &m, &s, &f); 79213884Sache /* Print track start */ 79313884Sache printf ("%2d:%02d.%02d ", m, s, f); 79413884Sache } 79510099Sjkh if (lastflag) { 79610099Sjkh /* Last track -- print block */ 79710099Sjkh printf (" - %6d - -\n", block); 79810099Sjkh return; 79910099Sjkh } 80010099Sjkh 80113884Sache if (msf) 80213884Sache next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, 80313884Sache e[1].addr.msf.frame); 80413884Sache else 80513884Sache next = ntohl(e[1].addr.lba); 80610099Sjkh len = next - block; 80710099Sjkh lba2msf (len, &m, &s, &f); 80810099Sjkh 80910099Sjkh /* Print duration, block, length, type */ 81010099Sjkh printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 81113985Sache (e->control & 4) ? "data" : "audio"); 81210099Sjkh} 81310099Sjkh 81410099Sjkhint play_track (int tstart, int istart, int tend, int iend) 81510099Sjkh{ 81610099Sjkh struct ioc_play_track t; 81710099Sjkh 81810099Sjkh t.start_track = tstart; 81910099Sjkh t.start_index = istart; 82010099Sjkh t.end_track = tend; 82110099Sjkh t.end_index = iend; 82213985Sache 82310099Sjkh return ioctl (fd, CDIOCPLAYTRACKS, &t); 82410099Sjkh} 82510099Sjkh 82610099Sjkhint play_blocks (int blk, int len) 82710099Sjkh{ 82813985Sache struct ioc_play_blocks t; 82910099Sjkh 83010099Sjkh t.blk = blk; 83110099Sjkh t.len = len; 83213985Sache 83310099Sjkh return ioctl (fd, CDIOCPLAYBLOCKS, &t); 83410099Sjkh} 83510099Sjkh 83613985Sacheint setvol (int left, int right) 83710099Sjkh{ 83813985Sache struct ioc_vol v; 83910099Sjkh 84013985Sache v.vol[0] = left; 84113985Sache v.vol[1] = right; 84210099Sjkh v.vol[2] = 0; 84310099Sjkh v.vol[3] = 0; 84413985Sache 84510099Sjkh return ioctl (fd, CDIOCSETVOL, &v); 84610099Sjkh} 84710099Sjkh 84810099Sjkhint read_toc_entrys (int len) 84910099Sjkh{ 85010099Sjkh struct ioc_read_toc_entry t; 85110099Sjkh 85213884Sache t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 85313737Sache t.starting_track = 0; 85410099Sjkh t.data_len = len; 85510099Sjkh t.data = toc_buffer; 85613985Sache 85713985Sache return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); 85810099Sjkh} 85910099Sjkh 86010099Sjkhint play_msf (int start_m, int start_s, int start_f, 86113985Sache int end_m, int end_s, int end_f) 86210099Sjkh{ 86313985Sache struct ioc_play_msf a; 86410099Sjkh 86510099Sjkh a.start_m = start_m; 86610099Sjkh a.start_s = start_s; 86710099Sjkh a.start_f = start_f; 86810099Sjkh a.end_m = end_m; 86910099Sjkh a.end_s = end_s; 87010099Sjkh a.end_f = end_f; 87113985Sache 87210099Sjkh return ioctl (fd, CDIOCPLAYMSF, (char *) &a); 87310099Sjkh} 87410099Sjkh 87510099Sjkhint status (int *trk, int *min, int *sec, int *frame) 87610099Sjkh{ 87710099Sjkh struct ioc_read_subchannel s; 87810099Sjkh struct cd_sub_channel_info data; 87913884Sache u_char mm, ss, ff; 88010099Sjkh 88110099Sjkh bzero (&s, sizeof (s)); 88210099Sjkh s.data = &data; 88310099Sjkh s.data_len = sizeof (data); 88413884Sache s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 88510099Sjkh s.data_format = CD_CURRENT_POSITION; 88613985Sache 88710099Sjkh if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 88810099Sjkh return -1; 88913985Sache 89010099Sjkh *trk = s.data->what.position.track_number; 89113884Sache if (msf) { 89213884Sache *min = s.data->what.position.reladdr.msf.minute; 89313884Sache *sec = s.data->what.position.reladdr.msf.second; 89413884Sache *frame = s.data->what.position.reladdr.msf.frame; 89513884Sache } else { 89613884Sache lba2msf(ntohl(s.data->what.position.reladdr.lba), 89713884Sache &mm, &ss, &ff); 89813884Sache *min = mm; 89913884Sache *sec = ss; 90013884Sache *frame = ff; 90113884Sache } 90213985Sache 90310099Sjkh return s.data->header.audio_status; 90410099Sjkh} 90510099Sjkh 90610099Sjkhchar *input (int *cmd) 90710099Sjkh{ 90810099Sjkh static char buf[80]; 90910099Sjkh char *p; 91010099Sjkh 91110099Sjkh do { 91210099Sjkh if (verbose) 91313985Sache fprintf (stderr, "%s> ", __progname); 91410099Sjkh if (! fgets (buf, sizeof (buf), stdin)) { 91510099Sjkh *cmd = CMD_QUIT; 91613985Sache fprintf (stderr, "\r\n"); 91713985Sache return (0); 91810099Sjkh } 91910099Sjkh p = parse (buf, cmd); 92010099Sjkh } while (! p); 92110099Sjkh return (p); 92210099Sjkh} 92310099Sjkh 92410099Sjkhchar *parse (char *buf, int *cmd) 92510099Sjkh{ 92610099Sjkh struct cmdtab *c; 92710099Sjkh char *p; 92810099Sjkh int len; 92910099Sjkh 93013985Sache for (p=buf; isspace (*p); p++) 93113985Sache continue; 93210099Sjkh 93313985Sache if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { 93413985Sache *cmd = CMD_PLAY; 93513985Sache return (p); 93613985Sache } 93710099Sjkh 93813985Sache for (buf = p; *p && ! isspace (*p); p++) 93913985Sache continue; 94013985Sache 94113985Sache len = p - buf; 94210099Sjkh if (! len) 94310099Sjkh return (0); 94413985Sache 94513985Sache if (*p) { /* It must be a spacing character! */ 94613985Sache char *q; 94713985Sache 94813985Sache *p++ = 0; 94913985Sache for (q=p; *q && *q != '\n' && *q != '\r'; q++) 95013985Sache continue; 95113985Sache *q = 0; 95213985Sache } 95313985Sache 95410099Sjkh *cmd = -1; 95510099Sjkh for (c=cmdtab; c->name; ++c) { 95613985Sache /* Is it an exact match? */ 95713985Sache if (! strcasecmp (buf, c->name)) { 95813985Sache *cmd = c->command; 95913985Sache break; 96013985Sache } 96113985Sache 96213985Sache /* Try short hand forms then... */ 96313985Sache if (len >= c->min && ! strncasecmp (buf, c->name, len)) { 96413985Sache if (*cmd != -1 && *cmd != c->command) { 96513985Sache fprintf (stderr, "Ambiguous command\n"); 96613985Sache return (0); 96713985Sache } 96810099Sjkh *cmd = c->command; 96913985Sache } 97013985Sache } 97110099Sjkh 97210099Sjkh if (*cmd == -1) { 97313985Sache fprintf (stderr, "%s: Invalid command, enter ``help'' for commands.\n", 97413985Sache __progname); 97510099Sjkh return (0); 97610099Sjkh } 97713985Sache 97813985Sache while (isspace (*p)) 97913985Sache p++; 98010099Sjkh return p; 98110099Sjkh} 98210099Sjkh 98310099Sjkhint open_cd () 98410099Sjkh{ 98510099Sjkh char devbuf[80]; 98610099Sjkh 98710099Sjkh if (fd > -1) 98810099Sjkh return (1); 98913985Sache 99010099Sjkh if (*cdname == '/') 99110099Sjkh strcpy (devbuf, cdname); 99210099Sjkh else if (*cdname == 'r') 99310099Sjkh sprintf (devbuf, "/dev/%s", cdname); 99410099Sjkh else 99510099Sjkh sprintf (devbuf, "/dev/r%s", cdname); 99613985Sache 99710099Sjkh fd = open (devbuf, O_RDONLY); 99813985Sache 99910099Sjkh if (fd < 0 && errno == ENOENT) { 100013985Sache strcat (devbuf, DEFAULT_CD_PARTITION); 100110099Sjkh fd = open (devbuf, O_RDONLY); 100210099Sjkh } 100313985Sache 100410099Sjkh if (fd < 0) { 100513985Sache if (errno == ENXIO) { 100613985Sache /* ENXIO has an overloaded meaning here. 100713985Sache * The original "Device not configured" should 100813985Sache * be interpreted as "No disc in drive %s". */ 100913985Sache fprintf (stderr, "%s: No disc in drive %s.\n", __progname, devbuf); 101013985Sache return (0); 101110099Sjkh } 101213985Sache perror (devbuf); 101313985Sache exit (1); 101410099Sjkh } 101510099Sjkh return (1); 101610099Sjkh} 1017