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