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