8672 proc_t changes broke genunix dmods and walker
[unleashed.git] / usr / src / cmd / mpathadm / cmdparse.c
blob9080e1cfdda7c77e15c1b5677190037f0e38a213
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <libintl.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <getopt.h>
35 #include "cmdparse.h"
37 /* Usage types */
38 #define GENERAL_USAGE 1
39 #define HELP_USAGE 2
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'},
56 {NULL, 0, NULL, 0}
59 /* standard subcommand options table to support -? */
60 struct option standardSubCmdOptions[] = {
61 {"help", no_argument, NULL, '?'},
62 {NULL, 0, NULL, 0}
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);
77 /* global data */
78 static struct option *_longOptions;
79 static subcommand_t *_subcommands;
80 static object_t *_objects;
81 static objectRules_t *_objectRules;
82 static optionRules_t *_optionRules;
83 static optionTbl_t *_clientOptionTbl;
84 static char *commandName;
88 * input:
89 * object - object value
90 * output:
91 * opCmd - pointer to opCmd_t structure allocated by caller
93 * On successful return, opCmd contains the rules for the value in
94 * object. On failure, the contents of opCmd is unspecified.
96 * Returns:
97 * zero on success
98 * non-zero on failure
101 static int
102 getObjectRules(uint_t object, objectRules_t **objectRules)
104 objectRules_t *sp;
106 for (sp = _objectRules; sp->value; sp++) {
107 if (sp->value == object) {
108 *objectRules = sp;
109 return (0);
112 return (1);
116 * input:
117 * arg - pointer to array of char containing object string
119 * output:
120 * object - pointer to object_t structure pointer
121 * on success, contains the matching object structure based on
122 * input object name
124 * Returns:
125 * zero on success
126 * non-zero otherwise
129 static int
130 getObject(char *arg, object_t **object)
133 object_t *op;
134 int len;
136 for (op = _objects; op->name; op++) {
137 len = strlen(arg);
138 if (len == strlen(op->name) &&
139 strncasecmp(arg, op->name, len) == 0) {
140 *object = op;
141 return (0);
144 return (1);
148 * input:
149 * arg - pointer to array of char containing subcommand string
150 * output:
151 * subcommand - pointer to subcommand_t pointer
152 * on success, contains the matching subcommand structure based on
153 * input subcommand name
155 * Returns:
156 * zero on success
157 * non-zero on failure
159 static int
160 getSubcommand(char *arg, subcommand_t **subcommand)
162 subcommand_t *sp;
163 int len;
165 for (sp = _subcommands; sp->name; sp++) {
166 len = strlen(arg);
167 if (len == strlen(sp->name) &&
168 strncasecmp(arg, sp->name, len) == 0) {
169 *subcommand = sp;
170 return (0);
173 return (1);
177 * input:
178 * object - object for which to get options
179 * subcommand - subcommand for which to get options
181 * Returns:
182 * on success, optionsProp_t pointer to structure matching input object
183 * value
184 * on failure, NULL is returned
186 static optionProp_t *
187 getOptions(uint_t object, uint_t subcommand)
189 uint_t currObject;
190 optionRules_t *op = _optionRules;
191 while (op && ((currObject = op->objectValue) != 0)) {
192 if ((currObject == object) &&
193 (op->subcommandValue == subcommand)) {
194 return (&(op->optionProp));
196 op++;
198 return (NULL);
202 * input:
203 * shortOption - short option character for which to return the
204 * associated long option string
206 * Returns:
207 * on success, long option name
208 * on failure, NULL
210 static char *
211 getLongOption(int shortOption)
213 struct option *op;
214 for (op = _longOptions; op->name; op++) {
215 if (shortOption == op->val) {
216 return (op->name);
219 return (NULL);
223 * input
224 * shortOption - short option character for which to return the
225 * option argument
226 * Returns:
227 * on success, argument string
228 * on failure, NULL
230 static char *
231 getOptionArgDesc(int shortOption)
233 optionTbl_t *op;
234 for (op = _clientOptionTbl; op->name; op++) {
235 if (op->val == shortOption &&
236 op->has_arg == required_argument) {
237 return (op->argDesc);
240 return (NULL);
245 * Print usage for a subcommand.
247 * input:
248 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
249 * subcommand - pointer to subcommand_t structure
251 * Returns:
252 * none
255 static void
256 subUsage(uint_t usageType, subcommand_t *subcommand)
258 int i;
259 object_t *objp;
262 (void) fprintf(stdout, "%s:\t%s %s [",
263 gettext("Usage"), commandName, subcommand->name);
265 for (i = 0; standardSubCmdOptions[i].name; i++) {
266 (void) fprintf(stdout, "-%c",
267 standardSubCmdOptions[i].val);
268 if (standardSubCmdOptions[i+1].name)
269 (void) fprintf(stdout, ",");
272 (void) fprintf(stdout, "] %s [", "<OBJECT>");
274 for (i = 0; standardSubCmdOptions[i].name; i++) {
275 (void) fprintf(stdout, "-%c",
276 standardSubCmdOptions[i].val);
277 if (standardSubCmdOptions[i+1].name)
278 (void) fprintf(stdout, ",");
281 (void) fprintf(stdout, "] %s", "[<OPERAND>]");
282 (void) fprintf(stdout, "\n");
284 if (usageType == GENERAL_USAGE) {
285 return;
288 (void) fprintf(stdout, "%s:\n", gettext("Usage by OBJECT"));
291 * iterate through object table
292 * For each object, print appropriate usage
293 * based on rules tables
295 for (objp = _objects; objp->value; objp++) {
296 subUsageObject(usageType, subcommand, objp);
301 * Print usage for a subcommand and object.
303 * input:
304 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE
305 * subcommand - pointer to subcommand_t structure
306 * objp - pointer to a object_t structure
308 * Returns:
309 * none
312 static void
313 subUsageObject(uint_t usageType, subcommand_t *subcommand, object_t *objp)
315 int i;
316 objectRules_t *objRules = NULL;
317 opCmd_t *opCmd = NULL;
318 optionProp_t *options;
319 char *optionArgDesc;
320 char *longOpt;
323 if (getObjectRules(objp->value, &objRules) != 0) {
325 * internal subcommand rules table error
326 * no object entry in object
328 assert(0);
331 opCmd = &(objRules->opCmd);
333 if (opCmd->invOpCmd & subcommand->value) {
334 return;
337 options = getOptions(objp->value, subcommand->value);
339 /* print generic subcommand usage */
340 (void) fprintf(stdout, "\t%s %s ", commandName, subcommand->name);
342 /* print object */
343 (void) fprintf(stdout, "%s ", objp->name);
345 /* print options if applicable */
346 if (options != NULL) {
347 if (options->required) {
348 (void) fprintf(stdout, "%s", gettext("<"));
349 } else {
350 (void) fprintf(stdout, "%s", gettext("["));
352 (void) fprintf(stdout, "%s", gettext("OPTIONS"));
353 if (options->required) {
354 (void) fprintf(stdout, "%s ", gettext(">"));
355 } else {
356 (void) fprintf(stdout, "%s ", gettext("]"));
360 /* print operand requirements */
361 if (opCmd->optOpCmd & subcommand->value) {
362 (void) fprintf(stdout, gettext("["));
364 if (!(opCmd->noOpCmd & subcommand->value)) {
365 (void) fprintf(stdout, gettext("<"));
366 if (objRules->operandDefinition) {
367 (void) fprintf(stdout, "%s",
368 objRules->operandDefinition);
369 } else {
371 * Missing operand description
372 * from table
374 assert(0);
377 if (opCmd->multOpCmd & subcommand->value) {
378 (void) fprintf(stdout, gettext(" ..."));
380 if (!(opCmd->noOpCmd & subcommand->value)) {
381 (void) fprintf(stdout, gettext(">"));
383 if (opCmd->optOpCmd & subcommand->value) {
384 (void) fprintf(stdout, gettext("]"));
387 if (usageType == HELP_USAGE) {
388 (void) fprintf(stdout, "\n");
389 return;
392 /* print options for subcommand, object */
393 if (options != NULL && options->optionString != NULL) {
394 (void) fprintf(stdout, "\n\t%s:", gettext("OPTIONS"));
395 for (i = 0; i < strlen(options->optionString); i++) {
396 if ((longOpt = getLongOption(
397 options->optionString[i]))
398 == NULL) {
399 /* no long option exists for short option */
400 assert(0);
402 (void) fprintf(stdout, "\n\t\t-%c, --%s ",
403 options->optionString[i], longOpt);
404 optionArgDesc =
405 getOptionArgDesc(options->optionString[i]);
406 if (optionArgDesc != NULL) {
407 (void) fprintf(stdout, "<%s>", optionArgDesc);
409 if (options->exclusive &&
410 strchr(options->exclusive,
411 options->optionString[i])) {
412 (void) fprintf(stdout, " (%s)",
413 gettext("exclusive"));
417 (void) fprintf(stdout, "\n");
421 * input:
422 * type of usage statement to print
424 * Returns:
425 * return value of subUsage
427 static void
428 usage(uint_t usageType)
430 int i;
431 subcommand_t subcommand;
432 subcommand_t *sp;
434 /* print general command usage */
435 (void) fprintf(stdout, "%s:\t%s ",
436 gettext("Usage"), commandName);
438 for (i = 0; standardCmdOptions[i].name; i++) {
439 (void) fprintf(stdout, "-%c",
440 standardCmdOptions[i].val);
441 if (standardCmdOptions[i+1].name)
442 (void) fprintf(stdout, ",");
445 if (usageType == HELP_USAGE || usageType == GENERAL_USAGE) {
446 for (i = 0; standardSubCmdOptions[i].name; i++) {
447 (void) fprintf(stdout, ",--%s",
448 standardSubCmdOptions[i].name);
449 if (standardSubCmdOptions[i+1].name)
450 (void) fprintf(stdout, ",");
454 (void) fprintf(stdout, "\n");
457 /* print all subcommand usage */
458 for (sp = _subcommands; sp->name; sp++) {
459 subcommand.name = sp->name;
460 subcommand.value = sp->value;
461 if (usageType == HELP_USAGE) {
462 (void) fprintf(stdout, "\n");
464 subUsage(usageType, &subcommand);
469 * input:
470 * execFullName - exec name of program (argv[0])
472 * Returns:
473 * command name portion of execFullName
475 static char *
476 getExecBasename(char *execFullname)
478 char *lastSlash, *execBasename;
480 /* guard against '/' at end of command invocation */
481 for (;;) {
482 lastSlash = strrchr(execFullname, '/');
483 if (lastSlash == NULL) {
484 execBasename = execFullname;
485 break;
486 } else {
487 execBasename = lastSlash + 1;
488 if (*execBasename == '\0') {
489 *lastSlash = '\0';
490 continue;
492 break;
495 return (execBasename);
499 * cmdParse is a parser that checks syntax of the input command against
500 * various rules tables.
502 * It provides usage feedback based upon the passed rules tables by calling
503 * two usage functions, usage, subUsage, and subUsageObject handling command,
504 * subcommand and object usage respectively.
506 * When syntax is successfully validated, the associated function is called
507 * using the subcommands table functions.
509 * Syntax is as follows:
510 * command subcommand object [<options>] [<operand>]
512 * There are two standard short and long options assumed:
513 * -?, --help Provides usage on a command or subcommand
514 * and stops further processing of the arguments
516 * -V, --version Provides version information on the command
517 * and stops further processing of the arguments
519 * These options are loaded by this function.
521 * input:
522 * argc, argv from main
523 * syntax rules tables (synTables_t structure)
524 * callArgs - void * passed by caller to be passed to subcommand function
526 * output:
527 * funcRet - pointer to int that holds subcommand function return value
529 * Returns:
531 * zero on successful syntax parse and function call
533 * 1 on unsuccessful syntax parse (no function has been called)
534 * This could be due to a version or help call or simply a
535 * general usage call.
537 * -1 check errno, call failed
539 * This module is not MT-safe.
543 cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs,
544 int *funcRet)
546 int getoptargc;
547 char **getoptargv;
548 int opt;
549 int operInd;
550 int i, j;
551 int len;
552 char *versionString;
553 char optionStringAll[MAXOPTIONSTRING + 1];
554 optionProp_t *availOptions;
555 objectRules_t *objRules = NULL;
556 opCmd_t *opCmd = NULL;
557 subcommand_t *subcommand;
558 object_t *object;
559 cmdOptions_t cmdOptions[MAXOPTIONS + 1];
560 struct option *lp;
561 optionTbl_t *optionTbl;
562 struct option intLongOpt[MAXOPTIONS + 1];
565 * Check for NULLs on mandatory input arguments
567 * Note: longOptionTbl and optionRulesTbl can be NULL in the case
568 * where there is no caller defined options
571 if (synTable.versionString == NULL ||
572 synTable.subcommandTbl == NULL ||
573 synTable.objectRulesTbl == NULL ||
574 synTable.objectTbl == NULL ||
575 funcRet == NULL) {
576 assert(0);
580 versionString = synTable.versionString;
582 /* set global command name */
583 commandName = getExecBasename(argv[0]);
585 /* Set unbuffered output */
586 setbuf(stdout, NULL);
588 /* load globals */
589 _subcommands = synTable.subcommandTbl;
590 _objectRules = synTable.objectRulesTbl;
591 _optionRules = synTable.optionRulesTbl;
592 _objects = synTable.objectTbl;
593 _clientOptionTbl = synTable.longOptionTbl;
595 /* There must be at least two arguments */
596 if (argc < 2) {
597 usage(GENERAL_USAGE);
598 return (1);
601 (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt));
604 * load standard subcommand options to internal long options table
605 * Two separate getopt_long(3C) tables are used.
607 for (i = 0; standardSubCmdOptions[i].name; i++) {
608 intLongOpt[i].name = standardSubCmdOptions[i].name;
609 intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg;
610 intLongOpt[i].flag = standardSubCmdOptions[i].flag;
611 intLongOpt[i].val = standardSubCmdOptions[i].val;
615 * copy caller's long options into internal long options table
616 * We do this for two reasons:
617 * 1) We need to use the getopt_long option structure internally
618 * 2) We need to prepend the table with the standard option
619 * for all subcommands (currently -?)
621 for (optionTbl = synTable.longOptionTbl;
622 optionTbl && optionTbl->name; optionTbl++, i++) {
623 if (i > MAXOPTIONS - 1) {
624 /* option table too long */
625 assert(0);
627 intLongOpt[i].name = optionTbl->name;
628 intLongOpt[i].has_arg = optionTbl->has_arg;
629 intLongOpt[i].flag = NULL;
630 intLongOpt[i].val = optionTbl->val;
633 /* set option table global */
634 _longOptions = &intLongOpt[0];
638 * Check for help/version request immediately following command
639 * '+' in option string ensures POSIX compliance in getopt_long()
640 * which means that processing will stop at first non-option
641 * argument.
643 while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions,
644 NULL)) != EOF) {
645 switch (opt) {
646 case '?':
648 * getopt can return a '?' when no
649 * option letters match string. Check for
650 * the 'real' '?' in optopt.
652 if (optopt == '?') {
653 usage(HELP_USAGE);
654 return (1);
655 } else {
656 usage(GENERAL_USAGE);
657 return (1);
659 case 'V':
660 (void) fprintf(stdout, "%s: %s %s\n",
661 commandName, gettext("Version"),
662 versionString);
663 return (1);
664 default:
665 break;
670 * subcommand is always in the second argument. If there is no
671 * recognized subcommand in the second argument, print error,
672 * general usage and then return.
674 if (getSubcommand(argv[1], &subcommand) != 0) {
675 (void) fprintf(stderr, "%s: %s\n",
676 commandName, gettext("invalid subcommand"));
677 usage(GENERAL_USAGE);
678 return (1);
681 if (argc == 2) {
682 (void) fprintf(stderr, "%s: %s\n",
683 commandName, gettext("missing object"));
684 subUsage(GENERAL_USAGE, subcommand);
685 return (1);
688 getoptargv = argv;
689 getoptargv++;
690 getoptargc = argc;
691 getoptargc -= 1;
693 while ((opt = getopt_long(getoptargc, getoptargv, "+?",
694 standardSubCmdOptions, NULL)) != EOF) {
695 switch (opt) {
696 case '?':
698 * getopt can return a '?' when no
699 * option letters match string. Check for
700 * the 'real' '?' in optopt.
702 if (optopt == '?') {
703 subUsage(HELP_USAGE, subcommand);
704 return (1);
705 } else {
706 subUsage(GENERAL_USAGE, subcommand);
707 return (1);
709 default:
710 break;
716 * object is always in the third argument. If there is no
717 * recognized object in the third argument, print error,
718 * help usage for the subcommand and then return.
720 if (getObject(argv[2], &object) != 0) {
721 (void) fprintf(stderr, "%s: %s\n",
722 commandName, gettext("invalid object"));
723 subUsage(HELP_USAGE, subcommand);
724 return (1);
727 if (getObjectRules(object->value, &objRules) != 0) {
729 * internal subcommand rules table error
730 * no object entry in object table
732 assert(0);
735 opCmd = &(objRules->opCmd);
738 * Is command valid for this object?
740 if (opCmd->invOpCmd & subcommand->value) {
741 (void) fprintf(stderr, "%s: %s %s\n", commandName,
742 gettext("invalid subcommand for"), object->name);
743 subUsage(HELP_USAGE, subcommand);
744 return (1);
748 * offset getopt arg begin since
749 * getopt(3C) assumes options
750 * follow first argument
752 getoptargv = argv;
753 getoptargv++;
754 getoptargv++;
755 getoptargc = argc;
756 getoptargc -= 2;
758 (void) memset(optionStringAll, 0, sizeof (optionStringAll));
759 (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions));
761 j = 0;
763 * Build optionStringAll from long options table
765 for (lp = _longOptions; lp->name; lp++, j++) {
766 /* sanity check on string length */
767 if (j + 1 >= sizeof (optionStringAll)) {
768 /* option table too long */
769 assert(0);
771 optionStringAll[j] = lp->val;
772 if (lp->has_arg == required_argument) {
773 optionStringAll[++j] = ':';
777 i = 0;
779 * Run getopt for all arguments against all possible options
780 * Store all options/option arguments in an array for retrieval
781 * later.
782 * Once all options are retrieved, check against object
783 * and subcommand (option rules table) for validity.
784 * This is done later.
786 while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll,
787 _longOptions, NULL)) != EOF) {
788 switch (opt) {
789 case '?':
790 if (optopt == '?') {
791 subUsageObject(DETAIL_USAGE,
792 subcommand, object);
793 return (1);
794 } else {
795 subUsage(GENERAL_USAGE, subcommand);
796 return (1);
798 default:
799 cmdOptions[i].optval = opt;
800 if (optarg) {
801 len = strlen(optarg);
802 if (len > sizeof (cmdOptions[i].optarg)
803 - 1) {
804 (void) fprintf(stderr,
805 "%s: %s\n", commandName,
806 gettext("option too long"));
807 errno = EINVAL;
808 return (-1);
810 (void) strncpy(cmdOptions[i].optarg,
811 optarg, len);
813 i++;
814 break;
819 * increment past last option
821 operInd = optind + 2;
824 * Check validity of given options, if any were given
827 /* get option string for this object and subcommand */
828 availOptions = getOptions(object->value, subcommand->value);
830 if (cmdOptions[0].optval != 0) { /* options were input */
831 if (availOptions == NULL) { /* no options permitted */
832 (void) fprintf(stderr, "%s: %s\n",
833 commandName, gettext("no options permitted"));
834 subUsageObject(HELP_USAGE, subcommand, object);
835 return (1);
837 for (i = 0; cmdOptions[i].optval; i++) {
838 /* Check for invalid options */
839 if (availOptions->optionString == NULL) {
841 * internal option table error
842 * There must be an option string if
843 * there is an entry in the table
845 assert(0);
847 /* is the option in the available option string? */
849 if (!(strchr(availOptions->optionString,
850 cmdOptions[i].optval))) {
851 (void) fprintf(stderr,
852 "%s: '-%c': %s\n",
853 commandName, cmdOptions[i].optval,
854 gettext("invalid option"));
855 subUsageObject(DETAIL_USAGE, subcommand,
856 object);
857 return (1);
859 /* Check for exclusive options */
860 } else if (cmdOptions[1].optval != 0 &&
861 availOptions->exclusive &&
862 strchr(availOptions->exclusive,
863 cmdOptions[i].optval)) {
864 (void) fprintf(stderr,
865 "%s: '-%c': %s\n",
866 commandName, cmdOptions[i].optval,
867 gettext("is an exclusive option"));
868 subUsageObject(DETAIL_USAGE, subcommand,
869 object);
870 return (1);
873 } else { /* no options were input */
874 if (availOptions != NULL &&
875 (availOptions->required)) {
876 (void) fprintf(stderr, "%s: %s\n",
877 commandName,
878 gettext("at least one option required"));
879 subUsageObject(DETAIL_USAGE, subcommand,
880 object);
881 return (1);
886 * If there are no more arguments (operands),
887 * check to see if this is okay
889 if ((operInd == argc) &&
890 (opCmd->reqOpCmd & subcommand->value)) {
891 (void) fprintf(stderr, "%s: %s %s %s\n",
892 commandName, subcommand->name,
893 object->name, gettext("requires an operand"));
894 subUsageObject(HELP_USAGE, subcommand, object);
895 return (1);
899 * If there are more operands,
900 * check to see if this is okay
902 if ((argc > operInd) &&
903 (opCmd->noOpCmd & subcommand->value)) {
904 (void) fprintf(stderr, "%s: %s %s %s\n",
905 commandName, subcommand->name,
906 object->name, gettext("takes no operands"));
907 subUsageObject(HELP_USAGE, subcommand, object);
908 return (1);
912 * If there is more than one more operand,
913 * check to see if this is okay
915 if ((argc > operInd) && ((argc - operInd) != 1) &&
916 !(opCmd->multOpCmd & subcommand->value)) {
917 (void) fprintf(stderr, "%s: %s %s %s\n",
918 commandName, subcommand->name, object->name,
919 gettext("accepts only a single operand"));
920 subUsageObject(HELP_USAGE, subcommand, object);
921 return (1);
924 /* Finished syntax checks */
927 /* Call appropriate function */
928 *funcRet = subcommand->handler(argc - operInd, &argv[operInd],
929 object->value, &cmdOptions[0], callArgs);
931 return (0);