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