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