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