]> git.saurik.com Git - bison.git/blob - lib/argmatch.c
81f95f75cda4e15b14f6f2ff651137b43e7557f4
[bison.git] / lib / argmatch.c
1 /* argmatch.c -- find a match for a string in an array
2 Copyright (C) 1990, 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 /* Written by David MacKenzie <djm@ai.mit.edu>
19 Modified by Akim Demaille <demaille@inf.enst.fr> */
20
21 #if HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 /* Specification. */
26 #include "argmatch.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 #include "error.h"
36 #include "quotearg.h"
37 #include "quote.h"
38 #include "unlocked-io.h"
39
40 /* When reporting an invalid argument, show nonprinting characters
41 by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use
42 literal_quoting_style. */
43 #ifndef ARGMATCH_QUOTING_STYLE
44 # define ARGMATCH_QUOTING_STYLE locale_quoting_style
45 #endif
46
47 /* Non failing version of argmatch call this function after failing. */
48 #ifndef ARGMATCH_DIE
49 # define ARGMATCH_DIE exit (EXIT_FAILURE)
50 #endif
51
52 #ifdef ARGMATCH_DIE_DECL
53 ARGMATCH_DIE_DECL;
54 #endif
55
56 static void
57 __argmatch_die (void)
58 {
59 ARGMATCH_DIE;
60 }
61
62 /* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h.
63 Default to __argmatch_die, but allow caller to change this at run-time. */
64 argmatch_exit_fn argmatch_die = __argmatch_die;
65
66 \f
67 /* If ARG is an unambiguous match for an element of the
68 null-terminated array ARGLIST, return the index in ARGLIST
69 of the matched element, else -1 if it does not match any element
70 or -2 if it is ambiguous (is a prefix of more than one element).
71 If SENSITIVE, comparison is case sensitive.
72
73 If VALLIST is none null, use it to resolve ambiguities limited to
74 synonyms, i.e., for
75 "yes", "yop" -> 0
76 "no", "nope" -> 1
77 "y" is a valid argument, for `0', and "n" for `1'. */
78
79 static int
80 __argmatch_internal (const char *arg, const char *const *arglist,
81 const char *vallist, size_t valsize,
82 int case_sensitive)
83 {
84 int i; /* Temporary index in ARGLIST. */
85 size_t arglen; /* Length of ARG. */
86 int matchind = -1; /* Index of first nonexact match. */
87 int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
88
89 arglen = strlen (arg);
90
91 /* Test all elements for either exact match or abbreviated matches. */
92 for (i = 0; arglist[i]; i++)
93 {
94 if (case_sensitive
95 ? !strncmp (arglist[i], arg, arglen)
96 : !strncasecmp (arglist[i], arg, arglen))
97 {
98 if (strlen (arglist[i]) == arglen)
99 /* Exact match found. */
100 return i;
101 else if (matchind == -1)
102 /* First nonexact match found. */
103 matchind = i;
104 else
105 {
106 /* Second nonexact match found. */
107 if (vallist == NULL
108 || memcmp (vallist + valsize * matchind,
109 vallist + valsize * i, valsize))
110 {
111 /* There is a real ambiguity, or we could not
112 disambiguate. */
113 ambiguous = 1;
114 }
115 }
116 }
117 }
118 if (ambiguous)
119 return -2;
120 else
121 return matchind;
122 }
123
124 /* argmatch - case sensitive version */
125 int
126 argmatch (const char *arg, const char *const *arglist,
127 const char *vallist, size_t valsize)
128 {
129 return __argmatch_internal (arg, arglist, vallist, valsize, 1);
130 }
131
132 /* argcasematch - case insensitive version */
133 int
134 argcasematch (const char *arg, const char *const *arglist,
135 const char *vallist, size_t valsize)
136 {
137 return __argmatch_internal (arg, arglist, vallist, valsize, 0);
138 }
139
140 /* Error reporting for argmatch.
141 CONTEXT is a description of the type of entity that was being matched.
142 VALUE is the invalid value that was given.
143 PROBLEM is the return value from argmatch. */
144
145 void
146 argmatch_invalid (const char *context, const char *value, int problem)
147 {
148 char const *format = (problem == -1
149 ? _("invalid argument %s for %s")
150 : _("ambiguous argument %s for %s"));
151
152 error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
153 quote_n (1, context));
154 }
155
156 /* List the valid arguments for argmatch.
157 ARGLIST is the same as in argmatch.
158 VALLIST is a pointer to an array of values.
159 VALSIZE is the size of the elements of VALLIST */
160 void
161 argmatch_valid (const char *const *arglist,
162 const char *vallist, size_t valsize)
163 {
164 int i;
165 const char *last_val = NULL;
166
167 /* We try to put synonyms on the same line. The assumption is that
168 synonyms follow each other */
169 fprintf (stderr, _("Valid arguments are:"));
170 for (i = 0; arglist[i]; i++)
171 if ((i == 0)
172 || memcmp (last_val, vallist + valsize * i, valsize))
173 {
174 fprintf (stderr, "\n - `%s'", arglist[i]);
175 last_val = vallist + valsize * i;
176 }
177 else
178 {
179 fprintf (stderr, ", `%s'", arglist[i]);
180 }
181 putc ('\n', stderr);
182 }
183
184 /* Never failing versions of the previous functions.
185
186 CONTEXT is the context for which argmatch is called (e.g.,
187 "--version-control", or "$VERSION_CONTROL" etc.). Upon failure,
188 calls the (supposed never to return) function EXIT_FN. */
189
190 int
191 __xargmatch_internal (const char *context,
192 const char *arg, const char *const *arglist,
193 const char *vallist, size_t valsize,
194 int case_sensitive,
195 argmatch_exit_fn exit_fn)
196 {
197 int res = __argmatch_internal (arg, arglist,
198 vallist, valsize,
199 case_sensitive);
200 if (res >= 0)
201 /* Success. */
202 return res;
203
204 /* We failed. Explain why. */
205 argmatch_invalid (context, arg, res);
206 argmatch_valid (arglist, vallist, valsize);
207 (*exit_fn) ();
208
209 return -1; /* To please the compilers. */
210 }
211
212 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
213 return the first corresponding argument in ARGLIST */
214 const char *
215 argmatch_to_argument (const char *value,
216 const char *const *arglist,
217 const char *vallist, size_t valsize)
218 {
219 int i;
220
221 for (i = 0; arglist[i]; i++)
222 if (!memcmp (value, vallist + valsize * i, valsize))
223 return arglist[i];
224 return NULL;
225 }
226
227 #ifdef TEST
228 /*
229 * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
230 */
231 char *program_name;
232 extern const char *getenv ();
233
234 /* When to make backup files. */
235 enum backup_type
236 {
237 /* Never make backups. */
238 none,
239
240 /* Make simple backups of every file. */
241 simple,
242
243 /* Make numbered backups of files that already have numbered backups,
244 and simple backups of the others. */
245 numbered_existing,
246
247 /* Make numbered backups of every file. */
248 numbered
249 };
250
251 /* Two tables describing arguments (keys) and their corresponding
252 values */
253 static const char *const backup_args[] =
254 {
255 "no", "none", "off",
256 "simple", "never",
257 "existing", "nil",
258 "numbered", "t",
259 0
260 };
261
262 static const enum backup_type backup_vals[] =
263 {
264 none, none, none,
265 simple, simple,
266 numbered_existing, numbered_existing,
267 numbered, numbered
268 };
269
270 int
271 main (int argc, const char *const *argv)
272 {
273 const char *cp;
274 enum backup_type backup_type = none;
275
276 program_name = (char *) argv[0];
277
278 if (argc > 2)
279 {
280 fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
281 exit (1);
282 }
283
284 if ((cp = getenv ("VERSION_CONTROL")))
285 backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp,
286 backup_args, backup_vals);
287
288 if (argc == 2)
289 backup_type = XARGCASEMATCH (program_name, argv[1],
290 backup_args, backup_vals);
291
292 printf ("The version control is `%s'\n",
293 ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
294
295 return 0;
296 }
297 #endif