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