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