/* * Copyright © 2021-2022 Nick Bowler * * Helper functions for formatting --help program output. * * In order to support localized output, this depends on the Gnulib gettext-h * and mbswidth modules. However, if ENABLE_NLS is not defined (or defined to * 0) then these modules are not required. * * License WTFPL2: Do What The Fuck You Want To Public License, version 2. * This is free software: you are free to do what the fuck you want to. * There is NO WARRANTY, to the extent permitted by law. */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include #include "help.h" #ifndef ENABLE_NLS # define ENABLE_NLS 0 #endif #if ENABLE_NLS # include # include # include #else # define gettext(s) (s) # define pgettext_expr(c, s) (s) # define mbsnwidth(a, b, c) (assert(0), 0) #endif #define _(s) gettext(s) /* Returns a single numeric value depending on the type of option: * * 6 - if the option has a short option character and an optional argument * 5 - if the option has a short option character and a mandatory argument * 4 - if the option has a short option charater and no argument * 3 - N/A * 2 - if the option has no short option character and an optional argument * 1 - if the option has no short option character and a mandatory argument * 0 - if the option has no short option character and no argument */ static int option_type(const struct option *opt) { int ret = opt->has_arg & 3; if (!opt->flag) ret |= (opt->val <= CHAR_MAX) << 2; return ret; } enum { OPT_SHORT_WITH_OPTIONAL_ARG = 6, OPT_SHORT_WITH_MANDATORY_ARG = 5, OPT_SHORT_WITHOUT_ARG = 4, OPT_LONG_WITH_OPTIONAL_ARG = 2, OPT_LONG_WITH_MANDATORY_ARG = 1, OPT_LONG_WITHOUT_ARG = 0, }; int help_print_optstring(const struct option *opt, const char *argname, int l) { char optstring[100]; int w; 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); } if (w < 0) goto no_translate; w = mbsnwidth(optstring, w, 0); printf("%s", optstring); 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); } out: if (w < 0 || w > l) { putchar('\n'); return 0; } return w; } 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; s = nl+1; } }