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