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