]> git.draconx.ca Git - dxcommon.git/blob - src/help.c
help: Add a single-dash long option mode.
[dxcommon.git] / src / help.c
1 /*
2  * Copyright © 2021 Nick Bowler
3  *
4  * Helper functions for formatting --help program output.
5  *
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.
9  *
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.
13  */
14
15 #if HAVE_CONFIG_H
16 #       include <config.h>
17 #endif
18 #include <stdio.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <limits.h>
22 #include <getopt.h>
23
24 #include "help.h"
25
26 #ifndef ENABLE_NLS
27 #       define ENABLE_NLS 0
28 #endif
29
30 #if ENABLE_NLS
31 #       include <gettext.h>
32 #       include <mbswidth.h>
33 #else
34 #       define gettext(s) (s)
35 #       define pgettext_expr(c, s) (s)
36 #       define mbsnwidth(a, b, c) (assert(0), 0)
37 #endif
38
39 #define _(s) gettext(s)
40
41 /* Returns a single numeric value depending on the type of option:
42  *
43  *   6 - if the option has a short option character and an optional argument
44  *   5 - if the option has a short option character and a mandatory argument
45  *   4 - if the option has a short option charater and no argument
46  *   3 - N/A
47  *   2 - if the option has no short option character and an optional argument
48  *   1 - if the option has no short option character and a mandatory argument
49  *   0 - if the option has no short option character and no argument
50  */
51 static int option_type(const struct option *opt)
52 {
53         return ((opt->val <= CHAR_MAX) << 2) | (opt->has_arg & 3);
54 }
55
56 enum {
57         OPT_SHORT_WITH_OPTIONAL_ARG  = 6,
58         OPT_SHORT_WITH_MANDATORY_ARG = 5,
59         OPT_SHORT_WITHOUT_ARG        = 4,
60         OPT_LONG_WITH_OPTIONAL_ARG   = 2,
61         OPT_LONG_WITH_MANDATORY_ARG  = 1,
62         OPT_LONG_WITHOUT_ARG         = 0,
63 };
64
65 int help_print_optstring(const struct option *opt, const char *argname, int l)
66 {
67         char optstring[100];
68         int w;
69
70         if (!ENABLE_NLS)
71                 goto no_translate;
72
73         switch (option_type(opt)) {
74 #if HELP_GETOPT_LONG_ONLY
75         case OPT_SHORT_WITH_OPTIONAL_ARG:
76         case OPT_LONG_WITH_OPTIONAL_ARG:
77                 w = snprintf(optstring, sizeof optstring,
78                              _("  -%s [%s]"), opt->name,
79                              pgettext_expr(opt->name, argname));
80                 break;
81         case OPT_SHORT_WITH_MANDATORY_ARG:
82         case OPT_LONG_WITH_MANDATORY_ARG:
83                 w = snprintf(optstring, sizeof optstring,
84                              _("  -%s %s"), opt->name,
85                              pgettext_expr(opt->name, argname));
86                 break;
87         case OPT_SHORT_WITHOUT_ARG:
88         case OPT_LONG_WITHOUT_ARG:
89                 w = snprintf(optstring, sizeof optstring,
90                              _("  -%s"), opt->name);
91                 break;
92 #else
93         case OPT_SHORT_WITH_OPTIONAL_ARG:
94                 w = snprintf(optstring, sizeof optstring,
95                              _("  -%c, --%s[=%s]"), opt->val, opt->name,
96                              pgettext_expr(opt->name, argname));
97                 break;
98         case OPT_LONG_WITH_OPTIONAL_ARG:
99                 w = snprintf(optstring, sizeof optstring,
100                              _("  --%s[=%s]"), opt->name,
101                              pgettext_expr(opt->name, argname));
102                 break;
103         case OPT_SHORT_WITH_MANDATORY_ARG:
104                 w = snprintf(optstring, sizeof optstring,
105                              _("  -%c, --%s=%s"), opt->val, opt->name,
106                              pgettext_expr(opt->name, argname));
107                 break;
108         case OPT_LONG_WITH_MANDATORY_ARG:
109                 w = snprintf(optstring, sizeof optstring,
110                              _("  --%s=%s"), opt->name,
111                              pgettext_expr(opt->name, argname));
112                 break;
113         case OPT_SHORT_WITHOUT_ARG:
114                 w = snprintf(optstring, sizeof optstring,
115                              _("  -%c, --%s"), opt->val, opt->name);
116                 break;
117         case OPT_LONG_WITHOUT_ARG:
118                 w = snprintf(optstring, sizeof optstring,
119                              _("  --%s"), opt->name);
120                 break;
121 #endif
122         default:
123                 assert(0);
124         }
125
126         if (w < 0)
127                 goto no_translate;
128
129         w = mbsnwidth(optstring, w, 0);
130         printf("%s", optstring);
131         goto out;
132
133 no_translate:
134         switch (option_type(opt)) {
135 #if HELP_GETOPT_LONG_ONLY
136         case OPT_SHORT_WITH_OPTIONAL_ARG:
137         case OPT_LONG_WITH_OPTIONAL_ARG:
138                 w = printf("  -%s [%s]", opt->name, argname);
139                 break;
140         case OPT_SHORT_WITH_MANDATORY_ARG:
141         case OPT_LONG_WITH_MANDATORY_ARG:
142                 w = printf("  -%s %s", opt->name, argname);
143                 break;
144         case OPT_SHORT_WITHOUT_ARG:
145         case OPT_LONG_WITHOUT_ARG:
146                 w = printf("  -%s", opt->name);
147                 break;
148 #else
149         case OPT_SHORT_WITH_OPTIONAL_ARG:
150                 w = printf("  -%c, --%s[=%s]", opt->val, opt->name, argname);
151                 break;
152         case OPT_LONG_WITH_OPTIONAL_ARG:
153                 w = printf("  --%s[=%s]", opt->name, argname);
154                 break;
155         case OPT_SHORT_WITH_MANDATORY_ARG:
156                 w = printf("  -%c, --%s=%s", opt->val, opt->name, argname);
157                 break;
158         case OPT_LONG_WITH_MANDATORY_ARG:
159                 w = printf("  --%s=%s", opt->name, argname);
160                 break;
161         case OPT_SHORT_WITHOUT_ARG:
162                 w = printf("  -%c, --%s", opt->val, opt->name);
163                 break;
164         case OPT_LONG_WITHOUT_ARG:
165                 w = printf("  --%s", opt->name);
166                 break;
167 #endif
168         default:
169                 assert(0);
170         }
171 out:
172         if (w < 0 || w > l) {
173                 putchar('\n');
174                 return 0;
175         }
176
177         return w;
178 }
179
180 void help_print_desc(const struct option *opt, const char *s, int i, int w)
181 {
182         for (s = pgettext_expr(opt->name, s); *s; w = 0) {
183                 const char *nl = strchr(s, '\n');
184                 int n = (nl ? nl-s : -1);
185
186                 printf("%*s%.*s\n", i-w, "", n, s);
187                 if (!nl)
188                         break;
189
190                 s = nl+1;
191         }
192 }