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 * Copyright 2020 Joyent Inc. 27 */ 28 29 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <sys/types.h> 33 #include <unistd.h> 34 #include <libintl.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <assert.h> 38 #include <getopt.h> 39 #include "cmdparse.h" 40 41 /* Usage types */ 42 #define GENERAL_USAGE 1 43 #define HELP_USAGE 2 44 #define DETAIL_USAGE 3 45 46 /* printable ascii character set len */ 47 #define MAXOPTIONS (uint_t)('~' - '!' + 1) 48 49 /* 50 * MAXOPTIONSTRING is the max length of the options string used in getopt and 51 * will be the printable character set + ':' for each character, 52 * providing for options with arguments. e.g. "t:Cs:hglr:" 53 */ 54 #define MAXOPTIONSTRING MAXOPTIONS * 2 55 56 /* standard command options table to support -?, -V */ 57 struct option standardCmdOptions[] = { 58 {"help", no_argument, NULL, '?'}, 59 {"version", no_argument, NULL, 'V'}, 60 {NULL, 0, NULL, 0} 61 }; 62 63 /* standard subcommand options table to support -? */ 64 struct option standardSubCmdOptions[] = { 65 {"help", no_argument, NULL, '?'}, 66 {NULL, 0, NULL, 0} 67 }; 68 69 /* forward declarations */ 70 static int getSubcommand(char *, subcommand_t **); 71 static char *getExecBasename(char *); 72 static void usage(uint_t); 73 static void subUsage(uint_t, subcommand_t *); 74 static void subUsageObject(uint_t, subcommand_t *, object_t *); 75 static int getObject(char *, object_t **); 76 static int getObjectRules(uint_t, objectRules_t **); 77 static const char *getLongOption(int); 78 static optionProp_t *getOptions(uint_t, uint_t); 79 static char *getOptionArgDesc(int); 80 81 /* global data */ 82 static struct option *_longOptions; 83 static subcommand_t *_subcommands; 84 static object_t *_objects; 85 static objectRules_t *_objectRules; 86 static optionRules_t *_optionRules; 87 static optionTbl_t *_clientOptionTbl; 88 static char *commandName; 89 90 91 /* 92 * input: 93 * object - object value 94 * output: 95 * opCmd - pointer to opCmd_t structure allocated by caller 96 * 97 * On successful return, opCmd contains the rules for the value in 98 * object. On failure, the contents of opCmd is unspecified. 99 * 100 * Returns: 101 * zero on success 102 * non-zero on failure 103 * 104 */ 105 static int 106 getObjectRules(uint_t object, objectRules_t **objectRules) 107 { 108 objectRules_t *sp; 109 110 for (sp = _objectRules; sp->value; sp++) { 111 if (sp->value == object) { 112 *objectRules = sp; 113 return (0); 114 } 115 } 116 return (1); 117 } 118 119 /* 120 * input: 121 * arg - pointer to array of char containing object string 122 * 123 * output: 124 * object - pointer to object_t structure pointer 125 * on success, contains the matching object structure based on 126 * input object name 127 * 128 * Returns: 129 * zero on success 130 * non-zero otherwise 131 * 132 */ 133 static int 134 getObject(char *arg, object_t **object) 135 { 136 137 object_t *op; 138 int len; 139 140 for (op = _objects; op->name; op++) { 141 len = strlen(arg); 142 if (len == strlen(op->name) && 143 strncasecmp(arg, op->name, len) == 0) { 144 *object = op; 145 return (0); 146 } 147 } 148 return (1); 149 } 150 151 /* 152 * input: 153 * arg - pointer to array of char containing subcommand string 154 * output: 155 * subcommand - pointer to subcommand_t pointer 156 * on success, contains the matching subcommand structure based on 157 * input subcommand name 158 * 159 * Returns: 160 * zero on success 161 * non-zero on failure 162 */ 163 static int 164 getSubcommand(char *arg, subcommand_t **subcommand) 165 { 166 subcommand_t *sp; 167 int len; 168 169 for (sp = _subcommands; sp->name; sp++) { 170 len = strlen(arg); 171 if (len == strlen(sp->name) && 172 strncasecmp(arg, sp->name, len) == 0) { 173 *subcommand = sp; 174 return (0); 175 } 176 } 177 return (1); 178 } 179 180 /* 181 * input: 182 * object - object for which to get options 183 * subcommand - subcommand for which to get options 184 * 185 * Returns: 186 * on success, optionsProp_t pointer to structure matching input object 187 * value 188 * on failure, NULL is returned 189 */ 190 static optionProp_t * 191 getOptions(uint_t object, uint_t subcommand) 192 { 193 uint_t currObject; 194 optionRules_t *op = _optionRules; 195 while (op && ((currObject = op->objectValue) != 0)) { 196 if ((currObject == object) && 197 (op->subcommandValue == subcommand)) { 198 return (&(op->optionProp)); 199 } 200 op++; 201 } 202 return (NULL); 203 } 204 205 /* 206 * input: 207 * shortOption - short option character for which to return the 208 * associated long option string 209 * 210 * Returns: 211 * on success, long option name 212 * on failure, NULL 213 */ 214 static const char * 215 getLongOption(int shortOption) 216 { 217 struct option *op; 218 for (op = _longOptions; op->name; op++) { 219 if (shortOption == op->val) { 220 return (op->name); 221 } 222 } 223 return (NULL); 224 } 225 226 /* 227 * input 228 * shortOption - short option character for which to return the 229 * option argument 230 * Returns: 231 * on success, argument string 232 * on failure, NULL 233 */ 234 static char * 235 getOptionArgDesc(int shortOption) 236 { 237 optionTbl_t *op; 238 for (op = _clientOptionTbl; op->name; op++) { 239 if (op->val == shortOption && 240 op->has_arg == required_argument) { 241 return (op->argDesc); 242 } 243 } 244 return (NULL); 245 } 246 247 248 /* 249 * Print usage for a subcommand. 250 * 251 * input: 252 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE 253 * subcommand - pointer to subcommand_t structure 254 * 255 * Returns: 256 * none 257 * 258 */ 259 static void 260 subUsage(uint_t usageType, subcommand_t *subcommand) 261 { 262 int i; 263 object_t *objp; 264 265 266 (void) fprintf(stdout, "%s:\t%s %s [", 267 gettext("Usage"), commandName, subcommand->name); 268 269 for (i = 0; standardSubCmdOptions[i].name; i++) { 270 (void) fprintf(stdout, "-%c", 271 standardSubCmdOptions[i].val); 272 if (standardSubCmdOptions[i+1].name) 273 (void) fprintf(stdout, ","); 274 } 275 276 (void) fprintf(stdout, "] %s [", "<OBJECT>"); 277 278 for (i = 0; standardSubCmdOptions[i].name; i++) { 279 (void) fprintf(stdout, "-%c", 280 standardSubCmdOptions[i].val); 281 if (standardSubCmdOptions[i+1].name) 282 (void) fprintf(stdout, ","); 283 } 284 285 (void) fprintf(stdout, "] %s", "[<OPERAND>]"); 286 (void) fprintf(stdout, "\n"); 287 288 if (usageType == GENERAL_USAGE) { 289 return; 290 } 291 292 (void) fprintf(stdout, "%s:\n", gettext("Usage by OBJECT")); 293 294 /* 295 * iterate through object table 296 * For each object, print appropriate usage 297 * based on rules tables 298 */ 299 for (objp = _objects; objp->value; objp++) { 300 subUsageObject(usageType, subcommand, objp); 301 } 302 } 303 304 /* 305 * Print usage for a subcommand and object. 306 * 307 * input: 308 * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE 309 * subcommand - pointer to subcommand_t structure 310 * objp - pointer to a object_t structure 311 * 312 * Returns: 313 * none 314 * 315 */ 316 static void 317 subUsageObject(uint_t usageType, subcommand_t *subcommand, object_t *objp) 318 { 319 int i; 320 objectRules_t *objRules = NULL; 321 opCmd_t *opCmd = NULL; 322 optionProp_t *options; 323 char *optionArgDesc; 324 const char *longOpt; 325 326 327 if (getObjectRules(objp->value, &objRules) != 0) { 328 /* 329 * internal subcommand rules table error 330 * no object entry in object 331 */ 332 assert(0); 333 } 334 335 opCmd = &(objRules->opCmd); 336 337 if (opCmd->invOpCmd & subcommand->value) { 338 return; 339 } 340 341 options = getOptions(objp->value, subcommand->value); 342 343 /* print generic subcommand usage */ 344 (void) fprintf(stdout, "\t%s %s ", commandName, subcommand->name); 345 346 /* print object */ 347 (void) fprintf(stdout, "%s ", objp->name); 348 349 /* print options if applicable */ 350 if (options != NULL) { 351 if (options->required) { 352 (void) fprintf(stdout, "%s", gettext("<")); 353 } else { 354 (void) fprintf(stdout, "%s", gettext("[")); 355 } 356 (void) fprintf(stdout, "%s", gettext("OPTIONS")); 357 if (options->required) { 358 (void) fprintf(stdout, "%s ", gettext(">")); 359 } else { 360 (void) fprintf(stdout, "%s ", gettext("]")); 361 } 362 } 363 364 /* print operand requirements */ 365 if (opCmd->optOpCmd & subcommand->value) { 366 (void) fprintf(stdout, gettext("[")); 367 } 368 if (!(opCmd->noOpCmd & subcommand->value)) { 369 (void) fprintf(stdout, gettext("<")); 370 if (objRules->operandDefinition) { 371 (void) fprintf(stdout, "%s", 372 objRules->operandDefinition); 373 } else { 374 /* 375 * Missing operand description 376 * from table 377 */ 378 assert(0); 379 } 380 } 381 if (opCmd->multOpCmd & subcommand->value) { 382 (void) fprintf(stdout, gettext(" ...")); 383 } 384 if (!(opCmd->noOpCmd & subcommand->value)) { 385 (void) fprintf(stdout, gettext(">")); 386 } 387 if (opCmd->optOpCmd & subcommand->value) { 388 (void) fprintf(stdout, gettext("]")); 389 } 390 391 if (usageType == HELP_USAGE) { 392 (void) fprintf(stdout, "\n"); 393 return; 394 } 395 396 /* print options for subcommand, object */ 397 if (options != NULL && options->optionString != NULL) { 398 (void) fprintf(stdout, "\n\t%s:", gettext("OPTIONS")); 399 for (i = 0; i < strlen(options->optionString); i++) { 400 if ((longOpt = getLongOption( 401 options->optionString[i])) 402 == NULL) { 403 /* no long option exists for short option */ 404 assert(0); 405 } 406 (void) fprintf(stdout, "\n\t\t-%c, --%s ", 407 options->optionString[i], longOpt); 408 optionArgDesc = 409 getOptionArgDesc(options->optionString[i]); 410 if (optionArgDesc != NULL) { 411 (void) fprintf(stdout, "<%s>", optionArgDesc); 412 } 413 if (options->exclusive && 414 strchr(options->exclusive, 415 options->optionString[i])) { 416 (void) fprintf(stdout, " (%s)", 417 gettext("exclusive")); 418 } 419 } 420 } 421 (void) fprintf(stdout, "\n"); 422 } 423 424 /* 425 * input: 426 * type of usage statement to print 427 * 428 * Returns: 429 * return value of subUsage 430 */ 431 static void 432 usage(uint_t usageType) 433 { 434 int i; 435 subcommand_t subcommand; 436 subcommand_t *sp; 437 438 /* print general command usage */ 439 (void) fprintf(stdout, "%s:\t%s ", 440 gettext("Usage"), commandName); 441 442 for (i = 0; standardCmdOptions[i].name; i++) { 443 (void) fprintf(stdout, "-%c", 444 standardCmdOptions[i].val); 445 if (standardCmdOptions[i+1].name) 446 (void) fprintf(stdout, ","); 447 } 448 449 if (usageType == HELP_USAGE || usageType == GENERAL_USAGE) { 450 for (i = 0; standardSubCmdOptions[i].name; i++) { 451 (void) fprintf(stdout, ",--%s", 452 standardSubCmdOptions[i].name); 453 if (standardSubCmdOptions[i+1].name) 454 (void) fprintf(stdout, ","); 455 } 456 } 457 458 (void) fprintf(stdout, "\n"); 459 460 461 /* print all subcommand usage */ 462 for (sp = _subcommands; sp->name; sp++) { 463 subcommand.name = sp->name; 464 subcommand.value = sp->value; 465 if (usageType == HELP_USAGE) { 466 (void) fprintf(stdout, "\n"); 467 } 468 subUsage(usageType, &subcommand); 469 } 470 } 471 472 /* 473 * input: 474 * execFullName - exec name of program (argv[0]) 475 * 476 * Returns: 477 * command name portion of execFullName 478 */ 479 static char * 480 getExecBasename(char *execFullname) 481 { 482 char *lastSlash, *execBasename; 483 484 /* guard against '/' at end of command invocation */ 485 for (;;) { 486 lastSlash = strrchr(execFullname, '/'); 487 if (lastSlash == NULL) { 488 execBasename = execFullname; 489 break; 490 } else { 491 execBasename = lastSlash + 1; 492 if (*execBasename == '\0') { 493 *lastSlash = '\0'; 494 continue; 495 } 496 break; 497 } 498 } 499 return (execBasename); 500 } 501 502 /* 503 * cmdParse is a parser that checks syntax of the input command against 504 * various rules tables. 505 * 506 * It provides usage feedback based upon the passed rules tables by calling 507 * two usage functions, usage, subUsage, and subUsageObject handling command, 508 * subcommand and object usage respectively. 509 * 510 * When syntax is successfully validated, the associated function is called 511 * using the subcommands table functions. 512 * 513 * Syntax is as follows: 514 * command subcommand object [<options>] [<operand>] 515 * 516 * There are two standard short and long options assumed: 517 * -?, --help Provides usage on a command or subcommand 518 * and stops further processing of the arguments 519 * 520 * -V, --version Provides version information on the command 521 * and stops further processing of the arguments 522 * 523 * These options are loaded by this function. 524 * 525 * input: 526 * argc, argv from main 527 * syntax rules tables (synTables_t structure) 528 * callArgs - void * passed by caller to be passed to subcommand function 529 * 530 * output: 531 * funcRet - pointer to int that holds subcommand function return value 532 * 533 * Returns: 534 * 535 * zero on successful syntax parse and function call 536 * 537 * 1 on unsuccessful syntax parse (no function has been called) 538 * This could be due to a version or help call or simply a 539 * general usage call. 540 * 541 * -1 check errno, call failed 542 * 543 * This module is not MT-safe. 544 * 545 */ 546 int 547 cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, 548 int *funcRet) 549 { 550 int getoptargc; 551 char **getoptargv; 552 int opt; 553 int operInd; 554 int i, j; 555 int len; 556 char *versionString; 557 char optionStringAll[MAXOPTIONSTRING + 1]; 558 optionProp_t *availOptions; 559 objectRules_t *objRules = NULL; 560 opCmd_t *opCmd = NULL; 561 subcommand_t *subcommand; 562 object_t *object; 563 cmdOptions_t cmdOptions[MAXOPTIONS + 1]; 564 struct option *lp; 565 optionTbl_t *optionTbl; 566 struct option intLongOpt[MAXOPTIONS + 1]; 567 568 /* 569 * Check for NULLs on mandatory input arguments 570 * 571 * Note: longOptionTbl and optionRulesTbl can be NULL in the case 572 * where there is no caller defined options 573 * 574 */ 575 if (synTable.versionString == NULL || 576 synTable.subcommandTbl == NULL || 577 synTable.objectRulesTbl == NULL || 578 synTable.objectTbl == NULL || 579 funcRet == NULL) { 580 assert(0); 581 } 582 583 584 versionString = synTable.versionString; 585 586 /* set global command name */ 587 commandName = getExecBasename(argv[0]); 588 589 /* Set unbuffered output */ 590 setbuf(stdout, NULL); 591 592 /* load globals */ 593 _subcommands = synTable.subcommandTbl; 594 _objectRules = synTable.objectRulesTbl; 595 _optionRules = synTable.optionRulesTbl; 596 _objects = synTable.objectTbl; 597 _clientOptionTbl = synTable.longOptionTbl; 598 599 /* There must be at least two arguments */ 600 if (argc < 2) { 601 usage(GENERAL_USAGE); 602 return (1); 603 } 604 605 (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt)); 606 607 /* 608 * load standard subcommand options to internal long options table 609 * Two separate getopt_long(3C) tables are used. 610 */ 611 for (i = 0; standardSubCmdOptions[i].name; i++) { 612 intLongOpt[i].name = standardSubCmdOptions[i].name; 613 intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; 614 intLongOpt[i].flag = standardSubCmdOptions[i].flag; 615 intLongOpt[i].val = standardSubCmdOptions[i].val; 616 } 617 618 /* 619 * copy caller's long options into internal long options table 620 * We do this for two reasons: 621 * 1) We need to use the getopt_long option structure internally 622 * 2) We need to prepend the table with the standard option 623 * for all subcommands (currently -?) 624 */ 625 for (optionTbl = synTable.longOptionTbl; 626 optionTbl && optionTbl->name; optionTbl++, i++) { 627 if (i > MAXOPTIONS - 1) { 628 /* option table too long */ 629 assert(0); 630 } 631 intLongOpt[i].name = optionTbl->name; 632 intLongOpt[i].has_arg = optionTbl->has_arg; 633 intLongOpt[i].flag = NULL; 634 intLongOpt[i].val = optionTbl->val; 635 } 636 637 /* set option table global */ 638 _longOptions = &intLongOpt[0]; 639 640 641 /* 642 * Check for help/version request immediately following command 643 * '+' in option string ensures POSIX compliance in getopt_long() 644 * which means that processing will stop at first non-option 645 * argument. 646 */ 647 while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, 648 NULL)) != EOF) { 649 switch (opt) { 650 case '?': 651 /* 652 * getopt can return a '?' when no 653 * option letters match string. Check for 654 * the 'real' '?' in optopt. 655 */ 656 if (optopt == '?') { 657 usage(HELP_USAGE); 658 return (1); 659 } else { 660 usage(GENERAL_USAGE); 661 return (1); 662 } 663 case 'V': 664 (void) fprintf(stdout, "%s: %s %s\n", 665 commandName, gettext("Version"), 666 versionString); 667 return (1); 668 default: 669 break; 670 } 671 } 672 673 /* 674 * subcommand is always in the second argument. If there is no 675 * recognized subcommand in the second argument, print error, 676 * general usage and then return. 677 */ 678 if (getSubcommand(argv[1], &subcommand) != 0) { 679 (void) fprintf(stderr, "%s: %s\n", 680 commandName, gettext("invalid subcommand")); 681 usage(GENERAL_USAGE); 682 return (1); 683 } 684 685 if (argc == 2) { 686 (void) fprintf(stderr, "%s: %s\n", 687 commandName, gettext("missing object")); 688 subUsage(GENERAL_USAGE, subcommand); 689 return (1); 690 } 691 692 getoptargv = argv; 693 getoptargv++; 694 getoptargc = argc; 695 getoptargc -= 1; 696 697 while ((opt = getopt_long(getoptargc, getoptargv, "+?", 698 standardSubCmdOptions, NULL)) != EOF) { 699 switch (opt) { 700 case '?': 701 /* 702 * getopt can return a '?' when no 703 * option letters match string. Check for 704 * the 'real' '?' in optopt. 705 */ 706 if (optopt == '?') { 707 subUsage(HELP_USAGE, subcommand); 708 return (1); 709 } else { 710 subUsage(GENERAL_USAGE, subcommand); 711 return (1); 712 } 713 default: 714 break; 715 } 716 } 717 718 719 /* 720 * object is always in the third argument. If there is no 721 * recognized object in the third argument, print error, 722 * help usage for the subcommand and then return. 723 */ 724 if (getObject(argv[2], &object) != 0) { 725 (void) fprintf(stderr, "%s: %s\n", 726 commandName, gettext("invalid object")); 727 subUsage(HELP_USAGE, subcommand); 728 return (1); 729 } 730 731 if (getObjectRules(object->value, &objRules) != 0) { 732 /* 733 * internal subcommand rules table error 734 * no object entry in object table 735 */ 736 assert(0); 737 } 738 739 opCmd = &(objRules->opCmd); 740 741 /* 742 * Is command valid for this object? 743 */ 744 if (opCmd->invOpCmd & subcommand->value) { 745 (void) fprintf(stderr, "%s: %s %s\n", commandName, 746 gettext("invalid subcommand for"), object->name); 747 subUsage(HELP_USAGE, subcommand); 748 return (1); 749 } 750 751 /* 752 * offset getopt arg begin since 753 * getopt(3C) assumes options 754 * follow first argument 755 */ 756 getoptargv = argv; 757 getoptargv++; 758 getoptargv++; 759 getoptargc = argc; 760 getoptargc -= 2; 761 762 (void) memset(optionStringAll, 0, sizeof (optionStringAll)); 763 (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions)); 764 765 j = 0; 766 /* 767 * Build optionStringAll from long options table 768 */ 769 for (lp = _longOptions; lp->name; lp++, j++) { 770 /* sanity check on string length */ 771 if (j + 1 >= sizeof (optionStringAll)) { 772 /* option table too long */ 773 assert(0); 774 } 775 optionStringAll[j] = lp->val; 776 if (lp->has_arg == required_argument) { 777 optionStringAll[++j] = ':'; 778 } 779 } 780 781 i = 0; 782 /* 783 * Run getopt for all arguments against all possible options 784 * Store all options/option arguments in an array for retrieval 785 * later. 786 * Once all options are retrieved, check against object 787 * and subcommand (option rules table) for validity. 788 * This is done later. 789 */ 790 while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, 791 _longOptions, NULL)) != EOF) { 792 switch (opt) { 793 case '?': 794 if (optopt == '?') { 795 subUsageObject(DETAIL_USAGE, 796 subcommand, object); 797 return (1); 798 } else { 799 subUsage(GENERAL_USAGE, subcommand); 800 return (1); 801 } 802 default: 803 cmdOptions[i].optval = opt; 804 if (optarg) { 805 len = strlen(optarg); 806 if (len > sizeof (cmdOptions[i].optarg) 807 - 1) { 808 (void) fprintf(stderr, 809 "%s: %s\n", commandName, 810 gettext("option too long")); 811 errno = EINVAL; 812 return (-1); 813 } 814 (void) strncpy(cmdOptions[i].optarg, 815 optarg, len); 816 } 817 i++; 818 break; 819 } 820 } 821 822 /* 823 * increment past last option 824 */ 825 operInd = optind + 2; 826 827 /* 828 * Check validity of given options, if any were given 829 */ 830 831 /* get option string for this object and subcommand */ 832 availOptions = getOptions(object->value, subcommand->value); 833 834 if (cmdOptions[0].optval != 0) { /* options were input */ 835 if (availOptions == NULL) { /* no options permitted */ 836 (void) fprintf(stderr, "%s: %s\n", 837 commandName, gettext("no options permitted")); 838 subUsageObject(HELP_USAGE, subcommand, object); 839 return (1); 840 } 841 for (i = 0; cmdOptions[i].optval; i++) { 842 /* Check for invalid options */ 843 if (availOptions->optionString == NULL) { 844 /* 845 * internal option table error 846 * There must be an option string if 847 * there is an entry in the table 848 */ 849 assert(0); 850 } 851 /* is the option in the available option string? */ 852 853 if (!(strchr(availOptions->optionString, 854 cmdOptions[i].optval))) { 855 (void) fprintf(stderr, 856 "%s: '-%c': %s\n", 857 commandName, cmdOptions[i].optval, 858 gettext("invalid option")); 859 subUsageObject(DETAIL_USAGE, subcommand, 860 object); 861 return (1); 862 863 /* Check for exclusive options */ 864 } else if (cmdOptions[1].optval != 0 && 865 availOptions->exclusive && 866 strchr(availOptions->exclusive, 867 cmdOptions[i].optval)) { 868 (void) fprintf(stderr, 869 "%s: '-%c': %s\n", 870 commandName, cmdOptions[i].optval, 871 gettext("is an exclusive option")); 872 subUsageObject(DETAIL_USAGE, subcommand, 873 object); 874 return (1); 875 } 876 } 877 } else { /* no options were input */ 878 if (availOptions != NULL && 879 (availOptions->required)) { 880 (void) fprintf(stderr, "%s: %s\n", 881 commandName, 882 gettext("at least one option required")); 883 subUsageObject(DETAIL_USAGE, subcommand, 884 object); 885 return (1); 886 } 887 } 888 889 /* 890 * If there are no more arguments (operands), 891 * check to see if this is okay 892 */ 893 if ((operInd == argc) && 894 (opCmd->reqOpCmd & subcommand->value)) { 895 (void) fprintf(stderr, "%s: %s %s %s\n", 896 commandName, subcommand->name, 897 object->name, gettext("requires an operand")); 898 subUsageObject(HELP_USAGE, subcommand, object); 899 return (1); 900 } 901 902 /* 903 * If there are more operands, 904 * check to see if this is okay 905 */ 906 if ((argc > operInd) && 907 (opCmd->noOpCmd & subcommand->value)) { 908 (void) fprintf(stderr, "%s: %s %s %s\n", 909 commandName, subcommand->name, 910 object->name, gettext("takes no operands")); 911 subUsageObject(HELP_USAGE, subcommand, object); 912 return (1); 913 } 914 915 /* 916 * If there is more than one more operand, 917 * check to see if this is okay 918 */ 919 if ((argc > operInd) && ((argc - operInd) != 1) && 920 !(opCmd->multOpCmd & subcommand->value)) { 921 (void) fprintf(stderr, "%s: %s %s %s\n", 922 commandName, subcommand->name, object->name, 923 gettext("accepts only a single operand")); 924 subUsageObject(HELP_USAGE, subcommand, object); 925 return (1); 926 } 927 928 /* Finished syntax checks */ 929 930 931 /* Call appropriate function */ 932 *funcRet = subcommand->handler(argc - operInd, &argv[operInd], 933 object->value, &cmdOptions[0], callArgs); 934 935 return (0); 936 } 937