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