17836SJohn.Forte@Sun.COM /* 27836SJohn.Forte@Sun.COM * CDDL HEADER START 37836SJohn.Forte@Sun.COM * 47836SJohn.Forte@Sun.COM * The contents of this file are subject to the terms of the 57836SJohn.Forte@Sun.COM * Common Development and Distribution License (the "License"). 67836SJohn.Forte@Sun.COM * You may not use this file except in compliance with the License. 77836SJohn.Forte@Sun.COM * 87836SJohn.Forte@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97836SJohn.Forte@Sun.COM * or http://www.opensolaris.org/os/licensing. 107836SJohn.Forte@Sun.COM * See the License for the specific language governing permissions 117836SJohn.Forte@Sun.COM * and limitations under the License. 127836SJohn.Forte@Sun.COM * 137836SJohn.Forte@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 147836SJohn.Forte@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157836SJohn.Forte@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 167836SJohn.Forte@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 177836SJohn.Forte@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 187836SJohn.Forte@Sun.COM * 197836SJohn.Forte@Sun.COM * CDDL HEADER END 207836SJohn.Forte@Sun.COM */ 217836SJohn.Forte@Sun.COM /* 229585STim.Szeto@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237836SJohn.Forte@Sun.COM * Use is subject to license terms. 247836SJohn.Forte@Sun.COM */ 257836SJohn.Forte@Sun.COM 267836SJohn.Forte@Sun.COM #include <stdlib.h> 277836SJohn.Forte@Sun.COM #include <stdio.h> 287836SJohn.Forte@Sun.COM #include <sys/types.h> 297836SJohn.Forte@Sun.COM #include <unistd.h> 307836SJohn.Forte@Sun.COM #include <libintl.h> 317836SJohn.Forte@Sun.COM #include <errno.h> 327836SJohn.Forte@Sun.COM #include <string.h> 337836SJohn.Forte@Sun.COM #include <assert.h> 347836SJohn.Forte@Sun.COM #include <getopt.h> 357836SJohn.Forte@Sun.COM #include <cmdparse.h> 367836SJohn.Forte@Sun.COM 377836SJohn.Forte@Sun.COM 387836SJohn.Forte@Sun.COM /* Usage types */ 397836SJohn.Forte@Sun.COM #define GENERAL_USAGE 1 407836SJohn.Forte@Sun.COM #define DETAIL_USAGE 2 417836SJohn.Forte@Sun.COM 427836SJohn.Forte@Sun.COM /* printable ascii character set len */ 437836SJohn.Forte@Sun.COM #define MAXOPTIONS (uint_t)('~' - '!' + 1) 447836SJohn.Forte@Sun.COM 457836SJohn.Forte@Sun.COM /* 467836SJohn.Forte@Sun.COM * MAXOPTIONSTRING is the max length of the options string used in getopt and 477836SJohn.Forte@Sun.COM * will be the printable character set + ':' for each character, 487836SJohn.Forte@Sun.COM * providing for options with arguments. e.g. "t:Cs:hglr:" 497836SJohn.Forte@Sun.COM */ 507836SJohn.Forte@Sun.COM #define MAXOPTIONSTRING MAXOPTIONS * 2 517836SJohn.Forte@Sun.COM 527836SJohn.Forte@Sun.COM /* standard command options table to support -?, -V */ 537836SJohn.Forte@Sun.COM struct option standardCmdOptions[] = { 547836SJohn.Forte@Sun.COM {"help", no_argument, NULL, '?'}, 557836SJohn.Forte@Sun.COM {"version", no_argument, NULL, 'V'}, 567836SJohn.Forte@Sun.COM {NULL, 0, NULL, 0} 577836SJohn.Forte@Sun.COM }; 587836SJohn.Forte@Sun.COM 597836SJohn.Forte@Sun.COM /* standard subcommand options table to support -? */ 607836SJohn.Forte@Sun.COM struct option standardSubCmdOptions[] = { 617836SJohn.Forte@Sun.COM {"help", no_argument, NULL, '?'}, 627836SJohn.Forte@Sun.COM {NULL, 0, NULL, 0} 637836SJohn.Forte@Sun.COM }; 647836SJohn.Forte@Sun.COM 657836SJohn.Forte@Sun.COM /* forward declarations */ 667836SJohn.Forte@Sun.COM static int getSubcommandProps(char *, subCommandProps_t **); 677836SJohn.Forte@Sun.COM static char *getExecBasename(char *); 687836SJohn.Forte@Sun.COM static void usage(uint_t); 697836SJohn.Forte@Sun.COM static void subUsage(uint_t, subCommandProps_t *); 707836SJohn.Forte@Sun.COM static char *getLongOption(int); 717836SJohn.Forte@Sun.COM static char *getOptionArgDesc(int); 727836SJohn.Forte@Sun.COM 737836SJohn.Forte@Sun.COM /* global data */ 747836SJohn.Forte@Sun.COM static struct option *_longOptions; 757836SJohn.Forte@Sun.COM static subCommandProps_t *_subCommandProps; 767836SJohn.Forte@Sun.COM static optionTbl_t *_clientOptionTbl; 777836SJohn.Forte@Sun.COM static char *commandName; 787836SJohn.Forte@Sun.COM 797836SJohn.Forte@Sun.COM 807836SJohn.Forte@Sun.COM /* 817836SJohn.Forte@Sun.COM * input: 827836SJohn.Forte@Sun.COM * subCommand - subcommand value 837836SJohn.Forte@Sun.COM * output: 847836SJohn.Forte@Sun.COM * subCommandProps - pointer to subCommandProps_t structure allocated by caller 857836SJohn.Forte@Sun.COM * 867836SJohn.Forte@Sun.COM * On successful return, subCommandProps contains the properties for the value 877836SJohn.Forte@Sun.COM * in subCommand. On failure, the contents of subCommandProps is unspecified. 887836SJohn.Forte@Sun.COM * 897836SJohn.Forte@Sun.COM * Returns: 907836SJohn.Forte@Sun.COM * zero on success 917836SJohn.Forte@Sun.COM * non-zero on failure 927836SJohn.Forte@Sun.COM * 937836SJohn.Forte@Sun.COM */ 947836SJohn.Forte@Sun.COM static int 957836SJohn.Forte@Sun.COM getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps) 967836SJohn.Forte@Sun.COM { 977836SJohn.Forte@Sun.COM subCommandProps_t *sp; 987836SJohn.Forte@Sun.COM int len; 997836SJohn.Forte@Sun.COM 1007836SJohn.Forte@Sun.COM for (sp = _subCommandProps; sp->name; sp++) { 1017836SJohn.Forte@Sun.COM len = strlen(subCommand); 1027836SJohn.Forte@Sun.COM if (len == strlen(sp->name) && 1037836SJohn.Forte@Sun.COM strncasecmp(subCommand, sp->name, len) == 0) { 1047836SJohn.Forte@Sun.COM *subCommandProps = sp; 1057836SJohn.Forte@Sun.COM return (0); 1067836SJohn.Forte@Sun.COM } 1077836SJohn.Forte@Sun.COM } 1087836SJohn.Forte@Sun.COM return (1); 1097836SJohn.Forte@Sun.COM } 1107836SJohn.Forte@Sun.COM 1117836SJohn.Forte@Sun.COM /* 1127836SJohn.Forte@Sun.COM * input: 1137836SJohn.Forte@Sun.COM * shortOption - short option character for which to return the 1147836SJohn.Forte@Sun.COM * associated long option string 1157836SJohn.Forte@Sun.COM * 1167836SJohn.Forte@Sun.COM * Returns: 1177836SJohn.Forte@Sun.COM * on success, long option name 1187836SJohn.Forte@Sun.COM * on failure, NULL 1197836SJohn.Forte@Sun.COM */ 1207836SJohn.Forte@Sun.COM static char * 1217836SJohn.Forte@Sun.COM getLongOption(int shortOption) 1227836SJohn.Forte@Sun.COM { 1237836SJohn.Forte@Sun.COM struct option *op; 1247836SJohn.Forte@Sun.COM for (op = _longOptions; op->name; op++) { 1257836SJohn.Forte@Sun.COM if (shortOption == op->val) { 1267836SJohn.Forte@Sun.COM return (op->name); 1277836SJohn.Forte@Sun.COM } 1287836SJohn.Forte@Sun.COM } 1297836SJohn.Forte@Sun.COM return (NULL); 1307836SJohn.Forte@Sun.COM } 1317836SJohn.Forte@Sun.COM 1327836SJohn.Forte@Sun.COM /* 1337836SJohn.Forte@Sun.COM * input 1347836SJohn.Forte@Sun.COM * shortOption - short option character for which to return the 1357836SJohn.Forte@Sun.COM * option argument 1367836SJohn.Forte@Sun.COM * Returns: 1377836SJohn.Forte@Sun.COM * on success, argument string 1387836SJohn.Forte@Sun.COM * on failure, NULL 1397836SJohn.Forte@Sun.COM */ 1407836SJohn.Forte@Sun.COM static char * 1417836SJohn.Forte@Sun.COM getOptionArgDesc(int shortOption) 1427836SJohn.Forte@Sun.COM { 1437836SJohn.Forte@Sun.COM optionTbl_t *op; 1447836SJohn.Forte@Sun.COM for (op = _clientOptionTbl; op->name; op++) { 1457836SJohn.Forte@Sun.COM if (op->val == shortOption && 1467836SJohn.Forte@Sun.COM op->has_arg == required_argument) { 1477836SJohn.Forte@Sun.COM return (op->argDesc); 1487836SJohn.Forte@Sun.COM } 1497836SJohn.Forte@Sun.COM } 1507836SJohn.Forte@Sun.COM return (NULL); 1517836SJohn.Forte@Sun.COM } 1527836SJohn.Forte@Sun.COM 1537836SJohn.Forte@Sun.COM 1547836SJohn.Forte@Sun.COM /* 1557836SJohn.Forte@Sun.COM * Print usage for a subcommand. 1567836SJohn.Forte@Sun.COM * 1577836SJohn.Forte@Sun.COM * input: 1587836SJohn.Forte@Sun.COM * usage type - GENERAL_USAGE, DETAIL_USAGE 1597836SJohn.Forte@Sun.COM * subcommand - pointer to subCommandProps_t structure 1607836SJohn.Forte@Sun.COM * 1617836SJohn.Forte@Sun.COM * Returns: 1627836SJohn.Forte@Sun.COM * none 1637836SJohn.Forte@Sun.COM * 1647836SJohn.Forte@Sun.COM */ 1657836SJohn.Forte@Sun.COM static void 1667836SJohn.Forte@Sun.COM subUsage(uint_t usageType, subCommandProps_t *subcommand) 1677836SJohn.Forte@Sun.COM { 1687836SJohn.Forte@Sun.COM int i; 1697836SJohn.Forte@Sun.COM char *optionArgDesc; 1707836SJohn.Forte@Sun.COM char *longOpt; 1717836SJohn.Forte@Sun.COM 1727836SJohn.Forte@Sun.COM if (usageType == GENERAL_USAGE) { 1737836SJohn.Forte@Sun.COM (void) printf("%s:\t%s %s [", gettext("Usage"), commandName, 1747836SJohn.Forte@Sun.COM subcommand->name); 1757836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 1767836SJohn.Forte@Sun.COM (void) printf("-%c", standardSubCmdOptions[i].val); 1777836SJohn.Forte@Sun.COM if (standardSubCmdOptions[i+1].name) 1787836SJohn.Forte@Sun.COM (void) printf(","); 1797836SJohn.Forte@Sun.COM } 1807836SJohn.Forte@Sun.COM (void) fprintf(stdout, "]\n"); 1817836SJohn.Forte@Sun.COM return; 1827836SJohn.Forte@Sun.COM } 1837836SJohn.Forte@Sun.COM 1847836SJohn.Forte@Sun.COM /* print subcommand usage */ 1857836SJohn.Forte@Sun.COM (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName, 1867836SJohn.Forte@Sun.COM subcommand->name); 1877836SJohn.Forte@Sun.COM 1887836SJohn.Forte@Sun.COM /* print options if applicable */ 1897836SJohn.Forte@Sun.COM if (subcommand->optionString != NULL) { 1907836SJohn.Forte@Sun.COM if (subcommand->required) { 1917836SJohn.Forte@Sun.COM (void) printf("%s", gettext("<")); 1927836SJohn.Forte@Sun.COM } else { 1937836SJohn.Forte@Sun.COM (void) printf("%s", gettext("[")); 1947836SJohn.Forte@Sun.COM } 1957836SJohn.Forte@Sun.COM (void) printf("%s", gettext("OPTIONS")); 1967836SJohn.Forte@Sun.COM if (subcommand->required) { 1977836SJohn.Forte@Sun.COM (void) printf("%s ", gettext(">")); 1987836SJohn.Forte@Sun.COM } else { 1997836SJohn.Forte@Sun.COM (void) printf("%s ", gettext("]")); 2007836SJohn.Forte@Sun.COM } 2017836SJohn.Forte@Sun.COM } 2027836SJohn.Forte@Sun.COM 2037836SJohn.Forte@Sun.COM /* print operand requirements */ 2047836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE) && 2057836SJohn.Forte@Sun.COM !(subcommand->operand & OPERAND_MANDATORY)) { 2067836SJohn.Forte@Sun.COM (void) printf(gettext("[")); 2077836SJohn.Forte@Sun.COM } 2087836SJohn.Forte@Sun.COM 2097836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MANDATORY) { 2107836SJohn.Forte@Sun.COM (void) printf(gettext("<")); 2117836SJohn.Forte@Sun.COM } 2127836SJohn.Forte@Sun.COM 2137836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE)) { 2147836SJohn.Forte@Sun.COM assert(subcommand->operandDefinition); 2157836SJohn.Forte@Sun.COM (void) printf("%s", subcommand->operandDefinition); 2167836SJohn.Forte@Sun.COM } 2177836SJohn.Forte@Sun.COM 2187836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MULTIPLE) { 2197836SJohn.Forte@Sun.COM (void) printf(gettext(" ...")); 2207836SJohn.Forte@Sun.COM } 2217836SJohn.Forte@Sun.COM 2227836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MANDATORY) { 2237836SJohn.Forte@Sun.COM (void) printf(gettext(">")); 2247836SJohn.Forte@Sun.COM } 2257836SJohn.Forte@Sun.COM 2267836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE) && 2277836SJohn.Forte@Sun.COM !(subcommand->operand & OPERAND_MANDATORY)) { 2287836SJohn.Forte@Sun.COM (void) printf(gettext("]")); 2297836SJohn.Forte@Sun.COM } 2307836SJohn.Forte@Sun.COM 2317836SJohn.Forte@Sun.COM /* print options for subcommand */ 2327836SJohn.Forte@Sun.COM if (subcommand->optionString != NULL) { 2337836SJohn.Forte@Sun.COM (void) printf("\n\t%s:", gettext("OPTIONS")); 2347836SJohn.Forte@Sun.COM for (i = 0; i < strlen(subcommand->optionString); i++) { 2357836SJohn.Forte@Sun.COM assert((longOpt = getLongOption( 2367836SJohn.Forte@Sun.COM subcommand->optionString[i])) != NULL); 2377836SJohn.Forte@Sun.COM (void) printf("\n\t\t-%c, --%s ", 2387836SJohn.Forte@Sun.COM subcommand->optionString[i], 2397836SJohn.Forte@Sun.COM longOpt); 2407836SJohn.Forte@Sun.COM optionArgDesc = 2417836SJohn.Forte@Sun.COM getOptionArgDesc(subcommand->optionString[i]); 2427836SJohn.Forte@Sun.COM if (optionArgDesc != NULL) { 2437836SJohn.Forte@Sun.COM (void) printf("<%s>", optionArgDesc); 2447836SJohn.Forte@Sun.COM } 2457836SJohn.Forte@Sun.COM if (subcommand->exclusive && 2467836SJohn.Forte@Sun.COM strchr(subcommand->exclusive, 2477836SJohn.Forte@Sun.COM subcommand->optionString[i])) { 2487836SJohn.Forte@Sun.COM (void) printf(" (%s)", gettext("exclusive")); 2497836SJohn.Forte@Sun.COM } 2507836SJohn.Forte@Sun.COM } 2517836SJohn.Forte@Sun.COM } 2527836SJohn.Forte@Sun.COM (void) fprintf(stdout, "\n"); 2539585STim.Szeto@Sun.COM if (subcommand->helpText) { 2549585STim.Szeto@Sun.COM (void) printf("%s\n", subcommand->helpText); 2559585STim.Szeto@Sun.COM } 2567836SJohn.Forte@Sun.COM } 2577836SJohn.Forte@Sun.COM 2587836SJohn.Forte@Sun.COM /* 2597836SJohn.Forte@Sun.COM * input: 2607836SJohn.Forte@Sun.COM * type of usage statement to print 2617836SJohn.Forte@Sun.COM * 2627836SJohn.Forte@Sun.COM * Returns: 2637836SJohn.Forte@Sun.COM * return value of subUsage 2647836SJohn.Forte@Sun.COM */ 2657836SJohn.Forte@Sun.COM static void 2667836SJohn.Forte@Sun.COM usage(uint_t usageType) 2677836SJohn.Forte@Sun.COM { 2687836SJohn.Forte@Sun.COM int i; 2697836SJohn.Forte@Sun.COM subCommandProps_t *sp; 2707836SJohn.Forte@Sun.COM 2717836SJohn.Forte@Sun.COM /* print general command usage */ 2727836SJohn.Forte@Sun.COM (void) printf("%s:\t%s ", gettext("Usage"), commandName); 2737836SJohn.Forte@Sun.COM 2747836SJohn.Forte@Sun.COM for (i = 0; standardCmdOptions[i].name; i++) { 2757836SJohn.Forte@Sun.COM (void) printf("-%c", standardCmdOptions[i].val); 2767836SJohn.Forte@Sun.COM if (standardCmdOptions[i+1].name) 2777836SJohn.Forte@Sun.COM (void) printf(","); 2787836SJohn.Forte@Sun.COM } 2797836SJohn.Forte@Sun.COM 2807836SJohn.Forte@Sun.COM if (usageType == GENERAL_USAGE) { 2817836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 2827836SJohn.Forte@Sun.COM (void) printf(",--%s", standardSubCmdOptions[i].name); 2837836SJohn.Forte@Sun.COM if (standardSubCmdOptions[i+1].name) 2847836SJohn.Forte@Sun.COM (void) printf(","); 2857836SJohn.Forte@Sun.COM } 2867836SJohn.Forte@Sun.COM } 2877836SJohn.Forte@Sun.COM 2887836SJohn.Forte@Sun.COM (void) fprintf(stdout, "\n"); 2897836SJohn.Forte@Sun.COM 2907836SJohn.Forte@Sun.COM 2917836SJohn.Forte@Sun.COM /* print all subcommand usage */ 2927836SJohn.Forte@Sun.COM for (sp = _subCommandProps; sp->name; sp++) { 2937836SJohn.Forte@Sun.COM subUsage(usageType, sp); 2947836SJohn.Forte@Sun.COM } 2957836SJohn.Forte@Sun.COM } 2967836SJohn.Forte@Sun.COM 2977836SJohn.Forte@Sun.COM /* 2987836SJohn.Forte@Sun.COM * input: 2997836SJohn.Forte@Sun.COM * execFullName - exec name of program (argv[0]) 3007836SJohn.Forte@Sun.COM * 3017836SJohn.Forte@Sun.COM * Returns: 3027836SJohn.Forte@Sun.COM * command name portion of execFullName 3037836SJohn.Forte@Sun.COM */ 3047836SJohn.Forte@Sun.COM static char * 3057836SJohn.Forte@Sun.COM getExecBasename(char *execFullname) 3067836SJohn.Forte@Sun.COM { 3077836SJohn.Forte@Sun.COM char *lastSlash, *execBasename; 3087836SJohn.Forte@Sun.COM 3097836SJohn.Forte@Sun.COM /* guard against '/' at end of command invocation */ 3107836SJohn.Forte@Sun.COM for (;;) { 3117836SJohn.Forte@Sun.COM lastSlash = strrchr(execFullname, '/'); 3127836SJohn.Forte@Sun.COM if (lastSlash == NULL) { 3137836SJohn.Forte@Sun.COM execBasename = execFullname; 3147836SJohn.Forte@Sun.COM break; 3157836SJohn.Forte@Sun.COM } else { 3167836SJohn.Forte@Sun.COM execBasename = lastSlash + 1; 3177836SJohn.Forte@Sun.COM if (*execBasename == '\0') { 3187836SJohn.Forte@Sun.COM *lastSlash = '\0'; 3197836SJohn.Forte@Sun.COM continue; 3207836SJohn.Forte@Sun.COM } 3217836SJohn.Forte@Sun.COM break; 3227836SJohn.Forte@Sun.COM } 3237836SJohn.Forte@Sun.COM } 3247836SJohn.Forte@Sun.COM return (execBasename); 3257836SJohn.Forte@Sun.COM } 3267836SJohn.Forte@Sun.COM 3277836SJohn.Forte@Sun.COM /* 3287836SJohn.Forte@Sun.COM * cmdParse is a parser that checks syntax of the input command against 3297836SJohn.Forte@Sun.COM * various rules tables. 3307836SJohn.Forte@Sun.COM * 3317836SJohn.Forte@Sun.COM * It provides usage feedback based upon the passed rules tables by calling 3327836SJohn.Forte@Sun.COM * two usage functions, usage, subUsage 3337836SJohn.Forte@Sun.COM * 3347836SJohn.Forte@Sun.COM * When syntax is successfully validated, the associated function is called 3357836SJohn.Forte@Sun.COM * using the subcommands table functions. 3367836SJohn.Forte@Sun.COM * 3377836SJohn.Forte@Sun.COM * Syntax is as follows: 3387836SJohn.Forte@Sun.COM * command subcommand [<options>] [<operand>] 3397836SJohn.Forte@Sun.COM * 3407836SJohn.Forte@Sun.COM * There are two standard short and long options assumed: 3417836SJohn.Forte@Sun.COM * -?, --help Provides usage on a command or subcommand 3427836SJohn.Forte@Sun.COM * and stops further processing of the arguments 3437836SJohn.Forte@Sun.COM * 3447836SJohn.Forte@Sun.COM * -V, --version Provides version information on the command 3457836SJohn.Forte@Sun.COM * and stops further processing of the arguments 3467836SJohn.Forte@Sun.COM * 3477836SJohn.Forte@Sun.COM * These options are loaded by this function. 3487836SJohn.Forte@Sun.COM * 3497836SJohn.Forte@Sun.COM * input: 3507836SJohn.Forte@Sun.COM * argc, argv from main 3517836SJohn.Forte@Sun.COM * syntax rules tables (synTables_t structure) 3527836SJohn.Forte@Sun.COM * callArgs - void * passed by caller to be passed to subcommand function 3537836SJohn.Forte@Sun.COM * 3547836SJohn.Forte@Sun.COM * output: 3557836SJohn.Forte@Sun.COM * funcRet - pointer to int that holds subcommand function return value 3567836SJohn.Forte@Sun.COM * 3577836SJohn.Forte@Sun.COM * Returns: 3587836SJohn.Forte@Sun.COM * 3597836SJohn.Forte@Sun.COM * zero on successful syntax parse and function call 3607836SJohn.Forte@Sun.COM * 3617836SJohn.Forte@Sun.COM * 1 on unsuccessful syntax parse (no function has been called) 3627836SJohn.Forte@Sun.COM * This could be due to a version or help call or simply a 3637836SJohn.Forte@Sun.COM * general usage call. 3647836SJohn.Forte@Sun.COM * 3657836SJohn.Forte@Sun.COM * -1 check errno, call failed 3667836SJohn.Forte@Sun.COM * 3677836SJohn.Forte@Sun.COM * This module is not MT-safe. 3687836SJohn.Forte@Sun.COM * 3697836SJohn.Forte@Sun.COM */ 3707836SJohn.Forte@Sun.COM int 3717836SJohn.Forte@Sun.COM cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, 3727836SJohn.Forte@Sun.COM int *funcRet) 3737836SJohn.Forte@Sun.COM { 3747836SJohn.Forte@Sun.COM int getoptargc; 3757836SJohn.Forte@Sun.COM char **getoptargv; 3767836SJohn.Forte@Sun.COM int opt; 3777836SJohn.Forte@Sun.COM int operInd; 3787836SJohn.Forte@Sun.COM int i, j; 3797836SJohn.Forte@Sun.COM int len; 3807836SJohn.Forte@Sun.COM int requiredOptionCnt = 0, requiredOptionEntered = 0; 3817836SJohn.Forte@Sun.COM char *availOptions; 3827836SJohn.Forte@Sun.COM char *versionString; 3837836SJohn.Forte@Sun.COM char optionStringAll[MAXOPTIONSTRING + 1]; 3847836SJohn.Forte@Sun.COM subCommandProps_t *subcommand; 3857836SJohn.Forte@Sun.COM cmdOptions_t cmdOptions[MAXOPTIONS + 1]; 3867836SJohn.Forte@Sun.COM optionTbl_t *optionTbl; 3877836SJohn.Forte@Sun.COM struct option *lp; 3887836SJohn.Forte@Sun.COM struct option intLongOpt[MAXOPTIONS + 1]; 3897836SJohn.Forte@Sun.COM 3907836SJohn.Forte@Sun.COM /* 3917836SJohn.Forte@Sun.COM * Check for NULLs on mandatory input arguments 3927836SJohn.Forte@Sun.COM * 3937836SJohn.Forte@Sun.COM * Note: longOptionTbl can be NULL in the case 3947836SJohn.Forte@Sun.COM * where there is no caller defined options 3957836SJohn.Forte@Sun.COM * 3967836SJohn.Forte@Sun.COM */ 3977836SJohn.Forte@Sun.COM assert(synTable.versionString); 3987836SJohn.Forte@Sun.COM assert(synTable.subCommandPropsTbl); 3997836SJohn.Forte@Sun.COM assert(funcRet); 4007836SJohn.Forte@Sun.COM 4017836SJohn.Forte@Sun.COM versionString = synTable.versionString; 4027836SJohn.Forte@Sun.COM 4037836SJohn.Forte@Sun.COM /* set global command name */ 4047836SJohn.Forte@Sun.COM commandName = getExecBasename(argv[0]); 4057836SJohn.Forte@Sun.COM 4067836SJohn.Forte@Sun.COM /* Set unbuffered output */ 4077836SJohn.Forte@Sun.COM setbuf(stdout, NULL); 4087836SJohn.Forte@Sun.COM 4097836SJohn.Forte@Sun.COM /* load globals */ 4107836SJohn.Forte@Sun.COM _subCommandProps = synTable.subCommandPropsTbl; 4117836SJohn.Forte@Sun.COM _clientOptionTbl = synTable.longOptionTbl; 4127836SJohn.Forte@Sun.COM 4137836SJohn.Forte@Sun.COM /* There must be at least two arguments */ 4147836SJohn.Forte@Sun.COM if (argc < 2) { 4157836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 4167836SJohn.Forte@Sun.COM return (1); 4177836SJohn.Forte@Sun.COM } 4187836SJohn.Forte@Sun.COM 4197836SJohn.Forte@Sun.COM (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt)); 4207836SJohn.Forte@Sun.COM 4217836SJohn.Forte@Sun.COM /* 4227836SJohn.Forte@Sun.COM * load standard subcommand options to internal long options table 4237836SJohn.Forte@Sun.COM * Two separate getopt_long(3C) tables are used. 4247836SJohn.Forte@Sun.COM */ 4257836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 4267836SJohn.Forte@Sun.COM intLongOpt[i].name = standardSubCmdOptions[i].name; 4277836SJohn.Forte@Sun.COM intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; 4287836SJohn.Forte@Sun.COM intLongOpt[i].flag = standardSubCmdOptions[i].flag; 4297836SJohn.Forte@Sun.COM intLongOpt[i].val = standardSubCmdOptions[i].val; 4307836SJohn.Forte@Sun.COM } 4317836SJohn.Forte@Sun.COM 4327836SJohn.Forte@Sun.COM /* 4337836SJohn.Forte@Sun.COM * copy caller's long options into internal long options table 4347836SJohn.Forte@Sun.COM * We do this for two reasons: 4357836SJohn.Forte@Sun.COM * 1) We need to use the getopt_long option structure internally 4367836SJohn.Forte@Sun.COM * 2) We need to prepend the table with the standard option 4377836SJohn.Forte@Sun.COM * for all subcommands (currently -?) 4387836SJohn.Forte@Sun.COM */ 4397836SJohn.Forte@Sun.COM for (optionTbl = synTable.longOptionTbl; 4407836SJohn.Forte@Sun.COM optionTbl && optionTbl->name; optionTbl++, i++) { 4417836SJohn.Forte@Sun.COM if (i > MAXOPTIONS - 1) { 4427836SJohn.Forte@Sun.COM /* option table too long */ 4437836SJohn.Forte@Sun.COM assert(0); 4447836SJohn.Forte@Sun.COM } 4457836SJohn.Forte@Sun.COM intLongOpt[i].name = optionTbl->name; 4467836SJohn.Forte@Sun.COM intLongOpt[i].has_arg = optionTbl->has_arg; 4477836SJohn.Forte@Sun.COM intLongOpt[i].flag = NULL; 4487836SJohn.Forte@Sun.COM intLongOpt[i].val = optionTbl->val; 4497836SJohn.Forte@Sun.COM } 4507836SJohn.Forte@Sun.COM 4517836SJohn.Forte@Sun.COM /* set option table global */ 4527836SJohn.Forte@Sun.COM _longOptions = &intLongOpt[0]; 4537836SJohn.Forte@Sun.COM 4547836SJohn.Forte@Sun.COM 4557836SJohn.Forte@Sun.COM /* 4567836SJohn.Forte@Sun.COM * Check for help/version request immediately following command 4577836SJohn.Forte@Sun.COM * '+' in option string ensures POSIX compliance in getopt_long() 4587836SJohn.Forte@Sun.COM * which means that processing will stop at first non-option 4597836SJohn.Forte@Sun.COM * argument. 4607836SJohn.Forte@Sun.COM */ 4617836SJohn.Forte@Sun.COM while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, 4627836SJohn.Forte@Sun.COM NULL)) != EOF) { 4637836SJohn.Forte@Sun.COM switch (opt) { 4647836SJohn.Forte@Sun.COM case '?': 4657836SJohn.Forte@Sun.COM /* 4667836SJohn.Forte@Sun.COM * getopt can return a '?' when no 4677836SJohn.Forte@Sun.COM * option letters match string. Check for 4687836SJohn.Forte@Sun.COM * the 'real' '?' in optopt. 4697836SJohn.Forte@Sun.COM */ 4707836SJohn.Forte@Sun.COM if (optopt == '?') { 4717836SJohn.Forte@Sun.COM usage(DETAIL_USAGE); 4727836SJohn.Forte@Sun.COM exit(0); 4737836SJohn.Forte@Sun.COM } else { 4747836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 4757836SJohn.Forte@Sun.COM return (1); 4767836SJohn.Forte@Sun.COM } 4777836SJohn.Forte@Sun.COM break; 4787836SJohn.Forte@Sun.COM case 'V': 4797836SJohn.Forte@Sun.COM (void) fprintf(stdout, "%s: %s %s\n", 4807836SJohn.Forte@Sun.COM commandName, gettext("Version"), 4817836SJohn.Forte@Sun.COM versionString); 4827836SJohn.Forte@Sun.COM exit(0); 4837836SJohn.Forte@Sun.COM break; 4847836SJohn.Forte@Sun.COM default: 4857836SJohn.Forte@Sun.COM break; 4867836SJohn.Forte@Sun.COM } 4877836SJohn.Forte@Sun.COM } 4887836SJohn.Forte@Sun.COM 4897836SJohn.Forte@Sun.COM /* 4907836SJohn.Forte@Sun.COM * subcommand is always in the second argument. If there is no 4917836SJohn.Forte@Sun.COM * recognized subcommand in the second argument, print error, 4927836SJohn.Forte@Sun.COM * general usage and then return. 4937836SJohn.Forte@Sun.COM */ 4947836SJohn.Forte@Sun.COM if (getSubcommandProps(argv[1], &subcommand) != 0) { 4957836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 4967836SJohn.Forte@Sun.COM gettext("invalid subcommand")); 4977836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 4987836SJohn.Forte@Sun.COM return (1); 4997836SJohn.Forte@Sun.COM } 5007836SJohn.Forte@Sun.COM 5017836SJohn.Forte@Sun.COM getoptargv = argv; 5027836SJohn.Forte@Sun.COM getoptargv++; 5037836SJohn.Forte@Sun.COM getoptargc = argc; 5047836SJohn.Forte@Sun.COM getoptargc -= 1; 5057836SJohn.Forte@Sun.COM 5067836SJohn.Forte@Sun.COM (void) memset(optionStringAll, 0, sizeof (optionStringAll)); 5077836SJohn.Forte@Sun.COM (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions)); 5087836SJohn.Forte@Sun.COM 5097836SJohn.Forte@Sun.COM j = 0; 5107836SJohn.Forte@Sun.COM /* 5117836SJohn.Forte@Sun.COM * Build optionStringAll from long options table 5127836SJohn.Forte@Sun.COM */ 5137836SJohn.Forte@Sun.COM for (lp = _longOptions; lp->name; lp++, j++) { 5147836SJohn.Forte@Sun.COM /* sanity check on string length */ 5157836SJohn.Forte@Sun.COM if (j + 1 >= sizeof (optionStringAll)) { 5167836SJohn.Forte@Sun.COM /* option table too long */ 5177836SJohn.Forte@Sun.COM assert(0); 5187836SJohn.Forte@Sun.COM } 5197836SJohn.Forte@Sun.COM optionStringAll[j] = lp->val; 5207836SJohn.Forte@Sun.COM if (lp->has_arg == required_argument) { 5217836SJohn.Forte@Sun.COM optionStringAll[++j] = ':'; 5227836SJohn.Forte@Sun.COM } 5237836SJohn.Forte@Sun.COM } 5247836SJohn.Forte@Sun.COM 5257836SJohn.Forte@Sun.COM i = 0; 5267836SJohn.Forte@Sun.COM /* 5277836SJohn.Forte@Sun.COM * Run getopt for all arguments against all possible options 5287836SJohn.Forte@Sun.COM * Store all options/option arguments in an array for retrieval 5297836SJohn.Forte@Sun.COM * later. 5307836SJohn.Forte@Sun.COM * 5317836SJohn.Forte@Sun.COM * Once all options are retrieved, a validity check against 5327836SJohn.Forte@Sun.COM * subcommand table is performed. 5337836SJohn.Forte@Sun.COM */ 5347836SJohn.Forte@Sun.COM while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, 5357836SJohn.Forte@Sun.COM _longOptions, NULL)) != EOF) { 5367836SJohn.Forte@Sun.COM switch (opt) { 5377836SJohn.Forte@Sun.COM case '?': 5387836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 539*10652SHyon.Kim@Sun.COM /* 540*10652SHyon.Kim@Sun.COM * getopt can return a '?' when no 541*10652SHyon.Kim@Sun.COM * option letters match string. Check for 542*10652SHyon.Kim@Sun.COM * the 'real' '?' in optopt. 543*10652SHyon.Kim@Sun.COM */ 544*10652SHyon.Kim@Sun.COM if (optopt == '?') { 545*10652SHyon.Kim@Sun.COM exit(0); 546*10652SHyon.Kim@Sun.COM } else { 547*10652SHyon.Kim@Sun.COM exit(1); 548*10652SHyon.Kim@Sun.COM } 5497836SJohn.Forte@Sun.COM default: 5507836SJohn.Forte@Sun.COM cmdOptions[i].optval = opt; 5517836SJohn.Forte@Sun.COM if (optarg) { 5527836SJohn.Forte@Sun.COM len = strlen(optarg); 5537836SJohn.Forte@Sun.COM if (len > sizeof (cmdOptions[i].optarg) 5547836SJohn.Forte@Sun.COM - 1) { 5557836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", 5567836SJohn.Forte@Sun.COM commandName, 5577836SJohn.Forte@Sun.COM gettext("option too long")); 5587836SJohn.Forte@Sun.COM errno = EINVAL; 5597836SJohn.Forte@Sun.COM return (-1); 5607836SJohn.Forte@Sun.COM } 5617836SJohn.Forte@Sun.COM (void) strncpy(cmdOptions[i].optarg, 5627836SJohn.Forte@Sun.COM optarg, len); 5637836SJohn.Forte@Sun.COM } 5647836SJohn.Forte@Sun.COM i++; 5657836SJohn.Forte@Sun.COM break; 5667836SJohn.Forte@Sun.COM } 5677836SJohn.Forte@Sun.COM } 5687836SJohn.Forte@Sun.COM 5697836SJohn.Forte@Sun.COM /* 5707836SJohn.Forte@Sun.COM * increment past last option 5717836SJohn.Forte@Sun.COM */ 5727836SJohn.Forte@Sun.COM operInd = optind + 1; 5737836SJohn.Forte@Sun.COM 5747836SJohn.Forte@Sun.COM /* 5757836SJohn.Forte@Sun.COM * Check validity of given options, if any were given 5767836SJohn.Forte@Sun.COM */ 5777836SJohn.Forte@Sun.COM 5787836SJohn.Forte@Sun.COM /* get option string for this subcommand */ 5797836SJohn.Forte@Sun.COM availOptions = subcommand->optionString; 5807836SJohn.Forte@Sun.COM 5817836SJohn.Forte@Sun.COM /* Get count of required options */ 5827836SJohn.Forte@Sun.COM if (subcommand->required) { 5837836SJohn.Forte@Sun.COM requiredOptionCnt = strlen(subcommand->required); 5847836SJohn.Forte@Sun.COM } 5857836SJohn.Forte@Sun.COM 5867836SJohn.Forte@Sun.COM if (cmdOptions[0].optval != 0) { /* options were input */ 5877836SJohn.Forte@Sun.COM if (availOptions == NULL) { /* no options permitted */ 5887836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 5897836SJohn.Forte@Sun.COM gettext("no options permitted")); 5907836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 5917836SJohn.Forte@Sun.COM return (1); 5927836SJohn.Forte@Sun.COM } 5937836SJohn.Forte@Sun.COM for (i = 0; cmdOptions[i].optval; i++) { 5947836SJohn.Forte@Sun.COM /* is the option in the available option string? */ 5957836SJohn.Forte@Sun.COM if (!(strchr(availOptions, cmdOptions[i].optval))) { 5967836SJohn.Forte@Sun.COM (void) printf("%s: '-%c': %s\n", commandName, 5977836SJohn.Forte@Sun.COM cmdOptions[i].optval, 5987836SJohn.Forte@Sun.COM gettext("invalid option")); 5997836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6007836SJohn.Forte@Sun.COM return (1); 6017836SJohn.Forte@Sun.COM /* increment required options entered */ 6027836SJohn.Forte@Sun.COM } else if (subcommand->required && 6037836SJohn.Forte@Sun.COM (strchr(subcommand->required, 6047836SJohn.Forte@Sun.COM cmdOptions[i].optval))) { 6057836SJohn.Forte@Sun.COM requiredOptionEntered++; 6067836SJohn.Forte@Sun.COM /* Check for exclusive options */ 6077836SJohn.Forte@Sun.COM } else if (cmdOptions[1].optval != 0 && 6087836SJohn.Forte@Sun.COM subcommand->exclusive && 6097836SJohn.Forte@Sun.COM strchr(subcommand->exclusive, 6107836SJohn.Forte@Sun.COM cmdOptions[i].optval)) { 6117836SJohn.Forte@Sun.COM (void) printf("%s: '-%c': %s\n", 6127836SJohn.Forte@Sun.COM commandName, cmdOptions[i].optval, 6137836SJohn.Forte@Sun.COM gettext("is an exclusive option")); 6147836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6157836SJohn.Forte@Sun.COM return (1); 6167836SJohn.Forte@Sun.COM } 6177836SJohn.Forte@Sun.COM } 6187836SJohn.Forte@Sun.COM } else { /* no options were input */ 6197836SJohn.Forte@Sun.COM if (availOptions != NULL && subcommand->required) { 6207836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 6217836SJohn.Forte@Sun.COM gettext("at least one option required")); 6227836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6237836SJohn.Forte@Sun.COM return (1); 6247836SJohn.Forte@Sun.COM } 6257836SJohn.Forte@Sun.COM } 6267836SJohn.Forte@Sun.COM 6277836SJohn.Forte@Sun.COM /* Were all required options entered? */ 6287836SJohn.Forte@Sun.COM if (requiredOptionEntered != requiredOptionCnt) { 6297836SJohn.Forte@Sun.COM (void) printf("%s: %s: %s\n", commandName, 6307836SJohn.Forte@Sun.COM gettext("Following option(s) required"), 6317836SJohn.Forte@Sun.COM subcommand->required); 6327836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6337836SJohn.Forte@Sun.COM return (1); 6347836SJohn.Forte@Sun.COM } 6357836SJohn.Forte@Sun.COM 6367836SJohn.Forte@Sun.COM 6377836SJohn.Forte@Sun.COM /* 6387836SJohn.Forte@Sun.COM * If there are no operands, 6397836SJohn.Forte@Sun.COM * check to see if this is okay 6407836SJohn.Forte@Sun.COM */ 6417836SJohn.Forte@Sun.COM if ((operInd == argc) && 6427836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_MANDATORY)) { 6437836SJohn.Forte@Sun.COM (void) printf("%s: %s %s\n", commandName, subcommand->name, 6447836SJohn.Forte@Sun.COM gettext("requires an operand")); 6457836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6467836SJohn.Forte@Sun.COM return (1); 6477836SJohn.Forte@Sun.COM } 6487836SJohn.Forte@Sun.COM 6497836SJohn.Forte@Sun.COM /* 6507836SJohn.Forte@Sun.COM * If there are more operands, 6517836SJohn.Forte@Sun.COM * check to see if this is okay 6527836SJohn.Forte@Sun.COM */ 6537836SJohn.Forte@Sun.COM if ((argc > operInd) && 6547836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_NONE)) { 6557836SJohn.Forte@Sun.COM (void) fprintf(stderr, "%s: %s %s\n", commandName, 6567836SJohn.Forte@Sun.COM subcommand->name, gettext("takes no operands")); 6577836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6587836SJohn.Forte@Sun.COM return (1); 6597836SJohn.Forte@Sun.COM } 6607836SJohn.Forte@Sun.COM 6617836SJohn.Forte@Sun.COM /* 6627836SJohn.Forte@Sun.COM * If there is more than one more operand, 6637836SJohn.Forte@Sun.COM * check to see if this is okay 6647836SJohn.Forte@Sun.COM */ 6657836SJohn.Forte@Sun.COM if ((argc > operInd) && ((argc - operInd) != 1) && 6667836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_SINGLE)) { 6677836SJohn.Forte@Sun.COM (void) printf("%s: %s %s\n", commandName, 6687836SJohn.Forte@Sun.COM subcommand->name, gettext("accepts only a single operand")); 6697836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 6707836SJohn.Forte@Sun.COM return (1); 6717836SJohn.Forte@Sun.COM } 6727836SJohn.Forte@Sun.COM 6737836SJohn.Forte@Sun.COM /* Finished syntax checks */ 6747836SJohn.Forte@Sun.COM 6757836SJohn.Forte@Sun.COM 6767836SJohn.Forte@Sun.COM /* Call appropriate function */ 6777836SJohn.Forte@Sun.COM *funcRet = subcommand->handler(argc - operInd, &argv[operInd], 6787836SJohn.Forte@Sun.COM &cmdOptions[0], callArgs); 6797836SJohn.Forte@Sun.COM 6807836SJohn.Forte@Sun.COM return (0); 6817836SJohn.Forte@Sun.COM } 682