X-Git-Url: https://git.draconx.ca/gitweb/dxcommon.git/blobdiff_plain/03a26752c80546ac8cf8fc81807bb5a153786599..HEAD:/src/help.c diff --git a/src/help.c b/src/help.c index 43bb266..2e6043b 100644 --- a/src/help.c +++ b/src/help.c @@ -1,5 +1,5 @@ /* - * Copyright © 2021-2022 Nick Bowler + * Copyright © 2021-2024 Nick Bowler * * Helper functions for formatting --help program output. * @@ -16,6 +16,7 @@ # include #endif #include +#include #include #include #include @@ -26,6 +27,9 @@ #ifndef ENABLE_NLS # define ENABLE_NLS 0 #endif +#ifndef HELP_GETOPT_LONG_ONLY +# define HELP_GETOPT_LONG_ONLY 0 +#endif #if ENABLE_NLS # include @@ -37,7 +41,8 @@ # define mbsnwidth(a, b, c) (assert(0), 0) #endif -#define _(s) gettext(s) +#define STR_L10N_(x) +#define N_(s) s /* Returns a single numeric value depending on the type of option: * @@ -53,8 +58,10 @@ static int option_type(const struct option *opt) { int ret = opt->has_arg & 3; +#if !HELP_GETOPT_LONG_ONLY if (!opt->flag) ret |= (opt->val <= CHAR_MAX) << 2; +#endif return ret; } @@ -65,71 +72,104 @@ enum { OPT_SHORT_WITHOUT_ARG = 4, OPT_LONG_WITH_OPTIONAL_ARG = 2, OPT_LONG_WITH_MANDATORY_ARG = 1, - OPT_LONG_WITHOUT_ARG = 0, + OPT_LONG_WITHOUT_ARG = 0 +}; + +#if HELP_GETOPT_LONG_ONLY +/* String table generated by gen-strtab.awk from help-glo.str */ +#define STRTAB_INITIALIZER \ + N_(" -%s [%s]") \ + "\0" "%*s%.*s\n" \ + "\0" N_(" -%s %s") \ + "\0" N_(" -%s") \ + "" +enum { + fmt_long_optional_arg = 0, + fmt_long_mandatory_arg = 19, + fmt_long_without_arg = 27, + fmt_desc = 10, + fmt_nl = 17 +}; +#else +/* String table generated by gen-strtab.awk from help-std.str */ +#define STRTAB_INITIALIZER \ + STR_L10N_(N_(" --%s[=%s]")) \ + STR_L10N_(N_(" --%s=%s")) \ + STR_L10N_(N_(" --%s")) \ + N_(" -%c, --%s[=%s]") \ + "\0" N_(" -%c, --%s=%s") \ + "\0" N_(" -%c, --%s") \ + "\0" "%*s%.*s\n" \ + "" +enum { + fmt_short_optional_arg = 0, + fmt_long_optional_arg = 5, + fmt_short_mandatory_arg = 16, + fmt_long_mandatory_arg = 21, + fmt_short_without_arg = 30, + fmt_long_without_arg = 35, + fmt_desc = 41, + fmt_nl = 48 }; +#endif + +static const struct optstr { + unsigned char map[HELP_GETOPT_LONG_ONLY ? 3 : 7]; + char tab[sizeof (STRTAB_INITIALIZER)]; +} optstr = { + { + offsetof(struct optstr, tab) + fmt_long_without_arg, + offsetof(struct optstr, tab) + fmt_long_mandatory_arg, + offsetof(struct optstr, tab) + fmt_long_optional_arg, +#if !HELP_GETOPT_LONG_ONLY + 0, + offsetof(struct optstr, tab) + fmt_short_without_arg, + offsetof(struct optstr, tab) + fmt_short_mandatory_arg, + offsetof(struct optstr, tab) + fmt_short_optional_arg, +#endif + }, + STRTAB_INITIALIZER +}; + +/* + * When NLS is enabled, we assume snprintf is available. The GNU libintl + * library should provide a suitable fallback if necessary. + */ +#if ENABLE_NLS && !defined(help_do_snprintf) +#define help_do_snprintf snprintf +#elif !ENABLE_NLS +#define help_do_snprintf fake_snprintf +static inline int fake_snprintf(char *buf, size_t n, const char *fmt, ...) +{ + return -1; +} +#endif + +#define has_short_char(type) (!HELP_GETOPT_LONG_ONLY && (type & 4)) int help_print_optstring(const struct option *opt, const char *argname, int l) { - char optstring[100]; + int type = option_type(opt); + const char *fmt, *nls_arg; + char optstring[256]; int w; + fmt = (char *)&optstr + optstr.map[type]; if (!ENABLE_NLS) goto no_translate; - switch (option_type(opt)) { -#if HELP_GETOPT_LONG_ONLY - case OPT_SHORT_WITH_OPTIONAL_ARG: - case OPT_LONG_WITH_OPTIONAL_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%s [%s]"), opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_SHORT_WITH_MANDATORY_ARG: - case OPT_LONG_WITH_MANDATORY_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%s %s"), opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_SHORT_WITHOUT_ARG: - case OPT_LONG_WITHOUT_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%s"), opt->name); - break; -#else - case OPT_SHORT_WITH_OPTIONAL_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%c, --%s[=%s]"), opt->val, opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_LONG_WITH_OPTIONAL_ARG: - w = snprintf(optstring, sizeof optstring, - _(" --%s[=%s]"), opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_SHORT_WITH_MANDATORY_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%c, --%s=%s"), opt->val, opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_LONG_WITH_MANDATORY_ARG: - w = snprintf(optstring, sizeof optstring, - _(" --%s=%s"), opt->name, - pgettext_expr(opt->name, argname)); - break; - case OPT_SHORT_WITHOUT_ARG: - w = snprintf(optstring, sizeof optstring, - _(" -%c, --%s"), opt->val, opt->name); - break; - case OPT_LONG_WITHOUT_ARG: - w = snprintf(optstring, sizeof optstring, - _(" --%s"), opt->name); - break; -#endif - default: - assert(0); + nls_arg = argname ? pgettext_expr(opt->name, argname) : argname; + if (has_short_char(type)) { + w = help_do_snprintf(optstring, sizeof optstring, + gettext(fmt), opt->val, opt->name, + nls_arg); + } else { + w = help_do_snprintf(optstring, sizeof optstring, + gettext(fmt), opt->name, + nls_arg); } - if (w < 0) + if (w < 0 || w >= sizeof optstring) goto no_translate; w = mbsnwidth(optstring, w, 0); @@ -137,45 +177,13 @@ int help_print_optstring(const struct option *opt, const char *argname, int l) goto out; no_translate: - switch (option_type(opt)) { -#if HELP_GETOPT_LONG_ONLY - case OPT_SHORT_WITH_OPTIONAL_ARG: - case OPT_LONG_WITH_OPTIONAL_ARG: - w = printf(" -%s [%s]", opt->name, argname); - break; - case OPT_SHORT_WITH_MANDATORY_ARG: - case OPT_LONG_WITH_MANDATORY_ARG: - w = printf(" -%s %s", opt->name, argname); - break; - case OPT_SHORT_WITHOUT_ARG: - case OPT_LONG_WITHOUT_ARG: - w = printf(" -%s", opt->name); - break; -#else - case OPT_SHORT_WITH_OPTIONAL_ARG: - w = printf(" -%c, --%s[=%s]", opt->val, opt->name, argname); - break; - case OPT_LONG_WITH_OPTIONAL_ARG: - w = printf(" --%s[=%s]", opt->name, argname); - break; - case OPT_SHORT_WITH_MANDATORY_ARG: - w = printf(" -%c, --%s=%s", opt->val, opt->name, argname); - break; - case OPT_LONG_WITH_MANDATORY_ARG: - w = printf(" --%s=%s", opt->name, argname); - break; - case OPT_SHORT_WITHOUT_ARG: - w = printf(" -%c, --%s", opt->val, opt->name); - break; - case OPT_LONG_WITHOUT_ARG: - w = printf(" --%s", opt->name); - break; -#endif - default: - assert(0); + if (has_short_char(type)) { + w = printf(fmt, opt->val, opt->name, argname); + } else { + w = printf(fmt, opt->name, argname); } out: - if (w < 0 || w > l) { + if (w <= 0 || w > l) { putchar('\n'); return 0; } @@ -188,14 +196,11 @@ void help_print_desc(const struct option *opt, const char *s, int i, int w) if (opt) s = pgettext_expr(opt->name, s); - for (; *s; w = 0) { - const char *nl = strchr(s, '\n'); - int n = (nl ? nl-s : -1); - - printf("%*s%.*s\n", i-w, "", n, s); - if (!nl) - break; + do { + size_t n = strcspn(s, &optstr.tab[fmt_nl]); - s = nl+1; - } + printf(&optstr.tab[fmt_desc], n ? i-w : 0, "", (int)n, s); + s += n + (s[n] != '\0'); + w = 0; + } while (*s); }