1 /* $OpenBSD: cdio.c,v 1.83 2021/11/28 19:28:41 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/types.h> 56 #include <sys/cdio.h> 57 #include <sys/ioctl.h> 58 #include <sys/queue.h> 59 #include <sys/scsiio.h> 60 #include <sys/stat.h> 61 62 #include <ctype.h> 63 #include <err.h> 64 #include <errno.h> 65 #include <fcntl.h> 66 #include <signal.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 #include <unistd.h> 71 #include <limits.h> 72 #include <histedit.h> 73 #include <util.h> 74 #include <vis.h> 75 76 #include "extern.h" 77 78 #define ASTS_INVALID 0x00 /* Audio status byte not valid */ 79 #define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 80 #define ASTS_PAUSED 0x12 /* Audio play operation paused */ 81 #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 82 #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 83 #define ASTS_VOID 0x15 /* No current audio status to return */ 84 85 #ifndef DEFAULT_CD_DRIVE 86 # define DEFAULT_CD_DRIVE "cd0" 87 #endif 88 89 #define CMD_DEBUG 1 90 #define CMD_DEVICE 2 91 #define CMD_EJECT 3 92 #define CMD_HELP 4 93 #define CMD_INFO 5 94 #define CMD_PAUSE 6 95 #define CMD_PLAY 7 96 #define CMD_QUIT 8 97 #define CMD_RESUME 9 98 #define CMD_STOP 10 99 #define CMD_VOLUME 11 100 #define CMD_CLOSE 12 101 #define CMD_RESET 13 102 #define CMD_SET 14 103 #define CMD_STATUS 15 104 #define CMD_NEXT 16 105 #define CMD_PREV 17 106 #define CMD_REPLAY 18 107 #define CMD_CDDB 19 108 #define CMD_CDID 20 109 #define CMD_BLANK 21 110 #define CMD_CDRIP 22 111 #define CMD_CDPLAY 23 112 113 struct cmdtab { 114 int command; 115 char *name; 116 int min; 117 char *args; 118 } cmdtab[] = { 119 { CMD_BLANK, "blank", 1, "" }, 120 { CMD_CDDB, "cddbinfo", 2, "[n]" }, 121 { CMD_CDID, "cdid", 3, "" }, 122 { CMD_CDPLAY, "cdplay", 3, "[track1-trackN ...]" }, 123 { CMD_CDRIP, "cdrip", 3, "[track1-trackN ...]" }, 124 { CMD_CLOSE, "close", 1, "" }, 125 { CMD_DEBUG, "debug", 3, "on | off" }, 126 { CMD_DEVICE, "device", 1, "devname" }, 127 { CMD_EJECT, "eject", 1, "" }, 128 { CMD_QUIT, "exit", 2, "" }, 129 { CMD_HELP, "?", 1, 0 }, 130 { CMD_HELP, "help", 1, "" }, 131 { CMD_INFO, "info", 1, "" }, 132 { CMD_NEXT, "next", 1, "" }, 133 { CMD_PAUSE, "pause", 2, "" }, 134 { CMD_PLAY, "play", 1, "[track1[.index1] [track2[.index2]]]" }, 135 { CMD_PLAY, "play", 1, "[[tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]]]" }, 136 { CMD_PLAY, "play", 1, "[#block [len]]" }, 137 { CMD_PREV, "previous", 2, "" }, 138 { CMD_QUIT, "quit", 1, "" }, 139 { CMD_REPLAY, "replay", 3, "" }, 140 { CMD_RESET, "reset", 4, "" }, 141 { CMD_RESUME, "resume", 1, "" }, 142 { CMD_SET, "set", 2, "lba | msf" }, 143 { CMD_STATUS, "status", 1, "" }, 144 { CMD_STOP, "stop", 3, "" }, 145 { CMD_VOLUME, "volume", 1, "left_channel right_channel" }, 146 { CMD_VOLUME, "volume", 1, "left | right | mono | stereo | mute" }, 147 { 0, 0, 0, 0} 148 }; 149 150 struct cd_toc_entry *toc_buffer; 151 152 struct track_head tracks; 153 154 char *cdname; 155 int fd = -1; 156 int writeperm = 0; 157 u_int8_t mediacap[MMC_FEATURE_MAX / NBBY]; 158 int verbose = 1; 159 int msf = 1; 160 const char *cddb_host; 161 char **track_names; 162 volatile sig_atomic_t signo; 163 164 EditLine *el = NULL; /* line-editing structure */ 165 History *hist = NULL; /* line-editing history */ 166 void switch_el(void); 167 168 extern char *__progname; 169 170 int setvol(int, int); 171 int read_toc_entrys(int); 172 int play_msf(int, int, int, int, int, int); 173 int play_track(int, int, int, int); 174 int status(int *, int *, int *, int *); 175 int is_wave(int); 176 __dead void tao(int argc, char **argv); 177 int play(char *arg); 178 int info(char *arg); 179 int cddbinfo(char *arg); 180 int pstatus(char *arg); 181 int play_next(char *arg); 182 int play_prev(char *arg); 183 int play_same(char *arg); 184 void sigint_handler(int); 185 char *input(int *); 186 char *prompt(void); 187 void prtrack(struct cd_toc_entry *e, int lastflag, char *name); 188 void lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f); 189 unsigned int msf2lba(u_char m, u_char s, u_char f); 190 int play_blocks(int blk, int len); 191 int run(int cmd, char *arg); 192 char *parse(char *buf, int *cmd); 193 void help(void); 194 void usage(void); 195 char *strstatus(int); 196 int cdid(void); 197 void addmsf(u_int *, u_int *, u_int *, u_char, u_char, u_char); 198 int cmpmsf(u_char, u_char, u_char, u_char, u_char, u_char); 199 void toc2msf(u_int, u_char *, u_char *, u_char *); 200 201 void 202 help(void) 203 { 204 struct cmdtab *c; 205 char *s, n; 206 int i; 207 208 for (c = cmdtab; c->name; ++c) { 209 if (!c->args) 210 continue; 211 printf("\t"); 212 for (i = c->min, s = c->name; *s; s++, i--) { 213 if (i > 0) 214 n = toupper((unsigned char)*s); 215 else 216 n = *s; 217 putchar(n); 218 } 219 if (*c->args) 220 printf(" %s", c->args); 221 printf("\n"); 222 } 223 printf("\n\tThe word \"play\" is not required for the play commands.\n"); 224 printf("\tThe plain target address is taken as a synonym for play.\n"); 225 } 226 227 void 228 usage(void) 229 { 230 fprintf(stderr, "usage: %s [-sv] [-d host:port] [-f device] [command args ...]\n", 231 __progname); 232 exit(1); 233 } 234 235 int 236 main(int argc, char **argv) 237 { 238 int ch, cmd; 239 char *arg; 240 241 cdname = getenv("DISC"); 242 if (!cdname) 243 cdname = getenv("CDROM"); 244 245 cddb_host = getenv("CDDB"); 246 if (!cddb_host) 247 cddb_host = "gnudb.gnudb.org"; 248 249 while ((ch = getopt(argc, argv, "svd:f:")) != -1) 250 switch (ch) { 251 case 's': 252 verbose = 0; 253 break; 254 case 'v': 255 verbose++; 256 break; 257 case 'f': 258 cdname = optarg; 259 break; 260 case 'd': 261 cddb_host = optarg; 262 break; 263 default: 264 usage(); 265 } 266 267 argc -= optind; 268 argv += optind; 269 270 if (argc > 0 && ! strcasecmp(*argv, "help")) 271 usage(); 272 273 if (!cdname) { 274 cdname = DEFAULT_CD_DRIVE; 275 if (verbose > 1) 276 fprintf(stderr, 277 "No CD device name specified. Defaulting to %s.\n", 278 cdname); 279 } 280 281 if (argc > 0 && !strcasecmp(*argv, "tao")) { 282 tao(argc, argv); 283 /* NOTREACHED */ 284 } 285 if (argc > 0) { 286 char buf[80], *p; 287 int len; 288 289 for (p=buf; argc-->0; ++argv) { 290 len = snprintf(p, buf + sizeof buf - p, 291 "%s%s", (p > buf) ? " " : "", *argv); 292 293 if (len < 0 || len >= buf + sizeof buf - p) 294 errx(1, "argument list too long."); 295 296 p += len; 297 } 298 arg = parse(buf, &cmd); 299 return (run(cmd, arg)); 300 } 301 302 if (verbose == 1) 303 verbose = isatty(0); 304 305 if (verbose) { 306 printf("Compact Disc Control utility, version %s\n", VERSION); 307 printf("Type `?' for command list\n\n"); 308 } 309 310 switch_el(); 311 312 for (;;) { 313 arg = input(&cmd); 314 if (run(cmd, arg) < 0) { 315 if (verbose) 316 warn(NULL); 317 close(fd); 318 fd = -1; 319 } 320 fflush(stdout); 321 } 322 } 323 324 int 325 run(int cmd, char *arg) 326 { 327 int l, r, rc; 328 static char newcdname[PATH_MAX]; 329 330 switch (cmd) { 331 332 case CMD_QUIT: 333 switch_el(); 334 exit(0); 335 336 case CMD_INFO: 337 if (!open_cd(cdname, 0)) 338 return (0); 339 340 return info(arg); 341 342 case CMD_CDDB: 343 if (!open_cd(cdname, 0)) 344 return (0); 345 346 return cddbinfo(arg); 347 348 case CMD_CDID: 349 if (!open_cd(cdname, 0)) 350 return (0); 351 return cdid(); 352 353 case CMD_STATUS: 354 if (!open_cd(cdname, 0)) 355 return (0); 356 357 return pstatus(arg); 358 359 case CMD_PAUSE: 360 if (!open_cd(cdname, 0)) 361 return (0); 362 363 return ioctl(fd, CDIOCPAUSE); 364 365 case CMD_RESUME: 366 if (!open_cd(cdname, 0)) 367 return (0); 368 369 return ioctl(fd, CDIOCRESUME); 370 371 case CMD_STOP: 372 if (!open_cd(cdname, 0)) 373 return (0); 374 375 rc = ioctl(fd, CDIOCSTOP); 376 377 (void) ioctl(fd, CDIOCALLOW); 378 379 return (rc); 380 381 case CMD_RESET: 382 if (!open_cd(cdname, 0)) 383 return (0); 384 385 rc = ioctl(fd, CDIOCRESET); 386 if (rc == -1) 387 return rc; 388 close(fd); 389 fd = -1; 390 return (0); 391 392 case CMD_DEBUG: 393 if (!open_cd(cdname, 0)) 394 return (0); 395 396 if (!strcasecmp(arg, "on")) 397 return ioctl(fd, CDIOCSETDEBUG); 398 399 if (!strcasecmp(arg, "off")) 400 return ioctl(fd, CDIOCCLRDEBUG); 401 402 printf("%s: Invalid command arguments\n", __progname); 403 404 return (0); 405 406 case CMD_DEVICE: 407 /* close old device */ 408 if (fd > -1) { 409 (void) ioctl(fd, CDIOCALLOW); 410 close(fd); 411 fd = -1; 412 } 413 414 if (strlen(arg) == 0) { 415 printf("%s: Invalid parameter\n", __progname); 416 return (0); 417 } 418 419 /* open new device */ 420 if (!open_cd(arg, 0)) 421 return (0); 422 (void) strlcpy(newcdname, arg, sizeof(newcdname)); 423 cdname = newcdname; 424 return (1); 425 426 case CMD_EJECT: 427 if (!open_cd(cdname, 0)) 428 return (0); 429 430 (void) ioctl(fd, CDIOCALLOW); 431 rc = ioctl(fd, CDIOCEJECT); 432 if (rc == -1) 433 return (rc); 434 #if defined(__OpenBSD__) 435 close(fd); 436 fd = -1; 437 #endif 438 if (track_names) 439 free_names(track_names); 440 track_names = NULL; 441 return (0); 442 443 case CMD_CLOSE: 444 #if defined(CDIOCCLOSE) 445 if (!open_cd(cdname, 0)) 446 return (0); 447 448 (void) ioctl(fd, CDIOCALLOW); 449 rc = ioctl(fd, CDIOCCLOSE); 450 if (rc == -1) 451 return (rc); 452 close(fd); 453 fd = -1; 454 return (0); 455 #else 456 printf("%s: Command not yet supported\n", __progname); 457 return (0); 458 #endif 459 460 case CMD_PLAY: 461 if (!open_cd(cdname, 0)) 462 return (0); 463 464 while (isspace((unsigned char)*arg)) 465 arg++; 466 467 return play(arg); 468 469 case CMD_SET: 470 if (!strcasecmp(arg, "msf")) 471 msf = 1; 472 else if (!strcasecmp(arg, "lba")) 473 msf = 0; 474 else 475 printf("%s: Invalid command arguments\n", __progname); 476 return (0); 477 478 case CMD_VOLUME: 479 if (!open_cd(cdname, 0)) 480 return (0); 481 482 if (!strncasecmp(arg, "left", strlen(arg))) 483 return ioctl(fd, CDIOCSETLEFT); 484 485 if (!strncasecmp(arg, "right", strlen(arg))) 486 return ioctl(fd, CDIOCSETRIGHT); 487 488 if (!strncasecmp(arg, "mono", strlen(arg))) 489 return ioctl(fd, CDIOCSETMONO); 490 491 if (!strncasecmp(arg, "stereo", strlen(arg))) 492 return ioctl(fd, CDIOCSETSTEREO); 493 494 if (!strncasecmp(arg, "mute", strlen(arg))) 495 return ioctl(fd, CDIOCSETMUTE); 496 497 if (2 != sscanf(arg, "%d%d", &l, &r)) { 498 printf("%s: Invalid command arguments\n", __progname); 499 return (0); 500 } 501 502 return setvol(l, r); 503 504 case CMD_NEXT: 505 if (!open_cd(cdname, 0)) 506 return (0); 507 508 return play_next(arg); 509 510 case CMD_PREV: 511 if (!open_cd(cdname, 0)) 512 return (0); 513 514 return play_prev(arg); 515 516 case CMD_REPLAY: 517 if (!open_cd(cdname, 0)) 518 return 0; 519 520 return play_same(arg); 521 case CMD_BLANK: 522 if (!open_cd(cdname, 1)) 523 return 0; 524 525 if (get_media_capabilities(mediacap, 1) == -1) { 526 warnx("Can't determine media type"); 527 return (0); 528 } 529 if (cdio_isset(mediacap, MMC_FEATURE_CDRW_WRITE) == 0 && 530 get_media_type() != MEDIATYPE_CDRW) { 531 warnx("The media doesn't support blanking"); 532 return (0); 533 } 534 535 return blank(); 536 case CMD_CDRIP: 537 if (!open_cd(cdname, 0)) 538 return (0); 539 540 while (isspace((unsigned char)*arg)) 541 arg++; 542 543 return cdrip(arg); 544 case CMD_CDPLAY: 545 if (!open_cd(cdname, 0)) 546 return (0); 547 548 while (isspace((unsigned char)*arg)) 549 arg++; 550 551 return cdplay(arg); 552 default: 553 case CMD_HELP: 554 help(); 555 return (0); 556 557 } 558 } 559 560 /* 561 * Check if audio file has RIFF WAVE format. If not, we assume it's just PCM. 562 */ 563 int 564 is_wave(int fd) 565 { 566 char buf[WAVHDRLEN]; 567 int rv; 568 569 rv = 0; 570 if (read(fd, buf, sizeof(buf)) == sizeof(buf)) { 571 if (memcmp(buf, "RIFF", 4) == 0 && 572 memcmp(buf + 8, "WAVE", 4) == 0) 573 rv = 1; 574 } 575 576 return (rv); 577 } 578 579 __dead void 580 tao(int argc, char **argv) 581 { 582 struct stat sb; 583 struct track_info *cur_track; 584 struct track_info *tr; 585 off_t availblk, needblk = 0; 586 u_int blklen; 587 u_int ntracks = 0; 588 char type; 589 int ch, speed; 590 const char *errstr; 591 592 if (argc == 1) 593 usage(); 594 595 SLIST_INIT(&tracks); 596 type = 'd'; 597 speed = DRIVE_SPEED_OPTIMAL; 598 blklen = 2048; 599 while (argc > 1) { 600 tr = malloc(sizeof(struct track_info)); 601 if (tr == NULL) 602 err(1, "tao"); 603 604 optreset = 1; 605 optind = 1; 606 while ((ch = getopt(argc, argv, "ads:")) != -1) { 607 switch (ch) { 608 case 'a': 609 type = 'a'; 610 blklen = 2352; 611 break; 612 case 'd': 613 type = 'd'; 614 blklen = 2048; 615 break; 616 case 's': 617 if (strcmp(optarg, "auto") == 0) { 618 speed = DRIVE_SPEED_OPTIMAL; 619 } else if (strcmp(optarg, "max") == 0) { 620 speed = DRIVE_SPEED_MAX; 621 } else { 622 speed = (int)strtonum(optarg, 1, 623 CD_MAX_SPEED, &errstr); 624 if (errstr != NULL) { 625 errx(1, 626 "incorrect speed value"); 627 } 628 } 629 break; 630 default: 631 usage(); 632 /* NOTREACHED */ 633 } 634 } 635 636 if (speed != DRIVE_SPEED_OPTIMAL && speed != DRIVE_SPEED_MAX) 637 tr->speed = CD_SPEED_TO_KBPS(speed, blklen); 638 else 639 tr->speed = speed; 640 641 tr->type = type; 642 tr->blklen = blklen; 643 argc -= optind; 644 argv += optind; 645 if (argv[0] == NULL) 646 usage(); 647 tr->file = argv[0]; 648 tr->fd = open(tr->file, O_RDONLY); 649 if (tr->fd == -1) 650 err(1, "cannot open file %s", tr->file); 651 if (fstat(tr->fd, &sb) == -1) 652 err(1, "cannot stat file %s", tr->file); 653 tr->sz = sb.st_size; 654 tr->off = 0; 655 if (tr->type == 'a') { 656 if (is_wave(tr->fd)) { 657 tr->sz -= WAVHDRLEN; 658 tr->off = WAVHDRLEN; 659 } 660 } 661 if (SLIST_EMPTY(&tracks)) 662 SLIST_INSERT_HEAD(&tracks, tr, track_list); 663 else 664 SLIST_INSERT_AFTER(cur_track, tr, track_list); 665 cur_track = tr; 666 } 667 668 if (!open_cd(cdname, 1)) 669 exit(1); 670 if (get_media_capabilities(mediacap, 1) == -1) 671 errx(1, "Can't determine media type"); 672 if (cdio_isset(mediacap, MMC_FEATURE_CD_TAO) == 0) 673 errx(1, "The media can't be written in TAO mode"); 674 675 get_disc_size(&availblk); 676 SLIST_FOREACH(tr, &tracks, track_list) { 677 needblk += tr->sz/tr->blklen; 678 ntracks++; 679 } 680 needblk += (ntracks - 1) * 150; /* transition area between tracks */ 681 if (needblk > availblk) 682 errx(1, "Only %llu of the required %llu blocks available", 683 availblk, needblk); 684 if (writetao(&tracks) != 0) 685 exit(1); 686 else 687 exit(0); 688 } 689 690 int 691 play(char *arg) 692 { 693 struct ioc_toc_header h; 694 unsigned char tm, ts, tf; 695 unsigned int tr1, tr2, m1, m2, s1, s2, f1, f2, i1, i2; 696 unsigned int blk, len, n; 697 char c; 698 int rc; 699 700 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 701 702 if (rc == -1) 703 return (rc); 704 705 if (h.starting_track > h.ending_track) { 706 printf("TOC starting_track > TOC ending_track\n"); 707 return (0); 708 } 709 710 n = h.ending_track - h.starting_track + 1; 711 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 712 713 if (rc < 0) 714 return (rc); 715 716 /* 717 * Truncate trailing white space. Then by adding %c to the end of the 718 * sscanf() formats we catch any errant trailing characters. 719 */ 720 rc = strlen(arg) - 1; 721 while (rc >= 0 && isspace((unsigned char)arg[rc])) { 722 arg[rc] = '\0'; 723 rc--; 724 } 725 726 if (!arg || ! *arg) { 727 /* Play the whole disc */ 728 return (play_track(h.starting_track, 1, h.ending_track, 1)); 729 } 730 731 if (strchr(arg, '#')) { 732 /* Play block #blk [ len ] */ 733 if (2 != sscanf(arg, "#%u%u%c", &blk, &len, &c) && 734 1 != sscanf(arg, "#%u%c", &blk, &c)) { 735 printf("%s: Invalid command arguments\n", __progname); 736 return (0); 737 } 738 739 if (len == 0) { 740 if (msf) 741 len = msf2lba(toc_buffer[n].addr.msf.minute, 742 toc_buffer[n].addr.msf.second, 743 toc_buffer[n].addr.msf.frame) - blk; 744 else 745 len = toc_buffer[n].addr.lba - blk; 746 } 747 return play_blocks(blk, len); 748 } 749 750 if (strchr(arg, ':') == NULL) { 751 /* 752 * Play track tr1[.i1] [tr2[.i2]] 753 */ 754 if (4 == sscanf(arg, "%u.%u%u.%u%c", &tr1, &i1, &tr2, &i2, &c)) 755 goto play_track; 756 757 i2 = 1; 758 if (3 == sscanf(arg, "%u.%u%u%c", &tr1, &i1, &tr2, &c)) 759 goto play_track; 760 761 i1 = 1; 762 if (3 == sscanf(arg, "%u%u.%u%c", &tr1, &tr2, &i2, &c)) 763 goto play_track; 764 765 tr2 = 0; 766 i2 = 1; 767 if (2 == sscanf(arg, "%u.%u%c", &tr1, &i1, &c)) 768 goto play_track; 769 770 i1 = i2 = 1; 771 if (2 == sscanf(arg, "%u%u%c", &tr1, &tr2, &c)) 772 goto play_track; 773 774 i1 = i2 = 1; 775 tr2 = 0; 776 if (1 == sscanf(arg, "%u%c", &tr1, &c)) 777 goto play_track; 778 779 printf("%s: Invalid command arguments\n", __progname); 780 return (0); 781 782 play_track: 783 if (tr1 > n || tr2 > n) { 784 printf("Track number must be between 0 and %u\n", n); 785 return (0); 786 } else if (tr2 == 0) 787 tr2 = h.ending_track; 788 789 if (tr1 > tr2) { 790 printf("starting_track > ending_track\n"); 791 return (0); 792 } 793 794 return (play_track(tr1, i1, tr2, i2)); 795 } 796 797 /* 798 * Play MSF [tr1] m1:s1[.f1] [tr2] [m2:s2[.f2]] 799 * 800 * Start Time End Time 801 * ---------- -------- 802 * tr1 m1:s1.f1 tr2 m2:s2.f2 803 * tr1 m1:s1 tr2 m2:s2.f2 804 * tr1 m1:s1.f1 tr2 m2:s2 805 * tr1 m1:s1 tr2 m2:s2 806 * m1:s1.f1 tr2 m2:s2.f2 807 * m1:s1 tr2 m2:s2.f2 808 * m1:s1.f1 tr2 m2:s2 809 * m1:s1 tr2 m2:s2 810 * tr1 m1:s1.f1 m2:s2.f2 811 * tr1 m1:s1 m2:s2.f2 812 * tr1 m1:s1.f1 m2:s2 813 * tr1 m1:s1 m2:s2 814 * m1:s1.f1 m2:s2.f2 815 * m1:s1 m2:s2.f2 816 * m1:s1.f1 m2:s2 817 * m1:s1 m2:s2 818 * tr1 m1:s1.f1 tr2 819 * tr1 m1:s1 tr2 820 * m1:s1.f1 tr2 821 * m1:s1 tr2 822 * tr1 m1:s1.f1 <end of disc> 823 * tr1 m1:s1 <end of disc> 824 * m1:s1.f1 <end of disc> 825 * m1:s1 <end of disc> 826 */ 827 828 /* tr1 m1:s1.f1 tr2 m2:s2.f2 */ 829 if (8 == sscanf(arg, "%u%u:%u.%u%u%u:%u.%u%c", 830 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c)) 831 goto play_msf; 832 833 /* tr1 m1:s1 tr2 m2:s2.f2 */ 834 f1 = 0; 835 if (7 == sscanf(arg, "%u%u:%u%u%u:%u.%u%c", 836 &tr1, &m1, &s1, &tr2, &m2, &s2, &f2, &c)) 837 goto play_msf; 838 839 /* tr1 m1:s1.f1 tr2 m2:s2 */ 840 f2 =0; 841 if (7 == sscanf(arg, "%u%u:%u.%u%u%u:%u%c", 842 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &c)) 843 goto play_msf; 844 845 /* m1:s1.f1 tr2 m2:s2.f2 */ 846 tr1 = 0; 847 if (7 == sscanf(arg, "%u:%u.%u%u%u:%u.%u%c", 848 &m1, &s1, &f1, &tr2, &m2, &s2, &f2, &c)) 849 goto play_msf; 850 851 /* tr1 m1:s1.f1 m2:s2.f2 */ 852 tr2 = 0; 853 if (7 == sscanf(arg, "%u%u:%u.%u%u:%u.%u%c", 854 &tr1, &m1, &s1, &f1, &m2, &s2, &f2, &c)) 855 goto play_msf; 856 857 /* m1:s1 tr2 m2:s2.f2 */ 858 tr1 = f1 = 0; 859 if (6 == sscanf(arg, "%u:%u%u%u:%u.%u%c", 860 &m1, &s1, &tr2, &m2, &s2, &f2, &c)) 861 goto play_msf; 862 863 /* m1:s1.f1 tr2 m2:s2 */ 864 tr1 = f2 = 0; 865 if (6 == sscanf(arg, "%u:%u.%u%u%u:%u%c", 866 &m1, &s1, &f1, &tr2, &m2, &s2, &c)) 867 goto play_msf; 868 869 /* m1:s1.f1 m2:s2.f2 */ 870 tr1 = tr2 = 0; 871 if (6 == sscanf(arg, "%u:%u.%u%u:%u.%u%c", 872 &m1, &s1, &f1, &m2, &s2, &f2, &c)) 873 goto play_msf; 874 875 /* tr1 m1:s1.f1 m2:s2 */ 876 tr2 = f2 = 0; 877 if (6 == sscanf(arg, "%u%u:%u.%u%u:%u%c", 878 &tr1, &m1, &s1, &f1, &m2, &s2, &c)) 879 goto play_msf; 880 881 /* tr1 m1:s1 m2:s2.f2 */ 882 tr2 = f1 = 0; 883 if (6 == sscanf(arg, "%u%u:%u%u:%u.%u%c", 884 &tr1, &m1, &s1, &m2, &s2, &f2, &c)) 885 goto play_msf; 886 887 /* tr1 m1:s1 tr2 m2:s2 */ 888 f1 = f2 = 0; 889 if (6 == sscanf(arg, "%u%u:%u%u%u:%u%c", 890 &tr1, &m1, &s1, &tr2, &m2, &s2, &c)) 891 goto play_msf; 892 893 /* m1:s1 tr2 m2:s2 */ 894 tr1 = f1 = f2 = 0; 895 if (5 == sscanf(arg, "%u:%u%u%u:%u%c", &m1, &s1, &tr2, &m2, &s2, &c)) 896 goto play_msf; 897 898 /* tr1 m1:s1 m2:s2 */ 899 f1 = tr2 = f2 = 0; 900 if (5 == sscanf(arg, "%u%u:%u%u:%u%c", &tr1, &m1, &s1, &m2, &s2, &c)) 901 goto play_msf; 902 903 /* m1:s1 m2:s2.f2 */ 904 tr1 = f1 = tr2 = 0; 905 if (5 == sscanf(arg, "%u:%u%u:%u.%u%c", &m1, &s1, &m2, &s2, &f2, &c)) 906 goto play_msf; 907 908 /* m1:s1.f1 m2:s2 */ 909 tr1 = tr2 = f2 = 0; 910 if (5 == sscanf(arg, "%u:%u.%u%u:%u%c", &m1, &s1, &f1, &m2, &s2, &c)) 911 goto play_msf; 912 913 /* tr1 m1:s1.f1 tr2 */ 914 m2 = s2 = f2 = 0; 915 if (5 == sscanf(arg, "%u%u:%u.%u%u%c", &tr1, &m1, &s1, &f1, &tr2, &c)) 916 goto play_msf; 917 918 /* m1:s1 m2:s2 */ 919 tr1 = f1 = tr2 = f2 = 0; 920 if (4 == sscanf(arg, "%u:%u%u:%u%c", &m1, &s1, &m2, &s2, &c)) 921 goto play_msf; 922 923 /* tr1 m1:s1.f1 <end of disc> */ 924 tr2 = m2 = s2 = f2 = 0; 925 if (4 == sscanf(arg, "%u%u:%u.%u%c", &tr1, &m1, &s1, &f1, &c)) 926 goto play_msf; 927 928 /* tr1 m1:s1 tr2 */ 929 f1 = m2 = s2 = f2 = 0; 930 if (4 == sscanf(arg, "%u%u:%u%u%c", &tr1, &m1, &s1, &tr2, &c)) 931 goto play_msf; 932 933 /* m1:s1.f1 tr2 */ 934 tr1 = m2 = s2 = f2 = 0; 935 if (4 == sscanf(arg, "%u%u:%u%u%c", &m1, &s1, &f1, &tr2, &c)) 936 goto play_msf; 937 938 /* m1:s1.f1 <end of disc> */ 939 tr1 = tr2 = m2 = s2 = f2 = 0; 940 if (3 == sscanf(arg, "%u:%u.%u%c", &m1, &s1, &f1, &c)) 941 goto play_msf; 942 943 /* tr1 m1:s1 <end of disc> */ 944 f1 = tr2 = m2 = s2 = f2 = 0; 945 if (3 == sscanf(arg, "%u%u:%u%c", &tr1, &m1, &s1, &c)) 946 goto play_msf; 947 948 /* m1:s1 tr2 */ 949 tr1 = f1 = m2 = s2 = f2 = 0; 950 if (3 == sscanf(arg, "%u:%u%u%c", &m1, &s1, &tr2, &c)) 951 goto play_msf; 952 953 /* m1:s1 <end of disc> */ 954 tr1 = f1 = tr2 = m2 = s2 = f2 = 0; 955 if (2 == sscanf(arg, "%u:%u%c", &m1, &s1, &c)) 956 goto play_msf; 957 958 printf("%s: Invalid command arguments\n", __progname); 959 return (0); 960 961 play_msf: 962 if (tr1 > n || tr2 > n) { 963 printf("Track number must be between 0 and %u\n", n); 964 return (0); 965 } else if (m1 > 99 || m2 > 99) { 966 printf("Minutes must be between 0 and 99\n"); 967 return (0); 968 } else if (s1 > 59 || s2 > 59) { 969 printf("Seconds must be between 0 and 59\n"); 970 return (0); 971 } else if (f1 > 74 || f2 > 74) { 972 printf("Frames number must be between 0 and 74\n"); 973 return (0); 974 } 975 976 if (tr1 > 0) { 977 /* 978 * Start time is relative to tr1, Add start time of tr1 979 * to (m1,s1,f1) to yield absolute start time. 980 */ 981 toc2msf(tr1, &tm, &ts, &tf); 982 addmsf(&m1, &s1, &f1, tm, ts, tf); 983 984 /* Compare (m1,s1,f1) to start time of next track. */ 985 toc2msf(tr1+1, &tm, &ts, &tf); 986 if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) { 987 printf("Track %u is not that long.\n", tr1); 988 return (0); 989 } 990 } 991 992 toc2msf(n+1, &tm, &ts, &tf); 993 if (cmpmsf(m1, s1, f1, tm, ts, tf) == 1) { 994 printf("Start time is after end of disc.\n"); 995 return (0); 996 } 997 998 if (tr2 > 0) { 999 /* 1000 * End time is relative to tr2, Add start time of tr2 1001 * to (m2,s2,f2) to yield absolute end time. 1002 */ 1003 toc2msf(tr2, &tm, &ts, &tf); 1004 addmsf(&m2, &s2, &f2, tm, ts, tf); 1005 1006 /* Compare (m2,s2,f2) to start time of next track. */ 1007 toc2msf(tr2+1, &tm, &ts, &tf); 1008 if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) { 1009 printf("Track %u is not that long.\n", tr2); 1010 return (0); 1011 } 1012 } 1013 1014 toc2msf(n+1, &tm, &ts, &tf); 1015 1016 if (!(tr2 || m2 || s2 || f2)) { 1017 /* Play to end of disc. */ 1018 m2 = tm; 1019 s2 = ts; 1020 f2 = tf; 1021 } else if (cmpmsf(m2, s2, f2, tm, ts, tf) == 1) { 1022 printf("End time is after end of disc.\n"); 1023 return (0); 1024 } 1025 1026 if (cmpmsf(m1, s1, f1, m2, s2, f2) == 1) { 1027 printf("Start time is after end time.\n"); 1028 return (0); 1029 } 1030 1031 return play_msf(m1, s1, f1, m2, s2, f2); 1032 } 1033 1034 /* ARGSUSED */ 1035 int 1036 play_prev(char *arg) 1037 { 1038 int trk, min, sec, frm, rc; 1039 struct ioc_toc_header h; 1040 1041 if (status(&trk, &min, &sec, &frm) >= 0) { 1042 trk--; 1043 1044 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1045 if (rc == -1) { 1046 warn("getting toc header"); 1047 return (rc); 1048 } 1049 1050 if (trk < h.starting_track) 1051 return play_track(h.starting_track, 1, 1052 h.ending_track + 1, 1); 1053 return play_track(trk, 1, h.ending_track, 1); 1054 } 1055 1056 return (0); 1057 } 1058 1059 /* ARGSUSED */ 1060 int 1061 play_same(char *arg) 1062 { 1063 int trk, min, sec, frm, rc; 1064 struct ioc_toc_header h; 1065 1066 if (status (&trk, &min, &sec, &frm) >= 0) { 1067 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1068 if (rc == -1) { 1069 warn("getting toc header"); 1070 return (rc); 1071 } 1072 1073 return play_track(trk, 1, h.ending_track, 1); 1074 } 1075 1076 return (0); 1077 } 1078 1079 /* ARGSUSED */ 1080 int 1081 play_next(char *arg) 1082 { 1083 int trk, min, sec, frm, rc; 1084 struct ioc_toc_header h; 1085 1086 if (status(&trk, &min, &sec, &frm) >= 0) { 1087 trk++; 1088 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1089 if (rc == -1) { 1090 warn("getting toc header"); 1091 return (rc); 1092 } 1093 1094 if (trk > h.ending_track) { 1095 printf("%s: end of CD\n", __progname); 1096 1097 rc = ioctl(fd, CDIOCSTOP); 1098 1099 (void) ioctl(fd, CDIOCALLOW); 1100 1101 return (rc); 1102 } 1103 1104 return play_track(trk, 1, h.ending_track, 1); 1105 } 1106 1107 return (0); 1108 } 1109 1110 char * 1111 strstatus(int sts) 1112 { 1113 switch (sts) { 1114 case ASTS_INVALID: 1115 return ("invalid"); 1116 case ASTS_PLAYING: 1117 return ("playing"); 1118 case ASTS_PAUSED: 1119 return ("paused"); 1120 case ASTS_COMPLETED: 1121 return ("completed"); 1122 case ASTS_ERROR: 1123 return ("error"); 1124 case ASTS_VOID: 1125 return ("void"); 1126 default: 1127 return ("??"); 1128 } 1129 } 1130 1131 /* ARGSUSED */ 1132 int 1133 pstatus(char *arg) 1134 { 1135 struct ioc_vol v; 1136 struct ioc_read_subchannel ss; 1137 struct cd_sub_channel_info data; 1138 int rc, trk, m, s, f; 1139 char vis_catalog[1 + 4 * 15]; 1140 1141 rc = status(&trk, &m, &s, &f); 1142 if (rc >= 0) { 1143 if (verbose) { 1144 if (track_names) 1145 printf("Audio status = %d<%s>, " 1146 "current track = %d (%s)\n" 1147 "\tcurrent position = %d:%02d.%02d\n", 1148 rc, strstatus(rc), trk, 1149 trk ? track_names[trk-1] : "", m, s, f); 1150 else 1151 printf("Audio status = %d<%s>, " 1152 "current track = %d, " 1153 "current position = %d:%02d.%02d\n", 1154 rc, strstatus(rc), trk, m, s, f); 1155 } else 1156 printf("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 1157 } else 1158 printf("No current status info available\n"); 1159 1160 bzero(&ss, sizeof (ss)); 1161 ss.data = &data; 1162 ss.data_len = sizeof (data); 1163 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1164 ss.data_format = CD_MEDIA_CATALOG; 1165 rc = ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &ss); 1166 if (rc >= 0) { 1167 printf("Media catalog is %sactive", 1168 ss.data->what.media_catalog.mc_valid ? "": "in"); 1169 if (ss.data->what.media_catalog.mc_valid && 1170 ss.data->what.media_catalog.mc_number[0]) { 1171 strvisx(vis_catalog, 1172 (char *)ss.data->what.media_catalog.mc_number, 1173 15, VIS_SAFE); 1174 printf(", number \"%.15s\"", vis_catalog); 1175 } 1176 putchar('\n'); 1177 } else 1178 printf("No media catalog info available\n"); 1179 1180 rc = ioctl(fd, CDIOCGETVOL, &v); 1181 if (rc >= 0) { 1182 if (verbose) 1183 printf("Left volume = %d, right volume = %d\n", 1184 v.vol[0], v.vol[1]); 1185 else 1186 printf("%d %d\n", v.vol[0], v.vol[1]); 1187 } else 1188 printf("No volume level info available\n"); 1189 return(0); 1190 } 1191 1192 int 1193 cdid(void) 1194 { 1195 unsigned long id; 1196 struct ioc_toc_header h; 1197 int rc, n; 1198 1199 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1200 if (rc == -1) { 1201 warn("getting toc header"); 1202 return (rc); 1203 } 1204 1205 n = h.ending_track - h.starting_track + 1; 1206 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 1207 if (rc < 0) 1208 return (rc); 1209 1210 id = cddb_discid(n, toc_buffer); 1211 if (id) { 1212 if (verbose) 1213 printf("CDID="); 1214 printf("%08lx\n", id); 1215 } 1216 return id ? 0 : 1; 1217 } 1218 1219 /* ARGSUSED */ 1220 int 1221 info(char *arg) 1222 { 1223 struct ioc_toc_header h; 1224 int rc, i, n; 1225 1226 if (get_media_capabilities(mediacap, 1) == -1) 1227 errx(1, "Can't determine media type"); 1228 1229 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1230 if (rc >= 0) { 1231 if (verbose) 1232 printf("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 1233 h.starting_track, h.ending_track, h.len); 1234 else 1235 printf("%d %d %d\n", h.starting_track, 1236 h.ending_track, h.len); 1237 } else { 1238 warn("getting toc header"); 1239 return (rc); 1240 } 1241 1242 n = h.ending_track - h.starting_track + 1; 1243 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 1244 if (rc < 0) 1245 return (rc); 1246 1247 if (verbose) { 1248 printf("track start duration block length type\n"); 1249 printf("-------------------------------------------------\n"); 1250 } 1251 1252 for (i = 0; i < n; i++) { 1253 printf("%5d ", toc_buffer[i].track); 1254 prtrack(toc_buffer + i, 0, NULL); 1255 } 1256 printf("%5d ", toc_buffer[n].track); 1257 prtrack(toc_buffer + n, 1, NULL); 1258 return (0); 1259 } 1260 1261 int 1262 cddbinfo(char *arg) 1263 { 1264 struct ioc_toc_header h; 1265 int rc, i, n; 1266 1267 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 1268 if (rc == -1) { 1269 warn("getting toc header"); 1270 return (rc); 1271 } 1272 1273 n = h.ending_track - h.starting_track + 1; 1274 rc = read_toc_entrys((n + 1) * sizeof (struct cd_toc_entry)); 1275 if (rc < 0) 1276 return (rc); 1277 1278 if (track_names) 1279 free_names(track_names); 1280 track_names = NULL; 1281 1282 track_names = cddb(cddb_host, n, toc_buffer, arg); 1283 if (!track_names) 1284 return(0); 1285 1286 printf("-------------------------------------------------\n"); 1287 1288 for (i = 0; i < n; i++) { 1289 printf("%5d ", toc_buffer[i].track); 1290 prtrack(toc_buffer + i, 0, track_names[i]); 1291 } 1292 printf("%5d ", toc_buffer[n].track); 1293 prtrack(toc_buffer + n, 1, ""); 1294 return (0); 1295 } 1296 1297 void 1298 lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f) 1299 { 1300 lba += 150; /* block start offset */ 1301 lba &= 0xffffff; /* negative lbas use only 24 bits */ 1302 *m = lba / (60 * 75); 1303 lba %= (60 * 75); 1304 *s = lba / 75; 1305 *f = lba % 75; 1306 } 1307 1308 unsigned int 1309 msf2lba(u_char m, u_char s, u_char f) 1310 { 1311 return (((m * 60) + s) * 75 + f) - 150; 1312 } 1313 1314 unsigned long 1315 entry2time(struct cd_toc_entry *e) 1316 { 1317 int block; 1318 u_char m, s, f; 1319 1320 if (msf) { 1321 return (e->addr.msf.minute * 60 + e->addr.msf.second); 1322 } else { 1323 block = e->addr.lba; 1324 lba2msf(block, &m, &s, &f); 1325 return (m*60+s); 1326 } 1327 } 1328 1329 unsigned long 1330 entry2frames(struct cd_toc_entry *e) 1331 { 1332 int block; 1333 unsigned char m, s, f; 1334 1335 if (msf) { 1336 return e->addr.msf.frame + e->addr.msf.second * 75 + 1337 e->addr.msf.minute * 60 * 75; 1338 } else { 1339 block = e->addr.lba; 1340 lba2msf(block, &m, &s, &f); 1341 return f + s * 75 + m * 60 * 75; 1342 } 1343 } 1344 1345 void 1346 prtrack(struct cd_toc_entry *e, int lastflag, char *name) 1347 { 1348 int block, next, len; 1349 u_char m, s, f; 1350 1351 if (msf) { 1352 if (!name || lastflag) 1353 /* Print track start */ 1354 printf("%2d:%02d.%02d ", e->addr.msf.minute, 1355 e->addr.msf.second, e->addr.msf.frame); 1356 1357 block = msf2lba(e->addr.msf.minute, e->addr.msf.second, 1358 e->addr.msf.frame); 1359 } else { 1360 block = e->addr.lba; 1361 if (!name || lastflag) { 1362 lba2msf(block, &m, &s, &f); 1363 /* Print track start */ 1364 printf("%2d:%02d.%02d ", m, s, f); 1365 } 1366 } 1367 if (lastflag) { 1368 if (!name) 1369 /* Last track -- print block */ 1370 printf(" - %6d - -\n", block); 1371 else 1372 printf("\n"); 1373 return; 1374 } 1375 1376 if (msf) 1377 next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second, 1378 e[1].addr.msf.frame); 1379 else 1380 next = e[1].addr.lba; 1381 len = next - block; 1382 lba2msf(len - 150, &m, &s, &f); 1383 1384 if (name) 1385 printf("%2d:%02d.%02d %s\n", m, s, f, name); 1386 /* Print duration, block, length, type */ 1387 else 1388 printf("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 1389 (e->control & 4) ? "data" : "audio"); 1390 } 1391 1392 int 1393 play_track(int tstart, int istart, int tend, int iend) 1394 { 1395 struct ioc_play_track t; 1396 1397 t.start_track = tstart; 1398 t.start_index = istart; 1399 t.end_track = tend; 1400 t.end_index = iend; 1401 1402 return ioctl(fd, CDIOCPLAYTRACKS, &t); 1403 } 1404 1405 int 1406 play_blocks(int blk, int len) 1407 { 1408 struct ioc_play_blocks t; 1409 1410 t.blk = blk; 1411 t.len = len; 1412 1413 return ioctl(fd, CDIOCPLAYBLOCKS, &t); 1414 } 1415 1416 int 1417 setvol(int left, int right) 1418 { 1419 struct ioc_vol v; 1420 1421 v.vol[0] = left; 1422 v.vol[1] = right; 1423 v.vol[2] = 0; 1424 v.vol[3] = 0; 1425 1426 return ioctl(fd, CDIOCSETVOL, &v); 1427 } 1428 1429 int 1430 read_toc_entrys(int len) 1431 { 1432 struct ioc_read_toc_entry t; 1433 1434 if (toc_buffer) { 1435 free(toc_buffer); 1436 toc_buffer = 0; 1437 } 1438 1439 toc_buffer = malloc(len); 1440 1441 if (!toc_buffer) { 1442 errno = ENOMEM; 1443 return (-1); 1444 } 1445 1446 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1447 t.starting_track = 0; 1448 t.data_len = len; 1449 t.data = toc_buffer; 1450 1451 return (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t)); 1452 } 1453 1454 int 1455 play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, int end_f) 1456 { 1457 struct ioc_play_msf a; 1458 1459 a.start_m = start_m; 1460 a.start_s = start_s; 1461 a.start_f = start_f; 1462 a.end_m = end_m; 1463 a.end_s = end_s; 1464 a.end_f = end_f; 1465 1466 return ioctl(fd, CDIOCPLAYMSF, (char *) &a); 1467 } 1468 1469 int 1470 status(int *trk, int *min, int *sec, int *frame) 1471 { 1472 struct ioc_read_subchannel s; 1473 struct cd_sub_channel_info data; 1474 u_char mm, ss, ff; 1475 1476 bzero(&s, sizeof (s)); 1477 s.data = &data; 1478 s.data_len = sizeof (data); 1479 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1480 s.data_format = CD_CURRENT_POSITION; 1481 1482 if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *) &s) == -1) 1483 return -1; 1484 1485 *trk = s.data->what.position.track_number; 1486 if (msf) { 1487 *min = s.data->what.position.reladdr.msf.minute; 1488 *sec = s.data->what.position.reladdr.msf.second; 1489 *frame = s.data->what.position.reladdr.msf.frame; 1490 } else { 1491 /* 1492 * NOTE: CDIOCREADSUBCHANNEL does not put the lba info into 1493 * host order like CDIOREADTOCENTRYS does. 1494 */ 1495 lba2msf(betoh32(s.data->what.position.reladdr.lba), &mm, &ss, 1496 &ff); 1497 *min = mm; 1498 *sec = ss; 1499 *frame = ff; 1500 } 1501 1502 return s.data->header.audio_status; 1503 } 1504 1505 void 1506 sigint_handler(int signo_arg) 1507 { 1508 signo = signo_arg; 1509 } 1510 1511 char * 1512 input(int *cmd) 1513 { 1514 struct sigaction sa; 1515 char *buf; 1516 int siz = 0; 1517 char *p; 1518 HistEvent hev; 1519 1520 memset(&sa, 0, sizeof(sa)); 1521 do { 1522 signo = 0; 1523 sa.sa_handler = sigint_handler; 1524 if (sigaction(SIGINT, &sa, NULL) == -1) 1525 err(1, "sigaction"); 1526 buf = (char *)el_gets(el, &siz); 1527 sa.sa_handler = SIG_DFL; 1528 if (sigaction(SIGINT, &sa, NULL) == -1) 1529 err(1, "sigaction"); 1530 if (buf == NULL || siz <= 0) { 1531 fprintf(stderr, "\r\n"); 1532 if (siz < 0 && errno == EINTR && signo == SIGINT) 1533 continue; 1534 *cmd = CMD_QUIT; 1535 return (0); 1536 } 1537 if (strlen(buf) > 1) 1538 history(hist, &hev, H_ENTER, buf); 1539 p = parse(buf, cmd); 1540 } while (!p); 1541 return (p); 1542 } 1543 1544 char * 1545 parse(char *buf, int *cmd) 1546 { 1547 struct cmdtab *c; 1548 char *p; 1549 size_t len; 1550 1551 for (p=buf; isspace((unsigned char)*p); p++) 1552 continue; 1553 1554 if (isdigit((unsigned char)*p) || 1555 (p[0] == '#' && isdigit((unsigned char)p[1]))) { 1556 *cmd = CMD_PLAY; 1557 return (p); 1558 } 1559 1560 for (buf = p; *p && ! isspace((unsigned char)*p); p++) 1561 continue; 1562 1563 len = p - buf; 1564 if (!len) 1565 return (0); 1566 1567 if (*p) { /* It must be a spacing character! */ 1568 char *q; 1569 1570 *p++ = 0; 1571 for (q=p; *q && *q != '\n' && *q != '\r'; q++) 1572 continue; 1573 *q = 0; 1574 } 1575 1576 *cmd = -1; 1577 for (c=cmdtab; c->name; ++c) { 1578 /* Is it an exact match? */ 1579 if (!strcasecmp(buf, c->name)) { 1580 *cmd = c->command; 1581 break; 1582 } 1583 1584 /* Try short hand forms then... */ 1585 if (len >= c->min && ! strncasecmp(buf, c->name, len)) { 1586 if (*cmd != -1 && *cmd != c->command) { 1587 fprintf(stderr, "Ambiguous command\n"); 1588 return (0); 1589 } 1590 *cmd = c->command; 1591 } 1592 } 1593 1594 if (*cmd == -1) { 1595 fprintf(stderr, "%s: Invalid command, enter ``help'' for commands.\n", 1596 __progname); 1597 return (0); 1598 } 1599 1600 while (isspace((unsigned char)*p)) 1601 p++; 1602 return p; 1603 } 1604 1605 int 1606 open_cd(char *dev, int needwrite) 1607 { 1608 char *realdev; 1609 int tries; 1610 1611 if (fd > -1) { 1612 if (needwrite && !writeperm) { 1613 close(fd); 1614 fd = -1; 1615 } else 1616 return (1); 1617 } 1618 1619 for (tries = 0; fd < 0 && tries < 10; tries++) { 1620 if (needwrite) 1621 fd = opendev(dev, O_RDWR, OPENDEV_PART, &realdev); 1622 else 1623 fd = opendev(dev, O_RDONLY, OPENDEV_PART, &realdev); 1624 if (fd == -1) { 1625 if (errno == ENXIO) { 1626 /* ENXIO has an overloaded meaning here. 1627 * The original "Device not configured" should 1628 * be interpreted as "No disc in drive %s". */ 1629 warnx("No disc in drive %s.", realdev); 1630 return (0); 1631 } else if (errno != EIO) { 1632 /* EIO may simply mean the device is not ready 1633 * yet which is common with CD changers. */ 1634 warn("Can't open %s", realdev); 1635 return (0); 1636 } 1637 } 1638 sleep(1); 1639 } 1640 if (fd == -1) { 1641 warn("Can't open %s", realdev); 1642 return (0); 1643 } 1644 writeperm = needwrite; 1645 return (1); 1646 } 1647 1648 char * 1649 prompt(void) 1650 { 1651 return (verbose ? "cdio> " : ""); 1652 } 1653 1654 void 1655 switch_el(void) 1656 { 1657 HistEvent hev; 1658 1659 if (el == NULL && hist == NULL) { 1660 el = el_init(__progname, stdin, stdout, stderr); 1661 hist = history_init(); 1662 history(hist, &hev, H_SETSIZE, 100); 1663 el_set(el, EL_HIST, history, hist); 1664 el_set(el, EL_EDITOR, "emacs"); 1665 el_set(el, EL_PROMPT, prompt); 1666 el_set(el, EL_SIGNAL, 1); 1667 el_source(el, NULL); 1668 1669 } else { 1670 if (hist != NULL) { 1671 history_end(hist); 1672 hist = NULL; 1673 } 1674 if (el != NULL) { 1675 el_end(el); 1676 el = NULL; 1677 } 1678 } 1679 } 1680 1681 void 1682 addmsf(u_int *m, u_int *s, u_int *f, u_char m_inc, u_char s_inc, u_char f_inc) 1683 { 1684 *f += f_inc; 1685 if (*f > 75) { 1686 *s += *f / 75; 1687 *f %= 75; 1688 } 1689 1690 *s += s_inc; 1691 if (*s > 60) { 1692 *m += *s / 60; 1693 *s %= 60; 1694 } 1695 1696 *m += m_inc; 1697 } 1698 1699 int 1700 cmpmsf(u_char m1, u_char s1, u_char f1, u_char m2, u_char s2, u_char f2) 1701 { 1702 if (m1 > m2) 1703 return (1); 1704 else if (m1 < m2) 1705 return (-1); 1706 1707 if (s1 > s2) 1708 return (1); 1709 else if (s1 < s2) 1710 return (-1); 1711 1712 if (f1 > f2) 1713 return (1); 1714 else if (f1 < f2) 1715 return (-1); 1716 1717 return (0); 1718 } 1719 1720 void 1721 toc2msf(u_int track, u_char *m, u_char *s, u_char *f) 1722 { 1723 struct cd_toc_entry *ctep; 1724 1725 ctep = &toc_buffer[track - 1]; 1726 1727 if (msf) { 1728 *m = ctep->addr.msf.minute; 1729 *s = ctep->addr.msf.second; 1730 *f = ctep->addr.msf.frame; 1731 } else 1732 lba2msf(ctep->addr.lba, m, s, f); 1733 } 1734