cdcontrol.c revision 13985
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 * 2013985Sache * $Id: cdcontrol.c,v 1.10 1996/02/03 15:21:30 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 40113985Sache if (! arg || ! *arg) 40213985Sache /* Play the whole disc */ 40310099Sjkh return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, 40413985Sache toc_buffer[n].addr.msf.second, 40513985Sache toc_buffer[n].addr.msf.frame)); 40610099Sjkh 40710099Sjkh if (strchr (arg, '#')) { 40813985Sache /* Play block #blk [ len ] */ 40910099Sjkh int blk, len = 0; 41010099Sjkh 41110099Sjkh if (2 != sscanf (arg, "#%d%d", &blk, &len) && 41213985Sache 1 != sscanf (arg, "#%d", &blk)) 41313985Sache goto Clean_up; 41413985Sache 41510099Sjkh if (len == 0) 41613985Sache len = msf2lba (toc_buffer[n].addr.msf.minute, 41713985Sache toc_buffer[n].addr.msf.second, 41813985Sache toc_buffer[n].addr.msf.frame) - blk; 41910099Sjkh return play_blocks (blk, len); 42010099Sjkh } 42110099Sjkh 42210099Sjkh if (strchr (arg, ':')) { 42310099Sjkh /* 42410099Sjkh * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 42513985Sache * 42613985Sache * Will now also undestand timed addresses relative 42713985Sache * to the beginning of a track in the form... 42813985Sache * 42913985Sache * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 43010099Sjkh */ 43113985Sache unsigned tr1, tr2; 43213985Sache unsigned m1, m2, s1, s2, f1, f2; 43310099Sjkh 43413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 43513985Sache if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", 43613985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 43713985Sache goto Play_Relative_Addresses; 43813985Sache 43913985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44013985Sache if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", 44113985Sache &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 44213985Sache goto Play_Relative_Addresses; 44313985Sache 44413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44513985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", 44613985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 44713985Sache goto Play_Relative_Addresses; 44813985Sache 44913985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45013985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", 45113985Sache &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 45213985Sache goto Play_Relative_Addresses; 45313985Sache 45413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45513985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", 45613985Sache &tr1, &m1, &s1, &f1, &m2, &s2)) 45713985Sache goto Play_Relative_Addresses; 45813985Sache 45913985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46013985Sache if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", 46113985Sache &tr1, &m1, &s1, &m2, &s2, &f2)) 46213985Sache goto Play_Relative_Addresses; 46313985Sache 46413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46513985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d %d", 46613985Sache &tr1, &m1, &s1, &f1, &tr2, &m2)) 46713985Sache goto Play_Relative_Addresses; 46813985Sache 46913985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47013985Sache if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 47113985Sache goto Play_Relative_Addresses; 47213985Sache 47313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47413985Sache if (5 == sscanf (arg, "%d %d:%d %d %d", 47513985Sache &tr1, &m1, &s1, &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", 48013985Sache &tr1, &m1, &s1, &f1, &tr2)) 48113985Sache goto Play_Relative_Addresses; 48213985Sache 48313985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48413985Sache if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 48513985Sache goto Play_Relative_Addresses; 48613985Sache 48713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48813985Sache if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 48913985Sache goto Play_Relative_Addresses; 49013985Sache 49113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49213985Sache if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) 49313985Sache goto Play_Relative_Addresses; 49413985Sache 49513985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49613985Sache goto Try_Absolute_Timed_Addresses; 49713985Sache 49813985SachePlay_Relative_Addresses: 49913985Sache if (! tr1) 50013985Sache tr1 = 1; 50113985Sache else if (tr1 > n) 50213985Sache tr1 = n; 50313985Sache 50413985Sache if ((m1 > toc_buffer[tr1].addr.msf.minute) 50513985Sache || ((m1 == toc_buffer[tr1].addr.msf.minute) 50613985Sache && ((s1 > toc_buffer[tr1].addr.msf.second) 50713985Sache || ((s1 == toc_buffer[tr1].addr.msf.second) 50813985Sache && (f1 > toc_buffer[tr1].addr.msf.frame))))) { 50913985Sache printf ("Track %d is not that long.\n", tr1); 51013985Sache return (0); 51113985Sache } 51213985Sache 51313985Sache tr1--; 51413985Sache 51513985Sache f1 += toc_buffer[tr1].addr.msf.frame; 51613985Sache if (f1 >= 75) { 51713985Sache s1 += f1 / 75; 51813985Sache f1 %= 75; 51913985Sache } 52013985Sache 52113985Sache s1 += toc_buffer[tr1].addr.msf.second; 52213985Sache if (s1 >= 60) { 52313985Sache m1 += s1 / 60; 52413985Sache s1 %= 60; 52513985Sache } 52613985Sache 52713985Sache m1 += toc_buffer[tr1].addr.msf.minute; 52813985Sache 52913985Sache if (! tr2) { 53013985Sache if (m2 || s2 || f2) { 53113985Sache tr2 = tr1; 53213985Sache f2 += f1; 53313985Sache if (f2 >= 75) { 53413985Sache s2 += f2 / 75; 53513985Sache f2 %= 75; 53613985Sache } 53713985Sache 53813985Sache s2 += s1; 53913985Sache if (s2 > 60) { 54013985Sache m2 += s2 / 60; 54113985Sache s2 %= 60; 54213985Sache } 54313985Sache 54413985Sache m2 += m1; 54513985Sache } else { 54613985Sache tr2 = n; 54713985Sache m2 = toc_buffer[n].addr.msf.minute; 54813985Sache s2 = toc_buffer[n].addr.msf.second; 54913985Sache f2 = toc_buffer[n].addr.msf.frame; 55013985Sache } 55113985Sache } else if (tr2 > n) { 55213985Sache tr2 = n; 55313985Sache m2 = s2 = f2 = 0; 55413985Sache } else { 55513985Sache if (m2 || s2 || f2) 55613985Sache tr2--; 55713985Sache f2 += toc_buffer[tr2].addr.msf.frame; 55813985Sache if (f2 >= 75) { 55913985Sache s2 += f2 / 75; 56013985Sache f2 %= 75; 56113985Sache } 56213985Sache 56313985Sache s2 += toc_buffer[tr2].addr.msf.second; 56413985Sache if (s2 > 60) { 56513985Sache m2 += s2 / 60; 56613985Sache s2 %= 60; 56713985Sache } 56813985Sache 56913985Sache m2 += toc_buffer[tr2].addr.msf.minute; 57013985Sache } 57113985Sache 57213985Sache if ((tr2 < n) 57313985Sache && ((m2 > toc_buffer[n].addr.msf.minute) 57413985Sache || ((m2 == toc_buffer[n].addr.msf.minute) 57513985Sache && ((s2 > toc_buffer[n].addr.msf.second) 57613985Sache || ((s2 == toc_buffer[n].addr.msf.second) 57713985Sache && (f2 > toc_buffer[n].addr.msf.frame)))))) { 57813985Sache printf ("The playing time of the disc is not that long.\n"); 57913985Sache return (0); 58013985Sache } 58113985Sache return (play_msf (m1, s1, f1, m2, s2, f2)); 58213985Sache 58313985SacheTry_Absolute_Timed_Addresses: 58413985Sache if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", 58513985Sache &m1, &s1, &f1, &m2, &s2, &f2) && 58610099Sjkh 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 58710099Sjkh 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 58810099Sjkh 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 58910099Sjkh 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 59010099Sjkh 2 != sscanf (arg, "%d:%d", &m1, &s1)) 59113985Sache goto Clean_up; 59213985Sache 59310099Sjkh if (m2 == 0) { 59410099Sjkh m2 = toc_buffer[n].addr.msf.minute; 59510099Sjkh s2 = toc_buffer[n].addr.msf.second; 59610099Sjkh f2 = toc_buffer[n].addr.msf.frame; 59710099Sjkh } 59810099Sjkh return play_msf (m1, s1, f1, m2, s2, f2); 59910099Sjkh } 60010099Sjkh 60110099Sjkh /* 60210099Sjkh * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 60310099Sjkh */ 60410099Sjkh if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 60510099Sjkh 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 60610099Sjkh 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 60710099Sjkh 2 != sscanf (arg, "%d.%d", &start, &istart) && 60810099Sjkh 2 != sscanf (arg, "%d%d", &start, &end) && 60910099Sjkh 1 != sscanf (arg, "%d", &start)) 61013985Sache goto Clean_up; 61113985Sache 61210099Sjkh if (end == 0) 61310099Sjkh end = n; 61413985Sache return (play_track (start, istart, end, iend)); 61513985Sache 61613985SacheClean_up: 61713985Sache printf ("%s: Invalid command arguments\n", __progname); 61813985Sache return (0); 61910099Sjkh} 62010099Sjkh 62110099Sjkhchar *strstatus (int sts) 62210099Sjkh{ 62310099Sjkh switch (sts) { 62410099Sjkh case ASTS_INVALID: return ("invalid"); 62510099Sjkh case ASTS_PLAYING: return ("playing"); 62610099Sjkh case ASTS_PAUSED: return ("paused"); 62710099Sjkh case ASTS_COMPLETED: return ("completed"); 62810099Sjkh case ASTS_ERROR: return ("error"); 62910099Sjkh case ASTS_VOID: return ("void"); 63010099Sjkh default: return ("??"); 63110099Sjkh } 63210099Sjkh} 63310099Sjkh 63413884Sacheint pstatus (char *arg) 63510099Sjkh{ 63610099Sjkh struct ioc_vol v; 63713888Sache struct ioc_read_subchannel ss; 63813888Sache struct cd_sub_channel_info data; 63913884Sache int rc, trk, m, s, f; 64010099Sjkh 64110099Sjkh rc = status (&trk, &m, &s, &f); 64210099Sjkh if (rc >= 0) 64310099Sjkh if (verbose) 64410099Sjkh printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", 64510099Sjkh rc, strstatus (rc), trk, m, s, f); 64610099Sjkh else 64710099Sjkh printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 64810099Sjkh else 64913888Sache printf ("No current status info available\n"); 65010099Sjkh 65113888Sache bzero (&ss, sizeof (ss)); 65213888Sache ss.data = &data; 65313888Sache ss.data_len = sizeof (data); 65413888Sache ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 65513888Sache ss.data_format = CD_MEDIA_CATALOG; 65613888Sache rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); 65713888Sache if (rc >= 0) { 65813889Sache printf("Media catalog is %sactive", 65913888Sache ss.data->what.media_catalog.mc_valid ? "": "in"); 66013889Sache if (ss.data->what.media_catalog.mc_number[0]) 66113889Sache printf(", number \"%.15s\"", 66213889Sache ss.data->what.media_catalog.mc_number); 66313889Sache putchar('\n'); 66413888Sache } else 66513888Sache printf("No media catalog info available\n"); 66613888Sache 66710099Sjkh rc = ioctl (fd, CDIOCGETVOL, &v); 66810099Sjkh if (rc >= 0) 66910099Sjkh if (verbose) 67010099Sjkh printf ("Left volume = %d, right volume = %d\n", 67110099Sjkh v.vol[0], v.vol[1]); 67210099Sjkh else 67310099Sjkh printf ("%d %d\n", v.vol[0], v.vol[1]); 67410099Sjkh else 67513888Sache printf ("No volume level info available\n"); 67613884Sache return(0); 67713884Sache} 67810099Sjkh 67913884Sacheint info (char *arg) 68013884Sache{ 68113884Sache struct ioc_toc_header h; 68213884Sache int rc, i, n; 68313884Sache 68410099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 68513888Sache if (rc >= 0) { 68610099Sjkh if (verbose) 68710099Sjkh printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 68810099Sjkh h.starting_track, h.ending_track, h.len); 68910099Sjkh else 69010099Sjkh printf ("%d %d %d\n", h.starting_track, 69110099Sjkh h.ending_track, h.len); 69213888Sache } else { 69310099Sjkh perror ("getting toc header"); 69410099Sjkh return (rc); 69510099Sjkh } 69610099Sjkh 69710099Sjkh n = h.ending_track - h.starting_track + 1; 69810099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 69910099Sjkh if (rc < 0) 70010099Sjkh return (rc); 70113985Sache 70210099Sjkh if (verbose) { 70310099Sjkh printf ("track start duration block length type\n"); 70410099Sjkh printf ("-------------------------------------------------\n"); 70510099Sjkh } 70613985Sache 70710099Sjkh for (i = 0; i < n; i++) { 70810099Sjkh printf ("%5d ", toc_buffer[i].track); 70910099Sjkh prtrack (toc_buffer + i, 0); 71010099Sjkh } 71113867Sache printf ("%5d ", toc_buffer[n].track); 71210099Sjkh prtrack (toc_buffer + n, 1); 71310099Sjkh return (0); 71410099Sjkh} 71510099Sjkh 71613985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) 71710099Sjkh{ 71813985Sache lba += 150; /* block start offset */ 71913985Sache lba &= 0xffffff; /* negative lbas use only 24 bits */ 72010099Sjkh *m = lba / (60 * 75); 72110099Sjkh lba %= (60 * 75); 72210099Sjkh *s = lba / 75; 72310099Sjkh *f = lba % 75; 72410099Sjkh} 72510099Sjkh 72613985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f) 72710099Sjkh{ 72810099Sjkh return (((m * 60) + s) * 75 + f) - 150; 72910099Sjkh} 73010099Sjkh 73110099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag) 73210099Sjkh{ 73310099Sjkh int block, next, len; 73410099Sjkh u_char m, s, f; 73510099Sjkh 73613884Sache if (msf) { 73713884Sache /* Print track start */ 73813884Sache printf ("%2d:%02d.%02d ", e->addr.msf.minute, 73913884Sache e->addr.msf.second, e->addr.msf.frame); 74010099Sjkh 74113884Sache block = msf2lba (e->addr.msf.minute, e->addr.msf.second, 74213884Sache e->addr.msf.frame); 74313884Sache } else { 74413884Sache block = ntohl(e->addr.lba); 74513884Sache lba2msf(block, &m, &s, &f); 74613884Sache /* Print track start */ 74713884Sache printf ("%2d:%02d.%02d ", m, s, f); 74813884Sache } 74910099Sjkh if (lastflag) { 75010099Sjkh /* Last track -- print block */ 75110099Sjkh printf (" - %6d - -\n", block); 75210099Sjkh return; 75310099Sjkh } 75410099Sjkh 75513884Sache if (msf) 75613884Sache next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, 75713884Sache e[1].addr.msf.frame); 75813884Sache else 75913884Sache next = ntohl(e[1].addr.lba); 76010099Sjkh len = next - block; 76110099Sjkh lba2msf (len, &m, &s, &f); 76210099Sjkh 76310099Sjkh /* Print duration, block, length, type */ 76410099Sjkh printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 76513985Sache (e->control & 4) ? "data" : "audio"); 76610099Sjkh} 76710099Sjkh 76810099Sjkhint play_track (int tstart, int istart, int tend, int iend) 76910099Sjkh{ 77010099Sjkh struct ioc_play_track t; 77110099Sjkh 77210099Sjkh t.start_track = tstart; 77310099Sjkh t.start_index = istart; 77410099Sjkh t.end_track = tend; 77510099Sjkh t.end_index = iend; 77613985Sache 77710099Sjkh return ioctl (fd, CDIOCPLAYTRACKS, &t); 77810099Sjkh} 77910099Sjkh 78010099Sjkhint play_blocks (int blk, int len) 78110099Sjkh{ 78213985Sache struct ioc_play_blocks t; 78310099Sjkh 78410099Sjkh t.blk = blk; 78510099Sjkh t.len = len; 78613985Sache 78710099Sjkh return ioctl (fd, CDIOCPLAYBLOCKS, &t); 78810099Sjkh} 78910099Sjkh 79013985Sacheint setvol (int left, int right) 79110099Sjkh{ 79213985Sache struct ioc_vol v; 79310099Sjkh 79413985Sache v.vol[0] = left; 79513985Sache v.vol[1] = right; 79610099Sjkh v.vol[2] = 0; 79710099Sjkh v.vol[3] = 0; 79813985Sache 79910099Sjkh return ioctl (fd, CDIOCSETVOL, &v); 80010099Sjkh} 80110099Sjkh 80210099Sjkhint read_toc_entrys (int len) 80310099Sjkh{ 80410099Sjkh struct ioc_read_toc_entry t; 80510099Sjkh 80613884Sache t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 80713737Sache t.starting_track = 0; 80810099Sjkh t.data_len = len; 80910099Sjkh t.data = toc_buffer; 81013985Sache 81113985Sache return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); 81210099Sjkh} 81310099Sjkh 81410099Sjkhint play_msf (int start_m, int start_s, int start_f, 81513985Sache int end_m, int end_s, int end_f) 81610099Sjkh{ 81713985Sache struct ioc_play_msf a; 81810099Sjkh 81910099Sjkh a.start_m = start_m; 82010099Sjkh a.start_s = start_s; 82110099Sjkh a.start_f = start_f; 82210099Sjkh a.end_m = end_m; 82310099Sjkh a.end_s = end_s; 82410099Sjkh a.end_f = end_f; 82513985Sache 82610099Sjkh return ioctl (fd, CDIOCPLAYMSF, (char *) &a); 82710099Sjkh} 82810099Sjkh 82910099Sjkhint status (int *trk, int *min, int *sec, int *frame) 83010099Sjkh{ 83110099Sjkh struct ioc_read_subchannel s; 83210099Sjkh struct cd_sub_channel_info data; 83313884Sache u_char mm, ss, ff; 83410099Sjkh 83510099Sjkh bzero (&s, sizeof (s)); 83610099Sjkh s.data = &data; 83710099Sjkh s.data_len = sizeof (data); 83813884Sache s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 83910099Sjkh s.data_format = CD_CURRENT_POSITION; 84013985Sache 84110099Sjkh if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 84210099Sjkh return -1; 84313985Sache 84410099Sjkh *trk = s.data->what.position.track_number; 84513884Sache if (msf) { 84613884Sache *min = s.data->what.position.reladdr.msf.minute; 84713884Sache *sec = s.data->what.position.reladdr.msf.second; 84813884Sache *frame = s.data->what.position.reladdr.msf.frame; 84913884Sache } else { 85013884Sache lba2msf(ntohl(s.data->what.position.reladdr.lba), 85113884Sache &mm, &ss, &ff); 85213884Sache *min = mm; 85313884Sache *sec = ss; 85413884Sache *frame = ff; 85513884Sache } 85613985Sache 85710099Sjkh return s.data->header.audio_status; 85810099Sjkh} 85910099Sjkh 86010099Sjkhchar *input (int *cmd) 86110099Sjkh{ 86210099Sjkh static char buf[80]; 86310099Sjkh char *p; 86410099Sjkh 86510099Sjkh do { 86610099Sjkh if (verbose) 86713985Sache fprintf (stderr, "%s> ", __progname); 86810099Sjkh if (! fgets (buf, sizeof (buf), stdin)) { 86910099Sjkh *cmd = CMD_QUIT; 87013985Sache fprintf (stderr, "\r\n"); 87113985Sache return (0); 87210099Sjkh } 87310099Sjkh p = parse (buf, cmd); 87410099Sjkh } while (! p); 87510099Sjkh return (p); 87610099Sjkh} 87710099Sjkh 87810099Sjkhchar *parse (char *buf, int *cmd) 87910099Sjkh{ 88010099Sjkh struct cmdtab *c; 88110099Sjkh char *p; 88210099Sjkh int len; 88310099Sjkh 88413985Sache for (p=buf; isspace (*p); p++) 88513985Sache continue; 88610099Sjkh 88713985Sache if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { 88813985Sache *cmd = CMD_PLAY; 88913985Sache return (p); 89013985Sache } 89110099Sjkh 89213985Sache for (buf = p; *p && ! isspace (*p); p++) 89313985Sache continue; 89413985Sache 89513985Sache len = p - buf; 89610099Sjkh if (! len) 89710099Sjkh return (0); 89813985Sache 89913985Sache if (*p) { /* It must be a spacing character! */ 90013985Sache char *q; 90113985Sache 90213985Sache *p++ = 0; 90313985Sache for (q=p; *q && *q != '\n' && *q != '\r'; q++) 90413985Sache continue; 90513985Sache *q = 0; 90613985Sache } 90713985Sache 90810099Sjkh *cmd = -1; 90910099Sjkh for (c=cmdtab; c->name; ++c) { 91013985Sache /* Is it an exact match? */ 91113985Sache if (! strcasecmp (buf, c->name)) { 91213985Sache *cmd = c->command; 91313985Sache break; 91413985Sache } 91513985Sache 91613985Sache /* Try short hand forms then... */ 91713985Sache if (len >= c->min && ! strncasecmp (buf, c->name, len)) { 91813985Sache if (*cmd != -1 && *cmd != c->command) { 91913985Sache fprintf (stderr, "Ambiguous command\n"); 92013985Sache return (0); 92113985Sache } 92210099Sjkh *cmd = c->command; 92313985Sache } 92413985Sache } 92510099Sjkh 92610099Sjkh if (*cmd == -1) { 92713985Sache fprintf (stderr, "%s: Invalid command, enter ``help'' for commands.\n", 92813985Sache __progname); 92910099Sjkh return (0); 93010099Sjkh } 93113985Sache 93213985Sache while (isspace (*p)) 93313985Sache p++; 93410099Sjkh return p; 93510099Sjkh} 93610099Sjkh 93710099Sjkhint open_cd () 93810099Sjkh{ 93910099Sjkh char devbuf[80]; 94010099Sjkh 94110099Sjkh if (fd > -1) 94210099Sjkh return (1); 94313985Sache 94410099Sjkh if (*cdname == '/') 94510099Sjkh strcpy (devbuf, cdname); 94610099Sjkh else if (*cdname == 'r') 94710099Sjkh sprintf (devbuf, "/dev/%s", cdname); 94810099Sjkh else 94910099Sjkh sprintf (devbuf, "/dev/r%s", cdname); 95013985Sache 95110099Sjkh fd = open (devbuf, O_RDONLY); 95213985Sache 95310099Sjkh if (fd < 0 && errno == ENOENT) { 95413985Sache strcat (devbuf, DEFAULT_CD_PARTITION); 95510099Sjkh fd = open (devbuf, O_RDONLY); 95610099Sjkh } 95713985Sache 95810099Sjkh if (fd < 0) { 95913985Sache if (errno == ENXIO) { 96013985Sache /* ENXIO has an overloaded meaning here. 96113985Sache * The original "Device not configured" should 96213985Sache * be interpreted as "No disc in drive %s". */ 96313985Sache fprintf (stderr, "%s: No disc in drive %s.\n", __progname, devbuf); 96413985Sache return (0); 96510099Sjkh } 96613985Sache perror (devbuf); 96713985Sache exit (1); 96810099Sjkh } 96910099Sjkh return (1); 97010099Sjkh} 971