]> git.draconx.ca Git - dxcommon.git/blob - src/help.c
Add a dedicated function to emit the copyright symbol.
[dxcommon.git] / src / help.c
1 /*
2  * Copyright © 2021-2023 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         int ret = opt->has_arg & 3;
55
56         if (!opt->flag)
57                 ret |= (opt->val <= CHAR_MAX) << 2;
58
59         return ret;
60 }
61
62 enum {
63         OPT_SHORT_WITH_OPTIONAL_ARG  = 6,
64         OPT_SHORT_WITH_MANDATORY_ARG = 5,
65         OPT_SHORT_WITHOUT_ARG        = 4,
66         OPT_LONG_WITH_OPTIONAL_ARG   = 2,
67         OPT_LONG_WITH_MANDATORY_ARG  = 1,
68         OPT_LONG_WITHOUT_ARG         = 0,
69 };
70
71 int help_print_optstring(const struct option *opt, const char *argname, int l)
72 {
73         char optstring[100];
74         int w;
75
76         if (!ENABLE_NLS)
77                 goto no_translate;
78
79         switch (option_type(opt)) {
80 #if HELP_GETOPT_LONG_ONLY
81         case OPT_SHORT_WITH_OPTIONAL_ARG:
82         case OPT_LONG_WITH_OPTIONAL_ARG:
83                 w = snprintf(optstring, sizeof optstring,
84                              _("  -%s [%s]"), opt->name,
85                              pgettext_expr(opt->name, argname));
86                 break;
87         case OPT_SHORT_WITH_MANDATORY_ARG:
88         case OPT_LONG_WITH_MANDATORY_ARG:
89                 w = snprintf(optstring, sizeof optstring,
90                              _("  -%s %s"), opt->name,
91                              pgettext_expr(opt->name, argname));
92                 break;
93         case OPT_SHORT_WITHOUT_ARG:
94         case OPT_LONG_WITHOUT_ARG:
95                 w = snprintf(optstring, sizeof optstring,
96                              _("  -%s"), opt->name);
97                 break;
98 #else
99         case OPT_SHORT_WITH_OPTIONAL_ARG:
100                 w = snprintf(optstring, sizeof optstring,
101                              _("  -%c, --%s[=%s]"), opt->val, opt->name,
102                              pgettext_expr(opt->name, argname));
103                 break;
104         case OPT_LONG_WITH_OPTIONAL_ARG:
105                 w = snprintf(optstring, sizeof optstring,
106                              _("  --%s[=%s]"), opt->name,
107                              pgettext_expr(opt->name, argname));
108                 break;
109         case OPT_SHORT_WITH_MANDATORY_ARG:
110                 w = snprintf(optstring, sizeof optstring,
111                              _("  -%c, --%s=%s"), opt->val, opt->name,
112                              pgettext_expr(opt->name, argname));
113                 break;
114         case OPT_LONG_WITH_MANDATORY_ARG:
115                 w = snprintf(optstring, sizeof optstring,
116                              _("  --%s=%s"), opt->name,
117                              pgettext_expr(opt->name, argname));
118                 break;
119         case OPT_SHORT_WITHOUT_ARG:
120                 w = snprintf(optstring, sizeof optstring,
121                              _("  -%c, --%s"), opt->val, opt->name);
122                 break;
123         case OPT_LONG_WITHOUT_ARG:
124                 w = snprintf(optstring, sizeof optstring,
125                              _("  --%s"), opt->name);
126                 break;
127 #endif
128         default:
129                 assert(0);
130         }
131
132         if (w < 0)
133                 goto no_translate;
134
135         w = mbsnwidth(optstring, w, 0);
136         printf("%s", optstring);
137         goto out;
138
139 no_translate:
140         switch (option_type(opt)) {
141 #if HELP_GETOPT_LONG_ONLY
142         case OPT_SHORT_WITH_OPTIONAL_ARG:
143         case OPT_LONG_WITH_OPTIONAL_ARG:
144                 w = printf("  -%s [%s]", opt->name, argname);
145                 break;
146         case OPT_SHORT_WITH_MANDATORY_ARG:
147         case OPT_LONG_WITH_MANDATORY_ARG:
148                 w = printf("  -%s %s", opt->name, argname);
149                 break;
150         case OPT_SHORT_WITHOUT_ARG:
151         case OPT_LONG_WITHOUT_ARG:
152                 w = printf("  -%s", opt->name);
153                 break;
154 #else
155         case OPT_SHORT_WITH_OPTIONAL_ARG:
156                 w = printf("  -%c, --%s[=%s]", opt->val, opt->name, argname);
157                 break;
158         case OPT_LONG_WITH_OPTIONAL_ARG:
159                 w = printf("  --%s[=%s]", opt->name, argname);
160                 break;
161         case OPT_SHORT_WITH_MANDATORY_ARG:
162                 w = printf("  -%c, --%s=%s", opt->val, opt->name, argname);
163                 break;
164         case OPT_LONG_WITH_MANDATORY_ARG:
165                 w = printf("  --%s=%s", opt->name, argname);
166                 break;
167         case OPT_SHORT_WITHOUT_ARG:
168                 w = printf("  -%c, --%s", opt->val, opt->name);
169                 break;
170         case OPT_LONG_WITHOUT_ARG:
171                 w = printf("  --%s", opt->name);
172                 break;
173 #endif
174         default:
175                 assert(0);
176         }
177 out:
178         if (w < 0 || w > l) {
179                 putchar('\n');
180                 return 0;
181         }
182
183         return w;
184 }
185
186 void help_print_desc(const struct option *opt, const char *s, int i, int w)
187 {
188         if (opt)
189                 s = pgettext_expr(opt->name, s);
190
191         if (i && s[0] == '\0')
192                 putchar('\n');
193
194         for (; *s; w = 0) {
195                 const char *nl = strchr(s, '\n');
196                 int n = (nl ? nl-s : -1);
197
198                 printf("%*s%.*s\n", i-w, "", n, s);
199                 if (!nl)
200                         break;
201
202                 s = nl+1;
203         }
204 }