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