1 2 /* 3 * \file usage.c 4 * 5 * This module implements the default usage procedure for 6 * Automated Options. It may be overridden, of course. 7 * 8 * @addtogroup autoopts 9 * @{ 10 */ 11 /* 12 * Sort options: 13 --start=END-[S]TATIC-FORWARD --patt='^/\*($|[^:])' \ 14 --out=xx.c key='^[a-zA-Z0-9_]+\(' --trail='^/\*:' \ 15 --spac=2 --input=usage.c 16 */ 17 18 /* 19 * This file is part of AutoOpts, a companion to AutoGen. 20 * AutoOpts is free software. 21 * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 22 * 23 * AutoOpts is available under any one of two licenses. The license 24 * in use must be one of these two and the choice is under the control 25 * of the user of the license. 26 * 27 * The GNU Lesser General Public License, version 3 or later 28 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 29 * 30 * The Modified Berkeley Software Distribution License 31 * See the file "COPYING.mbsd" 32 * 33 * These files have the following sha256 sums: 34 * 35 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 36 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 37 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 38 */ 39 40 /* = = = START-STATIC-FORWARD = = = */ 41 static unsigned int 42 parse_usage_flags(ao_flag_names_t const * fnt, char const * txt); 43 44 static inline bool 45 do_gnu_usage(tOptions * pOpts); 46 47 static inline bool 48 skip_misuse_usage(tOptions * pOpts); 49 50 static void 51 print_offer_usage(tOptions * opts); 52 53 static void 54 print_usage_details(tOptions * opts, int exit_code); 55 56 static void 57 print_one_paragraph(char const * text, bool plain, FILE * fp); 58 59 static void 60 prt_conflicts(tOptions * opts, tOptDesc * od); 61 62 static void 63 prt_one_vendor(tOptions * opts, tOptDesc * od, 64 arg_types_t * argtp, char const * usefmt); 65 66 static void 67 prt_vendor_opts(tOptions * opts, char const * title); 68 69 static void 70 prt_extd_usage(tOptions * opts, tOptDesc * od, char const * title); 71 72 static void 73 prt_ini_list(char const * const * papz, char const * ini_file, 74 char const * path_nm); 75 76 static void 77 prt_preamble(tOptions * opts, tOptDesc * od, arg_types_t * at); 78 79 static void 80 prt_one_usage(tOptions * opts, tOptDesc * od, arg_types_t * at); 81 82 static void 83 prt_opt_usage(tOptions * opts, int ex_code, char const * title); 84 85 static void 86 prt_prog_detail(tOptions * opts); 87 88 static int 89 setGnuOptFmts(tOptions * opts, char const ** ptxt); 90 91 static int 92 setStdOptFmts(tOptions * opts, char const ** ptxt); 93 /* = = = END-STATIC-FORWARD = = = */ 94 95 /** 96 * Parse the option usage flags string. Any parsing problems yield 97 * a zero (no flags set) result. This function is internal to 98 * set_usage_flags(). 99 * 100 * @param[in] fnt Flag Name Table - maps a name to a mask 101 * @param[in] txt the text to process. If NULL, then 102 * getenv("AUTOOPTS_USAGE") is used. 103 * @returns a bit mask indicating which \a fnt entries were found. 104 */ 105 static unsigned int 106 parse_usage_flags(ao_flag_names_t const * fnt, char const * txt) 107 { 108 unsigned int res = 0; 109 110 /* 111 * The text may be passed in. If not, use the environment variable. 112 */ 113 if (txt == NULL) { 114 txt = getenv("AUTOOPTS_USAGE"); 115 if (txt == NULL) 116 return 0; 117 } 118 119 txt = SPN_WHITESPACE_CHARS(txt); 120 if (*txt == NUL) 121 return 0; 122 123 /* 124 * search the string for table entries. We must understand everything 125 * we see in the string, or we give up on it. 126 */ 127 for (;;) { 128 int ix = 0; 129 130 for (;;) { 131 if (strneqvcmp(txt, fnt[ix].fnm_name, (int)fnt[ix].fnm_len) == 0) 132 break; 133 if (++ix >= AOUF_COUNT) 134 return 0; 135 } 136 137 /* 138 * Make sure we have a full match. Look for whitespace, 139 * a comma, or a NUL byte. 140 */ 141 if (! IS_END_LIST_ENTRY_CHAR(txt[fnt[ix].fnm_len])) 142 return 0; 143 144 res |= 1U << ix; 145 txt = SPN_WHITESPACE_CHARS(txt + fnt[ix].fnm_len); 146 147 switch (*txt) { 148 case NUL: 149 return res; 150 151 case ',': 152 txt = SPN_WHITESPACE_CHARS(txt + 1); 153 /* Something must follow the comma */ 154 155 default: 156 continue; 157 } 158 } 159 } 160 161 /** 162 * Set option usage flags. Any parsing problems yield no changes to options. 163 * Three different bits may be fiddled: \a OPTPROC_GNUUSAGE, \a OPTPROC_MISUSE 164 * and \a OPTPROC_COMPUTE. 165 * 166 * @param[in] flg_txt text to parse. If NULL, then the AUTOOPTS_USAGE 167 * environment variable is parsed. 168 * @param[in,out] opts the program option descriptor 169 */ 170 LOCAL void 171 set_usage_flags(tOptions * opts, char const * flg_txt) 172 { 173 # define _aof_(_n, _f) { sizeof(#_n)-1, _f, #_n }, 174 static ao_flag_names_t const fn_table[AOUF_COUNT] = { 175 AOFLAG_TABLE 176 }; 177 # undef _aof_ 178 179 /* 180 * the flag word holds a bit for each selected table entry. 181 */ 182 unsigned int flg = parse_usage_flags(fn_table, flg_txt); 183 if (flg == 0) return; 184 185 /* 186 * Ensure we do not have conflicting selections 187 */ 188 { 189 static unsigned int const form_mask = 190 AOUF_gnu | AOUF_autoopts; 191 static unsigned int const misuse_mask = 192 AOUF_no_misuse_usage | AOUF_misuse_usage; 193 if ( ((flg & form_mask) == form_mask) 194 || ((flg & misuse_mask) == misuse_mask) ) 195 return; 196 } 197 198 /* 199 * Now fiddle the fOptSet bits, based on settings. 200 * The OPTPROC_LONGOPT bit is immutable, thus if it is set, 201 * then fnm points to a mask off mask. 202 */ 203 { 204 ao_flag_names_t const * fnm = fn_table; 205 for (;;) { 206 if ((flg & 1) != 0) { 207 if ((fnm->fnm_mask & OPTPROC_LONGOPT) != 0) 208 opts->fOptSet &= fnm->fnm_mask; 209 else opts->fOptSet |= fnm->fnm_mask; 210 } 211 flg >>= 1; 212 if (flg == 0) 213 break; 214 fnm++; 215 } 216 } 217 } 218 219 /* 220 * Figure out if we should try to format usage text sort-of like 221 * the way many GNU programs do. 222 */ 223 static inline bool 224 do_gnu_usage(tOptions * pOpts) 225 { 226 return (pOpts->fOptSet & OPTPROC_GNUUSAGE) ? true : false; 227 } 228 229 /* 230 * Figure out if we should try to format usage text sort-of like 231 * the way many GNU programs do. 232 */ 233 static inline bool 234 skip_misuse_usage(tOptions * pOpts) 235 { 236 return (pOpts->fOptSet & OPTPROC_MISUSE) ? true : false; 237 } 238 239 240 /*=export_func optionOnlyUsage 241 * 242 * what: Print usage text for just the options 243 * arg: + tOptions * + pOpts + program options descriptor + 244 * arg: + int + ex_code + exit code for calling exit(3) + 245 * 246 * doc: 247 * This routine will print only the usage for each option. 248 * This function may be used when the emitted usage must incorporate 249 * information not available to AutoOpts. 250 =*/ 251 void 252 optionOnlyUsage(tOptions * pOpts, int ex_code) 253 { 254 char const * pOptTitle = NULL; 255 256 set_usage_flags(pOpts, NULL); 257 if ((ex_code != EXIT_SUCCESS) && 258 skip_misuse_usage(pOpts)) 259 return; 260 261 /* 262 * Determine which header and which option formatting strings to use 263 */ 264 if (do_gnu_usage(pOpts)) 265 (void)setGnuOptFmts(pOpts, &pOptTitle); 266 else 267 (void)setStdOptFmts(pOpts, &pOptTitle); 268 269 prt_opt_usage(pOpts, ex_code, pOptTitle); 270 271 fflush(option_usage_fp); 272 if (ferror(option_usage_fp) != 0) 273 fserr_exit(pOpts->pzProgName, zwriting, (option_usage_fp == stderr) 274 ? zstderr_name : zstdout_name); 275 } 276 277 /** 278 * Print a message suggesting how to get help. 279 * 280 * @param[in] opts the program options 281 */ 282 static void 283 print_offer_usage(tOptions * opts) 284 { 285 char help[24]; 286 287 if (HAS_opt_usage_t(opts)) { 288 int ix = opts->presetOptCt; 289 tOptDesc * od = opts->pOptDesc + ix; 290 while (od->optUsage != AOUSE_HELP) { 291 if (++ix >= opts->optCt) 292 ao_bug(zmissing_help_msg); 293 od++; 294 } 295 switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) { 296 case OPTPROC_SHORTOPT: 297 help[0] = '-'; 298 help[1] = od->optValue; 299 help[2] = NUL; 300 break; 301 302 case OPTPROC_LONGOPT: 303 case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT): 304 help[0] = help[1] = '-'; 305 strncpy(help + 2, od->pz_Name, 20); 306 break; 307 308 case 0: 309 strncpy(help, od->pz_Name, 20); 310 break; 311 } 312 313 } else { 314 switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) { 315 case OPTPROC_SHORTOPT: 316 strcpy(help, "-h"); 317 break; 318 319 case OPTPROC_LONGOPT: 320 case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT): 321 strcpy(help, "--help"); 322 break; 323 324 case 0: 325 strcpy(help, "help"); 326 break; 327 } 328 } 329 330 fprintf(option_usage_fp, zoffer_usage_fmt, opts->pzProgName, help); 331 } 332 333 /** 334 * Print information about each option. 335 * 336 * @param[in] opts the program options 337 * @param[in] exit_code whether or not there was a usage error reported. 338 * used to select full usage versus abbreviated. 339 */ 340 static void 341 print_usage_details(tOptions * opts, int exit_code) 342 { 343 { 344 char const * pOptTitle = NULL; 345 int flen; 346 347 /* 348 * Determine which header and which option formatting strings to use 349 */ 350 if (do_gnu_usage(opts)) { 351 flen = setGnuOptFmts(opts, &pOptTitle); 352 sprintf(line_fmt_buf, zFmtFmt, flen); 353 fputc(NL, option_usage_fp); 354 } 355 else { 356 flen = setStdOptFmts(opts, &pOptTitle); 357 sprintf(line_fmt_buf, zFmtFmt, flen); 358 359 /* 360 * When we exit with EXIT_SUCCESS and the first option is a doc 361 * option, we do *NOT* want to emit the column headers. 362 * Otherwise, we do. 363 */ 364 if ( (exit_code != EXIT_SUCCESS) 365 || ((opts->pOptDesc->fOptState & OPTST_DOCUMENT) == 0) ) 366 367 fputs(pOptTitle, option_usage_fp); 368 } 369 370 flen = 4 - ((flen + 15) / 8); 371 if (flen > 0) 372 tab_skip_ct = flen; 373 prt_opt_usage(opts, exit_code, pOptTitle); 374 } 375 376 /* 377 * Describe the mechanics of denoting the options 378 */ 379 switch (opts->fOptSet & OPTPROC_L_N_S) { 380 case OPTPROC_L_N_S: fputs(zFlagOkay, option_usage_fp); break; 381 case OPTPROC_SHORTOPT: break; 382 case OPTPROC_LONGOPT: fputs(zNoFlags, option_usage_fp); break; 383 case 0: fputs(zOptsOnly, option_usage_fp); break; 384 } 385 386 if ((opts->fOptSet & OPTPROC_NUM_OPT) != 0) 387 fputs(zNumberOpt, option_usage_fp); 388 389 if ((opts->fOptSet & OPTPROC_REORDER) != 0) 390 fputs(zReorder, option_usage_fp); 391 392 if (opts->pzExplain != NULL) 393 fputs(opts->pzExplain, option_usage_fp); 394 395 /* 396 * IF the user is asking for help (thus exiting with SUCCESS), 397 * THEN see what additional information we can provide. 398 */ 399 if (exit_code == EXIT_SUCCESS) 400 prt_prog_detail(opts); 401 402 /* 403 * Give bug notification preference to the packager information 404 */ 405 if (HAS_pzPkgDataDir(opts) && (opts->pzPackager != NULL)) 406 fputs(opts->pzPackager, option_usage_fp); 407 408 else if (opts->pzBugAddr != NULL) 409 fprintf(option_usage_fp, zPlsSendBugs, opts->pzBugAddr); 410 411 fflush(option_usage_fp); 412 413 if (ferror(option_usage_fp) != 0) 414 fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stderr) 415 ? zstderr_name : zstdout_name); 416 } 417 418 static void 419 print_one_paragraph(char const * text, bool plain, FILE * fp) 420 { 421 if (plain) { 422 #ifdef ENABLE_NLS 423 #ifdef HAVE_LIBINTL_H 424 #ifdef DEBUG_ENABLED 425 #undef gettext 426 #endif 427 char * buf = dgettext("libopts", text); 428 if (buf == text) 429 text = gettext(text); 430 #endif /* HAVE_LIBINTL_H */ 431 #endif /* ENABLE_NLS */ 432 fputs(text, fp); 433 } 434 435 else { 436 char const * t = optionQuoteString(text, LINE_SPLICE); 437 fprintf(fp, PUTS_FMT, t); 438 AGFREE(t); 439 } 440 } 441 442 /*=export_func optionPrintParagraphs 443 * private: 444 * 445 * what: Print a paragraph of usage text 446 * arg: + char const * + text + a block of text that has bee i18n-ed + 447 * arg: + bool + plain + false -> wrap text in fputs() + 448 * arg: + FILE * + fp + the stream file pointer for output + 449 * 450 * doc: 451 * This procedure is called in two contexts: when a full or short usage text 452 * has been provided for display, and when autogen is assembling a list of 453 * translatable texts in the optmain.tlib template. In the former case, \a 454 * plain is set to \a true, otherwise \a false. 455 * 456 * Anything less than 256 characters in size is printed as a single unit. 457 * Otherwise, paragraphs are detected. A paragraph break is defined as just 458 * before a non-empty line preceded by two newlines or a line that starts 459 * with at least one space character but fewer than 8 space characters. 460 * Lines indented with tabs or more than 7 spaces are considered continuation 461 * lines. 462 * 463 * If 'plain' is true, we are emitting text for a user to see. So, if it is 464 * true and NLS is not enabled, then just write the whole thing at once. 465 =*/ 466 void 467 optionPrintParagraphs(char const * text, bool plain, FILE * fp) 468 { 469 size_t len = strlen(text); 470 char * buf; 471 #ifndef ENABLE_NLS 472 if (plain || (len < 256)) 473 #else 474 if (len < 256) 475 #endif 476 { 477 print_one_paragraph(text, plain, fp); 478 return; 479 } 480 481 AGDUPSTR(buf, text, "ppara"); 482 text = buf; 483 484 for (;;) { 485 char * scan; 486 487 if (len < 256) { 488 done: 489 print_one_paragraph(buf, plain, fp); 490 break; 491 } 492 scan = buf; 493 494 try_longer: 495 scan = strchr(scan, NL); 496 if (scan == NULL) 497 goto done; 498 499 if ((scan - buf) < 40) { 500 scan++; 501 goto try_longer; 502 } 503 504 scan++; 505 if ((! isspace((int)*scan)) || (*scan == HT)) 506 /* 507 * line starts with tab or non-whitespace --> continuation 508 */ 509 goto try_longer; 510 511 if (*scan == NL) { 512 /* 513 * Double newline -> paragraph break 514 * Include all newlines in current paragraph. 515 */ 516 while (*++scan == NL) /*continue*/; 517 518 } else { 519 char * p = scan; 520 int sp_ct = 0; 521 522 while (*p == ' ') { 523 if (++sp_ct >= 8) { 524 /* 525 * Too many spaces --> continuation line 526 */ 527 scan = p; 528 goto try_longer; 529 } 530 p++; 531 } 532 } 533 534 /* 535 * "scan" points to the first character of a paragraph or the 536 * terminating NUL byte. 537 */ 538 { 539 char svch = *scan; 540 *scan = NUL; 541 print_one_paragraph(buf, plain, fp); 542 len -= scan - buf; 543 if (len <= 0) 544 break; 545 *scan = svch; 546 buf = scan; 547 } 548 } 549 AGFREE(text); 550 } 551 552 /*=export_func optionUsage 553 * private: 554 * 555 * what: Print usage text 556 * arg: + tOptions * + opts + program options descriptor + 557 * arg: + int + exitCode + exit code for calling exit(3) + 558 * 559 * doc: 560 * This routine will print usage in both GNU-standard and AutoOpts-expanded 561 * formats. The descriptor specifies the default, but AUTOOPTS_USAGE will 562 * over-ride this, providing the value of it is set to either "gnu" or 563 * "autoopts". This routine will @strong{not} return. 564 * 565 * If "exitCode" is "AO_EXIT_REQ_USAGE" (normally 64), then output will to 566 * to stdout and the actual exit code will be "EXIT_SUCCESS". 567 =*/ 568 void 569 optionUsage(tOptions * opts, int usage_exit_code) 570 { 571 int exit_code = (usage_exit_code == AO_EXIT_REQ_USAGE) 572 ? EXIT_SUCCESS : usage_exit_code; 573 574 displayEnum = false; 575 set_usage_flags(opts, NULL); 576 577 /* 578 * Paged usage will preset option_usage_fp to an output file. 579 * If it hasn't already been set, then set it to standard output 580 * on successful exit (help was requested), otherwise error out. 581 * 582 * Test the version before obtaining pzFullUsage or pzShortUsage. 583 * These fields do not exist before revision 30. 584 */ 585 { 586 char const * pz; 587 588 if (exit_code == EXIT_SUCCESS) { 589 pz = (opts->structVersion >= 30 * 4096) 590 ? opts->pzFullUsage : NULL; 591 592 if (option_usage_fp == NULL) 593 option_usage_fp = print_exit ? stderr : stdout; 594 595 } else { 596 pz = (opts->structVersion >= 30 * 4096) 597 ? opts->pzShortUsage : NULL; 598 599 if (option_usage_fp == NULL) 600 option_usage_fp = stderr; 601 } 602 603 if (((opts->fOptSet & OPTPROC_COMPUTE) == 0) && (pz != NULL)) { 604 if ((opts->fOptSet & OPTPROC_TRANSLATE) != 0) 605 optionPrintParagraphs(pz, true, option_usage_fp); 606 else 607 fputs(pz, option_usage_fp); 608 goto flush_and_exit; 609 } 610 } 611 612 fprintf(option_usage_fp, opts->pzUsageTitle, opts->pzProgName); 613 614 if ((exit_code == EXIT_SUCCESS) || 615 (! skip_misuse_usage(opts))) 616 617 print_usage_details(opts, usage_exit_code); 618 else 619 print_offer_usage(opts); 620 621 flush_and_exit: 622 fflush(option_usage_fp); 623 if (ferror(option_usage_fp) != 0) 624 fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stdout) 625 ? zstdout_name : zstderr_name); 626 627 option_exits(exit_code); 628 } 629 630 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 631 * PER OPTION TYPE USAGE INFORMATION 632 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 633 /** 634 * print option conflicts. 635 * 636 * @param opts the program option descriptor 637 * @param od the option descriptor 638 */ 639 static void 640 prt_conflicts(tOptions * opts, tOptDesc * od) 641 { 642 const int * opt_no; 643 fputs(zTabHyp + tab_skip_ct, option_usage_fp); 644 645 /* 646 * REQUIRED: 647 */ 648 if (od->pOptMust != NULL) { 649 opt_no = od->pOptMust; 650 651 if (opt_no[1] == NO_EQUIVALENT) { 652 fprintf(option_usage_fp, zReqOne, 653 opts->pOptDesc[*opt_no].pz_Name); 654 } else { 655 fputs(zReqThese, option_usage_fp); 656 for (;;) { 657 fprintf(option_usage_fp, zTabout + tab_skip_ct, 658 opts->pOptDesc[*opt_no].pz_Name); 659 if (*++opt_no == NO_EQUIVALENT) 660 break; 661 } 662 } 663 664 if (od->pOptCant != NULL) 665 fputs(zTabHypAnd + tab_skip_ct, option_usage_fp); 666 } 667 668 /* 669 * CONFLICTS: 670 */ 671 if (od->pOptCant == NULL) 672 return; 673 674 opt_no = od->pOptCant; 675 676 if (opt_no[1] == NO_EQUIVALENT) { 677 fprintf(option_usage_fp, zProhibOne, 678 opts->pOptDesc[*opt_no].pz_Name); 679 return; 680 } 681 682 fputs(zProhib, option_usage_fp); 683 for (;;) { 684 fprintf(option_usage_fp, zTabout + tab_skip_ct, 685 opts->pOptDesc[*opt_no].pz_Name); 686 if (*++opt_no == NO_EQUIVALENT) 687 break; 688 } 689 } 690 691 /** 692 * Print the usage information for a single vendor option. 693 * 694 * @param[in] opts the program option descriptor 695 * @param[in] od the option descriptor 696 * @param[in] argtp names of the option argument types 697 * @param[in] usefmt format for primary usage line 698 */ 699 static void 700 prt_one_vendor(tOptions * opts, tOptDesc * od, 701 arg_types_t * argtp, char const * usefmt) 702 { 703 prt_preamble(opts, od, argtp); 704 705 { 706 char z[ 80 ]; 707 char const * pzArgType; 708 709 /* 710 * Determine the argument type string first on its usage, then, 711 * when the option argument is required, base the type string on the 712 * argument type. 713 */ 714 if (od->fOptState & OPTST_ARG_OPTIONAL) { 715 pzArgType = argtp->pzOpt; 716 717 } else switch (OPTST_GET_ARGTYPE(od->fOptState)) { 718 case OPARG_TYPE_NONE: pzArgType = argtp->pzNo; break; 719 case OPARG_TYPE_ENUMERATION: pzArgType = argtp->pzKey; break; 720 case OPARG_TYPE_FILE: pzArgType = argtp->pzFile; break; 721 case OPARG_TYPE_MEMBERSHIP: pzArgType = argtp->pzKeyL; break; 722 case OPARG_TYPE_BOOLEAN: pzArgType = argtp->pzBool; break; 723 case OPARG_TYPE_NUMERIC: pzArgType = argtp->pzNum; break; 724 case OPARG_TYPE_HIERARCHY: pzArgType = argtp->pzNest; break; 725 case OPARG_TYPE_STRING: pzArgType = argtp->pzStr; break; 726 case OPARG_TYPE_TIME: pzArgType = argtp->pzTime; break; 727 default: goto bogus_desc; 728 } 729 730 pzArgType = SPN_WHITESPACE_CHARS(pzArgType); 731 if (*pzArgType == NUL) 732 snprintf(z, sizeof(z), "%s", od->pz_Name); 733 else 734 snprintf(z, sizeof(z), "%s=%s", od->pz_Name, pzArgType); 735 fprintf(option_usage_fp, usefmt, z, od->pzText); 736 737 switch (OPTST_GET_ARGTYPE(od->fOptState)) { 738 case OPARG_TYPE_ENUMERATION: 739 case OPARG_TYPE_MEMBERSHIP: 740 displayEnum = (od->pOptProc != NULL) ? true : displayEnum; 741 } 742 } 743 744 return; 745 746 bogus_desc: 747 fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name); 748 ao_bug(zbad_arg_type_msg); 749 } 750 751 /** 752 * Print the long options processed with "-W". These options will be the 753 * ones that do *not* have flag characters. 754 * 755 * @param opts the program option descriptor 756 * @param title the title for the options 757 */ 758 static void 759 prt_vendor_opts(tOptions * opts, char const * title) 760 { 761 static unsigned int const not_vended_mask = 762 OPTST_NO_USAGE_MASK | OPTST_DOCUMENT; 763 764 static char const vfmtfmt[] = "%%-%us %%s\n"; 765 char vfmt[sizeof(vfmtfmt)]; 766 767 /* 768 * Only handle client specified options. The "vendor option" follows 769 * "presetOptCt", so we won't loop/recurse indefinitely. 770 */ 771 int ct = opts->presetOptCt; 772 tOptDesc * od = opts->pOptDesc; 773 fprintf(option_usage_fp, zTabout + tab_skip_ct, zVendOptsAre); 774 775 { 776 size_t nmlen = 0; 777 do { 778 size_t l; 779 if ( ((od->fOptState & not_vended_mask) != 0) 780 || IS_GRAPHIC_CHAR(od->optValue)) 781 continue; 782 783 l = strlen(od->pz_Name); 784 if (l > nmlen) nmlen = l; 785 } while (od++, (--ct > 0)); 786 787 snprintf(vfmt, sizeof(vfmt), vfmtfmt, (unsigned int)nmlen + 4); 788 } 789 790 if (tab_skip_ct > 0) 791 tab_skip_ct--; 792 793 ct = opts->presetOptCt; 794 od = opts->pOptDesc; 795 796 do { 797 if ( ((od->fOptState & not_vended_mask) != 0) 798 || IS_GRAPHIC_CHAR(od->optValue)) 799 continue; 800 801 prt_one_vendor(opts, od, &argTypes, vfmt); 802 prt_extd_usage(opts, od, title); 803 804 } while (od++, (--ct > 0)); 805 806 /* no need to restore "tab_skip_ct" - options are done now */ 807 } 808 809 /** 810 * Print extended usage. Usage/help was requested. 811 * 812 * @param opts the program option descriptor 813 * @param od the option descriptor 814 * @param title the title for the options 815 */ 816 static void 817 prt_extd_usage(tOptions * opts, tOptDesc * od, char const * title) 818 { 819 if ( ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0) 820 && (od->optActualValue == VENDOR_OPTION_VALUE)) { 821 prt_vendor_opts(opts, title); 822 return; 823 } 824 825 /* 826 * IF there are option conflicts or dependencies, 827 * THEN print them here. 828 */ 829 if ((od->pOptMust != NULL) || (od->pOptCant != NULL)) 830 prt_conflicts(opts, od); 831 832 /* 833 * IF there is a disablement string 834 * THEN print the disablement info 835 */ 836 if (od->pz_DisableName != NULL ) 837 fprintf(option_usage_fp, zDis + tab_skip_ct, od->pz_DisableName); 838 839 /* 840 * Check for argument types that have callbacks with magical properties 841 */ 842 switch (OPTST_GET_ARGTYPE(od->fOptState)) { 843 case OPARG_TYPE_NUMERIC: 844 /* 845 * IF the numeric option has a special callback, 846 * THEN call it, requesting the range or other special info 847 */ 848 if ( (od->pOptProc != NULL) 849 && (od->pOptProc != optionNumericVal) ) { 850 (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od); 851 } 852 break; 853 854 case OPARG_TYPE_FILE: 855 (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od); 856 break; 857 } 858 859 /* 860 * IF the option defaults to being enabled, 861 * THEN print that out 862 */ 863 if (od->fOptState & OPTST_INITENABLED) 864 fputs(zEnab + tab_skip_ct, option_usage_fp); 865 866 /* 867 * IF the option is in an equivalence class 868 * AND not the designated lead 869 * THEN print equivalence and leave it at that. 870 */ 871 if ( (od->optEquivIndex != NO_EQUIVALENT) 872 && (od->optEquivIndex != od->optActualIndex ) ) { 873 fprintf(option_usage_fp, zalt_opt + tab_skip_ct, 874 opts->pOptDesc[ od->optEquivIndex ].pz_Name); 875 return; 876 } 877 878 /* 879 * IF this particular option can NOT be preset 880 * AND some form of presetting IS allowed, 881 * AND it is not an auto-managed option (e.g. --help, et al.) 882 * THEN advise that this option may not be preset. 883 */ 884 if ( ((od->fOptState & OPTST_NO_INIT) != 0) 885 && ( (opts->papzHomeList != NULL) 886 || (opts->pzPROGNAME != NULL) 887 ) 888 && (od->optIndex < opts->presetOptCt) 889 ) 890 891 fputs(zNoPreset + tab_skip_ct, option_usage_fp); 892 893 /* 894 * Print the appearance requirements. 895 */ 896 if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_MEMBERSHIP) 897 fputs(zMembers + tab_skip_ct, option_usage_fp); 898 899 else switch (od->optMinCt) { 900 case 1: 901 case 0: 902 switch (od->optMaxCt) { 903 case 0: fputs(zPreset + tab_skip_ct, option_usage_fp); break; 904 case NOLIMIT: fputs(zNoLim + tab_skip_ct, option_usage_fp); break; 905 case 1: break; 906 /* 907 * IF the max is more than one but limited, print "UP TO" message 908 */ 909 default: 910 fprintf(option_usage_fp, zUpTo + tab_skip_ct, od->optMaxCt); break; 911 } 912 break; 913 914 default: 915 /* 916 * More than one is required. Print the range. 917 */ 918 fprintf(option_usage_fp, zMust + tab_skip_ct, 919 od->optMinCt, od->optMaxCt); 920 } 921 922 if ( NAMED_OPTS(opts) 923 && (opts->specOptIdx.default_opt == od->optIndex)) 924 fputs(zDefaultOpt + tab_skip_ct, option_usage_fp); 925 } 926 927 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 928 /** 929 * Figure out where all the initialization files might live. This requires 930 * translating some environment variables and testing to see if a name is a 931 * directory or a file. It's squishy, but important to tell users how to 932 * find these files. 933 * 934 * @param[in] papz search path 935 * @param[out] ini_file an output buffer of AG_PATH_MAX+1 bytes 936 * @param[in] path_nm the name of the file we're hunting for 937 */ 938 static void 939 prt_ini_list(char const * const * papz, char const * ini_file, 940 char const * path_nm) 941 { 942 char pth_buf[AG_PATH_MAX+1]; 943 944 fputs(zPresetIntro, option_usage_fp); 945 946 for (;;) { 947 char const * path = *(papz++); 948 char const * nm_buf = pth_buf; 949 950 if (path == NULL) 951 break; 952 953 /* 954 * Ignore any invalid paths 955 */ 956 if (! optionMakePath(pth_buf, (int)sizeof(pth_buf), path, path_nm)) 957 nm_buf = path; 958 959 /* 960 * Expand paths that are relative to the executable or installation 961 * directories. Leave alone paths that use environment variables. 962 */ 963 else if ((*path == '$') 964 && ((path[1] == '$') || (path[1] == '@'))) 965 path = nm_buf; 966 967 /* 968 * Print the name of the "homerc" file. If the "rcfile" name is 969 * not empty, we may or may not print that, too... 970 */ 971 fprintf(option_usage_fp, zPathFmt, path); 972 if (*ini_file != NUL) { 973 struct stat sb; 974 975 /* 976 * IF the "homerc" file is a directory, 977 * then append the "rcfile" name. 978 */ 979 if ((stat(nm_buf, &sb) == 0) && S_ISDIR(sb.st_mode)) { 980 fputc(DIRCH, option_usage_fp); 981 fputs(ini_file, option_usage_fp); 982 } 983 } 984 985 fputc(NL, option_usage_fp); 986 } 987 } 988 989 /** 990 * Print the usage line preamble text 991 * 992 * @param opts the program option descriptor 993 * @param od the option descriptor 994 * @param at names of the option argument types 995 */ 996 static void 997 prt_preamble(tOptions * opts, tOptDesc * od, arg_types_t * at) 998 { 999 /* 1000 * Flag prefix: IF no flags at all, then omit it. If not printable 1001 * (not allowed for this option), then blank, else print it. 1002 * Follow it with a comma if we are doing GNU usage and long 1003 * opts are to be printed too. 1004 */ 1005 if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0) 1006 fputs(at->pzSpc, option_usage_fp); 1007 1008 else if (! IS_GRAPHIC_CHAR(od->optValue)) { 1009 if ( (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT)) 1010 == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT)) 1011 fputc(' ', option_usage_fp); 1012 fputs(at->pzNoF, option_usage_fp); 1013 1014 } else { 1015 fprintf(option_usage_fp, " -%c", od->optValue); 1016 if ( (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT)) 1017 == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT)) 1018 fputs(", ", option_usage_fp); 1019 } 1020 } 1021 1022 /** 1023 * Print the usage information for a single option. 1024 * 1025 * @param opts the program option descriptor 1026 * @param od the option descriptor 1027 * @param at names of the option argument types 1028 */ 1029 static void 1030 prt_one_usage(tOptions * opts, tOptDesc * od, arg_types_t * at) 1031 { 1032 prt_preamble(opts, od, at); 1033 1034 { 1035 char z[80]; 1036 char const * atyp; 1037 1038 /* 1039 * Determine the argument type string first on its usage, then, 1040 * when the option argument is required, base the type string on the 1041 * argument type. 1042 */ 1043 if (od->fOptState & OPTST_ARG_OPTIONAL) { 1044 atyp = at->pzOpt; 1045 1046 } else switch (OPTST_GET_ARGTYPE(od->fOptState)) { 1047 case OPARG_TYPE_NONE: atyp = at->pzNo; break; 1048 case OPARG_TYPE_ENUMERATION: atyp = at->pzKey; break; 1049 case OPARG_TYPE_FILE: atyp = at->pzFile; break; 1050 case OPARG_TYPE_MEMBERSHIP: atyp = at->pzKeyL; break; 1051 case OPARG_TYPE_BOOLEAN: atyp = at->pzBool; break; 1052 case OPARG_TYPE_NUMERIC: atyp = at->pzNum; break; 1053 case OPARG_TYPE_HIERARCHY: atyp = at->pzNest; break; 1054 case OPARG_TYPE_STRING: atyp = at->pzStr; break; 1055 case OPARG_TYPE_TIME: atyp = at->pzTime; break; 1056 default: goto bogus_desc; 1057 } 1058 1059 #ifdef _WIN32 1060 if (at->pzOptFmt == zGnuOptFmt) 1061 snprintf(z, sizeof(z), "--%s%s", od->pz_Name, atyp); 1062 else if (at->pzOptFmt == zGnuOptFmt + 2) 1063 snprintf(z, sizeof(z), "%s%s", od->pz_Name, atyp); 1064 else 1065 #endif 1066 snprintf(z, sizeof(z), at->pzOptFmt, atyp, od->pz_Name, 1067 (od->optMinCt != 0) ? at->pzReq : at->pzOpt); 1068 1069 fprintf(option_usage_fp, line_fmt_buf, z, od->pzText); 1070 1071 switch (OPTST_GET_ARGTYPE(od->fOptState)) { 1072 case OPARG_TYPE_ENUMERATION: 1073 case OPARG_TYPE_MEMBERSHIP: 1074 displayEnum = (od->pOptProc != NULL) ? true : displayEnum; 1075 } 1076 } 1077 1078 return; 1079 1080 bogus_desc: 1081 fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name); 1082 option_exits(EX_SOFTWARE); 1083 } 1084 1085 /** 1086 * Print out the usage information for just the options. 1087 */ 1088 static void 1089 prt_opt_usage(tOptions * opts, int ex_code, char const * title) 1090 { 1091 int ct = opts->optCt; 1092 int optNo = 0; 1093 tOptDesc * od = opts->pOptDesc; 1094 int docCt = 0; 1095 1096 do { 1097 /* 1098 * no usage --> disallowed on command line (OPTST_NO_COMMAND), or 1099 * deprecated -- strongly discouraged (OPTST_DEPRECATED), or 1100 * compiled out of current object code (OPTST_OMITTED) 1101 */ 1102 if ((od->fOptState & OPTST_NO_USAGE_MASK) != 0) { 1103 1104 /* 1105 * IF this is a compiled-out option 1106 * *AND* usage was requested with "omitted-usage" 1107 * *AND* this is NOT abbreviated usage 1108 * THEN display this option. 1109 */ 1110 if ( (od->fOptState == (OPTST_OMITTED | OPTST_NO_INIT)) 1111 && (od->pz_Name != NULL) 1112 && (ex_code == EXIT_SUCCESS)) { 1113 1114 char const * why_pz = 1115 (od->pzText == NULL) ? zDisabledWhy : od->pzText; 1116 prt_preamble(opts, od, &argTypes); 1117 fprintf(option_usage_fp, zDisabledOpt, od->pz_Name, why_pz); 1118 } 1119 1120 continue; 1121 } 1122 1123 if ((od->fOptState & OPTST_DOCUMENT) != 0) { 1124 if (ex_code == EXIT_SUCCESS) { 1125 fprintf(option_usage_fp, argTypes.pzBrk, od->pzText, 1126 title); 1127 docCt++; 1128 } 1129 1130 continue; 1131 } 1132 1133 /* Skip name only options when we have a vendor option */ 1134 if ( ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0) 1135 && (! IS_GRAPHIC_CHAR(od->optValue))) 1136 continue; 1137 1138 /* 1139 * IF this is the first auto-opt maintained option 1140 * *AND* we are doing a full help 1141 * *AND* there are documentation options 1142 * *AND* the last one was not a doc option, 1143 * THEN document that the remaining options are not user opts 1144 */ 1145 if ((docCt > 0) && (ex_code == EXIT_SUCCESS)) { 1146 if (opts->presetOptCt == optNo) { 1147 if ((od[-1].fOptState & OPTST_DOCUMENT) == 0) 1148 fprintf(option_usage_fp, argTypes.pzBrk, zAuto, title); 1149 1150 } else if ((ct == 1) && 1151 (opts->fOptSet & OPTPROC_VENDOR_OPT)) 1152 fprintf(option_usage_fp, argTypes.pzBrk, zVendIntro, title); 1153 } 1154 1155 prt_one_usage(opts, od, &argTypes); 1156 1157 /* 1158 * IF we were invoked because of the --help option, 1159 * THEN print all the extra info 1160 */ 1161 if (ex_code == EXIT_SUCCESS) 1162 prt_extd_usage(opts, od, title); 1163 1164 } while (od++, optNo++, (--ct > 0)); 1165 1166 fputc(NL, option_usage_fp); 1167 } 1168 1169 1170 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1171 /** 1172 * Print program details. 1173 * @param[in] opts the program option descriptor 1174 */ 1175 static void 1176 prt_prog_detail(tOptions * opts) 1177 { 1178 bool need_intro = (opts->papzHomeList == NULL); 1179 1180 /* 1181 * Display all the places we look for config files, if we have 1182 * a list of directories to search. 1183 */ 1184 if (! need_intro) 1185 prt_ini_list(opts->papzHomeList, opts->pzRcName, opts->pzProgPath); 1186 1187 /* 1188 * Let the user know about environment variable settings 1189 */ 1190 if ((opts->fOptSet & OPTPROC_ENVIRON) != 0) { 1191 if (need_intro) 1192 fputs(zPresetIntro, option_usage_fp); 1193 1194 fprintf(option_usage_fp, zExamineFmt, opts->pzPROGNAME); 1195 } 1196 1197 /* 1198 * IF we found an enumeration, 1199 * THEN hunt for it again. Call the handler proc with a NULL 1200 * option struct pointer. That tells it to display the keywords. 1201 */ 1202 if (displayEnum) { 1203 int ct = opts->optCt; 1204 int optNo = 0; 1205 tOptDesc * od = opts->pOptDesc; 1206 1207 fputc(NL, option_usage_fp); 1208 fflush(option_usage_fp); 1209 do { 1210 switch (OPTST_GET_ARGTYPE(od->fOptState)) { 1211 case OPARG_TYPE_ENUMERATION: 1212 case OPARG_TYPE_MEMBERSHIP: 1213 (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od); 1214 } 1215 } while (od++, optNo++, (--ct > 0)); 1216 } 1217 1218 /* 1219 * If there is a detail string, now is the time for that. 1220 */ 1221 if (opts->pzDetail != NULL) 1222 fputs(opts->pzDetail, option_usage_fp); 1223 } 1224 1225 1226 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1227 * 1228 * OPTION LINE FORMATTING SETUP 1229 * 1230 * The "OptFmt" formats receive three arguments: 1231 * 1. the type of the option's argument 1232 * 2. the long name of the option 1233 * 3. "YES" or "no ", depending on whether or not the option must appear 1234 * on the command line. 1235 * These formats are used immediately after the option flag (if used) has 1236 * been printed. 1237 * 1238 * Set up the formatting for GNU-style output 1239 */ 1240 static int 1241 setGnuOptFmts(tOptions * opts, char const ** ptxt) 1242 { 1243 static char const zOneSpace[] = " "; 1244 int flen = 22; 1245 *ptxt = zNoRq_ShrtTtl; 1246 1247 argTypes.pzStr = zGnuStrArg; 1248 argTypes.pzReq = zOneSpace; 1249 argTypes.pzNum = zGnuNumArg; 1250 argTypes.pzKey = zGnuKeyArg; 1251 argTypes.pzKeyL = zGnuKeyLArg; 1252 argTypes.pzTime = zGnuTimeArg; 1253 argTypes.pzFile = zGnuFileArg; 1254 argTypes.pzBool = zGnuBoolArg; 1255 argTypes.pzNest = zGnuNestArg; 1256 argTypes.pzOpt = zGnuOptArg; 1257 argTypes.pzNo = zOneSpace; 1258 argTypes.pzBrk = zGnuBreak; 1259 argTypes.pzNoF = zSixSpaces; 1260 argTypes.pzSpc = zThreeSpaces; 1261 1262 switch (opts->fOptSet & OPTPROC_L_N_S) { 1263 case OPTPROC_L_N_S: argTypes.pzOptFmt = zGnuOptFmt; break; 1264 case OPTPROC_LONGOPT: argTypes.pzOptFmt = zGnuOptFmt; break; 1265 case 0: argTypes.pzOptFmt = zGnuOptFmt + 2; break; 1266 case OPTPROC_SHORTOPT: 1267 argTypes.pzOptFmt = zShrtGnuOptFmt; 1268 zGnuStrArg[0] = zGnuNumArg[0] = zGnuKeyArg[0] = zGnuBoolArg[0] = ' '; 1269 argTypes.pzOpt = " [arg]"; 1270 flen = 8; 1271 break; 1272 } 1273 1274 return flen; 1275 } 1276 1277 1278 /* 1279 * Standard (AutoOpts normal) option line formatting 1280 */ 1281 static int 1282 setStdOptFmts(tOptions * opts, char const ** ptxt) 1283 { 1284 int flen = 0; 1285 1286 argTypes.pzStr = zStdStrArg; 1287 argTypes.pzReq = zStdReqArg; 1288 argTypes.pzNum = zStdNumArg; 1289 argTypes.pzKey = zStdKeyArg; 1290 argTypes.pzKeyL = zStdKeyLArg; 1291 argTypes.pzTime = zStdTimeArg; 1292 argTypes.pzFile = zStdFileArg; 1293 argTypes.pzBool = zStdBoolArg; 1294 argTypes.pzNest = zStdNestArg; 1295 argTypes.pzOpt = zStdOptArg; 1296 argTypes.pzNo = zStdNoArg; 1297 argTypes.pzBrk = zStdBreak; 1298 argTypes.pzNoF = zFiveSpaces; 1299 argTypes.pzSpc = zTwoSpaces; 1300 1301 switch (opts->fOptSet & (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT)) { 1302 case (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT): 1303 *ptxt = zNoRq_ShrtTtl; 1304 argTypes.pzOptFmt = zNrmOptFmt; 1305 flen = 19; 1306 break; 1307 1308 case OPTPROC_NO_REQ_OPT: 1309 *ptxt = zNoRq_NoShrtTtl; 1310 argTypes.pzOptFmt = zNrmOptFmt; 1311 flen = 19; 1312 break; 1313 1314 case OPTPROC_SHORTOPT: 1315 *ptxt = zReq_ShrtTtl; 1316 argTypes.pzOptFmt = zReqOptFmt; 1317 flen = 24; 1318 break; 1319 1320 case 0: 1321 *ptxt = zReq_NoShrtTtl; 1322 argTypes.pzOptFmt = zReqOptFmt; 1323 flen = 24; 1324 } 1325 1326 return flen; 1327 } 1328 1329 /** @} 1330 * 1331 * Local Variables: 1332 * mode: C 1333 * c-file-style: "stroustrup" 1334 * indent-tabs-mode: nil 1335 * End: 1336 * end of autoopts/usage.c */ 1337