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