Home | History | Annotate | Line # | Download | only in libopts
      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