1 /* argmatch.h -- definitions and prototypes for argmatch.c
3 Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2021 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
29 # include <string.h> /* memcmp */
39 # define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
41 /* Assert there are as many real arguments as there are values
42 (argument list ends with a NULL guard). */
44 # define ARGMATCH_VERIFY(Arglist, Vallist) \
45 verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)
47 /* Return the index of the element of ARGLIST (NULL terminated) that
48 matches with ARG. If VALLIST is not NULL, then use it to resolve
49 false ambiguities (i.e., different matches of ARG but corresponding
50 to the same values in VALLIST). */
52 ptrdiff_t argmatch (char const *arg
, char const *const *arglist
,
53 void const *vallist
, size_t valsize
) _GL_ATTRIBUTE_PURE
;
55 # define ARGMATCH(Arg, Arglist, Vallist) \
56 argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist))
58 /* xargmatch calls this function when it fails. This function should not
59 return. By default, this is a function that calls ARGMATCH_DIE which
60 in turn defaults to 'exit (exit_failure)'. */
61 typedef void (*argmatch_exit_fn
) (void);
62 extern argmatch_exit_fn argmatch_die
;
64 /* Report on stderr why argmatch failed. Report correct values. */
66 void argmatch_invalid (char const *context
, char const *value
,
69 /* Left for compatibility with the old name invalid_arg */
71 # define invalid_arg(Context, Value, Problem) \
72 argmatch_invalid (Context, Value, Problem)
76 /* Report on stderr the list of possible arguments. */
78 void argmatch_valid (char const *const *arglist
,
79 void const *vallist
, size_t valsize
);
81 # define ARGMATCH_VALID(Arglist, Vallist) \
82 argmatch_valid (Arglist, (void const *) (Vallist), sizeof *(Vallist))
86 /* Same as argmatch, but upon failure, report an explanation of the
87 failure, and exit using the function EXIT_FN. */
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
);
94 /* Programmer friendly interface to __xargmatch_internal. */
96 # define XARGMATCH(Context, Arg, Arglist, Vallist) \
97 ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \
98 (void const *) (Vallist), \
102 /* Convert a value into a corresponding argument. */
104 char const *argmatch_to_argument (void const *value
,
105 char const *const *arglist
,
106 void const *vallist
, size_t valsize
)
109 # define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \
110 argmatch_to_argument (Value, Arglist, \
111 (void const *) (Vallist), sizeof *(Vallist))
113 # define ARGMATCH_DEFINE_GROUP(Name, Type) \
114 /* The type of the values of this group. */ \
115 typedef Type argmatch_##Name##_type; \
117 /* The size of the type of the values of this group. */ \
118 enum argmatch_##Name##_size_enum \
120 argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \
123 /* Argument mapping of this group. */ \
126 /* Argument (e.g., "simple"). */ \
128 /* Value (e.g., simple_backups). */ \
129 const argmatch_##Name##_type val; \
130 } argmatch_##Name##_arg; \
132 /* Documentation of this group. */ \
135 /* Argument (e.g., "simple"). */ \
137 /* Documentation (e.g., N_("always make simple backups")). */ \
139 } argmatch_##Name##_doc; \
141 /* All the features of an argmatch group. */ \
144 const argmatch_##Name##_arg* args; \
145 const argmatch_##Name##_doc* docs; \
147 /* Printed before the usage message. */ \
148 const char *doc_pre; \
149 /* Printed after the usage message. */ \
150 const char *doc_post; \
151 } argmatch_##Name##_group_type; \
153 /* The structure the user must build. */ \
154 extern const argmatch_##Name##_group_type argmatch_##Name##_group; \
156 /* Print the documentation of this group. */ \
157 void argmatch_##Name##_usage (FILE *out); \
159 /* If nonnegative, the index I of ARG in ARGS, i.e, \
161 Return -1 for invalid argument, -2 for ambiguous argument. */ \
162 ptrdiff_t argmatch_##Name##_choice (const char *arg); \
164 /* A pointer to the corresponding value if it exists, or \
165 report an error and exit with failure if the argument was \
167 const argmatch_##Name##_type* \
168 argmatch_##Name##_value (const char *context, const char *arg); \
170 /* The first argument in ARGS that matches this value, or NULL. */ \
172 argmatch_##Name##_argument (const argmatch_##Name##_type *val); \
175 argmatch_##Name##_choice (const char *arg) \
177 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
178 size_t size = argmatch_##Name##_size; \
179 ptrdiff_t res = -1; /* Index of first nonexact match. */ \
180 bool ambiguous = false; /* Whether multiple nonexact match(es). */ \
181 size_t arglen = strlen (arg); \
183 /* Test all elements for either exact match or abbreviated \
185 for (size_t i = 0; g->args[i].arg; i++) \
186 if (!strncmp (g->args[i].arg, arg, arglen)) \
188 if (strlen (g->args[i].arg) == arglen) \
189 /* Exact match found. */ \
191 else if (res == -1) \
192 /* First nonexact match found. */ \
194 else if (memcmp (&g->args[res].val, &g->args[i].val, size)) \
195 /* Second nonexact match found. */ \
196 /* There is a real ambiguity, or we could not \
200 return ambiguous ? -2 : res; \
204 argmatch_##Name##_argument (const argmatch_##Name##_type *val) \
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; \
214 /* List the valid values of this group. */ \
216 argmatch_##Name##_valid (FILE *out) \
218 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
219 size_t size = argmatch_##Name##_size; \
221 /* Try to put synonyms on the same line. Synonyms are expected \
222 to follow each other. */ \
223 fputs (gettext ("Valid arguments are:"), out); \
224 for (int i = 0; g->args[i].arg; i++) \
226 || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
227 fprintf (out, "\n - %s", quote (g->args[i].arg)); \
229 fprintf (out, ", %s", quote (g->args[i].arg)); \
233 const argmatch_##Name##_type* \
234 argmatch_##Name##_value (const char *context, const char *arg) \
236 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
237 ptrdiff_t res = argmatch_##Name##_choice (arg); \
240 argmatch_invalid (context, arg, res); \
241 argmatch_##Name##_valid (stderr); \
244 return &g->args[res].val; \
247 /* The column in which the documentation is displayed. \
248 The leftmost possible, but no more than 20. */ \
250 argmatch_##Name##_doc_col (void) \
252 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
253 size_t size = argmatch_##Name##_size; \
255 for (int i = 0; g->docs[i].arg; ++i) \
258 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
260 /* Pseudo argument, display it. */ \
261 col += strlen (g->docs[i].arg); \
263 /* Genuine argument, display it with its synonyms. */ \
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); \
268 res = col <= 20 ? col : 20; \
270 return res ? res : 20; \
274 argmatch_##Name##_usage (FILE *out) \
276 const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
277 size_t size = argmatch_##Name##_size; \
278 /* Width of the screen. Help2man does not seem to support \
279 arguments on several lines, so in that case pretend a very \
281 const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
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) \
289 int ival = argmatch_##Name##_choice (g->docs[i].arg); \
291 /* Pseudo argument, display it. */ \
292 col += fprintf (out, " %s", g->docs[i].arg); \
294 /* Genuine argument, display it with its synonyms. */ \
295 for (int j = 0; g->args[j].arg; ++j) \
296 if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
299 && screen_width < col + 2 + strlen (g->args[j].arg)) \
301 fprintf (out, ",\n"); \
307 col += fprintf (out, " "); \
311 col += fprintf (out, ","); \
312 col += fprintf (out, " %s", g->args[j].arg); \
314 /* The doc. Separated by at least two spaces. */ \
315 if (doc_col < col + 2) \
317 fprintf (out, "\n"); \
320 fprintf (out, "%*s%s\n", \
321 doc_col - col, "", gettext (g->docs[i].doc)); \
324 fprintf (out, "%s\n", gettext (g->doc_post)); \
331 #endif /* ARGMATCH_H_ */