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