+/*
+ * Copyright © 2021 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 <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "help.h"
+
+#ifndef ENABLE_NLS
+# define ENABLE_NLS 0
+#endif
+
+#if ENABLE_NLS
+# include <gettext.h>
+# include <mbswidth.h>
+#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)
+{
+ return ((opt->val <= CHAR_MAX) << 2) | (opt->has_arg & 3);
+}
+
+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)) {
+ 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;
+ 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)) {
+ 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;
+ 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)
+{
+ for (s = pgettext_expr(opt->name, s); *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;
+ }
+}