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>
38 #define GENERAL_USAGE 1
40 #define DETAIL_USAGE 3
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 getSubcommand(char *, subcommand_t
**);
67 static char *getExecBasename(char *);
68 static void usage(uint_t
);
69 static void subUsage(uint_t
, subcommand_t
*);
70 static void subUsageObject(uint_t
, subcommand_t
*, object_t
*);
71 static int getObject(char *, object_t
**);
72 static int getObjectRules(uint_t
, objectRules_t
**);
73 static char *getLongOption(int);
74 static optionProp_t
*getOptions(uint_t
, uint_t
);
75 static char *getOptionArgDesc(int);
76 extern void seeMan(void);
79 static struct option
*_longOptions
;
80 static subcommand_t
*_subcommands
;
81 static object_t
*_objects
;
82 static objectRules_t
*_objectRules
;
83 static optionRules_t
*_optionRules
;
84 static optionTbl_t
*_clientOptionTbl
;
85 static char *commandName
;
90 * object - object value
92 * opCmd - pointer to opCmd_t structure allocated by caller
94 * On successful return, opCmd contains the rules for the value in
95 * object. On failure, the contents of opCmd is unspecified.
103 getObjectRules(uint_t object
, objectRules_t
**objectRules
)
107 for (sp
= _objectRules
; sp
->value
; sp
++) {
108 if (sp
->value
== object
) {
118 * arg - pointer to array of char containing object string
121 * object - pointer to object_t structure pointer
122 * on success, contains the matching object structure based on
131 getObject(char *arg
, object_t
**object
)
137 for (op
= _objects
; op
->name
; op
++) {
139 if (len
== strlen(op
->name
) &&
140 strncasecmp(arg
, op
->name
, len
) == 0) {
150 * arg - pointer to array of char containing subcommand string
152 * subcommand - pointer to subcommand_t pointer
153 * on success, contains the matching subcommand structure based on
154 * input subcommand name
158 * non-zero on failure
161 getSubcommand(char *arg
, subcommand_t
**subcommand
)
166 for (sp
= _subcommands
; sp
->name
; sp
++) {
168 if (len
== strlen(sp
->name
) &&
169 strncasecmp(arg
, sp
->name
, len
) == 0) {
179 * object - object for which to get options
180 * subcommand - subcommand for which to get options
183 * on success, optionsProp_t pointer to structure matching input object
185 * on failure, NULL is returned
187 static optionProp_t
*
188 getOptions(uint_t object
, uint_t subcommand
)
191 optionRules_t
*op
= _optionRules
;
192 while (op
&& ((currObject
= op
->objectValue
) != 0)) {
193 if ((currObject
== object
) &&
194 (op
->subcommandValue
== subcommand
)) {
195 return (&(op
->optionProp
));
204 * shortOption - short option character for which to return the
205 * associated long option string
208 * on success, long option name
212 getLongOption(int shortOption
)
215 for (op
= _longOptions
; op
->name
; op
++) {
216 if (shortOption
== op
->val
) {
225 * shortOption - short option character for which to return the
228 * on success, argument string
232 getOptionArgDesc(int shortOption
)
235 for (op
= _clientOptionTbl
; op
->name
; op
++) {
236 if (op
->val
== shortOption
&&
237 op
->has_arg
== required_argument
) {
238 return (op
->argDesc
);
246 * Print usage for a subcommand.
249 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
250 * subcommand - pointer to subcommand_t structure
257 subUsage(uint_t usageType
, subcommand_t
*subcommand
)
263 (void) fprintf(stdout
, "%s:\t%s %s [",
264 gettext("Usage"), commandName
, subcommand
->name
);
266 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
267 (void) fprintf(stdout
, "-%c",
268 standardSubCmdOptions
[i
].val
);
269 if (standardSubCmdOptions
[i
+1].name
)
270 (void) fprintf(stdout
, ",");
273 (void) fprintf(stdout
, "] %s [", "<OBJECT>");
275 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
276 (void) fprintf(stdout
, "-%c",
277 standardSubCmdOptions
[i
].val
);
278 if (standardSubCmdOptions
[i
+1].name
)
279 (void) fprintf(stdout
, ",");
282 (void) fprintf(stdout
, "] %s", "[<OPERAND>]");
283 (void) fprintf(stdout
, "\n");
285 if (usageType
== GENERAL_USAGE
) {
289 (void) fprintf(stdout
, "%s:\n", gettext("Usage by OBJECT"));
292 * iterate through object table
293 * For each object, print appropriate usage
294 * based on rules tables
296 for (objp
= _objects
; objp
->value
; objp
++) {
297 subUsageObject(usageType
, subcommand
, objp
);
299 (void) atexit(seeMan
);
303 * Print usage for a subcommand and object.
306 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
307 * subcommand - pointer to subcommand_t structure
308 * objp - pointer to a object_t structure
315 subUsageObject(uint_t usageType
, subcommand_t
*subcommand
, object_t
*objp
)
318 objectRules_t
*objRules
= NULL
;
319 opCmd_t
*opCmd
= NULL
;
320 optionProp_t
*options
;
325 if (getObjectRules(objp
->value
, &objRules
) != 0) {
327 * internal subcommand rules table error
328 * no object entry in object
333 opCmd
= &(objRules
->opCmd
);
335 if (opCmd
->invOpCmd
& subcommand
->value
) {
339 options
= getOptions(objp
->value
, subcommand
->value
);
341 /* print generic subcommand usage */
342 (void) fprintf(stdout
, "\t%s %s ", commandName
, subcommand
->name
);
345 (void) fprintf(stdout
, "%s ", objp
->name
);
347 /* print options if applicable */
348 if (options
!= NULL
) {
349 if (options
->required
) {
350 (void) fprintf(stdout
, "%s", gettext("<"));
352 (void) fprintf(stdout
, "%s", gettext("["));
354 (void) fprintf(stdout
, "%s", gettext("OPTIONS"));
355 if (options
->required
) {
356 (void) fprintf(stdout
, "%s ", gettext(">"));
358 (void) fprintf(stdout
, "%s ", gettext("]"));
362 /* print operand requirements */
363 if (opCmd
->optOpCmd
& subcommand
->value
) {
364 (void) fprintf(stdout
, gettext("["));
366 if (!(opCmd
->noOpCmd
& subcommand
->value
)) {
367 (void) fprintf(stdout
, gettext("<"));
368 if (objRules
->operandDefinition
) {
369 (void) fprintf(stdout
, "%s",
370 objRules
->operandDefinition
);
373 * Missing operand description
379 if (opCmd
->multOpCmd
& subcommand
->value
) {
380 (void) fprintf(stdout
, gettext(" ..."));
382 if (!(opCmd
->noOpCmd
& subcommand
->value
)) {
383 (void) fprintf(stdout
, gettext(">"));
385 if (opCmd
->optOpCmd
& subcommand
->value
) {
386 (void) fprintf(stdout
, gettext("]"));
389 if (usageType
== HELP_USAGE
) {
390 (void) fprintf(stdout
, "\n");
394 /* print options for subcommand, object */
395 if (options
!= NULL
&& options
->optionString
!= NULL
) {
396 (void) fprintf(stdout
, "\n\t%s:", gettext("OPTIONS"));
397 for (i
= 0; i
< strlen(options
->optionString
); i
++) {
398 if ((longOpt
= getLongOption(
399 options
->optionString
[i
]))
401 /* no long option exists for short option */
404 (void) fprintf(stdout
, "\n\t\t-%c, --%s ",
405 options
->optionString
[i
], longOpt
);
407 getOptionArgDesc(options
->optionString
[i
]);
408 if (optionArgDesc
!= NULL
) {
409 (void) fprintf(stdout
, "<%s>", optionArgDesc
);
411 if (options
->exclusive
&&
412 strchr(options
->exclusive
,
413 options
->optionString
[i
])) {
414 (void) fprintf(stdout
, " (%s)",
415 gettext("exclusive"));
419 (void) fprintf(stdout
, "\n");
420 (void) atexit(seeMan
);
425 * type of usage statement to print
428 * return value of subUsage
431 usage(uint_t usageType
)
434 subcommand_t subcommand
;
437 /* print general command usage */
438 (void) fprintf(stdout
, "%s:\t%s ",
439 gettext("Usage"), commandName
);
441 for (i
= 0; standardCmdOptions
[i
].name
; i
++) {
442 (void) fprintf(stdout
, "-%c",
443 standardCmdOptions
[i
].val
);
444 if (standardCmdOptions
[i
+1].name
)
445 (void) fprintf(stdout
, ",");
448 if (usageType
== HELP_USAGE
|| usageType
== GENERAL_USAGE
) {
449 for (i
= 0; standardCmdOptions
[i
].name
; i
++) {
455 (void) fprintf(stdout
, "\n");
458 /* print all subcommand usage */
459 for (sp
= _subcommands
; sp
->name
; sp
++) {
460 subcommand
.name
= sp
->name
;
461 subcommand
.value
= sp
->value
;
462 if (usageType
== HELP_USAGE
) {
463 (void) fprintf(stdout
, "\n");
465 subUsage(usageType
, &subcommand
);
467 (void) atexit(seeMan
);
472 * execFullName - exec name of program (argv[0])
475 * command name portion of execFullName
478 getExecBasename(char *execFullname
)
480 char *lastSlash
, *execBasename
;
482 /* guard against '/' at end of command invocation */
484 lastSlash
= strrchr(execFullname
, '/');
485 if (lastSlash
== NULL
) {
486 execBasename
= execFullname
;
489 execBasename
= lastSlash
+ 1;
490 if (*execBasename
== '\0') {
497 return (execBasename
);
501 * cmdParse is a parser that checks syntax of the input command against
502 * various rules tables.
504 * It provides usage feedback based upon the passed rules tables by calling
505 * two usage functions, usage, subUsage, and subUsageObject handling command,
506 * subcommand and object usage respectively.
508 * When syntax is successfully validated, the associated function is called
509 * using the subcommands table functions.
511 * Syntax is as follows:
512 * command subcommand object [<options>] [<operand>]
514 * There are two standard short and long options assumed:
515 * -?, --help Provides usage on a command or subcommand
516 * and stops further processing of the arguments
518 * -V, --version Provides version information on the command
519 * and stops further processing of the arguments
521 * These options are loaded by this function.
524 * argc, argv from main
525 * syntax rules tables (synTables_t structure)
526 * callArgs - void * passed by caller to be passed to subcommand function
529 * funcRet - pointer to int that holds subcommand function return value
533 * zero on successful syntax parse and function call
535 * 1 on unsuccessful syntax parse (no function has been called)
536 * This could be due to a version or help call or simply a
537 * general usage call.
539 * -1 check errno, call failed
541 * This module is not MT-safe.
545 cmdParse(int argc
, char *argv
[], synTables_t synTable
, void *callArgs
,
555 char optionStringAll
[MAXOPTIONSTRING
+ 1];
556 optionProp_t
*availOptions
;
557 objectRules_t
*objRules
= NULL
;
558 opCmd_t
*opCmd
= NULL
;
559 subcommand_t
*subcommand
;
561 cmdOptions_t cmdOptions
[MAXOPTIONS
+ 1];
563 optionTbl_t
*optionTbl
;
564 struct option intLongOpt
[MAXOPTIONS
+ 1];
567 * Check for NULLs on mandatory input arguments
569 * Note: longOptionTbl and optionRulesTbl can be NULL in the case
570 * where there is no caller defined options
573 if (synTable
.versionString
== NULL
||
574 synTable
.subcommandTbl
== NULL
||
575 synTable
.objectRulesTbl
== NULL
||
576 synTable
.objectTbl
== NULL
||
582 versionString
= synTable
.versionString
;
584 /* set global command name */
585 commandName
= getExecBasename(argv
[0]);
587 /* Set unbuffered output */
588 setbuf(stdout
, NULL
);
591 _subcommands
= synTable
.subcommandTbl
;
592 _objectRules
= synTable
.objectRulesTbl
;
593 _optionRules
= synTable
.optionRulesTbl
;
594 _objects
= synTable
.objectTbl
;
595 _clientOptionTbl
= synTable
.longOptionTbl
;
597 /* There must be at least two arguments */
599 usage(GENERAL_USAGE
);
603 (void) memset(&intLongOpt
[0], 0, sizeof (intLongOpt
));
606 * load standard subcommand options to internal long options table
607 * Two separate getopt_long(3C) tables are used.
609 for (i
= 0; standardSubCmdOptions
[i
].name
; i
++) {
610 intLongOpt
[i
].name
= standardSubCmdOptions
[i
].name
;
611 intLongOpt
[i
].has_arg
= standardSubCmdOptions
[i
].has_arg
;
612 intLongOpt
[i
].flag
= standardSubCmdOptions
[i
].flag
;
613 intLongOpt
[i
].val
= standardSubCmdOptions
[i
].val
;
617 * copy caller's long options into internal long options table
618 * We do this for two reasons:
619 * 1) We need to use the getopt_long option structure internally
620 * 2) We need to prepend the table with the standard option
621 * for all subcommands (currently -?)
623 for (optionTbl
= synTable
.longOptionTbl
;
624 optionTbl
&& optionTbl
->name
; optionTbl
++, i
++) {
625 if (i
> MAXOPTIONS
- 1) {
626 /* option table too long */
629 intLongOpt
[i
].name
= optionTbl
->name
;
630 intLongOpt
[i
].has_arg
= optionTbl
->has_arg
;
631 intLongOpt
[i
].flag
= NULL
;
632 intLongOpt
[i
].val
= optionTbl
->val
;
635 /* set option table global */
636 _longOptions
= &intLongOpt
[0];
640 * Check for help/version request immediately following command
641 * '+' in option string ensures POSIX compliance in getopt_long()
642 * which means that processing will stop at first non-option
645 while ((opt
= getopt_long(argc
, argv
, "+?V", standardCmdOptions
,
650 * getopt can return a '?' when no
651 * option letters match string. Check for
652 * the 'real' '?' in optopt.
658 usage(GENERAL_USAGE
);
662 (void) fprintf(stdout
, "%s: %s %s\n",
663 commandName
, gettext("Version"),
665 (void) atexit(seeMan
);
673 * subcommand is always in the second argument. If there is no
674 * recognized subcommand in the second argument, print error,
675 * general usage and then return.
677 if (getSubcommand(argv
[1], &subcommand
) != 0) {
678 (void) fprintf(stderr
, "%s: %s\n",
679 commandName
, gettext("invalid subcommand"));
680 usage(GENERAL_USAGE
);
685 (void) fprintf(stderr
, "%s: %s\n",
686 commandName
, gettext("missing object"));
687 subUsage(GENERAL_USAGE
, subcommand
);
688 (void) atexit(seeMan
);
697 while ((opt
= getopt_long(getoptargc
, getoptargv
, "+?",
698 standardSubCmdOptions
, NULL
)) != EOF
) {
702 * getopt can return a '?' when no
703 * option letters match string. Check for
704 * the 'real' '?' in optopt.
707 subUsage(HELP_USAGE
, subcommand
);
710 subUsage(GENERAL_USAGE
, subcommand
);
720 * object is always in the third argument. If there is no
721 * recognized object in the third argument, print error,
722 * help usage for the subcommand and then return.
724 if (getObject(argv
[2], &object
) != 0) {
725 (void) fprintf(stderr
, "%s: %s\n",
726 commandName
, gettext("invalid object"));
727 subUsage(HELP_USAGE
, subcommand
);
732 if (getObjectRules(object
->value
, &objRules
) != 0) {
734 * internal subcommand rules table error
735 * no object entry in object table
740 opCmd
= &(objRules
->opCmd
);
743 * Is command valid for this object?
745 if (opCmd
->invOpCmd
& subcommand
->value
) {
746 (void) fprintf(stderr
, "%s: %s %s\n", commandName
,
747 gettext("invalid subcommand for"), object
->name
);
748 subUsage(HELP_USAGE
, subcommand
);
753 * offset getopt arg begin since
754 * getopt(3C) assumes options
755 * follow first argument
763 (void) memset(optionStringAll
, 0, sizeof (optionStringAll
));
764 (void) memset(&cmdOptions
[0], 0, sizeof (cmdOptions
));
768 * Build optionStringAll from long options table
770 for (lp
= _longOptions
; lp
->name
; lp
++, j
++) {
771 /* sanity check on string length */
772 if (j
+ 1 >= sizeof (optionStringAll
)) {
773 /* option table too long */
776 optionStringAll
[j
] = lp
->val
;
777 if (lp
->has_arg
== required_argument
) {
778 optionStringAll
[++j
] = ':';
784 * Run getopt for all arguments against all possible options
785 * Store all options/option arguments in an array for retrieval
787 * Once all options are retrieved, check against object
788 * and subcommand (option rules table) for validity.
789 * This is done later.
791 while ((opt
= getopt_long(getoptargc
, getoptargv
, optionStringAll
,
792 _longOptions
, NULL
)) != EOF
) {
796 subUsageObject(DETAIL_USAGE
,
800 subUsage(GENERAL_USAGE
, subcommand
);
804 cmdOptions
[i
].optval
= opt
;
806 len
= strlen(optarg
);
807 if (len
> sizeof (cmdOptions
[i
].optarg
)
809 (void) fprintf(stderr
,
812 gettext("option too long"));
816 (void) strncpy(cmdOptions
[i
].optarg
,
825 * increment past last option
827 operInd
= optind
+ 2;
830 * Check validity of given options, if any were given
833 /* get option string for this object and subcommand */
834 availOptions
= getOptions(object
->value
, subcommand
->value
);
836 if (cmdOptions
[0].optval
!= 0) { /* options were input */
837 if (availOptions
== NULL
) { /* no options permitted */
838 (void) fprintf(stderr
, "%s: %s\n",
839 commandName
, gettext("no options permitted"));
840 subUsageObject(HELP_USAGE
, subcommand
, object
);
843 for (i
= 0; cmdOptions
[i
].optval
; i
++) {
844 /* Check for invalid options */
845 if (availOptions
->optionString
== NULL
) {
847 * internal option table error
848 * There must be an option string if
849 * there is an entry in the table
853 /* is the option in the available option string? */
855 if (!(strchr(availOptions
->optionString
,
856 cmdOptions
[i
].optval
))) {
857 (void) fprintf(stderr
,
859 commandName
, cmdOptions
[i
].optval
,
860 gettext("invalid option"));
861 subUsageObject(DETAIL_USAGE
, subcommand
,
865 /* Check for exclusive options */
866 } else if (cmdOptions
[1].optval
!= 0 &&
868 availOptions
->exclusive
&&
869 strchr(availOptions
->exclusive
,
870 cmdOptions
[i
].optval
)) {
872 (void) fprintf(stderr
,
875 commandName
, cmdOptions
[i
].optval
,
876 gettext("is an exclusive option"));
878 subUsageObject(DETAIL_USAGE
, subcommand
,
883 } else { /* no options were input */
884 if (availOptions
!= NULL
&&
885 (availOptions
->required
)) {
886 (void) fprintf(stderr
, "%s: %s\n",
888 gettext("at least one option required"));
890 subUsageObject(DETAIL_USAGE
, subcommand
,
897 * If there are no more arguments (operands),
898 * check to see if this is okay
900 if ((operInd
== argc
) &&
901 (opCmd
->reqOpCmd
& subcommand
->value
)) {
902 (void) fprintf(stderr
, "%s: %s %s %s\n",
903 commandName
, subcommand
->name
,
904 object
->name
, gettext("requires an operand"));
906 subUsageObject(HELP_USAGE
, subcommand
, object
);
907 (void) atexit(seeMan
);
912 * If there are more operands,
913 * check to see if this is okay
915 if ((argc
> operInd
) &&
916 (opCmd
->noOpCmd
& subcommand
->value
)) {
917 (void) fprintf(stderr
, "%s: %s %s %s\n",
918 commandName
, subcommand
->name
,
919 object
->name
, gettext("takes no operands"));
920 subUsageObject(HELP_USAGE
, subcommand
, object
);
925 * If there is more than one more operand,
926 * check to see if this is okay
928 if ((argc
> operInd
) && ((argc
- operInd
) != 1) &&
929 !(opCmd
->multOpCmd
& subcommand
->value
)) {
930 (void) fprintf(stderr
, "%s: %s %s %s\n",
931 commandName
, subcommand
->name
, object
->name
,
932 gettext("accepts only a single operand"));
933 subUsageObject(HELP_USAGE
, subcommand
, object
);
937 /* Finished syntax checks */
940 /* Call appropriate function */
942 return (subcommand
->handler(argc
- operInd
, &argv
[operInd
],
943 object
->value
, &cmdOptions
[0], callArgs
, funcRet
));