xref: /illumos-gate/usr/src/cmd/mpathadm/cmdparse.c (revision 0250c53ad267726f2438e3c6556199a0bbf588a2)
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
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
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
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 *
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 *
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 *
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
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
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
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 *
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
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