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