xref: /onnv-gate/usr/src/common/cmdparse/cmdparse.c (revision 10652:9d0aff74d6fd)
17836SJohn.Forte@Sun.COM /*
27836SJohn.Forte@Sun.COM  * CDDL HEADER START
37836SJohn.Forte@Sun.COM  *
47836SJohn.Forte@Sun.COM  * The contents of this file are subject to the terms of the
57836SJohn.Forte@Sun.COM  * Common Development and Distribution License (the "License").
67836SJohn.Forte@Sun.COM  * You may not use this file except in compliance with the License.
77836SJohn.Forte@Sun.COM  *
87836SJohn.Forte@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97836SJohn.Forte@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107836SJohn.Forte@Sun.COM  * See the License for the specific language governing permissions
117836SJohn.Forte@Sun.COM  * and limitations under the License.
127836SJohn.Forte@Sun.COM  *
137836SJohn.Forte@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147836SJohn.Forte@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157836SJohn.Forte@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167836SJohn.Forte@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177836SJohn.Forte@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187836SJohn.Forte@Sun.COM  *
197836SJohn.Forte@Sun.COM  * CDDL HEADER END
207836SJohn.Forte@Sun.COM  */
217836SJohn.Forte@Sun.COM /*
229585STim.Szeto@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237836SJohn.Forte@Sun.COM  * Use is subject to license terms.
247836SJohn.Forte@Sun.COM  */
257836SJohn.Forte@Sun.COM 
267836SJohn.Forte@Sun.COM #include <stdlib.h>
277836SJohn.Forte@Sun.COM #include <stdio.h>
287836SJohn.Forte@Sun.COM #include <sys/types.h>
297836SJohn.Forte@Sun.COM #include <unistd.h>
307836SJohn.Forte@Sun.COM #include <libintl.h>
317836SJohn.Forte@Sun.COM #include <errno.h>
327836SJohn.Forte@Sun.COM #include <string.h>
337836SJohn.Forte@Sun.COM #include <assert.h>
347836SJohn.Forte@Sun.COM #include <getopt.h>
357836SJohn.Forte@Sun.COM #include <cmdparse.h>
367836SJohn.Forte@Sun.COM 
377836SJohn.Forte@Sun.COM 
387836SJohn.Forte@Sun.COM /* Usage types */
397836SJohn.Forte@Sun.COM #define	GENERAL_USAGE	1
407836SJohn.Forte@Sun.COM #define	DETAIL_USAGE	2
417836SJohn.Forte@Sun.COM 
427836SJohn.Forte@Sun.COM /* printable ascii character set len */
437836SJohn.Forte@Sun.COM #define	MAXOPTIONS	(uint_t)('~' - '!' + 1)
447836SJohn.Forte@Sun.COM 
457836SJohn.Forte@Sun.COM /*
467836SJohn.Forte@Sun.COM  * MAXOPTIONSTRING is the max length of the options string used in getopt and
477836SJohn.Forte@Sun.COM  * will be the printable character set + ':' for each character,
487836SJohn.Forte@Sun.COM  * providing for options with arguments. e.g. "t:Cs:hglr:"
497836SJohn.Forte@Sun.COM  */
507836SJohn.Forte@Sun.COM #define	MAXOPTIONSTRING		MAXOPTIONS * 2
517836SJohn.Forte@Sun.COM 
527836SJohn.Forte@Sun.COM /* standard command options table to support -?, -V */
537836SJohn.Forte@Sun.COM struct option standardCmdOptions[] = {
547836SJohn.Forte@Sun.COM 	{"help", no_argument, NULL, '?'},
557836SJohn.Forte@Sun.COM 	{"version", no_argument, NULL, 'V'},
567836SJohn.Forte@Sun.COM 	{NULL, 0, NULL, 0}
577836SJohn.Forte@Sun.COM };
587836SJohn.Forte@Sun.COM 
597836SJohn.Forte@Sun.COM /* standard subcommand options table to support -? */
607836SJohn.Forte@Sun.COM struct option standardSubCmdOptions[] = {
617836SJohn.Forte@Sun.COM 	{"help", no_argument, NULL, '?'},
627836SJohn.Forte@Sun.COM 	{NULL, 0, NULL, 0}
637836SJohn.Forte@Sun.COM };
647836SJohn.Forte@Sun.COM 
657836SJohn.Forte@Sun.COM /* forward declarations */
667836SJohn.Forte@Sun.COM static int getSubcommandProps(char *, subCommandProps_t **);
677836SJohn.Forte@Sun.COM static char *getExecBasename(char *);
687836SJohn.Forte@Sun.COM static void usage(uint_t);
697836SJohn.Forte@Sun.COM static void subUsage(uint_t, subCommandProps_t *);
707836SJohn.Forte@Sun.COM static char *getLongOption(int);
717836SJohn.Forte@Sun.COM static char *getOptionArgDesc(int);
727836SJohn.Forte@Sun.COM 
737836SJohn.Forte@Sun.COM /* global data */
747836SJohn.Forte@Sun.COM static struct option *_longOptions;
757836SJohn.Forte@Sun.COM static subCommandProps_t *_subCommandProps;
767836SJohn.Forte@Sun.COM static optionTbl_t *_clientOptionTbl;
777836SJohn.Forte@Sun.COM static char *commandName;
787836SJohn.Forte@Sun.COM 
797836SJohn.Forte@Sun.COM 
807836SJohn.Forte@Sun.COM /*
817836SJohn.Forte@Sun.COM  * input:
827836SJohn.Forte@Sun.COM  *  subCommand - subcommand value
837836SJohn.Forte@Sun.COM  * output:
847836SJohn.Forte@Sun.COM  *  subCommandProps - pointer to subCommandProps_t structure allocated by caller
857836SJohn.Forte@Sun.COM  *
867836SJohn.Forte@Sun.COM  * On successful return, subCommandProps contains the properties for the value
877836SJohn.Forte@Sun.COM  * in subCommand. On failure, the contents of subCommandProps is unspecified.
887836SJohn.Forte@Sun.COM  *
897836SJohn.Forte@Sun.COM  * Returns:
907836SJohn.Forte@Sun.COM  *  zero on success
917836SJohn.Forte@Sun.COM  *  non-zero on failure
927836SJohn.Forte@Sun.COM  *
937836SJohn.Forte@Sun.COM  */
947836SJohn.Forte@Sun.COM static int
getSubcommandProps(char * subCommand,subCommandProps_t ** subCommandProps)957836SJohn.Forte@Sun.COM getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps)
967836SJohn.Forte@Sun.COM {
977836SJohn.Forte@Sun.COM 	subCommandProps_t *sp;
987836SJohn.Forte@Sun.COM 	int len;
997836SJohn.Forte@Sun.COM 
1007836SJohn.Forte@Sun.COM 	for (sp = _subCommandProps; sp->name; sp++) {
1017836SJohn.Forte@Sun.COM 		len = strlen(subCommand);
1027836SJohn.Forte@Sun.COM 		if (len == strlen(sp->name) &&
1037836SJohn.Forte@Sun.COM 		    strncasecmp(subCommand, sp->name, len) == 0) {
1047836SJohn.Forte@Sun.COM 			*subCommandProps = sp;
1057836SJohn.Forte@Sun.COM 			return (0);
1067836SJohn.Forte@Sun.COM 		}
1077836SJohn.Forte@Sun.COM 	}
1087836SJohn.Forte@Sun.COM 	return (1);
1097836SJohn.Forte@Sun.COM }
1107836SJohn.Forte@Sun.COM 
1117836SJohn.Forte@Sun.COM /*
1127836SJohn.Forte@Sun.COM  * input:
1137836SJohn.Forte@Sun.COM  *  shortOption - short option character for which to return the
1147836SJohn.Forte@Sun.COM  *	associated long option string
1157836SJohn.Forte@Sun.COM  *
1167836SJohn.Forte@Sun.COM  * Returns:
1177836SJohn.Forte@Sun.COM  *  on success, long option name
1187836SJohn.Forte@Sun.COM  *  on failure, NULL
1197836SJohn.Forte@Sun.COM  */
1207836SJohn.Forte@Sun.COM static char *
getLongOption(int shortOption)1217836SJohn.Forte@Sun.COM getLongOption(int shortOption)
1227836SJohn.Forte@Sun.COM {
1237836SJohn.Forte@Sun.COM 	struct option *op;
1247836SJohn.Forte@Sun.COM 	for (op = _longOptions; op->name; op++) {
1257836SJohn.Forte@Sun.COM 		if (shortOption == op->val) {
1267836SJohn.Forte@Sun.COM 			return (op->name);
1277836SJohn.Forte@Sun.COM 		}
1287836SJohn.Forte@Sun.COM 	}
1297836SJohn.Forte@Sun.COM 	return (NULL);
1307836SJohn.Forte@Sun.COM }
1317836SJohn.Forte@Sun.COM 
1327836SJohn.Forte@Sun.COM /*
1337836SJohn.Forte@Sun.COM  * input
1347836SJohn.Forte@Sun.COM  *  shortOption - short option character for which to return the
1357836SJohn.Forte@Sun.COM  *	option argument
1367836SJohn.Forte@Sun.COM  * Returns:
1377836SJohn.Forte@Sun.COM  *  on success, argument string
1387836SJohn.Forte@Sun.COM  *  on failure, NULL
1397836SJohn.Forte@Sun.COM  */
1407836SJohn.Forte@Sun.COM static char *
getOptionArgDesc(int shortOption)1417836SJohn.Forte@Sun.COM getOptionArgDesc(int shortOption)
1427836SJohn.Forte@Sun.COM {
1437836SJohn.Forte@Sun.COM 	optionTbl_t *op;
1447836SJohn.Forte@Sun.COM 	for (op = _clientOptionTbl; op->name; op++) {
1457836SJohn.Forte@Sun.COM 		if (op->val == shortOption &&
1467836SJohn.Forte@Sun.COM 		    op->has_arg == required_argument) {
1477836SJohn.Forte@Sun.COM 			return (op->argDesc);
1487836SJohn.Forte@Sun.COM 		}
1497836SJohn.Forte@Sun.COM 	}
1507836SJohn.Forte@Sun.COM 	return (NULL);
1517836SJohn.Forte@Sun.COM }
1527836SJohn.Forte@Sun.COM 
1537836SJohn.Forte@Sun.COM 
1547836SJohn.Forte@Sun.COM /*
1557836SJohn.Forte@Sun.COM  * Print usage for a subcommand.
1567836SJohn.Forte@Sun.COM  *
1577836SJohn.Forte@Sun.COM  * input:
1587836SJohn.Forte@Sun.COM  *  usage type - GENERAL_USAGE, DETAIL_USAGE
1597836SJohn.Forte@Sun.COM  *  subcommand - pointer to subCommandProps_t structure
1607836SJohn.Forte@Sun.COM  *
1617836SJohn.Forte@Sun.COM  * Returns:
1627836SJohn.Forte@Sun.COM  *  none
1637836SJohn.Forte@Sun.COM  *
1647836SJohn.Forte@Sun.COM  */
1657836SJohn.Forte@Sun.COM static void
subUsage(uint_t usageType,subCommandProps_t * subcommand)1667836SJohn.Forte@Sun.COM subUsage(uint_t usageType, subCommandProps_t *subcommand)
1677836SJohn.Forte@Sun.COM {
1687836SJohn.Forte@Sun.COM 	int i;
1697836SJohn.Forte@Sun.COM 	char *optionArgDesc;
1707836SJohn.Forte@Sun.COM 	char *longOpt;
1717836SJohn.Forte@Sun.COM 
1727836SJohn.Forte@Sun.COM 	if (usageType == GENERAL_USAGE) {
1737836SJohn.Forte@Sun.COM 		(void) printf("%s:\t%s %s [", gettext("Usage"), commandName,
1747836SJohn.Forte@Sun.COM 		    subcommand->name);
1757836SJohn.Forte@Sun.COM 		for (i = 0; standardSubCmdOptions[i].name; i++) {
1767836SJohn.Forte@Sun.COM 			(void) printf("-%c", standardSubCmdOptions[i].val);
1777836SJohn.Forte@Sun.COM 			if (standardSubCmdOptions[i+1].name)
1787836SJohn.Forte@Sun.COM 				(void) printf(",");
1797836SJohn.Forte@Sun.COM 		}
1807836SJohn.Forte@Sun.COM 		(void) fprintf(stdout, "]\n");
1817836SJohn.Forte@Sun.COM 		return;
1827836SJohn.Forte@Sun.COM 	}
1837836SJohn.Forte@Sun.COM 
1847836SJohn.Forte@Sun.COM 	/* print subcommand usage */
1857836SJohn.Forte@Sun.COM 	(void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName,
1867836SJohn.Forte@Sun.COM 	    subcommand->name);
1877836SJohn.Forte@Sun.COM 
1887836SJohn.Forte@Sun.COM 	/* print options if applicable */
1897836SJohn.Forte@Sun.COM 	if (subcommand->optionString != NULL) {
1907836SJohn.Forte@Sun.COM 		if (subcommand->required) {
1917836SJohn.Forte@Sun.COM 			(void) printf("%s", gettext("<"));
1927836SJohn.Forte@Sun.COM 		} else {
1937836SJohn.Forte@Sun.COM 			(void) printf("%s", gettext("["));
1947836SJohn.Forte@Sun.COM 		}
1957836SJohn.Forte@Sun.COM 		(void) printf("%s", gettext("OPTIONS"));
1967836SJohn.Forte@Sun.COM 		if (subcommand->required) {
1977836SJohn.Forte@Sun.COM 			(void) printf("%s ", gettext(">"));
1987836SJohn.Forte@Sun.COM 		} else {
1997836SJohn.Forte@Sun.COM 			(void) printf("%s ", gettext("]"));
2007836SJohn.Forte@Sun.COM 		}
2017836SJohn.Forte@Sun.COM 	}
2027836SJohn.Forte@Sun.COM 
2037836SJohn.Forte@Sun.COM 	/* print operand requirements */
2047836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE) &&
2057836SJohn.Forte@Sun.COM 	    !(subcommand->operand & OPERAND_MANDATORY)) {
2067836SJohn.Forte@Sun.COM 		(void) printf(gettext("["));
2077836SJohn.Forte@Sun.COM 	}
2087836SJohn.Forte@Sun.COM 
2097836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MANDATORY) {
2107836SJohn.Forte@Sun.COM 		(void) printf(gettext("<"));
2117836SJohn.Forte@Sun.COM 	}
2127836SJohn.Forte@Sun.COM 
2137836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE)) {
2147836SJohn.Forte@Sun.COM 		assert(subcommand->operandDefinition);
2157836SJohn.Forte@Sun.COM 		(void) printf("%s", subcommand->operandDefinition);
2167836SJohn.Forte@Sun.COM 	}
2177836SJohn.Forte@Sun.COM 
2187836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MULTIPLE) {
2197836SJohn.Forte@Sun.COM 		(void) printf(gettext(" ..."));
2207836SJohn.Forte@Sun.COM 	}
2217836SJohn.Forte@Sun.COM 
2227836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MANDATORY) {
2237836SJohn.Forte@Sun.COM 		(void) printf(gettext(">"));
2247836SJohn.Forte@Sun.COM 	}
2257836SJohn.Forte@Sun.COM 
2267836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE) &&
2277836SJohn.Forte@Sun.COM 	    !(subcommand->operand & OPERAND_MANDATORY)) {
2287836SJohn.Forte@Sun.COM 		(void) printf(gettext("]"));
2297836SJohn.Forte@Sun.COM 	}
2307836SJohn.Forte@Sun.COM 
2317836SJohn.Forte@Sun.COM 	/* print options for subcommand */
2327836SJohn.Forte@Sun.COM 	if (subcommand->optionString != NULL) {
2337836SJohn.Forte@Sun.COM 		(void) printf("\n\t%s:", gettext("OPTIONS"));
2347836SJohn.Forte@Sun.COM 		for (i = 0; i < strlen(subcommand->optionString); i++) {
2357836SJohn.Forte@Sun.COM 			assert((longOpt = getLongOption(
2367836SJohn.Forte@Sun.COM 			    subcommand->optionString[i])) != NULL);
2377836SJohn.Forte@Sun.COM 			(void) printf("\n\t\t-%c, --%s  ",
2387836SJohn.Forte@Sun.COM 			    subcommand->optionString[i],
2397836SJohn.Forte@Sun.COM 			    longOpt);
2407836SJohn.Forte@Sun.COM 			optionArgDesc =
2417836SJohn.Forte@Sun.COM 			    getOptionArgDesc(subcommand->optionString[i]);
2427836SJohn.Forte@Sun.COM 			if (optionArgDesc != NULL) {
2437836SJohn.Forte@Sun.COM 				(void) printf("<%s>", optionArgDesc);
2447836SJohn.Forte@Sun.COM 			}
2457836SJohn.Forte@Sun.COM 			if (subcommand->exclusive &&
2467836SJohn.Forte@Sun.COM 			    strchr(subcommand->exclusive,
2477836SJohn.Forte@Sun.COM 			    subcommand->optionString[i])) {
2487836SJohn.Forte@Sun.COM 				(void) printf(" (%s)", gettext("exclusive"));
2497836SJohn.Forte@Sun.COM 			}
2507836SJohn.Forte@Sun.COM 		}
2517836SJohn.Forte@Sun.COM 	}
2527836SJohn.Forte@Sun.COM 	(void) fprintf(stdout, "\n");
2539585STim.Szeto@Sun.COM 	if (subcommand->helpText) {
2549585STim.Szeto@Sun.COM 		(void) printf("%s\n", subcommand->helpText);
2559585STim.Szeto@Sun.COM 	}
2567836SJohn.Forte@Sun.COM }
2577836SJohn.Forte@Sun.COM 
2587836SJohn.Forte@Sun.COM /*
2597836SJohn.Forte@Sun.COM  * input:
2607836SJohn.Forte@Sun.COM  *  type of usage statement to print
2617836SJohn.Forte@Sun.COM  *
2627836SJohn.Forte@Sun.COM  * Returns:
2637836SJohn.Forte@Sun.COM  *  return value of subUsage
2647836SJohn.Forte@Sun.COM  */
2657836SJohn.Forte@Sun.COM static void
usage(uint_t usageType)2667836SJohn.Forte@Sun.COM usage(uint_t usageType)
2677836SJohn.Forte@Sun.COM {
2687836SJohn.Forte@Sun.COM 	int i;
2697836SJohn.Forte@Sun.COM 	subCommandProps_t *sp;
2707836SJohn.Forte@Sun.COM 
2717836SJohn.Forte@Sun.COM 	/* print general command usage */
2727836SJohn.Forte@Sun.COM 	(void) printf("%s:\t%s ", gettext("Usage"), commandName);
2737836SJohn.Forte@Sun.COM 
2747836SJohn.Forte@Sun.COM 	for (i = 0; standardCmdOptions[i].name; i++) {
2757836SJohn.Forte@Sun.COM 		(void) printf("-%c", standardCmdOptions[i].val);
2767836SJohn.Forte@Sun.COM 		if (standardCmdOptions[i+1].name)
2777836SJohn.Forte@Sun.COM 			(void) printf(",");
2787836SJohn.Forte@Sun.COM 	}
2797836SJohn.Forte@Sun.COM 
2807836SJohn.Forte@Sun.COM 	if (usageType == GENERAL_USAGE) {
2817836SJohn.Forte@Sun.COM 		for (i = 0; standardSubCmdOptions[i].name; i++) {
2827836SJohn.Forte@Sun.COM 			(void) printf(",--%s", standardSubCmdOptions[i].name);
2837836SJohn.Forte@Sun.COM 			if (standardSubCmdOptions[i+1].name)
2847836SJohn.Forte@Sun.COM 				(void) printf(",");
2857836SJohn.Forte@Sun.COM 		}
2867836SJohn.Forte@Sun.COM 	}
2877836SJohn.Forte@Sun.COM 
2887836SJohn.Forte@Sun.COM 	(void) fprintf(stdout, "\n");
2897836SJohn.Forte@Sun.COM 
2907836SJohn.Forte@Sun.COM 
2917836SJohn.Forte@Sun.COM 	/* print all subcommand usage */
2927836SJohn.Forte@Sun.COM 	for (sp = _subCommandProps; sp->name; sp++) {
2937836SJohn.Forte@Sun.COM 		subUsage(usageType, sp);
2947836SJohn.Forte@Sun.COM 	}
2957836SJohn.Forte@Sun.COM }
2967836SJohn.Forte@Sun.COM 
2977836SJohn.Forte@Sun.COM /*
2987836SJohn.Forte@Sun.COM  * input:
2997836SJohn.Forte@Sun.COM  *  execFullName - exec name of program (argv[0])
3007836SJohn.Forte@Sun.COM  *
3017836SJohn.Forte@Sun.COM  * Returns:
3027836SJohn.Forte@Sun.COM  *  command name portion of execFullName
3037836SJohn.Forte@Sun.COM  */
3047836SJohn.Forte@Sun.COM static char *
getExecBasename(char * execFullname)3057836SJohn.Forte@Sun.COM getExecBasename(char *execFullname)
3067836SJohn.Forte@Sun.COM {
3077836SJohn.Forte@Sun.COM 	char *lastSlash, *execBasename;
3087836SJohn.Forte@Sun.COM 
3097836SJohn.Forte@Sun.COM 	/* guard against '/' at end of command invocation */
3107836SJohn.Forte@Sun.COM 	for (;;) {
3117836SJohn.Forte@Sun.COM 		lastSlash = strrchr(execFullname, '/');
3127836SJohn.Forte@Sun.COM 		if (lastSlash == NULL) {
3137836SJohn.Forte@Sun.COM 			execBasename = execFullname;
3147836SJohn.Forte@Sun.COM 			break;
3157836SJohn.Forte@Sun.COM 		} else {
3167836SJohn.Forte@Sun.COM 			execBasename = lastSlash + 1;
3177836SJohn.Forte@Sun.COM 			if (*execBasename == '\0') {
3187836SJohn.Forte@Sun.COM 				*lastSlash = '\0';
3197836SJohn.Forte@Sun.COM 				continue;
3207836SJohn.Forte@Sun.COM 			}
3217836SJohn.Forte@Sun.COM 			break;
3227836SJohn.Forte@Sun.COM 		}
3237836SJohn.Forte@Sun.COM 	}
3247836SJohn.Forte@Sun.COM 	return (execBasename);
3257836SJohn.Forte@Sun.COM }
3267836SJohn.Forte@Sun.COM 
3277836SJohn.Forte@Sun.COM /*
3287836SJohn.Forte@Sun.COM  * cmdParse is a parser that checks syntax of the input command against
3297836SJohn.Forte@Sun.COM  * various rules tables.
3307836SJohn.Forte@Sun.COM  *
3317836SJohn.Forte@Sun.COM  * It provides usage feedback based upon the passed rules tables by calling
3327836SJohn.Forte@Sun.COM  * two usage functions, usage, subUsage
3337836SJohn.Forte@Sun.COM  *
3347836SJohn.Forte@Sun.COM  * When syntax is successfully validated, the associated function is called
3357836SJohn.Forte@Sun.COM  * using the subcommands table functions.
3367836SJohn.Forte@Sun.COM  *
3377836SJohn.Forte@Sun.COM  * Syntax is as follows:
3387836SJohn.Forte@Sun.COM  *	command subcommand [<options>] [<operand>]
3397836SJohn.Forte@Sun.COM  *
3407836SJohn.Forte@Sun.COM  * There are two standard short and long options assumed:
3417836SJohn.Forte@Sun.COM  *	-?, --help	Provides usage on a command or subcommand
3427836SJohn.Forte@Sun.COM  *			and stops further processing of the arguments
3437836SJohn.Forte@Sun.COM  *
3447836SJohn.Forte@Sun.COM  *	-V, --version	Provides version information on the command
3457836SJohn.Forte@Sun.COM  *			and stops further processing of the arguments
3467836SJohn.Forte@Sun.COM  *
3477836SJohn.Forte@Sun.COM  *	These options are loaded by this function.
3487836SJohn.Forte@Sun.COM  *
3497836SJohn.Forte@Sun.COM  * input:
3507836SJohn.Forte@Sun.COM  *  argc, argv from main
3517836SJohn.Forte@Sun.COM  *  syntax rules tables (synTables_t structure)
3527836SJohn.Forte@Sun.COM  *  callArgs - void * passed by caller to be passed to subcommand function
3537836SJohn.Forte@Sun.COM  *
3547836SJohn.Forte@Sun.COM  * output:
3557836SJohn.Forte@Sun.COM  *  funcRet - pointer to int that holds subcommand function return value
3567836SJohn.Forte@Sun.COM  *
3577836SJohn.Forte@Sun.COM  * Returns:
3587836SJohn.Forte@Sun.COM  *
3597836SJohn.Forte@Sun.COM  *     zero on successful syntax parse and function call
3607836SJohn.Forte@Sun.COM  *
3617836SJohn.Forte@Sun.COM  *     1 on unsuccessful syntax parse (no function has been called)
3627836SJohn.Forte@Sun.COM  *		This could be due to a version or help call or simply a
3637836SJohn.Forte@Sun.COM  *		general usage call.
3647836SJohn.Forte@Sun.COM  *
3657836SJohn.Forte@Sun.COM  *     -1 check errno, call failed
3667836SJohn.Forte@Sun.COM  *
3677836SJohn.Forte@Sun.COM  *  This module is not MT-safe.
3687836SJohn.Forte@Sun.COM  *
3697836SJohn.Forte@Sun.COM  */
3707836SJohn.Forte@Sun.COM int
cmdParse(int argc,char * argv[],synTables_t synTable,void * callArgs,int * funcRet)3717836SJohn.Forte@Sun.COM cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
3727836SJohn.Forte@Sun.COM     int *funcRet)
3737836SJohn.Forte@Sun.COM {
3747836SJohn.Forte@Sun.COM 	int	getoptargc;
3757836SJohn.Forte@Sun.COM 	char	**getoptargv;
3767836SJohn.Forte@Sun.COM 	int	opt;
3777836SJohn.Forte@Sun.COM 	int	operInd;
3787836SJohn.Forte@Sun.COM 	int	i, j;
3797836SJohn.Forte@Sun.COM 	int	len;
3807836SJohn.Forte@Sun.COM 	int	requiredOptionCnt = 0, requiredOptionEntered = 0;
3817836SJohn.Forte@Sun.COM 	char	*availOptions;
3827836SJohn.Forte@Sun.COM 	char	*versionString;
3837836SJohn.Forte@Sun.COM 	char	optionStringAll[MAXOPTIONSTRING + 1];
3847836SJohn.Forte@Sun.COM 	subCommandProps_t *subcommand;
3857836SJohn.Forte@Sun.COM 	cmdOptions_t cmdOptions[MAXOPTIONS + 1];
3867836SJohn.Forte@Sun.COM 	optionTbl_t *optionTbl;
3877836SJohn.Forte@Sun.COM 	struct option *lp;
3887836SJohn.Forte@Sun.COM 	struct option intLongOpt[MAXOPTIONS + 1];
3897836SJohn.Forte@Sun.COM 
3907836SJohn.Forte@Sun.COM 	/*
3917836SJohn.Forte@Sun.COM 	 * Check for NULLs on mandatory input arguments
3927836SJohn.Forte@Sun.COM 	 *
3937836SJohn.Forte@Sun.COM 	 * Note: longOptionTbl can be NULL in the case
3947836SJohn.Forte@Sun.COM 	 * where there is no caller defined options
3957836SJohn.Forte@Sun.COM 	 *
3967836SJohn.Forte@Sun.COM 	 */
3977836SJohn.Forte@Sun.COM 	assert(synTable.versionString);
3987836SJohn.Forte@Sun.COM 	assert(synTable.subCommandPropsTbl);
3997836SJohn.Forte@Sun.COM 	assert(funcRet);
4007836SJohn.Forte@Sun.COM 
4017836SJohn.Forte@Sun.COM 	versionString = synTable.versionString;
4027836SJohn.Forte@Sun.COM 
4037836SJohn.Forte@Sun.COM 	/* set global command name */
4047836SJohn.Forte@Sun.COM 	commandName = getExecBasename(argv[0]);
4057836SJohn.Forte@Sun.COM 
4067836SJohn.Forte@Sun.COM 	/* Set unbuffered output */
4077836SJohn.Forte@Sun.COM 	setbuf(stdout, NULL);
4087836SJohn.Forte@Sun.COM 
4097836SJohn.Forte@Sun.COM 	/* load globals */
4107836SJohn.Forte@Sun.COM 	_subCommandProps = synTable.subCommandPropsTbl;
4117836SJohn.Forte@Sun.COM 	_clientOptionTbl = synTable.longOptionTbl;
4127836SJohn.Forte@Sun.COM 
4137836SJohn.Forte@Sun.COM 	/* There must be at least two arguments */
4147836SJohn.Forte@Sun.COM 	if (argc < 2) {
4157836SJohn.Forte@Sun.COM 		usage(GENERAL_USAGE);
4167836SJohn.Forte@Sun.COM 		return (1);
4177836SJohn.Forte@Sun.COM 	}
4187836SJohn.Forte@Sun.COM 
4197836SJohn.Forte@Sun.COM 	(void) memset(&intLongOpt[0], 0, sizeof (intLongOpt));
4207836SJohn.Forte@Sun.COM 
4217836SJohn.Forte@Sun.COM 	/*
4227836SJohn.Forte@Sun.COM 	 * load standard subcommand options to internal long options table
4237836SJohn.Forte@Sun.COM 	 * Two separate getopt_long(3C) tables are used.
4247836SJohn.Forte@Sun.COM 	 */
4257836SJohn.Forte@Sun.COM 	for (i = 0; standardSubCmdOptions[i].name; i++) {
4267836SJohn.Forte@Sun.COM 		intLongOpt[i].name = standardSubCmdOptions[i].name;
4277836SJohn.Forte@Sun.COM 		intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
4287836SJohn.Forte@Sun.COM 		intLongOpt[i].flag = standardSubCmdOptions[i].flag;
4297836SJohn.Forte@Sun.COM 		intLongOpt[i].val = standardSubCmdOptions[i].val;
4307836SJohn.Forte@Sun.COM 	}
4317836SJohn.Forte@Sun.COM 
4327836SJohn.Forte@Sun.COM 	/*
4337836SJohn.Forte@Sun.COM 	 * copy caller's long options into internal long options table
4347836SJohn.Forte@Sun.COM 	 * We do this for two reasons:
4357836SJohn.Forte@Sun.COM 	 *  1) We need to use the getopt_long option structure internally
4367836SJohn.Forte@Sun.COM 	 *  2) We need to prepend the table with the standard option
4377836SJohn.Forte@Sun.COM 	 *	for all subcommands (currently -?)
4387836SJohn.Forte@Sun.COM 	 */
4397836SJohn.Forte@Sun.COM 	for (optionTbl = synTable.longOptionTbl;
4407836SJohn.Forte@Sun.COM 	    optionTbl && optionTbl->name; optionTbl++, i++) {
4417836SJohn.Forte@Sun.COM 		if (i > MAXOPTIONS - 1) {
4427836SJohn.Forte@Sun.COM 			/* option table too long */
4437836SJohn.Forte@Sun.COM 			assert(0);
4447836SJohn.Forte@Sun.COM 		}
4457836SJohn.Forte@Sun.COM 		intLongOpt[i].name = optionTbl->name;
4467836SJohn.Forte@Sun.COM 		intLongOpt[i].has_arg = optionTbl->has_arg;
4477836SJohn.Forte@Sun.COM 		intLongOpt[i].flag = NULL;
4487836SJohn.Forte@Sun.COM 		intLongOpt[i].val = optionTbl->val;
4497836SJohn.Forte@Sun.COM 	}
4507836SJohn.Forte@Sun.COM 
4517836SJohn.Forte@Sun.COM 	/* set option table global */
4527836SJohn.Forte@Sun.COM 	_longOptions = &intLongOpt[0];
4537836SJohn.Forte@Sun.COM 
4547836SJohn.Forte@Sun.COM 
4557836SJohn.Forte@Sun.COM 	/*
4567836SJohn.Forte@Sun.COM 	 * Check for help/version request immediately following command
4577836SJohn.Forte@Sun.COM 	 * '+' in option string ensures POSIX compliance in getopt_long()
4587836SJohn.Forte@Sun.COM 	 * which means that processing will stop at first non-option
4597836SJohn.Forte@Sun.COM 	 * argument.
4607836SJohn.Forte@Sun.COM 	 */
4617836SJohn.Forte@Sun.COM 	while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
4627836SJohn.Forte@Sun.COM 	    NULL)) != EOF) {
4637836SJohn.Forte@Sun.COM 		switch (opt) {
4647836SJohn.Forte@Sun.COM 			case '?':
4657836SJohn.Forte@Sun.COM 				/*
4667836SJohn.Forte@Sun.COM 				 * getopt can return a '?' when no
4677836SJohn.Forte@Sun.COM 				 * option letters match string. Check for
4687836SJohn.Forte@Sun.COM 				 * the 'real' '?' in optopt.
4697836SJohn.Forte@Sun.COM 				 */
4707836SJohn.Forte@Sun.COM 				if (optopt == '?') {
4717836SJohn.Forte@Sun.COM 					usage(DETAIL_USAGE);
4727836SJohn.Forte@Sun.COM 					exit(0);
4737836SJohn.Forte@Sun.COM 				} else {
4747836SJohn.Forte@Sun.COM 					usage(GENERAL_USAGE);
4757836SJohn.Forte@Sun.COM 					return (1);
4767836SJohn.Forte@Sun.COM 				}
4777836SJohn.Forte@Sun.COM 				break;
4787836SJohn.Forte@Sun.COM 			case 'V':
4797836SJohn.Forte@Sun.COM 				(void) fprintf(stdout, "%s: %s %s\n",
4807836SJohn.Forte@Sun.COM 				    commandName, gettext("Version"),
4817836SJohn.Forte@Sun.COM 				    versionString);
4827836SJohn.Forte@Sun.COM 				exit(0);
4837836SJohn.Forte@Sun.COM 				break;
4847836SJohn.Forte@Sun.COM 			default:
4857836SJohn.Forte@Sun.COM 				break;
4867836SJohn.Forte@Sun.COM 		}
4877836SJohn.Forte@Sun.COM 	}
4887836SJohn.Forte@Sun.COM 
4897836SJohn.Forte@Sun.COM 	/*
4907836SJohn.Forte@Sun.COM 	 * subcommand is always in the second argument. If there is no
4917836SJohn.Forte@Sun.COM 	 * recognized subcommand in the second argument, print error,
4927836SJohn.Forte@Sun.COM 	 * general usage and then return.
4937836SJohn.Forte@Sun.COM 	 */
4947836SJohn.Forte@Sun.COM 	if (getSubcommandProps(argv[1], &subcommand) != 0) {
4957836SJohn.Forte@Sun.COM 		(void) printf("%s: %s\n", commandName,
4967836SJohn.Forte@Sun.COM 		    gettext("invalid subcommand"));
4977836SJohn.Forte@Sun.COM 		usage(GENERAL_USAGE);
4987836SJohn.Forte@Sun.COM 		return (1);
4997836SJohn.Forte@Sun.COM 	}
5007836SJohn.Forte@Sun.COM 
5017836SJohn.Forte@Sun.COM 	getoptargv = argv;
5027836SJohn.Forte@Sun.COM 	getoptargv++;
5037836SJohn.Forte@Sun.COM 	getoptargc = argc;
5047836SJohn.Forte@Sun.COM 	getoptargc -= 1;
5057836SJohn.Forte@Sun.COM 
5067836SJohn.Forte@Sun.COM 	(void) memset(optionStringAll, 0, sizeof (optionStringAll));
5077836SJohn.Forte@Sun.COM 	(void) memset(&cmdOptions[0], 0, sizeof (cmdOptions));
5087836SJohn.Forte@Sun.COM 
5097836SJohn.Forte@Sun.COM 	j = 0;
5107836SJohn.Forte@Sun.COM 	/*
5117836SJohn.Forte@Sun.COM 	 * Build optionStringAll from long options table
5127836SJohn.Forte@Sun.COM 	 */
5137836SJohn.Forte@Sun.COM 	for (lp = _longOptions;  lp->name; lp++, j++) {
5147836SJohn.Forte@Sun.COM 		/* sanity check on string length */
5157836SJohn.Forte@Sun.COM 		if (j + 1 >= sizeof (optionStringAll)) {
5167836SJohn.Forte@Sun.COM 			/* option table too long */
5177836SJohn.Forte@Sun.COM 			assert(0);
5187836SJohn.Forte@Sun.COM 		}
5197836SJohn.Forte@Sun.COM 		optionStringAll[j] = lp->val;
5207836SJohn.Forte@Sun.COM 		if (lp->has_arg == required_argument) {
5217836SJohn.Forte@Sun.COM 			optionStringAll[++j] = ':';
5227836SJohn.Forte@Sun.COM 		}
5237836SJohn.Forte@Sun.COM 	}
5247836SJohn.Forte@Sun.COM 
5257836SJohn.Forte@Sun.COM 	i = 0;
5267836SJohn.Forte@Sun.COM 	/*
5277836SJohn.Forte@Sun.COM 	 * Run getopt for all arguments against all possible options
5287836SJohn.Forte@Sun.COM 	 * Store all options/option arguments in an array for retrieval
5297836SJohn.Forte@Sun.COM 	 * later.
5307836SJohn.Forte@Sun.COM 	 *
5317836SJohn.Forte@Sun.COM 	 * Once all options are retrieved, a validity check against
5327836SJohn.Forte@Sun.COM 	 * subcommand table is performed.
5337836SJohn.Forte@Sun.COM 	 */
5347836SJohn.Forte@Sun.COM 	while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
5357836SJohn.Forte@Sun.COM 	    _longOptions, NULL)) != EOF) {
5367836SJohn.Forte@Sun.COM 		switch (opt) {
5377836SJohn.Forte@Sun.COM 			case '?':
5387836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
539*10652SHyon.Kim@Sun.COM 				/*
540*10652SHyon.Kim@Sun.COM 				 * getopt can return a '?' when no
541*10652SHyon.Kim@Sun.COM 				 * option letters match string. Check for
542*10652SHyon.Kim@Sun.COM 				 * the 'real' '?' in optopt.
543*10652SHyon.Kim@Sun.COM 				 */
544*10652SHyon.Kim@Sun.COM 				if (optopt == '?') {
545*10652SHyon.Kim@Sun.COM 					exit(0);
546*10652SHyon.Kim@Sun.COM 				} else {
547*10652SHyon.Kim@Sun.COM 					exit(1);
548*10652SHyon.Kim@Sun.COM 				}
5497836SJohn.Forte@Sun.COM 			default:
5507836SJohn.Forte@Sun.COM 				cmdOptions[i].optval = opt;
5517836SJohn.Forte@Sun.COM 				if (optarg) {
5527836SJohn.Forte@Sun.COM 					len = strlen(optarg);
5537836SJohn.Forte@Sun.COM 					if (len > sizeof (cmdOptions[i].optarg)
5547836SJohn.Forte@Sun.COM 					    - 1) {
5557836SJohn.Forte@Sun.COM 						(void) printf("%s: %s\n",
5567836SJohn.Forte@Sun.COM 						    commandName,
5577836SJohn.Forte@Sun.COM 						    gettext("option too long"));
5587836SJohn.Forte@Sun.COM 						errno = EINVAL;
5597836SJohn.Forte@Sun.COM 						return (-1);
5607836SJohn.Forte@Sun.COM 					}
5617836SJohn.Forte@Sun.COM 					(void) strncpy(cmdOptions[i].optarg,
5627836SJohn.Forte@Sun.COM 					    optarg, len);
5637836SJohn.Forte@Sun.COM 				}
5647836SJohn.Forte@Sun.COM 				i++;
5657836SJohn.Forte@Sun.COM 				break;
5667836SJohn.Forte@Sun.COM 		}
5677836SJohn.Forte@Sun.COM 	}
5687836SJohn.Forte@Sun.COM 
5697836SJohn.Forte@Sun.COM 	/*
5707836SJohn.Forte@Sun.COM 	 * increment past last option
5717836SJohn.Forte@Sun.COM 	 */
5727836SJohn.Forte@Sun.COM 	operInd = optind + 1;
5737836SJohn.Forte@Sun.COM 
5747836SJohn.Forte@Sun.COM 	/*
5757836SJohn.Forte@Sun.COM 	 * Check validity of given options, if any were given
5767836SJohn.Forte@Sun.COM 	 */
5777836SJohn.Forte@Sun.COM 
5787836SJohn.Forte@Sun.COM 	/* get option string for this subcommand */
5797836SJohn.Forte@Sun.COM 	availOptions = subcommand->optionString;
5807836SJohn.Forte@Sun.COM 
5817836SJohn.Forte@Sun.COM 	/* Get count of required options */
5827836SJohn.Forte@Sun.COM 	if (subcommand->required) {
5837836SJohn.Forte@Sun.COM 		requiredOptionCnt = strlen(subcommand->required);
5847836SJohn.Forte@Sun.COM 	}
5857836SJohn.Forte@Sun.COM 
5867836SJohn.Forte@Sun.COM 	if (cmdOptions[0].optval != 0) { /* options were input */
5877836SJohn.Forte@Sun.COM 		if (availOptions == NULL) { /* no options permitted */
5887836SJohn.Forte@Sun.COM 			(void) printf("%s: %s\n", commandName,
5897836SJohn.Forte@Sun.COM 			    gettext("no options permitted"));
5907836SJohn.Forte@Sun.COM 			subUsage(DETAIL_USAGE, subcommand);
5917836SJohn.Forte@Sun.COM 			return (1);
5927836SJohn.Forte@Sun.COM 		}
5937836SJohn.Forte@Sun.COM 		for (i = 0; cmdOptions[i].optval; i++) {
5947836SJohn.Forte@Sun.COM 			/* is the option in the available option string? */
5957836SJohn.Forte@Sun.COM 			if (!(strchr(availOptions, cmdOptions[i].optval))) {
5967836SJohn.Forte@Sun.COM 				(void) printf("%s: '-%c': %s\n", commandName,
5977836SJohn.Forte@Sun.COM 				    cmdOptions[i].optval,
5987836SJohn.Forte@Sun.COM 				    gettext("invalid option"));
5997836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
6007836SJohn.Forte@Sun.COM 				return (1);
6017836SJohn.Forte@Sun.COM 			/* increment required options entered */
6027836SJohn.Forte@Sun.COM 			} else if (subcommand->required &&
6037836SJohn.Forte@Sun.COM 			    (strchr(subcommand->required,
6047836SJohn.Forte@Sun.COM 			    cmdOptions[i].optval))) {
6057836SJohn.Forte@Sun.COM 				requiredOptionEntered++;
6067836SJohn.Forte@Sun.COM 			/* Check for exclusive options */
6077836SJohn.Forte@Sun.COM 			} else if (cmdOptions[1].optval != 0 &&
6087836SJohn.Forte@Sun.COM 			    subcommand->exclusive &&
6097836SJohn.Forte@Sun.COM 			    strchr(subcommand->exclusive,
6107836SJohn.Forte@Sun.COM 			    cmdOptions[i].optval)) {
6117836SJohn.Forte@Sun.COM 					(void) printf("%s: '-%c': %s\n",
6127836SJohn.Forte@Sun.COM 					    commandName, cmdOptions[i].optval,
6137836SJohn.Forte@Sun.COM 					    gettext("is an exclusive option"));
6147836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
6157836SJohn.Forte@Sun.COM 					return (1);
6167836SJohn.Forte@Sun.COM 			}
6177836SJohn.Forte@Sun.COM 		}
6187836SJohn.Forte@Sun.COM 	} else { /* no options were input */
6197836SJohn.Forte@Sun.COM 		if (availOptions != NULL && subcommand->required) {
6207836SJohn.Forte@Sun.COM 			(void) printf("%s: %s\n", commandName,
6217836SJohn.Forte@Sun.COM 			    gettext("at least one option required"));
6227836SJohn.Forte@Sun.COM 			subUsage(DETAIL_USAGE, subcommand);
6237836SJohn.Forte@Sun.COM 			return (1);
6247836SJohn.Forte@Sun.COM 		}
6257836SJohn.Forte@Sun.COM 	}
6267836SJohn.Forte@Sun.COM 
6277836SJohn.Forte@Sun.COM 	/* Were all required options entered? */
6287836SJohn.Forte@Sun.COM 	if (requiredOptionEntered != requiredOptionCnt) {
6297836SJohn.Forte@Sun.COM 		(void) printf("%s: %s: %s\n", commandName,
6307836SJohn.Forte@Sun.COM 		    gettext("Following option(s) required"),
6317836SJohn.Forte@Sun.COM 		    subcommand->required);
6327836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
6337836SJohn.Forte@Sun.COM 		return (1);
6347836SJohn.Forte@Sun.COM 	}
6357836SJohn.Forte@Sun.COM 
6367836SJohn.Forte@Sun.COM 
6377836SJohn.Forte@Sun.COM 	/*
6387836SJohn.Forte@Sun.COM 	 * If there are no operands,
6397836SJohn.Forte@Sun.COM 	 * check to see if this is okay
6407836SJohn.Forte@Sun.COM 	 */
6417836SJohn.Forte@Sun.COM 	if ((operInd == argc) &&
6427836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_MANDATORY)) {
6437836SJohn.Forte@Sun.COM 		(void) printf("%s: %s %s\n", commandName, subcommand->name,
6447836SJohn.Forte@Sun.COM 		    gettext("requires an operand"));
6457836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
6467836SJohn.Forte@Sun.COM 		return (1);
6477836SJohn.Forte@Sun.COM 	}
6487836SJohn.Forte@Sun.COM 
6497836SJohn.Forte@Sun.COM 	/*
6507836SJohn.Forte@Sun.COM 	 * If there are more operands,
6517836SJohn.Forte@Sun.COM 	 * check to see if this is okay
6527836SJohn.Forte@Sun.COM 	 */
6537836SJohn.Forte@Sun.COM 	if ((argc > operInd) &&
6547836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_NONE)) {
6557836SJohn.Forte@Sun.COM 		(void) fprintf(stderr, "%s: %s %s\n", commandName,
6567836SJohn.Forte@Sun.COM 		    subcommand->name, gettext("takes no operands"));
6577836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
6587836SJohn.Forte@Sun.COM 		return (1);
6597836SJohn.Forte@Sun.COM 	}
6607836SJohn.Forte@Sun.COM 
6617836SJohn.Forte@Sun.COM 	/*
6627836SJohn.Forte@Sun.COM 	 * If there is more than one more operand,
6637836SJohn.Forte@Sun.COM 	 * check to see if this is okay
6647836SJohn.Forte@Sun.COM 	 */
6657836SJohn.Forte@Sun.COM 	if ((argc > operInd) && ((argc - operInd) != 1) &&
6667836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_SINGLE)) {
6677836SJohn.Forte@Sun.COM 		(void) printf("%s: %s %s\n", commandName,
6687836SJohn.Forte@Sun.COM 		    subcommand->name, gettext("accepts only a single operand"));
6697836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
6707836SJohn.Forte@Sun.COM 		return (1);
6717836SJohn.Forte@Sun.COM 	}
6727836SJohn.Forte@Sun.COM 
6737836SJohn.Forte@Sun.COM 	/* Finished syntax checks */
6747836SJohn.Forte@Sun.COM 
6757836SJohn.Forte@Sun.COM 
6767836SJohn.Forte@Sun.COM 	/* Call appropriate function */
6777836SJohn.Forte@Sun.COM 	*funcRet = subcommand->handler(argc - operInd, &argv[operInd],
6787836SJohn.Forte@Sun.COM 	    &cmdOptions[0], callArgs);
6797836SJohn.Forte@Sun.COM 
6807836SJohn.Forte@Sun.COM 	return (0);
6817836SJohn.Forte@Sun.COM }
682