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