4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #include <sys/types.h>
39 #define GENERAL_USAGE 1
40 #define DETAIL_USAGE 2
42 /* printable ascii character set len */
43 #define MAXOPTIONS (uint_t)('~' - '!' + 1)
46 * MAXOPTIONSTRING is the max length of the options string used in getopt and
47 * will be the printable character set + ':' for each character,
48 * providing for options with arguments. e.g. "t:Cs:hglr:"
50 #define MAXOPTIONSTRING MAXOPTIONS * 2
52 /* standard command options table to support -?, -V */
53 struct option standardCmdOptions
[] = {
54 {"help", no_argument
, NULL
, '?'},
55 {"version", no_argument
, NULL
, 'V'},
59 /* standard subcommand options table to support -? */
60 struct option standardSubCmdOptions
[] = {
61 {"help", no_argument
, NULL
, '?'},
65 /* forward declarations */
66 static int getSubcommandProps(char *, subCommandProps_t
**);
67 static char *getExecBasename(char *);
68 static void usage(uint_t
);
69 static void subUsage(uint_t
, subCommandProps_t
*);
70 static char *getLongOption(int);
71 static char *getOptionArgDesc(int);
74 static struct option
*_longOptions
;
75 static subCommandProps_t
*_subCommandProps
;
76 static optionTbl_t
*_clientOptionTbl
;
77 static char *commandName
;
82 * subCommand - subcommand value
84 * subCommandProps - pointer to subCommandProps_t structure allocated by caller
86 * On successful return, subCommandProps contains the properties for the value
87 * in subCommand. On failure, the contents of subCommandProps is unspecified.
95 getSubcommandProps(char *subCommand
, subCommandProps_t
**subCommandProps
)
97 subCommandProps_t
*sp
;
100 for (sp
= _subCommandProps
; sp
->name
; sp
++) {
101 len
= strlen(subCommand
);
102 if (len
== strlen(sp
->name
) &&
103 strncasecmp(subCommand
, sp
->name
, len
) == 0) {
104 *subCommandProps
= sp
;
113 * shortOption - short option character for which to return the
114 * associated long option string
117 * on success, long option name
121 getLongOption(int shortOption
)
124 for (op
= _longOptions
; op
->name
; op
++) {
125 if (shortOption
== op
->val
) {
134 * shortOption - short option character for which to return the
137 * on success, argument string
141 getOptionArgDesc(int shortOption
)
144 for (op
= _clientOptionTbl
; op
->name
; op
++) {
145 if (op
->val
== shortOption
&&
146 op
->has_arg
== required_argument
) {
147 return (op
->argDesc
);
155 * Print usage for a subcommand.
158 * usage type - GENERAL_USAGE, DETAIL_USAGE
159 * subcommand - pointer to subCommandProps_t structure
166 subUsage(uint_t usageType
, subCommandProps_t
*subcommand
)
172 if (usageType
== GENERAL_USAGE
) {
173 (void) printf("%s:\t%s %s [", gettext("Usage"), commandName
,
175 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
176 (void) printf("-%c", standardSubCmdOptions
[i
].val
);
177 if (standardSubCmdOptions
[i
+1].name
)
180 (void) fprintf(stdout
, "]\n");
184 /* print subcommand usage */
185 (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName
,
188 /* print options if applicable */
189 if (subcommand
->optionString
!= NULL
) {
190 if (subcommand
->required
) {
191 (void) printf("%s", gettext("<"));
193 (void) printf("%s", gettext("["));
195 (void) printf("%s", gettext("OPTIONS"));
196 if (subcommand
->required
) {
197 (void) printf("%s ", gettext(">"));
199 (void) printf("%s ", gettext("]"));
203 /* print operand requirements */
204 if (!(subcommand
->operand
& OPERAND_NONE
) &&
205 !(subcommand
->operand
& OPERAND_MANDATORY
)) {
206 (void) printf(gettext("["));
209 if (subcommand
->operand
& OPERAND_MANDATORY
) {
210 (void) printf(gettext("<"));
213 if (!(subcommand
->operand
& OPERAND_NONE
)) {
214 assert(subcommand
->operandDefinition
);
215 (void) printf("%s", subcommand
->operandDefinition
);
218 if (subcommand
->operand
& OPERAND_MULTIPLE
) {
219 (void) printf(gettext(" ..."));
222 if (subcommand
->operand
& OPERAND_MANDATORY
) {
223 (void) printf(gettext(">"));
226 if (!(subcommand
->operand
& OPERAND_NONE
) &&
227 !(subcommand
->operand
& OPERAND_MANDATORY
)) {
228 (void) printf(gettext("]"));
231 /* print options for subcommand */
232 if (subcommand
->optionString
!= NULL
) {
233 (void) printf("\n\t%s:", gettext("OPTIONS"));
234 for (i
= 0; i
< strlen(subcommand
->optionString
); i
++) {
235 assert((longOpt
= getLongOption(
236 subcommand
->optionString
[i
])) != NULL
);
237 (void) printf("\n\t\t-%c, --%s ",
238 subcommand
->optionString
[i
],
241 getOptionArgDesc(subcommand
->optionString
[i
]);
242 if (optionArgDesc
!= NULL
) {
243 (void) printf("<%s>", optionArgDesc
);
245 if (subcommand
->exclusive
&&
246 strchr(subcommand
->exclusive
,
247 subcommand
->optionString
[i
])) {
248 (void) printf(" (%s)", gettext("exclusive"));
252 (void) fprintf(stdout
, "\n");
253 if (subcommand
->helpText
) {
254 (void) printf("%s\n", subcommand
->helpText
);
260 * type of usage statement to print
263 * return value of subUsage
266 usage(uint_t usageType
)
269 subCommandProps_t
*sp
;
271 /* print general command usage */
272 (void) printf("%s:\t%s ", gettext("Usage"), commandName
);
274 for (i
= 0; standardCmdOptions
[i
].name
; i
++) {
275 (void) printf("-%c", standardCmdOptions
[i
].val
);
276 if (standardCmdOptions
[i
+1].name
)
280 if (usageType
== GENERAL_USAGE
) {
281 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
282 (void) printf(",--%s", standardSubCmdOptions
[i
].name
);
283 if (standardSubCmdOptions
[i
+1].name
)
288 (void) fprintf(stdout
, "\n");
291 /* print all subcommand usage */
292 for (sp
= _subCommandProps
; sp
->name
; sp
++) {
293 subUsage(usageType
, sp
);
299 * execFullName - exec name of program (argv[0])
302 * command name portion of execFullName
305 getExecBasename(char *execFullname
)
307 char *lastSlash
, *execBasename
;
309 /* guard against '/' at end of command invocation */
311 lastSlash
= strrchr(execFullname
, '/');
312 if (lastSlash
== NULL
) {
313 execBasename
= execFullname
;
316 execBasename
= lastSlash
+ 1;
317 if (*execBasename
== '\0') {
324 return (execBasename
);
328 * cmdParse is a parser that checks syntax of the input command against
329 * various rules tables.
331 * It provides usage feedback based upon the passed rules tables by calling
332 * two usage functions, usage, subUsage
334 * When syntax is successfully validated, the associated function is called
335 * using the subcommands table functions.
337 * Syntax is as follows:
338 * command subcommand [<options>] [<operand>]
340 * There are two standard short and long options assumed:
341 * -?, --help Provides usage on a command or subcommand
342 * and stops further processing of the arguments
344 * -V, --version Provides version information on the command
345 * and stops further processing of the arguments
347 * These options are loaded by this function.
350 * argc, argv from main
351 * syntax rules tables (synTables_t structure)
352 * callArgs - void * passed by caller to be passed to subcommand function
355 * funcRet - pointer to int that holds subcommand function return value
359 * zero on successful syntax parse and function call
361 * 1 on unsuccessful syntax parse (no function has been called)
362 * This could be due to a version or help call or simply a
363 * general usage call.
365 * -1 check errno, call failed
367 * This module is not MT-safe.
371 cmdParse(int argc
, char *argv
[], synTables_t synTable
, void *callArgs
,
380 int requiredOptionCnt
= 0, requiredOptionEntered
= 0;
383 char optionStringAll
[MAXOPTIONSTRING
+ 1];
384 subCommandProps_t
*subcommand
;
385 cmdOptions_t cmdOptions
[MAXOPTIONS
+ 1];
386 optionTbl_t
*optionTbl
;
388 struct option intLongOpt
[MAXOPTIONS
+ 1];
391 * Check for NULLs on mandatory input arguments
393 * Note: longOptionTbl can be NULL in the case
394 * where there is no caller defined options
397 assert(synTable
.versionString
);
398 assert(synTable
.subCommandPropsTbl
);
401 versionString
= synTable
.versionString
;
403 /* set global command name */
404 commandName
= getExecBasename(argv
[0]);
406 /* Set unbuffered output */
407 setbuf(stdout
, NULL
);
410 _subCommandProps
= synTable
.subCommandPropsTbl
;
411 _clientOptionTbl
= synTable
.longOptionTbl
;
413 /* There must be at least two arguments */
415 usage(GENERAL_USAGE
);
419 (void) memset(&intLongOpt
[0], 0, sizeof (intLongOpt
));
422 * load standard subcommand options to internal long options table
423 * Two separate getopt_long(3C) tables are used.
425 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
426 intLongOpt
[i
].name
= standardSubCmdOptions
[i
].name
;
427 intLongOpt
[i
].has_arg
= standardSubCmdOptions
[i
].has_arg
;
428 intLongOpt
[i
].flag
= standardSubCmdOptions
[i
].flag
;
429 intLongOpt
[i
].val
= standardSubCmdOptions
[i
].val
;
433 * copy caller's long options into internal long options table
434 * We do this for two reasons:
435 * 1) We need to use the getopt_long option structure internally
436 * 2) We need to prepend the table with the standard option
437 * for all subcommands (currently -?)
439 for (optionTbl
= synTable
.longOptionTbl
;
440 optionTbl
&& optionTbl
->name
; optionTbl
++, i
++) {
441 if (i
> MAXOPTIONS
- 1) {
442 /* option table too long */
445 intLongOpt
[i
].name
= optionTbl
->name
;
446 intLongOpt
[i
].has_arg
= optionTbl
->has_arg
;
447 intLongOpt
[i
].flag
= NULL
;
448 intLongOpt
[i
].val
= optionTbl
->val
;
451 /* set option table global */
452 _longOptions
= &intLongOpt
[0];
456 * Check for help/version request immediately following command
457 * '+' in option string ensures POSIX compliance in getopt_long()
458 * which means that processing will stop at first non-option
461 while ((opt
= getopt_long(argc
, argv
, "+?V", standardCmdOptions
,
466 * getopt can return a '?' when no
467 * option letters match string. Check for
468 * the 'real' '?' in optopt.
474 usage(GENERAL_USAGE
);
479 (void) fprintf(stdout
, "%s: %s %s\n",
480 commandName
, gettext("Version"),
490 * subcommand is always in the second argument. If there is no
491 * recognized subcommand in the second argument, print error,
492 * general usage and then return.
494 if (getSubcommandProps(argv
[1], &subcommand
) != 0) {
495 (void) printf("%s: %s\n", commandName
,
496 gettext("invalid subcommand"));
497 usage(GENERAL_USAGE
);
506 (void) memset(optionStringAll
, 0, sizeof (optionStringAll
));
507 (void) memset(&cmdOptions
[0], 0, sizeof (cmdOptions
));
511 * Build optionStringAll from long options table
513 for (lp
= _longOptions
; lp
->name
; lp
++, j
++) {
514 /* sanity check on string length */
515 if (j
+ 1 >= sizeof (optionStringAll
)) {
516 /* option table too long */
519 optionStringAll
[j
] = lp
->val
;
520 if (lp
->has_arg
== required_argument
) {
521 optionStringAll
[++j
] = ':';
527 * Run getopt for all arguments against all possible options
528 * Store all options/option arguments in an array for retrieval
531 * Once all options are retrieved, a validity check against
532 * subcommand table is performed.
534 while ((opt
= getopt_long(getoptargc
, getoptargv
, optionStringAll
,
535 _longOptions
, NULL
)) != EOF
) {
538 subUsage(DETAIL_USAGE
, subcommand
);
540 * getopt can return a '?' when no
541 * option letters match string. Check for
542 * the 'real' '?' in optopt.
550 cmdOptions
[i
].optval
= opt
;
552 len
= strlen(optarg
);
553 if (len
> sizeof (cmdOptions
[i
].optarg
)
555 (void) printf("%s: %s\n",
557 gettext("option too long"));
561 (void) strncpy(cmdOptions
[i
].optarg
,
570 * increment past last option
572 operInd
= optind
+ 1;
575 * Check validity of given options, if any were given
578 /* get option string for this subcommand */
579 availOptions
= subcommand
->optionString
;
581 /* Get count of required options */
582 if (subcommand
->required
) {
583 requiredOptionCnt
= strlen(subcommand
->required
);
586 if (cmdOptions
[0].optval
!= 0) { /* options were input */
587 if (availOptions
== NULL
) { /* no options permitted */
588 (void) printf("%s: %s\n", commandName
,
589 gettext("no options permitted"));
590 subUsage(DETAIL_USAGE
, subcommand
);
593 for (i
= 0; cmdOptions
[i
].optval
; i
++) {
594 /* is the option in the available option string? */
595 if (!(strchr(availOptions
, cmdOptions
[i
].optval
))) {
596 (void) printf("%s: '-%c': %s\n", commandName
,
597 cmdOptions
[i
].optval
,
598 gettext("invalid option"));
599 subUsage(DETAIL_USAGE
, subcommand
);
601 /* increment required options entered */
602 } else if (subcommand
->required
&&
603 (strchr(subcommand
->required
,
604 cmdOptions
[i
].optval
))) {
605 requiredOptionEntered
++;
606 /* Check for exclusive options */
607 } else if (cmdOptions
[1].optval
!= 0 &&
608 subcommand
->exclusive
&&
609 strchr(subcommand
->exclusive
,
610 cmdOptions
[i
].optval
)) {
611 (void) printf("%s: '-%c': %s\n",
612 commandName
, cmdOptions
[i
].optval
,
613 gettext("is an exclusive option"));
614 subUsage(DETAIL_USAGE
, subcommand
);
618 } else { /* no options were input */
619 if (availOptions
!= NULL
&& subcommand
->required
) {
620 (void) printf("%s: %s\n", commandName
,
621 gettext("at least one option required"));
622 subUsage(DETAIL_USAGE
, subcommand
);
627 /* Were all required options entered? */
628 if (requiredOptionEntered
!= requiredOptionCnt
) {
629 (void) printf("%s: %s: %s\n", commandName
,
630 gettext("Following option(s) required"),
631 subcommand
->required
);
632 subUsage(DETAIL_USAGE
, subcommand
);
638 * If there are no operands,
639 * check to see if this is okay
641 if ((operInd
== argc
) &&
642 (subcommand
->operand
& OPERAND_MANDATORY
)) {
643 (void) printf("%s: %s %s\n", commandName
, subcommand
->name
,
644 gettext("requires an operand"));
645 subUsage(DETAIL_USAGE
, subcommand
);
650 * If there are more operands,
651 * check to see if this is okay
653 if ((argc
> operInd
) &&
654 (subcommand
->operand
& OPERAND_NONE
)) {
655 (void) fprintf(stderr
, "%s: %s %s\n", commandName
,
656 subcommand
->name
, gettext("takes no operands"));
657 subUsage(DETAIL_USAGE
, subcommand
);
662 * If there is more than one more operand,
663 * check to see if this is okay
665 if ((argc
> operInd
) && ((argc
- operInd
) != 1) &&
666 (subcommand
->operand
& OPERAND_SINGLE
)) {
667 (void) printf("%s: %s %s\n", commandName
,
668 subcommand
->name
, gettext("accepts only a single operand"));
669 subUsage(DETAIL_USAGE
, subcommand
);
673 /* Finished syntax checks */
676 /* Call appropriate function */
677 *funcRet
= subcommand
->handler(argc
- operInd
, &argv
[operInd
],
678 &cmdOptions
[0], callArgs
);