int main(int argc, char *argv[]) { synTables_t synTables; char versionString[VERSION_STRING_MAX_LEN]; int ret; int funcRet; void *subcommandArgs = NULL; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* set global command name */ cmdName = getExecBasename(argv[0]); (void) snprintf(versionString, VERSION_STRING_MAX_LEN, "%s.%s", VERSION_STRING_MAJOR, VERSION_STRING_MINOR); synTables.versionString = versionString; synTables.longOptionTbl = options; synTables.subCommandPropsTbl = subCommands; ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); if (ret != 0) { return (ret); } return (funcRet); } /* end main */
/* * main calls a parser that checks syntax of the input command against * various rules tables. * * The return value from the function is placed in funcRet */ int main(int argc, char *argv[]) { synTables_t synTables; char versionString[VERSION_STRING_MAX_LEN]; int ret; int funcRet; void *subcommandArgs = NULL; /* to support locale */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); /* set global command name */ cmdName = getExecBasename(argv[0]); /* check if is global zone */ if (getzoneid() != GLOBAL_ZONEID) { (void *) fprintf(stdout, "%s %s\n", cmdName, gettext("does not support non-global zone.")); return (1); } (void *) snprintf(versionString, sizeof (versionString), "%s.%s", VERSION_STRING_MAJOR, VERSION_STRING_MINOR); synTables.versionString = versionString; synTables.longOptionTbl = &sasinfolongOptions[0]; synTables.subCommandPropsTbl = &sasinfosubcommands[0]; /* call the CLI parser */ ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); if (ret == 1) { (void *) fprintf(stdout, "%s %s(1M)\n", gettext("For more information, please see"), cmdName); return (1); } else if (ret == -1) { (void *) fprintf(stderr, "%s %s\n", cmdName, strerror(errno)); return (1); } if (funcRet != 0) { return (1); } return (0); }
/* * main calls a parser that checks syntax of the input command against * various rules tables. * * The parser provides usage feedback based upon same tables by calling * two usage functions, usage and subUsage, handling command and subcommand * usage respectively. * * The parser handles all printing of usage syntactical errors * * When syntax is successfully validated, the parser calls the associated * function using the subcommands table functions. * * Syntax is as follows: * command subcommand [options] resource-type [<object>] * * The return value from the function is placed in funcRet */ int main(int argc, char *argv[]) { synTables_t synTables; char versionString[VERSION_STRING_MAX_LEN]; int ret; int funcRet; void *subcommandArgs = NULL; /* set global command name */ cmdName = getExecBasename(argv[0]); if (getzoneid() != GLOBAL_ZONEID) { (void) fprintf(stderr, "%s: this command is only available in the 'global' " "zone\n", cmdName); exit(1); } (void) snprintf(versionString, sizeof (versionString), "%s.%s", VERSION_STRING_MAJOR, VERSION_STRING_MINOR); synTables.versionString = versionString; synTables.longOptionTbl = &longOptions[0]; synTables.subcommandTbl = &subcommands[0]; synTables.objectTbl = &objects[0]; synTables.objectRulesTbl = &objectRules[0]; synTables.optionRulesTbl = &optionRules[0]; /* call the CLI parser */ ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); if (ret == 1) { (void) printf("%s %s(1M)\n", gettext("For more information, please see"), cmdName); return (1); } else if (ret == -1) { perror(cmdName); return (1); } return (funcRet); }
/* * cmdParse is a parser that checks syntax of the input command against * various rules tables. * * It provides usage feedback based upon the passed rules tables by calling * two usage functions, usage, subUsage, and subUsageObject handling command, * subcommand and object usage respectively. * * When syntax is successfully validated, the associated function is called * using the subcommands table functions. * * Syntax is as follows: * command subcommand object [<options>] [<operand>] * * There are two standard short and long options assumed: * -?, --help Provides usage on a command or subcommand * and stops further processing of the arguments * * -V, --version Provides version information on the command * and stops further processing of the arguments * * These options are loaded by this function. * * input: * argc, argv from main * syntax rules tables (synTables_t structure) * callArgs - void * passed by caller to be passed to subcommand function * * output: * funcRet - pointer to int that holds subcommand function return value * * Returns: * * zero on successful syntax parse and function call * * 1 on unsuccessful syntax parse (no function has been called) * This could be due to a version or help call or simply a * general usage call. * * -1 check errno, call failed * * This module is not MT-safe. * */ int cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, int *funcRet) { int getoptargc; char **getoptargv; int opt; int operInd; int i, j; int len; char *versionString; char optionStringAll[MAXOPTIONSTRING + 1]; optionProp_t *availOptions; objectRules_t *objRules = NULL; opCmd_t *opCmd = NULL; subcommand_t *subcommand; object_t *object; cmdOptions_t cmdOptions[MAXOPTIONS + 1]; struct option *lp; optionTbl_t *optionTbl; struct option intLongOpt[MAXOPTIONS + 1]; /* * Check for NULLs on mandatory input arguments * * Note: longOptionTbl and optionRulesTbl can be NULL in the case * where there is no caller defined options * */ if (synTable.versionString == NULL || synTable.subcommandTbl == NULL || synTable.objectRulesTbl == NULL || synTable.objectTbl == NULL || funcRet == NULL) { assert(0); } versionString = synTable.versionString; /* set global command name */ commandName = getExecBasename(argv[0]); /* Set unbuffered output */ setbuf(stdout, NULL); /* load globals */ _subcommands = synTable.subcommandTbl; _objectRules = synTable.objectRulesTbl; _optionRules = synTable.optionRulesTbl; _objects = synTable.objectTbl; _clientOptionTbl = synTable.longOptionTbl; /* There must be at least two arguments */ if (argc < 2) { usage(GENERAL_USAGE); return (1); } bzero(&intLongOpt[0], sizeof (intLongOpt)); /* * load standard subcommand options to internal long options table * Two separate getopt_long(3C) tables are used. */ for (i = 0; standardSubCmdOptions[i].name; i++) { intLongOpt[i].name = standardSubCmdOptions[i].name; intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; intLongOpt[i].flag = standardSubCmdOptions[i].flag; intLongOpt[i].val = standardSubCmdOptions[i].val; } /* * copy caller's long options into internal long options table * We do this for two reasons: * 1) We need to use the getopt_long option structure internally * 2) We need to prepend the table with the standard option * for all subcommands (currently -?) */ for (optionTbl = synTable.longOptionTbl; optionTbl && optionTbl->name; optionTbl++, i++) { if (i > MAXOPTIONS - 1) { /* option table too long */ assert(0); } intLongOpt[i].name = optionTbl->name; intLongOpt[i].has_arg = optionTbl->has_arg; intLongOpt[i].flag = NULL; intLongOpt[i].val = optionTbl->val; } /* set option table global */ _longOptions = &intLongOpt[0]; /* * Check for help/version request immediately following command * '+' in option string ensures POSIX compliance in getopt_long() * which means that processing will stop at first non-option * argument. */ while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, NULL)) != EOF) { switch (opt) { case '?': /* * getopt can return a '?' when no * option letters match string. Check for * the 'real' '?' in optopt. */ if (optopt == '?') { usage(HELP_USAGE); return (1); } else { usage(GENERAL_USAGE); return (1); } case 'V': (void) fprintf(stdout, "%s: %s %s\n", commandName, gettext("Version"), versionString); return (1); default: break; } } /* * subcommand is always in the second argument. If there is no * recognized subcommand in the second argument, print error, * general usage and then return. */ if (getSubcommand(argv[1], &subcommand) != 0) { (void) fprintf(stderr, "%s: %s\n", commandName, gettext("invalid subcommand")); usage(GENERAL_USAGE); return (1); } if (argc == 2) { (void) fprintf(stderr, "%s: %s\n", commandName, gettext("missing object")); subUsage(GENERAL_USAGE, subcommand); return (1); } getoptargv = argv; getoptargv++; getoptargc = argc; getoptargc -= 1; while ((opt = getopt_long(getoptargc, getoptargv, "+?", standardSubCmdOptions, NULL)) != EOF) { switch (opt) { case '?': /* * getopt can return a '?' when no * option letters match string. Check for * the 'real' '?' in optopt. */ if (optopt == '?') { subUsage(HELP_USAGE, subcommand); return (1); } else { subUsage(GENERAL_USAGE, subcommand); return (1); } default: break; } } /* * object is always in the third argument. If there is no * recognized object in the third argument, print error, * help usage for the subcommand and then return. */ if (getObject(argv[2], &object) != 0) { (void) fprintf(stderr, "%s: %s\n", commandName, gettext("invalid object")); subUsage(HELP_USAGE, subcommand); return (1); } if (getObjectRules(object->value, &objRules) != 0) { /* * internal subcommand rules table error * no object entry in object table */ assert(0); } opCmd = &(objRules->opCmd); /* * Is command valid for this object? */ if (opCmd->invOpCmd & subcommand->value) { (void) fprintf(stderr, "%s: %s %s\n", commandName, gettext("invalid subcommand for"), object->name); subUsage(HELP_USAGE, subcommand); return (1); } /* * offset getopt arg begin since * getopt(3C) assumes options * follow first argument */ getoptargv = argv; getoptargv++; getoptargv++; getoptargc = argc; getoptargc -= 2; bzero(optionStringAll, sizeof (optionStringAll)); bzero(&cmdOptions[0], sizeof (cmdOptions)); j = 0; /* * Build optionStringAll from long options table */ for (lp = _longOptions; lp->name; lp++, j++) { /* sanity check on string length */ if (j + 1 >= sizeof (optionStringAll)) { /* option table too long */ assert(0); } optionStringAll[j] = lp->val; if (lp->has_arg == required_argument) { optionStringAll[++j] = ':'; } } i = 0; /* * Run getopt for all arguments against all possible options * Store all options/option arguments in an array for retrieval * later. * Once all options are retrieved, check against object * and subcommand (option rules table) for validity. * This is done later. */ while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, _longOptions, NULL)) != EOF) { switch (opt) { case '?': if (optopt == '?') { subUsageObject(DETAIL_USAGE, subcommand, object); return (1); } else { subUsage(GENERAL_USAGE, subcommand); return (1); } default: cmdOptions[i].optval = opt; if (optarg) { len = strlen(optarg); if (len > sizeof (cmdOptions[i].optarg) - 1) { (void) fprintf(stderr, "%s: %s\n", commandName, gettext("option too long")); errno = EINVAL; return (-1); } (void) strncpy(cmdOptions[i].optarg, optarg, len); } i++; break; } } /* * increment past last option */ operInd = optind + 2; /* * Check validity of given options, if any were given */ /* get option string for this object and subcommand */ availOptions = getOptions(object->value, subcommand->value); if (cmdOptions[0].optval != 0) { /* options were input */ if (availOptions == NULL) { /* no options permitted */ (void) fprintf(stderr, "%s: %s\n", commandName, gettext("no options permitted")); subUsageObject(HELP_USAGE, subcommand, object); return (1); } for (i = 0; cmdOptions[i].optval; i++) { /* Check for invalid options */ if (availOptions->optionString == NULL) { /* * internal option table error * There must be an option string if * there is an entry in the table */ assert(0); } /* is the option in the available option string? */ if (!(strchr(availOptions->optionString, cmdOptions[i].optval))) { (void) fprintf(stderr, "%s: '-%c': %s\n", commandName, cmdOptions[i].optval, gettext("invalid option")); subUsageObject(DETAIL_USAGE, subcommand, object); return (1); /* Check for exclusive options */ } else if (cmdOptions[1].optval != 0 && availOptions->exclusive && strchr(availOptions->exclusive, cmdOptions[i].optval)) { (void) fprintf(stderr, "%s: '-%c': %s\n", commandName, cmdOptions[i].optval, gettext("is an exclusive option")); subUsageObject(DETAIL_USAGE, subcommand, object); return (1); } } } else { /* no options were input */ if (availOptions != NULL && (availOptions->required)) { (void) fprintf(stderr, "%s: %s\n", commandName, gettext("at least one option required")); subUsageObject(DETAIL_USAGE, subcommand, object); return (1); } } /* * If there are no more arguments (operands), * check to see if this is okay */ if ((operInd == argc) && (opCmd->reqOpCmd & subcommand->value)) { (void) fprintf(stderr, "%s: %s %s %s\n", commandName, subcommand->name, object->name, gettext("requires an operand")); subUsageObject(HELP_USAGE, subcommand, object); return (1); } /* * If there are more operands, * check to see if this is okay */ if ((argc > operInd) && (opCmd->noOpCmd & subcommand->value)) { (void) fprintf(stderr, "%s: %s %s %s\n", commandName, subcommand->name, object->name, gettext("takes no operands")); subUsageObject(HELP_USAGE, subcommand, object); return (1); } /* * If there is more than one more operand, * check to see if this is okay */ if ((argc > operInd) && ((argc - operInd) != 1) && !(opCmd->multOpCmd & subcommand->value)) { (void) fprintf(stderr, "%s: %s %s %s\n", commandName, subcommand->name, object->name, gettext("accepts only a single operand")); subUsageObject(HELP_USAGE, subcommand, object); return (1); } /* Finished syntax checks */ /* Call appropriate function */ *funcRet = subcommand->handler(argc - operInd, &argv[operInd], object->value, &cmdOptions[0], callArgs); return (0); }
/* * cmdParse is a parser that checks syntax of the input command against * various rules tables. * * It provides usage feedback based upon the passed rules tables by calling * two usage functions, usage, subUsage * * When syntax is successfully validated, the associated function is called * using the subcommands table functions. * * Syntax is as follows: * command subcommand [<options>] [<operand>] * * There are two standard short and long options assumed: * -?, --help Provides usage on a command or subcommand * and stops further processing of the arguments * * -V, --version Provides version information on the command * and stops further processing of the arguments * * These options are loaded by this function. * * input: * argc, argv from main * syntax rules tables (synTables_t structure) * callArgs - void * passed by caller to be passed to subcommand function * * output: * funcRet - pointer to int that holds subcommand function return value * * Returns: * * zero on successful syntax parse and function call * * 1 on unsuccessful syntax parse (no function has been called) * This could be due to a version or help call or simply a * general usage call. * * -1 check errno, call failed * * This module is not MT-safe. * */ int cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, int *funcRet) { int getoptargc; char **getoptargv; int opt; int operInd; int i, j; int len; int requiredOptionCnt = 0, requiredOptionEntered = 0; char *availOptions; char *versionString; char optionStringAll[MAXOPTIONSTRING + 1]; subCommandProps_t *subcommand; cmdOptions_t cmdOptions[MAXOPTIONS + 1]; optionTbl_t *optionTbl; struct option *lp; struct option intLongOpt[MAXOPTIONS + 1]; /* * Check for NULLs on mandatory input arguments * * Note: longOptionTbl can be NULL in the case * where there is no caller defined options * */ assert(synTable.versionString); assert(synTable.subCommandPropsTbl); assert(funcRet); versionString = synTable.versionString; /* set global command name */ commandName = getExecBasename(argv[0]); /* Set unbuffered output */ setbuf(stdout, NULL); /* load globals */ _subCommandProps = synTable.subCommandPropsTbl; _clientOptionTbl = synTable.longOptionTbl; /* There must be at least two arguments */ if (argc < 2) { usage(GENERAL_USAGE); return (1); } memset(&intLongOpt[0], 0, sizeof (intLongOpt)); /* * load standard subcommand options to internal long options table * Two separate getopt_long(3C) tables are used. */ for (i = 0; standardSubCmdOptions[i].name; i++) { intLongOpt[i].name = standardSubCmdOptions[i].name; intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; intLongOpt[i].flag = standardSubCmdOptions[i].flag; intLongOpt[i].val = standardSubCmdOptions[i].val; } /* * copy caller's long options into internal long options table * We do this for two reasons: * 1) We need to use the getopt_long option structure internally * 2) We need to prepend the table with the standard option * for all subcommands (currently -?) */ for (optionTbl = synTable.longOptionTbl; optionTbl && optionTbl->name; optionTbl++, i++) { if (i > MAXOPTIONS - 1) { /* option table too long */ assert(0); } intLongOpt[i].name = optionTbl->name; intLongOpt[i].has_arg = optionTbl->has_arg; intLongOpt[i].flag = NULL; intLongOpt[i].val = optionTbl->val; } /* set option table global */ _longOptions = &intLongOpt[0]; /* * Check for help/version request immediately following command * '+' in option string ensures POSIX compliance in getopt_long() * which means that processing will stop at first non-option * argument. */ while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, NULL)) != EOF) { switch (opt) { case '?': /* * getopt can return a '?' when no * option letters match string. Check for * the 'real' '?' in optopt. */ if (optopt == '?') { usage(DETAIL_USAGE); exit(0); } else { usage(GENERAL_USAGE); return (1); } break; case 'V': fprintf(stdout, "%s: %s %s\n", commandName, gettext("Version"), versionString); exit(0); break; default: break; } } /* * subcommand is always in the second argument. If there is no * recognized subcommand in the second argument, print error, * general usage and then return. */ if (getSubcommandProps(argv[1], &subcommand) != 0) { printf("%s: %s\n", commandName, gettext("invalid subcommand")); usage(GENERAL_USAGE); return (1); } getoptargv = argv; getoptargv++; getoptargc = argc; getoptargc -= 1; memset(optionStringAll, 0, sizeof (optionStringAll)); memset(&cmdOptions[0], 0, sizeof (cmdOptions)); j = 0; /* * Build optionStringAll from long options table */ for (lp = _longOptions; lp->name; lp++, j++) { /* sanity check on string length */ if (j + 1 >= sizeof (optionStringAll)) { /* option table too long */ assert(0); } optionStringAll[j] = lp->val; if (lp->has_arg == required_argument) { optionStringAll[++j] = ':'; } } i = 0; /* * Run getopt for all arguments against all possible options * Store all options/option arguments in an array for retrieval * later. * * Once all options are retrieved, a validity check against * subcommand table is performed. */ while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, _longOptions, NULL)) != EOF) { switch (opt) { case '?': subUsage(DETAIL_USAGE, subcommand); exit(0); default: cmdOptions[i].optval = opt; if (optarg) { len = strlen(optarg); if (len > sizeof (cmdOptions[i].optarg) - 1) { printf("%s: %s\n", commandName, gettext("option too long")); errno = EINVAL; return (-1); } strncpy(cmdOptions[i].optarg, optarg, len); } i++; break; } } /* * increment past last option */ operInd = optind + 1; /* * Check validity of given options, if any were given */ /* get option string for this subcommand */ availOptions = subcommand->optionString; /* Get count of required options */ if (subcommand->required) { requiredOptionCnt = strlen(subcommand->required); } if (cmdOptions[0].optval != 0) { /* options were input */ if (availOptions == NULL) { /* no options permitted */ printf("%s: %s\n", commandName, gettext("no options permitted")); subUsage(DETAIL_USAGE, subcommand); return (1); } for (i = 0; cmdOptions[i].optval; i++) { /* is the option in the available option string? */ if (!(strchr(availOptions, cmdOptions[i].optval))) { printf("%s: '-%c': %s\n", commandName, cmdOptions[i].optval, gettext("invalid option")); subUsage(DETAIL_USAGE, subcommand); return (1); /* increment required options entered */ } else if (subcommand->required && (strchr(subcommand->required, cmdOptions[i].optval))) { requiredOptionEntered++; /* Check for exclusive options */ } else if (cmdOptions[1].optval != 0 && subcommand->exclusive && strchr(subcommand->exclusive, cmdOptions[i].optval)) { printf("%s: '-%c': %s\n", commandName, cmdOptions[i].optval, gettext("is an exclusive option")); subUsage(DETAIL_USAGE, subcommand); return (1); } } } else { /* no options were input */ if (availOptions != NULL && subcommand->required) { printf("%s: %s\n", commandName, gettext("at least one option required")); subUsage(DETAIL_USAGE, subcommand); return (1); } } /* Were all required options entered? */ if (requiredOptionEntered != requiredOptionCnt) { printf("%s: %s: %s\n", commandName, gettext("Following option(s) required"), subcommand->required); subUsage(DETAIL_USAGE, subcommand); return (1); } /* * If there are no operands, * check to see if this is okay */ if ((operInd == argc) && (subcommand->operand & OPERAND_MANDATORY)) { printf("%s: %s %s\n", commandName, subcommand->name, gettext("requires an operand")); subUsage(DETAIL_USAGE, subcommand); return (1); } /* * If there are more operands, * check to see if this is okay */ if ((argc > operInd) && (subcommand->operand & OPERAND_NONE)) { fprintf(stderr, "%s: %s %s\n", commandName, subcommand->name, gettext("takes no operands")); subUsage(DETAIL_USAGE, subcommand); return (1); } /* * If there is more than one more operand, * check to see if this is okay */ if ((argc > operInd) && ((argc - operInd) != 1) && (subcommand->operand & OPERAND_SINGLE)) { printf("%s: %s %s\n", commandName, subcommand->name, gettext("accepts only a single operand")); subUsage(DETAIL_USAGE, subcommand); return (1); } /* Finished syntax checks */ /* Call appropriate function */ *funcRet = subcommand->handler(argc - operInd, &argv[operInd], &cmdOptions[0], callArgs); return (0); }