1*7836SJohn.Forte@Sun.COM /*
2*7836SJohn.Forte@Sun.COM  * CDDL HEADER START
3*7836SJohn.Forte@Sun.COM  *
4*7836SJohn.Forte@Sun.COM  * The contents of this file are subject to the terms of the
5*7836SJohn.Forte@Sun.COM  * Common Development and Distribution License (the "License").
6*7836SJohn.Forte@Sun.COM  * You may not use this file except in compliance with the License.
7*7836SJohn.Forte@Sun.COM  *
8*7836SJohn.Forte@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*7836SJohn.Forte@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*7836SJohn.Forte@Sun.COM  * See the License for the specific language governing permissions
11*7836SJohn.Forte@Sun.COM  * and limitations under the License.
12*7836SJohn.Forte@Sun.COM  *
13*7836SJohn.Forte@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*7836SJohn.Forte@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*7836SJohn.Forte@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*7836SJohn.Forte@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*7836SJohn.Forte@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*7836SJohn.Forte@Sun.COM  *
19*7836SJohn.Forte@Sun.COM  * CDDL HEADER END
20*7836SJohn.Forte@Sun.COM  */
21*7836SJohn.Forte@Sun.COM /*
22*7836SJohn.Forte@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*7836SJohn.Forte@Sun.COM  * Use is subject to license terms.
24*7836SJohn.Forte@Sun.COM  */
25*7836SJohn.Forte@Sun.COM 
26*7836SJohn.Forte@Sun.COM #include <stdlib.h>
27*7836SJohn.Forte@Sun.COM #include <stdio.h>
28*7836SJohn.Forte@Sun.COM #include <sys/types.h>
29*7836SJohn.Forte@Sun.COM #include <unistd.h>
30*7836SJohn.Forte@Sun.COM #include <libintl.h>
31*7836SJohn.Forte@Sun.COM #include <errno.h>
32*7836SJohn.Forte@Sun.COM #include <string.h>
33*7836SJohn.Forte@Sun.COM #include <assert.h>
34*7836SJohn.Forte@Sun.COM #include <getopt.h>
35*7836SJohn.Forte@Sun.COM #include <cmdparse.h>
36*7836SJohn.Forte@Sun.COM 
37*7836SJohn.Forte@Sun.COM 
38*7836SJohn.Forte@Sun.COM /* Usage types */
39*7836SJohn.Forte@Sun.COM #define	GENERAL_USAGE	1
40*7836SJohn.Forte@Sun.COM #define	DETAIL_USAGE	2
41*7836SJohn.Forte@Sun.COM 
42*7836SJohn.Forte@Sun.COM /* printable ascii character set len */
43*7836SJohn.Forte@Sun.COM #define	MAXOPTIONS	(uint_t)('~' - '!' + 1)
44*7836SJohn.Forte@Sun.COM 
45*7836SJohn.Forte@Sun.COM /*
46*7836SJohn.Forte@Sun.COM  * MAXOPTIONSTRING is the max length of the options string used in getopt and
47*7836SJohn.Forte@Sun.COM  * will be the printable character set + ':' for each character,
48*7836SJohn.Forte@Sun.COM  * providing for options with arguments. e.g. "t:Cs:hglr:"
49*7836SJohn.Forte@Sun.COM  */
50*7836SJohn.Forte@Sun.COM #define	MAXOPTIONSTRING		MAXOPTIONS * 2
51*7836SJohn.Forte@Sun.COM 
52*7836SJohn.Forte@Sun.COM /* standard command options table to support -?, -V */
53*7836SJohn.Forte@Sun.COM struct option standardCmdOptions[] = {
54*7836SJohn.Forte@Sun.COM 	{"help", no_argument, NULL, '?'},
55*7836SJohn.Forte@Sun.COM 	{"version", no_argument, NULL, 'V'},
56*7836SJohn.Forte@Sun.COM 	{NULL, 0, NULL, 0}
57*7836SJohn.Forte@Sun.COM };
58*7836SJohn.Forte@Sun.COM 
59*7836SJohn.Forte@Sun.COM /* standard subcommand options table to support -? */
60*7836SJohn.Forte@Sun.COM struct option standardSubCmdOptions[] = {
61*7836SJohn.Forte@Sun.COM 	{"help", no_argument, NULL, '?'},
62*7836SJohn.Forte@Sun.COM 	{NULL, 0, NULL, 0}
63*7836SJohn.Forte@Sun.COM };
64*7836SJohn.Forte@Sun.COM 
65*7836SJohn.Forte@Sun.COM /* forward declarations */
66*7836SJohn.Forte@Sun.COM static int getSubcommandProps(char *, subCommandProps_t **);
67*7836SJohn.Forte@Sun.COM static char *getExecBasename(char *);
68*7836SJohn.Forte@Sun.COM static void usage(uint_t);
69*7836SJohn.Forte@Sun.COM static void subUsage(uint_t, subCommandProps_t *);
70*7836SJohn.Forte@Sun.COM static char *getLongOption(int);
71*7836SJohn.Forte@Sun.COM static char *getOptionArgDesc(int);
72*7836SJohn.Forte@Sun.COM 
73*7836SJohn.Forte@Sun.COM /* global data */
74*7836SJohn.Forte@Sun.COM static struct option *_longOptions;
75*7836SJohn.Forte@Sun.COM static subCommandProps_t *_subCommandProps;
76*7836SJohn.Forte@Sun.COM static optionTbl_t *_clientOptionTbl;
77*7836SJohn.Forte@Sun.COM static char *commandName;
78*7836SJohn.Forte@Sun.COM 
79*7836SJohn.Forte@Sun.COM 
80*7836SJohn.Forte@Sun.COM /*
81*7836SJohn.Forte@Sun.COM  * input:
82*7836SJohn.Forte@Sun.COM  *  subCommand - subcommand value
83*7836SJohn.Forte@Sun.COM  * output:
84*7836SJohn.Forte@Sun.COM  *  subCommandProps - pointer to subCommandProps_t structure allocated by caller
85*7836SJohn.Forte@Sun.COM  *
86*7836SJohn.Forte@Sun.COM  * On successful return, subCommandProps contains the properties for the value
87*7836SJohn.Forte@Sun.COM  * in subCommand. On failure, the contents of subCommandProps is unspecified.
88*7836SJohn.Forte@Sun.COM  *
89*7836SJohn.Forte@Sun.COM  * Returns:
90*7836SJohn.Forte@Sun.COM  *  zero on success
91*7836SJohn.Forte@Sun.COM  *  non-zero on failure
92*7836SJohn.Forte@Sun.COM  *
93*7836SJohn.Forte@Sun.COM  */
94*7836SJohn.Forte@Sun.COM static int
95*7836SJohn.Forte@Sun.COM getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps)
96*7836SJohn.Forte@Sun.COM {
97*7836SJohn.Forte@Sun.COM 	subCommandProps_t *sp;
98*7836SJohn.Forte@Sun.COM 	int len;
99*7836SJohn.Forte@Sun.COM 
100*7836SJohn.Forte@Sun.COM 	for (sp = _subCommandProps; sp->name; sp++) {
101*7836SJohn.Forte@Sun.COM 		len = strlen(subCommand);
102*7836SJohn.Forte@Sun.COM 		if (len == strlen(sp->name) &&
103*7836SJohn.Forte@Sun.COM 		    strncasecmp(subCommand, sp->name, len) == 0) {
104*7836SJohn.Forte@Sun.COM 			*subCommandProps = sp;
105*7836SJohn.Forte@Sun.COM 			return (0);
106*7836SJohn.Forte@Sun.COM 		}
107*7836SJohn.Forte@Sun.COM 	}
108*7836SJohn.Forte@Sun.COM 	return (1);
109*7836SJohn.Forte@Sun.COM }
110*7836SJohn.Forte@Sun.COM 
111*7836SJohn.Forte@Sun.COM /*
112*7836SJohn.Forte@Sun.COM  * input:
113*7836SJohn.Forte@Sun.COM  *  shortOption - short option character for which to return the
114*7836SJohn.Forte@Sun.COM  *	associated long option string
115*7836SJohn.Forte@Sun.COM  *
116*7836SJohn.Forte@Sun.COM  * Returns:
117*7836SJohn.Forte@Sun.COM  *  on success, long option name
118*7836SJohn.Forte@Sun.COM  *  on failure, NULL
119*7836SJohn.Forte@Sun.COM  */
120*7836SJohn.Forte@Sun.COM static char *
121*7836SJohn.Forte@Sun.COM getLongOption(int shortOption)
122*7836SJohn.Forte@Sun.COM {
123*7836SJohn.Forte@Sun.COM 	struct option *op;
124*7836SJohn.Forte@Sun.COM 	for (op = _longOptions; op->name; op++) {
125*7836SJohn.Forte@Sun.COM 		if (shortOption == op->val) {
126*7836SJohn.Forte@Sun.COM 			return (op->name);
127*7836SJohn.Forte@Sun.COM 		}
128*7836SJohn.Forte@Sun.COM 	}
129*7836SJohn.Forte@Sun.COM 	return (NULL);
130*7836SJohn.Forte@Sun.COM }
131*7836SJohn.Forte@Sun.COM 
132*7836SJohn.Forte@Sun.COM /*
133*7836SJohn.Forte@Sun.COM  * input
134*7836SJohn.Forte@Sun.COM  *  shortOption - short option character for which to return the
135*7836SJohn.Forte@Sun.COM  *	option argument
136*7836SJohn.Forte@Sun.COM  * Returns:
137*7836SJohn.Forte@Sun.COM  *  on success, argument string
138*7836SJohn.Forte@Sun.COM  *  on failure, NULL
139*7836SJohn.Forte@Sun.COM  */
140*7836SJohn.Forte@Sun.COM static char *
141*7836SJohn.Forte@Sun.COM getOptionArgDesc(int shortOption)
142*7836SJohn.Forte@Sun.COM {
143*7836SJohn.Forte@Sun.COM 	optionTbl_t *op;
144*7836SJohn.Forte@Sun.COM 	for (op = _clientOptionTbl; op->name; op++) {
145*7836SJohn.Forte@Sun.COM 		if (op->val == shortOption &&
146*7836SJohn.Forte@Sun.COM 		    op->has_arg == required_argument) {
147*7836SJohn.Forte@Sun.COM 			return (op->argDesc);
148*7836SJohn.Forte@Sun.COM 		}
149*7836SJohn.Forte@Sun.COM 	}
150*7836SJohn.Forte@Sun.COM 	return (NULL);
151*7836SJohn.Forte@Sun.COM }
152*7836SJohn.Forte@Sun.COM 
153*7836SJohn.Forte@Sun.COM 
154*7836SJohn.Forte@Sun.COM /*
155*7836SJohn.Forte@Sun.COM  * Print usage for a subcommand.
156*7836SJohn.Forte@Sun.COM  *
157*7836SJohn.Forte@Sun.COM  * input:
158*7836SJohn.Forte@Sun.COM  *  usage type - GENERAL_USAGE, DETAIL_USAGE
159*7836SJohn.Forte@Sun.COM  *  subcommand - pointer to subCommandProps_t structure
160*7836SJohn.Forte@Sun.COM  *
161*7836SJohn.Forte@Sun.COM  * Returns:
162*7836SJohn.Forte@Sun.COM  *  none
163*7836SJohn.Forte@Sun.COM  *
164*7836SJohn.Forte@Sun.COM  */
165*7836SJohn.Forte@Sun.COM static void
166*7836SJohn.Forte@Sun.COM subUsage(uint_t usageType, subCommandProps_t *subcommand)
167*7836SJohn.Forte@Sun.COM {
168*7836SJohn.Forte@Sun.COM 	int i;
169*7836SJohn.Forte@Sun.COM 	char *optionArgDesc;
170*7836SJohn.Forte@Sun.COM 	char *longOpt;
171*7836SJohn.Forte@Sun.COM 
172*7836SJohn.Forte@Sun.COM 	if (usageType == GENERAL_USAGE) {
173*7836SJohn.Forte@Sun.COM 		(void) printf("%s:\t%s %s [", gettext("Usage"), commandName,
174*7836SJohn.Forte@Sun.COM 		    subcommand->name);
175*7836SJohn.Forte@Sun.COM 		for (i = 0; standardSubCmdOptions[i].name; i++) {
176*7836SJohn.Forte@Sun.COM 			(void) printf("-%c", standardSubCmdOptions[i].val);
177*7836SJohn.Forte@Sun.COM 			if (standardSubCmdOptions[i+1].name)
178*7836SJohn.Forte@Sun.COM 				(void) printf(",");
179*7836SJohn.Forte@Sun.COM 		}
180*7836SJohn.Forte@Sun.COM 		(void) fprintf(stdout, "]\n");
181*7836SJohn.Forte@Sun.COM 		return;
182*7836SJohn.Forte@Sun.COM 	}
183*7836SJohn.Forte@Sun.COM 
184*7836SJohn.Forte@Sun.COM 	/* print subcommand usage */
185*7836SJohn.Forte@Sun.COM 	(void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName,
186*7836SJohn.Forte@Sun.COM 	    subcommand->name);
187*7836SJohn.Forte@Sun.COM 
188*7836SJohn.Forte@Sun.COM 	/* print options if applicable */
189*7836SJohn.Forte@Sun.COM 	if (subcommand->optionString != NULL) {
190*7836SJohn.Forte@Sun.COM 		if (subcommand->required) {
191*7836SJohn.Forte@Sun.COM 			(void) printf("%s", gettext("<"));
192*7836SJohn.Forte@Sun.COM 		} else {
193*7836SJohn.Forte@Sun.COM 			(void) printf("%s", gettext("["));
194*7836SJohn.Forte@Sun.COM 		}
195*7836SJohn.Forte@Sun.COM 		(void) printf("%s", gettext("OPTIONS"));
196*7836SJohn.Forte@Sun.COM 		if (subcommand->required) {
197*7836SJohn.Forte@Sun.COM 			(void) printf("%s ", gettext(">"));
198*7836SJohn.Forte@Sun.COM 		} else {
199*7836SJohn.Forte@Sun.COM 			(void) printf("%s ", gettext("]"));
200*7836SJohn.Forte@Sun.COM 		}
201*7836SJohn.Forte@Sun.COM 	}
202*7836SJohn.Forte@Sun.COM 
203*7836SJohn.Forte@Sun.COM 	/* print operand requirements */
204*7836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE) &&
205*7836SJohn.Forte@Sun.COM 	    !(subcommand->operand & OPERAND_MANDATORY)) {
206*7836SJohn.Forte@Sun.COM 		(void) printf(gettext("["));
207*7836SJohn.Forte@Sun.COM 	}
208*7836SJohn.Forte@Sun.COM 
209*7836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MANDATORY) {
210*7836SJohn.Forte@Sun.COM 		(void) printf(gettext("<"));
211*7836SJohn.Forte@Sun.COM 	}
212*7836SJohn.Forte@Sun.COM 
213*7836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE)) {
214*7836SJohn.Forte@Sun.COM 		assert(subcommand->operandDefinition);
215*7836SJohn.Forte@Sun.COM 		(void) printf("%s", subcommand->operandDefinition);
216*7836SJohn.Forte@Sun.COM 	}
217*7836SJohn.Forte@Sun.COM 
218*7836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MULTIPLE) {
219*7836SJohn.Forte@Sun.COM 		(void) printf(gettext(" ..."));
220*7836SJohn.Forte@Sun.COM 	}
221*7836SJohn.Forte@Sun.COM 
222*7836SJohn.Forte@Sun.COM 	if (subcommand->operand & OPERAND_MANDATORY) {
223*7836SJohn.Forte@Sun.COM 		(void) printf(gettext(">"));
224*7836SJohn.Forte@Sun.COM 	}
225*7836SJohn.Forte@Sun.COM 
226*7836SJohn.Forte@Sun.COM 	if (!(subcommand->operand & OPERAND_NONE) &&
227*7836SJohn.Forte@Sun.COM 	    !(subcommand->operand & OPERAND_MANDATORY)) {
228*7836SJohn.Forte@Sun.COM 		(void) printf(gettext("]"));
229*7836SJohn.Forte@Sun.COM 	}
230*7836SJohn.Forte@Sun.COM 
231*7836SJohn.Forte@Sun.COM 	/* print options for subcommand */
232*7836SJohn.Forte@Sun.COM 	if (subcommand->optionString != NULL) {
233*7836SJohn.Forte@Sun.COM 		(void) printf("\n\t%s:", gettext("OPTIONS"));
234*7836SJohn.Forte@Sun.COM 		for (i = 0; i < strlen(subcommand->optionString); i++) {
235*7836SJohn.Forte@Sun.COM 			assert((longOpt = getLongOption(
236*7836SJohn.Forte@Sun.COM 			    subcommand->optionString[i])) != NULL);
237*7836SJohn.Forte@Sun.COM 			(void) printf("\n\t\t-%c, --%s  ",
238*7836SJohn.Forte@Sun.COM 			    subcommand->optionString[i],
239*7836SJohn.Forte@Sun.COM 			    longOpt);
240*7836SJohn.Forte@Sun.COM 			optionArgDesc =
241*7836SJohn.Forte@Sun.COM 			    getOptionArgDesc(subcommand->optionString[i]);
242*7836SJohn.Forte@Sun.COM 			if (optionArgDesc != NULL) {
243*7836SJohn.Forte@Sun.COM 				(void) printf("<%s>", optionArgDesc);
244*7836SJohn.Forte@Sun.COM 			}
245*7836SJohn.Forte@Sun.COM 			if (subcommand->exclusive &&
246*7836SJohn.Forte@Sun.COM 			    strchr(subcommand->exclusive,
247*7836SJohn.Forte@Sun.COM 			    subcommand->optionString[i])) {
248*7836SJohn.Forte@Sun.COM 				(void) printf(" (%s)", gettext("exclusive"));
249*7836SJohn.Forte@Sun.COM 			}
250*7836SJohn.Forte@Sun.COM 		}
251*7836SJohn.Forte@Sun.COM 	}
252*7836SJohn.Forte@Sun.COM 	(void) fprintf(stdout, "\n");
253*7836SJohn.Forte@Sun.COM }
254*7836SJohn.Forte@Sun.COM 
255*7836SJohn.Forte@Sun.COM /*
256*7836SJohn.Forte@Sun.COM  * input:
257*7836SJohn.Forte@Sun.COM  *  type of usage statement to print
258*7836SJohn.Forte@Sun.COM  *
259*7836SJohn.Forte@Sun.COM  * Returns:
260*7836SJohn.Forte@Sun.COM  *  return value of subUsage
261*7836SJohn.Forte@Sun.COM  */
262*7836SJohn.Forte@Sun.COM static void
263*7836SJohn.Forte@Sun.COM usage(uint_t usageType)
264*7836SJohn.Forte@Sun.COM {
265*7836SJohn.Forte@Sun.COM 	int i;
266*7836SJohn.Forte@Sun.COM 	subCommandProps_t *sp;
267*7836SJohn.Forte@Sun.COM 
268*7836SJohn.Forte@Sun.COM 	/* print general command usage */
269*7836SJohn.Forte@Sun.COM 	(void) printf("%s:\t%s ", gettext("Usage"), commandName);
270*7836SJohn.Forte@Sun.COM 
271*7836SJohn.Forte@Sun.COM 	for (i = 0; standardCmdOptions[i].name; i++) {
272*7836SJohn.Forte@Sun.COM 		(void) printf("-%c", standardCmdOptions[i].val);
273*7836SJohn.Forte@Sun.COM 		if (standardCmdOptions[i+1].name)
274*7836SJohn.Forte@Sun.COM 			(void) printf(",");
275*7836SJohn.Forte@Sun.COM 	}
276*7836SJohn.Forte@Sun.COM 
277*7836SJohn.Forte@Sun.COM 	if (usageType == GENERAL_USAGE) {
278*7836SJohn.Forte@Sun.COM 		for (i = 0; standardSubCmdOptions[i].name; i++) {
279*7836SJohn.Forte@Sun.COM 			(void) printf(",--%s", standardSubCmdOptions[i].name);
280*7836SJohn.Forte@Sun.COM 			if (standardSubCmdOptions[i+1].name)
281*7836SJohn.Forte@Sun.COM 				(void) printf(",");
282*7836SJohn.Forte@Sun.COM 		}
283*7836SJohn.Forte@Sun.COM 	}
284*7836SJohn.Forte@Sun.COM 
285*7836SJohn.Forte@Sun.COM 	(void) fprintf(stdout, "\n");
286*7836SJohn.Forte@Sun.COM 
287*7836SJohn.Forte@Sun.COM 
288*7836SJohn.Forte@Sun.COM 	/* print all subcommand usage */
289*7836SJohn.Forte@Sun.COM 	for (sp = _subCommandProps; sp->name; sp++) {
290*7836SJohn.Forte@Sun.COM 		subUsage(usageType, sp);
291*7836SJohn.Forte@Sun.COM 	}
292*7836SJohn.Forte@Sun.COM }
293*7836SJohn.Forte@Sun.COM 
294*7836SJohn.Forte@Sun.COM /*
295*7836SJohn.Forte@Sun.COM  * input:
296*7836SJohn.Forte@Sun.COM  *  execFullName - exec name of program (argv[0])
297*7836SJohn.Forte@Sun.COM  *
298*7836SJohn.Forte@Sun.COM  * Returns:
299*7836SJohn.Forte@Sun.COM  *  command name portion of execFullName
300*7836SJohn.Forte@Sun.COM  */
301*7836SJohn.Forte@Sun.COM static char *
302*7836SJohn.Forte@Sun.COM getExecBasename(char *execFullname)
303*7836SJohn.Forte@Sun.COM {
304*7836SJohn.Forte@Sun.COM 	char *lastSlash, *execBasename;
305*7836SJohn.Forte@Sun.COM 
306*7836SJohn.Forte@Sun.COM 	/* guard against '/' at end of command invocation */
307*7836SJohn.Forte@Sun.COM 	for (;;) {
308*7836SJohn.Forte@Sun.COM 		lastSlash = strrchr(execFullname, '/');
309*7836SJohn.Forte@Sun.COM 		if (lastSlash == NULL) {
310*7836SJohn.Forte@Sun.COM 			execBasename = execFullname;
311*7836SJohn.Forte@Sun.COM 			break;
312*7836SJohn.Forte@Sun.COM 		} else {
313*7836SJohn.Forte@Sun.COM 			execBasename = lastSlash + 1;
314*7836SJohn.Forte@Sun.COM 			if (*execBasename == '\0') {
315*7836SJohn.Forte@Sun.COM 				*lastSlash = '\0';
316*7836SJohn.Forte@Sun.COM 				continue;
317*7836SJohn.Forte@Sun.COM 			}
318*7836SJohn.Forte@Sun.COM 			break;
319*7836SJohn.Forte@Sun.COM 		}
320*7836SJohn.Forte@Sun.COM 	}
321*7836SJohn.Forte@Sun.COM 	return (execBasename);
322*7836SJohn.Forte@Sun.COM }
323*7836SJohn.Forte@Sun.COM 
324*7836SJohn.Forte@Sun.COM /*
325*7836SJohn.Forte@Sun.COM  * cmdParse is a parser that checks syntax of the input command against
326*7836SJohn.Forte@Sun.COM  * various rules tables.
327*7836SJohn.Forte@Sun.COM  *
328*7836SJohn.Forte@Sun.COM  * It provides usage feedback based upon the passed rules tables by calling
329*7836SJohn.Forte@Sun.COM  * two usage functions, usage, subUsage
330*7836SJohn.Forte@Sun.COM  *
331*7836SJohn.Forte@Sun.COM  * When syntax is successfully validated, the associated function is called
332*7836SJohn.Forte@Sun.COM  * using the subcommands table functions.
333*7836SJohn.Forte@Sun.COM  *
334*7836SJohn.Forte@Sun.COM  * Syntax is as follows:
335*7836SJohn.Forte@Sun.COM  *	command subcommand [<options>] [<operand>]
336*7836SJohn.Forte@Sun.COM  *
337*7836SJohn.Forte@Sun.COM  * There are two standard short and long options assumed:
338*7836SJohn.Forte@Sun.COM  *	-?, --help	Provides usage on a command or subcommand
339*7836SJohn.Forte@Sun.COM  *			and stops further processing of the arguments
340*7836SJohn.Forte@Sun.COM  *
341*7836SJohn.Forte@Sun.COM  *	-V, --version	Provides version information on the command
342*7836SJohn.Forte@Sun.COM  *			and stops further processing of the arguments
343*7836SJohn.Forte@Sun.COM  *
344*7836SJohn.Forte@Sun.COM  *	These options are loaded by this function.
345*7836SJohn.Forte@Sun.COM  *
346*7836SJohn.Forte@Sun.COM  * input:
347*7836SJohn.Forte@Sun.COM  *  argc, argv from main
348*7836SJohn.Forte@Sun.COM  *  syntax rules tables (synTables_t structure)
349*7836SJohn.Forte@Sun.COM  *  callArgs - void * passed by caller to be passed to subcommand function
350*7836SJohn.Forte@Sun.COM  *
351*7836SJohn.Forte@Sun.COM  * output:
352*7836SJohn.Forte@Sun.COM  *  funcRet - pointer to int that holds subcommand function return value
353*7836SJohn.Forte@Sun.COM  *
354*7836SJohn.Forte@Sun.COM  * Returns:
355*7836SJohn.Forte@Sun.COM  *
356*7836SJohn.Forte@Sun.COM  *     zero on successful syntax parse and function call
357*7836SJohn.Forte@Sun.COM  *
358*7836SJohn.Forte@Sun.COM  *     1 on unsuccessful syntax parse (no function has been called)
359*7836SJohn.Forte@Sun.COM  *		This could be due to a version or help call or simply a
360*7836SJohn.Forte@Sun.COM  *		general usage call.
361*7836SJohn.Forte@Sun.COM  *
362*7836SJohn.Forte@Sun.COM  *     -1 check errno, call failed
363*7836SJohn.Forte@Sun.COM  *
364*7836SJohn.Forte@Sun.COM  *  This module is not MT-safe.
365*7836SJohn.Forte@Sun.COM  *
366*7836SJohn.Forte@Sun.COM  */
367*7836SJohn.Forte@Sun.COM int
368*7836SJohn.Forte@Sun.COM cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
369*7836SJohn.Forte@Sun.COM     int *funcRet)
370*7836SJohn.Forte@Sun.COM {
371*7836SJohn.Forte@Sun.COM 	int	getoptargc;
372*7836SJohn.Forte@Sun.COM 	char	**getoptargv;
373*7836SJohn.Forte@Sun.COM 	int	opt;
374*7836SJohn.Forte@Sun.COM 	int	operInd;
375*7836SJohn.Forte@Sun.COM 	int	i, j;
376*7836SJohn.Forte@Sun.COM 	int	len;
377*7836SJohn.Forte@Sun.COM 	int	requiredOptionCnt = 0, requiredOptionEntered = 0;
378*7836SJohn.Forte@Sun.COM 	char	*availOptions;
379*7836SJohn.Forte@Sun.COM 	char	*versionString;
380*7836SJohn.Forte@Sun.COM 	char	optionStringAll[MAXOPTIONSTRING + 1];
381*7836SJohn.Forte@Sun.COM 	subCommandProps_t *subcommand;
382*7836SJohn.Forte@Sun.COM 	cmdOptions_t cmdOptions[MAXOPTIONS + 1];
383*7836SJohn.Forte@Sun.COM 	optionTbl_t *optionTbl;
384*7836SJohn.Forte@Sun.COM 	struct option *lp;
385*7836SJohn.Forte@Sun.COM 	struct option intLongOpt[MAXOPTIONS + 1];
386*7836SJohn.Forte@Sun.COM 
387*7836SJohn.Forte@Sun.COM 	/*
388*7836SJohn.Forte@Sun.COM 	 * Check for NULLs on mandatory input arguments
389*7836SJohn.Forte@Sun.COM 	 *
390*7836SJohn.Forte@Sun.COM 	 * Note: longOptionTbl can be NULL in the case
391*7836SJohn.Forte@Sun.COM 	 * where there is no caller defined options
392*7836SJohn.Forte@Sun.COM 	 *
393*7836SJohn.Forte@Sun.COM 	 */
394*7836SJohn.Forte@Sun.COM 	assert(synTable.versionString);
395*7836SJohn.Forte@Sun.COM 	assert(synTable.subCommandPropsTbl);
396*7836SJohn.Forte@Sun.COM 	assert(funcRet);
397*7836SJohn.Forte@Sun.COM 
398*7836SJohn.Forte@Sun.COM 	versionString = synTable.versionString;
399*7836SJohn.Forte@Sun.COM 
400*7836SJohn.Forte@Sun.COM 	/* set global command name */
401*7836SJohn.Forte@Sun.COM 	commandName = getExecBasename(argv[0]);
402*7836SJohn.Forte@Sun.COM 
403*7836SJohn.Forte@Sun.COM 	/* Set unbuffered output */
404*7836SJohn.Forte@Sun.COM 	setbuf(stdout, NULL);
405*7836SJohn.Forte@Sun.COM 
406*7836SJohn.Forte@Sun.COM 	/* load globals */
407*7836SJohn.Forte@Sun.COM 	_subCommandProps = synTable.subCommandPropsTbl;
408*7836SJohn.Forte@Sun.COM 	_clientOptionTbl = synTable.longOptionTbl;
409*7836SJohn.Forte@Sun.COM 
410*7836SJohn.Forte@Sun.COM 	/* There must be at least two arguments */
411*7836SJohn.Forte@Sun.COM 	if (argc < 2) {
412*7836SJohn.Forte@Sun.COM 		usage(GENERAL_USAGE);
413*7836SJohn.Forte@Sun.COM 		return (1);
414*7836SJohn.Forte@Sun.COM 	}
415*7836SJohn.Forte@Sun.COM 
416*7836SJohn.Forte@Sun.COM 	(void) memset(&intLongOpt[0], 0, sizeof (intLongOpt));
417*7836SJohn.Forte@Sun.COM 
418*7836SJohn.Forte@Sun.COM 	/*
419*7836SJohn.Forte@Sun.COM 	 * load standard subcommand options to internal long options table
420*7836SJohn.Forte@Sun.COM 	 * Two separate getopt_long(3C) tables are used.
421*7836SJohn.Forte@Sun.COM 	 */
422*7836SJohn.Forte@Sun.COM 	for (i = 0; standardSubCmdOptions[i].name; i++) {
423*7836SJohn.Forte@Sun.COM 		intLongOpt[i].name = standardSubCmdOptions[i].name;
424*7836SJohn.Forte@Sun.COM 		intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
425*7836SJohn.Forte@Sun.COM 		intLongOpt[i].flag = standardSubCmdOptions[i].flag;
426*7836SJohn.Forte@Sun.COM 		intLongOpt[i].val = standardSubCmdOptions[i].val;
427*7836SJohn.Forte@Sun.COM 	}
428*7836SJohn.Forte@Sun.COM 
429*7836SJohn.Forte@Sun.COM 	/*
430*7836SJohn.Forte@Sun.COM 	 * copy caller's long options into internal long options table
431*7836SJohn.Forte@Sun.COM 	 * We do this for two reasons:
432*7836SJohn.Forte@Sun.COM 	 *  1) We need to use the getopt_long option structure internally
433*7836SJohn.Forte@Sun.COM 	 *  2) We need to prepend the table with the standard option
434*7836SJohn.Forte@Sun.COM 	 *	for all subcommands (currently -?)
435*7836SJohn.Forte@Sun.COM 	 */
436*7836SJohn.Forte@Sun.COM 	for (optionTbl = synTable.longOptionTbl;
437*7836SJohn.Forte@Sun.COM 	    optionTbl && optionTbl->name; optionTbl++, i++) {
438*7836SJohn.Forte@Sun.COM 		if (i > MAXOPTIONS - 1) {
439*7836SJohn.Forte@Sun.COM 			/* option table too long */
440*7836SJohn.Forte@Sun.COM 			assert(0);
441*7836SJohn.Forte@Sun.COM 		}
442*7836SJohn.Forte@Sun.COM 		intLongOpt[i].name = optionTbl->name;
443*7836SJohn.Forte@Sun.COM 		intLongOpt[i].has_arg = optionTbl->has_arg;
444*7836SJohn.Forte@Sun.COM 		intLongOpt[i].flag = NULL;
445*7836SJohn.Forte@Sun.COM 		intLongOpt[i].val = optionTbl->val;
446*7836SJohn.Forte@Sun.COM 	}
447*7836SJohn.Forte@Sun.COM 
448*7836SJohn.Forte@Sun.COM 	/* set option table global */
449*7836SJohn.Forte@Sun.COM 	_longOptions = &intLongOpt[0];
450*7836SJohn.Forte@Sun.COM 
451*7836SJohn.Forte@Sun.COM 
452*7836SJohn.Forte@Sun.COM 	/*
453*7836SJohn.Forte@Sun.COM 	 * Check for help/version request immediately following command
454*7836SJohn.Forte@Sun.COM 	 * '+' in option string ensures POSIX compliance in getopt_long()
455*7836SJohn.Forte@Sun.COM 	 * which means that processing will stop at first non-option
456*7836SJohn.Forte@Sun.COM 	 * argument.
457*7836SJohn.Forte@Sun.COM 	 */
458*7836SJohn.Forte@Sun.COM 	while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
459*7836SJohn.Forte@Sun.COM 	    NULL)) != EOF) {
460*7836SJohn.Forte@Sun.COM 		switch (opt) {
461*7836SJohn.Forte@Sun.COM 			case '?':
462*7836SJohn.Forte@Sun.COM 				/*
463*7836SJohn.Forte@Sun.COM 				 * getopt can return a '?' when no
464*7836SJohn.Forte@Sun.COM 				 * option letters match string. Check for
465*7836SJohn.Forte@Sun.COM 				 * the 'real' '?' in optopt.
466*7836SJohn.Forte@Sun.COM 				 */
467*7836SJohn.Forte@Sun.COM 				if (optopt == '?') {
468*7836SJohn.Forte@Sun.COM 					usage(DETAIL_USAGE);
469*7836SJohn.Forte@Sun.COM 					exit(0);
470*7836SJohn.Forte@Sun.COM 				} else {
471*7836SJohn.Forte@Sun.COM 					usage(GENERAL_USAGE);
472*7836SJohn.Forte@Sun.COM 					return (1);
473*7836SJohn.Forte@Sun.COM 				}
474*7836SJohn.Forte@Sun.COM 				break;
475*7836SJohn.Forte@Sun.COM 			case 'V':
476*7836SJohn.Forte@Sun.COM 				(void) fprintf(stdout, "%s: %s %s\n",
477*7836SJohn.Forte@Sun.COM 				    commandName, gettext("Version"),
478*7836SJohn.Forte@Sun.COM 				    versionString);
479*7836SJohn.Forte@Sun.COM 				exit(0);
480*7836SJohn.Forte@Sun.COM 				break;
481*7836SJohn.Forte@Sun.COM 			default:
482*7836SJohn.Forte@Sun.COM 				break;
483*7836SJohn.Forte@Sun.COM 		}
484*7836SJohn.Forte@Sun.COM 	}
485*7836SJohn.Forte@Sun.COM 
486*7836SJohn.Forte@Sun.COM 	/*
487*7836SJohn.Forte@Sun.COM 	 * subcommand is always in the second argument. If there is no
488*7836SJohn.Forte@Sun.COM 	 * recognized subcommand in the second argument, print error,
489*7836SJohn.Forte@Sun.COM 	 * general usage and then return.
490*7836SJohn.Forte@Sun.COM 	 */
491*7836SJohn.Forte@Sun.COM 	if (getSubcommandProps(argv[1], &subcommand) != 0) {
492*7836SJohn.Forte@Sun.COM 		(void) printf("%s: %s\n", commandName,
493*7836SJohn.Forte@Sun.COM 		    gettext("invalid subcommand"));
494*7836SJohn.Forte@Sun.COM 		usage(GENERAL_USAGE);
495*7836SJohn.Forte@Sun.COM 		return (1);
496*7836SJohn.Forte@Sun.COM 	}
497*7836SJohn.Forte@Sun.COM 
498*7836SJohn.Forte@Sun.COM 	getoptargv = argv;
499*7836SJohn.Forte@Sun.COM 	getoptargv++;
500*7836SJohn.Forte@Sun.COM 	getoptargc = argc;
501*7836SJohn.Forte@Sun.COM 	getoptargc -= 1;
502*7836SJohn.Forte@Sun.COM 
503*7836SJohn.Forte@Sun.COM 	(void) memset(optionStringAll, 0, sizeof (optionStringAll));
504*7836SJohn.Forte@Sun.COM 	(void) memset(&cmdOptions[0], 0, sizeof (cmdOptions));
505*7836SJohn.Forte@Sun.COM 
506*7836SJohn.Forte@Sun.COM 	j = 0;
507*7836SJohn.Forte@Sun.COM 	/*
508*7836SJohn.Forte@Sun.COM 	 * Build optionStringAll from long options table
509*7836SJohn.Forte@Sun.COM 	 */
510*7836SJohn.Forte@Sun.COM 	for (lp = _longOptions;  lp->name; lp++, j++) {
511*7836SJohn.Forte@Sun.COM 		/* sanity check on string length */
512*7836SJohn.Forte@Sun.COM 		if (j + 1 >= sizeof (optionStringAll)) {
513*7836SJohn.Forte@Sun.COM 			/* option table too long */
514*7836SJohn.Forte@Sun.COM 			assert(0);
515*7836SJohn.Forte@Sun.COM 		}
516*7836SJohn.Forte@Sun.COM 		optionStringAll[j] = lp->val;
517*7836SJohn.Forte@Sun.COM 		if (lp->has_arg == required_argument) {
518*7836SJohn.Forte@Sun.COM 			optionStringAll[++j] = ':';
519*7836SJohn.Forte@Sun.COM 		}
520*7836SJohn.Forte@Sun.COM 	}
521*7836SJohn.Forte@Sun.COM 
522*7836SJohn.Forte@Sun.COM 	i = 0;
523*7836SJohn.Forte@Sun.COM 	/*
524*7836SJohn.Forte@Sun.COM 	 * Run getopt for all arguments against all possible options
525*7836SJohn.Forte@Sun.COM 	 * Store all options/option arguments in an array for retrieval
526*7836SJohn.Forte@Sun.COM 	 * later.
527*7836SJohn.Forte@Sun.COM 	 *
528*7836SJohn.Forte@Sun.COM 	 * Once all options are retrieved, a validity check against
529*7836SJohn.Forte@Sun.COM 	 * subcommand table is performed.
530*7836SJohn.Forte@Sun.COM 	 */
531*7836SJohn.Forte@Sun.COM 	while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
532*7836SJohn.Forte@Sun.COM 	    _longOptions, NULL)) != EOF) {
533*7836SJohn.Forte@Sun.COM 		switch (opt) {
534*7836SJohn.Forte@Sun.COM 			case '?':
535*7836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
536*7836SJohn.Forte@Sun.COM 				exit(0);
537*7836SJohn.Forte@Sun.COM 			default:
538*7836SJohn.Forte@Sun.COM 				cmdOptions[i].optval = opt;
539*7836SJohn.Forte@Sun.COM 				if (optarg) {
540*7836SJohn.Forte@Sun.COM 					len = strlen(optarg);
541*7836SJohn.Forte@Sun.COM 					if (len > sizeof (cmdOptions[i].optarg)
542*7836SJohn.Forte@Sun.COM 					    - 1) {
543*7836SJohn.Forte@Sun.COM 						(void) printf("%s: %s\n",
544*7836SJohn.Forte@Sun.COM 						    commandName,
545*7836SJohn.Forte@Sun.COM 						    gettext("option too long"));
546*7836SJohn.Forte@Sun.COM 						errno = EINVAL;
547*7836SJohn.Forte@Sun.COM 						return (-1);
548*7836SJohn.Forte@Sun.COM 					}
549*7836SJohn.Forte@Sun.COM 					(void) strncpy(cmdOptions[i].optarg,
550*7836SJohn.Forte@Sun.COM 					    optarg, len);
551*7836SJohn.Forte@Sun.COM 				}
552*7836SJohn.Forte@Sun.COM 				i++;
553*7836SJohn.Forte@Sun.COM 				break;
554*7836SJohn.Forte@Sun.COM 		}
555*7836SJohn.Forte@Sun.COM 	}
556*7836SJohn.Forte@Sun.COM 
557*7836SJohn.Forte@Sun.COM 	/*
558*7836SJohn.Forte@Sun.COM 	 * increment past last option
559*7836SJohn.Forte@Sun.COM 	 */
560*7836SJohn.Forte@Sun.COM 	operInd = optind + 1;
561*7836SJohn.Forte@Sun.COM 
562*7836SJohn.Forte@Sun.COM 	/*
563*7836SJohn.Forte@Sun.COM 	 * Check validity of given options, if any were given
564*7836SJohn.Forte@Sun.COM 	 */
565*7836SJohn.Forte@Sun.COM 
566*7836SJohn.Forte@Sun.COM 	/* get option string for this subcommand */
567*7836SJohn.Forte@Sun.COM 	availOptions = subcommand->optionString;
568*7836SJohn.Forte@Sun.COM 
569*7836SJohn.Forte@Sun.COM 	/* Get count of required options */
570*7836SJohn.Forte@Sun.COM 	if (subcommand->required) {
571*7836SJohn.Forte@Sun.COM 		requiredOptionCnt = strlen(subcommand->required);
572*7836SJohn.Forte@Sun.COM 	}
573*7836SJohn.Forte@Sun.COM 
574*7836SJohn.Forte@Sun.COM 	if (cmdOptions[0].optval != 0) { /* options were input */
575*7836SJohn.Forte@Sun.COM 		if (availOptions == NULL) { /* no options permitted */
576*7836SJohn.Forte@Sun.COM 			(void) printf("%s: %s\n", commandName,
577*7836SJohn.Forte@Sun.COM 			    gettext("no options permitted"));
578*7836SJohn.Forte@Sun.COM 			subUsage(DETAIL_USAGE, subcommand);
579*7836SJohn.Forte@Sun.COM 			return (1);
580*7836SJohn.Forte@Sun.COM 		}
581*7836SJohn.Forte@Sun.COM 		for (i = 0; cmdOptions[i].optval; i++) {
582*7836SJohn.Forte@Sun.COM 			/* is the option in the available option string? */
583*7836SJohn.Forte@Sun.COM 			if (!(strchr(availOptions, cmdOptions[i].optval))) {
584*7836SJohn.Forte@Sun.COM 				(void) printf("%s: '-%c': %s\n", commandName,
585*7836SJohn.Forte@Sun.COM 				    cmdOptions[i].optval,
586*7836SJohn.Forte@Sun.COM 				    gettext("invalid option"));
587*7836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
588*7836SJohn.Forte@Sun.COM 				return (1);
589*7836SJohn.Forte@Sun.COM 			/* increment required options entered */
590*7836SJohn.Forte@Sun.COM 			} else if (subcommand->required &&
591*7836SJohn.Forte@Sun.COM 			    (strchr(subcommand->required,
592*7836SJohn.Forte@Sun.COM 			    cmdOptions[i].optval))) {
593*7836SJohn.Forte@Sun.COM 				requiredOptionEntered++;
594*7836SJohn.Forte@Sun.COM 			/* Check for exclusive options */
595*7836SJohn.Forte@Sun.COM 			} else if (cmdOptions[1].optval != 0 &&
596*7836SJohn.Forte@Sun.COM 			    subcommand->exclusive &&
597*7836SJohn.Forte@Sun.COM 			    strchr(subcommand->exclusive,
598*7836SJohn.Forte@Sun.COM 			    cmdOptions[i].optval)) {
599*7836SJohn.Forte@Sun.COM 					(void) printf("%s: '-%c': %s\n",
600*7836SJohn.Forte@Sun.COM 					    commandName, cmdOptions[i].optval,
601*7836SJohn.Forte@Sun.COM 					    gettext("is an exclusive option"));
602*7836SJohn.Forte@Sun.COM 				subUsage(DETAIL_USAGE, subcommand);
603*7836SJohn.Forte@Sun.COM 					return (1);
604*7836SJohn.Forte@Sun.COM 			}
605*7836SJohn.Forte@Sun.COM 		}
606*7836SJohn.Forte@Sun.COM 	} else { /* no options were input */
607*7836SJohn.Forte@Sun.COM 		if (availOptions != NULL && subcommand->required) {
608*7836SJohn.Forte@Sun.COM 			(void) printf("%s: %s\n", commandName,
609*7836SJohn.Forte@Sun.COM 			    gettext("at least one option required"));
610*7836SJohn.Forte@Sun.COM 			subUsage(DETAIL_USAGE, subcommand);
611*7836SJohn.Forte@Sun.COM 			return (1);
612*7836SJohn.Forte@Sun.COM 		}
613*7836SJohn.Forte@Sun.COM 	}
614*7836SJohn.Forte@Sun.COM 
615*7836SJohn.Forte@Sun.COM 	/* Were all required options entered? */
616*7836SJohn.Forte@Sun.COM 	if (requiredOptionEntered != requiredOptionCnt) {
617*7836SJohn.Forte@Sun.COM 		(void) printf("%s: %s: %s\n", commandName,
618*7836SJohn.Forte@Sun.COM 		    gettext("Following option(s) required"),
619*7836SJohn.Forte@Sun.COM 		    subcommand->required);
620*7836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
621*7836SJohn.Forte@Sun.COM 		return (1);
622*7836SJohn.Forte@Sun.COM 	}
623*7836SJohn.Forte@Sun.COM 
624*7836SJohn.Forte@Sun.COM 
625*7836SJohn.Forte@Sun.COM 	/*
626*7836SJohn.Forte@Sun.COM 	 * If there are no operands,
627*7836SJohn.Forte@Sun.COM 	 * check to see if this is okay
628*7836SJohn.Forte@Sun.COM 	 */
629*7836SJohn.Forte@Sun.COM 	if ((operInd == argc) &&
630*7836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_MANDATORY)) {
631*7836SJohn.Forte@Sun.COM 		(void) printf("%s: %s %s\n", commandName, subcommand->name,
632*7836SJohn.Forte@Sun.COM 		    gettext("requires an operand"));
633*7836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
634*7836SJohn.Forte@Sun.COM 		return (1);
635*7836SJohn.Forte@Sun.COM 	}
636*7836SJohn.Forte@Sun.COM 
637*7836SJohn.Forte@Sun.COM 	/*
638*7836SJohn.Forte@Sun.COM 	 * If there are more operands,
639*7836SJohn.Forte@Sun.COM 	 * check to see if this is okay
640*7836SJohn.Forte@Sun.COM 	 */
641*7836SJohn.Forte@Sun.COM 	if ((argc > operInd) &&
642*7836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_NONE)) {
643*7836SJohn.Forte@Sun.COM 		(void) fprintf(stderr, "%s: %s %s\n", commandName,
644*7836SJohn.Forte@Sun.COM 		    subcommand->name, gettext("takes no operands"));
645*7836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
646*7836SJohn.Forte@Sun.COM 		return (1);
647*7836SJohn.Forte@Sun.COM 	}
648*7836SJohn.Forte@Sun.COM 
649*7836SJohn.Forte@Sun.COM 	/*
650*7836SJohn.Forte@Sun.COM 	 * If there is more than one more operand,
651*7836SJohn.Forte@Sun.COM 	 * check to see if this is okay
652*7836SJohn.Forte@Sun.COM 	 */
653*7836SJohn.Forte@Sun.COM 	if ((argc > operInd) && ((argc - operInd) != 1) &&
654*7836SJohn.Forte@Sun.COM 	    (subcommand->operand & OPERAND_SINGLE)) {
655*7836SJohn.Forte@Sun.COM 		(void) printf("%s: %s %s\n", commandName,
656*7836SJohn.Forte@Sun.COM 		    subcommand->name, gettext("accepts only a single operand"));
657*7836SJohn.Forte@Sun.COM 		subUsage(DETAIL_USAGE, subcommand);
658*7836SJohn.Forte@Sun.COM 		return (1);
659*7836SJohn.Forte@Sun.COM 	}
660*7836SJohn.Forte@Sun.COM 
661*7836SJohn.Forte@Sun.COM 	/* Finished syntax checks */
662*7836SJohn.Forte@Sun.COM 
663*7836SJohn.Forte@Sun.COM 
664*7836SJohn.Forte@Sun.COM 	/* Call appropriate function */
665*7836SJohn.Forte@Sun.COM 	*funcRet = subcommand->handler(argc - operInd, &argv[operInd],
666*7836SJohn.Forte@Sun.COM 	    &cmdOptions[0], callArgs);
667*7836SJohn.Forte@Sun.COM 
668*7836SJohn.Forte@Sun.COM 	return (0);
669*7836SJohn.Forte@Sun.COM }
670