chio.c revision 1.30 1 /* $NetBSD: chio.c,v 1.30 2008/07/20 00:52:39 lukem 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.30 2008/07/20 00:52:39 lukem 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 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