1 /* argmatch.h -- definitions and prototypes for argmatch.c
3 Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2024 Free Software
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> */
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."
33 # include <string.h> /* memcmp */
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
)
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
,
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), \
114 # define XARGMATCH_EXACT(Context, Arg, Arglist, Vallist) \
115 ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
116 (void const *) (Vallist), \
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
)
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) \
142 /* Argument mapping of this group. */ \
145 /* Argument (e.g., "simple"). */ \
147 /* Value (e.g., simple_backups). */ \
148 const argmatch_##Name##_type val; \
149 } argmatch_##Name##_arg; \
151 /* Documentation of this group. */ \
154 /* Argument (e.g., "simple"). */ \
156 /* Documentation (e.g., N_("always make simple backups")). */ \
158 } argmatch_##Name##_doc; \
160 /* All the features of an argmatch group. */ \
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, \
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 \
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. */ \
191 argmatch_##Name##_argument (const argmatch_##Name##_type *val); \
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 \
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. */ \
210 else if (res == -1) \
211 /* First nonexact match found. */ \
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 \
219 return ambiguous ? -2 : res; \
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; \
233 /* List the valid values of this group. */ \
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++) \
245 || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
246 fprintf (out, "\n - %s", quote (g->args[i].arg)); \
248 fprintf (out, ", %s", quote (g->args[i].arg)); \
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); \
259 argmatch_invalid (context, arg, res); \
260 argmatch_##Name##_valid (stderr); \
263 return &g->args[res].val; \
266 /* The column in which the documentation is displayed. \
267 The leftmost possible, but no more than 20. */ \
269 argmatch_##Name##_doc_col (void) \
271 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
272 size_t size = argmatch_##Name##_size; \
274 for (int i = 0; g->docs[i].arg; ++i) \
277 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
279 /* Pseudo argument, display it. */ \
280 col += strlen (g->docs[i].arg); \
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); \
287 res = col <= 20 ? col : 20; \
289 return res ? res : 20; \
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 \
300 const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
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) \
308 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
310 /* Pseudo argument, display it. */ \
311 col += fprintf (out, " %s", g->docs[i].arg); \
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)) \
318 && screen_width < col + 2 + strlen (g->args[j].arg)) \
320 fprintf (out, ",\n"); \
326 col += fprintf (out, " "); \
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"); \
339 fprintf (out, "%*s%s\n", \
340 doc_col - col, "", gettext (g->docs[i].doc)); \
343 fprintf (out, "%s\n", gettext (g->doc_post)); \
350 #endif /* ARGMATCH_H_ */