1 /* $NetBSD: cdplay.c,v 1.32 2006/01/12 18:15:59 garbled Exp $ */ 2 3 /* 4 * Copyright (c) 1999, 2000, 2001 Andrew Doran. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 /* 31 * Compact Disc Control Utility, originally by Serge V. Vakulenko 32 * <vak@cronyx.ru>. First appeared in FreeBSD under the guise of 33 * cdcontrol(1). Based on the non-X based CD player by Jean-Marc 34 * Zucconi and Andrey A. Chernov. Fixed and further modified on 35 * by Jukka Ukkonen <jau@funet.fi>. Lots of fixes and improvements 36 * made subsequently by The NetBSD Project. 37 * 38 * from FreeBSD: cdcontrol.c,v 1.17.2.1 1999/01/31 15:36:01 billf Exp 39 */ 40 41 #include <sys/cdefs.h> 42 #ifndef lint 43 __RCSID("$NetBSD: cdplay.c,v 1.32 2006/01/12 18:15:59 garbled Exp $"); 44 #endif /* not lint */ 45 46 #include <sys/types.h> 47 48 #include <sys/endian.h> 49 #include <sys/ioctl.h> 50 #include <sys/file.h> 51 #include <sys/cdio.h> 52 53 #include <assert.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <histedit.h> 59 #include <limits.h> 60 #include <signal.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <util.h> 66 67 enum cmd { 68 CMD_CLOSE, 69 CMD_EJECT, 70 CMD_HELP, 71 CMD_INFO, 72 CMD_NEXT, 73 CMD_PAUSE, 74 CMD_PLAY, 75 CMD_PREV, 76 CMD_QUIT, 77 CMD_RESET, 78 CMD_RESUME, 79 CMD_SET, 80 CMD_SHUFFLE, 81 CMD_SINGLE, 82 CMD_SKIP, 83 CMD_STATUS, 84 CMD_STOP, 85 CMD_VOLUME, 86 }; 87 88 struct cmdtab { 89 enum cmd command; 90 const char *name; 91 unsigned int min; 92 const char *args; 93 } const cmdtab[] = { 94 { CMD_HELP, "?", 1, 0 }, 95 { CMD_CLOSE, "close", 1, NULL }, 96 { CMD_EJECT, "eject", 1, NULL }, 97 { CMD_HELP, "help", 1, NULL }, 98 { CMD_INFO, "info", 1, NULL }, 99 { CMD_NEXT, "next", 1, NULL }, 100 { CMD_PAUSE, "pause", 2, NULL }, 101 { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 102 { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 103 { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 104 { CMD_PLAY, "play", 1, "[#block [len]]" }, 105 { CMD_PREV, "prev", 2, NULL }, 106 { CMD_QUIT, "quit", 1, NULL }, 107 { CMD_RESET, "reset", 4, NULL }, 108 { CMD_RESUME, "resume", 4, NULL }, 109 { CMD_SET, "set", 2, "msf | lba" }, 110 { CMD_SHUFFLE, "shuffle", 2, NULL }, 111 { CMD_SINGLE, "single", 2, "[<track>]" }, 112 { CMD_SKIP, "skip", 2, NULL }, 113 { CMD_STATUS, "status", 3, NULL }, 114 { CMD_STOP, "stop", 3, NULL }, 115 { CMD_VOLUME, "volume", 1, "<l> <r>|left|right|mute|mono|stereo" }, 116 }; 117 118 #define IOCTL_SIMPLE(fd, ctl) \ 119 do { \ 120 if ((rv = ioctl(fd, ctl)) >= 0) { \ 121 close(fd); \ 122 fd = -1; \ 123 } else \ 124 warn("ioctl(" #ctl ")"); \ 125 } while (0) 126 127 #define CD_MAX_TRACK 99 /* largest 2 digit BCD number */ 128 129 struct cd_toc_entry toc_buffer[CD_MAX_TRACK + 1]; 130 131 const char *cdname; 132 int fd = -1; 133 int msf = 1; 134 int shuffle; 135 int interactive = 1; 136 struct itimerval itv_timer; 137 138 History *hist; 139 HistEvent he; 140 EditLine *elptr; 141 142 int get_vol(int *, int *); 143 int get_status(int *, int *, int *, int *, int *); 144 void help(void); 145 int info(const char *); 146 void lba2msf(u_long, u_int *, u_int *, u_int *); 147 int main(int, char **); 148 u_int msf2lba(u_int, u_int, u_int); 149 int opencd(void); 150 const char *parse(char *, int *); 151 int play(const char *, int); 152 int play_blocks(int, int); 153 int play_msf(int, int, int, int, int, int); 154 int play_track(int, int, int, int); 155 int print_status(const char *); 156 void print_track(struct cd_toc_entry *); 157 const char *prompt(void); 158 int read_toc_entrys(int); 159 int run(int, const char *); 160 int setvol(int, int); 161 void sig_timer(int); 162 int skip(int, int); 163 const char *strstatus(int); 164 void usage(void); 165 166 void toc2msf(u_int, u_int *, u_int *, u_int *); 167 int toc2lba(u_int); 168 void addmsf(u_int *, u_int *, u_int *, u_int, u_int, u_int); 169 170 int 171 main(int argc, char **argv) 172 { 173 const char *arg; 174 char buf[80], *p; 175 static char defdev[16]; 176 int cmd, len, c; 177 char *line; 178 const char *elline; 179 int scratch, rv; 180 struct sigaction sa_timer; 181 182 cdname = getenv("MUSIC_CD"); 183 if (cdname == NULL) 184 cdname = getenv("CD_DRIVE"); 185 if (cdname == NULL) 186 cdname = getenv("DISC"); 187 if (cdname == NULL) 188 cdname = getenv("CDPLAY"); 189 190 while ((c = getopt(argc, argv, "f:h")) != -1) 191 switch (c) { 192 case 'f': 193 cdname = optarg; 194 continue; 195 case 'h': 196 default: 197 usage(); 198 /* NOTREACHED */ 199 } 200 argc -= optind; 201 argv += optind; 202 203 if (argc > 0 && strcasecmp(*argv, "help") == 0) 204 usage(); 205 206 if (cdname == NULL) { 207 snprintf(defdev, sizeof(defdev), "cd0%c", 208 'a' + getrawpartition()); 209 cdname = defdev; 210 } 211 212 opencd(); 213 214 if (argc > 0) { 215 interactive = 0; 216 for (p = buf; argc-- > 0; argv++) { 217 len = strlen(*argv); 218 219 if (p + len >= buf + sizeof(buf) - 1) 220 usage(); 221 if (p > buf) 222 *p++ = ' '; 223 224 strlcpy(p, *argv, sizeof(buf) - (p - buf)); 225 p += len; 226 } 227 *p = '\0'; 228 arg = parse(buf, &cmd); 229 return (run(cmd, arg)); 230 } 231 232 printf("Type `?' for command list\n\n"); 233 234 hist = history_init(); 235 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 236 elptr = el_init(getprogname(), stdin, stdout, stderr); 237 el_set(elptr, EL_EDITOR, "emacs"); 238 el_set(elptr, EL_PROMPT, prompt); 239 el_set(elptr, EL_HIST, history, hist); 240 el_source(elptr, NULL); 241 242 sigemptyset(&sa_timer.sa_mask); 243 sa_timer.sa_handler = sig_timer; 244 sa_timer.sa_flags = SA_RESTART; 245 if ((rv = sigaction(SIGALRM, &sa_timer, NULL)) < 0) 246 err(EXIT_FAILURE, "sigaction()"); 247 248 for (;;) { 249 line = NULL; 250 do { 251 if (((elline = el_gets(elptr, &scratch)) != NULL) 252 && (scratch != 0)){ 253 history(hist, &he, H_ENTER, elline); 254 line = strdup(elline); 255 arg = parse(line, &cmd); 256 } else { 257 cmd = CMD_QUIT; 258 fprintf(stderr, "\r\n"); 259 arg = 0; 260 break; 261 } 262 } while (arg == NULL); 263 264 if (run(cmd, arg) < 0) { 265 if (fd != -1) 266 close(fd); 267 fd = -1; 268 } 269 fflush(stdout); 270 if (line != NULL) 271 free(line); 272 } 273 274 el_end(elptr); 275 history_end(hist); 276 exit(EXIT_SUCCESS); 277 /* NOTREACHED */ 278 } 279 280 void 281 usage(void) 282 { 283 284 fprintf(stderr, "usage: cdplay [-f device] [command ...]\n"); 285 exit(EXIT_FAILURE); 286 /* NOTREACHED */ 287 } 288 289 void 290 help(void) 291 { 292 const struct cmdtab *c, *mc; 293 const char *s; 294 int i, n; 295 296 mc = cmdtab + sizeof(cmdtab) / sizeof(cmdtab[0]); 297 for (c = cmdtab; c < mc; c++) { 298 for (i = c->min, s = c->name; *s != '\0'; s++, i--) { 299 n = (i > 0 ? toupper((unsigned char)*s) : *s); 300 putchar(n); 301 } 302 if (c->args != NULL) 303 printf(" %s", c->args); 304 putchar('\n'); 305 } 306 printf( 307 "\nThe word \"play\" is not required for the play commands.\n" 308 "The plain target address is taken as a synonym for play.\n"); 309 } 310 311 int 312 run(int cmd, const char *arg) 313 { 314 int l, r, rv; 315 316 rv = 0; 317 if (cmd == CMD_QUIT) { 318 close(fd); 319 exit(EXIT_SUCCESS); 320 /* NOTREACHED */ 321 } 322 323 if (fd < 0 && !opencd()) 324 return (0); 325 326 switch (cmd) { 327 case CMD_INFO: 328 rv = info(arg); 329 break; 330 331 case CMD_STATUS: 332 rv = print_status(arg); 333 break; 334 335 case CMD_PAUSE: 336 if ((rv = ioctl(fd, CDIOCPAUSE)) < 0) 337 warn("ioctl(CDIOCPAUSE)"); 338 break; 339 340 case CMD_RESUME: 341 if ((rv = ioctl(fd, CDIOCRESUME)) < 0) 342 warn("ioctl(CDIOCRESUME)"); 343 break; 344 345 case CMD_STOP: 346 if ((rv = ioctl(fd, CDIOCSTOP)) < 0) 347 warn("ioctl(CDIOCSTOP)"); 348 if (ioctl(fd, CDIOCALLOW) < 0) 349 warn("ioctl(CDIOCALLOW)"); 350 break; 351 352 case CMD_RESET: 353 IOCTL_SIMPLE(fd, CDIOCRESET); 354 return (0); 355 356 case CMD_EJECT: 357 if (shuffle) 358 run(CMD_SHUFFLE, NULL); 359 if (ioctl(fd, CDIOCALLOW) < 0) 360 warn("ioctl(CDIOCALLOW)"); 361 IOCTL_SIMPLE(fd, CDIOCEJECT); 362 break; 363 364 case CMD_CLOSE: 365 ioctl(fd, CDIOCALLOW); 366 IOCTL_SIMPLE(fd, CDIOCCLOSE); 367 if (interactive && fd == -1) 368 opencd(); 369 break; 370 371 case CMD_PLAY: 372 while (isspace((unsigned char)*arg)) 373 arg++; 374 rv = play(arg, 1); 375 break; 376 377 case CMD_PREV: 378 rv = skip(-1, 1); 379 break; 380 381 case CMD_NEXT: 382 rv = skip(1, 1); 383 break; 384 385 case CMD_SINGLE: 386 if (interactive == 0) 387 errx(EXIT_FAILURE, 388 "'single' valid only in interactive mode"); 389 /*FALLTHROUGH*/ 390 case CMD_SHUFFLE: 391 if (interactive == 0) 392 errx(EXIT_FAILURE, 393 "`shuffle' valid only in interactive mode"); 394 if (shuffle == 0) { 395 itv_timer.it_interval.tv_sec = 1; 396 itv_timer.it_interval.tv_usec = 0; 397 itv_timer.it_value.tv_sec = 1; 398 itv_timer.it_value.tv_usec = 0; 399 if (setitimer(ITIMER_REAL, &itv_timer, NULL) == 0) { 400 if (cmd == CMD_SHUFFLE) { 401 shuffle = 1; 402 } else { 403 while (isspace((unsigned char)*arg)) 404 arg++; 405 shuffle = -atoi(arg); 406 } 407 skip(0, 1); 408 } 409 } else { 410 itv_timer.it_interval.tv_sec = 0; 411 itv_timer.it_interval.tv_usec = 0; 412 itv_timer.it_value.tv_sec = 0; 413 itv_timer.it_value.tv_usec = 0; 414 if (setitimer(ITIMER_REAL, &itv_timer, NULL) == 0) 415 shuffle = 0; 416 } 417 if (shuffle < 0) 418 printf("single track:\t%d\n", -shuffle); 419 else 420 printf("shuffle play:\t%s\n", (shuffle != 0) ? "on" : "off"); 421 rv = 0; 422 break; 423 424 case CMD_SKIP: 425 if (!interactive) 426 errx(EXIT_FAILURE, 427 "`skip' valid only in interactive mode"); 428 if (!shuffle) 429 warnx("`skip' valid only in shuffle mode"); 430 else 431 skip(0, 1); 432 break; 433 434 case CMD_SET: 435 if (strcasecmp(arg, "msf") == 0) 436 msf = 1; 437 else if (strcasecmp(arg, "lba") == 0) 438 msf = 0; 439 else 440 warnx("invalid command arguments"); 441 break; 442 443 case CMD_VOLUME: 444 if (strncasecmp(arg, "left", strlen(arg)) == 0) 445 rv = ioctl(fd, CDIOCSETLEFT); 446 else if (strncasecmp(arg, "right", strlen(arg)) == 0) 447 rv = ioctl(fd, CDIOCSETRIGHT); 448 else if (strncasecmp(arg, "mono", strlen(arg)) == 0) 449 rv = ioctl(fd, CDIOCSETMONO); 450 else if (strncasecmp(arg, "stereo", strlen(arg)) == 0) 451 rv = ioctl(fd, CDIOCSETSTEREO); 452 else if (strncasecmp(arg, "mute", strlen(arg)) == 0) 453 rv = ioctl(fd, CDIOCSETMUTE); 454 else { 455 rv = 0; 456 if (sscanf(arg, "%d %d", &l, &r) != 2) { 457 if (sscanf(arg, "%d", &l) == 1) 458 r = l; 459 else { 460 warnx("invalid command arguments"); 461 break; 462 } 463 } 464 rv = setvol(l, r); 465 } 466 break; 467 468 case CMD_HELP: 469 default: 470 help(); 471 rv = 0; 472 break; 473 } 474 475 return (rv); 476 } 477 478 int 479 play(const char *arg, int fromuser) 480 { 481 int rv, n, start, end, istart, iend, blk, len, relend; 482 u_int tr1, tr2, m1, m2, s1, s2, f1, f2, tm, ts, tf; 483 struct ioc_toc_header h; 484 485 if (shuffle && fromuser) { 486 warnx("`play' not valid in shuffle mode"); 487 return (0); 488 } 489 490 if ((rv = ioctl(fd, CDIOREADTOCHEADER, &h)) < 0) { 491 warn("ioctl(CDIOREADTOCHEADER)"); 492 return (rv); 493 } 494 495 end = 0; 496 istart = iend = 1; 497 n = h.ending_track - h.starting_track + 1; 498 rv = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry)); 499 if (rv < 0) 500 return (rv); 501 502 if (arg == NULL || *arg == '\0') { 503 /* Play the whole disc */ 504 return (play_track(h.starting_track, 1, h.ending_track, 99)); 505 } 506 507 if (strchr(arg, '#') != NULL) { 508 /* Play block #blk [ len ] */ 509 len = 0; 510 511 if (2 != sscanf(arg, "#%d%d", &blk, &len) && 512 1 != sscanf(arg, "#%d", &blk)) 513 goto Clean_up; 514 515 if (len == 0) { 516 len = toc2lba(n); 517 } 518 return (play_blocks(blk, len)); 519 } 520 521 if (strchr(arg, ':') != NULL) { 522 /* 523 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 524 * 525 * Will now also undestand timed addresses relative 526 * to the beginning of a track in the form... 527 * 528 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 529 */ 530 relend = 1; 531 tr2 = m2 = s2 = f2 = f1 = 0; 532 if (8 == sscanf(arg, "%d %d:%d.%d %d %d:%d.%d", &tr1, &m1, 533 &s1, &f1, &tr2, &m2, &s2, &f2)) 534 goto Play_Relative_Addresses; 535 536 tr2 = m2 = s2 = f2 = f1 = 0; 537 if (7 == sscanf(arg, "%d %d:%d %d %d:%d.%d", &tr1, &m1, &s1, 538 &tr2, &m2, &s2, &f2)) 539 goto Play_Relative_Addresses; 540 541 tr2 = m2 = s2 = f2 = f1 = 0; 542 if (7 == sscanf(arg, "%d %d:%d.%d %d %d:%d", &tr1, &m1, &s1, 543 &f1, &tr2, &m2, &s2)) 544 goto Play_Relative_Addresses; 545 546 tr2 = m2 = s2 = f2 = f1 = 0; 547 if (7 == sscanf(arg, "%d %d:%d.%d %d:%d.%d", &tr1, &m1, &s1, 548 &f1, &m2, &s2, &f2)) 549 goto Play_Relative_Addresses; 550 551 tr2 = m2 = s2 = f2 = f1 = 0; 552 if (6 == sscanf(arg, "%d %d:%d.%d %d:%d", &tr1, &m1, &s1, &f1, 553 &m2, &s2)) 554 goto Play_Relative_Addresses; 555 556 tr2 = m2 = s2 = f2 = f1 = 0; 557 if (6 == sscanf(arg, "%d %d:%d %d:%d.%d", &tr1, &m1, &s1, &m2, 558 &s2, &f2)) 559 goto Play_Relative_Addresses; 560 561 tr2 = m2 = s2 = f2 = f1 = 0; 562 if (6 == sscanf(arg, "%d %d:%d.%d %d %d", &tr1, &m1, &s1, &f1, 563 &tr2, &m2)) 564 goto Play_Relative_Addresses; 565 566 tr2 = m2 = s2 = f2 = f1 = 0; 567 if (6 == sscanf(arg, "%d %d:%d %d %d:%d", &tr1, &m1, &s1, &tr2, 568 &m2, &s2)) 569 goto Play_Relative_Addresses; 570 571 tr2 = m2 = s2 = f2 = f1 = 0; 572 if (5 == sscanf(arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, 573 &s2)) 574 goto Play_Relative_Addresses; 575 576 tr2 = m2 = s2 = f2 = f1 = 0; 577 if (5 == sscanf(arg, "%d %d:%d %d %d", &tr1, &m1, &s1, &tr2, 578 &m2)) 579 goto Play_Relative_Addresses; 580 581 relend=0; 582 tr2 = m2 = s2 = f2 = f1 = 0; 583 if (5 == sscanf(arg, "%d %d:%d.%d %d", &tr1, &m1, &s1, &f1, 584 &tr2)) 585 goto Play_Relative_Addresses; 586 587 tr2 = m2 = s2 = f2 = f1 = 0; 588 if (4 == sscanf(arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 589 goto Play_Relative_Addresses; 590 591 tr2 = m2 = s2 = f2 = f1 = 0; 592 if (4 == sscanf(arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 593 goto Play_Relative_Addresses; 594 595 tr2 = m2 = s2 = f2 = f1 = 0; 596 if (3 == sscanf(arg, "%d %d:%d", &tr1, &m1, &s1)) 597 goto Play_Relative_Addresses; 598 599 tr2 = m2 = s2 = f2 = f1 = 0; 600 goto Try_Absolute_Timed_Addresses; 601 602 Play_Relative_Addresses: 603 if (!tr1) 604 tr1 = 1; 605 else if (tr1 > n) 606 tr1 = n; 607 608 toc2msf(tr1-1, &tm, &ts, &tf); 609 addmsf(&m1, &s1, &f1, tm, ts, tf); 610 611 toc2msf(tr1, &tm, &ts, &tf); 612 613 if ((m1 > tm) || ((m1 == tm) && ((s1 > ts) || ((s1 == ts) && 614 (f1 > tf))))) { 615 warnx("Track %d is not that long.", tr1); 616 return (0); 617 } 618 tr1--; /* XXXXX ???? */ 619 620 621 if (!tr2) { 622 if (relend) { 623 tr2 = tr1; 624 625 addmsf(&m2, &s2, &f2, m1, s1, f1); 626 } else { 627 tr2 = n; 628 629 toc2msf(n, &m2, &s2, &f2); 630 } 631 } else { 632 if (tr2 > n) { 633 tr2 = n; 634 m2 = s2 = f2 = 0; 635 } else { 636 if (relend) 637 tr2--; 638 639 toc2msf(tr2, &tm, &ts, &tf); 640 addmsf(&m2, &s2, &f2, tm, ts, tf); 641 } 642 } 643 644 toc2msf(n, &tm, &ts, &tf); 645 646 if ((tr2 < n) && ((m2 > tm) || ((m2 == tm) && ((s2 > ts) || 647 ((s2 == ts) && (f2 > tf)))))) { 648 warnx("The playing time of the disc is not that long."); 649 return (0); 650 } 651 652 return (play_msf(m1, s1, f1, m2, s2, f2)); 653 654 Try_Absolute_Timed_Addresses: 655 m2 = UINT_MAX; 656 657 if (6 != sscanf(arg, "%d:%d.%d%d:%d.%d", 658 &m1, &s1, &f1, &m2, &s2, &f2) && 659 5 != sscanf(arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 660 5 != sscanf(arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 661 3 != sscanf(arg, "%d:%d.%d", &m1, &s1, &f1) && 662 4 != sscanf(arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 663 2 != sscanf(arg, "%d:%d", &m1, &s1)) 664 goto Clean_up; 665 666 if (m2 == UINT_MAX) { 667 if (msf) { 668 m2 = toc_buffer[n].addr.msf.minute; 669 s2 = toc_buffer[n].addr.msf.second; 670 f2 = toc_buffer[n].addr.msf.frame; 671 } else { 672 lba2msf(be32toh(toc_buffer[n].addr.lba), 673 &tm, &ts, &tf); 674 m2 = tm; 675 s2 = ts; 676 f2 = tf; 677 } 678 } 679 return (play_msf(m1, s1, f1, m2, s2, f2)); 680 } 681 682 /* 683 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 684 */ 685 if (4 != sscanf(arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 686 3 != sscanf(arg, "%d.%d%d", &start, &istart, &end) && 687 3 != sscanf(arg, "%d%d.%d", &start, &end, &iend) && 688 2 != sscanf(arg, "%d.%d", &start, &istart) && 689 2 != sscanf(arg, "%d%d", &start, &end) && 690 1 != sscanf(arg, "%d", &start)) 691 goto Clean_up; 692 693 if (end == 0) 694 end = n; 695 return (play_track(start, istart, end, iend)); 696 697 Clean_up: 698 warnx("invalid command arguments"); 699 return (0); 700 } 701 702 void 703 sig_timer(int sig) 704 { 705 sigset_t anymore; 706 707 sigpending(&anymore); 708 if (sigismember(&anymore, SIGALRM)) 709 return; 710 setitimer(ITIMER_REAL, &itv_timer, NULL); 711 if (fd != -1) 712 skip(0, 0); 713 } 714 715 int 716 skip(int dir, int fromuser) 717 { 718 char str[16]; 719 int rv, trk, idx, m, s, f; 720 struct ioc_toc_header h; 721 722 if ((rv = ioctl(fd, CDIOREADTOCHEADER, &h)) < 0) { 723 warn("ioctl(CDIOREADTOCHEADER)"); 724 return (rv); 725 } 726 if ((rv = get_status(&trk, &idx, &m, &s, &f)) < 0) 727 return (rv); 728 729 if (dir == 0) { 730 if (fromuser || (rv != CD_AS_PLAY_IN_PROGRESS && 731 rv != CD_AS_PLAY_PAUSED)) 732 trk = shuffle < 0 ? (-shuffle) : (h.starting_track + 733 arc4random() % (h.ending_track - h.starting_track + 1)); 734 else 735 return (0); 736 } else { 737 trk += dir; 738 if (trk > h.ending_track) 739 trk = h.starting_track; 740 else if(trk < h.starting_track) 741 trk = h.ending_track; 742 } 743 744 if (shuffle) 745 snprintf(str, sizeof(str), "%d %d", trk, trk); 746 else 747 snprintf(str, sizeof(str), "%d", trk); 748 749 return (play(str, 0)); 750 } 751 752 const char * 753 strstatus(int sts) 754 { 755 const char *str; 756 757 switch (sts) { 758 case CD_AS_AUDIO_INVALID: 759 str = "invalid"; 760 break; 761 case CD_AS_PLAY_IN_PROGRESS: 762 str = "playing"; 763 break; 764 case CD_AS_PLAY_PAUSED: 765 str = "paused"; 766 break; 767 case CD_AS_PLAY_COMPLETED: 768 str = "completed"; 769 break; 770 case CD_AS_PLAY_ERROR: 771 str = "error"; 772 break; 773 case CD_AS_NO_STATUS: 774 str = "not playing"; 775 break; 776 default: 777 str = "<unknown>"; 778 break; 779 } 780 781 return (str); 782 } 783 784 int 785 print_status(const char *arg) 786 { 787 struct cd_sub_channel_info data; 788 struct ioc_read_subchannel ss; 789 int rv, trk, idx, m, s, f; 790 struct ioc_vol v; 791 792 if ((rv = get_status(&trk, &idx, &m, &s, &f)) >= 0) { 793 printf("audio status:\t%s\n", strstatus(rv)); 794 printf("current track:\t%d\n", trk); 795 printf("current index:\t%d\n", idx); 796 printf("position:\t%d:%02d.%02d\n", m, s, f); 797 } else 798 printf("audio status:\tno info available\n"); 799 800 if (shuffle < 0) 801 printf("single track:\t%d\n", -shuffle); 802 else 803 printf("shuffle play:\t%s\n", (shuffle != 0) ? "on" : "off"); 804 805 bzero(&ss, sizeof(ss)); 806 ss.data = &data; 807 ss.data_len = sizeof(data); 808 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 809 ss.data_format = CD_MEDIA_CATALOG; 810 811 if (ioctl(fd, CDIOCREADSUBCHANNEL, (char *)&ss) >= 0) { 812 printf("media catalog:\t%sactive", 813 ss.data->what.media_catalog.mc_valid ? "" : "in"); 814 if (ss.data->what.media_catalog.mc_valid && 815 ss.data->what.media_catalog.mc_number[0]) 816 printf(" (%.15s)", 817 ss.data->what.media_catalog.mc_number); 818 putchar('\n'); 819 } else 820 printf("media catalog:\tnone\n"); 821 822 if (ioctl(fd, CDIOCGETVOL, &v) >= 0) { 823 printf("left volume:\t%d\n", v.vol[0]); 824 printf("right volume:\t%d\n", v.vol[1]); 825 } else { 826 printf("left volume:\tnot available\n"); 827 printf("right volume:\tnot available\n"); 828 } 829 830 ; return (0); 831 } 832 833 int 834 info(const char *arg) 835 { 836 struct ioc_toc_header h; 837 int rc, i, n; 838 839 if ((rc = ioctl(fd, CDIOREADTOCHEADER, &h)) < 0) { 840 warn("ioctl(CDIOREADTOCHEADER)"); 841 return (rc); 842 } 843 844 n = h.ending_track - h.starting_track + 1; 845 rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry)); 846 if (rc < 0) 847 return (rc); 848 849 printf("track start duration block length type\n"); 850 printf("--------------------------------------------------\n"); 851 852 for (i = 0; i < n; i++) { 853 printf("%5d ", toc_buffer[i].track); 854 print_track(toc_buffer + i); 855 } 856 printf(" - "); /* Lead-out area */ 857 print_track(toc_buffer + n); 858 return (0); 859 } 860 861 void 862 lba2msf(u_long lba, u_int *m, u_int *s, u_int *f) 863 { 864 865 lba += 150; /* block start offset */ 866 lba &= 0xffffff; /* negative lbas use only 24 bits */ 867 *m = lba / (60 * 75); 868 lba %= (60 * 75); 869 *s = lba / 75; 870 *f = lba % 75; 871 } 872 873 u_int 874 msf2lba(u_int m, u_int s, u_int f) 875 { 876 877 return (((m * 60) + s) * 75 + f) - 150; 878 } 879 880 void 881 print_track(struct cd_toc_entry *e) 882 { 883 int block, next, len; 884 u_int m, s, f; 885 886 if (msf) { 887 /* Print track start */ 888 printf("%2d:%02d.%02d ", e->addr.msf.minute, 889 e->addr.msf.second, e->addr.msf.frame); 890 891 block = msf2lba(e->addr.msf.minute, e->addr.msf.second, 892 e->addr.msf.frame); 893 } else { 894 block = e->addr.lba; 895 lba2msf(block, &m, &s, &f); 896 /* Print track start */ 897 printf("%2d:%02d.%02d ", m, s, f); 898 } 899 if (e->track > CD_MAX_TRACK) { 900 /* lead-out area -- print block */ 901 printf(" - %6d - lead-out\n", block); 902 return; 903 } 904 if (msf) 905 next = msf2lba(e[1].addr.msf.minute, e[1].addr.msf.second, 906 e[1].addr.msf.frame); 907 else 908 next = e[1].addr.lba; 909 len = next - block; 910 lba2msf(len, &m, &s, &f); 911 912 /* Print duration, block, length, type */ 913 printf("%2d:%02d.%02d %6d %6d %8s\n", m, s, f, block, len, 914 (e->control & 4) ? "data" : "audio"); 915 } 916 917 int 918 play_track(int tstart, int istart, int tend, int iend) 919 { 920 struct ioc_play_track t; 921 int rv; 922 923 t.start_track = tstart; 924 t.start_index = istart; 925 t.end_track = tend; 926 t.end_index = iend; 927 928 if ((rv = ioctl(fd, CDIOCPLAYTRACKS, &t)) < 0) 929 warn("ioctl(CDIOCPLAYTRACKS)"); 930 return (rv); 931 } 932 933 int 934 play_blocks(int blk, int len) 935 { 936 struct ioc_play_blocks t; 937 int rv; 938 939 t.blk = blk; 940 t.len = len; 941 942 if ((rv = ioctl(fd, CDIOCPLAYBLOCKS, &t)) < 0) 943 warn("ioctl(CDIOCPLAYBLOCKS"); 944 return (rv); 945 } 946 947 int 948 setvol(int left, int right) 949 { 950 struct ioc_vol v; 951 int rv; 952 953 v.vol[0] = left; 954 v.vol[1] = right; 955 v.vol[2] = 0; 956 v.vol[3] = 0; 957 958 if ((rv = ioctl(fd, CDIOCSETVOL, &v)) < 0) 959 warn("ioctl(CDIOCSETVOL)"); 960 return (rv); 961 } 962 963 int 964 read_toc_entrys(int len) 965 { 966 struct ioc_read_toc_entry t; 967 int rv; 968 969 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 970 t.starting_track = 0; 971 t.data_len = len; 972 t.data = toc_buffer; 973 974 if ((rv = ioctl(fd, CDIOREADTOCENTRYS, &t)) < 0) 975 warn("ioctl(CDIOREADTOCENTRYS)"); 976 return (rv); 977 } 978 979 int 980 play_msf(int start_m, int start_s, int start_f, int end_m, int end_s, 981 int end_f) 982 { 983 struct ioc_play_msf a; 984 int rv; 985 986 a.start_m = start_m; 987 a.start_s = start_s; 988 a.start_f = start_f; 989 a.end_m = end_m; 990 a.end_s = end_s; 991 a.end_f = end_f; 992 993 if ((rv = ioctl(fd, CDIOCPLAYMSF, &a)) < 0) 994 warn("ioctl(CDIOREADTOCENTRYS)"); 995 return (rv); 996 } 997 998 int 999 get_status(int *trk, int *idx, int *min, int *sec, int *frame) 1000 { 1001 struct ioc_read_subchannel s; 1002 struct cd_sub_channel_info data; 1003 u_int mm, ss, ff; 1004 int rv; 1005 1006 bzero(&s, sizeof(s)); 1007 s.data = &data; 1008 s.data_len = sizeof(data); 1009 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 1010 s.data_format = CD_CURRENT_POSITION; 1011 1012 if ((rv = ioctl(fd, CDIOCREADSUBCHANNEL, &s)) < 0) { 1013 warn("ioctl(CDIOCREADSUBCHANNEL)"); 1014 return (rv); 1015 } 1016 1017 *trk = s.data->what.position.track_number; 1018 *idx = s.data->what.position.index_number; 1019 if (msf) { 1020 *min = s.data->what.position.reladdr.msf.minute; 1021 *sec = s.data->what.position.reladdr.msf.second; 1022 *frame = s.data->what.position.reladdr.msf.frame; 1023 } else { 1024 lba2msf(be32toh(s.data->what.position.reladdr.lba), &mm, 1025 &ss, &ff); 1026 *min = mm; 1027 *sec = ss; 1028 *frame = ff; 1029 } 1030 1031 return (s.data->header.audio_status); 1032 } 1033 1034 const char * 1035 prompt(void) 1036 { 1037 1038 return ("cdplay> "); 1039 } 1040 1041 const char * 1042 parse(char *buf, int *cmd) 1043 { 1044 const struct cmdtab *c, *mc; 1045 char *p, *q; 1046 int len; 1047 1048 for (p = buf; isspace((unsigned char)*p); p++) 1049 continue; 1050 1051 if (isdigit((unsigned char)*p) || (p[0] == '#' && isdigit((unsigned char)p[1]))) { 1052 *cmd = CMD_PLAY; 1053 return (p); 1054 } 1055 1056 for (buf = p; *p != '\0' && !isspace((unsigned char)*p); p++) 1057 continue; 1058 1059 if ((len = p - buf) == 0) 1060 return (0); 1061 1062 if (*p != '\0') { /* It must be a spacing character! */ 1063 *p++ = 0; 1064 for (q = p; *q != '\0' && *q != '\n' && *q != '\r'; q++) 1065 continue; 1066 *q = 0; 1067 } 1068 1069 *cmd = -1; 1070 1071 mc = cmdtab + sizeof(cmdtab) / sizeof(cmdtab[0]); 1072 for (c = cmdtab; c < mc; c++) { 1073 /* Is it an exact match? */ 1074 if (strcasecmp(buf, c->name) == 0) { 1075 *cmd = c->command; 1076 break; 1077 } 1078 /* Try short hand forms then... */ 1079 if (len >= c->min && strncasecmp(buf, c->name, len) == 0) { 1080 if (*cmd != -1 && *cmd != c->command) { 1081 warnx("ambiguous command"); 1082 return (0); 1083 } 1084 *cmd = c->command; 1085 } 1086 } 1087 1088 if (*cmd == -1) { 1089 warnx("invalid command, enter ``help'' for commands"); 1090 return (0); 1091 } 1092 1093 while (isspace((unsigned char)*p)) 1094 p++; 1095 return (p); 1096 } 1097 1098 int 1099 opencd(void) 1100 { 1101 char devbuf[80]; 1102 1103 if (fd > -1) 1104 return (1); 1105 1106 fd = opendisk(cdname, O_RDONLY, devbuf, sizeof(devbuf), 0); 1107 if (fd < 0) { 1108 if (errno == ENXIO) { 1109 /* 1110 * ENXIO has an overloaded meaning here. The 1111 * original "Device not configured" should be 1112 * interpreted as "No disc in drive %s". 1113 */ 1114 warnx("no disc in drive %s", devbuf); 1115 return (0); 1116 } 1117 err(EXIT_FAILURE, "%s", devbuf); 1118 } 1119 1120 return (1); 1121 } 1122 1123 void 1124 toc2msf(u_int i, u_int *m, u_int *s, u_int *f) 1125 { 1126 struct cd_toc_entry *ctep; 1127 1128 assert(i >= 0); 1129 assert(i <= CD_MAX_TRACK); 1130 1131 ctep = &toc_buffer[i]; 1132 1133 if (msf) { 1134 *m = ctep->addr.msf.minute; 1135 *s = ctep->addr.msf.second; 1136 *f = ctep->addr.msf.frame; 1137 } else { 1138 lba2msf(be32toh(ctep->addr.lba), m, s, f); 1139 } 1140 } 1141 1142 int 1143 toc2lba(u_int i) 1144 { 1145 struct cd_toc_entry *ctep; 1146 1147 assert(i > 0); 1148 assert(i <= CD_MAX_TRACK); 1149 1150 ctep = &toc_buffer[i-1]; 1151 1152 if (msf) { 1153 return msf2lba( 1154 ctep->addr.msf.minute, 1155 ctep->addr.msf.second, 1156 ctep->addr.msf.frame); 1157 } else { 1158 return be32toh(ctep->addr.lba); 1159 } 1160 } 1161 1162 void 1163 addmsf(u_int *m, u_int *s, u_int *f, u_int m2, u_int s2, u_int f2) 1164 { 1165 *f += f2; 1166 if (*f > 75) { 1167 *s += *f / 75; 1168 *f %= 75; 1169 } 1170 1171 *s += s2; 1172 if (*s > 60) { 1173 *m += *s / 60; 1174 *s %= 60; 1175 } 1176 1177 *m += m2; 1178 } 1179