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