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