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