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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #include <sys/types.h>
37 #pragma ident "@(#)cmdparse.c 1.1 07/09/21 SMI"
41 #define GENERAL_USAGE 1
42 #define DETAIL_USAGE 2
44 /* printable ascii character set len */
45 #define MAXOPTIONS (uint_t)('~' - '!' + 1)
48 * MAXOPTIONSTRING is the max length of the options string used in getopt and
49 * will be the printable character set + ':' for each character,
50 * providing for options with arguments. e.g. "t:Cs:hglr:"
52 #define MAXOPTIONSTRING MAXOPTIONS * 2
54 /* standard command options table to support -?, -V */
55 struct option standardCmdOptions
[] = {
56 {"help", no_argument
, NULL
, '?'},
57 {"version", no_argument
, NULL
, 'V'},
61 /* standard subcommand options table to support -? */
62 struct option standardSubCmdOptions
[] = {
63 {"help", no_argument
, NULL
, '?'},
67 /* forward declarations */
68 static int getSubcommandProps(char *, subCommandProps_t
**);
69 static char *getExecBasename(char *);
70 static void usage(uint_t
);
71 static void subUsage(uint_t
, subCommandProps_t
*);
72 static char *getLongOption(int);
73 static char *getOptionArgDesc(int);
76 static struct option
*_longOptions
;
77 static subCommandProps_t
*_subCommandProps
;
78 static optionTbl_t
*_clientOptionTbl
;
79 static char *commandName
;
84 * subCommand - subcommand value
86 * subCommandProps - pointer to subCommandProps_t structure allocated by caller
88 * On successful return, subCommandProps contains the properties for the value
89 * in subCommand. On failure, the contents of subCommandProps is unspecified.
97 getSubcommandProps(char *subCommand
, subCommandProps_t
**subCommandProps
)
99 subCommandProps_t
*sp
;
102 for (sp
= _subCommandProps
; sp
->name
; sp
++) {
103 len
= strlen(subCommand
);
104 if (len
== strlen(sp
->name
) &&
105 strncasecmp(subCommand
, sp
->name
, len
) == 0) {
106 *subCommandProps
= sp
;
115 * shortOption - short option character for which to return the
116 * associated long option string
119 * on success, long option name
123 getLongOption(int shortOption
)
126 for (op
= _longOptions
; op
->name
; op
++) {
127 if (shortOption
== op
->val
) {
136 * shortOption - short option character for which to return the
139 * on success, argument string
143 getOptionArgDesc(int shortOption
)
146 for (op
= _clientOptionTbl
; op
->name
; op
++) {
147 if (op
->val
== shortOption
&&
148 op
->has_arg
== required_argument
) {
149 return (op
->argDesc
);
157 * Print usage for a subcommand.
160 * usage type - GENERAL_USAGE, DETAIL_USAGE
161 * subcommand - pointer to subCommandProps_t structure
168 subUsage(uint_t usageType
, subCommandProps_t
*subcommand
)
174 if (usageType
== GENERAL_USAGE
) {
175 (void) printf("%s:\t%s %s [", gettext("Usage"), commandName
,
178 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
179 (void) printf("-%c", standardSubCmdOptions
[i
].val
);
180 if (standardSubCmdOptions
[i
+1].name
)
184 (void) fprintf(stdout
, "]\n");
188 /* print subcommand usage */
189 (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName
,
192 /* print options if applicable */
193 if (subcommand
->optionString
!= NULL
) {
194 if (subcommand
->required
) {
195 (void) printf("%s", gettext("<"));
197 (void) printf("%s", gettext("["));
199 (void) printf("%s", gettext("OPTIONS"));
200 if (subcommand
->required
) {
201 (void) printf("%s ", gettext(">"));
203 (void) printf("%s ", gettext("]"));
207 /* print operand requirements */
208 if (!(subcommand
->operand
& OPERAND_NONE
) &&
209 !(subcommand
->operand
& OPERAND_MANDATORY
)) {
210 (void) printf(gettext("["));
213 if (subcommand
->operand
& OPERAND_MANDATORY
) {
214 (void) printf(gettext("<"));
217 if (!(subcommand
->operand
& OPERAND_NONE
)) {
218 assert(subcommand
->operandDefinition
);
219 (void) printf("%s", subcommand
->operandDefinition
);
222 if (subcommand
->operand
& OPERAND_MULTIPLE
) {
223 (void) printf(gettext(" ..."));
226 if (subcommand
->operand
& OPERAND_MANDATORY
) {
227 (void) printf(gettext(">"));
230 if (!(subcommand
->operand
& OPERAND_NONE
) &&
231 !(subcommand
->operand
& OPERAND_MANDATORY
)) {
232 (void) printf(gettext("]"));
235 /* print options for subcommand */
236 if (subcommand
->optionString
!= NULL
) {
237 (void) printf("\n\t%s:", gettext("OPTIONS"));
238 for (i
= 0; i
< strlen(subcommand
->optionString
); i
++) {
239 if ((longOpt
= getLongOption(
240 subcommand
->optionString
[i
]))
242 /* no long option exists for short option */
245 (void) printf("\n\t\t-%c, --%s ",
246 subcommand
->optionString
[i
], longOpt
);
248 getOptionArgDesc(subcommand
->optionString
[i
]);
249 if (optionArgDesc
!= NULL
) {
250 (void) printf("<%s>", optionArgDesc
);
252 if (subcommand
->exclusive
&&
253 strchr(subcommand
->exclusive
,
254 subcommand
->optionString
[i
])) {
255 (void) printf(" (%s)", gettext("exclusive"));
259 (void) fprintf(stdout
, "\n");
264 * type of usage statement to print
267 * return value of subUsage
270 usage(uint_t usageType
)
273 subCommandProps_t
*sp
;
275 /* print general command usage */
276 (void) printf("%s:\t%s ", gettext("Usage"), commandName
);
278 for (i
= 0; standardCmdOptions
[i
].name
; i
++) {
279 (void) printf("-%c", standardCmdOptions
[i
].val
);
280 if (standardCmdOptions
[i
+1].name
)
284 if (usageType
== GENERAL_USAGE
) {
285 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
286 (void) printf(",--%s", standardSubCmdOptions
[i
].name
);
287 if (standardSubCmdOptions
[i
+1].name
)
292 (void) fprintf(stdout
, "\n");
295 /* print all subcommand usage */
296 for (sp
= _subCommandProps
; sp
->name
; sp
++) {
297 subUsage(usageType
, sp
);
303 * execFullName - exec name of program (argv[0])
306 * command name portion of execFullName
309 getExecBasename(char *execFullname
)
311 char *lastSlash
, *execBasename
;
313 /* guard against '/' at end of command invocation */
315 lastSlash
= strrchr(execFullname
, '/');
316 if (lastSlash
== NULL
) {
317 execBasename
= execFullname
;
320 execBasename
= lastSlash
+ 1;
321 if (*execBasename
== '\0') {
328 return (execBasename
);
332 * cmdParse is a parser that checks syntax of the input command against
333 * various rules tables.
335 * It provides usage feedback based upon the passed rules tables by calling
336 * two usage functions, usage, subUsage
338 * When syntax is successfully validated, the associated function is called
339 * using the subcommands table functions.
341 * Syntax is as follows:
342 * command subcommand [<options>] [<operand>]
344 * There are two standard short and long options assumed:
345 * -?, --help Provides usage on a command or subcommand
346 * and stops further processing of the arguments
348 * -V, --version Provides version information on the command
349 * and stops further processing of the arguments
351 * These options are loaded by this function.
354 * argc, argv from main
355 * syntax rules tables (synTables_t structure)
356 * callArgs - void * passed by caller to be passed to subcommand function
359 * funcRet - pointer to int that holds subcommand function return value
363 * zero on successful syntax parse and function call
365 * 1 on unsuccessful syntax parse (no function has been called)
366 * This could be due to a version or help call or simply a
367 * general usage call.
369 * -1 check errno, call failed
371 * This module is not MT-safe.
375 cmdParse(int argc
, char *argv
[], synTables_t synTable
, void *callArgs
,
386 char optionStringAll
[MAXOPTIONSTRING
+ 1];
387 subCommandProps_t
*subcommand
;
388 cmdOptions_t cmdOptions
[MAXOPTIONS
+ 1];
389 optionTbl_t
*optionTbl
;
391 struct option intLongOpt
[MAXOPTIONS
+ 1];
394 * Check for NULLs on mandatory input arguments
396 * Note: longOptionTbl can be NULL in the case
397 * where there is no caller defined options
400 assert(synTable
.versionString
);
401 assert(synTable
.subCommandPropsTbl
);
404 versionString
= synTable
.versionString
;
406 /* set global command name */
407 commandName
= getExecBasename(argv
[0]);
409 /* Set unbuffered output */
410 setbuf(stdout
, NULL
);
413 _subCommandProps
= synTable
.subCommandPropsTbl
;
414 _clientOptionTbl
= synTable
.longOptionTbl
;
416 /* There must be at least two arguments */
418 usage(GENERAL_USAGE
);
422 (void) memset(&intLongOpt
[0], 0, sizeof (intLongOpt
));
425 * load standard subcommand options to internal long options table
426 * Two separate getopt_long(3C) tables are used.
428 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
429 intLongOpt
[i
].name
= standardSubCmdOptions
[i
].name
;
430 intLongOpt
[i
].has_arg
= standardSubCmdOptions
[i
].has_arg
;
431 intLongOpt
[i
].flag
= standardSubCmdOptions
[i
].flag
;
432 intLongOpt
[i
].val
= standardSubCmdOptions
[i
].val
;
436 * copy caller's long options into internal long options table
437 * We do this for two reasons:
438 * 1) We need to use the getopt_long option structure internally
439 * 2) We need to prepend the table with the standard option
440 * for all subcommands (currently -?)
442 for (optionTbl
= synTable
.longOptionTbl
;
443 optionTbl
&& optionTbl
->name
; optionTbl
++, i
++) {
444 if (i
> MAXOPTIONS
- 1) {
445 /* option table too long */
448 intLongOpt
[i
].name
= optionTbl
->name
;
449 intLongOpt
[i
].has_arg
= optionTbl
->has_arg
;
450 intLongOpt
[i
].flag
= NULL
;
451 intLongOpt
[i
].val
= optionTbl
->val
;
454 /* set option table global */
455 _longOptions
= &intLongOpt
[0];
459 * Check for help/version request immediately following command
460 * '+' in option string ensures POSIX compliance in getopt_long()
461 * which means that processing will stop at first non-option
464 while ((opt
= getopt_long(argc
, argv
, "+?V", standardCmdOptions
,
469 * getopt can return a '?' when no
470 * option letters match string. Check for
471 * the 'real' '?' in optopt.
477 usage(GENERAL_USAGE
);
481 (void) fprintf(stdout
, "%s: %s %s\n",
482 commandName
, gettext("Version"),
491 * subcommand is always in the second argument. If there is no
492 * recognized subcommand in the second argument, print error,
493 * general usage and then return.
495 if (getSubcommandProps(argv
[1], &subcommand
) != 0) {
496 (void) printf("%s: %s\n", commandName
,
497 gettext("invalid subcommand"));
498 usage(GENERAL_USAGE
);
507 (void) memset(optionStringAll
, 0, sizeof (optionStringAll
));
508 (void) memset(&cmdOptions
[0], 0, sizeof (cmdOptions
));
512 * Build optionStringAll from long options table
514 for (lp
= _longOptions
; lp
->name
; lp
++, j
++) {
515 /* sanity check on string length */
516 if (j
+ 1 >= sizeof (optionStringAll
)) {
517 /* option table too long */
520 optionStringAll
[j
] = lp
->val
;
521 if (lp
->has_arg
== required_argument
) {
522 optionStringAll
[++j
] = ':';
528 * Run getopt for all arguments against all possible options
529 * Store all options/option arguments in an array for retrieval
532 * Once all options are retrieved, a validity check against
533 * subcommand table is performed.
535 while ((opt
= getopt_long(getoptargc
, getoptargv
, optionStringAll
,
536 _longOptions
, NULL
)) != EOF
) {
539 subUsage(DETAIL_USAGE
, subcommand
);
542 cmdOptions
[i
].optval
= opt
;
544 len
= strlen(optarg
);
545 if (len
> sizeof (cmdOptions
[i
].optarg
)
547 (void) printf("%s: %s\n",
549 gettext("option too long"));
553 (void) strncpy(cmdOptions
[i
].optarg
,
562 * increment past last option
564 operInd
= optind
+ 1;
567 * Check validity of given options, if any were given
570 /* get option string for this subcommand */
571 availOptions
= subcommand
->optionString
;
573 if (cmdOptions
[0].optval
!= 0) { /* options were input */
574 if (availOptions
== NULL
) { /* no options permitted */
575 (void) printf("%s: %s\n", commandName
,
576 gettext("no options permitted"));
577 subUsage(DETAIL_USAGE
, subcommand
);
580 for (i
= 0; cmdOptions
[i
].optval
; i
++) {
581 /* is the option in the available option string? */
582 if (!(strchr(availOptions
, cmdOptions
[i
].optval
))) {
583 (void) printf("%s: '-%c': %s\n", commandName
,
584 cmdOptions
[i
].optval
,
585 gettext("invalid option"));
586 subUsage(DETAIL_USAGE
, subcommand
);
589 /* Check for exclusive options */
590 } else if (cmdOptions
[1].optval
!= 0 &&
591 subcommand
->exclusive
&&
592 strchr(subcommand
->exclusive
,
593 cmdOptions
[i
].optval
)) {
594 (void) printf("%s: '-%c': %s\n",
595 commandName
, cmdOptions
[i
].optval
,
596 gettext("is an exclusive option"));
597 subUsage(DETAIL_USAGE
, subcommand
);
601 } else { /* no options were input */
602 if (availOptions
!= NULL
&& subcommand
->required
) {
603 (void) printf("%s: %s\n", commandName
,
604 gettext("at least one option required"));
605 subUsage(DETAIL_USAGE
, subcommand
);
611 * If there are no operands,
612 * check to see if this is okay
614 if ((operInd
== argc
) &&
615 (subcommand
->operand
& OPERAND_MANDATORY
)) {
616 (void) printf("%s: %s %s\n", commandName
, subcommand
->name
,
617 gettext("requires an operand"));
618 subUsage(DETAIL_USAGE
, subcommand
);
623 * If there are more operands,
624 * check to see if this is okay
626 if ((argc
> operInd
) &&
627 (subcommand
->operand
& OPERAND_NONE
)) {
628 (void) fprintf(stderr
, "%s: %s %s\n",
629 commandName
, subcommand
->name
,
630 gettext("takes no operands"));
631 subUsage(DETAIL_USAGE
, subcommand
);
636 * If there is more than one more operand,
637 * check to see if this is okay
639 if ((argc
> operInd
) && ((argc
- operInd
) != 1) &&
640 (subcommand
->operand
& OPERAND_SINGLE
)) {
641 (void) printf("%s: %s %s\n", commandName
,
642 subcommand
->name
, gettext("accepts only a single operand"));
643 subUsage(DETAIL_USAGE
, subcommand
);
647 /* Finished syntax checks */
650 /* Call appropriate function */
651 *funcRet
= subcommand
->handler(argc
- operInd
, &argv
[operInd
],
652 &cmdOptions
[0], callArgs
);