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