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
parse_usage_flags(ao_flag_names_t const * fnt,char const * txt)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
set_usage_flags(tOptions * opts,char const * flg_txt)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
do_gnu_usage(tOptions * pOpts)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
skip_misuse_usage(tOptions * pOpts)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
optionOnlyUsage(tOptions * pOpts,int ex_code)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
print_offer_usage(tOptions * opts)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
print_usage_details(tOptions * opts,int exit_code)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
print_one_paragraph(char const * text,bool plain,FILE * fp)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
optionPrintParagraphs(char const * text,bool plain,FILE * fp)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
optionUsage(tOptions * opts,int usage_exit_code)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
prt_conflicts(tOptions * opts,tOptDesc * od)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
prt_one_vendor(tOptions * opts,tOptDesc * od,arg_types_t * argtp,char const * usefmt)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
prt_vendor_opts(tOptions * opts,char const * title)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
prt_extd_usage(tOptions * opts,tOptDesc * od,char const * title)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
prt_ini_list(char const * const * papz,char const * ini_file,char const * path_nm)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
prt_preamble(tOptions * opts,tOptDesc * od,arg_types_t * at)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
prt_one_usage(tOptions * opts,tOptDesc * od,arg_types_t * at)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
prt_opt_usage(tOptions * opts,int ex_code,char const * title)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
prt_prog_detail(tOptions * opts)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
setGnuOptFmts(tOptions * opts,char const ** ptxt)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
setStdOptFmts(tOptions * opts,char const ** ptxt)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