1 /* $OpenBSD: cdio.c,v 1.30 2003/06/10 22:20:45 deraadt Exp $ */ 2 3 /* Copyright (c) 1995 Serge V. Vakulenko 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Serge V. Vakulenko. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>. 35 * Based on the non-X based CD player by Jean-Marc Zucconi and 36 * Andrey A. Chernov. 37 * 38 * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>. 39 * 40 * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 41 * A couple of further fixes to my own earlier "fixes". 42 * 43 * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 44 * Added an ability to specify addresses relative to the 45 * beginning of a track. This is in fact a variation of 46 * doing the simple play_msf() call. 47 * 48 * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru> 49 * New eject algorithm. 50 * Some code style reformatting. 51 * 52 * $FreeBSD: cdcontrol.c,v 1.13 1996/06/25 21:01:27 ache Exp $ 53 */ 54 55 #include <sys/param.h> 56 #include <sys/file.h> 57 #include <sys/cdio.h> 58 #include <sys/ioctl.h> 59 60 #include <ctype.h> 61 #include <err.h> 62 #include <errno.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 #include <histedit.h> 68 #include <util.h> 69 #include <vis.h> 70 71 #include "extern.h" 72 73 #define ASTS_INVALID 0x00 /* Audio status byte not valid */ 74 #define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 75 #define ASTS_PAUSED 0x12 /* Audio play operation paused */ 76 #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 77 #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 78 #define ASTS_VOID 0x15 /* No current audio status to return */ 79 80 #ifndef DEFAULT_CD_DRIVE 81 # define DEFAULT_CD_DRIVE "cd0" 82 #endif 83 84 #define CMD_DEBUG 1 85 #define CMD_DEVICE 2 86 #define CMD_EJECT 3 87 #define CMD_HELP 4 88 #define CMD_INFO 5 89 #define CMD_PAUSE 6 90 #define CMD_PLAY 7 91 #define CMD_QUIT 8 92 #define CMD_RESUME 9 93 #define CMD_STOP 10 94 #define CMD_VOLUME 11 95 #define CMD_CLOSE 12 96 #define CMD_RESET 13 97 #define CMD_SET 14 98 #define CMD_STATUS 15 99 #define CMD_NEXT 16 100 #define CMD_PREV 17 101 #define CMD_REPLAY 18 102 #define CMD_CDDB 19 103 #define CMD_CDID 20 104 105 struct cmdtab { 106 int command; 107 char *name; 108 unsigned int min; 109 char *args; 110 } cmdtab[] = { 111 { CMD_CLOSE, "close", 1, "" }, 112 { CMD_DEBUG, "debug", 1, "on | off" }, 113 { CMD_DEVICE, "device", 1, "devname" }, 114 { CMD_EJECT, "eject", 1, "" }, 115 { CMD_HELP, "?", 1, 0 }, 116 { CMD_HELP, "help", 1, "" }, 117 { CMD_INFO, "info", 1, "" }, 118 { CMD_NEXT, "next", 1, "" }, 119 { CMD_PAUSE, "pause", 2, "" }, 120 { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 121 { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 122 { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 123 { CMD_PLAY, "play", 1, "[#block [len]]" }, 124 { CMD_PREV, "previous", 2, "" }, 125 { CMD_QUIT, "quit", 1, "" }, 126 { CMD_RESET, "reset", 4, "" }, 127 { CMD_RESUME, "resume", 1, "" }, 128 { CMD_REPLAY, "replay", 3, "" }, 129 { CMD_SET, "set", 2, "msf | lba" }, 130 { CMD_STATUS, "status", 1, "" }, 131 { CMD_STOP, "stop", 3, "" }, 132 { CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" }, 133 { CMD_CDDB, "cddbinfo", 2, "[n]" }, 134 { CMD_CDID, "cdid", 3, "" }, 135 { 0, 0, 0, 0} 136 }; 137 138 struct cd_toc_entry *toc_buffer; 139 140 char *cdname; 141 int fd = -1; 142 int verbose = 1; 143 int msf = 1; 144 const char *cddb_host; 145 char **track_names; 146 147 EditLine *el = NULL; /* line-editing structure */ 148 History *hist = NULL; /* line-editing history */ 149 void switch_el(void); 150 151 extern char *__progname; 152 153 int setvol(int, int); 154 int read_toc_entrys(int); 155 int play_msf(int, int, int, int, int, int); 156 int play_track(int, int, int, int); 157 int get_vol(int *, int *); 158 int status(int *, int *, int *, int *); 159 int open_cd(char *); 160 int play(char *arg); 161 int info(char *arg); 162 int cddbinfo(char *arg); 163 int pstatus(char *arg); 164 int play_next(char *arg); 165 int play_prev(char *arg); 166 int play_same(char *arg); 167 char *input(int *); 168 char *prompt(void); 169 void prtrack(struct cd_toc_entry *e, int lastflag, char *name); 170 void lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f); 171 unsigned int msf2lba(u_char m, u_char s, u_char f); 172 int play_blocks(int blk, int len); 173 int run(int cmd, char *arg); 174 char *parse(char *buf, int *cmd); 175 void help(void); 176 void usage(void); 177 char *strstatus(int); 178 int cdid(void); 179 180 void 181 help(void) 182 { 183 struct cmdtab *c; 184 char *s, n; 185 int i; 186 187 for (c=cmdtab; c->name; ++c) { 188 if (! c->args) 189 continue; 190 printf("\t"); 191 for (i = c->min, s = c->name; *s; s++, i--) { 192 if (i > 0) 193 n = toupper(*s); 194 else 195 n = *s; 196 putchar(n); 197 } 198 if (*c->args) 199 printf(" %s", c->args); 200 printf("\n"); 201 } 202 printf("\n\tThe word \"play\" is not required for the play commands.\n"); 203 printf("\tThe plain target address is taken as a synonym for play.\n"); 204 } 205 206 void 207 usage(void) 208 { 209 fprintf(stderr, "usage: %s [-sv] [-f device] [command args ...]\n", 210 __progname); 211 exit(1); 212 } 213 214 int 215 main(int argc, char **argv) 216 { 217 int cmd; 218 char *arg; 219 220 cdname = getenv("DISC"); 221 if (! cdname) 222 cdname = getenv("CDROM"); 223 224 cddb_host = getenv("CDDB"); 225 if (!cddb_host) 226 cddb_host = "freedb.freedb.org"; 227 228 for (;;) { 229 switch (getopt(argc, argv, "svd:f:")) { 230 case -1: 231 break; 232 case 's': 233 verbose = 0; 234 continue; 235 case 'v': 236 verbose = 2; 237 continue; 238 case 'f': 239 cdname = optarg; 240 continue; 241 case 'd': 242 cddb_host = optarg; 243 continue; 244 default: 245 usage(); 246 } 247 break; 248 } 249 argc -= optind; 250 argv += optind; 251 252 if (argc > 0 && ! strcasecmp(*argv, "help")) 253 usage(); 254 255 if (! cdname) { 256 cdname = DEFAULT_CD_DRIVE; 257 fprintf(stderr, 258 "No CD device name specified. Defaulting to %s.\n", cdname); 259 } 260 261 if (argc > 0) { 262 char buf[80], *p; 263 int len; 264 265 for (p=buf; argc-->0; ++argv) { 266 len = snprintf(p, buf + sizeof buf - p, 267 "%s%s", (p > buf) ? " " : "", *argv); 268 269 if (len >= buf + sizeof buf - p) 270 errx(1, "argument list too long."); 271 272 p += len; 273 } 274 arg = parse(buf, &cmd); 275 return (run(cmd, arg)); 276 } 277 278 if (verbose == 1) 279 verbose = isatty(0); 280 281 if (verbose) { 282 printf("Compact Disc Control utility, version %s\n", VERSION); 283 printf("Type `?' for command list\n\n"); 284 } 285 286 switch_el(); 287 288 for (;;) { 289 arg = input(&cmd); 290 if (run(cmd, arg) < 0) { 291 if (verbose) 292 warn(NULL); 293 close(fd); 294 fd = -1; 295 } 296 fflush(stdout); 297 } 298 } 299 300 int 301 run(int cmd, char *arg) 302 { 303 int l, r, rc; 304 static char newcdname[MAXPATHLEN]; 305 306 switch (cmd) { 307 308 case CMD_QUIT: 309 switch_el(); 310 exit(0); 311 312 case CMD_INFO: 313 if (fd < 0 && ! open_cd(cdname)) 314 return (0); 315 316 return info(arg); 317 318 case CMD_CDDB: 319 if (fd < 0 && ! open_cd(cdname)) 320 return (0); 321 322 return cddbinfo(arg); 323 324 case CMD_CDID: 325 if (fd < 0 && ! open_cd(cdname)) 326 return (0); 327 return cdid(); 328 329 case CMD_STATUS: 330 if (fd < 0 && ! open_cd(cdname)) 331 return (0); 332 333 return pstatus(arg); 334 335 case CMD_PAUSE: 336 if (fd < 0 && ! open_cd(cdname)) 337 return (0); 338 339 return ioctl(fd, CDIOCPAUSE); 340 341 case CMD_RESUME: 342 if (fd < 0 && ! open_cd(cdname)) 343 return (0); 344 345 return ioctl(fd, CDIOCRESUME); 346 347 case CMD_STOP: 348 if (fd < 0 && ! open_cd(cdname)) 349 return (0); 350 351 rc = ioctl(fd, CDIOCSTOP); 352 353 (void) ioctl(fd, CDIOCALLOW); 354 355 return (rc); 356 357 case CMD_RESET: 358 if (fd < 0 && ! open_cd(cdname)) 359 return (0); 360 361 rc = ioctl(fd, CDIOCRESET); 362 if (rc < 0) 363 return rc; 364 close(fd); 365 fd = -1; 366 return (0); 367 368 case CMD_DEBUG: 369 if (fd < 0 && ! open_cd(cdname)) 370 return (0); 371 372 if (! strcasecmp(arg, "on")) 373 return ioctl(fd, CDIOCSETDEBUG); 374 375 if (! strcasecmp(arg, "off")) 376 return ioctl(fd, CDIOCCLRDEBUG); 377 378 printf("%s: Invalid command arguments\n", __progname); 379 380 return (0); 381 382 case CMD_DEVICE: 383 /* close old device */ 384 if (fd > -1) { 385 (void) ioctl(fd, CDIOCALLOW); 386 close(fd); 387 fd = -1; 388 } 389 390 /* open new device */ 391 if (!open_cd(arg)) 392 return (0); 393 (void) strlcpy(newcdname, arg, sizeof(newcdname)); 394 cdname = newcdname; 395 return (1); 396 397 case CMD_EJECT: 398 if (fd < 0 && ! open_cd(cdname)) 399 return (0); 400 401 (void) ioctl(fd, CDIOCALLOW); 402 rc = ioctl(fd, CDIOCEJECT); 403 if (rc < 0) 404 return (rc); 405 #if defined(__OpenBSD__) 406 close(fd); 407 fd = -1; 408 #endif 409 if (track_names) 410 free_names(track_names); 411 track_names = NULL; 412 return (0); 413 414 case CMD_CLOSE: 415 #if defined(CDIOCCLOSE) 416 if (fd < 0 && ! open_cd(cdname)) 417 return (0); 418 419 (void) ioctl(fd, CDIOCALLOW); 420 rc = ioctl(fd, CDIOCCLOSE); 421 if (rc < 0) 422 return (rc); 423 close(fd); 424 fd = -1; 425 return (0); 426 #else 427 printf("%s: Command not yet supported\n", __progname); 428 return (0); 429 #endif 430 431 case CMD_PLAY: 432 if (fd < 0 && ! open_cd(cdname)) 433 return (0); 434 435 while (isspace(*arg)) 436 arg++; 437 438 return play(arg); 439 440 case CMD_SET: 441 if (!strcasecmp(arg, "msf")) 442 msf = 1; 443 else if (!strcasecmp(arg, "lba")) 444 msf = 0; 445 else 446 printf("%s: Invalid command arguments\n", __progname); 447 return (0); 448 449 case CMD_VOLUME: 450 if (fd < 0 && !open_cd(cdname)) 451 return (0); 452 453 if (!strncasecmp(arg, "left", strlen(arg))) 454 return ioctl(fd, CDIOCSETLEFT); 455 456 if (!strncasecmp(arg, "right", strlen(arg))) 457 return ioctl(fd, CDIOCSETRIGHT); 458 459 if (!strncasecmp(arg, "mono", strlen(arg))) 460 return ioctl(fd, CDIOCSETMONO); 461 462 if (!strncasecmp(arg, "stereo", strlen(arg))) 463 return ioctl(fd, CDIOCSETSTEREO); 464 465 if (!strncasecmp(arg, "mute", strlen(arg))) 466 return ioctl(fd, CDIOCSETMUTE); 467 468 if (2 != sscanf(arg, "%d %d", &l, &r)) { 469 printf("%s: Invalid command arguments\n", __progname); 470 return (0); 471 } 472 473 return setvol(l, r); 474 475 case CMD_NEXT: 476 if (fd < 0 && ! open_cd(cdname)) 477 return (0); 478 479 return play_next(arg); 480 481 case CMD_PREV: 482 if (fd < 0 && ! open_cd(cdname)) 483 return (0); 484 485 return play_prev(arg); 486 487 case CMD_REPLAY: 488 if (fd < 0 && ! open_cd(cdname)) 489 return 0; 490 491 return play_same(arg); 492 default: 493 case CMD_HELP: 494 help(); 495 return (0); 496 497 } 498 } 499 500 int 501 play(char *arg) 502 { 503 struct ioc_toc_header h; 504 int rc, n, start, end = 0, istart = 1, iend = 1; 505 506 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 507 508 if (rc < 0) 509 return (rc); 510 511 n = h.ending_track - h.starting_track + 1; 512 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 513 514 if (rc < 0) 515 return (rc); 516 517 if (! arg || ! *arg) { 518 /* Play the whole disc */ 519 520 return (play_track(h.starting_track, 1, 521 h.ending_track, 1)); 522 } 523 524 if (strchr(arg, '#')) { 525 /* Play block #blk [ len ] */ 526 int blk, len = 0; 527 528 if (2 != sscanf(arg, "#%d%d", &blk, &len) && 529 1 != sscanf(arg, "#%d", &blk)) 530 goto Clean_up; 531 532 if (len == 0) { 533 if (msf) 534 len = msf2lba(toc_buffer[n].addr.msf.minute, 535 toc_buffer[n].addr.msf.second, 536 toc_buffer[n].addr.msf.frame) - blk; 537 else 538 len = ntohl(toc_buffer[n].addr.lba) - blk; 539 } 540 return play_blocks(blk, len); 541 } 542 543 if (strchr(arg, ':')) { 544 /* 545 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 546 * 547 * Will now also undestand timed addresses relative 548 * to the beginning of a track in the form... 549 * 550 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 551 */ 552 int tr1, tr2; 553 unsigned int m1, m2, s1, s2, f1, f2; 554 unsigned char tm, ts, tf; 555 556 tr2 = m2 = s2 = f2 = f1 = 0; 557 if (8 == sscanf(arg, "%d %d:%d.%d %d %d:%d.%d", 558 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 559 goto Play_Relative_Addresses; 560 561 tr2 = m2 = s2 = f2 = f1 = 0; 562 if (7 == sscanf(arg, "%d %d:%d %d %d:%d.%d", 563 &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 564 goto Play_Relative_Addresses; 565 566 tr2 = m2 = s2 = f2 = f1 = 0; 567 if (7 == sscanf(arg, "%d %d:%d.%d %d %d:%d", 568 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 569 goto Play_Relative_Addresses; 570 571 tr2 = m2 = s2 = f2 = f1 = 0; 572 if (7 == sscanf(arg, "%d %d:%d.%d %d:%d.%d", 573 &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 574 goto Play_Relative_Addresses; 575 576 tr2 = m2 = s2 = f2 = f1 = 0; 577 if (6 == sscanf(arg, "%d %d:%d.%d %d:%d", 578 &tr1, &m1, &s1, &f1, &m2, &s2)) 579 goto Play_Relative_Addresses; 580 581 tr2 = m2 = s2 = f2 = f1 = 0; 582 if (6 == sscanf(arg, "%d %d:%d %d:%d.%d", 583 &tr1, &m1, &s1, &m2, &s2, &f2)) 584 goto Play_Relative_Addresses; 585 586 tr2 = m2 = s2 = f2 = f1 = 0; 587 if (6 == sscanf(arg, "%d %d:%d.%d %d %d", 588 &tr1, &m1, &s1, &f1, &tr2, &m2)) 589 goto Play_Relative_Addresses; 590 591 tr2 = m2 = s2 = f2 = f1 = 0; 592 if (5 == sscanf(arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 593 goto Play_Relative_Addresses; 594 595 tr2 = m2 = s2 = f2 = f1 = 0; 596 if (5 == sscanf(arg, "%d %d:%d %d %d", 597 &tr1, &m1, &s1, &tr2, &m2)) 598 goto Play_Relative_Addresses; 599 600 tr2 = m2 = s2 = f2 = f1 = 0; 601 if (5 == sscanf(arg, "%d %d:%d.%d %d", 602 &tr1, &m1, &s1, &f1, &tr2)) 603 goto Play_Relative_Addresses; 604 605 tr2 = m2 = s2 = f2 = f1 = 0; 606 if (4 == sscanf(arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 607 goto Play_Relative_Addresses; 608 609 tr2 = m2 = s2 = f2 = f1 = 0; 610 if (4 == sscanf(arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 611 goto Play_Relative_Addresses; 612 613 tr2 = m2 = s2 = f2 = f1 = 0; 614 if (3 == sscanf(arg, "%d %d:%d", &tr1, &m1, &s1)) 615 goto Play_Relative_Addresses; 616 617 tr2 = m2 = s2 = f2 = f1 = 0; 618 goto Try_Absolute_Timed_Addresses; 619 620 Play_Relative_Addresses: 621 if (tr1 <= 0) 622 tr1 = 1; 623 else if (tr1 > n) 624 tr1 = n; 625 626 if (msf) { 627 tm = toc_buffer[tr1].addr.msf.minute; 628 ts = toc_buffer[tr1].addr.msf.second; 629 tf = toc_buffer[tr1].addr.msf.frame; 630 } else 631 lba2msf(ntohl(toc_buffer[tr1].addr.lba), 632 &tm, &ts, &tf); 633 if ((m1 > tm) 634 || ((m1 == tm) 635 && ((s1 > ts) 636 || ((s1 == ts) 637 && (f1 > tf))))) { 638 printf("Track %d is not that long.\n", tr1); 639 return (0); 640 } 641 642 tr1--; 643 644 f1 += tf; 645 if (f1 >= 75) { 646 s1 += f1 / 75; 647 f1 %= 75; 648 } 649 650 s1 += ts; 651 if (s1 >= 60) { 652 m1 += s1 / 60; 653 s1 %= 60; 654 } 655 656 m1 += tm; 657 658 if (tr2 <= 0) { 659 if (m2 || s2 || f2) { 660 tr2 = tr1; 661 f2 += f1; 662 if (f2 >= 75) { 663 s2 += f2 / 75; 664 f2 %= 75; 665 } 666 667 s2 += s1; 668 if (s2 > 60) { 669 m2 += s2 / 60; 670 s2 %= 60; 671 } 672 673 m2 += m1; 674 } else { 675 tr2 = n; 676 if (msf) { 677 m2 = toc_buffer[n].addr.msf.minute; 678 s2 = toc_buffer[n].addr.msf.second; 679 f2 = toc_buffer[n].addr.msf.frame; 680 } else { 681 lba2msf(ntohl(toc_buffer[n].addr.lba), 682 &tm, &ts, &tf); 683 m2 = tm; 684 s2 = ts; 685 f2 = tf; 686 } 687 } 688 } else if (tr2 > n) { 689 tr2 = n; 690 m2 = s2 = f2 = 0; 691 } else { 692 if (m2 || s2 || f2) 693 tr2--; 694 if (msf) { 695 tm = toc_buffer[tr2].addr.msf.minute; 696 ts = toc_buffer[tr2].addr.msf.second; 697 tf = toc_buffer[tr2].addr.msf.frame; 698 } else 699 lba2msf(ntohl(toc_buffer[tr2].addr.lba), 700 &tm, &ts, &tf); 701 f2 += tf; 702 if (f2 >= 75) { 703 s2 += f2 / 75; 704 f2 %= 75; 705 } 706 707 s2 += ts; 708 if (s2 > 60) { 709 m2 += s2 / 60; 710 s2 %= 60; 711 } 712 713 m2 += tm; 714 } 715 716 if (msf) { 717 tm = toc_buffer[n].addr.msf.minute; 718 ts = toc_buffer[n].addr.msf.second; 719 tf = toc_buffer[n].addr.msf.frame; 720 } else 721 lba2msf(ntohl(toc_buffer[n].addr.lba), 722 &tm, &ts, &tf); 723 if ((tr2 < n) 724 && ((m2 > tm) 725 || ((m2 == tm) 726 && ((s2 > ts) 727 || ((s2 == ts) 728 && (f2 > tf)))))) { 729 printf("The playing time of the disc is not that long.\n"); 730 return (0); 731 } 732 return (play_msf(m1, s1, f1, m2, s2, f2)); 733 734 Try_Absolute_Timed_Addresses: 735 if (6 != sscanf(arg, "%d:%d.%d%d:%d.%d", 736 &m1, &s1, &f1, &m2, &s2, &f2) && 737 5 != sscanf(arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 738 5 != sscanf(arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 739 3 != sscanf(arg, "%d:%d.%d", &m1, &s1, &f1) && 740 4 != sscanf(arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 741 2 != sscanf(arg, "%d:%d", &m1, &s1)) 742 goto Clean_up; 743 744 if (m2 == 0) { 745 if (msf) { 746 m2 = toc_buffer[n].addr.msf.minute; 747 s2 = toc_buffer[n].addr.msf.second; 748 f2 = toc_buffer[n].addr.msf.frame; 749 } else { 750 lba2msf(ntohl(toc_buffer[n].addr.lba), 751 &tm, &ts, &tf); 752 m2 = tm; 753 s2 = ts; 754 f2 = tf; 755 } 756 } 757 return play_msf(m1, s1, f1, m2, s2, f2); 758 } 759 760 /* 761 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 762 */ 763 if (4 != sscanf(arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 764 3 != sscanf(arg, "%d.%d%d", &start, &istart, &end) && 765 3 != sscanf(arg, "%d%d.%d", &start, &end, &iend) && 766 2 != sscanf(arg, "%d.%d", &start, &istart) && 767 2 != sscanf(arg, "%d%d", &start, &end) && 768 1 != sscanf(arg, "%d", &start)) 769 goto Clean_up; 770 771 if (end == 0) 772 end = h.ending_track; 773 return (play_track(start, istart, end, iend)); 774 775 Clean_up: 776 printf("%s: Invalid command arguments\n", __progname); 777 return (0); 778 } 779 780 int 781 play_prev(char *arg) 782 { 783 int trk, min, sec, frm, rc; 784 struct ioc_toc_header h; 785 786 if (status(&trk, &min, &sec, &frm) >= 0) { 787 trk--; 788 789 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 790 if (rc < 0) { 791 warn("getting toc header"); 792 return (rc); 793 } 794 795 if (trk < h.starting_track) 796 return play_track(h.starting_track, 1, 797 h.ending_track + 1, 1); 798 return play_track(trk, 1, h.ending_track, 1); 799 } 800 801 return (0); 802 } 803 804 int 805 play_same(char *arg) 806 { 807 int trk, min, sec, frm, rc; 808 struct ioc_toc_header h; 809 810 if (status (&trk, &min, &sec, &frm) >= 0) { 811 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 812 if (rc < 0) { 813 warn("getting toc header"); 814 return (rc); 815 } 816 817 return play_track(trk, 1, h.ending_track, 1); 818 } 819 820 return (0); 821 } 822 823 int 824 play_next(char *arg) 825 { 826 int trk, min, sec, frm, rc; 827 struct ioc_toc_header h; 828 829 if (status(&trk, &min, &sec, &frm) >= 0) { 830 trk++; 831 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 832 if (rc < 0) { 833 warn("getting toc header"); 834 return (rc); 835 } 836 837 if (trk > h.ending_track) { 838 printf("%s: end of CD\n", __progname); 839 840 rc = ioctl(fd, CDIOCSTOP); 841 842 (void) ioctl(fd, CDIOCALLOW); 843 844 return (rc); 845 } 846 847 return play_track(trk, 1, h.ending_track, 1); 848 } 849 850 return (0); 851 } 852 853 char * 854 strstatus(int sts) 855 { 856 switch (sts) { 857 case ASTS_INVALID: 858 return ("invalid"); 859 case ASTS_PLAYING: 860 return ("playing"); 861 case ASTS_PAUSED: 862 return ("paused"); 863 case ASTS_COMPLETED: 864 return ("completed"); 865 case ASTS_ERROR: 866 return ("error"); 867 case ASTS_VOID: 868 return ("void"); 869 default: 870 return ("??"); 871 } 872 } 873 874 int 875 pstatus(char *arg) 876 { 877 struct ioc_vol v; 878 struct ioc_read_subchannel ss; 879 struct cd_sub_channel_info data; 880 int rc, trk, m, s, f; 881 char vis_catalog[1 + 4 * 15]; 882 883 rc = status(&trk, &m, &s, &f); 884 if (rc >= 0) { 885 if (verbose) { 886 if (track_names) 887 printf("Audio status = %d<%s>, " 888 "current track = %d (%s)\n" 889 "\tcurrent position = %d:%02d.%02d\n", 890 rc, strstatus(rc), trk, 891 trk ? track_names[trk-1] : "", m, s, f); 892 else 893 printf("Audio status = %d<%s>, " 894 "current track = %d, " 895 "current position = %d:%02d.%02d\n", 896 rc, strstatus(rc), trk, m, s, f); 897 } else 898 printf("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 899 } else 900 printf("No current status info available\n"); 901 902 bzero(&ss, sizeof (ss)); 903 ss.data = &data; 904 ss.data_len = sizeof (data); 905 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 906 ss.data_format = CD_MEDIA_CATALOG; 907 rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss); 908 if (rc >= 0) { 909 printf("Media catalog is %sactive", 910 ss.data->what.media_catalog.mc_valid ? "": "in"); 911 if (ss.data->what.media_catalog.mc_valid && 912 ss.data->what.media_catalog.mc_number[0]) { 913 strvisx(vis_catalog, 914 ss.data->what.media_catalog.mc_number, 915 15, VIS_SAFE); 916 printf(", number \"%.15s\"", vis_catalog); 917 } 918 putchar('\n'); 919 } else 920 printf("No media catalog info available\n"); 921 922 rc = ioctl(fd, CDIOCGETVOL, &v); 923 if (rc >= 0) { 924 if (verbose) 925 printf("Left volume = %d, right volume = %d\n", 926 v.vol[0], v.vol[1]); 927 else 928 printf("%d %d\n", v.vol[0], v.vol[1]); 929 } else 930 printf("No volume level info available\n"); 931 return(0); 932 } 933 934 int 935 cdid(void) 936 { 937 unsigned long id; 938 struct ioc_toc_header h; 939 int rc, n; 940 941 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 942 if (rc == -1) { 943 warn("getting toc header"); 944 return (rc); 945 } 946 947 n = h.ending_track - h.starting_track + 1; 948 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 949 if (rc < 0) 950 return (rc); 951 952 id = cddb_discid(n, toc_buffer); 953 if (id) { 954 if (verbose) 955 printf("CDID="); 956 printf("%08lx\n", id); 957 } 958 return id ? 0 : 1; 959 } 960 961 int 962 info(char *arg) 963 { 964 struct ioc_toc_header h; 965 int rc, i, n; 966 967 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 968 if (rc >= 0) { 969 if (verbose) 970 printf("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 971 h.starting_track, h.ending_track, h.len); 972 else 973 printf("%d %d %d\n", h.starting_track, 974 h.ending_track, h.len); 975 } else { 976 warn("getting toc header"); 977 return (rc); 978 } 979 980 n = h.ending_track - h.starting_track + 1; 981 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 982 if (rc < 0) 983 return (rc); 984 985 if (verbose) { 986 printf("track start duration block length type\n"); 987 printf("-------------------------------------------------\n"); 988 } 989 990 for (i = 0; i < n; i++) { 991 printf("%5d ", toc_buffer[i].track); 992 prtrack(toc_buffer + i, 0, NULL); 993 } 994 printf("%5d ", toc_buffer[n].track); 995 prtrack(toc_buffer + n, 1, NULL); 996 return (0); 997 } 998 999 int 1000 cddbinfo(char *arg) 1001 { 1002 struct ioc_toc_header h; 1003 int rc, i, n; 1004 1005 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1006 if (rc == -1) { 1007 warn("getting toc header"); 1008 return (rc); 1009 } 1010 1011 n = h.ending_track - h.starting_track + 1; 1012 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 1013 if (rc < 0) 1014 return (rc); 1015 1016 if (track_names) 1017 free_names(track_names); 1018 track_names = NULL; 1019 1020 track_names = cddb(cddb_host, n, toc_buffer, arg); 1021 if (!track_names) 1022 return(0); 1023 1024 printf("-------------------------------------------------\n"); 1025 1026 for (i = 0; i < n; i++) { 1027 printf("%5d ", toc_buffer[i].track); 1028 prtrack(toc_buffer + i, 0, track_names[i]); 1029 } 1030 printf("%5d ", toc_buffer[n].track); 1031 prtrack(toc_buffer + n, 1, ""); 1032 return (0); 1033 } 1034 1035 void 1036 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f) 1037 { 1038 lba += 150; /* block start offset */ 1039 lba &= 0xffffff; /* negative lbas use only 24 bits */ 1040 *m = lba / (60 * 75); 1041 lba %= (60 * 75); 1042 *s = lba / 75; 1043 *f = lba % 75; 1044 } 1045 1046 unsigned int 1047 msf2lba(u_char m, u_char s, u_char f) 1048 { 1049 return (((m * 60) + s) * 75 + f) - 150; 1050 } 1051 1052 unsigned long 1053 entry2time(struct cd_toc_entry *e) 1054 { 1055 int block; 1056 u_char m, s, f; 1057 1058 if (msf) { 1059 return (e->addr.msf.minute * 60 + e->addr.msf.second); 1060 } else { 1061 block = ntohl(e->addr.lba); 1062 lba2msf(block, &m, &s, &f); 1063 return (m*60+s); 1064 } 1065 } 1066 1067 unsigned long 1068 entry2frames(struct cd_toc_entry *e) 1069 { 1070 int block; 1071 unsigned char m, s, f; 1072 1073 if (msf) { 1074 return e->addr.msf.frame + e->addr.msf.second * 75 + 1075 e->addr.msf.minute * 60 * 75; 1076 } else { 1077 block = ntohl(e->addr.lba); 1078 lba2msf(block, &m, &s, &f); 1079 return f + s * 75 + m * 60 * 75; 1080 } 1081 } 1082 1083 void 1084 prtrack(struct cd_toc_entry *e, int lastflag, char *name) 1085 { 1086 int block, next, len; 1087 u_char m, s, f; 1088 1089 if (msf) { 1090 if (!name || lastflag) 1091 /* Print track start */ 1092 printf("%2d:%02d.%02d ", e->addr.msf.minute, 1093 e->addr.msf.second, e->addr.msf.frame); 1094 1095 block = msf2lba(e->addr.msf.minute, e->addr.msf.second, 1096 e->addr.msf.frame); 1097 } else { 1098 block = ntohl(e->addr.lba); 1099 if (!name || lastflag) { 1100 lba2msf(block, &m, &s, &f); 1101 /* Print track start */ 1102 printf("%2d:%02d.%02d ", m, s, f); 1103 } 1104 } 1105 if (lastflag) { 1106 if (!name) 1107 /* Last track -- print block */ 1108 printf(" - %6d - -\n", block); 1109 else 1110 printf("\n"); 1111 return; 1112 } 1113 1114 if (msf) 1115 next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second, 1116 e[1].addr.msf.frame); 1117 else 1118 next = ntohl(e[1].addr.lba); 1119 len = next - block; 1120 lba2msf(len, &m, &s, &f); 1121 1122 if (name) 1123 printf("%2d:%02d.%02d %s\n", m, s, f, name); 1124 /* Print duration, block, length, type */ 1125 else 1126 printf("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 1127 (e->control & 4) ? "data" : "audio"); 1128 } 1129 1130 int 1131 play_track(int tstart, int istart, int tend, int iend) 1132 { 1133 struct ioc_play_track t; 1134 1135 t.start_track = tstart; 1136 t.start_index = istart; 1137 t.end_track = tend; 1138 t.end_index = iend; 1139 1140 return ioctl(fd, CDIOCPLAYTRACKS, &t); 1141 } 1142 1143 int 1144 play_blocks(int blk, int len) 1145 { 1146 struct ioc_play_blocks t; 1147 1148 t.blk = blk; 1149 t.len = len; 1150 1151 return ioctl(fd, CDIOCPLAYBLOCKS, &t); 1152 } 1153 1154 int 1155 setvol(int left, int right) 1156 { 1157 struct ioc_vol v; 1158 1159 v.vol[0] = left; 1160 v.vol[1] = right; 1161 v.vol[2] = 0; 1162 v.vol[3] = 0; 1163 1164 return ioctl(fd, CDIOCSETVOL, &v); 1165 } 1166 1167 int 1168 read_toc_entrys(int len) 1169 { 1170 struct ioc_read_toc_entry t; 1171 1172 if (toc_buffer) { 1173 free(toc_buffer); 1174 toc_buffer = 0; 1175 } 1176 1177 toc_buffer = malloc(len); 1178 1179 if (!toc_buffer) { 1180 errno = ENOMEM; 1181 return (-1); 1182 } 1183 1184 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1185 t.starting_track = 0; 1186 t.data_len = len; 1187 t.data = toc_buffer; 1188 1189 return (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t)); 1190 } 1191 1192 int 1193 play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, int end_f) 1194 { 1195 struct ioc_play_msf a; 1196 1197 a.start_m = start_m; 1198 a.start_s = start_s; 1199 a.start_f = start_f; 1200 a.end_m = end_m; 1201 a.end_s = end_s; 1202 a.end_f = end_f; 1203 1204 return ioctl(fd, CDIOCPLAYMSF, (char *) &a); 1205 } 1206 1207 int 1208 status(int *trk, int *min, int *sec, int *frame) 1209 { 1210 struct ioc_read_subchannel s; 1211 struct cd_sub_channel_info data; 1212 u_char mm, ss, ff; 1213 1214 bzero(&s, sizeof (s)); 1215 s.data = &data; 1216 s.data_len = sizeof (data); 1217 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1218 s.data_format = CD_CURRENT_POSITION; 1219 1220 if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 1221 return -1; 1222 1223 *trk = s.data->what.position.track_number; 1224 if (msf) { 1225 *min = s.data->what.position.reladdr.msf.minute; 1226 *sec = s.data->what.position.reladdr.msf.second; 1227 *frame = s.data->what.position.reladdr.msf.frame; 1228 } else { 1229 lba2msf(ntohl(s.data->what.position.reladdr.lba), 1230 &mm, &ss, &ff); 1231 *min = mm; 1232 *sec = ss; 1233 *frame = ff; 1234 } 1235 1236 return s.data->header.audio_status; 1237 } 1238 1239 char * 1240 input(int *cmd) 1241 { 1242 char *buf; 1243 int siz = 0; 1244 char *p; 1245 1246 do { 1247 if ((buf = (char *) el_gets(el, &siz)) == NULL || !siz) { 1248 *cmd = CMD_QUIT; 1249 fprintf(stderr, "\r\n"); 1250 return (0); 1251 } 1252 if (strlen(buf) > 1) 1253 history(hist, H_ENTER, buf); 1254 p = parse(buf, cmd); 1255 } while (!p); 1256 return (p); 1257 } 1258 1259 char * 1260 parse(char *buf, int *cmd) 1261 { 1262 struct cmdtab *c; 1263 char *p; 1264 int len; 1265 1266 for (p=buf; isspace(*p); p++) 1267 continue; 1268 1269 if (isdigit(*p) || (p[0] == '#' && isdigit(p[1]))) { 1270 *cmd = CMD_PLAY; 1271 return (p); 1272 } 1273 1274 for (buf = p; *p && ! isspace(*p); p++) 1275 continue; 1276 1277 len = p - buf; 1278 if (! len) 1279 return (0); 1280 1281 if (*p) { /* It must be a spacing character! */ 1282 char *q; 1283 1284 *p++ = 0; 1285 for (q=p; *q && *q != '\n' && *q != '\r'; q++) 1286 continue; 1287 *q = 0; 1288 } 1289 1290 *cmd = -1; 1291 for (c=cmdtab; c->name; ++c) { 1292 /* Is it an exact match? */ 1293 if (! strcasecmp(buf, c->name)) { 1294 *cmd = c->command; 1295 break; 1296 } 1297 1298 /* Try short hand forms then... */ 1299 if (len >= c->min && ! strncasecmp(buf, c->name, len)) { 1300 if (*cmd != -1 && *cmd != c->command) { 1301 fprintf(stderr, "Ambiguous command\n"); 1302 return (0); 1303 } 1304 *cmd = c->command; 1305 } 1306 } 1307 1308 if (*cmd == -1) { 1309 fprintf(stderr, "%s: Invalid command, enter ``help'' for commands.\n", 1310 __progname); 1311 return (0); 1312 } 1313 1314 while (isspace(*p)) 1315 p++; 1316 return p; 1317 } 1318 1319 int 1320 open_cd(char *dev) 1321 { 1322 char *realdev; 1323 int tries; 1324 1325 if (fd > -1) 1326 return (1); 1327 1328 for (tries = 0; fd < 0 && tries < 10; tries++) { 1329 fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev); 1330 if (fd < 0) { 1331 if (errno == ENXIO) { 1332 /* ENXIO has an overloaded meaning here. 1333 * The original "Device not configured" should 1334 * be interpreted as "No disc in drive %s". */ 1335 warnx("No disc in drive %s.", realdev); 1336 return (0); 1337 } else if (errno != EIO) { 1338 /* EIO may simply mean the device is not ready 1339 * yet which is common with CD changers. */ 1340 warn("Can't open %s", realdev); 1341 return (0); 1342 } 1343 } 1344 sleep(1); 1345 } 1346 if (fd < 0) { 1347 warn("Can't open %s", realdev); 1348 return (0); 1349 } 1350 return (1); 1351 } 1352 1353 char * 1354 prompt(void) 1355 { 1356 return (verbose ? "cdio> " : ""); 1357 } 1358 1359 void 1360 switch_el(void) 1361 { 1362 if (el == NULL && hist == NULL) { 1363 el = el_init(__progname, stdin, stdout); 1364 hist = history_init(); 1365 history(hist, H_EVENT, 100); 1366 el_set(el, EL_HIST, history, hist); 1367 el_set(el, EL_EDITOR, "emacs"); 1368 el_set(el, EL_PROMPT, prompt); 1369 el_set(el, EL_SIGNAL, 1); 1370 el_source(el, NULL); 1371 1372 } else { 1373 if (hist != NULL) { 1374 history_end(hist); 1375 hist = NULL; 1376 } 1377 if (el != NULL) { 1378 el_end(el); 1379 el = NULL; 1380 } 1381 } 1382 } 1383