exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / argmatch.h
blobca60dfe1ceff8521da7dbeae840907c36e16e1d7
1 /* argmatch.h -- definitions and prototypes for argmatch.c
3 Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2024 Free Software
4 Foundation, Inc.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 /* Written by David MacKenzie <djm@ai.mit.edu>
20 Modified by Akim Demaille <demaille@inf.enst.fr> */
22 #ifndef ARGMATCH_H_
23 # define ARGMATCH_H_ 1
25 /* This file uses _GL_ATTRIBUTE_PURE. */
26 # if !_GL_CONFIG_H_INCLUDED
27 # error "Please include config.h first."
28 # endif
30 # include <limits.h>
31 # include <stddef.h>
32 # include <stdio.h>
33 # include <string.h> /* memcmp */
35 # include "gettext.h"
36 # include "quote.h"
38 # ifdef __cplusplus
39 extern "C" {
40 # endif
42 # define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
44 /* Assert there are as many real arguments as there are values
45 (argument list ends with a NULL guard). */
47 # define ARGMATCH_VERIFY(Arglist, Vallist) \
48 static_assert (ARRAY_CARDINALITY (Arglist) \
49 == ARRAY_CARDINALITY (Vallist) + 1)
51 /* Return the index of the element of ARGLIST (NULL terminated) that
52 matches with ARG. If VALLIST is not NULL, then use it to resolve
53 false ambiguities (i.e., different matches of ARG but corresponding
54 to the same values in VALLIST). */
56 ptrdiff_t argmatch (char const *arg, char const *const *arglist,
57 void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE;
59 ptrdiff_t argmatch_exact (char const *arg, char const *const *arglist)
60 _GL_ATTRIBUTE_PURE;
62 # define ARGMATCH(Arg, Arglist, Vallist) \
63 argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
65 # define ARGMATCH_EXACT(Arg, Arglist) \
66 argmatch_exact (Arg, Arglist)
68 /* xargmatch calls this function when it fails. This function should not
69 return. By default, this is a function that calls ARGMATCH_DIE which
70 in turn defaults to 'exit (exit_failure)'. */
71 typedef void (*argmatch_exit_fn) (void);
72 extern argmatch_exit_fn argmatch_die;
74 /* Report on stderr why argmatch failed. Report correct values. */
76 void argmatch_invalid (char const *context, char const *value,
77 ptrdiff_t problem);
79 /* Left for compatibility with the old name invalid_arg */
81 # define invalid_arg(Context, Value, Problem) \
82 argmatch_invalid (Context, Value, Problem)
86 /* Report on stderr the list of possible arguments. */
88 void argmatch_valid (char const *const *arglist,
89 void const *vallist, size_t valsize);
91 # define ARGMATCH_VALID(Arglist, Vallist) \
92 argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
96 /* Like argmatch/argmatch_exact, but upon failure, report an explanation
97 of the failure, and exit using the function EXIT_FN. */
99 ptrdiff_t __xargmatch_internal (char const *context,
100 char const *arg, char const *const *arglist,
101 void const *vallist, size_t valsize,
102 argmatch_exit_fn exit_fn,
103 bool allow_abbreviation);
105 /* Programmer friendly interface to __xargmatch_internal. */
107 # define XARGMATCH(Context, Arg, Arglist, Vallist) \
108 ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
109 (void const *) (Vallist), \
110 sizeof *(Vallist), \
111 argmatch_die, \
112 true)])
114 # define XARGMATCH_EXACT(Context, Arg, Arglist, Vallist) \
115 ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
116 (void const *) (Vallist), \
117 sizeof *(Vallist), \
118 argmatch_die, \
119 false)])
121 /* Convert a value into a corresponding argument. */
123 char const *argmatch_to_argument (void const *value,
124 char const *const *arglist,
125 void const *vallist, size_t valsize)
126 _GL_ATTRIBUTE_PURE;
128 # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \
129 argmatch_to_argument (Value, Arglist, \
130 (void const *) (Vallist), sizeof *(Vallist))
132 # define ARGMATCH_DEFINE_GROUP(Name, Type) \
133 /* The type of the values of this group. */ \
134 typedef Type argmatch_##Name##_type; \
136 /* The size of the type of the values of this group. */ \
137 enum argmatch_##Name##_size_enum \
139 argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \
140 }; \
142 /* Argument mapping of this group. */ \
143 typedef struct \
145 /* Argument (e.g., "simple"). */ \
146 const char *arg; \
147 /* Value (e.g., simple_backups). */ \
148 const argmatch_##Name##_type val; \
149 } argmatch_##Name##_arg; \
151 /* Documentation of this group. */ \
152 typedef struct \
154 /* Argument (e.g., "simple"). */ \
155 const char *arg; \
156 /* Documentation (e.g., N_("always make simple backups")). */ \
157 const char *doc; \
158 } argmatch_##Name##_doc; \
160 /* All the features of an argmatch group. */ \
161 typedef struct \
163 const argmatch_##Name##_arg* args; \
164 const argmatch_##Name##_doc* docs; \
166 /* Printed before the usage message. */ \
167 const char *doc_pre; \
168 /* Printed after the usage message. */ \
169 const char *doc_post; \
170 } argmatch_##Name##_group_type; \
172 /* The structure the user must build. */ \
173 extern const argmatch_##Name##_group_type argmatch_##Name##_group; \
175 /* Print the documentation of this group. */ \
176 void argmatch_##Name##_usage (FILE *out); \
178 /* If nonnegative, the index I of ARG in ARGS, i.e, \
179 ARGS[I] == ARG. \
180 Return -1 for invalid argument, -2 for ambiguous argument. */ \
181 ptrdiff_t argmatch_##Name##_choice (const char *arg); \
183 /* A pointer to the corresponding value if it exists, or \
184 report an error and exit with failure if the argument was \
185 not recognized. */ \
186 const argmatch_##Name##_type* \
187 argmatch_##Name##_value (const char *context, const char *arg); \
189 /* The first argument in ARGS that matches this value, or NULL. */ \
190 const char * \
191 argmatch_##Name##_argument (const argmatch_##Name##_type *val); \
193 ptrdiff_t \
194 argmatch_##Name##_choice (const char *arg) \
196 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
197 size_t size = argmatch_##Name##_size; \
198 ptrdiff_t res = -1; /* Index of first nonexact match. */ \
199 bool ambiguous = false; /* Whether multiple nonexact match(es). */ \
200 size_t arglen = strlen (arg); \
202 /* Test all elements for either exact match or abbreviated \
203 matches. */ \
204 for (size_t i = 0; g->args[i].arg; i++) \
205 if (!strncmp (g->args[i].arg, arg, arglen)) \
207 if (strlen (g->args[i].arg) == arglen) \
208 /* Exact match found. */ \
209 return i; \
210 else if (res == -1) \
211 /* First nonexact match found. */ \
212 res = i; \
213 else if (memcmp (&g->args[res].val, &g->args[i].val, size)) \
214 /* Second nonexact match found. */ \
215 /* There is a real ambiguity, or we could not \
216 disambiguate. */ \
217 ambiguous = true; \
219 return ambiguous ? -2 : res; \
222 const char * \
223 argmatch_##Name##_argument (const argmatch_##Name##_type *val) \
225 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
226 size_t size = argmatch_##Name##_size; \
227 for (size_t i = 0; g->args[i].arg; i++) \
228 if (!memcmp (val, &g->args[i].val, size)) \
229 return g->args[i].arg; \
230 return NULL; \
233 /* List the valid values of this group. */ \
234 static void \
235 argmatch_##Name##_valid (FILE *out) \
237 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
238 size_t size = argmatch_##Name##_size; \
240 /* Try to put synonyms on the same line. Synonyms are expected \
241 to follow each other. */ \
242 fputs (gettext ("Valid arguments are:"), out); \
243 for (int i = 0; g->args[i].arg; i++) \
244 if (i == 0 \
245 || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
246 fprintf (out, "\n - %s", quote (g->args[i].arg)); \
247 else \
248 fprintf (out, ", %s", quote (g->args[i].arg)); \
249 putc ('\n', out); \
252 const argmatch_##Name##_type* \
253 argmatch_##Name##_value (const char *context, const char *arg) \
255 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
256 ptrdiff_t res = argmatch_##Name##_choice (arg); \
257 if (res < 0) \
259 argmatch_invalid (context, arg, res); \
260 argmatch_##Name##_valid (stderr); \
261 argmatch_die (); \
263 return &g->args[res].val; \
266 /* The column in which the documentation is displayed. \
267 The leftmost possible, but no more than 20. */ \
268 static int \
269 argmatch_##Name##_doc_col (void) \
271 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
272 size_t size = argmatch_##Name##_size; \
273 int res = 0; \
274 for (int i = 0; g->docs[i].arg; ++i) \
276 int col = 4; \
277 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
278 if (ival < 0) \
279 /* Pseudo argument, display it. */ \
280 col += strlen (g->docs[i].arg); \
281 else \
282 /* Genuine argument, display it with its synonyms. */ \
283 for (int j = 0; g->args[j].arg; ++j) \
284 if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
285 col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg); \
286 if (res <= col) \
287 res = col <= 20 ? col : 20; \
289 return res ? res : 20; \
292 void \
293 argmatch_##Name##_usage (FILE *out) \
295 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
296 size_t size = argmatch_##Name##_size; \
297 /* Width of the screen. Help2man does not seem to support \
298 arguments on several lines, so in that case pretend a very \
299 large width. */ \
300 const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
301 if (g->doc_pre) \
302 fprintf (out, "%s\n", gettext (g->doc_pre)); \
303 int doc_col = argmatch_##Name##_doc_col (); \
304 for (int i = 0; g->docs[i].arg; ++i) \
306 int col = 0; \
307 bool first = true; \
308 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
309 if (ival < 0) \
310 /* Pseudo argument, display it. */ \
311 col += fprintf (out, " %s", g->docs[i].arg); \
312 else \
313 /* Genuine argument, display it with its synonyms. */ \
314 for (int j = 0; g->args[j].arg; ++j) \
315 if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
317 if (!first \
318 && screen_width < col + 2 + strlen (g->args[j].arg)) \
320 fprintf (out, ",\n"); \
321 col = 0; \
322 first = true; \
324 if (first) \
326 col += fprintf (out, " "); \
327 first = false; \
329 else \
330 col += fprintf (out, ","); \
331 col += fprintf (out, " %s", g->args[j].arg); \
333 /* The doc. Separated by at least two spaces. */ \
334 if (doc_col < col + 2) \
336 fprintf (out, "\n"); \
337 col = 0; \
339 fprintf (out, "%*s%s\n", \
340 doc_col - col, "", gettext (g->docs[i].doc)); \
342 if (g->doc_post) \
343 fprintf (out, "%s\n", gettext (g->doc_post)); \
346 # ifdef __cplusplus
348 # endif
350 #endif /* ARGMATCH_H_ */