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