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