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