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