1*7836SJohn.Forte@Sun.COM /* 2*7836SJohn.Forte@Sun.COM * CDDL HEADER START 3*7836SJohn.Forte@Sun.COM * 4*7836SJohn.Forte@Sun.COM * The contents of this file are subject to the terms of the 5*7836SJohn.Forte@Sun.COM * Common Development and Distribution License (the "License"). 6*7836SJohn.Forte@Sun.COM * You may not use this file except in compliance with the License. 7*7836SJohn.Forte@Sun.COM * 8*7836SJohn.Forte@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*7836SJohn.Forte@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*7836SJohn.Forte@Sun.COM * See the License for the specific language governing permissions 11*7836SJohn.Forte@Sun.COM * and limitations under the License. 12*7836SJohn.Forte@Sun.COM * 13*7836SJohn.Forte@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*7836SJohn.Forte@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*7836SJohn.Forte@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*7836SJohn.Forte@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*7836SJohn.Forte@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*7836SJohn.Forte@Sun.COM * 19*7836SJohn.Forte@Sun.COM * CDDL HEADER END 20*7836SJohn.Forte@Sun.COM */ 21*7836SJohn.Forte@Sun.COM /* 22*7836SJohn.Forte@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23*7836SJohn.Forte@Sun.COM * Use is subject to license terms. 24*7836SJohn.Forte@Sun.COM */ 25*7836SJohn.Forte@Sun.COM 26*7836SJohn.Forte@Sun.COM #include <stdlib.h> 27*7836SJohn.Forte@Sun.COM #include <stdio.h> 28*7836SJohn.Forte@Sun.COM #include <sys/types.h> 29*7836SJohn.Forte@Sun.COM #include <unistd.h> 30*7836SJohn.Forte@Sun.COM #include <libintl.h> 31*7836SJohn.Forte@Sun.COM #include <errno.h> 32*7836SJohn.Forte@Sun.COM #include <string.h> 33*7836SJohn.Forte@Sun.COM #include <assert.h> 34*7836SJohn.Forte@Sun.COM #include <getopt.h> 35*7836SJohn.Forte@Sun.COM #include <cmdparse.h> 36*7836SJohn.Forte@Sun.COM 37*7836SJohn.Forte@Sun.COM 38*7836SJohn.Forte@Sun.COM /* Usage types */ 39*7836SJohn.Forte@Sun.COM #define GENERAL_USAGE 1 40*7836SJohn.Forte@Sun.COM #define DETAIL_USAGE 2 41*7836SJohn.Forte@Sun.COM 42*7836SJohn.Forte@Sun.COM /* printable ascii character set len */ 43*7836SJohn.Forte@Sun.COM #define MAXOPTIONS (uint_t)('~' - '!' + 1) 44*7836SJohn.Forte@Sun.COM 45*7836SJohn.Forte@Sun.COM /* 46*7836SJohn.Forte@Sun.COM * MAXOPTIONSTRING is the max length of the options string used in getopt and 47*7836SJohn.Forte@Sun.COM * will be the printable character set + ':' for each character, 48*7836SJohn.Forte@Sun.COM * providing for options with arguments. e.g. "t:Cs:hglr:" 49*7836SJohn.Forte@Sun.COM */ 50*7836SJohn.Forte@Sun.COM #define MAXOPTIONSTRING MAXOPTIONS * 2 51*7836SJohn.Forte@Sun.COM 52*7836SJohn.Forte@Sun.COM /* standard command options table to support -?, -V */ 53*7836SJohn.Forte@Sun.COM struct option standardCmdOptions[] = { 54*7836SJohn.Forte@Sun.COM {"help", no_argument, NULL, '?'}, 55*7836SJohn.Forte@Sun.COM {"version", no_argument, NULL, 'V'}, 56*7836SJohn.Forte@Sun.COM {NULL, 0, NULL, 0} 57*7836SJohn.Forte@Sun.COM }; 58*7836SJohn.Forte@Sun.COM 59*7836SJohn.Forte@Sun.COM /* standard subcommand options table to support -? */ 60*7836SJohn.Forte@Sun.COM struct option standardSubCmdOptions[] = { 61*7836SJohn.Forte@Sun.COM {"help", no_argument, NULL, '?'}, 62*7836SJohn.Forte@Sun.COM {NULL, 0, NULL, 0} 63*7836SJohn.Forte@Sun.COM }; 64*7836SJohn.Forte@Sun.COM 65*7836SJohn.Forte@Sun.COM /* forward declarations */ 66*7836SJohn.Forte@Sun.COM static int getSubcommandProps(char *, subCommandProps_t **); 67*7836SJohn.Forte@Sun.COM static char *getExecBasename(char *); 68*7836SJohn.Forte@Sun.COM static void usage(uint_t); 69*7836SJohn.Forte@Sun.COM static void subUsage(uint_t, subCommandProps_t *); 70*7836SJohn.Forte@Sun.COM static char *getLongOption(int); 71*7836SJohn.Forte@Sun.COM static char *getOptionArgDesc(int); 72*7836SJohn.Forte@Sun.COM 73*7836SJohn.Forte@Sun.COM /* global data */ 74*7836SJohn.Forte@Sun.COM static struct option *_longOptions; 75*7836SJohn.Forte@Sun.COM static subCommandProps_t *_subCommandProps; 76*7836SJohn.Forte@Sun.COM static optionTbl_t *_clientOptionTbl; 77*7836SJohn.Forte@Sun.COM static char *commandName; 78*7836SJohn.Forte@Sun.COM 79*7836SJohn.Forte@Sun.COM 80*7836SJohn.Forte@Sun.COM /* 81*7836SJohn.Forte@Sun.COM * input: 82*7836SJohn.Forte@Sun.COM * subCommand - subcommand value 83*7836SJohn.Forte@Sun.COM * output: 84*7836SJohn.Forte@Sun.COM * subCommandProps - pointer to subCommandProps_t structure allocated by caller 85*7836SJohn.Forte@Sun.COM * 86*7836SJohn.Forte@Sun.COM * On successful return, subCommandProps contains the properties for the value 87*7836SJohn.Forte@Sun.COM * in subCommand. On failure, the contents of subCommandProps is unspecified. 88*7836SJohn.Forte@Sun.COM * 89*7836SJohn.Forte@Sun.COM * Returns: 90*7836SJohn.Forte@Sun.COM * zero on success 91*7836SJohn.Forte@Sun.COM * non-zero on failure 92*7836SJohn.Forte@Sun.COM * 93*7836SJohn.Forte@Sun.COM */ 94*7836SJohn.Forte@Sun.COM static int 95*7836SJohn.Forte@Sun.COM getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps) 96*7836SJohn.Forte@Sun.COM { 97*7836SJohn.Forte@Sun.COM subCommandProps_t *sp; 98*7836SJohn.Forte@Sun.COM int len; 99*7836SJohn.Forte@Sun.COM 100*7836SJohn.Forte@Sun.COM for (sp = _subCommandProps; sp->name; sp++) { 101*7836SJohn.Forte@Sun.COM len = strlen(subCommand); 102*7836SJohn.Forte@Sun.COM if (len == strlen(sp->name) && 103*7836SJohn.Forte@Sun.COM strncasecmp(subCommand, sp->name, len) == 0) { 104*7836SJohn.Forte@Sun.COM *subCommandProps = sp; 105*7836SJohn.Forte@Sun.COM return (0); 106*7836SJohn.Forte@Sun.COM } 107*7836SJohn.Forte@Sun.COM } 108*7836SJohn.Forte@Sun.COM return (1); 109*7836SJohn.Forte@Sun.COM } 110*7836SJohn.Forte@Sun.COM 111*7836SJohn.Forte@Sun.COM /* 112*7836SJohn.Forte@Sun.COM * input: 113*7836SJohn.Forte@Sun.COM * shortOption - short option character for which to return the 114*7836SJohn.Forte@Sun.COM * associated long option string 115*7836SJohn.Forte@Sun.COM * 116*7836SJohn.Forte@Sun.COM * Returns: 117*7836SJohn.Forte@Sun.COM * on success, long option name 118*7836SJohn.Forte@Sun.COM * on failure, NULL 119*7836SJohn.Forte@Sun.COM */ 120*7836SJohn.Forte@Sun.COM static char * 121*7836SJohn.Forte@Sun.COM getLongOption(int shortOption) 122*7836SJohn.Forte@Sun.COM { 123*7836SJohn.Forte@Sun.COM struct option *op; 124*7836SJohn.Forte@Sun.COM for (op = _longOptions; op->name; op++) { 125*7836SJohn.Forte@Sun.COM if (shortOption == op->val) { 126*7836SJohn.Forte@Sun.COM return (op->name); 127*7836SJohn.Forte@Sun.COM } 128*7836SJohn.Forte@Sun.COM } 129*7836SJohn.Forte@Sun.COM return (NULL); 130*7836SJohn.Forte@Sun.COM } 131*7836SJohn.Forte@Sun.COM 132*7836SJohn.Forte@Sun.COM /* 133*7836SJohn.Forte@Sun.COM * input 134*7836SJohn.Forte@Sun.COM * shortOption - short option character for which to return the 135*7836SJohn.Forte@Sun.COM * option argument 136*7836SJohn.Forte@Sun.COM * Returns: 137*7836SJohn.Forte@Sun.COM * on success, argument string 138*7836SJohn.Forte@Sun.COM * on failure, NULL 139*7836SJohn.Forte@Sun.COM */ 140*7836SJohn.Forte@Sun.COM static char * 141*7836SJohn.Forte@Sun.COM getOptionArgDesc(int shortOption) 142*7836SJohn.Forte@Sun.COM { 143*7836SJohn.Forte@Sun.COM optionTbl_t *op; 144*7836SJohn.Forte@Sun.COM for (op = _clientOptionTbl; op->name; op++) { 145*7836SJohn.Forte@Sun.COM if (op->val == shortOption && 146*7836SJohn.Forte@Sun.COM op->has_arg == required_argument) { 147*7836SJohn.Forte@Sun.COM return (op->argDesc); 148*7836SJohn.Forte@Sun.COM } 149*7836SJohn.Forte@Sun.COM } 150*7836SJohn.Forte@Sun.COM return (NULL); 151*7836SJohn.Forte@Sun.COM } 152*7836SJohn.Forte@Sun.COM 153*7836SJohn.Forte@Sun.COM 154*7836SJohn.Forte@Sun.COM /* 155*7836SJohn.Forte@Sun.COM * Print usage for a subcommand. 156*7836SJohn.Forte@Sun.COM * 157*7836SJohn.Forte@Sun.COM * input: 158*7836SJohn.Forte@Sun.COM * usage type - GENERAL_USAGE, DETAIL_USAGE 159*7836SJohn.Forte@Sun.COM * subcommand - pointer to subCommandProps_t structure 160*7836SJohn.Forte@Sun.COM * 161*7836SJohn.Forte@Sun.COM * Returns: 162*7836SJohn.Forte@Sun.COM * none 163*7836SJohn.Forte@Sun.COM * 164*7836SJohn.Forte@Sun.COM */ 165*7836SJohn.Forte@Sun.COM static void 166*7836SJohn.Forte@Sun.COM subUsage(uint_t usageType, subCommandProps_t *subcommand) 167*7836SJohn.Forte@Sun.COM { 168*7836SJohn.Forte@Sun.COM int i; 169*7836SJohn.Forte@Sun.COM char *optionArgDesc; 170*7836SJohn.Forte@Sun.COM char *longOpt; 171*7836SJohn.Forte@Sun.COM 172*7836SJohn.Forte@Sun.COM if (usageType == GENERAL_USAGE) { 173*7836SJohn.Forte@Sun.COM (void) printf("%s:\t%s %s [", gettext("Usage"), commandName, 174*7836SJohn.Forte@Sun.COM subcommand->name); 175*7836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 176*7836SJohn.Forte@Sun.COM (void) printf("-%c", standardSubCmdOptions[i].val); 177*7836SJohn.Forte@Sun.COM if (standardSubCmdOptions[i+1].name) 178*7836SJohn.Forte@Sun.COM (void) printf(","); 179*7836SJohn.Forte@Sun.COM } 180*7836SJohn.Forte@Sun.COM (void) fprintf(stdout, "]\n"); 181*7836SJohn.Forte@Sun.COM return; 182*7836SJohn.Forte@Sun.COM } 183*7836SJohn.Forte@Sun.COM 184*7836SJohn.Forte@Sun.COM /* print subcommand usage */ 185*7836SJohn.Forte@Sun.COM (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName, 186*7836SJohn.Forte@Sun.COM subcommand->name); 187*7836SJohn.Forte@Sun.COM 188*7836SJohn.Forte@Sun.COM /* print options if applicable */ 189*7836SJohn.Forte@Sun.COM if (subcommand->optionString != NULL) { 190*7836SJohn.Forte@Sun.COM if (subcommand->required) { 191*7836SJohn.Forte@Sun.COM (void) printf("%s", gettext("<")); 192*7836SJohn.Forte@Sun.COM } else { 193*7836SJohn.Forte@Sun.COM (void) printf("%s", gettext("[")); 194*7836SJohn.Forte@Sun.COM } 195*7836SJohn.Forte@Sun.COM (void) printf("%s", gettext("OPTIONS")); 196*7836SJohn.Forte@Sun.COM if (subcommand->required) { 197*7836SJohn.Forte@Sun.COM (void) printf("%s ", gettext(">")); 198*7836SJohn.Forte@Sun.COM } else { 199*7836SJohn.Forte@Sun.COM (void) printf("%s ", gettext("]")); 200*7836SJohn.Forte@Sun.COM } 201*7836SJohn.Forte@Sun.COM } 202*7836SJohn.Forte@Sun.COM 203*7836SJohn.Forte@Sun.COM /* print operand requirements */ 204*7836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE) && 205*7836SJohn.Forte@Sun.COM !(subcommand->operand & OPERAND_MANDATORY)) { 206*7836SJohn.Forte@Sun.COM (void) printf(gettext("[")); 207*7836SJohn.Forte@Sun.COM } 208*7836SJohn.Forte@Sun.COM 209*7836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MANDATORY) { 210*7836SJohn.Forte@Sun.COM (void) printf(gettext("<")); 211*7836SJohn.Forte@Sun.COM } 212*7836SJohn.Forte@Sun.COM 213*7836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE)) { 214*7836SJohn.Forte@Sun.COM assert(subcommand->operandDefinition); 215*7836SJohn.Forte@Sun.COM (void) printf("%s", subcommand->operandDefinition); 216*7836SJohn.Forte@Sun.COM } 217*7836SJohn.Forte@Sun.COM 218*7836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MULTIPLE) { 219*7836SJohn.Forte@Sun.COM (void) printf(gettext(" ...")); 220*7836SJohn.Forte@Sun.COM } 221*7836SJohn.Forte@Sun.COM 222*7836SJohn.Forte@Sun.COM if (subcommand->operand & OPERAND_MANDATORY) { 223*7836SJohn.Forte@Sun.COM (void) printf(gettext(">")); 224*7836SJohn.Forte@Sun.COM } 225*7836SJohn.Forte@Sun.COM 226*7836SJohn.Forte@Sun.COM if (!(subcommand->operand & OPERAND_NONE) && 227*7836SJohn.Forte@Sun.COM !(subcommand->operand & OPERAND_MANDATORY)) { 228*7836SJohn.Forte@Sun.COM (void) printf(gettext("]")); 229*7836SJohn.Forte@Sun.COM } 230*7836SJohn.Forte@Sun.COM 231*7836SJohn.Forte@Sun.COM /* print options for subcommand */ 232*7836SJohn.Forte@Sun.COM if (subcommand->optionString != NULL) { 233*7836SJohn.Forte@Sun.COM (void) printf("\n\t%s:", gettext("OPTIONS")); 234*7836SJohn.Forte@Sun.COM for (i = 0; i < strlen(subcommand->optionString); i++) { 235*7836SJohn.Forte@Sun.COM assert((longOpt = getLongOption( 236*7836SJohn.Forte@Sun.COM subcommand->optionString[i])) != NULL); 237*7836SJohn.Forte@Sun.COM (void) printf("\n\t\t-%c, --%s ", 238*7836SJohn.Forte@Sun.COM subcommand->optionString[i], 239*7836SJohn.Forte@Sun.COM longOpt); 240*7836SJohn.Forte@Sun.COM optionArgDesc = 241*7836SJohn.Forte@Sun.COM getOptionArgDesc(subcommand->optionString[i]); 242*7836SJohn.Forte@Sun.COM if (optionArgDesc != NULL) { 243*7836SJohn.Forte@Sun.COM (void) printf("<%s>", optionArgDesc); 244*7836SJohn.Forte@Sun.COM } 245*7836SJohn.Forte@Sun.COM if (subcommand->exclusive && 246*7836SJohn.Forte@Sun.COM strchr(subcommand->exclusive, 247*7836SJohn.Forte@Sun.COM subcommand->optionString[i])) { 248*7836SJohn.Forte@Sun.COM (void) printf(" (%s)", gettext("exclusive")); 249*7836SJohn.Forte@Sun.COM } 250*7836SJohn.Forte@Sun.COM } 251*7836SJohn.Forte@Sun.COM } 252*7836SJohn.Forte@Sun.COM (void) fprintf(stdout, "\n"); 253*7836SJohn.Forte@Sun.COM } 254*7836SJohn.Forte@Sun.COM 255*7836SJohn.Forte@Sun.COM /* 256*7836SJohn.Forte@Sun.COM * input: 257*7836SJohn.Forte@Sun.COM * type of usage statement to print 258*7836SJohn.Forte@Sun.COM * 259*7836SJohn.Forte@Sun.COM * Returns: 260*7836SJohn.Forte@Sun.COM * return value of subUsage 261*7836SJohn.Forte@Sun.COM */ 262*7836SJohn.Forte@Sun.COM static void 263*7836SJohn.Forte@Sun.COM usage(uint_t usageType) 264*7836SJohn.Forte@Sun.COM { 265*7836SJohn.Forte@Sun.COM int i; 266*7836SJohn.Forte@Sun.COM subCommandProps_t *sp; 267*7836SJohn.Forte@Sun.COM 268*7836SJohn.Forte@Sun.COM /* print general command usage */ 269*7836SJohn.Forte@Sun.COM (void) printf("%s:\t%s ", gettext("Usage"), commandName); 270*7836SJohn.Forte@Sun.COM 271*7836SJohn.Forte@Sun.COM for (i = 0; standardCmdOptions[i].name; i++) { 272*7836SJohn.Forte@Sun.COM (void) printf("-%c", standardCmdOptions[i].val); 273*7836SJohn.Forte@Sun.COM if (standardCmdOptions[i+1].name) 274*7836SJohn.Forte@Sun.COM (void) printf(","); 275*7836SJohn.Forte@Sun.COM } 276*7836SJohn.Forte@Sun.COM 277*7836SJohn.Forte@Sun.COM if (usageType == GENERAL_USAGE) { 278*7836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 279*7836SJohn.Forte@Sun.COM (void) printf(",--%s", standardSubCmdOptions[i].name); 280*7836SJohn.Forte@Sun.COM if (standardSubCmdOptions[i+1].name) 281*7836SJohn.Forte@Sun.COM (void) printf(","); 282*7836SJohn.Forte@Sun.COM } 283*7836SJohn.Forte@Sun.COM } 284*7836SJohn.Forte@Sun.COM 285*7836SJohn.Forte@Sun.COM (void) fprintf(stdout, "\n"); 286*7836SJohn.Forte@Sun.COM 287*7836SJohn.Forte@Sun.COM 288*7836SJohn.Forte@Sun.COM /* print all subcommand usage */ 289*7836SJohn.Forte@Sun.COM for (sp = _subCommandProps; sp->name; sp++) { 290*7836SJohn.Forte@Sun.COM subUsage(usageType, sp); 291*7836SJohn.Forte@Sun.COM } 292*7836SJohn.Forte@Sun.COM } 293*7836SJohn.Forte@Sun.COM 294*7836SJohn.Forte@Sun.COM /* 295*7836SJohn.Forte@Sun.COM * input: 296*7836SJohn.Forte@Sun.COM * execFullName - exec name of program (argv[0]) 297*7836SJohn.Forte@Sun.COM * 298*7836SJohn.Forte@Sun.COM * Returns: 299*7836SJohn.Forte@Sun.COM * command name portion of execFullName 300*7836SJohn.Forte@Sun.COM */ 301*7836SJohn.Forte@Sun.COM static char * 302*7836SJohn.Forte@Sun.COM getExecBasename(char *execFullname) 303*7836SJohn.Forte@Sun.COM { 304*7836SJohn.Forte@Sun.COM char *lastSlash, *execBasename; 305*7836SJohn.Forte@Sun.COM 306*7836SJohn.Forte@Sun.COM /* guard against '/' at end of command invocation */ 307*7836SJohn.Forte@Sun.COM for (;;) { 308*7836SJohn.Forte@Sun.COM lastSlash = strrchr(execFullname, '/'); 309*7836SJohn.Forte@Sun.COM if (lastSlash == NULL) { 310*7836SJohn.Forte@Sun.COM execBasename = execFullname; 311*7836SJohn.Forte@Sun.COM break; 312*7836SJohn.Forte@Sun.COM } else { 313*7836SJohn.Forte@Sun.COM execBasename = lastSlash + 1; 314*7836SJohn.Forte@Sun.COM if (*execBasename == '\0') { 315*7836SJohn.Forte@Sun.COM *lastSlash = '\0'; 316*7836SJohn.Forte@Sun.COM continue; 317*7836SJohn.Forte@Sun.COM } 318*7836SJohn.Forte@Sun.COM break; 319*7836SJohn.Forte@Sun.COM } 320*7836SJohn.Forte@Sun.COM } 321*7836SJohn.Forte@Sun.COM return (execBasename); 322*7836SJohn.Forte@Sun.COM } 323*7836SJohn.Forte@Sun.COM 324*7836SJohn.Forte@Sun.COM /* 325*7836SJohn.Forte@Sun.COM * cmdParse is a parser that checks syntax of the input command against 326*7836SJohn.Forte@Sun.COM * various rules tables. 327*7836SJohn.Forte@Sun.COM * 328*7836SJohn.Forte@Sun.COM * It provides usage feedback based upon the passed rules tables by calling 329*7836SJohn.Forte@Sun.COM * two usage functions, usage, subUsage 330*7836SJohn.Forte@Sun.COM * 331*7836SJohn.Forte@Sun.COM * When syntax is successfully validated, the associated function is called 332*7836SJohn.Forte@Sun.COM * using the subcommands table functions. 333*7836SJohn.Forte@Sun.COM * 334*7836SJohn.Forte@Sun.COM * Syntax is as follows: 335*7836SJohn.Forte@Sun.COM * command subcommand [<options>] [<operand>] 336*7836SJohn.Forte@Sun.COM * 337*7836SJohn.Forte@Sun.COM * There are two standard short and long options assumed: 338*7836SJohn.Forte@Sun.COM * -?, --help Provides usage on a command or subcommand 339*7836SJohn.Forte@Sun.COM * and stops further processing of the arguments 340*7836SJohn.Forte@Sun.COM * 341*7836SJohn.Forte@Sun.COM * -V, --version Provides version information on the command 342*7836SJohn.Forte@Sun.COM * and stops further processing of the arguments 343*7836SJohn.Forte@Sun.COM * 344*7836SJohn.Forte@Sun.COM * These options are loaded by this function. 345*7836SJohn.Forte@Sun.COM * 346*7836SJohn.Forte@Sun.COM * input: 347*7836SJohn.Forte@Sun.COM * argc, argv from main 348*7836SJohn.Forte@Sun.COM * syntax rules tables (synTables_t structure) 349*7836SJohn.Forte@Sun.COM * callArgs - void * passed by caller to be passed to subcommand function 350*7836SJohn.Forte@Sun.COM * 351*7836SJohn.Forte@Sun.COM * output: 352*7836SJohn.Forte@Sun.COM * funcRet - pointer to int that holds subcommand function return value 353*7836SJohn.Forte@Sun.COM * 354*7836SJohn.Forte@Sun.COM * Returns: 355*7836SJohn.Forte@Sun.COM * 356*7836SJohn.Forte@Sun.COM * zero on successful syntax parse and function call 357*7836SJohn.Forte@Sun.COM * 358*7836SJohn.Forte@Sun.COM * 1 on unsuccessful syntax parse (no function has been called) 359*7836SJohn.Forte@Sun.COM * This could be due to a version or help call or simply a 360*7836SJohn.Forte@Sun.COM * general usage call. 361*7836SJohn.Forte@Sun.COM * 362*7836SJohn.Forte@Sun.COM * -1 check errno, call failed 363*7836SJohn.Forte@Sun.COM * 364*7836SJohn.Forte@Sun.COM * This module is not MT-safe. 365*7836SJohn.Forte@Sun.COM * 366*7836SJohn.Forte@Sun.COM */ 367*7836SJohn.Forte@Sun.COM int 368*7836SJohn.Forte@Sun.COM cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, 369*7836SJohn.Forte@Sun.COM int *funcRet) 370*7836SJohn.Forte@Sun.COM { 371*7836SJohn.Forte@Sun.COM int getoptargc; 372*7836SJohn.Forte@Sun.COM char **getoptargv; 373*7836SJohn.Forte@Sun.COM int opt; 374*7836SJohn.Forte@Sun.COM int operInd; 375*7836SJohn.Forte@Sun.COM int i, j; 376*7836SJohn.Forte@Sun.COM int len; 377*7836SJohn.Forte@Sun.COM int requiredOptionCnt = 0, requiredOptionEntered = 0; 378*7836SJohn.Forte@Sun.COM char *availOptions; 379*7836SJohn.Forte@Sun.COM char *versionString; 380*7836SJohn.Forte@Sun.COM char optionStringAll[MAXOPTIONSTRING + 1]; 381*7836SJohn.Forte@Sun.COM subCommandProps_t *subcommand; 382*7836SJohn.Forte@Sun.COM cmdOptions_t cmdOptions[MAXOPTIONS + 1]; 383*7836SJohn.Forte@Sun.COM optionTbl_t *optionTbl; 384*7836SJohn.Forte@Sun.COM struct option *lp; 385*7836SJohn.Forte@Sun.COM struct option intLongOpt[MAXOPTIONS + 1]; 386*7836SJohn.Forte@Sun.COM 387*7836SJohn.Forte@Sun.COM /* 388*7836SJohn.Forte@Sun.COM * Check for NULLs on mandatory input arguments 389*7836SJohn.Forte@Sun.COM * 390*7836SJohn.Forte@Sun.COM * Note: longOptionTbl can be NULL in the case 391*7836SJohn.Forte@Sun.COM * where there is no caller defined options 392*7836SJohn.Forte@Sun.COM * 393*7836SJohn.Forte@Sun.COM */ 394*7836SJohn.Forte@Sun.COM assert(synTable.versionString); 395*7836SJohn.Forte@Sun.COM assert(synTable.subCommandPropsTbl); 396*7836SJohn.Forte@Sun.COM assert(funcRet); 397*7836SJohn.Forte@Sun.COM 398*7836SJohn.Forte@Sun.COM versionString = synTable.versionString; 399*7836SJohn.Forte@Sun.COM 400*7836SJohn.Forte@Sun.COM /* set global command name */ 401*7836SJohn.Forte@Sun.COM commandName = getExecBasename(argv[0]); 402*7836SJohn.Forte@Sun.COM 403*7836SJohn.Forte@Sun.COM /* Set unbuffered output */ 404*7836SJohn.Forte@Sun.COM setbuf(stdout, NULL); 405*7836SJohn.Forte@Sun.COM 406*7836SJohn.Forte@Sun.COM /* load globals */ 407*7836SJohn.Forte@Sun.COM _subCommandProps = synTable.subCommandPropsTbl; 408*7836SJohn.Forte@Sun.COM _clientOptionTbl = synTable.longOptionTbl; 409*7836SJohn.Forte@Sun.COM 410*7836SJohn.Forte@Sun.COM /* There must be at least two arguments */ 411*7836SJohn.Forte@Sun.COM if (argc < 2) { 412*7836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 413*7836SJohn.Forte@Sun.COM return (1); 414*7836SJohn.Forte@Sun.COM } 415*7836SJohn.Forte@Sun.COM 416*7836SJohn.Forte@Sun.COM (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt)); 417*7836SJohn.Forte@Sun.COM 418*7836SJohn.Forte@Sun.COM /* 419*7836SJohn.Forte@Sun.COM * load standard subcommand options to internal long options table 420*7836SJohn.Forte@Sun.COM * Two separate getopt_long(3C) tables are used. 421*7836SJohn.Forte@Sun.COM */ 422*7836SJohn.Forte@Sun.COM for (i = 0; standardSubCmdOptions[i].name; i++) { 423*7836SJohn.Forte@Sun.COM intLongOpt[i].name = standardSubCmdOptions[i].name; 424*7836SJohn.Forte@Sun.COM intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; 425*7836SJohn.Forte@Sun.COM intLongOpt[i].flag = standardSubCmdOptions[i].flag; 426*7836SJohn.Forte@Sun.COM intLongOpt[i].val = standardSubCmdOptions[i].val; 427*7836SJohn.Forte@Sun.COM } 428*7836SJohn.Forte@Sun.COM 429*7836SJohn.Forte@Sun.COM /* 430*7836SJohn.Forte@Sun.COM * copy caller's long options into internal long options table 431*7836SJohn.Forte@Sun.COM * We do this for two reasons: 432*7836SJohn.Forte@Sun.COM * 1) We need to use the getopt_long option structure internally 433*7836SJohn.Forte@Sun.COM * 2) We need to prepend the table with the standard option 434*7836SJohn.Forte@Sun.COM * for all subcommands (currently -?) 435*7836SJohn.Forte@Sun.COM */ 436*7836SJohn.Forte@Sun.COM for (optionTbl = synTable.longOptionTbl; 437*7836SJohn.Forte@Sun.COM optionTbl && optionTbl->name; optionTbl++, i++) { 438*7836SJohn.Forte@Sun.COM if (i > MAXOPTIONS - 1) { 439*7836SJohn.Forte@Sun.COM /* option table too long */ 440*7836SJohn.Forte@Sun.COM assert(0); 441*7836SJohn.Forte@Sun.COM } 442*7836SJohn.Forte@Sun.COM intLongOpt[i].name = optionTbl->name; 443*7836SJohn.Forte@Sun.COM intLongOpt[i].has_arg = optionTbl->has_arg; 444*7836SJohn.Forte@Sun.COM intLongOpt[i].flag = NULL; 445*7836SJohn.Forte@Sun.COM intLongOpt[i].val = optionTbl->val; 446*7836SJohn.Forte@Sun.COM } 447*7836SJohn.Forte@Sun.COM 448*7836SJohn.Forte@Sun.COM /* set option table global */ 449*7836SJohn.Forte@Sun.COM _longOptions = &intLongOpt[0]; 450*7836SJohn.Forte@Sun.COM 451*7836SJohn.Forte@Sun.COM 452*7836SJohn.Forte@Sun.COM /* 453*7836SJohn.Forte@Sun.COM * Check for help/version request immediately following command 454*7836SJohn.Forte@Sun.COM * '+' in option string ensures POSIX compliance in getopt_long() 455*7836SJohn.Forte@Sun.COM * which means that processing will stop at first non-option 456*7836SJohn.Forte@Sun.COM * argument. 457*7836SJohn.Forte@Sun.COM */ 458*7836SJohn.Forte@Sun.COM while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, 459*7836SJohn.Forte@Sun.COM NULL)) != EOF) { 460*7836SJohn.Forte@Sun.COM switch (opt) { 461*7836SJohn.Forte@Sun.COM case '?': 462*7836SJohn.Forte@Sun.COM /* 463*7836SJohn.Forte@Sun.COM * getopt can return a '?' when no 464*7836SJohn.Forte@Sun.COM * option letters match string. Check for 465*7836SJohn.Forte@Sun.COM * the 'real' '?' in optopt. 466*7836SJohn.Forte@Sun.COM */ 467*7836SJohn.Forte@Sun.COM if (optopt == '?') { 468*7836SJohn.Forte@Sun.COM usage(DETAIL_USAGE); 469*7836SJohn.Forte@Sun.COM exit(0); 470*7836SJohn.Forte@Sun.COM } else { 471*7836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 472*7836SJohn.Forte@Sun.COM return (1); 473*7836SJohn.Forte@Sun.COM } 474*7836SJohn.Forte@Sun.COM break; 475*7836SJohn.Forte@Sun.COM case 'V': 476*7836SJohn.Forte@Sun.COM (void) fprintf(stdout, "%s: %s %s\n", 477*7836SJohn.Forte@Sun.COM commandName, gettext("Version"), 478*7836SJohn.Forte@Sun.COM versionString); 479*7836SJohn.Forte@Sun.COM exit(0); 480*7836SJohn.Forte@Sun.COM break; 481*7836SJohn.Forte@Sun.COM default: 482*7836SJohn.Forte@Sun.COM break; 483*7836SJohn.Forte@Sun.COM } 484*7836SJohn.Forte@Sun.COM } 485*7836SJohn.Forte@Sun.COM 486*7836SJohn.Forte@Sun.COM /* 487*7836SJohn.Forte@Sun.COM * subcommand is always in the second argument. If there is no 488*7836SJohn.Forte@Sun.COM * recognized subcommand in the second argument, print error, 489*7836SJohn.Forte@Sun.COM * general usage and then return. 490*7836SJohn.Forte@Sun.COM */ 491*7836SJohn.Forte@Sun.COM if (getSubcommandProps(argv[1], &subcommand) != 0) { 492*7836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 493*7836SJohn.Forte@Sun.COM gettext("invalid subcommand")); 494*7836SJohn.Forte@Sun.COM usage(GENERAL_USAGE); 495*7836SJohn.Forte@Sun.COM return (1); 496*7836SJohn.Forte@Sun.COM } 497*7836SJohn.Forte@Sun.COM 498*7836SJohn.Forte@Sun.COM getoptargv = argv; 499*7836SJohn.Forte@Sun.COM getoptargv++; 500*7836SJohn.Forte@Sun.COM getoptargc = argc; 501*7836SJohn.Forte@Sun.COM getoptargc -= 1; 502*7836SJohn.Forte@Sun.COM 503*7836SJohn.Forte@Sun.COM (void) memset(optionStringAll, 0, sizeof (optionStringAll)); 504*7836SJohn.Forte@Sun.COM (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions)); 505*7836SJohn.Forte@Sun.COM 506*7836SJohn.Forte@Sun.COM j = 0; 507*7836SJohn.Forte@Sun.COM /* 508*7836SJohn.Forte@Sun.COM * Build optionStringAll from long options table 509*7836SJohn.Forte@Sun.COM */ 510*7836SJohn.Forte@Sun.COM for (lp = _longOptions; lp->name; lp++, j++) { 511*7836SJohn.Forte@Sun.COM /* sanity check on string length */ 512*7836SJohn.Forte@Sun.COM if (j + 1 >= sizeof (optionStringAll)) { 513*7836SJohn.Forte@Sun.COM /* option table too long */ 514*7836SJohn.Forte@Sun.COM assert(0); 515*7836SJohn.Forte@Sun.COM } 516*7836SJohn.Forte@Sun.COM optionStringAll[j] = lp->val; 517*7836SJohn.Forte@Sun.COM if (lp->has_arg == required_argument) { 518*7836SJohn.Forte@Sun.COM optionStringAll[++j] = ':'; 519*7836SJohn.Forte@Sun.COM } 520*7836SJohn.Forte@Sun.COM } 521*7836SJohn.Forte@Sun.COM 522*7836SJohn.Forte@Sun.COM i = 0; 523*7836SJohn.Forte@Sun.COM /* 524*7836SJohn.Forte@Sun.COM * Run getopt for all arguments against all possible options 525*7836SJohn.Forte@Sun.COM * Store all options/option arguments in an array for retrieval 526*7836SJohn.Forte@Sun.COM * later. 527*7836SJohn.Forte@Sun.COM * 528*7836SJohn.Forte@Sun.COM * Once all options are retrieved, a validity check against 529*7836SJohn.Forte@Sun.COM * subcommand table is performed. 530*7836SJohn.Forte@Sun.COM */ 531*7836SJohn.Forte@Sun.COM while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, 532*7836SJohn.Forte@Sun.COM _longOptions, NULL)) != EOF) { 533*7836SJohn.Forte@Sun.COM switch (opt) { 534*7836SJohn.Forte@Sun.COM case '?': 535*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 536*7836SJohn.Forte@Sun.COM exit(0); 537*7836SJohn.Forte@Sun.COM default: 538*7836SJohn.Forte@Sun.COM cmdOptions[i].optval = opt; 539*7836SJohn.Forte@Sun.COM if (optarg) { 540*7836SJohn.Forte@Sun.COM len = strlen(optarg); 541*7836SJohn.Forte@Sun.COM if (len > sizeof (cmdOptions[i].optarg) 542*7836SJohn.Forte@Sun.COM - 1) { 543*7836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", 544*7836SJohn.Forte@Sun.COM commandName, 545*7836SJohn.Forte@Sun.COM gettext("option too long")); 546*7836SJohn.Forte@Sun.COM errno = EINVAL; 547*7836SJohn.Forte@Sun.COM return (-1); 548*7836SJohn.Forte@Sun.COM } 549*7836SJohn.Forte@Sun.COM (void) strncpy(cmdOptions[i].optarg, 550*7836SJohn.Forte@Sun.COM optarg, len); 551*7836SJohn.Forte@Sun.COM } 552*7836SJohn.Forte@Sun.COM i++; 553*7836SJohn.Forte@Sun.COM break; 554*7836SJohn.Forte@Sun.COM } 555*7836SJohn.Forte@Sun.COM } 556*7836SJohn.Forte@Sun.COM 557*7836SJohn.Forte@Sun.COM /* 558*7836SJohn.Forte@Sun.COM * increment past last option 559*7836SJohn.Forte@Sun.COM */ 560*7836SJohn.Forte@Sun.COM operInd = optind + 1; 561*7836SJohn.Forte@Sun.COM 562*7836SJohn.Forte@Sun.COM /* 563*7836SJohn.Forte@Sun.COM * Check validity of given options, if any were given 564*7836SJohn.Forte@Sun.COM */ 565*7836SJohn.Forte@Sun.COM 566*7836SJohn.Forte@Sun.COM /* get option string for this subcommand */ 567*7836SJohn.Forte@Sun.COM availOptions = subcommand->optionString; 568*7836SJohn.Forte@Sun.COM 569*7836SJohn.Forte@Sun.COM /* Get count of required options */ 570*7836SJohn.Forte@Sun.COM if (subcommand->required) { 571*7836SJohn.Forte@Sun.COM requiredOptionCnt = strlen(subcommand->required); 572*7836SJohn.Forte@Sun.COM } 573*7836SJohn.Forte@Sun.COM 574*7836SJohn.Forte@Sun.COM if (cmdOptions[0].optval != 0) { /* options were input */ 575*7836SJohn.Forte@Sun.COM if (availOptions == NULL) { /* no options permitted */ 576*7836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 577*7836SJohn.Forte@Sun.COM gettext("no options permitted")); 578*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 579*7836SJohn.Forte@Sun.COM return (1); 580*7836SJohn.Forte@Sun.COM } 581*7836SJohn.Forte@Sun.COM for (i = 0; cmdOptions[i].optval; i++) { 582*7836SJohn.Forte@Sun.COM /* is the option in the available option string? */ 583*7836SJohn.Forte@Sun.COM if (!(strchr(availOptions, cmdOptions[i].optval))) { 584*7836SJohn.Forte@Sun.COM (void) printf("%s: '-%c': %s\n", commandName, 585*7836SJohn.Forte@Sun.COM cmdOptions[i].optval, 586*7836SJohn.Forte@Sun.COM gettext("invalid option")); 587*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 588*7836SJohn.Forte@Sun.COM return (1); 589*7836SJohn.Forte@Sun.COM /* increment required options entered */ 590*7836SJohn.Forte@Sun.COM } else if (subcommand->required && 591*7836SJohn.Forte@Sun.COM (strchr(subcommand->required, 592*7836SJohn.Forte@Sun.COM cmdOptions[i].optval))) { 593*7836SJohn.Forte@Sun.COM requiredOptionEntered++; 594*7836SJohn.Forte@Sun.COM /* Check for exclusive options */ 595*7836SJohn.Forte@Sun.COM } else if (cmdOptions[1].optval != 0 && 596*7836SJohn.Forte@Sun.COM subcommand->exclusive && 597*7836SJohn.Forte@Sun.COM strchr(subcommand->exclusive, 598*7836SJohn.Forte@Sun.COM cmdOptions[i].optval)) { 599*7836SJohn.Forte@Sun.COM (void) printf("%s: '-%c': %s\n", 600*7836SJohn.Forte@Sun.COM commandName, cmdOptions[i].optval, 601*7836SJohn.Forte@Sun.COM gettext("is an exclusive option")); 602*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 603*7836SJohn.Forte@Sun.COM return (1); 604*7836SJohn.Forte@Sun.COM } 605*7836SJohn.Forte@Sun.COM } 606*7836SJohn.Forte@Sun.COM } else { /* no options were input */ 607*7836SJohn.Forte@Sun.COM if (availOptions != NULL && subcommand->required) { 608*7836SJohn.Forte@Sun.COM (void) printf("%s: %s\n", commandName, 609*7836SJohn.Forte@Sun.COM gettext("at least one option required")); 610*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 611*7836SJohn.Forte@Sun.COM return (1); 612*7836SJohn.Forte@Sun.COM } 613*7836SJohn.Forte@Sun.COM } 614*7836SJohn.Forte@Sun.COM 615*7836SJohn.Forte@Sun.COM /* Were all required options entered? */ 616*7836SJohn.Forte@Sun.COM if (requiredOptionEntered != requiredOptionCnt) { 617*7836SJohn.Forte@Sun.COM (void) printf("%s: %s: %s\n", commandName, 618*7836SJohn.Forte@Sun.COM gettext("Following option(s) required"), 619*7836SJohn.Forte@Sun.COM subcommand->required); 620*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 621*7836SJohn.Forte@Sun.COM return (1); 622*7836SJohn.Forte@Sun.COM } 623*7836SJohn.Forte@Sun.COM 624*7836SJohn.Forte@Sun.COM 625*7836SJohn.Forte@Sun.COM /* 626*7836SJohn.Forte@Sun.COM * If there are no operands, 627*7836SJohn.Forte@Sun.COM * check to see if this is okay 628*7836SJohn.Forte@Sun.COM */ 629*7836SJohn.Forte@Sun.COM if ((operInd == argc) && 630*7836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_MANDATORY)) { 631*7836SJohn.Forte@Sun.COM (void) printf("%s: %s %s\n", commandName, subcommand->name, 632*7836SJohn.Forte@Sun.COM gettext("requires an operand")); 633*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 634*7836SJohn.Forte@Sun.COM return (1); 635*7836SJohn.Forte@Sun.COM } 636*7836SJohn.Forte@Sun.COM 637*7836SJohn.Forte@Sun.COM /* 638*7836SJohn.Forte@Sun.COM * If there are more operands, 639*7836SJohn.Forte@Sun.COM * check to see if this is okay 640*7836SJohn.Forte@Sun.COM */ 641*7836SJohn.Forte@Sun.COM if ((argc > operInd) && 642*7836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_NONE)) { 643*7836SJohn.Forte@Sun.COM (void) fprintf(stderr, "%s: %s %s\n", commandName, 644*7836SJohn.Forte@Sun.COM subcommand->name, gettext("takes no operands")); 645*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 646*7836SJohn.Forte@Sun.COM return (1); 647*7836SJohn.Forte@Sun.COM } 648*7836SJohn.Forte@Sun.COM 649*7836SJohn.Forte@Sun.COM /* 650*7836SJohn.Forte@Sun.COM * If there is more than one more operand, 651*7836SJohn.Forte@Sun.COM * check to see if this is okay 652*7836SJohn.Forte@Sun.COM */ 653*7836SJohn.Forte@Sun.COM if ((argc > operInd) && ((argc - operInd) != 1) && 654*7836SJohn.Forte@Sun.COM (subcommand->operand & OPERAND_SINGLE)) { 655*7836SJohn.Forte@Sun.COM (void) printf("%s: %s %s\n", commandName, 656*7836SJohn.Forte@Sun.COM subcommand->name, gettext("accepts only a single operand")); 657*7836SJohn.Forte@Sun.COM subUsage(DETAIL_USAGE, subcommand); 658*7836SJohn.Forte@Sun.COM return (1); 659*7836SJohn.Forte@Sun.COM } 660*7836SJohn.Forte@Sun.COM 661*7836SJohn.Forte@Sun.COM /* Finished syntax checks */ 662*7836SJohn.Forte@Sun.COM 663*7836SJohn.Forte@Sun.COM 664*7836SJohn.Forte@Sun.COM /* Call appropriate function */ 665*7836SJohn.Forte@Sun.COM *funcRet = subcommand->handler(argc - operInd, &argv[operInd], 666*7836SJohn.Forte@Sun.COM &cmdOptions[0], callArgs); 667*7836SJohn.Forte@Sun.COM 668*7836SJohn.Forte@Sun.COM return (0); 669*7836SJohn.Forte@Sun.COM } 670