]>
Commit | Line | Data |
---|---|---|
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 | |
63 | ARGMATCH_DIE_DECL; | |
64 | #endif | |
65 | ||
66 | static 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. */ | |
74 | argmatch_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 | ||
89 | static 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 */ | |
135 | int | |
136 | argmatch (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 */ | |
143 | int | |
144 | argcasematch (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 | ||
155 | void | |
156 | argmatch_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 */ | |
169 | void | |
170 | argmatch_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 | ||
199 | int | |
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 */ | |
223 | const char * | |
224 | argmatch_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 | */ | |
240 | char *program_name; | |
241 | extern const char *getenv (); | |
242 | ||
243 | /* When to make backup files. */ | |
244 | enum 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 */ | |
262 | static const char *const backup_args[] = | |
263 | { | |
264 | "no", "none", "off", | |
265 | "simple", "never", | |
266 | "existing", "nil", | |
267 | "numbered", "t", | |
268 | 0 | |
269 | }; | |
270 | ||
271 | static const enum backup_type backup_vals[] = | |
272 | { | |
273 | none, none, none, | |
274 | simple, simple, | |
275 | numbered_existing, numbered_existing, | |
276 | numbered, numbered | |
277 | }; | |
278 | ||
279 | int | |
280 | main (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 |