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