2 * Copyright © 2021-2023 Nick Bowler
4 * Helper functions for formatting --help program output.
6 * In order to support localized output, this depends on the Gnulib gettext-h
7 * and mbswidth modules. However, if ENABLE_NLS is not defined (or defined to
8 * 0) then these modules are not required.
10 * License WTFPL2: Do What The Fuck You Want To Public License, version 2.
11 * This is free software: you are free to do what the fuck you want to.
12 * There is NO WARRANTY, to the extent permitted by law.
30 #ifndef HELP_GETOPT_LONG_ONLY
31 # define HELP_GETOPT_LONG_ONLY 0
37 # include <mbswidth.h>
39 # define gettext(s) (s)
40 # define pgettext_expr(c, s) (s)
41 # define mbsnwidth(a, b, c) (assert(0), 0)
47 /* Returns a single numeric value depending on the type of option:
49 * 6 - if the option has a short option character and an optional argument
50 * 5 - if the option has a short option character and a mandatory argument
51 * 4 - if the option has a short option charater and no argument
53 * 2 - if the option has no short option character and an optional argument
54 * 1 - if the option has no short option character and a mandatory argument
55 * 0 - if the option has no short option character and no argument
57 static int option_type(const struct option *opt)
59 int ret = opt->has_arg & 3;
61 #if !HELP_GETOPT_LONG_ONLY
63 ret |= (opt->val <= CHAR_MAX) << 2;
70 OPT_SHORT_WITH_OPTIONAL_ARG = 6,
71 OPT_SHORT_WITH_MANDATORY_ARG = 5,
72 OPT_SHORT_WITHOUT_ARG = 4,
73 OPT_LONG_WITH_OPTIONAL_ARG = 2,
74 OPT_LONG_WITH_MANDATORY_ARG = 1,
75 OPT_LONG_WITHOUT_ARG = 0,
78 #if HELP_GETOPT_LONG_ONLY
79 /* String table generated by gen-strtab.awk from help-glo.str */
80 #define STRTAB_INITIALIZER \
86 fmt_long_optional_arg = 0,
87 fmt_long_mandatory_arg = 11,
88 fmt_long_without_arg = 20
91 /* String table generated by gen-strtab.awk from help-std.str */
92 #define STRTAB_INITIALIZER \
93 N_(" -%c, --%s[=%s]") \
94 "\0" N_(" -%c, --%s=%s") \
95 "\0" N_(" --%s[=%s]") \
96 "\0" N_(" -%c, --%s") \
101 fmt_short_optional_arg = 0,
102 fmt_long_optional_arg = 32,
103 fmt_short_mandatory_arg = 17,
104 fmt_long_mandatory_arg = 56,
105 fmt_short_without_arg = 44,
106 fmt_long_without_arg = 66
110 static const struct optstr {
111 unsigned char map[HELP_GETOPT_LONG_ONLY ? 3 : 7];
112 char tab[sizeof (STRTAB_INITIALIZER)];
115 offsetof(struct optstr, tab) + fmt_long_without_arg,
116 offsetof(struct optstr, tab) + fmt_long_mandatory_arg,
117 offsetof(struct optstr, tab) + fmt_long_optional_arg,
118 #if !HELP_GETOPT_LONG_ONLY
120 offsetof(struct optstr, tab) + fmt_short_without_arg,
121 offsetof(struct optstr, tab) + fmt_short_mandatory_arg,
122 offsetof(struct optstr, tab) + fmt_short_optional_arg,
129 * When NLS is enabled, we assume snprintf is available. The GNU libintl
130 * library should provide a suitable fallback if necessary.
132 #if ENABLE_NLS && !defined(help_do_snprintf)
133 #define help_do_snprintf snprintf
135 #define help_do_snprintf fake_snprintf
136 static inline int fake_snprintf(char *buf, size_t n, const char *fmt, ...)
142 #define has_short_char(type) (!HELP_GETOPT_LONG_ONLY && (type & 4))
144 int help_print_optstring(const struct option *opt, const char *argname, int l)
146 int type = option_type(opt);
147 const char *fmt, *nls_arg;
151 fmt = (char *)&optstr + optstr.map[type];
155 nls_arg = argname ? pgettext_expr(opt->name, argname) : argname;
156 if (has_short_char(type)) {
157 w = help_do_snprintf(optstring, sizeof optstring,
158 gettext(fmt), opt->val, opt->name,
161 w = help_do_snprintf(optstring, sizeof optstring,
162 gettext(fmt), opt->name,
166 if (w < 0 || w >= sizeof optstring)
169 w = mbsnwidth(optstring, w, 0);
170 printf("%s", optstring);
174 if (has_short_char(type)) {
175 w = printf(fmt, opt->val, opt->name, argname);
177 w = printf(fmt, opt->name, argname);
180 if (w < 0 || w > l) {
188 void help_print_desc(const struct option *opt, const char *s, int i, int w)
191 s = pgettext_expr(opt->name, s);
194 size_t n = strcspn(s, "\n");
196 printf("%*s%.*s\n", n ? i-w : 0, "", (int)n, s);
197 s += n + (s[n] != '\0');