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