xref: /illumos-gate/usr/src/cmd/iscsiadm/cmdparse.c (revision 99ea293e719ac006d413e4fde6ac0d5cd4dd6c59)
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
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
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
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 *
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 *
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 *
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
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
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
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 *
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
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