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