1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #ifndef ARGMATCH_H_
23 # define ARGMATCH_H_ 1
24
25 # include <limits.h>
26 # include <stdbool.h>
27 # include <stddef.h>
28 # include <stdio.h>
29 # include <string.h>
30
31 # include "gettext.h"
32 # include "quote.h"
33 # include "verify.h"
34
35 # ifdef __cplusplus
36 extern "C" {
37 # endif
38
39 # define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
40
41
42
43
44 # define ARGMATCH_VERIFY(Arglist, Vallist) \
45 verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)
46
47
48
49
50
51
52 ptrdiff_t argmatch (char const *arg, char const *const *arglist,
53 void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE;
54
55 # define ARGMATCH(Arg, Arglist, Vallist) \
56 argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
57
58
59
60
61 typedef void (*argmatch_exit_fn) (void);
62 extern argmatch_exit_fn argmatch_die;
63
64
65
66 void argmatch_invalid (char const *context, char const *value,
67 ptrdiff_t problem);
68
69
70
71 # define invalid_arg(Context, Value, Problem) \
72 argmatch_invalid (Context, Value, Problem)
73
74
75
76
77
78 void argmatch_valid (char const *const *arglist,
79 void const *vallist, size_t valsize);
80
81 # define ARGMATCH_VALID(Arglist, Vallist) \
82 argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
83
84
85
86
87
88
89 ptrdiff_t __xargmatch_internal (char const *context,
90 char const *arg, char const *const *arglist,
91 void const *vallist, size_t valsize,
92 argmatch_exit_fn exit_fn);
93
94
95
96 # define XARGMATCH(Context, Arg, Arglist, Vallist) \
97 ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
98 (void const *) (Vallist), \
99 sizeof *(Vallist), \
100 argmatch_die)])
101
102
103
104 char const *argmatch_to_argument (void const *value,
105 char const *const *arglist,
106 void const *vallist, size_t valsize)
107 _GL_ATTRIBUTE_PURE;
108
109 # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \
110 argmatch_to_argument (Value, Arglist, \
111 (void const *) (Vallist), sizeof *(Vallist))
112
113 # define ARGMATCH_DEFINE_GROUP(Name, Type) \
114 \
115 typedef Type argmatch_##Name##_type; \
116 \
117 \
118 enum argmatch_##Name##_size_enum \
119 { \
120 argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \
121 }; \
122 \
123 \
124 typedef struct \
125 { \
126 \
127 const char *arg; \
128 \
129 const argmatch_##Name##_type val; \
130 } argmatch_##Name##_arg; \
131 \
132 \
133 typedef struct \
134 { \
135 \
136 const char *arg; \
137 \
138 const char *doc; \
139 } argmatch_##Name##_doc; \
140 \
141 \
142 typedef struct \
143 { \
144 const argmatch_##Name##_arg* args; \
145 const argmatch_##Name##_doc* docs; \
146 \
147 \
148 const char *doc_pre; \
149 \
150 const char *doc_post; \
151 } argmatch_##Name##_group_type; \
152 \
153 \
154 extern const argmatch_##Name##_group_type argmatch_##Name##_group; \
155 \
156 \
157 void argmatch_##Name##_usage (FILE *out); \
158 \
159
160
161 \
162 ptrdiff_t argmatch_##Name##_choice (const char *arg); \
163 \
164
165
166 \
167 const argmatch_##Name##_type* \
168 argmatch_##Name##_value (const char *context, const char *arg); \
169 \
170 \
171 const char * \
172 argmatch_##Name##_argument (const argmatch_##Name##_type *val); \
173 \
174 ptrdiff_t \
175 argmatch_##Name##_choice (const char *arg) \
176 { \
177 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
178 size_t size = argmatch_##Name##_size; \
179 ptrdiff_t res = -1; \
180 bool ambiguous = false; \
181 size_t arglen = strlen (arg); \
182 \
183
184 \
185 for (size_t i = 0; g->args[i].arg; i++) \
186 if (!strncmp (g->args[i].arg, arg, arglen)) \
187 { \
188 if (strlen (g->args[i].arg) == arglen) \
189 \
190 return i; \
191 else if (res == -1) \
192 \
193 res = i; \
194 else if (memcmp (&g->args[res].val, &g->args[i].val, size)) \
195 \
196
197 \
198 ambiguous = true; \
199 } \
200 return ambiguous ? -2 : res; \
201 } \
202 \
203 const char * \
204 argmatch_##Name##_argument (const argmatch_##Name##_type *val) \
205 { \
206 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
207 size_t size = argmatch_##Name##_size; \
208 for (size_t i = 0; g->args[i].arg; i++) \
209 if (!memcmp (val, &g->args[i].val, size)) \
210 return g->args[i].arg; \
211 return NULL; \
212 } \
213 \
214 \
215 static void \
216 argmatch_##Name##_valid (FILE *out) \
217 { \
218 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
219 size_t size = argmatch_##Name##_size; \
220 \
221
222 \
223 fputs (gettext ("Valid arguments are:"), out); \
224 for (int i = 0; g->args[i].arg; i++) \
225 if (i == 0 \
226 || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
227 fprintf (out, "\n - %s", quote (g->args[i].arg)); \
228 else \
229 fprintf (out, ", %s", quote (g->args[i].arg)); \
230 putc ('\n', out); \
231 } \
232 \
233 const argmatch_##Name##_type* \
234 argmatch_##Name##_value (const char *context, const char *arg) \
235 { \
236 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
237 ptrdiff_t res = argmatch_##Name##_choice (arg); \
238 if (res < 0) \
239 { \
240 argmatch_invalid (context, arg, res); \
241 argmatch_##Name##_valid (stderr); \
242 argmatch_die (); \
243 } \
244 return &g->args[res].val; \
245 } \
246 \
247
248 \
249 static int \
250 argmatch_##Name##_doc_col (void) \
251 { \
252 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
253 size_t size = argmatch_##Name##_size; \
254 int res = 0; \
255 for (int i = 0; g->docs[i].arg; ++i) \
256 { \
257 int col = 4; \
258 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
259 if (ival < 0) \
260 \
261 col += strlen (g->docs[i].arg); \
262 else \
263 \
264 for (int j = 0; g->args[j].arg; ++j) \
265 if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
266 col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg); \
267 if (res <= col) \
268 res = col <= 20 ? col : 20; \
269 } \
270 return res ? res : 20; \
271 } \
272 \
273 void \
274 argmatch_##Name##_usage (FILE *out) \
275 { \
276 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
277 size_t size = argmatch_##Name##_size; \
278
279
280 \
281 const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
282 if (g->doc_pre) \
283 fprintf (out, "%s\n", gettext (g->doc_pre)); \
284 int doc_col = argmatch_##Name##_doc_col (); \
285 for (int i = 0; g->docs[i].arg; ++i) \
286 { \
287 int col = 0; \
288 bool first = true; \
289 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
290 if (ival < 0) \
291 \
292 col += fprintf (out, " %s", g->docs[i].arg); \
293 else \
294 \
295 for (int j = 0; g->args[j].arg; ++j) \
296 if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
297 { \
298 if (!first \
299 && screen_width < col + 2 + strlen (g->args[j].arg)) \
300 { \
301 fprintf (out, ",\n"); \
302 col = 0; \
303 first = true; \
304 } \
305 if (first) \
306 { \
307 col += fprintf (out, " "); \
308 first = false; \
309 } \
310 else \
311 col += fprintf (out, ","); \
312 col += fprintf (out, " %s", g->args[j].arg); \
313 } \
314 \
315 if (doc_col < col + 2) \
316 { \
317 fprintf (out, "\n"); \
318 col = 0; \
319 } \
320 fprintf (out, "%*s%s\n", \
321 doc_col - col, "", gettext (g->docs[i].doc)); \
322 } \
323 if (g->doc_post) \
324 fprintf (out, "%s\n", gettext (g->doc_post)); \
325 }
326
327 # ifdef __cplusplus
328 }
329 # endif
330
331 #endif