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