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