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