]> git.draconx.ca Git - dxcommon.git/blobdiff - src/help.c
Add common option formatting routines.
[dxcommon.git] / src / help.c
diff --git a/src/help.c b/src/help.c
new file mode 100644 (file)
index 0000000..0ceb8f4
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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;
+       }
+}