Sync usage with man page.
[netbsd-mini2440.git] / bin / chio / chio.c
blob379af371642e3ebf6210eb4b55198a7b0c4e7d66
1 /* $NetBSD: chio.c,v 1.29 2008/04/28 20:22:51 martin Exp $ */
3 /*-
4 * Copyright (c) 1996, 1998, 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
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.
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.
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.
34 * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr.
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.29 2008/04/28 20:22:51 martin Exp $");
43 #endif
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 */
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>
60 #include "defs.h"
61 #include "pathnames.h"
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 *);
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 **);
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 },
91 /* Valid commands. */
92 const struct changer_command commands[] = {
93 { "move", " <from ET> <from EU> <to ET> <to EU> [inv]",
94 do_move },
96 { "exchange", " <src ET> <src EU> <dst1 ET> <dst1 EU>\n"
97 "\t\t [<dst2 ET> <dst2 EU>] [inv1] [inv2]",
98 do_exchange },
100 { "position", " <to ET> <to EU> [inv]", do_position },
102 { "params", "",
103 do_params },
105 { "getpicker", "",
106 do_getpicker },
108 { "setpicker", " <picker>",
109 do_setpicker },
111 { "status", " [<ET> [unit [count]]] [voltags]",
112 do_status },
114 { "ielem", "",
115 do_ielem },
117 { "cdlu", " load|unload <slot>\n"
118 "\t abort",
119 do_cdlu },
121 { NULL, NULL,
122 NULL },
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 },
134 static const char *changer_name;
135 static int changer_fd;
138 main(int argc, char *argv[])
140 int ch, i;
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 */
153 argc -= optind;
154 argv += optind;
156 if (argc == 0)
157 usage();
158 /* NOTREACHED */
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;
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 */
170 /* Register cleanup function. */
171 if (atexit(cleanup))
172 err(EXIT_FAILURE, "can't register cleanup function");
173 /* NOTREACHED */
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 */
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 */
189 static int
190 do_move(const char *cname, int argc, char **argv)
192 struct changer_move_request cmd;
193 int val;
196 * On a move command, we expect the following:
198 * <from ET> <from EU> <to ET> <to EU> [inv]
200 * where ET == element type and EU == element unit.
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*/
211 (void)memset(&cmd, 0, sizeof(cmd));
213 /* <from ET> */
214 cmd.cm_fromtype = parse_element_type(*argv);
215 ++argv; --argc;
217 /* <from EU> */
218 cmd.cm_fromunit = parse_element_unit(*argv);
219 ++argv; --argc;
221 /* <to ET> */
222 cmd.cm_totype = parse_element_type(*argv);
223 ++argv; --argc;
225 /* <to EU> */
226 cmd.cm_tounit = parse_element_unit(*argv);
227 ++argv; --argc;
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 */
243 /* Send command to changer. */
244 if (ioctl(changer_fd, CHIOMOVE, &cmd))
245 err(EXIT_FAILURE, "%s: CHIOMOVE", changer_name);
246 /* NOTREACHED */
248 return (0);
251 static int
252 do_exchange(const char *cname, int argc, char **argv)
254 struct changer_exchange_request cmd;
255 int val;
258 * On an exchange command, we expect the following:
260 * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2]
262 * where ET == element type and EU == element unit.
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*/
273 (void)memset(&cmd, 0, sizeof(cmd));
275 /* <src ET> */
276 cmd.ce_srctype = parse_element_type(*argv);
277 ++argv; --argc;
279 /* <src EU> */
280 cmd.ce_srcunit = parse_element_unit(*argv);
281 ++argv; --argc;
283 /* <dst1 ET> */
284 cmd.ce_fdsttype = parse_element_type(*argv);
285 ++argv; --argc;
287 /* <dst1 EU> */
288 cmd.ce_fdstunit = parse_element_unit(*argv);
289 ++argv; --argc;
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.
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;
302 /* <dst2 ET> */
303 cmd.ce_sdsttype = parse_element_type(*argv);
304 ++argv; --argc;
306 /* <dst2 EU> */
307 cmd.ce_sdstunit = parse_element_unit(*argv);
308 ++argv; --argc;
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 */
329 /* Send command to changer. */
330 if (ioctl(changer_fd, CHIOEXCHANGE, &cmd))
331 err(EXIT_FAILURE, "%s: CHIOEXCHANGE", changer_name);
332 /* NOTREACHED */
334 return (0);
337 static int
338 do_position(const char *cname, int argc, char **argv)
340 struct changer_position_request cmd;
341 int val;
344 * On a position command, we expect the following:
346 * <to ET> <to EU> [inv]
348 * where ET == element type and EU == element unit.
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*/
359 (void)memset(&cmd, 0, sizeof(cmd));
361 /* <to ET> */
362 cmd.cp_type = parse_element_type(*argv);
363 ++argv; --argc;
365 /* <to EU> */
366 cmd.cp_unit = parse_element_unit(*argv);
367 ++argv; --argc;
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 */
383 /* Send command to changer. */
384 if (ioctl(changer_fd, CHIOPOSITION, &cmd))
385 err(EXIT_FAILURE, "%s: CHIOPOSITION", changer_name);
386 /* NOTREACHED */
388 return (0);
391 /* ARGSUSED */
392 static int
393 do_params(const char *cname, int argc, char **argv)
395 struct changer_params data;
397 /* No arguments to this command. */
398 if (argc) {
399 warnx("%s: no arguments expected", cname);
400 usage();
401 /* NOTREACHED */
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 */
410 #define PLURAL(n) (n) > 1 ? "s" : ""
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));
421 #undef PLURAL
423 (void)printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker);
425 return (0);
428 /* ARGSUSED */
429 static int
430 do_getpicker(const char *cname, int argc, char **argv)
432 int picker;
434 /* No arguments to this command. */
435 if (argc) {
436 warnx("%s: no arguments expected", cname);
437 usage();
438 /*NOTREACHED*/
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 */
446 (void)printf("%s: current picker: %d\n", changer_name, picker);
448 return (0);
451 static int
452 do_setpicker(const char *cname, int argc, char **argv)
454 int picker;
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*/
466 picker = parse_element_unit(*argv);
468 /* Set the changer picker. */
469 if (ioctl(changer_fd, CHIOSPICKER, &picker))
470 err(EXIT_FAILURE, "%s: CHIOSPICKER", changer_name);
472 return (0);
475 static int
476 do_status(const char *cname, int argc, char **argv)
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;
485 flags = 0;
486 ucount = 0;
487 unit = 0;
488 have_ucount = 0;
489 have_unit = 0;
492 * On a status command, we expect the following:
494 * [<ET> [unit [count]]] [voltags]
496 * where ET == element type.
498 * If we get no element-related arguments, we get the status of all
499 * known element types.
501 if (argc > 4) {
502 warnx("%s: too many arguments", cname);
503 usage();
504 /*NOTREACHED*/
508 * Get params from changer. Specifically, we need the element
509 * counts.
511 (void)memset(&data, 0, sizeof(data));
512 if (ioctl(changer_fd, CHIOGPARAMS, &data))
513 err(EXIT_FAILURE, "%s: CHIOGPARAMS", changer_name);
514 /* NOTREACHED */
516 schet = CHET_MT;
517 echet = CHET_DT;
519 for (; argc != 0; argc--, argv++) {
521 * If we have the voltags modifier, it must be the
522 * last argument.
524 if (is_special(argv[0])) {
525 if (argc != 1) {
526 warnx("%s: malformed command line", cname);
527 usage();
528 /*NOTREACHED*/
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;
540 * If we get an element type, we can't have specified
541 * anything else.
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*/
550 schet = echet = parse_element_type(argv[0]);
551 continue;
555 * We know we have a digit here. If we do, we must
556 * have specified an element type.
558 if (schet != echet) {
559 warnx("%s: malformed command line", cname);
560 usage();
561 /*NOTREACHED*/
564 i = parse_element_unit(argv[0]);
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*/
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;
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);
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.
613 * Make sure both are initialized.
615 if (have_unit) {
616 if (have_ucount == 0)
617 ucount = 1;
618 } else {
619 unit = 0;
620 ucount = count;
623 if ((unit + ucount) > count)
624 errx(EXIT_FAILURE, "%s: unvalid unit/count %d/%d",
625 cname, unit, ucount);
626 /* NOTREACHED */
628 size = ucount * sizeof(struct changer_element_status);
630 /* Allocate storage for the status bytes. */
631 if ((ces = malloc(size)) == NULL)
632 errx(EXIT_FAILURE, "can't allocate status storage");
633 /* NOTREACHED */
635 (void)memset(ces, 0, size);
636 (void)memset(&cmd, 0, sizeof(cmd));
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;
645 * Should we deal with this eventually?
647 cmd.cesr_vendor_data = NULL;
649 if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) {
650 free(ces);
651 err(EXIT_FAILURE, "%s: CHIOGSTATUS", changer_name);
652 /* NOTREACHED */
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;
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);
686 free(ces);
689 return (0);
692 /* ARGSUSED */
693 static int
694 do_ielem(const char *cname, int argc, char **argv)
697 if (ioctl(changer_fd, CHIOIELEM, NULL))
698 err(EXIT_FAILURE, "%s: CHIOIELEM", changer_name);
699 /* NOTREACHED */
701 return (0);
704 /* ARGSUSED */
705 static int
706 do_cdlu(const char *cname, int argc, char **argv)
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 },
714 struct ioc_load_unload cmd;
715 int i;
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).
722 * We have 3 sub-commands: "load", "unload", and "abort". The
723 * first two take a slot number. The latter does not.
726 if (argc < 1 || argc > 2)
727 usage();
728 /*NOTREACHED*/
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;
736 if (cdlu_subcmds[i].sw_name == NULL)
737 usage();
738 /*NOTREACHED*/
740 if (strcmp(argv[0], "abort") == 0)
741 cmd.slot = 0;
742 else
743 cmd.slot = parse_element_unit(argv[1]);
746 * XXX Should maybe do something different with the device
747 * XXX handling for cdlu; think about this some more.
749 if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd))
750 err(EXIT_FAILURE, "%s: CDIOCLOADUNLOAD", changer_name);
751 /* NOTREACHED */
753 return (0);
756 static int
757 parse_element_type(const char *cp)
759 int i;
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);
765 errx(EXIT_FAILURE, "invalid element type `%s'", cp);
766 /* NOTREACHED */
769 static int
770 parse_element_unit(const char *cp)
772 char *p;
773 int i;
775 i = (int)strtol(cp, &p, 10);
776 if ((i < 0) || (*p != '\0'))
777 errx(EXIT_FAILURE, "invalid unit number `%s'", cp);
779 return (i);
782 static int
783 parse_special(const char *cp)
785 int val;
787 val = is_special(cp);
788 if (val)
789 return (val);
791 errx(EXIT_FAILURE, "invalid modifier `%s'", cp);
792 /* NOTREACHED */
795 static int
796 is_special(const char *cp)
798 int i;
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);
804 return (0);
807 static const char *
808 bits_to_string(int v, const char *cp)
810 static char buf[128];
811 const char *np;
812 char *bp, f;
813 int first;
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;
829 *bp++ = '>';
830 *bp = '\0';
832 return (buf);
835 static void
836 cleanup(void)
839 /* Simple enough... */
840 (void)close(changer_fd);
843 static void
844 usage(void)
846 int i;
848 (void)fprintf(stderr,
849 "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n",
850 getprogname());
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 */