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