1 /* $NetBSD: chio.c,v 1.31 2011/08/29 14:51:17 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1998, 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. 35 */ 36 37 #include <sys/cdefs.h> 38 #ifndef lint 39 __COPYRIGHT( 40 "@(#) Copyright (c) 1996, 1998, 1999\ 41 The NetBSD Foundation, Inc. All rights reserved."); 42 __RCSID("$NetBSD: chio.c,v 1.31 2011/08/29 14:51:17 joerg Exp $"); 43 #endif 44 45 #include <sys/param.h> 46 #include <sys/ioctl.h> 47 #include <sys/chio.h> 48 #include <sys/cdio.h> /* for ATAPI CD changer; too bad it uses a lame API */ 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <limits.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "defs.h" 61 #include "pathnames.h" 62 63 int main(int, char *[]); 64 __dead static void usage(void); 65 static void cleanup(void); 66 static int parse_element_type(const char *); 67 static int parse_element_unit(const char *); 68 static int parse_special(const char *); 69 static int is_special(const char *); 70 static const char *bits_to_string(int, const char *); 71 72 static int do_move(const char *, int, char **); 73 static int do_exchange(const char *, int, char **); 74 static int do_position(const char *, int, char **); 75 static int do_params(const char *, int, char **); 76 static int do_getpicker(const char *, int, char **); 77 static int do_setpicker(const char *, int, char **); 78 static int do_status(const char *, int, char **); 79 static int do_ielem(const char *, int, char **); 80 static int do_cdlu(const char *, int, char **); 81 82 /* Valid changer element types. */ 83 const struct element_type elements[] = { 84 { "picker", CHET_MT }, 85 { "slot", CHET_ST }, 86 { "portal", CHET_IE }, 87 { "drive", CHET_DT }, 88 { NULL, 0 }, 89 }; 90 91 /* Valid commands. */ 92 const struct changer_command commands[] = { 93 { "move", " <from ET> <from EU> <to ET> <to EU> [inv]", 94 do_move }, 95 96 { "exchange", " <src ET> <src EU> <dst1 ET> <dst1 EU>\n" 97 "\t\t [<dst2 ET> <dst2 EU>] [inv1] [inv2]", 98 do_exchange }, 99 100 { "position", " <to ET> <to EU> [inv]", do_position }, 101 102 { "params", "", 103 do_params }, 104 105 { "getpicker", "", 106 do_getpicker }, 107 108 { "setpicker", " <picker>", 109 do_setpicker }, 110 111 { "status", " [<ET> [unit [count]]] [voltags]", 112 do_status }, 113 114 { "ielem", "", 115 do_ielem }, 116 117 { "cdlu", " load|unload <slot>\n" 118 "\t abort", 119 do_cdlu }, 120 121 { NULL, NULL, 122 NULL }, 123 }; 124 125 /* Valid special words. */ 126 const struct special_word specials[] = { 127 { "inv", SW_INVERT }, 128 { "inv1", SW_INVERT1 }, 129 { "inv2", SW_INVERT2 }, 130 { "voltags", SW_VOLTAGS }, 131 { NULL, 0 }, 132 }; 133 134 static const char *changer_name; 135 static int changer_fd; 136 137 int 138 main(int argc, char *argv[]) 139 { 140 int ch, i; 141 142 setprogname(argv[0]); 143 while ((ch = getopt(argc, argv, "f:")) != -1) { 144 switch (ch) { 145 case 'f': 146 changer_name = optarg; 147 break; 148 default: 149 usage(); 150 /* NOTREACHED */ 151 } 152 } 153 argc -= optind; 154 argv += optind; 155 156 if (argc == 0) 157 usage(); 158 /* NOTREACHED */ 159 160 /* Get the default changer if not already specified. */ 161 if (changer_name == NULL) 162 if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) 163 changer_name = _PATH_CH; 164 165 /* Open the changer device. */ 166 if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) 167 err(EXIT_FAILURE, "%s: open", changer_name); 168 /* NOTREACHED */ 169 170 /* Register cleanup function. */ 171 if (atexit(cleanup)) 172 err(EXIT_FAILURE, "can't register cleanup function"); 173 /* NOTREACHED */ 174 175 /* Find the specified command. */ 176 for (i = 0; commands[i].cc_name != NULL; ++i) 177 if (strcmp(*argv, commands[i].cc_name) == 0) 178 break; 179 if (commands[i].cc_name == NULL) 180 errx(EXIT_FAILURE, "unknown command: %s", *argv); 181 /* NOTREACHED */ 182 183 /* Skip over the command name and call handler. */ 184 ++argv; --argc; 185 exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); 186 /* NOTREACHED */ 187 } 188 189 static int 190 do_move(const char *cname, int argc, char **argv) 191 { 192 struct changer_move_request cmd; 193 int val; 194 195 /* 196 * On a move command, we expect the following: 197 * 198 * <from ET> <from EU> <to ET> <to EU> [inv] 199 * 200 * where ET == element type and EU == element unit. 201 */ 202 if (argc < 4) { 203 warnx("%s: too few arguments", cname); 204 usage(); 205 /*NOTREACHED*/ 206 } else if (argc > 5) { 207 warnx("%s: too many arguments", cname); 208 usage(); 209 /*NOTREACHED*/ 210 } 211 (void)memset(&cmd, 0, sizeof(cmd)); 212 213 /* <from ET> */ 214 cmd.cm_fromtype = parse_element_type(*argv); 215 ++argv; --argc; 216 217 /* <from EU> */ 218 cmd.cm_fromunit = parse_element_unit(*argv); 219 ++argv; --argc; 220 221 /* <to ET> */ 222 cmd.cm_totype = parse_element_type(*argv); 223 ++argv; --argc; 224 225 /* <to EU> */ 226 cmd.cm_tounit = parse_element_unit(*argv); 227 ++argv; --argc; 228 229 /* Deal with optional command modifier. */ 230 if (argc) { 231 val = parse_special(*argv); 232 switch (val) { 233 case SW_INVERT: 234 cmd.cm_flags |= CM_INVERT; 235 break; 236 default: 237 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 238 cname, *argv); 239 /* NOTREACHED */ 240 } 241 } 242 243 /* Send command to changer. */ 244 if (ioctl(changer_fd, CHIOMOVE, &cmd)) 245 err(EXIT_FAILURE, "%s: CHIOMOVE", changer_name); 246 /* NOTREACHED */ 247 248 return (0); 249 } 250 251 static int 252 do_exchange(const char *cname, int argc, char **argv) 253 { 254 struct changer_exchange_request cmd; 255 int val; 256 257 /* 258 * On an exchange command, we expect the following: 259 * 260 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] 261 * 262 * where ET == element type and EU == element unit. 263 */ 264 if (argc < 4) { 265 warnx("%s: too few arguments", cname); 266 usage(); 267 /*NOTREACHED*/ 268 } else if (argc > 8) { 269 warnx("%s: too many arguments", cname); 270 usage(); 271 /*NOTREACHED*/ 272 } 273 (void)memset(&cmd, 0, sizeof(cmd)); 274 275 /* <src ET> */ 276 cmd.ce_srctype = parse_element_type(*argv); 277 ++argv; --argc; 278 279 /* <src EU> */ 280 cmd.ce_srcunit = parse_element_unit(*argv); 281 ++argv; --argc; 282 283 /* <dst1 ET> */ 284 cmd.ce_fdsttype = parse_element_type(*argv); 285 ++argv; --argc; 286 287 /* <dst1 EU> */ 288 cmd.ce_fdstunit = parse_element_unit(*argv); 289 ++argv; --argc; 290 291 /* 292 * If the next token is a special word or there are no more 293 * arguments, then this is a case of simple exchange. 294 * dst2 == src. 295 */ 296 if ((argc == 0) || is_special(*argv)) { 297 cmd.ce_sdsttype = cmd.ce_srctype; 298 cmd.ce_sdstunit = cmd.ce_srcunit; 299 goto do_special; 300 } 301 302 /* <dst2 ET> */ 303 cmd.ce_sdsttype = parse_element_type(*argv); 304 ++argv; --argc; 305 306 /* <dst2 EU> */ 307 cmd.ce_sdstunit = parse_element_unit(*argv); 308 ++argv; --argc; 309 310 do_special: 311 /* Deal with optional command modifiers. */ 312 while (argc) { 313 val = parse_special(*argv); 314 ++argv; --argc; 315 switch (val) { 316 case SW_INVERT1: 317 cmd.ce_flags |= CE_INVERT1; 318 break; 319 case SW_INVERT2: 320 cmd.ce_flags |= CE_INVERT2; 321 break; 322 default: 323 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 324 cname, *argv); 325 /* NOTREACHED */ 326 } 327 } 328 329 /* Send command to changer. */ 330 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) 331 err(EXIT_FAILURE, "%s: CHIOEXCHANGE", changer_name); 332 /* NOTREACHED */ 333 334 return (0); 335 } 336 337 static int 338 do_position(const char *cname, int argc, char **argv) 339 { 340 struct changer_position_request cmd; 341 int val; 342 343 /* 344 * On a position command, we expect the following: 345 * 346 * <to ET> <to EU> [inv] 347 * 348 * where ET == element type and EU == element unit. 349 */ 350 if (argc < 2) { 351 warnx("%s: too few arguments", cname); 352 usage(); 353 /*NOTREACHED*/ 354 } else if (argc > 3) { 355 warnx("%s: too many arguments", cname); 356 usage(); 357 /*NOTREACHED*/ 358 } 359 (void)memset(&cmd, 0, sizeof(cmd)); 360 361 /* <to ET> */ 362 cmd.cp_type = parse_element_type(*argv); 363 ++argv; --argc; 364 365 /* <to EU> */ 366 cmd.cp_unit = parse_element_unit(*argv); 367 ++argv; --argc; 368 369 /* Deal with optional command modifier. */ 370 if (argc) { 371 val = parse_special(*argv); 372 switch (val) { 373 case SW_INVERT: 374 cmd.cp_flags |= CP_INVERT; 375 break; 376 default: 377 errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'", 378 cname, *argv); 379 /* NOTREACHED */ 380 } 381 } 382 383 /* Send command to changer. */ 384 if (ioctl(changer_fd, CHIOPOSITION, &cmd)) 385 err(EXIT_FAILURE, "%s: CHIOPOSITION", changer_name); 386 /* NOTREACHED */ 387 388 return (0); 389 } 390 391 /* ARGSUSED */ 392 static int 393 do_params(const char *cname, int argc, char **argv) 394 { 395 struct changer_params data; 396 397 /* No arguments to this command. */ 398 if (argc) { 399 warnx("%s: no arguments expected", cname); 400 usage(); 401 /* NOTREACHED */ 402 } 403 404 /* Get params from changer and display them. */ 405 (void)memset(&data, 0, sizeof(data)); 406 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 407 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name); 408 /* NOTREACHED */ 409 410 #define PLURAL(n) (n) > 1 ? "s" : "" 411 412 (void)printf("%s: %d slot%s, %d drive%s, %d picker%s", 413 changer_name, 414 data.cp_nslots, PLURAL(data.cp_nslots), 415 data.cp_ndrives, PLURAL(data.cp_ndrives), 416 data.cp_npickers, PLURAL(data.cp_npickers)); 417 if (data.cp_nportals) 418 (void)printf(", %d portal%s", data.cp_nportals, 419 PLURAL(data.cp_nportals)); 420 421 #undef PLURAL 422 423 (void)printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); 424 425 return (0); 426 } 427 428 /* ARGSUSED */ 429 static int 430 do_getpicker(const char *cname, int argc, char **argv) 431 { 432 int picker; 433 434 /* No arguments to this command. */ 435 if (argc) { 436 warnx("%s: no arguments expected", cname); 437 usage(); 438 /*NOTREACHED*/ 439 } 440 441 /* Get current picker from changer and display it. */ 442 if (ioctl(changer_fd, CHIOGPICKER, &picker)) 443 err(EXIT_FAILURE, "%s: CHIOGPICKER", changer_name); 444 /* NOTREACHED */ 445 446 (void)printf("%s: current picker: %d\n", changer_name, picker); 447 448 return (0); 449 } 450 451 static int 452 do_setpicker(const char *cname, int argc, char **argv) 453 { 454 int picker; 455 456 if (argc < 1) { 457 warnx("%s: too few arguments", cname); 458 usage(); 459 /*NOTREACHED*/ 460 } else if (argc > 1) { 461 warnx("%s: too many arguments", cname); 462 usage(); 463 /*NOTREACHED*/ 464 } 465 466 picker = parse_element_unit(*argv); 467 468 /* Set the changer picker. */ 469 if (ioctl(changer_fd, CHIOSPICKER, &picker)) 470 err(EXIT_FAILURE, "%s: CHIOSPICKER", changer_name); 471 472 return (0); 473 } 474 475 static int 476 do_status(const char *cname, int argc, char **argv) 477 { 478 struct changer_element_status_request cmd; 479 struct changer_params data; 480 struct changer_element_status *ces; 481 int i, chet, count, echet, flags, have_ucount, have_unit; 482 int schet, ucount, unit; 483 size_t size; 484 485 flags = 0; 486 ucount = 0; 487 unit = 0; 488 have_ucount = 0; 489 have_unit = 0; 490 491 /* 492 * On a status command, we expect the following: 493 * 494 * [<ET> [unit [count]]] [voltags] 495 * 496 * where ET == element type. 497 * 498 * If we get no element-related arguments, we get the status of all 499 * known element types. 500 */ 501 if (argc > 4) { 502 warnx("%s: too many arguments", cname); 503 usage(); 504 /*NOTREACHED*/ 505 } 506 507 /* 508 * Get params from changer. Specifically, we need the element 509 * counts. 510 */ 511 (void)memset(&data, 0, sizeof(data)); 512 if (ioctl(changer_fd, CHIOGPARAMS, &data)) 513 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name); 514 /* NOTREACHED */ 515 516 schet = CHET_MT; 517 echet = CHET_DT; 518 519 for (; argc != 0; argc--, argv++) { 520 /* 521 * If we have the voltags modifier, it must be the 522 * last argument. 523 */ 524 if (is_special(argv[0])) { 525 if (argc != 1) { 526 warnx("%s: malformed command line", cname); 527 usage(); 528 /*NOTREACHED*/ 529 } 530 if (parse_special(argv[0]) != SW_VOLTAGS) 531 errx(EXIT_FAILURE, 532 "%s: inappropriate special word: %s", 533 cname, argv[0]); 534 /* NOTREACHED */ 535 flags |= CESR_VOLTAGS; 536 continue; 537 } 538 539 /* 540 * If we get an element type, we can't have specified 541 * anything else. 542 */ 543 if (isdigit((unsigned char)*argv[0]) == 0) { 544 if (schet == echet || flags != 0 || have_unit || 545 have_ucount) { 546 warnx("%s: malformed command line", cname); 547 usage(); 548 /*NOTREACHED*/ 549 } 550 schet = echet = parse_element_type(argv[0]); 551 continue; 552 } 553 554 /* 555 * We know we have a digit here. If we do, we must 556 * have specified an element type. 557 */ 558 if (schet != echet) { 559 warnx("%s: malformed command line", cname); 560 usage(); 561 /*NOTREACHED*/ 562 } 563 564 i = parse_element_unit(argv[0]); 565 566 if (have_unit == 0) { 567 unit = i; 568 have_unit = 1; 569 } else if (have_ucount == 0) { 570 ucount = i; 571 have_ucount = 1; 572 } else { 573 warnx("%s: malformed command line", cname); 574 usage(); 575 /*NOTREACHED*/ 576 } 577 } 578 579 for (chet = schet; chet <= echet; ++chet) { 580 switch (chet) { 581 case CHET_MT: 582 count = data.cp_npickers; 583 break; 584 case CHET_ST: 585 count = data.cp_nslots; 586 break; 587 case CHET_IE: 588 count = data.cp_nportals; 589 break; 590 case CHET_DT: 591 count = data.cp_ndrives; 592 break; 593 default: 594 /* To appease gcc -Wuninitialized. */ 595 count = 0; 596 } 597 598 if (count == 0) { 599 if (schet != echet) 600 continue; 601 else { 602 (void)printf("%s: no %s elements\n", 603 changer_name, 604 elements[chet].et_name); 605 return (0); 606 } 607 } 608 609 /* 610 * If we have a unit, we may or may not have a count. 611 * If we don't have a unit, we don't have a count, either. 612 * 613 * Make sure both are initialized. 614 */ 615 if (have_unit) { 616 if (have_ucount == 0) 617 ucount = 1; 618 } else { 619 unit = 0; 620 ucount = count; 621 } 622 623 if ((unit + ucount) > count) 624 errx(EXIT_FAILURE, "%s: unvalid unit/count %d/%d", 625 cname, unit, ucount); 626 /* NOTREACHED */ 627 628 size = ucount * sizeof(struct changer_element_status); 629 630 /* Allocate storage for the status bytes. */ 631 if ((ces = malloc(size)) == NULL) 632 errx(EXIT_FAILURE, "can't allocate status storage"); 633 /* NOTREACHED */ 634 635 (void)memset(ces, 0, size); 636 (void)memset(&cmd, 0, sizeof(cmd)); 637 638 cmd.cesr_type = chet; 639 cmd.cesr_unit = unit; 640 cmd.cesr_count = ucount; 641 cmd.cesr_flags = flags; 642 cmd.cesr_data = ces; 643 644 /* 645 * Should we deal with this eventually? 646 */ 647 cmd.cesr_vendor_data = NULL; 648 649 if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) { 650 free(ces); 651 err(EXIT_FAILURE, "%s: CHIOGSTATUS", changer_name); 652 /* NOTREACHED */ 653 } 654 655 /* Dump the status for each element of this type. */ 656 for (i = 0; i < ucount; i++) { 657 (void)printf("%s %d: ", elements[chet].et_name, 658 unit + i); 659 if ((ces[i].ces_flags & CESTATUS_STATUS_VALID) == 0) { 660 (void)printf("status not available\n"); 661 continue; 662 } 663 (void)printf("%s", bits_to_string(ces[i].ces_flags, 664 CESTATUS_BITS)); 665 if (ces[i].ces_flags & CESTATUS_XNAME_VALID) 666 (void)printf(" (%s)", ces[i].ces_xname); 667 (void)printf("\n"); 668 if (ces[i].ces_flags & CESTATUS_PVOL_VALID) 669 (void)printf("\tPrimary volume tag: %s " 670 "ver. %d\n", 671 ces[i].ces_pvoltag.cv_tag, 672 ces[i].ces_pvoltag.cv_serial); 673 if (ces[i].ces_flags & CESTATUS_AVOL_VALID) 674 (void)printf("\tAlternate volume tag: %s " 675 "ver. %d\n", 676 ces[i].ces_avoltag.cv_tag, 677 ces[i].ces_avoltag.cv_serial); 678 if (ces[i].ces_flags & CESTATUS_FROM_VALID) 679 (void)printf("\tFrom: %s %d\n", 680 elements[ces[i].ces_from_type].et_name, 681 ces[i].ces_from_unit); 682 if (ces[i].ces_vendor_len) 683 (void)printf("\tVendor-specific data size: " 684 "%lu\n", (u_long)ces[i].ces_vendor_len); 685 } 686 free(ces); 687 } 688 689 return (0); 690 } 691 692 /* ARGSUSED */ 693 static int 694 do_ielem(const char *cname, int argc, char **argv) 695 { 696 697 if (ioctl(changer_fd, CHIOIELEM, NULL)) 698 err(EXIT_FAILURE, "%s: CHIOIELEM", changer_name); 699 /* NOTREACHED */ 700 701 return (0); 702 } 703 704 /* ARGSUSED */ 705 static int 706 do_cdlu(const char *cname, int argc, char **argv) 707 { 708 static const struct special_word cdlu_subcmds[] = { 709 { "load", CD_LU_LOAD }, 710 { "unload", CD_LU_UNLOAD }, 711 { "abort", CD_LU_ABORT }, 712 { NULL, 0 }, 713 }; 714 struct ioc_load_unload cmd; 715 int i; 716 717 /* 718 * This command is a little different, since we are mostly dealing 719 * with ATAPI CD changers, which have a lame API (since ATAPI doesn't 720 * have LUNs). 721 * 722 * We have 3 sub-commands: "load", "unload", and "abort". The 723 * first two take a slot number. The latter does not. 724 */ 725 726 if (argc < 1 || argc > 2) 727 usage(); 728 /*NOTREACHED*/ 729 730 for (i = 0; cdlu_subcmds[i].sw_name != NULL; i++) { 731 if (strcmp(argv[0], cdlu_subcmds[i].sw_name) == 0) { 732 cmd.options = cdlu_subcmds[i].sw_value; 733 break; 734 } 735 } 736 if (cdlu_subcmds[i].sw_name == NULL) 737 usage(); 738 /*NOTREACHED*/ 739 740 if (strcmp(argv[0], "abort") == 0) 741 cmd.slot = 0; 742 else 743 cmd.slot = parse_element_unit(argv[1]); 744 745 /* 746 * XXX Should maybe do something different with the device 747 * XXX handling for cdlu; think about this some more. 748 */ 749 if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd)) 750 err(EXIT_FAILURE, "%s: CDIOCLOADUNLOAD", changer_name); 751 /* NOTREACHED */ 752 753 return (0); 754 } 755 756 static int 757 parse_element_type(const char *cp) 758 { 759 int i; 760 761 for (i = 0; elements[i].et_name != NULL; ++i) 762 if (strcmp(elements[i].et_name, cp) == 0) 763 return (elements[i].et_type); 764 765 errx(EXIT_FAILURE, "invalid element type `%s'", cp); 766 /* NOTREACHED */ 767 } 768 769 static int 770 parse_element_unit(const char *cp) 771 { 772 char *p; 773 int i; 774 775 i = (int)strtol(cp, &p, 10); 776 if ((i < 0) || (*p != '\0')) 777 errx(EXIT_FAILURE, "invalid unit number `%s'", cp); 778 779 return (i); 780 } 781 782 static int 783 parse_special(const char *cp) 784 { 785 int val; 786 787 val = is_special(cp); 788 if (val) 789 return (val); 790 791 errx(EXIT_FAILURE, "invalid modifier `%s'", cp); 792 /* NOTREACHED */ 793 } 794 795 static int 796 is_special(const char *cp) 797 { 798 int i; 799 800 for (i = 0; specials[i].sw_name != NULL; ++i) 801 if (strcmp(specials[i].sw_name, cp) == 0) 802 return (specials[i].sw_value); 803 804 return (0); 805 } 806 807 static const char * 808 bits_to_string(int v, const char *cp) 809 { 810 static char buf[128]; 811 const char *np; 812 char *bp, f; 813 int first; 814 815 bp = buf; 816 *bp++ = '<'; 817 for (first = 1; (f = *cp++) != 0; cp = np) { 818 for (np = cp; *np >= ' ';) 819 np++; 820 if ((v & (1 << (f - 1))) == 0) 821 continue; 822 if (first) 823 first = 0; 824 else 825 *bp++ = ','; 826 (void)memcpy(bp, cp, np - cp); 827 bp += np - cp; 828 } 829 *bp++ = '>'; 830 *bp = '\0'; 831 832 return (buf); 833 } 834 835 static void 836 cleanup(void) 837 { 838 839 /* Simple enough... */ 840 (void)close(changer_fd); 841 } 842 843 static void 844 usage(void) 845 { 846 int i; 847 848 (void)fprintf(stderr, 849 "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n", 850 getprogname()); 851 852 (void)fprintf(stderr, "Where command (and args) are:\n"); 853 for (i = 0; commands[i].cc_name != NULL; i++) 854 (void)fprintf(stderr, "\t%s%s\n", commands[i].cc_name, 855 commands[i].cc_args); 856 exit(1); 857 /* NOTREACHED */ 858 } 859