1181834Sroberto
2181834Sroberto/*
3290001Sglebius * \file usage.c
4181834Sroberto *
5181834Sroberto *  This module implements the default usage procedure for
6181834Sroberto *  Automated Options.  It may be overridden, of course.
7181834Sroberto *
8290001Sglebius * @addtogroup autoopts
9290001Sglebius * @{
10290001Sglebius */
11290001Sglebius/*
12181834Sroberto *  Sort options:
13181834Sroberto    --start=END-[S]TATIC-FORWARD --patt='^/\*($|[^:])' \
14181834Sroberto    --out=xx.c key='^[a-zA-Z0-9_]+\(' --trail='^/\*:' \
15181834Sroberto    --spac=2 --input=usage.c
16181834Sroberto */
17181834Sroberto
18181834Sroberto/*
19290001Sglebius *  This file is part of AutoOpts, a companion to AutoGen.
20290001Sglebius *  AutoOpts is free software.
21290001Sglebius *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
22181834Sroberto *
23290001Sglebius *  AutoOpts is available under any one of two licenses.  The license
24290001Sglebius *  in use must be one of these two and the choice is under the control
25290001Sglebius *  of the user of the license.
26181834Sroberto *
27290001Sglebius *   The GNU Lesser General Public License, version 3 or later
28290001Sglebius *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
29181834Sroberto *
30290001Sglebius *   The Modified Berkeley Software Distribution License
31290001Sglebius *      See the file "COPYING.mbsd"
32181834Sroberto *
33290001Sglebius *  These files have the following sha256 sums:
34181834Sroberto *
35290001Sglebius *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
36290001Sglebius *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
37290001Sglebius *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
38181834Sroberto */
39181834Sroberto
40290001Sglebius/* = = = START-STATIC-FORWARD = = = */
41290001Sglebiusstatic unsigned int
42290001Sglebiusparse_usage_flags(ao_flag_names_t const * fnt, char const * txt);
43181834Sroberto
44290001Sglebiusstatic inline bool
45290001Sglebiusdo_gnu_usage(tOptions * pOpts);
46181834Sroberto
47290001Sglebiusstatic inline bool
48290001Sglebiusskip_misuse_usage(tOptions * pOpts);
49181834Sroberto
50290001Sglebiusstatic void
51290001Sglebiusprint_offer_usage(tOptions * opts);
52181834Sroberto
53181834Srobertostatic void
54290001Sglebiusprint_usage_details(tOptions * opts, int exit_code);
55181834Sroberto
56181834Srobertostatic void
57290001Sglebiusprint_one_paragraph(char const * text, bool plain, FILE * fp);
58181834Sroberto
59181834Srobertostatic void
60290001Sglebiusprt_conflicts(tOptions * opts, tOptDesc * od);
61181834Sroberto
62181834Srobertostatic void
63290001Sglebiusprt_one_vendor(tOptions *    opts,  tOptDesc *   od,
64290001Sglebius               arg_types_t * argtp, char const * usefmt);
65181834Sroberto
66181834Srobertostatic void
67290001Sglebiusprt_vendor_opts(tOptions * opts, char const * title);
68181834Sroberto
69290001Sglebiusstatic void
70290001Sglebiusprt_extd_usage(tOptions * opts, tOptDesc * od, char const * title);
71290001Sglebius
72290001Sglebiusstatic void
73290001Sglebiusprt_ini_list(char const * const * papz, char const * ini_file,
74290001Sglebius             char const * path_nm);
75290001Sglebius
76290001Sglebiusstatic void
77290001Sglebiusprt_preamble(tOptions * opts, tOptDesc * od, arg_types_t * at);
78290001Sglebius
79290001Sglebiusstatic void
80290001Sglebiusprt_one_usage(tOptions * opts, tOptDesc * od, arg_types_t * at);
81290001Sglebius
82290001Sglebiusstatic void
83290001Sglebiusprt_opt_usage(tOptions * opts, int ex_code, char const * title);
84290001Sglebius
85290001Sglebiusstatic void
86290001Sglebiusprt_prog_detail(tOptions * opts);
87290001Sglebius
88181834Srobertostatic int
89290001SglebiussetGnuOptFmts(tOptions * opts, char const ** ptxt);
90181834Sroberto
91181834Srobertostatic int
92290001SglebiussetStdOptFmts(tOptions * opts, char const ** ptxt);
93181834Sroberto/* = = = END-STATIC-FORWARD = = = */
94181834Sroberto
95290001Sglebius/**
96290001Sglebius * Parse the option usage flags string.  Any parsing problems yield
97290001Sglebius * a zero (no flags set) result.  This function is internal to
98290001Sglebius * set_usage_flags().
99290001Sglebius *
100290001Sglebius * @param[in] fnt   Flag Name Table - maps a name to a mask
101290001Sglebius * @param[in] txt   the text to process.  If NULL, then
102290001Sglebius *                  getenv("AUTOOPTS_USAGE") is used.
103290001Sglebius * @returns a bit mask indicating which \a fnt entries were found.
104290001Sglebius */
105290001Sglebiusstatic unsigned int
106290001Sglebiusparse_usage_flags(ao_flag_names_t const * fnt, char const * txt)
107290001Sglebius{
108290001Sglebius    unsigned int res = 0;
109181834Sroberto
110290001Sglebius    /*
111290001Sglebius     * The text may be passed in.  If not, use the environment variable.
112290001Sglebius     */
113290001Sglebius    if (txt == NULL) {
114290001Sglebius        txt = getenv("AUTOOPTS_USAGE");
115290001Sglebius        if (txt == NULL)
116290001Sglebius            return 0;
117290001Sglebius    }
118290001Sglebius
119290001Sglebius    txt = SPN_WHITESPACE_CHARS(txt);
120290001Sglebius    if (*txt == NUL)
121290001Sglebius        return 0;
122290001Sglebius
123290001Sglebius    /*
124290001Sglebius     * search the string for table entries.  We must understand everything
125290001Sglebius     * we see in the string, or we give up on it.
126290001Sglebius     */
127290001Sglebius    for (;;) {
128290001Sglebius        int ix = 0;
129290001Sglebius
130290001Sglebius        for (;;) {
131290001Sglebius            if (strneqvcmp(txt, fnt[ix].fnm_name, (int)fnt[ix].fnm_len) == 0)
132290001Sglebius                break;
133290001Sglebius            if (++ix >= AOUF_COUNT)
134290001Sglebius                return 0;
135290001Sglebius        }
136290001Sglebius
137290001Sglebius        /*
138290001Sglebius         *  Make sure we have a full match.  Look for whitespace,
139290001Sglebius         *  a comma, or a NUL byte.
140290001Sglebius         */
141290001Sglebius        if (! IS_END_LIST_ENTRY_CHAR(txt[fnt[ix].fnm_len]))
142290001Sglebius            return 0;
143290001Sglebius
144290001Sglebius        res |= 1U << ix;
145290001Sglebius        txt = SPN_WHITESPACE_CHARS(txt + fnt[ix].fnm_len);
146290001Sglebius
147290001Sglebius        switch (*txt) {
148290001Sglebius        case NUL:
149290001Sglebius            return res;
150290001Sglebius
151290001Sglebius        case ',':
152290001Sglebius            txt = SPN_WHITESPACE_CHARS(txt + 1);
153290001Sglebius            /* Something must follow the comma */
154290001Sglebius
155290001Sglebius        default:
156290001Sglebius            continue;
157290001Sglebius        }
158290001Sglebius    }
159290001Sglebius}
160290001Sglebius
161290001Sglebius/**
162290001Sglebius * Set option usage flags.  Any parsing problems yield no changes to options.
163290001Sglebius * Three different bits may be fiddled: \a OPTPROC_GNUUSAGE, \a OPTPROC_MISUSE
164290001Sglebius * and \a OPTPROC_COMPUTE.
165290001Sglebius *
166290001Sglebius * @param[in] flg_txt   text to parse.  If NULL, then the AUTOOPTS_USAGE
167290001Sglebius *                      environment variable is parsed.
168290001Sglebius * @param[in,out] opts  the program option descriptor
169290001Sglebius */
170290001SglebiusLOCAL void
171290001Sglebiusset_usage_flags(tOptions * opts, char const * flg_txt)
172290001Sglebius{
173290001Sglebius#   define _aof_(_n, _f)   { sizeof(#_n)-1, _f, #_n },
174290001Sglebius    static ao_flag_names_t const fn_table[AOUF_COUNT] = {
175290001Sglebius        AOFLAG_TABLE
176290001Sglebius    };
177290001Sglebius#   undef  _aof_
178290001Sglebius
179290001Sglebius    /*
180290001Sglebius     * the flag word holds a bit for each selected table entry.
181290001Sglebius     */
182290001Sglebius    unsigned int flg = parse_usage_flags(fn_table, flg_txt);
183290001Sglebius    if (flg == 0) return;
184290001Sglebius
185290001Sglebius    /*
186290001Sglebius     * Ensure we do not have conflicting selections
187290001Sglebius     */
188290001Sglebius    {
189290001Sglebius        static unsigned int const form_mask =
190290001Sglebius            AOUF_gnu | AOUF_autoopts;
191290001Sglebius        static unsigned int const misuse_mask =
192290001Sglebius            AOUF_no_misuse_usage | AOUF_misuse_usage;
193290001Sglebius        if (  ((flg & form_mask)   == form_mask)
194290001Sglebius           || ((flg & misuse_mask) == misuse_mask) )
195290001Sglebius            return;
196290001Sglebius    }
197290001Sglebius
198290001Sglebius    /*
199290001Sglebius     * Now fiddle the fOptSet bits, based on settings.
200290001Sglebius     * The OPTPROC_LONGOPT bit is immutable, thus if it is set,
201290001Sglebius     * then fnm points to a mask off mask.
202290001Sglebius     */
203290001Sglebius    {
204290001Sglebius        ao_flag_names_t const * fnm = fn_table;
205290001Sglebius        for (;;) {
206290001Sglebius            if ((flg & 1) != 0) {
207290001Sglebius                if ((fnm->fnm_mask & OPTPROC_LONGOPT) != 0)
208290001Sglebius                     opts->fOptSet &= fnm->fnm_mask;
209290001Sglebius                else opts->fOptSet |= fnm->fnm_mask;
210290001Sglebius            }
211290001Sglebius            flg >>= 1;
212290001Sglebius            if (flg == 0)
213290001Sglebius                break;
214290001Sglebius            fnm++;
215290001Sglebius        }
216290001Sglebius    }
217290001Sglebius}
218290001Sglebius
219181834Sroberto/*
220181834Sroberto *  Figure out if we should try to format usage text sort-of like
221181834Sroberto *  the way many GNU programs do.
222181834Sroberto */
223290001Sglebiusstatic inline bool
224290001Sglebiusdo_gnu_usage(tOptions * pOpts)
225181834Sroberto{
226290001Sglebius    return (pOpts->fOptSet & OPTPROC_GNUUSAGE) ? true : false;
227290001Sglebius}
228181834Sroberto
229290001Sglebius/*
230290001Sglebius *  Figure out if we should try to format usage text sort-of like
231290001Sglebius *  the way many GNU programs do.
232290001Sglebius */
233290001Sglebiusstatic inline bool
234290001Sglebiusskip_misuse_usage(tOptions * pOpts)
235290001Sglebius{
236290001Sglebius    return (pOpts->fOptSet & OPTPROC_MISUSE) ? true : false;
237181834Sroberto}
238181834Sroberto
239181834Sroberto
240181834Sroberto/*=export_func  optionOnlyUsage
241181834Sroberto *
242181834Sroberto * what:  Print usage text for just the options
243290001Sglebius * arg:   + tOptions *  + pOpts    + program options descriptor +
244181834Sroberto * arg:   + int         + ex_code  + exit code for calling exit(3) +
245181834Sroberto *
246181834Sroberto * doc:
247181834Sroberto *  This routine will print only the usage for each option.
248181834Sroberto *  This function may be used when the emitted usage must incorporate
249181834Sroberto *  information not available to AutoOpts.
250181834Sroberto=*/
251181834Srobertovoid
252290001SglebiusoptionOnlyUsage(tOptions * pOpts, int ex_code)
253181834Sroberto{
254290001Sglebius    char const * pOptTitle = NULL;
255181834Sroberto
256290001Sglebius    set_usage_flags(pOpts, NULL);
257290001Sglebius    if ((ex_code != EXIT_SUCCESS) &&
258290001Sglebius        skip_misuse_usage(pOpts))
259290001Sglebius        return;
260290001Sglebius
261181834Sroberto    /*
262181834Sroberto     *  Determine which header and which option formatting strings to use
263181834Sroberto     */
264290001Sglebius    if (do_gnu_usage(pOpts))
265290001Sglebius        (void)setGnuOptFmts(pOpts, &pOptTitle);
266290001Sglebius    else
267290001Sglebius        (void)setStdOptFmts(pOpts, &pOptTitle);
268181834Sroberto
269290001Sglebius    prt_opt_usage(pOpts, ex_code, pOptTitle);
270290001Sglebius
271290001Sglebius    fflush(option_usage_fp);
272290001Sglebius    if (ferror(option_usage_fp) != 0)
273290001Sglebius        fserr_exit(pOpts->pzProgName, zwriting, (option_usage_fp == stderr)
274290001Sglebius                   ? zstderr_name : zstdout_name);
275181834Sroberto}
276181834Sroberto
277290001Sglebius/**
278290001Sglebius * Print a message suggesting how to get help.
279181834Sroberto *
280290001Sglebius * @param[in] opts      the program options
281290001Sglebius */
282290001Sglebiusstatic void
283290001Sglebiusprint_offer_usage(tOptions * opts)
284181834Sroberto{
285290001Sglebius    char help[24];
286181834Sroberto
287290001Sglebius    if (HAS_opt_usage_t(opts)) {
288290001Sglebius        int ix = opts->presetOptCt;
289290001Sglebius        tOptDesc * od = opts->pOptDesc + ix;
290290001Sglebius        while (od->optUsage != AOUSE_HELP) {
291290001Sglebius            if (++ix >= opts->optCt)
292290001Sglebius                ao_bug(zmissing_help_msg);
293290001Sglebius            od++;
294290001Sglebius        }
295290001Sglebius        switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) {
296290001Sglebius        case OPTPROC_SHORTOPT:
297290001Sglebius            help[0] = '-';
298290001Sglebius            help[1] = od->optValue;
299290001Sglebius            help[2] = NUL;
300290001Sglebius            break;
301181834Sroberto
302290001Sglebius        case OPTPROC_LONGOPT:
303290001Sglebius        case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT):
304290001Sglebius            help[0] = help[1] = '-';
305290001Sglebius            strncpy(help + 2, od->pz_Name, 20);
306290001Sglebius            break;
307290001Sglebius
308290001Sglebius        case 0:
309290001Sglebius            strncpy(help, od->pz_Name, 20);
310290001Sglebius            break;
311290001Sglebius        }
312181834Sroberto
313290001Sglebius    } else {
314290001Sglebius        switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) {
315290001Sglebius        case OPTPROC_SHORTOPT:
316290001Sglebius            strcpy(help, "-h");
317290001Sglebius            break;
318181834Sroberto
319290001Sglebius        case OPTPROC_LONGOPT:
320290001Sglebius        case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT):
321290001Sglebius            strcpy(help, "--help");
322290001Sglebius            break;
323290001Sglebius
324290001Sglebius        case 0:
325290001Sglebius            strcpy(help, "help");
326290001Sglebius            break;
327290001Sglebius        }
328290001Sglebius    }
329290001Sglebius
330290001Sglebius    fprintf(option_usage_fp, zoffer_usage_fmt, opts->pzProgName, help);
331290001Sglebius}
332290001Sglebius
333290001Sglebius/**
334290001Sglebius * Print information about each option.
335290001Sglebius *
336290001Sglebius * @param[in] opts      the program options
337290001Sglebius * @param[in] exit_code whether or not there was a usage error reported.
338290001Sglebius *                      used to select full usage versus abbreviated.
339290001Sglebius */
340290001Sglebiusstatic void
341290001Sglebiusprint_usage_details(tOptions * opts, int exit_code)
342290001Sglebius{
343181834Sroberto    {
344290001Sglebius        char const * pOptTitle = NULL;
345290001Sglebius        int flen;
346181834Sroberto
347181834Sroberto        /*
348181834Sroberto         *  Determine which header and which option formatting strings to use
349181834Sroberto         */
350290001Sglebius        if (do_gnu_usage(opts)) {
351290001Sglebius            flen = setGnuOptFmts(opts, &pOptTitle);
352290001Sglebius            sprintf(line_fmt_buf, zFmtFmt, flen);
353290001Sglebius            fputc(NL, option_usage_fp);
354181834Sroberto        }
355181834Sroberto        else {
356290001Sglebius            flen = setStdOptFmts(opts, &pOptTitle);
357290001Sglebius            sprintf(line_fmt_buf, zFmtFmt, flen);
358181834Sroberto
359181834Sroberto            /*
360181834Sroberto             *  When we exit with EXIT_SUCCESS and the first option is a doc
361181834Sroberto             *  option, we do *NOT* want to emit the column headers.
362181834Sroberto             *  Otherwise, we do.
363181834Sroberto             */
364290001Sglebius            if (  (exit_code != EXIT_SUCCESS)
365290001Sglebius               || ((opts->pOptDesc->fOptState & OPTST_DOCUMENT) == 0) )
366181834Sroberto
367290001Sglebius                fputs(pOptTitle, option_usage_fp);
368181834Sroberto        }
369181834Sroberto
370290001Sglebius        flen = 4 - ((flen + 15) / 8);
371290001Sglebius        if (flen > 0)
372290001Sglebius            tab_skip_ct = flen;
373290001Sglebius        prt_opt_usage(opts, exit_code, pOptTitle);
374181834Sroberto    }
375181834Sroberto
376181834Sroberto    /*
377181834Sroberto     *  Describe the mechanics of denoting the options
378181834Sroberto     */
379290001Sglebius    switch (opts->fOptSet & OPTPROC_L_N_S) {
380290001Sglebius    case OPTPROC_L_N_S:     fputs(zFlagOkay, option_usage_fp); break;
381181834Sroberto    case OPTPROC_SHORTOPT:  break;
382290001Sglebius    case OPTPROC_LONGOPT:   fputs(zNoFlags,  option_usage_fp); break;
383290001Sglebius    case 0:                 fputs(zOptsOnly, option_usage_fp); break;
384181834Sroberto    }
385181834Sroberto
386290001Sglebius    if ((opts->fOptSet & OPTPROC_NUM_OPT) != 0)
387290001Sglebius        fputs(zNumberOpt, option_usage_fp);
388181834Sroberto
389290001Sglebius    if ((opts->fOptSet & OPTPROC_REORDER) != 0)
390290001Sglebius        fputs(zReorder, option_usage_fp);
391181834Sroberto
392290001Sglebius    if (opts->pzExplain != NULL)
393290001Sglebius        fputs(opts->pzExplain, option_usage_fp);
394181834Sroberto
395181834Sroberto    /*
396181834Sroberto     *  IF the user is asking for help (thus exiting with SUCCESS),
397181834Sroberto     *  THEN see what additional information we can provide.
398181834Sroberto     */
399290001Sglebius    if (exit_code == EXIT_SUCCESS)
400290001Sglebius        prt_prog_detail(opts);
401181834Sroberto
402290001Sglebius    /*
403290001Sglebius     * Give bug notification preference to the packager information
404290001Sglebius     */
405290001Sglebius    if (HAS_pzPkgDataDir(opts) && (opts->pzPackager != NULL))
406290001Sglebius        fputs(opts->pzPackager, option_usage_fp);
407181834Sroberto
408290001Sglebius    else if (opts->pzBugAddr != NULL)
409290001Sglebius        fprintf(option_usage_fp, zPlsSendBugs, opts->pzBugAddr);
410290001Sglebius
411290001Sglebius    fflush(option_usage_fp);
412290001Sglebius
413290001Sglebius    if (ferror(option_usage_fp) != 0)
414290001Sglebius        fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stderr)
415290001Sglebius                   ? zstderr_name : zstdout_name);
416181834Sroberto}
417181834Sroberto
418290001Sglebiusstatic void
419290001Sglebiusprint_one_paragraph(char const * text, bool plain, FILE * fp)
420290001Sglebius{
421290001Sglebius    if (plain) {
422290001Sglebius#ifdef ENABLE_NLS
423290001Sglebius#ifdef HAVE_LIBINTL_H
424290001Sglebius#ifdef DEBUG_ENABLED
425290001Sglebius#undef gettext
426290001Sglebius#endif
427290001Sglebius        char * buf = dgettext("libopts", text);
428290001Sglebius        if (buf == text)
429290001Sglebius            text = gettext(text);
430290001Sglebius#endif /* HAVE_LIBINTL_H */
431290001Sglebius#endif /* ENABLE_NLS */
432290001Sglebius        fputs(text, fp);
433290001Sglebius    }
434181834Sroberto
435290001Sglebius    else {
436290001Sglebius        char const * t = optionQuoteString(text, LINE_SPLICE);
437290001Sglebius        fprintf(fp, PUTS_FMT, t);
438290001Sglebius        AGFREE(t);
439290001Sglebius    }
440290001Sglebius}
441290001Sglebius
442290001Sglebius/*=export_func  optionPrintParagraphs
443290001Sglebius * private:
444290001Sglebius *
445290001Sglebius * what:  Print a paragraph of usage text
446290001Sglebius * arg:   + char const * + text  + a block of text that has bee i18n-ed +
447290001Sglebius * arg:   + bool         + plain + false -> wrap text in fputs()        +
448290001Sglebius * arg:   + FILE *       + fp    + the stream file pointer for output   +
449290001Sglebius *
450290001Sglebius * doc:
451290001Sglebius *  This procedure is called in two contexts: when a full or short usage text
452290001Sglebius *  has been provided for display, and when autogen is assembling a list of
453290001Sglebius *  translatable texts in the optmain.tlib template.  In the former case, \a
454290001Sglebius *  plain is set to \a true, otherwise \a false.
455290001Sglebius *
456290001Sglebius *  Anything less than 256 characters in size is printed as a single unit.
457290001Sglebius *  Otherwise, paragraphs are detected.  A paragraph break is defined as just
458290001Sglebius *  before a non-empty line preceded by two newlines or a line that starts
459290001Sglebius *  with at least one space character but fewer than 8 space characters.
460290001Sglebius *  Lines indented with tabs or more than 7 spaces are considered continuation
461290001Sglebius *  lines.
462290001Sglebius *
463290001Sglebius *  If 'plain' is true, we are emitting text for a user to see.  So, if it is
464290001Sglebius *  true and NLS is not enabled, then just write the whole thing at once.
465290001Sglebius=*/
466290001Sglebiusvoid
467290001SglebiusoptionPrintParagraphs(char const * text, bool plain, FILE * fp)
468290001Sglebius{
469290001Sglebius    size_t len = strlen(text);
470290001Sglebius    char * buf;
471290001Sglebius#ifndef ENABLE_NLS
472290001Sglebius    if (plain || (len < 256))
473290001Sglebius#else
474290001Sglebius    if (len < 256)
475290001Sglebius#endif
476290001Sglebius    {
477290001Sglebius        print_one_paragraph(text, plain, fp);
478290001Sglebius        return;
479290001Sglebius    }
480290001Sglebius
481290001Sglebius    AGDUPSTR(buf, text, "ppara");
482290001Sglebius    text = buf;
483290001Sglebius
484290001Sglebius    for (;;) {
485290001Sglebius        char * scan;
486290001Sglebius
487290001Sglebius        if (len < 256) {
488290001Sglebius        done:
489290001Sglebius            print_one_paragraph(buf, plain, fp);
490290001Sglebius            break;
491290001Sglebius        }
492290001Sglebius        scan = buf;
493290001Sglebius
494290001Sglebius    try_longer:
495290001Sglebius        scan = strchr(scan, NL);
496290001Sglebius        if (scan == NULL)
497290001Sglebius            goto done;
498290001Sglebius
499290001Sglebius        if ((scan - buf) < 40) {
500290001Sglebius            scan++;
501290001Sglebius            goto try_longer;
502290001Sglebius        }
503290001Sglebius
504290001Sglebius        scan++;
505290001Sglebius        if ((! isspace((int)*scan)) || (*scan == HT))
506290001Sglebius            /*
507290001Sglebius             * line starts with tab or non-whitespace --> continuation
508290001Sglebius             */
509290001Sglebius            goto try_longer;
510290001Sglebius
511290001Sglebius        if (*scan == NL) {
512290001Sglebius            /*
513290001Sglebius             * Double newline -> paragraph break
514290001Sglebius             * Include all newlines in current paragraph.
515290001Sglebius             */
516290001Sglebius            while (*++scan == NL)  /*continue*/;
517290001Sglebius
518290001Sglebius        } else {
519290001Sglebius            char * p = scan;
520290001Sglebius            int   sp_ct = 0;
521290001Sglebius
522290001Sglebius            while (*p == ' ') {
523290001Sglebius                if (++sp_ct >= 8) {
524290001Sglebius                    /*
525290001Sglebius                     * Too many spaces --> continuation line
526290001Sglebius                     */
527290001Sglebius                    scan = p;
528290001Sglebius                    goto try_longer;
529290001Sglebius                }
530290001Sglebius                p++;
531290001Sglebius            }
532290001Sglebius        }
533290001Sglebius
534290001Sglebius        /*
535290001Sglebius         * "scan" points to the first character of a paragraph or the
536290001Sglebius         * terminating NUL byte.
537290001Sglebius         */
538290001Sglebius        {
539290001Sglebius            char svch = *scan;
540290001Sglebius            *scan = NUL;
541290001Sglebius            print_one_paragraph(buf, plain, fp);
542290001Sglebius            len -= scan - buf;
543290001Sglebius            if (len <= 0)
544290001Sglebius                break;
545290001Sglebius            *scan = svch;
546290001Sglebius            buf = scan;
547290001Sglebius        }
548290001Sglebius    }
549290001Sglebius    AGFREE(text);
550290001Sglebius}
551290001Sglebius
552290001Sglebius/*=export_func  optionUsage
553290001Sglebius * private:
554290001Sglebius *
555290001Sglebius * what:  Print usage text
556290001Sglebius * arg:   + tOptions * + opts + program options descriptor +
557290001Sglebius * arg:   + int        + exitCode + exit code for calling exit(3) +
558290001Sglebius *
559290001Sglebius * doc:
560290001Sglebius *  This routine will print usage in both GNU-standard and AutoOpts-expanded
561290001Sglebius *  formats.  The descriptor specifies the default, but AUTOOPTS_USAGE will
562290001Sglebius *  over-ride this, providing the value of it is set to either "gnu" or
563290001Sglebius *  "autoopts".  This routine will @strong{not} return.
564290001Sglebius *
565290001Sglebius *  If "exitCode" is "AO_EXIT_REQ_USAGE" (normally 64), then output will to
566290001Sglebius *  to stdout and the actual exit code will be "EXIT_SUCCESS".
567290001Sglebius=*/
568290001Sglebiusvoid
569290001SglebiusoptionUsage(tOptions * opts, int usage_exit_code)
570290001Sglebius{
571290001Sglebius    int exit_code = (usage_exit_code == AO_EXIT_REQ_USAGE)
572290001Sglebius        ? EXIT_SUCCESS : usage_exit_code;
573290001Sglebius
574290001Sglebius    displayEnum = false;
575290001Sglebius    set_usage_flags(opts, NULL);
576290001Sglebius
577290001Sglebius    /*
578290001Sglebius     *  Paged usage will preset option_usage_fp to an output file.
579290001Sglebius     *  If it hasn't already been set, then set it to standard output
580290001Sglebius     *  on successful exit (help was requested), otherwise error out.
581290001Sglebius     *
582290001Sglebius     *  Test the version before obtaining pzFullUsage or pzShortUsage.
583290001Sglebius     *  These fields do not exist before revision 30.
584290001Sglebius     */
585290001Sglebius    {
586290001Sglebius        char const * pz;
587290001Sglebius
588290001Sglebius        if (exit_code == EXIT_SUCCESS) {
589290001Sglebius            pz = (opts->structVersion >= 30 * 4096)
590290001Sglebius                ? opts->pzFullUsage : NULL;
591290001Sglebius
592290001Sglebius            if (option_usage_fp == NULL)
593290001Sglebius                option_usage_fp = print_exit ? stderr : stdout;
594290001Sglebius
595290001Sglebius        } else {
596290001Sglebius            pz = (opts->structVersion >= 30 * 4096)
597290001Sglebius                ? opts->pzShortUsage : NULL;
598290001Sglebius
599290001Sglebius            if (option_usage_fp == NULL)
600290001Sglebius                option_usage_fp = stderr;
601290001Sglebius        }
602290001Sglebius
603290001Sglebius        if (((opts->fOptSet & OPTPROC_COMPUTE) == 0) && (pz != NULL)) {
604290001Sglebius            if ((opts->fOptSet & OPTPROC_TRANSLATE) != 0)
605290001Sglebius                optionPrintParagraphs(pz, true, option_usage_fp);
606290001Sglebius            else
607290001Sglebius                fputs(pz, option_usage_fp);
608290001Sglebius            goto flush_and_exit;
609290001Sglebius        }
610290001Sglebius    }
611290001Sglebius
612290001Sglebius    fprintf(option_usage_fp, opts->pzUsageTitle, opts->pzProgName);
613290001Sglebius
614290001Sglebius    if ((exit_code == EXIT_SUCCESS) ||
615290001Sglebius        (! skip_misuse_usage(opts)))
616290001Sglebius
617290001Sglebius        print_usage_details(opts, usage_exit_code);
618290001Sglebius    else
619290001Sglebius        print_offer_usage(opts);
620290001Sglebius
621290001Sglebius flush_and_exit:
622290001Sglebius    fflush(option_usage_fp);
623290001Sglebius    if (ferror(option_usage_fp) != 0)
624290001Sglebius        fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stdout)
625290001Sglebius                   ? zstdout_name : zstderr_name);
626290001Sglebius
627290001Sglebius    option_exits(exit_code);
628290001Sglebius}
629290001Sglebius
630181834Sroberto/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
631290001Sglebius *   PER OPTION TYPE USAGE INFORMATION
632290001Sglebius * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
633290001Sglebius/**
634290001Sglebius * print option conflicts.
635181834Sroberto *
636290001Sglebius * @param opts the program option descriptor
637290001Sglebius * @param od   the option descriptor
638181834Sroberto */
639181834Srobertostatic void
640290001Sglebiusprt_conflicts(tOptions * opts, tOptDesc * od)
641181834Sroberto{
642290001Sglebius    const int * opt_no;
643290001Sglebius    fputs(zTabHyp + tab_skip_ct, option_usage_fp);
644290001Sglebius
645181834Sroberto    /*
646290001Sglebius     *  REQUIRED:
647181834Sroberto     */
648290001Sglebius    if (od->pOptMust != NULL) {
649290001Sglebius        opt_no = od->pOptMust;
650181834Sroberto
651290001Sglebius        if (opt_no[1] == NO_EQUIVALENT) {
652290001Sglebius            fprintf(option_usage_fp, zReqOne,
653290001Sglebius                    opts->pOptDesc[*opt_no].pz_Name);
654290001Sglebius        } else {
655290001Sglebius            fputs(zReqThese, option_usage_fp);
656181834Sroberto            for (;;) {
657290001Sglebius                fprintf(option_usage_fp, zTabout + tab_skip_ct,
658290001Sglebius                        opts->pOptDesc[*opt_no].pz_Name);
659290001Sglebius                if (*++opt_no == NO_EQUIVALENT)
660181834Sroberto                    break;
661181834Sroberto            }
662181834Sroberto        }
663181834Sroberto
664290001Sglebius        if (od->pOptCant != NULL)
665290001Sglebius            fputs(zTabHypAnd + tab_skip_ct, option_usage_fp);
666290001Sglebius    }
667290001Sglebius
668290001Sglebius    /*
669290001Sglebius     *  CONFLICTS:
670290001Sglebius     */
671290001Sglebius    if (od->pOptCant == NULL)
672290001Sglebius        return;
673290001Sglebius
674290001Sglebius    opt_no = od->pOptCant;
675290001Sglebius
676290001Sglebius    if (opt_no[1] == NO_EQUIVALENT) {
677290001Sglebius        fprintf(option_usage_fp, zProhibOne,
678290001Sglebius                opts->pOptDesc[*opt_no].pz_Name);
679290001Sglebius        return;
680290001Sglebius    }
681290001Sglebius
682290001Sglebius    fputs(zProhib, option_usage_fp);
683290001Sglebius    for (;;) {
684290001Sglebius        fprintf(option_usage_fp, zTabout + tab_skip_ct,
685290001Sglebius                opts->pOptDesc[*opt_no].pz_Name);
686290001Sglebius        if (*++opt_no == NO_EQUIVALENT)
687290001Sglebius            break;
688290001Sglebius    }
689290001Sglebius}
690290001Sglebius
691290001Sglebius/**
692290001Sglebius *  Print the usage information for a single vendor option.
693290001Sglebius *
694290001Sglebius * @param[in] opts    the program option descriptor
695290001Sglebius * @param[in] od      the option descriptor
696290001Sglebius * @param[in] argtp   names of the option argument types
697290001Sglebius * @param[in] usefmt  format for primary usage line
698290001Sglebius */
699290001Sglebiusstatic void
700290001Sglebiusprt_one_vendor(tOptions *    opts,  tOptDesc *   od,
701290001Sglebius               arg_types_t * argtp, char const * usefmt)
702290001Sglebius{
703290001Sglebius    prt_preamble(opts, od, argtp);
704290001Sglebius
705290001Sglebius    {
706290001Sglebius        char z[ 80 ];
707290001Sglebius        char const *  pzArgType;
708290001Sglebius
709181834Sroberto        /*
710290001Sglebius         *  Determine the argument type string first on its usage, then,
711290001Sglebius         *  when the option argument is required, base the type string on the
712290001Sglebius         *  argument type.
713181834Sroberto         */
714290001Sglebius        if (od->fOptState & OPTST_ARG_OPTIONAL) {
715290001Sglebius            pzArgType = argtp->pzOpt;
716181834Sroberto
717290001Sglebius        } else switch (OPTST_GET_ARGTYPE(od->fOptState)) {
718290001Sglebius        case OPARG_TYPE_NONE:        pzArgType = argtp->pzNo;   break;
719290001Sglebius        case OPARG_TYPE_ENUMERATION: pzArgType = argtp->pzKey;  break;
720290001Sglebius        case OPARG_TYPE_FILE:        pzArgType = argtp->pzFile; break;
721290001Sglebius        case OPARG_TYPE_MEMBERSHIP:  pzArgType = argtp->pzKeyL; break;
722290001Sglebius        case OPARG_TYPE_BOOLEAN:     pzArgType = argtp->pzBool; break;
723290001Sglebius        case OPARG_TYPE_NUMERIC:     pzArgType = argtp->pzNum;  break;
724290001Sglebius        case OPARG_TYPE_HIERARCHY:   pzArgType = argtp->pzNest; break;
725290001Sglebius        case OPARG_TYPE_STRING:      pzArgType = argtp->pzStr;  break;
726290001Sglebius        case OPARG_TYPE_TIME:        pzArgType = argtp->pzTime; break;
727290001Sglebius        default:                     goto bogus_desc;
728181834Sroberto        }
729290001Sglebius
730290001Sglebius        pzArgType = SPN_WHITESPACE_CHARS(pzArgType);
731290001Sglebius        if (*pzArgType == NUL)
732290001Sglebius            snprintf(z, sizeof(z), "%s", od->pz_Name);
733290001Sglebius        else
734290001Sglebius            snprintf(z, sizeof(z), "%s=%s", od->pz_Name, pzArgType);
735290001Sglebius        fprintf(option_usage_fp, usefmt, z, od->pzText);
736290001Sglebius
737290001Sglebius        switch (OPTST_GET_ARGTYPE(od->fOptState)) {
738290001Sglebius        case OPARG_TYPE_ENUMERATION:
739290001Sglebius        case OPARG_TYPE_MEMBERSHIP:
740290001Sglebius            displayEnum = (od->pOptProc != NULL) ? true : displayEnum;
741290001Sglebius        }
742181834Sroberto    }
743181834Sroberto
744290001Sglebius    return;
745290001Sglebius
746290001Sglebius bogus_desc:
747290001Sglebius    fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name);
748290001Sglebius    ao_bug(zbad_arg_type_msg);
749290001Sglebius}
750290001Sglebius
751290001Sglebius/**
752290001Sglebius * Print the long options processed with "-W".  These options will be the
753290001Sglebius * ones that do *not* have flag characters.
754290001Sglebius *
755290001Sglebius * @param opts  the program option descriptor
756290001Sglebius * @param title the title for the options
757290001Sglebius */
758290001Sglebiusstatic void
759290001Sglebiusprt_vendor_opts(tOptions * opts, char const * title)
760290001Sglebius{
761290001Sglebius    static unsigned int const not_vended_mask =
762290001Sglebius        OPTST_NO_USAGE_MASK | OPTST_DOCUMENT;
763290001Sglebius
764290001Sglebius    static char const vfmtfmt[] = "%%-%us %%s\n";
765290001Sglebius    char vfmt[sizeof(vfmtfmt)];
766290001Sglebius
767181834Sroberto    /*
768290001Sglebius     *  Only handle client specified options.  The "vendor option" follows
769290001Sglebius     *  "presetOptCt", so we won't loop/recurse indefinitely.
770290001Sglebius     */
771290001Sglebius    int          ct     = opts->presetOptCt;
772290001Sglebius    tOptDesc *   od     = opts->pOptDesc;
773290001Sglebius    fprintf(option_usage_fp, zTabout + tab_skip_ct, zVendOptsAre);
774290001Sglebius
775290001Sglebius    {
776290001Sglebius        size_t   nmlen  = 0;
777290001Sglebius        do  {
778290001Sglebius            size_t l;
779290001Sglebius            if (  ((od->fOptState & not_vended_mask) != 0)
780290001Sglebius               || IS_GRAPHIC_CHAR(od->optValue))
781290001Sglebius                continue;
782290001Sglebius
783290001Sglebius            l = strlen(od->pz_Name);
784290001Sglebius            if (l > nmlen)  nmlen = l;
785290001Sglebius        } while (od++, (--ct > 0));
786290001Sglebius
787290001Sglebius        snprintf(vfmt, sizeof(vfmt), vfmtfmt, (unsigned int)nmlen + 4);
788290001Sglebius    }
789290001Sglebius
790290001Sglebius    if (tab_skip_ct > 0)
791290001Sglebius        tab_skip_ct--;
792290001Sglebius
793290001Sglebius    ct    = opts->presetOptCt;
794290001Sglebius    od    = opts->pOptDesc;
795290001Sglebius
796290001Sglebius    do  {
797290001Sglebius        if (  ((od->fOptState & not_vended_mask) != 0)
798290001Sglebius           || IS_GRAPHIC_CHAR(od->optValue))
799290001Sglebius            continue;
800290001Sglebius
801290001Sglebius        prt_one_vendor(opts, od, &argTypes, vfmt);
802290001Sglebius        prt_extd_usage(opts, od, title);
803290001Sglebius
804290001Sglebius    } while (od++, (--ct > 0));
805290001Sglebius
806290001Sglebius    /* no need to restore "tab_skip_ct" - options are done now */
807290001Sglebius}
808290001Sglebius
809290001Sglebius/**
810290001Sglebius * Print extended usage.  Usage/help was requested.
811290001Sglebius *
812290001Sglebius * @param opts  the program option descriptor
813290001Sglebius * @param od   the option descriptor
814290001Sglebius * @param title the title for the options
815290001Sglebius */
816290001Sglebiusstatic void
817290001Sglebiusprt_extd_usage(tOptions * opts, tOptDesc * od, char const * title)
818290001Sglebius{
819290001Sglebius    if (  ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0)
820290001Sglebius       && (od->optActualValue == VENDOR_OPTION_VALUE)) {
821290001Sglebius        prt_vendor_opts(opts, title);
822290001Sglebius        return;
823290001Sglebius    }
824290001Sglebius
825290001Sglebius    /*
826290001Sglebius     *  IF there are option conflicts or dependencies,
827290001Sglebius     *  THEN print them here.
828290001Sglebius     */
829290001Sglebius    if ((od->pOptMust != NULL) || (od->pOptCant != NULL))
830290001Sglebius        prt_conflicts(opts, od);
831290001Sglebius
832290001Sglebius    /*
833181834Sroberto     *  IF there is a disablement string
834181834Sroberto     *  THEN print the disablement info
835181834Sroberto     */
836290001Sglebius    if (od->pz_DisableName != NULL )
837290001Sglebius        fprintf(option_usage_fp, zDis + tab_skip_ct, od->pz_DisableName);
838181834Sroberto
839181834Sroberto    /*
840290001Sglebius     *  Check for argument types that have callbacks with magical properties
841181834Sroberto     */
842290001Sglebius    switch (OPTST_GET_ARGTYPE(od->fOptState)) {
843290001Sglebius    case OPARG_TYPE_NUMERIC:
844290001Sglebius        /*
845290001Sglebius         *  IF the numeric option has a special callback,
846290001Sglebius         *  THEN call it, requesting the range or other special info
847290001Sglebius         */
848290001Sglebius        if (  (od->pOptProc != NULL)
849290001Sglebius           && (od->pOptProc != optionNumericVal) ) {
850290001Sglebius            (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
851290001Sglebius        }
852290001Sglebius        break;
853290001Sglebius
854290001Sglebius    case OPARG_TYPE_FILE:
855290001Sglebius        (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
856290001Sglebius        break;
857181834Sroberto    }
858181834Sroberto
859181834Sroberto    /*
860181834Sroberto     *  IF the option defaults to being enabled,
861181834Sroberto     *  THEN print that out
862181834Sroberto     */
863290001Sglebius    if (od->fOptState & OPTST_INITENABLED)
864290001Sglebius        fputs(zEnab + tab_skip_ct, option_usage_fp);
865181834Sroberto
866181834Sroberto    /*
867181834Sroberto     *  IF  the option is in an equivalence class
868181834Sroberto     *        AND not the designated lead
869181834Sroberto     *  THEN print equivalence and leave it at that.
870181834Sroberto     */
871290001Sglebius    if (  (od->optEquivIndex != NO_EQUIVALENT)
872290001Sglebius       && (od->optEquivIndex != od->optActualIndex )  )  {
873290001Sglebius        fprintf(option_usage_fp, zalt_opt + tab_skip_ct,
874290001Sglebius                 opts->pOptDesc[ od->optEquivIndex ].pz_Name);
875181834Sroberto        return;
876181834Sroberto    }
877181834Sroberto
878181834Sroberto    /*
879181834Sroberto     *  IF this particular option can NOT be preset
880181834Sroberto     *    AND some form of presetting IS allowed,
881181834Sroberto     *    AND it is not an auto-managed option (e.g. --help, et al.)
882181834Sroberto     *  THEN advise that this option may not be preset.
883181834Sroberto     */
884290001Sglebius    if (  ((od->fOptState & OPTST_NO_INIT) != 0)
885290001Sglebius       && (  (opts->papzHomeList != NULL)
886290001Sglebius          || (opts->pzPROGNAME != NULL)
887181834Sroberto          )
888290001Sglebius       && (od->optIndex < opts->presetOptCt)
889181834Sroberto       )
890181834Sroberto
891290001Sglebius        fputs(zNoPreset + tab_skip_ct, option_usage_fp);
892181834Sroberto
893181834Sroberto    /*
894181834Sroberto     *  Print the appearance requirements.
895181834Sroberto     */
896290001Sglebius    if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_MEMBERSHIP)
897290001Sglebius        fputs(zMembers + tab_skip_ct, option_usage_fp);
898181834Sroberto
899290001Sglebius    else switch (od->optMinCt) {
900181834Sroberto    case 1:
901181834Sroberto    case 0:
902290001Sglebius        switch (od->optMaxCt) {
903290001Sglebius        case 0:       fputs(zPreset + tab_skip_ct, option_usage_fp); break;
904290001Sglebius        case NOLIMIT: fputs(zNoLim  + tab_skip_ct, option_usage_fp); break;
905181834Sroberto        case 1:       break;
906181834Sroberto            /*
907181834Sroberto             * IF the max is more than one but limited, print "UP TO" message
908181834Sroberto             */
909290001Sglebius        default:
910290001Sglebius            fprintf(option_usage_fp, zUpTo + tab_skip_ct, od->optMaxCt); break;
911181834Sroberto        }
912181834Sroberto        break;
913181834Sroberto
914181834Sroberto    default:
915181834Sroberto        /*
916181834Sroberto         *  More than one is required.  Print the range.
917181834Sroberto         */
918290001Sglebius        fprintf(option_usage_fp, zMust + tab_skip_ct,
919290001Sglebius                od->optMinCt, od->optMaxCt);
920181834Sroberto    }
921181834Sroberto
922290001Sglebius    if (  NAMED_OPTS(opts)
923290001Sglebius       && (opts->specOptIdx.default_opt == od->optIndex))
924290001Sglebius        fputs(zDefaultOpt + tab_skip_ct, option_usage_fp);
925181834Sroberto}
926181834Sroberto
927290001Sglebius/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
928290001Sglebius/**
929290001Sglebius * Figure out where all the initialization files might live.  This requires
930290001Sglebius * translating some environment variables and testing to see if a name is a
931290001Sglebius * directory or a file.  It's squishy, but important to tell users how to
932290001Sglebius * find these files.
933181834Sroberto *
934290001Sglebius * @param[in]  papz        search path
935290001Sglebius * @param[out] ini_file    an output buffer of AG_PATH_MAX+1 bytes
936290001Sglebius * @param[in]  path_nm     the name of the file we're hunting for
937181834Sroberto */
938181834Srobertostatic void
939290001Sglebiusprt_ini_list(char const * const * papz, char const * ini_file,
940290001Sglebius             char const * path_nm)
941181834Sroberto{
942290001Sglebius    char pth_buf[AG_PATH_MAX+1];
943181834Sroberto
944290001Sglebius    fputs(zPresetIntro, option_usage_fp);
945181834Sroberto
946181834Sroberto    for (;;) {
947290001Sglebius        char const * path   = *(papz++);
948290001Sglebius        char const * nm_buf = pth_buf;
949181834Sroberto
950290001Sglebius        if (path == NULL)
951181834Sroberto            break;
952181834Sroberto
953290001Sglebius        /*
954290001Sglebius         * Ignore any invalid paths
955290001Sglebius         */
956290001Sglebius        if (! optionMakePath(pth_buf, (int)sizeof(pth_buf), path, path_nm))
957290001Sglebius            nm_buf = path;
958181834Sroberto
959181834Sroberto        /*
960290001Sglebius         * Expand paths that are relative to the executable or installation
961290001Sglebius         * directories.  Leave alone paths that use environment variables.
962290001Sglebius         */
963290001Sglebius        else if ((*path == '$')
964290001Sglebius                 && ((path[1] == '$') || (path[1] == '@')))
965290001Sglebius            path = nm_buf;
966290001Sglebius
967290001Sglebius        /*
968181834Sroberto         *  Print the name of the "homerc" file.  If the "rcfile" name is
969181834Sroberto         *  not empty, we may or may not print that, too...
970181834Sroberto         */
971290001Sglebius        fprintf(option_usage_fp, zPathFmt, path);
972290001Sglebius        if (*ini_file != NUL) {
973181834Sroberto            struct stat sb;
974181834Sroberto
975181834Sroberto            /*
976181834Sroberto             *  IF the "homerc" file is a directory,
977181834Sroberto             *  then append the "rcfile" name.
978181834Sroberto             */
979290001Sglebius            if ((stat(nm_buf, &sb) == 0) && S_ISDIR(sb.st_mode)) {
980290001Sglebius                fputc(DIRCH,    option_usage_fp);
981290001Sglebius                fputs(ini_file, option_usage_fp);
982181834Sroberto            }
983181834Sroberto        }
984181834Sroberto
985290001Sglebius        fputc(NL, option_usage_fp);
986181834Sroberto    }
987181834Sroberto}
988181834Sroberto
989290001Sglebius/**
990290001Sglebius *  Print the usage line preamble text
991290001Sglebius *
992290001Sglebius * @param opts  the program option descriptor
993290001Sglebius * @param od    the option descriptor
994290001Sglebius * @param at    names of the option argument types
995181834Sroberto */
996181834Srobertostatic void
997290001Sglebiusprt_preamble(tOptions * opts, tOptDesc * od, arg_types_t * at)
998181834Sroberto{
999181834Sroberto    /*
1000181834Sroberto     *  Flag prefix: IF no flags at all, then omit it.  If not printable
1001181834Sroberto     *  (not allowed for this option), then blank, else print it.
1002181834Sroberto     *  Follow it with a comma if we are doing GNU usage and long
1003181834Sroberto     *  opts are to be printed too.
1004181834Sroberto     */
1005290001Sglebius    if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0)
1006290001Sglebius        fputs(at->pzSpc, option_usage_fp);
1007290001Sglebius
1008290001Sglebius    else if (! IS_GRAPHIC_CHAR(od->optValue)) {
1009290001Sglebius        if (  (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
1010181834Sroberto           == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
1011290001Sglebius            fputc(' ', option_usage_fp);
1012290001Sglebius        fputs(at->pzNoF, option_usage_fp);
1013290001Sglebius
1014181834Sroberto    } else {
1015290001Sglebius        fprintf(option_usage_fp, "   -%c", od->optValue);
1016290001Sglebius        if (  (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
1017181834Sroberto           == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
1018290001Sglebius            fputs(", ", option_usage_fp);
1019181834Sroberto    }
1020290001Sglebius}
1021181834Sroberto
1022290001Sglebius/**
1023290001Sglebius *  Print the usage information for a single option.
1024290001Sglebius *
1025290001Sglebius * @param opts  the program option descriptor
1026290001Sglebius * @param od    the option descriptor
1027290001Sglebius * @param at    names of the option argument types
1028290001Sglebius */
1029290001Sglebiusstatic void
1030290001Sglebiusprt_one_usage(tOptions * opts, tOptDesc * od, arg_types_t * at)
1031290001Sglebius{
1032290001Sglebius    prt_preamble(opts, od, at);
1033290001Sglebius
1034181834Sroberto    {
1035290001Sglebius        char z[80];
1036290001Sglebius        char const * atyp;
1037290001Sglebius
1038181834Sroberto        /*
1039181834Sroberto         *  Determine the argument type string first on its usage, then,
1040181834Sroberto         *  when the option argument is required, base the type string on the
1041181834Sroberto         *  argument type.
1042181834Sroberto         */
1043290001Sglebius        if (od->fOptState & OPTST_ARG_OPTIONAL) {
1044290001Sglebius            atyp = at->pzOpt;
1045181834Sroberto
1046290001Sglebius        } else switch (OPTST_GET_ARGTYPE(od->fOptState)) {
1047290001Sglebius        case OPARG_TYPE_NONE:        atyp = at->pzNo;   break;
1048290001Sglebius        case OPARG_TYPE_ENUMERATION: atyp = at->pzKey;  break;
1049290001Sglebius        case OPARG_TYPE_FILE:        atyp = at->pzFile; break;
1050290001Sglebius        case OPARG_TYPE_MEMBERSHIP:  atyp = at->pzKeyL; break;
1051290001Sglebius        case OPARG_TYPE_BOOLEAN:     atyp = at->pzBool; break;
1052290001Sglebius        case OPARG_TYPE_NUMERIC:     atyp = at->pzNum;  break;
1053290001Sglebius        case OPARG_TYPE_HIERARCHY:   atyp = at->pzNest; break;
1054290001Sglebius        case OPARG_TYPE_STRING:      atyp = at->pzStr;  break;
1055290001Sglebius        case OPARG_TYPE_TIME:        atyp = at->pzTime; break;
1056290001Sglebius        default:                     goto bogus_desc;
1057181834Sroberto        }
1058181834Sroberto
1059290001Sglebius#ifdef _WIN32
1060290001Sglebius        if (at->pzOptFmt == zGnuOptFmt)
1061290001Sglebius            snprintf(z, sizeof(z), "--%s%s", od->pz_Name, atyp);
1062290001Sglebius        else if (at->pzOptFmt == zGnuOptFmt + 2)
1063290001Sglebius            snprintf(z, sizeof(z), "%s%s", od->pz_Name, atyp);
1064290001Sglebius        else
1065290001Sglebius#endif
1066290001Sglebius        snprintf(z, sizeof(z), at->pzOptFmt, atyp, od->pz_Name,
1067290001Sglebius                 (od->optMinCt != 0) ? at->pzReq : at->pzOpt);
1068181834Sroberto
1069290001Sglebius        fprintf(option_usage_fp, line_fmt_buf, z, od->pzText);
1070181834Sroberto
1071290001Sglebius        switch (OPTST_GET_ARGTYPE(od->fOptState)) {
1072181834Sroberto        case OPARG_TYPE_ENUMERATION:
1073181834Sroberto        case OPARG_TYPE_MEMBERSHIP:
1074290001Sglebius            displayEnum = (od->pOptProc != NULL) ? true : displayEnum;
1075181834Sroberto        }
1076181834Sroberto    }
1077290001Sglebius
1078181834Sroberto    return;
1079181834Sroberto
1080181834Sroberto bogus_desc:
1081290001Sglebius    fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name);
1082290001Sglebius    option_exits(EX_SOFTWARE);
1083181834Sroberto}
1084181834Sroberto
1085290001Sglebius/**
1086181834Sroberto *  Print out the usage information for just the options.
1087181834Sroberto */
1088181834Srobertostatic void
1089290001Sglebiusprt_opt_usage(tOptions * opts, int ex_code, char const * title)
1090181834Sroberto{
1091290001Sglebius    int         ct     = opts->optCt;
1092290001Sglebius    int         optNo  = 0;
1093290001Sglebius    tOptDesc *  od     = opts->pOptDesc;
1094290001Sglebius    int         docCt  = 0;
1095181834Sroberto
1096181834Sroberto    do  {
1097290001Sglebius        /*
1098290001Sglebius         * no usage --> disallowed on command line (OPTST_NO_COMMAND), or
1099290001Sglebius         * deprecated -- strongly discouraged (OPTST_DEPRECATED), or
1100290001Sglebius         * compiled out of current object code (OPTST_OMITTED)
1101290001Sglebius         */
1102290001Sglebius        if ((od->fOptState & OPTST_NO_USAGE_MASK) != 0) {
1103290001Sglebius
1104290001Sglebius            /*
1105290001Sglebius             * IF      this is a compiled-out option
1106290001Sglebius             *   *AND* usage was requested with "omitted-usage"
1107290001Sglebius             *   *AND* this is NOT abbreviated usage
1108290001Sglebius             * THEN display this option.
1109290001Sglebius             */
1110290001Sglebius            if (  (od->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
1111290001Sglebius               && (od->pz_Name != NULL)
1112290001Sglebius               && (ex_code == EXIT_SUCCESS))  {
1113290001Sglebius
1114290001Sglebius                char const * why_pz =
1115290001Sglebius                    (od->pzText == NULL) ? zDisabledWhy : od->pzText;
1116290001Sglebius                prt_preamble(opts, od, &argTypes);
1117290001Sglebius                fprintf(option_usage_fp, zDisabledOpt, od->pz_Name, why_pz);
1118290001Sglebius            }
1119290001Sglebius
1120181834Sroberto            continue;
1121290001Sglebius        }
1122181834Sroberto
1123290001Sglebius        if ((od->fOptState & OPTST_DOCUMENT) != 0) {
1124181834Sroberto            if (ex_code == EXIT_SUCCESS) {
1125290001Sglebius                fprintf(option_usage_fp, argTypes.pzBrk, od->pzText,
1126290001Sglebius                        title);
1127181834Sroberto                docCt++;
1128181834Sroberto            }
1129181834Sroberto
1130181834Sroberto            continue;
1131181834Sroberto        }
1132181834Sroberto
1133290001Sglebius        /* Skip name only options when we have a vendor option */
1134290001Sglebius        if (  ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0)
1135290001Sglebius           && (! IS_GRAPHIC_CHAR(od->optValue)))
1136290001Sglebius            continue;
1137290001Sglebius
1138181834Sroberto        /*
1139181834Sroberto         *  IF       this is the first auto-opt maintained option
1140181834Sroberto         *    *AND*  we are doing a full help
1141181834Sroberto         *    *AND*  there are documentation options
1142181834Sroberto         *    *AND*  the last one was not a doc option,
1143181834Sroberto         *  THEN document that the remaining options are not user opts
1144181834Sroberto         */
1145290001Sglebius        if ((docCt > 0) && (ex_code == EXIT_SUCCESS)) {
1146290001Sglebius            if (opts->presetOptCt == optNo) {
1147290001Sglebius                if ((od[-1].fOptState & OPTST_DOCUMENT) == 0)
1148290001Sglebius                    fprintf(option_usage_fp, argTypes.pzBrk, zAuto, title);
1149181834Sroberto
1150290001Sglebius            } else if ((ct == 1) &&
1151290001Sglebius                       (opts->fOptSet & OPTPROC_VENDOR_OPT))
1152290001Sglebius                fprintf(option_usage_fp, argTypes.pzBrk, zVendIntro, title);
1153290001Sglebius        }
1154181834Sroberto
1155290001Sglebius        prt_one_usage(opts, od, &argTypes);
1156290001Sglebius
1157181834Sroberto        /*
1158181834Sroberto         *  IF we were invoked because of the --help option,
1159181834Sroberto         *  THEN print all the extra info
1160181834Sroberto         */
1161181834Sroberto        if (ex_code == EXIT_SUCCESS)
1162290001Sglebius            prt_extd_usage(opts, od, title);
1163181834Sroberto
1164290001Sglebius    } while (od++, optNo++, (--ct > 0));
1165181834Sroberto
1166290001Sglebius    fputc(NL, option_usage_fp);
1167181834Sroberto}
1168181834Sroberto
1169181834Sroberto
1170290001Sglebius/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1171290001Sglebius/**
1172290001Sglebius *  Print program details.
1173290001Sglebius * @param[in] opts  the program option descriptor
1174181834Sroberto */
1175181834Srobertostatic void
1176290001Sglebiusprt_prog_detail(tOptions * opts)
1177181834Sroberto{
1178290001Sglebius    bool need_intro = (opts->papzHomeList == NULL);
1179181834Sroberto
1180181834Sroberto    /*
1181290001Sglebius     *  Display all the places we look for config files, if we have
1182290001Sglebius     *  a list of directories to search.
1183181834Sroberto     */
1184290001Sglebius    if (! need_intro)
1185290001Sglebius        prt_ini_list(opts->papzHomeList, opts->pzRcName, opts->pzProgPath);
1186181834Sroberto
1187181834Sroberto    /*
1188181834Sroberto     *  Let the user know about environment variable settings
1189181834Sroberto     */
1190290001Sglebius    if ((opts->fOptSet & OPTPROC_ENVIRON) != 0) {
1191290001Sglebius        if (need_intro)
1192290001Sglebius            fputs(zPresetIntro, option_usage_fp);
1193181834Sroberto
1194290001Sglebius        fprintf(option_usage_fp, zExamineFmt, opts->pzPROGNAME);
1195181834Sroberto    }
1196181834Sroberto
1197181834Sroberto    /*
1198181834Sroberto     *  IF we found an enumeration,
1199181834Sroberto     *  THEN hunt for it again.  Call the handler proc with a NULL
1200181834Sroberto     *       option struct pointer.  That tells it to display the keywords.
1201181834Sroberto     */
1202181834Sroberto    if (displayEnum) {
1203290001Sglebius        int        ct     = opts->optCt;
1204181834Sroberto        int        optNo  = 0;
1205290001Sglebius        tOptDesc * od     = opts->pOptDesc;
1206181834Sroberto
1207290001Sglebius        fputc(NL, option_usage_fp);
1208290001Sglebius        fflush(option_usage_fp);
1209181834Sroberto        do  {
1210290001Sglebius            switch (OPTST_GET_ARGTYPE(od->fOptState)) {
1211181834Sroberto            case OPARG_TYPE_ENUMERATION:
1212181834Sroberto            case OPARG_TYPE_MEMBERSHIP:
1213290001Sglebius                (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
1214181834Sroberto            }
1215290001Sglebius        } while (od++, optNo++, (--ct > 0));
1216181834Sroberto    }
1217181834Sroberto
1218181834Sroberto    /*
1219181834Sroberto     *  If there is a detail string, now is the time for that.
1220181834Sroberto     */
1221290001Sglebius    if (opts->pzDetail != NULL)
1222290001Sglebius        fputs(opts->pzDetail, option_usage_fp);
1223181834Sroberto}
1224181834Sroberto
1225181834Sroberto
1226181834Sroberto/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1227181834Sroberto *
1228181834Sroberto *   OPTION LINE FORMATTING SETUP
1229181834Sroberto *
1230181834Sroberto *  The "OptFmt" formats receive three arguments:
1231181834Sroberto *  1.  the type of the option's argument
1232181834Sroberto *  2.  the long name of the option
1233181834Sroberto *  3.  "YES" or "no ", depending on whether or not the option must appear
1234181834Sroberto *      on the command line.
1235181834Sroberto *  These formats are used immediately after the option flag (if used) has
1236181834Sroberto *  been printed.
1237181834Sroberto *
1238181834Sroberto *  Set up the formatting for GNU-style output
1239181834Sroberto */
1240181834Srobertostatic int
1241290001SglebiussetGnuOptFmts(tOptions * opts, char const ** ptxt)
1242181834Sroberto{
1243290001Sglebius    static char const zOneSpace[] = " ";
1244181834Sroberto    int  flen = 22;
1245290001Sglebius    *ptxt = zNoRq_ShrtTtl;
1246181834Sroberto
1247181834Sroberto    argTypes.pzStr  = zGnuStrArg;
1248181834Sroberto    argTypes.pzReq  = zOneSpace;
1249181834Sroberto    argTypes.pzNum  = zGnuNumArg;
1250181834Sroberto    argTypes.pzKey  = zGnuKeyArg;
1251181834Sroberto    argTypes.pzKeyL = zGnuKeyLArg;
1252290001Sglebius    argTypes.pzTime = zGnuTimeArg;
1253290001Sglebius    argTypes.pzFile = zGnuFileArg;
1254181834Sroberto    argTypes.pzBool = zGnuBoolArg;
1255181834Sroberto    argTypes.pzNest = zGnuNestArg;
1256181834Sroberto    argTypes.pzOpt  = zGnuOptArg;
1257181834Sroberto    argTypes.pzNo   = zOneSpace;
1258181834Sroberto    argTypes.pzBrk  = zGnuBreak;
1259181834Sroberto    argTypes.pzNoF  = zSixSpaces;
1260181834Sroberto    argTypes.pzSpc  = zThreeSpaces;
1261181834Sroberto
1262290001Sglebius    switch (opts->fOptSet & OPTPROC_L_N_S) {
1263181834Sroberto    case OPTPROC_L_N_S:    argTypes.pzOptFmt = zGnuOptFmt;     break;
1264181834Sroberto    case OPTPROC_LONGOPT:  argTypes.pzOptFmt = zGnuOptFmt;     break;
1265181834Sroberto    case 0:                argTypes.pzOptFmt = zGnuOptFmt + 2; break;
1266181834Sroberto    case OPTPROC_SHORTOPT:
1267181834Sroberto        argTypes.pzOptFmt = zShrtGnuOptFmt;
1268181834Sroberto        zGnuStrArg[0] = zGnuNumArg[0] = zGnuKeyArg[0] = zGnuBoolArg[0] = ' ';
1269181834Sroberto        argTypes.pzOpt = " [arg]";
1270181834Sroberto        flen = 8;
1271181834Sroberto        break;
1272181834Sroberto    }
1273181834Sroberto
1274181834Sroberto    return flen;
1275181834Sroberto}
1276181834Sroberto
1277181834Sroberto
1278181834Sroberto/*
1279181834Sroberto *  Standard (AutoOpts normal) option line formatting
1280181834Sroberto */
1281181834Srobertostatic int
1282290001SglebiussetStdOptFmts(tOptions * opts, char const ** ptxt)
1283181834Sroberto{
1284181834Sroberto    int  flen = 0;
1285181834Sroberto
1286181834Sroberto    argTypes.pzStr  = zStdStrArg;
1287181834Sroberto    argTypes.pzReq  = zStdReqArg;
1288181834Sroberto    argTypes.pzNum  = zStdNumArg;
1289181834Sroberto    argTypes.pzKey  = zStdKeyArg;
1290181834Sroberto    argTypes.pzKeyL = zStdKeyLArg;
1291290001Sglebius    argTypes.pzTime = zStdTimeArg;
1292290001Sglebius    argTypes.pzFile = zStdFileArg;
1293181834Sroberto    argTypes.pzBool = zStdBoolArg;
1294181834Sroberto    argTypes.pzNest = zStdNestArg;
1295181834Sroberto    argTypes.pzOpt  = zStdOptArg;
1296181834Sroberto    argTypes.pzNo   = zStdNoArg;
1297181834Sroberto    argTypes.pzBrk  = zStdBreak;
1298181834Sroberto    argTypes.pzNoF  = zFiveSpaces;
1299181834Sroberto    argTypes.pzSpc  = zTwoSpaces;
1300181834Sroberto
1301290001Sglebius    switch (opts->fOptSet & (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT)) {
1302181834Sroberto    case (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT):
1303290001Sglebius        *ptxt = zNoRq_ShrtTtl;
1304181834Sroberto        argTypes.pzOptFmt = zNrmOptFmt;
1305181834Sroberto        flen = 19;
1306181834Sroberto        break;
1307181834Sroberto
1308181834Sroberto    case OPTPROC_NO_REQ_OPT:
1309290001Sglebius        *ptxt = zNoRq_NoShrtTtl;
1310181834Sroberto        argTypes.pzOptFmt = zNrmOptFmt;
1311181834Sroberto        flen = 19;
1312181834Sroberto        break;
1313181834Sroberto
1314181834Sroberto    case OPTPROC_SHORTOPT:
1315290001Sglebius        *ptxt = zReq_ShrtTtl;
1316181834Sroberto        argTypes.pzOptFmt = zReqOptFmt;
1317181834Sroberto        flen = 24;
1318181834Sroberto        break;
1319181834Sroberto
1320181834Sroberto    case 0:
1321290001Sglebius        *ptxt = zReq_NoShrtTtl;
1322181834Sroberto        argTypes.pzOptFmt = zReqOptFmt;
1323181834Sroberto        flen = 24;
1324181834Sroberto    }
1325181834Sroberto
1326181834Sroberto    return flen;
1327181834Sroberto}
1328181834Sroberto
1329290001Sglebius/** @}
1330290001Sglebius *
1331181834Sroberto * Local Variables:
1332181834Sroberto * mode: C
1333181834Sroberto * c-file-style: "stroustrup"
1334181834Sroberto * indent-tabs-mode: nil
1335181834Sroberto * End:
1336181834Sroberto * end of autoopts/usage.c */
1337