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