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