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