2 * Copyright © 2021-2024 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');