1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2020 Joyent Inc.
27 */
28
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <libintl.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <getopt.h>
39 #include "cmdparse.h"
40
41 /* Usage types */
42 #define GENERAL_USAGE 1
43 #define HELP_USAGE 2
44 #define DETAIL_USAGE 3
45
46 /* printable ascii character set len */
47 #define MAXOPTIONS (uint_t)('~' - '!' + 1)
48
49 /*
50 * MAXOPTIONSTRING is the max length of the options string used in getopt and
51 * will be the printable character set + ':' for each character,
52 * providing for options with arguments. e.g. "t:Cs:hglr:"
53 */
54 #define MAXOPTIONSTRING MAXOPTIONS * 2
55
56 /* standard command options table to support -?, -V */
57 struct option standardCmdOptions[] = {
58 {"help", no_argument, NULL, '?'},
59 {"version", no_argument, NULL, 'V'},
60 {NULL, 0, NULL, 0}
61 };
62
63 /* standard subcommand options table to support -? */
64 struct option standardSubCmdOptions[] = {
65 {"help", no_argument, NULL, '?'},
66 {NULL, 0, NULL, 0}
67 };
68
69 /* forward declarations */
70 static int getSubcommand(char *, subcommand_t **);
71 static char *getExecBasename(char *);
72 static void usage(uint_t);
73 static void subUsage(uint_t, subcommand_t *);
74 static void subUsageObject(uint_t, subcommand_t *, object_t *);
75 static int getObject(char *, object_t **);
76 static int getObjectRules(uint_t, objectRules_t **);
77 static const char *getLongOption(int);
78 static optionProp_t *getOptions(uint_t, uint_t);
79 static char *getOptionArgDesc(int);
80
81 /* global data */
82 static struct option *_longOptions;
83 static subcommand_t *_subcommands;
84 static object_t *_objects;
85 static objectRules_t *_objectRules;
86 static optionRules_t *_optionRules;
87 static optionTbl_t *_clientOptionTbl;
88 static char *commandName;
89
90
91 /*
92 * input:
93 * object - object value
94 * output:
95 * opCmd - pointer to opCmd_t structure allocated by caller
96 *
97 * On successful return, opCmd contains the rules for the value in
98 * object. On failure, the contents of opCmd is unspecified.
99 *
100 * Returns:
101 * zero on success
102 * non-zero on failure
103 *
104 */
105 static int
getObjectRules(uint_t object,objectRules_t ** objectRules)106 getObjectRules(uint_t object, objectRules_t **objectRules)
107 {
108 objectRules_t *sp;
109
110 for (sp = _objectRules; sp->value; sp++) {
111 if (sp->value == object) {
112 *objectRules = sp;
113 return (0);
114 }
115 }
116 return (1);
117 }
118
119 /*
120 * input:
121 * arg - pointer to array of char containing object string
122 *
123 * output:
124 * object - pointer to object_t structure pointer
125 * on success, contains the matching object structure based on
126 * input object name
127 *
128 * Returns:
129 * zero on success
130 * non-zero otherwise
131 *
132 */
133 static int
getObject(char * arg,object_t ** object)134 getObject(char *arg, object_t **object)
135 {
136
137 object_t *op;
138 int len;
139
140 for (op = _objects; op->name; op++) {
141 len = strlen(arg);
142 if (len == strlen(op->name) &&
143 strncasecmp(arg, op->name, len) == 0) {
144 *object = op;
145 return (0);
146 }
147 }
148 return (1);
149 }
150
151 /*
152 * input:
153 * arg - pointer to array of char containing subcommand string
154 * output:
155 * subcommand - pointer to subcommand_t pointer
156 * on success, contains the matching subcommand structure based on
157 * input subcommand name
158 *
159 * Returns:
160 * zero on success
161 * non-zero on failure
162 */
163 static int
getSubcommand(char * arg,subcommand_t ** subcommand)164 getSubcommand(char *arg, subcommand_t **subcommand)
165 {
166 subcommand_t *sp;
167 int len;
168
169 for (sp = _subcommands; sp->name; sp++) {
170 len = strlen(arg);
171 if (len == strlen(sp->name) &&
172 strncasecmp(arg, sp->name, len) == 0) {
173 *subcommand = sp;
174 return (0);
175 }
176 }
177 return (1);
178 }
179
180 /*
181 * input:
182 * object - object for which to get options
183 * subcommand - subcommand for which to get options
184 *
185 * Returns:
186 * on success, optionsProp_t pointer to structure matching input object
187 * value
188 * on failure, NULL is returned
189 */
190 static optionProp_t *
getOptions(uint_t object,uint_t subcommand)191 getOptions(uint_t object, uint_t subcommand)
192 {
193 uint_t currObject;
194 optionRules_t *op = _optionRules;
195 while (op && ((currObject = op->objectValue) != 0)) {
196 if ((currObject == object) &&
197 (op->subcommandValue == subcommand)) {
198 return (&(op->optionProp));
199 }
200 op++;
201 }
202 return (NULL);
203 }
204
205 /*
206 * input:
207 * shortOption - short option character for which to return the
208 * associated long option string
209 *
210 * Returns:
211 * on success, long option name
212 * on failure, NULL
213 */
214 static const char *
getLongOption(int shortOption)215 getLongOption(int shortOption)
216 {
217 struct option *op;
218 for (op = _longOptions; op->name; op++) {
219 if (shortOption == op->val) {
220 return (op->name);
221 }
222 }
223 return (NULL);
224 }
225
226 /*
227 * input
228 * shortOption - short option character for which to return the
229 * option argument
230 * Returns:
231 * on success, argument string
232 * on failure, NULL
233 */
234 static char *
getOptionArgDesc(int shortOption)235 getOptionArgDesc(int shortOption)
236 {
237 optionTbl_t *op;
238 for (op = _clientOptionTbl; op->name; op++) {
239 if (op->val == shortOption &&
240 op->has_arg == required_argument) {
241 return (op->argDesc);
242 }
243 }
244 return (NULL);
245 }
246
247
248 /*
249 * Print usage for a subcommand.
250 *
251 * input:
252 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
253 * subcommand - pointer to subcommand_t structure
254 *
255 * Returns:
256 * none
257 *
258 */
259 static void
subUsage(uint_t usageType,subcommand_t * subcommand)260 subUsage(uint_t usageType, subcommand_t *subcommand)
261 {
262 int i;
263 object_t *objp;
264
265
266 (void) fprintf(stdout, "%s:\t%s %s [",
267 gettext("Usage"), commandName, subcommand->name);
268
269 for (i = 0; standardSubCmdOptions[i].name; i++) {
270 (void) fprintf(stdout, "-%c",
271 standardSubCmdOptions[i].val);
272 if (standardSubCmdOptions[i+1].name)
273 (void) fprintf(stdout, ",");
274 }
275
276 (void) fprintf(stdout, "] %s [", "<OBJECT>");
277
278 for (i = 0; standardSubCmdOptions[i].name; i++) {
279 (void) fprintf(stdout, "-%c",
280 standardSubCmdOptions[i].val);
281 if (standardSubCmdOptions[i+1].name)
282 (void) fprintf(stdout, ",");
283 }
284
285 (void) fprintf(stdout, "] %s", "[<OPERAND>]");
286 (void) fprintf(stdout, "\n");
287
288 if (usageType == GENERAL_USAGE) {
289 return;
290 }
291
292 (void) fprintf(stdout, "%s:\n", gettext("Usage by OBJECT"));
293
294 /*
295 * iterate through object table
296 * For each object, print appropriate usage
297 * based on rules tables
298 */
299 for (objp = _objects; objp->value; objp++) {
300 subUsageObject(usageType, subcommand, objp);
301 }
302 }
303
304 /*
305 * Print usage for a subcommand and object.
306 *
307 * input:
308 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
309 * subcommand - pointer to subcommand_t structure
310 * objp - pointer to a object_t structure
311 *
312 * Returns:
313 * none
314 *
315 */
316 static void
subUsageObject(uint_t usageType,subcommand_t * subcommand,object_t * objp)317 subUsageObject(uint_t usageType, subcommand_t *subcommand, object_t *objp)
318 {
319 int i;
320 objectRules_t *objRules = NULL;
321 opCmd_t *opCmd = NULL;
322 optionProp_t *options;
323 char *optionArgDesc;
324 const char *longOpt;
325
326
327 if (getObjectRules(objp->value, &objRules) != 0) {
328 /*
329 * internal subcommand rules table error
330 * no object entry in object
331 */
332 assert(0);
333 }
334
335 opCmd = &(objRules->opCmd);
336
337 if (opCmd->invOpCmd & subcommand->value) {
338 return;
339 }
340
341 options = getOptions(objp->value, subcommand->value);
342
343 /* print generic subcommand usage */
344 (void) fprintf(stdout, "\t%s %s ", commandName, subcommand->name);
345
346 /* print object */
347 (void) fprintf(stdout, "%s ", objp->name);
348
349 /* print options if applicable */
350 if (options != NULL) {
351 if (options->required) {
352 (void) fprintf(stdout, "%s", gettext("<"));
353 } else {
354 (void) fprintf(stdout, "%s", gettext("["));
355 }
356 (void) fprintf(stdout, "%s", gettext("OPTIONS"));
357 if (options->required) {
358 (void) fprintf(stdout, "%s ", gettext(">"));
359 } else {
360 (void) fprintf(stdout, "%s ", gettext("]"));
361 }
362 }
363
364 /* print operand requirements */
365 if (opCmd->optOpCmd & subcommand->value) {
366 (void) fprintf(stdout, gettext("["));
367 }
368 if (!(opCmd->noOpCmd & subcommand->value)) {
369 (void) fprintf(stdout, gettext("<"));
370 if (objRules->operandDefinition) {
371 (void) fprintf(stdout, "%s",
372 objRules->operandDefinition);
373 } else {
374 /*
375 * Missing operand description
376 * from table
377 */
378 assert(0);
379 }
380 }
381 if (opCmd->multOpCmd & subcommand->value) {
382 (void) fprintf(stdout, gettext(" ..."));
383 }
384 if (!(opCmd->noOpCmd & subcommand->value)) {
385 (void) fprintf(stdout, gettext(">"));
386 }
387 if (opCmd->optOpCmd & subcommand->value) {
388 (void) fprintf(stdout, gettext("]"));
389 }
390
391 if (usageType == HELP_USAGE) {
392 (void) fprintf(stdout, "\n");
393 return;
394 }
395
396 /* print options for subcommand, object */
397 if (options != NULL && options->optionString != NULL) {
398 (void) fprintf(stdout, "\n\t%s:", gettext("OPTIONS"));
399 for (i = 0; i < strlen(options->optionString); i++) {
400 if ((longOpt = getLongOption(
401 options->optionString[i]))
402 == NULL) {
403 /* no long option exists for short option */
404 assert(0);
405 }
406 (void) fprintf(stdout, "\n\t\t-%c, --%s ",
407 options->optionString[i], longOpt);
408 optionArgDesc =
409 getOptionArgDesc(options->optionString[i]);
410 if (optionArgDesc != NULL) {
411 (void) fprintf(stdout, "<%s>", optionArgDesc);
412 }
413 if (options->exclusive &&
414 strchr(options->exclusive,
415 options->optionString[i])) {
416 (void) fprintf(stdout, " (%s)",
417 gettext("exclusive"));
418 }
419 }
420 }
421 (void) fprintf(stdout, "\n");
422 }
423
424 /*
425 * input:
426 * type of usage statement to print
427 *
428 * Returns:
429 * return value of subUsage
430 */
431 static void
usage(uint_t usageType)432 usage(uint_t usageType)
433 {
434 int i;
435 subcommand_t subcommand;
436 subcommand_t *sp;
437
438 /* print general command usage */
439 (void) fprintf(stdout, "%s:\t%s ",
440 gettext("Usage"), commandName);
441
442 for (i = 0; standardCmdOptions[i].name; i++) {
443 (void) fprintf(stdout, "-%c",
444 standardCmdOptions[i].val);
445 if (standardCmdOptions[i+1].name)
446 (void) fprintf(stdout, ",");
447 }
448
449 if (usageType == HELP_USAGE || usageType == GENERAL_USAGE) {
450 for (i = 0; standardSubCmdOptions[i].name; i++) {
451 (void) fprintf(stdout, ",--%s",
452 standardSubCmdOptions[i].name);
453 if (standardSubCmdOptions[i+1].name)
454 (void) fprintf(stdout, ",");
455 }
456 }
457
458 (void) fprintf(stdout, "\n");
459
460
461 /* print all subcommand usage */
462 for (sp = _subcommands; sp->name; sp++) {
463 subcommand.name = sp->name;
464 subcommand.value = sp->value;
465 if (usageType == HELP_USAGE) {
466 (void) fprintf(stdout, "\n");
467 }
468 subUsage(usageType, &subcommand);
469 }
470 }
471
472 /*
473 * input:
474 * execFullName - exec name of program (argv[0])
475 *
476 * Returns:
477 * command name portion of execFullName
478 */
479 static char *
getExecBasename(char * execFullname)480 getExecBasename(char *execFullname)
481 {
482 char *lastSlash, *execBasename;
483
484 /* guard against '/' at end of command invocation */
485 for (;;) {
486 lastSlash = strrchr(execFullname, '/');
487 if (lastSlash == NULL) {
488 execBasename = execFullname;
489 break;
490 } else {
491 execBasename = lastSlash + 1;
492 if (*execBasename == '\0') {
493 *lastSlash = '\0';
494 continue;
495 }
496 break;
497 }
498 }
499 return (execBasename);
500 }
501
502 /*
503 * cmdParse is a parser that checks syntax of the input command against
504 * various rules tables.
505 *
506 * It provides usage feedback based upon the passed rules tables by calling
507 * two usage functions, usage, subUsage, and subUsageObject handling command,
508 * subcommand and object usage respectively.
509 *
510 * When syntax is successfully validated, the associated function is called
511 * using the subcommands table functions.
512 *
513 * Syntax is as follows:
514 * command subcommand object [<options>] [<operand>]
515 *
516 * There are two standard short and long options assumed:
517 * -?, --help Provides usage on a command or subcommand
518 * and stops further processing of the arguments
519 *
520 * -V, --version Provides version information on the command
521 * and stops further processing of the arguments
522 *
523 * These options are loaded by this function.
524 *
525 * input:
526 * argc, argv from main
527 * syntax rules tables (synTables_t structure)
528 * callArgs - void * passed by caller to be passed to subcommand function
529 *
530 * output:
531 * funcRet - pointer to int that holds subcommand function return value
532 *
533 * Returns:
534 *
535 * zero on successful syntax parse and function call
536 *
537 * 1 on unsuccessful syntax parse (no function has been called)
538 * This could be due to a version or help call or simply a
539 * general usage call.
540 *
541 * -1 check errno, call failed
542 *
543 * This module is not MT-safe.
544 *
545 */
546 int
cmdParse(int argc,char * argv[],synTables_t synTable,void * callArgs,int * funcRet)547 cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
548 int *funcRet)
549 {
550 int getoptargc;
551 char **getoptargv;
552 int opt;
553 int operInd;
554 int i, j;
555 int len;
556 char *versionString;
557 char optionStringAll[MAXOPTIONSTRING + 1];
558 optionProp_t *availOptions;
559 objectRules_t *objRules = NULL;
560 opCmd_t *opCmd = NULL;
561 subcommand_t *subcommand;
562 object_t *object;
563 cmdOptions_t cmdOptions[MAXOPTIONS + 1];
564 struct option *lp;
565 optionTbl_t *optionTbl;
566 struct option intLongOpt[MAXOPTIONS + 1];
567
568 /*
569 * Check for NULLs on mandatory input arguments
570 *
571 * Note: longOptionTbl and optionRulesTbl can be NULL in the case
572 * where there is no caller defined options
573 *
574 */
575 if (synTable.versionString == NULL ||
576 synTable.subcommandTbl == NULL ||
577 synTable.objectRulesTbl == NULL ||
578 synTable.objectTbl == NULL ||
579 funcRet == NULL) {
580 assert(0);
581 }
582
583
584 versionString = synTable.versionString;
585
586 /* set global command name */
587 commandName = getExecBasename(argv[0]);
588
589 /* Set unbuffered output */
590 setbuf(stdout, NULL);
591
592 /* load globals */
593 _subcommands = synTable.subcommandTbl;
594 _objectRules = synTable.objectRulesTbl;
595 _optionRules = synTable.optionRulesTbl;
596 _objects = synTable.objectTbl;
597 _clientOptionTbl = synTable.longOptionTbl;
598
599 /* There must be at least two arguments */
600 if (argc < 2) {
601 usage(GENERAL_USAGE);
602 return (1);
603 }
604
605 (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt));
606
607 /*
608 * load standard subcommand options to internal long options table
609 * Two separate getopt_long(3C) tables are used.
610 */
611 for (i = 0; standardSubCmdOptions[i].name; i++) {
612 intLongOpt[i].name = standardSubCmdOptions[i].name;
613 intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
614 intLongOpt[i].flag = standardSubCmdOptions[i].flag;
615 intLongOpt[i].val = standardSubCmdOptions[i].val;
616 }
617
618 /*
619 * copy caller's long options into internal long options table
620 * We do this for two reasons:
621 * 1) We need to use the getopt_long option structure internally
622 * 2) We need to prepend the table with the standard option
623 * for all subcommands (currently -?)
624 */
625 for (optionTbl = synTable.longOptionTbl;
626 optionTbl && optionTbl->name; optionTbl++, i++) {
627 if (i > MAXOPTIONS - 1) {
628 /* option table too long */
629 assert(0);
630 }
631 intLongOpt[i].name = optionTbl->name;
632 intLongOpt[i].has_arg = optionTbl->has_arg;
633 intLongOpt[i].flag = NULL;
634 intLongOpt[i].val = optionTbl->val;
635 }
636
637 /* set option table global */
638 _longOptions = &intLongOpt[0];
639
640
641 /*
642 * Check for help/version request immediately following command
643 * '+' in option string ensures POSIX compliance in getopt_long()
644 * which means that processing will stop at first non-option
645 * argument.
646 */
647 while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
648 NULL)) != EOF) {
649 switch (opt) {
650 case '?':
651 /*
652 * getopt can return a '?' when no
653 * option letters match string. Check for
654 * the 'real' '?' in optopt.
655 */
656 if (optopt == '?') {
657 usage(HELP_USAGE);
658 return (1);
659 } else {
660 usage(GENERAL_USAGE);
661 return (1);
662 }
663 case 'V':
664 (void) fprintf(stdout, "%s: %s %s\n",
665 commandName, gettext("Version"),
666 versionString);
667 return (1);
668 default:
669 break;
670 }
671 }
672
673 /*
674 * subcommand is always in the second argument. If there is no
675 * recognized subcommand in the second argument, print error,
676 * general usage and then return.
677 */
678 if (getSubcommand(argv[1], &subcommand) != 0) {
679 (void) fprintf(stderr, "%s: %s\n",
680 commandName, gettext("invalid subcommand"));
681 usage(GENERAL_USAGE);
682 return (1);
683 }
684
685 if (argc == 2) {
686 (void) fprintf(stderr, "%s: %s\n",
687 commandName, gettext("missing object"));
688 subUsage(GENERAL_USAGE, subcommand);
689 return (1);
690 }
691
692 getoptargv = argv;
693 getoptargv++;
694 getoptargc = argc;
695 getoptargc -= 1;
696
697 while ((opt = getopt_long(getoptargc, getoptargv, "+?",
698 standardSubCmdOptions, NULL)) != EOF) {
699 switch (opt) {
700 case '?':
701 /*
702 * getopt can return a '?' when no
703 * option letters match string. Check for
704 * the 'real' '?' in optopt.
705 */
706 if (optopt == '?') {
707 subUsage(HELP_USAGE, subcommand);
708 return (1);
709 } else {
710 subUsage(GENERAL_USAGE, subcommand);
711 return (1);
712 }
713 default:
714 break;
715 }
716 }
717
718
719 /*
720 * object is always in the third argument. If there is no
721 * recognized object in the third argument, print error,
722 * help usage for the subcommand and then return.
723 */
724 if (getObject(argv[2], &object) != 0) {
725 (void) fprintf(stderr, "%s: %s\n",
726 commandName, gettext("invalid object"));
727 subUsage(HELP_USAGE, subcommand);
728 return (1);
729 }
730
731 if (getObjectRules(object->value, &objRules) != 0) {
732 /*
733 * internal subcommand rules table error
734 * no object entry in object table
735 */
736 assert(0);
737 }
738
739 opCmd = &(objRules->opCmd);
740
741 /*
742 * Is command valid for this object?
743 */
744 if (opCmd->invOpCmd & subcommand->value) {
745 (void) fprintf(stderr, "%s: %s %s\n", commandName,
746 gettext("invalid subcommand for"), object->name);
747 subUsage(HELP_USAGE, subcommand);
748 return (1);
749 }
750
751 /*
752 * offset getopt arg begin since
753 * getopt(3C) assumes options
754 * follow first argument
755 */
756 getoptargv = argv;
757 getoptargv++;
758 getoptargv++;
759 getoptargc = argc;
760 getoptargc -= 2;
761
762 (void) memset(optionStringAll, 0, sizeof (optionStringAll));
763 (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions));
764
765 j = 0;
766 /*
767 * Build optionStringAll from long options table
768 */
769 for (lp = _longOptions; lp->name; lp++, j++) {
770 /* sanity check on string length */
771 if (j + 1 >= sizeof (optionStringAll)) {
772 /* option table too long */
773 assert(0);
774 }
775 optionStringAll[j] = lp->val;
776 if (lp->has_arg == required_argument) {
777 optionStringAll[++j] = ':';
778 }
779 }
780
781 i = 0;
782 /*
783 * Run getopt for all arguments against all possible options
784 * Store all options/option arguments in an array for retrieval
785 * later.
786 * Once all options are retrieved, check against object
787 * and subcommand (option rules table) for validity.
788 * This is done later.
789 */
790 while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
791 _longOptions, NULL)) != EOF) {
792 switch (opt) {
793 case '?':
794 if (optopt == '?') {
795 subUsageObject(DETAIL_USAGE,
796 subcommand, object);
797 return (1);
798 } else {
799 subUsage(GENERAL_USAGE, subcommand);
800 return (1);
801 }
802 default:
803 cmdOptions[i].optval = opt;
804 if (optarg) {
805 len = strlen(optarg);
806 if (len > sizeof (cmdOptions[i].optarg)
807 - 1) {
808 (void) fprintf(stderr,
809 "%s: %s\n", commandName,
810 gettext("option too long"));
811 errno = EINVAL;
812 return (-1);
813 }
814 (void) strncpy(cmdOptions[i].optarg,
815 optarg, len);
816 }
817 i++;
818 break;
819 }
820 }
821
822 /*
823 * increment past last option
824 */
825 operInd = optind + 2;
826
827 /*
828 * Check validity of given options, if any were given
829 */
830
831 /* get option string for this object and subcommand */
832 availOptions = getOptions(object->value, subcommand->value);
833
834 if (cmdOptions[0].optval != 0) { /* options were input */
835 if (availOptions == NULL) { /* no options permitted */
836 (void) fprintf(stderr, "%s: %s\n",
837 commandName, gettext("no options permitted"));
838 subUsageObject(HELP_USAGE, subcommand, object);
839 return (1);
840 }
841 for (i = 0; cmdOptions[i].optval; i++) {
842 /* Check for invalid options */
843 if (availOptions->optionString == NULL) {
844 /*
845 * internal option table error
846 * There must be an option string if
847 * there is an entry in the table
848 */
849 assert(0);
850 }
851 /* is the option in the available option string? */
852
853 if (!(strchr(availOptions->optionString,
854 cmdOptions[i].optval))) {
855 (void) fprintf(stderr,
856 "%s: '-%c': %s\n",
857 commandName, cmdOptions[i].optval,
858 gettext("invalid option"));
859 subUsageObject(DETAIL_USAGE, subcommand,
860 object);
861 return (1);
862
863 /* Check for exclusive options */
864 } else if (cmdOptions[1].optval != 0 &&
865 availOptions->exclusive &&
866 strchr(availOptions->exclusive,
867 cmdOptions[i].optval)) {
868 (void) fprintf(stderr,
869 "%s: '-%c': %s\n",
870 commandName, cmdOptions[i].optval,
871 gettext("is an exclusive option"));
872 subUsageObject(DETAIL_USAGE, subcommand,
873 object);
874 return (1);
875 }
876 }
877 } else { /* no options were input */
878 if (availOptions != NULL &&
879 (availOptions->required)) {
880 (void) fprintf(stderr, "%s: %s\n",
881 commandName,
882 gettext("at least one option required"));
883 subUsageObject(DETAIL_USAGE, subcommand,
884 object);
885 return (1);
886 }
887 }
888
889 /*
890 * If there are no more arguments (operands),
891 * check to see if this is okay
892 */
893 if ((operInd == argc) &&
894 (opCmd->reqOpCmd & subcommand->value)) {
895 (void) fprintf(stderr, "%s: %s %s %s\n",
896 commandName, subcommand->name,
897 object->name, gettext("requires an operand"));
898 subUsageObject(HELP_USAGE, subcommand, object);
899 return (1);
900 }
901
902 /*
903 * If there are more operands,
904 * check to see if this is okay
905 */
906 if ((argc > operInd) &&
907 (opCmd->noOpCmd & subcommand->value)) {
908 (void) fprintf(stderr, "%s: %s %s %s\n",
909 commandName, subcommand->name,
910 object->name, gettext("takes no operands"));
911 subUsageObject(HELP_USAGE, subcommand, object);
912 return (1);
913 }
914
915 /*
916 * If there is more than one more operand,
917 * check to see if this is okay
918 */
919 if ((argc > operInd) && ((argc - operInd) != 1) &&
920 !(opCmd->multOpCmd & subcommand->value)) {
921 (void) fprintf(stderr, "%s: %s %s %s\n",
922 commandName, subcommand->name, object->name,
923 gettext("accepts only a single operand"));
924 subUsageObject(HELP_USAGE, subcommand, object);
925 return (1);
926 }
927
928 /* Finished syntax checks */
929
930
931 /* Call appropriate function */
932 *funcRet = subcommand->handler(argc - operInd, &argv[operInd],
933 object->value, &cmdOptions[0], callArgs);
934
935 return (0);
936 }
937