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