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 2009 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 if (subcommand->helpText) { 254 (void) printf("%s\n", subcommand->helpText); 255 } 256 } 257 258 /* 259 * input: 260 * type of usage statement to print 261 * 262 * Returns: 263 * return value of subUsage 264 */ 265 static void 266 usage(uint_t usageType) 267 { 268 int i; 269 subCommandProps_t *sp; 270 271 /* print general command usage */ 272 (void) printf("%s:\t%s ", gettext("Usage"), commandName); 273 274 for (i = 0; standardCmdOptions[i].name; i++) { 275 (void) printf("-%c", standardCmdOptions[i].val); 276 if (standardCmdOptions[i+1].name) 277 (void) printf(","); 278 } 279 280 if (usageType == GENERAL_USAGE) { 281 for (i = 0; standardSubCmdOptions[i].name; i++) { 282 (void) printf(",--%s", standardSubCmdOptions[i].name); 283 if (standardSubCmdOptions[i+1].name) 284 (void) printf(","); 285 } 286 } 287 288 (void) fprintf(stdout, "\n"); 289 290 291 /* print all subcommand usage */ 292 for (sp = _subCommandProps; sp->name; sp++) { 293 subUsage(usageType, sp); 294 } 295 } 296 297 /* 298 * input: 299 * execFullName - exec name of program (argv[0]) 300 * 301 * Returns: 302 * command name portion of execFullName 303 */ 304 static char * 305 getExecBasename(char *execFullname) 306 { 307 char *lastSlash, *execBasename; 308 309 /* guard against '/' at end of command invocation */ 310 for (;;) { 311 lastSlash = strrchr(execFullname, '/'); 312 if (lastSlash == NULL) { 313 execBasename = execFullname; 314 break; 315 } else { 316 execBasename = lastSlash + 1; 317 if (*execBasename == '\0') { 318 *lastSlash = '\0'; 319 continue; 320 } 321 break; 322 } 323 } 324 return (execBasename); 325 } 326 327 /* 328 * cmdParse is a parser that checks syntax of the input command against 329 * various rules tables. 330 * 331 * It provides usage feedback based upon the passed rules tables by calling 332 * two usage functions, usage, subUsage 333 * 334 * When syntax is successfully validated, the associated function is called 335 * using the subcommands table functions. 336 * 337 * Syntax is as follows: 338 * command subcommand [<options>] [<operand>] 339 * 340 * There are two standard short and long options assumed: 341 * -?, --help Provides usage on a command or subcommand 342 * and stops further processing of the arguments 343 * 344 * -V, --version Provides version information on the command 345 * and stops further processing of the arguments 346 * 347 * These options are loaded by this function. 348 * 349 * input: 350 * argc, argv from main 351 * syntax rules tables (synTables_t structure) 352 * callArgs - void * passed by caller to be passed to subcommand function 353 * 354 * output: 355 * funcRet - pointer to int that holds subcommand function return value 356 * 357 * Returns: 358 * 359 * zero on successful syntax parse and function call 360 * 361 * 1 on unsuccessful syntax parse (no function has been called) 362 * This could be due to a version or help call or simply a 363 * general usage call. 364 * 365 * -1 check errno, call failed 366 * 367 * This module is not MT-safe. 368 * 369 */ 370 int 371 cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, 372 int *funcRet) 373 { 374 int getoptargc; 375 char **getoptargv; 376 int opt; 377 int operInd; 378 int i, j; 379 int len; 380 int requiredOptionCnt = 0, requiredOptionEntered = 0; 381 char *availOptions; 382 char *versionString; 383 char optionStringAll[MAXOPTIONSTRING + 1]; 384 subCommandProps_t *subcommand; 385 cmdOptions_t cmdOptions[MAXOPTIONS + 1]; 386 optionTbl_t *optionTbl; 387 struct option *lp; 388 struct option intLongOpt[MAXOPTIONS + 1]; 389 390 /* 391 * Check for NULLs on mandatory input arguments 392 * 393 * Note: longOptionTbl can be NULL in the case 394 * where there is no caller defined options 395 * 396 */ 397 assert(synTable.versionString); 398 assert(synTable.subCommandPropsTbl); 399 assert(funcRet); 400 401 versionString = synTable.versionString; 402 403 /* set global command name */ 404 commandName = getExecBasename(argv[0]); 405 406 /* Set unbuffered output */ 407 setbuf(stdout, NULL); 408 409 /* load globals */ 410 _subCommandProps = synTable.subCommandPropsTbl; 411 _clientOptionTbl = synTable.longOptionTbl; 412 413 /* There must be at least two arguments */ 414 if (argc < 2) { 415 usage(GENERAL_USAGE); 416 return (1); 417 } 418 419 (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt)); 420 421 /* 422 * load standard subcommand options to internal long options table 423 * Two separate getopt_long(3C) tables are used. 424 */ 425 for (i = 0; standardSubCmdOptions[i].name; i++) { 426 intLongOpt[i].name = standardSubCmdOptions[i].name; 427 intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; 428 intLongOpt[i].flag = standardSubCmdOptions[i].flag; 429 intLongOpt[i].val = standardSubCmdOptions[i].val; 430 } 431 432 /* 433 * copy caller's long options into internal long options table 434 * We do this for two reasons: 435 * 1) We need to use the getopt_long option structure internally 436 * 2) We need to prepend the table with the standard option 437 * for all subcommands (currently -?) 438 */ 439 for (optionTbl = synTable.longOptionTbl; 440 optionTbl && optionTbl->name; optionTbl++, i++) { 441 if (i > MAXOPTIONS - 1) { 442 /* option table too long */ 443 assert(0); 444 } 445 intLongOpt[i].name = optionTbl->name; 446 intLongOpt[i].has_arg = optionTbl->has_arg; 447 intLongOpt[i].flag = NULL; 448 intLongOpt[i].val = optionTbl->val; 449 } 450 451 /* set option table global */ 452 _longOptions = &intLongOpt[0]; 453 454 455 /* 456 * Check for help/version request immediately following command 457 * '+' in option string ensures POSIX compliance in getopt_long() 458 * which means that processing will stop at first non-option 459 * argument. 460 */ 461 while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, 462 NULL)) != EOF) { 463 switch (opt) { 464 case '?': 465 /* 466 * getopt can return a '?' when no 467 * option letters match string. Check for 468 * the 'real' '?' in optopt. 469 */ 470 if (optopt == '?') { 471 usage(DETAIL_USAGE); 472 exit(0); 473 } else { 474 usage(GENERAL_USAGE); 475 return (1); 476 } 477 break; 478 case 'V': 479 (void) fprintf(stdout, "%s: %s %s\n", 480 commandName, gettext("Version"), 481 versionString); 482 exit(0); 483 break; 484 default: 485 break; 486 } 487 } 488 489 /* 490 * subcommand is always in the second argument. If there is no 491 * recognized subcommand in the second argument, print error, 492 * general usage and then return. 493 */ 494 if (getSubcommandProps(argv[1], &subcommand) != 0) { 495 (void) printf("%s: %s\n", commandName, 496 gettext("invalid subcommand")); 497 usage(GENERAL_USAGE); 498 return (1); 499 } 500 501 getoptargv = argv; 502 getoptargv++; 503 getoptargc = argc; 504 getoptargc -= 1; 505 506 (void) memset(optionStringAll, 0, sizeof (optionStringAll)); 507 (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions)); 508 509 j = 0; 510 /* 511 * Build optionStringAll from long options table 512 */ 513 for (lp = _longOptions; lp->name; lp++, j++) { 514 /* sanity check on string length */ 515 if (j + 1 >= sizeof (optionStringAll)) { 516 /* option table too long */ 517 assert(0); 518 } 519 optionStringAll[j] = lp->val; 520 if (lp->has_arg == required_argument) { 521 optionStringAll[++j] = ':'; 522 } 523 } 524 525 i = 0; 526 /* 527 * Run getopt for all arguments against all possible options 528 * Store all options/option arguments in an array for retrieval 529 * later. 530 * 531 * Once all options are retrieved, a validity check against 532 * subcommand table is performed. 533 */ 534 while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, 535 _longOptions, NULL)) != EOF) { 536 switch (opt) { 537 case '?': 538 subUsage(DETAIL_USAGE, subcommand); 539 /* 540 * getopt can return a '?' when no 541 * option letters match string. Check for 542 * the 'real' '?' in optopt. 543 */ 544 if (optopt == '?') { 545 exit(0); 546 } else { 547 exit(1); 548 } 549 default: 550 cmdOptions[i].optval = opt; 551 if (optarg) { 552 len = strlen(optarg); 553 if (len > sizeof (cmdOptions[i].optarg) 554 - 1) { 555 (void) printf("%s: %s\n", 556 commandName, 557 gettext("option too long")); 558 errno = EINVAL; 559 return (-1); 560 } 561 (void) strncpy(cmdOptions[i].optarg, 562 optarg, len); 563 } 564 i++; 565 break; 566 } 567 } 568 569 /* 570 * increment past last option 571 */ 572 operInd = optind + 1; 573 574 /* 575 * Check validity of given options, if any were given 576 */ 577 578 /* get option string for this subcommand */ 579 availOptions = subcommand->optionString; 580 581 /* Get count of required options */ 582 if (subcommand->required) { 583 requiredOptionCnt = strlen(subcommand->required); 584 } 585 586 if (cmdOptions[0].optval != 0) { /* options were input */ 587 if (availOptions == NULL) { /* no options permitted */ 588 (void) printf("%s: %s\n", commandName, 589 gettext("no options permitted")); 590 subUsage(DETAIL_USAGE, subcommand); 591 return (1); 592 } 593 for (i = 0; cmdOptions[i].optval; i++) { 594 /* is the option in the available option string? */ 595 if (!(strchr(availOptions, cmdOptions[i].optval))) { 596 (void) printf("%s: '-%c': %s\n", commandName, 597 cmdOptions[i].optval, 598 gettext("invalid option")); 599 subUsage(DETAIL_USAGE, subcommand); 600 return (1); 601 /* increment required options entered */ 602 } else if (subcommand->required && 603 (strchr(subcommand->required, 604 cmdOptions[i].optval))) { 605 requiredOptionEntered++; 606 /* Check for exclusive options */ 607 } else if (cmdOptions[1].optval != 0 && 608 subcommand->exclusive && 609 strchr(subcommand->exclusive, 610 cmdOptions[i].optval)) { 611 (void) printf("%s: '-%c': %s\n", 612 commandName, cmdOptions[i].optval, 613 gettext("is an exclusive option")); 614 subUsage(DETAIL_USAGE, subcommand); 615 return (1); 616 } 617 } 618 } else { /* no options were input */ 619 if (availOptions != NULL && subcommand->required) { 620 (void) printf("%s: %s\n", commandName, 621 gettext("at least one option required")); 622 subUsage(DETAIL_USAGE, subcommand); 623 return (1); 624 } 625 } 626 627 /* Were all required options entered? */ 628 if (requiredOptionEntered != requiredOptionCnt) { 629 (void) printf("%s: %s: %s\n", commandName, 630 gettext("Following option(s) required"), 631 subcommand->required); 632 subUsage(DETAIL_USAGE, subcommand); 633 return (1); 634 } 635 636 637 /* 638 * If there are no operands, 639 * check to see if this is okay 640 */ 641 if ((operInd == argc) && 642 (subcommand->operand & OPERAND_MANDATORY)) { 643 (void) printf("%s: %s %s\n", commandName, subcommand->name, 644 gettext("requires an operand")); 645 subUsage(DETAIL_USAGE, subcommand); 646 return (1); 647 } 648 649 /* 650 * If there are more operands, 651 * check to see if this is okay 652 */ 653 if ((argc > operInd) && 654 (subcommand->operand & OPERAND_NONE)) { 655 (void) fprintf(stderr, "%s: %s %s\n", commandName, 656 subcommand->name, gettext("takes no operands")); 657 subUsage(DETAIL_USAGE, subcommand); 658 return (1); 659 } 660 661 /* 662 * If there is more than one more operand, 663 * check to see if this is okay 664 */ 665 if ((argc > operInd) && ((argc - operInd) != 1) && 666 (subcommand->operand & OPERAND_SINGLE)) { 667 (void) printf("%s: %s %s\n", commandName, 668 subcommand->name, gettext("accepts only a single operand")); 669 subUsage(DETAIL_USAGE, subcommand); 670 return (1); 671 } 672 673 /* Finished syntax checks */ 674 675 676 /* Call appropriate function */ 677 *funcRet = subcommand->handler(argc - operInd, &argv[operInd], 678 &cmdOptions[0], callArgs); 679 680 return (0); 681 }