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 \
87 fmt_long_optional_arg = 0,
88 fmt_long_mandatory_arg = 19,
89 fmt_long_without_arg = 27,
94 /* String table generated by gen-strtab.awk from help-std.str */
95 #define STRTAB_INITIALIZER \
96 STR_L10N_(N_(" --%s[=%s]")) \
97 STR_L10N_(N_(" --%s=%s")) \
98 STR_L10N_(N_(" --%s")) \
99 N_(" -%c, --%s[=%s]") \
100 "\0" N_(" -%c, --%s=%s") \
101 "\0" N_(" -%c, --%s") \
105 fmt_short_optional_arg = 0,
106 fmt_long_optional_arg = 5,
107 fmt_short_mandatory_arg = 16,
108 fmt_long_mandatory_arg = 21,
109 fmt_short_without_arg = 30,
110 fmt_long_without_arg = 35,
116 static const struct optstr {
117 unsigned char map[HELP_GETOPT_LONG_ONLY ? 3 : 7];
118 char tab[sizeof (STRTAB_INITIALIZER)];
121 offsetof(struct optstr, tab) + fmt_long_without_arg,
122 offsetof(struct optstr, tab) + fmt_long_mandatory_arg,
123 offsetof(struct optstr, tab) + fmt_long_optional_arg,
124 #if !HELP_GETOPT_LONG_ONLY
126 offsetof(struct optstr, tab) + fmt_short_without_arg,
127 offsetof(struct optstr, tab) + fmt_short_mandatory_arg,
128 offsetof(struct optstr, tab) + fmt_short_optional_arg,
135 * When NLS is enabled, we assume snprintf is available. The GNU libintl
136 * library should provide a suitable fallback if necessary.
138 #if ENABLE_NLS && !defined(help_do_snprintf)
139 #define help_do_snprintf snprintf
141 #define help_do_snprintf fake_snprintf
142 static inline int fake_snprintf(char *buf, size_t n, const char *fmt, ...)
148 #define has_short_char(type) (!HELP_GETOPT_LONG_ONLY && (type & 4))
150 int help_print_optstring(const struct option *opt, const char *argname, int l)
152 int type = option_type(opt);
153 const char *fmt, *nls_arg;
157 fmt = (char *)&optstr + optstr.map[type];
161 nls_arg = argname ? pgettext_expr(opt->name, argname) : argname;
162 if (has_short_char(type)) {
163 w = help_do_snprintf(optstring, sizeof optstring,
164 gettext(fmt), opt->val, opt->name,
167 w = help_do_snprintf(optstring, sizeof optstring,
168 gettext(fmt), opt->name,
172 if (w < 0 || w >= sizeof optstring)
175 w = mbsnwidth(optstring, w, 0);
176 printf("%s", optstring);
180 if (has_short_char(type)) {
181 w = printf(fmt, opt->val, opt->name, argname);
183 w = printf(fmt, opt->name, argname);
186 if (w <= 0 || w > l) {
194 void help_print_desc(const struct option *opt, const char *s, int i, int w)
197 s = pgettext_expr(opt->name, s);
200 size_t n = strcspn(s, &optstr.tab[fmt_nl]);
202 printf(&optstr.tab[fmt_desc], n ? i-w : 0, "", (int)n, s);
203 s += n + (s[n] != '\0');