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