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