chio.c revision 1.25 1 /* $NetBSD: chio.c,v 1.25 2004/06/25 14:27:57 wiz 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.25 2004/06/25 14:27:57 wiz 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 arguements 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 have_ucount = 0;
494 have_unit = 0;
495
496 /*
497 * On a status command, we expect the following:
498 *
499 * [<ET> [unit [count]]] [voltags]
500 *
501 * where ET == element type.
502 *
503 * If we get no element-related arguments, we get the status of all
504 * known element types.
505 */
506 if (argc > 4) {
507 warnx("%s: too many arguments", cname);
508 usage();
509 /*NOTREACHED*/
510 }
511
512 /*
513 * Get params from changer. Specifically, we need the element
514 * counts.
515 */
516 (void)memset(&data, 0, sizeof(data));
517 if (ioctl(changer_fd, CHIOGPARAMS, &data))
518 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name);
519 /* NOTREACHED */
520
521 schet = CHET_MT;
522 echet = CHET_DT;
523
524 for (; argc != 0; argc--, argv++) {
525 /*
526 * If we have the voltags modifier, it must be the
527 * last argument.
528 */
529 if (is_special(argv[0])) {
530 if (argc != 1) {
531 warnx("%s: malformed command line", cname);
532 usage();
533 /*NOTREACHED*/
534 }
535 if (parse_special(argv[0]) != SW_VOLTAGS)
536 errx(EXIT_FAILURE,
537 "%s: inappropriate special word: %s",
538 cname, argv[0]);
539 /* NOTREACHED */
540 flags |= CESR_VOLTAGS;
541 continue;
542 }
543
544 /*
545 * If we get an element type, we can't have specified
546 * anything else.
547 */
548 if (isdigit(*argv[0]) == 0) {
549 if (schet == echet || flags != 0 || have_unit ||
550 have_ucount) {
551 warnx("%s: malformed command line", cname);
552 usage();
553 /*NOTREACHED*/
554 }
555 schet = echet = parse_element_type(argv[0]);
556 continue;
557 }
558
559 /*
560 * We know we have a digit here. If we do, we must
561 * have specified an element type.
562 */
563 if (schet != echet) {
564 warnx("%s: malformed command line", cname);
565 usage();
566 /*NOTREACHED*/
567 }
568
569 i = parse_element_unit(argv[0]);
570
571 if (have_unit == 0) {
572 unit = i;
573 have_unit = 1;
574 } else if (have_ucount == 0) {
575 ucount = i;
576 have_ucount = 1;
577 } else {
578 warnx("%s: malformed command line", cname);
579 usage();
580 /*NOTREACHED*/
581 }
582 }
583
584 for (chet = schet; chet <= echet; ++chet) {
585 switch (chet) {
586 case CHET_MT:
587 count = data.cp_npickers;
588 break;
589 case CHET_ST:
590 count = data.cp_nslots;
591 break;
592 case CHET_IE:
593 count = data.cp_nportals;
594 break;
595 case CHET_DT:
596 count = data.cp_ndrives;
597 break;
598 default:
599 /* To appease gcc -Wuninitialized. */
600 count = 0;
601 }
602
603 if (count == 0) {
604 if (schet != echet)
605 continue;
606 else {
607 (void)printf("%s: no %s elements\n",
608 changer_name,
609 elements[chet].et_name);
610 return (0);
611 }
612 }
613
614 /*
615 * If we have a unit, we may or may not have a count.
616 * If we don't have a unit, we don't have a count, either.
617 *
618 * Make sure both are initialized.
619 */
620 if (have_unit) {
621 if (have_ucount == 0)
622 ucount = 1;
623 } else {
624 unit = 0;
625 ucount = count;
626 }
627
628 if ((unit + ucount) > count)
629 errx(EXIT_FAILURE, "%s: unvalid unit/count %d/%d",
630 cname, unit, ucount);
631 /* NOTREACHED */
632
633 size = ucount * sizeof(struct changer_element_status);
634
635 /* Allocate storage for the status bytes. */
636 if ((ces = malloc(size)) == NULL)
637 errx(EXIT_FAILURE, "can't allocate status storage");
638 /* NOTREACHED */
639
640 (void)memset(ces, 0, size);
641 (void)memset(&cmd, 0, sizeof(cmd));
642
643 cmd.cesr_type = chet;
644 cmd.cesr_unit = unit;
645 cmd.cesr_count = ucount;
646 cmd.cesr_flags = flags;
647 cmd.cesr_data = ces;
648
649 /*
650 * Should we deal with this eventually?
651 */
652 cmd.cesr_vendor_data = NULL;
653
654 if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) {
655 free(ces);
656 err(EXIT_FAILURE, "%s: CHIOGSTATUS", changer_name);
657 /* NOTREACHED */
658 }
659
660 /* Dump the status for each element of this type. */
661 for (i = 0; i < ucount; i++) {
662 (void)printf("%s %d: ", elements[chet].et_name,
663 unit + i);
664 if ((ces[i].ces_flags & CESTATUS_STATUS_VALID) == 0) {
665 (void)printf("status not available\n");
666 continue;
667 }
668 (void)printf("%s", bits_to_string(ces[i].ces_flags,
669 CESTATUS_BITS));
670 if (ces[i].ces_flags & CESTATUS_XNAME_VALID)
671 (void)printf(" (%s)", ces[i].ces_xname);
672 (void)printf("\n");
673 if (ces[i].ces_flags & CESTATUS_PVOL_VALID)
674 (void)printf("\tPrimary volume tag: %s "
675 "ver. %d\n",
676 ces[i].ces_pvoltag.cv_tag,
677 ces[i].ces_pvoltag.cv_serial);
678 if (ces[i].ces_flags & CESTATUS_AVOL_VALID)
679 (void)printf("\tAlternate volume tag: %s "
680 "ver. %d\n",
681 ces[i].ces_avoltag.cv_tag,
682 ces[i].ces_avoltag.cv_serial);
683 if (ces[i].ces_flags & CESTATUS_FROM_VALID)
684 (void)printf("\tFrom: %s %d\n",
685 elements[ces[i].ces_from_type].et_name,
686 ces[i].ces_from_unit);
687 if (ces[i].ces_vendor_len)
688 (void)printf("\tVendor-specific data size: "
689 "%lu\n", (u_long)ces[i].ces_vendor_len);
690 }
691 free(ces);
692 }
693
694 return (0);
695 }
696
697 /* ARGSUSED */
698 static int
699 do_ielem(const char *cname, int argc, char **argv)
700 {
701
702 if (ioctl(changer_fd, CHIOIELEM, NULL))
703 err(EXIT_FAILURE, "%s: CHIOIELEM", changer_name);
704 /* NOTREACHED */
705
706 return (0);
707 }
708
709 /* ARGSUSED */
710 static int
711 do_cdlu(const char *cname, int argc, char **argv)
712 {
713 static const struct special_word cdlu_subcmds[] = {
714 { "load", CD_LU_LOAD },
715 { "unload", CD_LU_UNLOAD },
716 { "abort", CD_LU_ABORT },
717 { NULL, 0 },
718 };
719 struct ioc_load_unload cmd;
720 int i;
721
722 /*
723 * This command is a little different, since we are mostly dealing
724 * with ATAPI CD changers, which have a lame API (since ATAPI doesn't
725 * have LUNs).
726 *
727 * We have 3 sub-commands: "load", "unload", and "abort". The
728 * first two take a slot number. The latter does not.
729 */
730
731 if (argc < 1 || argc > 2)
732 usage();
733 /*NOTREACHED*/
734
735 for (i = 0; cdlu_subcmds[i].sw_name != NULL; i++) {
736 if (strcmp(argv[0], cdlu_subcmds[i].sw_name) == 0) {
737 cmd.options = cdlu_subcmds[i].sw_value;
738 break;
739 }
740 }
741 if (cdlu_subcmds[i].sw_name == NULL)
742 usage();
743 /*NOTREACHED*/
744
745 if (strcmp(argv[0], "abort") == 0)
746 cmd.slot = 0;
747 else
748 cmd.slot = parse_element_unit(argv[1]);
749
750 /*
751 * XXX Should maybe do something different with the device
752 * XXX handling for cdlu; think about this some more.
753 */
754 if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd))
755 err(EXIT_FAILURE, "%s: CDIOCLOADUNLOAD", changer_name);
756 /* NOTREACHED */
757
758 return (0);
759 }
760
761 static int
762 parse_element_type(const 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(EXIT_FAILURE, "invalid element type `%s'", cp);
771 /* NOTREACHED */
772 }
773
774 static int
775 parse_element_unit(const char *cp)
776 {
777 char *p;
778 int i;
779
780 i = (int)strtol(cp, &p, 10);
781 if ((i < 0) || (*p != '\0'))
782 errx(EXIT_FAILURE, "invalid unit number `%s'", cp);
783
784 return (i);
785 }
786
787 static int
788 parse_special(const char *cp)
789 {
790 int val;
791
792 val = is_special(cp);
793 if (val)
794 return (val);
795
796 errx(EXIT_FAILURE, "invalid modifier `%s'", cp);
797 /* NOTREACHED */
798 }
799
800 static int
801 is_special(const char *cp)
802 {
803 int i;
804
805 for (i = 0; specials[i].sw_name != NULL; ++i)
806 if (strcmp(specials[i].sw_name, cp) == 0)
807 return (specials[i].sw_value);
808
809 return (0);
810 }
811
812 static const char *
813 bits_to_string(int v, const char *cp)
814 {
815 static char buf[128];
816 const char *np;
817 char *bp, f;
818 int first;
819
820 bp = buf;
821 *bp++ = '<';
822 for (first = 1; (f = *cp++) != 0; cp = np) {
823 for (np = cp; *np >= ' ';)
824 np++;
825 if ((v & (1 << (f - 1))) == 0)
826 continue;
827 if (first)
828 first = 0;
829 else
830 *bp++ = ',';
831 (void)memcpy(bp, cp, np - cp);
832 bp += np - cp;
833 }
834 *bp++ = '>';
835 *bp = '\0';
836
837 return (buf);
838 }
839
840 static void
841 cleanup(void)
842 {
843
844 /* Simple enough... */
845 (void)close(changer_fd);
846 }
847
848 static void
849 usage(void)
850 {
851 int i;
852
853 (void)fprintf(stderr,
854 "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n",
855 getprogname());
856
857 (void)fprintf(stderr, "Where command (and args) are:\n");
858 for (i = 0; commands[i].cc_name != NULL; i++)
859 (void)fprintf(stderr, "\t%s%s\n", commands[i].cc_name,
860 commands[i].cc_args);
861 exit(1);
862 /* NOTREACHED */
863 }
864