lots of changes from Akim Demaille.
[gnulib.git] / lib / argmatch.c
1 /* argmatch.c -- find a match for a string in an array
2    Copyright (C) 1990, 1998 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 #if !HAVE_SETLOCALE
32 # define setlocale(Category, Locale) /* empty */
33 #endif
34
35 #if ENABLE_NLS
36 # include <libintl.h>
37 # define _(Text) gettext (Text)
38 #else
39 # define bindtextdomain(Domain, Directory) /* empty */
40 # define textdomain(Domain) /* empty */
41 # define _(Text) Text
42 #endif
43
44 #ifndef EXIT_BADARG
45 # define EXIT_BADARG 1
46 #endif
47
48 #include "quotearg.h"
49
50 /* When reporting a failing argument, make sure to show invisible
51    characters hidden using the quoting style
52    ARGMATCH_QUOTING_STYLE. literal_quoting_style is not good.*/
53
54 #ifndef ARGMATCH_QUOTING_STYLE
55 # define ARGMATCH_QUOTING_STYLE c_quoting_style
56 #endif
57
58 #if !HAVE_STRNCASECMP
59 # include <ctype.h>
60 /* Compare no more than N characters of S1 and S2,
61    ignoring case, returning less than, equal to or
62    greater than zero if S1 is lexicographically less
63    than, equal to or greater than S2.  */
64 int
65 strncasecmp (const char *s1, const char *s2, size_t n)
66 {
67   register const unsigned char *p1 = (const unsigned char *) s1;
68   register const unsigned char *p2 = (const unsigned char *) s2;
69   unsigned char c1, c2;
70
71   if (p1 == p2 || n == 0)
72     return 0;
73
74   do
75     {
76       c1 = tolower (*p1++);
77       c2 = tolower (*p2++);
78       if (c1 == '\0' || c1 != c2)
79         return c1 - c2;
80     } while (--n > 0);
81
82   return c1 - c2;
83 }
84 #endif
85
86 extern char *program_name;
87
88 /* If ARG is an unambiguous match for an element of the
89    null-terminated array ARGLIST, return the index in ARGLIST
90    of the matched element, else -1 if it does not match any element
91    or -2 if it is ambiguous (is a prefix of more than one element).
92    If SENSITIVE, comparison is case sensitive.
93
94    If VALLIST is none null, use it to resolve ambiguities limited to
95    synonyms, i.e., for
96      "yes", "yop" -> 0
97      "no", "nope" -> 1
98    "y" is a valid argument, for `0', and "n" for `1'.  */
99
100 static int
101 __argmatch_internal (const char *arg, const char *const *arglist,
102                      const char *vallist, size_t valsize, 
103                      int sensitive)
104 {
105   int i;                        /* Temporary index in ARGLIST.  */
106   size_t arglen;                /* Length of ARG.  */
107   int matchind = -1;            /* Index of first nonexact match.  */
108   int ambiguous = 0;            /* If nonzero, multiple nonexact match(es).  */
109
110   arglen = strlen (arg);
111
112   /* Test all elements for either exact match or abbreviated matches.  */
113   for (i = 0; arglist[i]; i++)
114     {
115       if (sensitive ? !strncmp (arglist[i], arg, arglen)
116                     : !strncasecmp (arglist[i], arg, arglen))
117         {
118           if (strlen (arglist[i]) == arglen)
119             /* Exact match found.  */
120             return i;
121           else if (matchind == -1)
122             /* First nonexact match found.  */
123             matchind = i;
124           else
125             /* Second nonexact match found.  */
126             if (vallist == NULL
127                 || memcmp (vallist + valsize * matchind, 
128                            vallist + valsize * i, valsize))
129               /* There is a real ambiguity, or we could not
130                  desambiguise. */
131               ambiguous = 1;
132         }
133     }
134   if (ambiguous)
135     return -2;
136   else
137     return matchind;
138 }
139
140 /* argmatch - case sensitive version */
141 int
142 argmatch (const char *arg, const char *const *arglist,
143           const char *vallist, size_t valsize)
144 {
145   return __argmatch_internal (arg, arglist, vallist, valsize, 1);
146 }
147
148 /* argcasematch - case insensitive version */
149 int
150 argcasematch (const char *arg, const char *const *arglist,
151               const char *vallist, size_t valsize)
152 {
153   return __argmatch_internal (arg, arglist, vallist, valsize, 0);
154 }
155
156 /* Error reporting for argmatch.
157    KIND is a description of the type of entity that was being matched.
158    VALUE is the invalid value that was given.
159    PROBLEM is the return value from argmatch.  */
160
161 void
162 argmatch_invalid (const char *kind, const char *value, int problem)
163 {
164   enum quoting_style saved_quoting_style;
165
166   /* Make sure to have a good quoting style to report errors.
167      literal is insane here. */
168   saved_quoting_style = get_quoting_style (NULL);
169   set_quoting_style (NULL, ARGMATCH_QUOTING_STYLE);
170
171   /* There is an error */
172   fprintf (stderr, "%s: ", program_name);
173   if (problem == -1)
174     fprintf (stderr,  _("invalid argument %s for `%s'"),
175              quotearg (value), kind);
176   else                          /* Assume -2.  */
177     fprintf (stderr, _("ambiguous argument %s for `%s'"),
178              quotearg (value), kind);
179   putc ('\n', stderr);
180
181   set_quoting_style (NULL, saved_quoting_style);
182 }
183
184 /* List the valid arguments for argmatch.
185    ARGLIST is the same as in argmatch.
186    VALLIST is a pointer to an array of values.
187    VALSIZE is the size of the elements of VALLIST */
188 void
189 argmatch_valid (const char *const *arglist,
190                 const char *vallist, size_t valsize)
191 {
192   int i;
193   const char * last_val = NULL;
194
195   /* We try to put synonyms on the same line.  The assumption is that
196      synonyms follow each other */
197   fprintf (stderr, _("Valid arguments are:"));
198   for (i = 0 ; arglist[i] ; i++)
199     if ((i == 0)
200         || memcmp (last_val, vallist + valsize * i, valsize))
201       {
202         fprintf (stderr, "\n  - `%s'", arglist[i]);
203         last_val = vallist + valsize * i;
204       }
205     else
206       {
207         fprintf (stderr, ", `%s'", arglist[i]);
208       }
209   putc ('\n', stderr);
210 }
211
212 /* Call __argmatch_internal, but handle the error so that it never
213    returns.  Errors are reported to the users with a list of valid
214    values.
215
216    KIND is a description of the type of entity that was being matched.
217    ARG, ARGLIST, and SENSITIVE are the same as in __argmatch_internal
218    VALIST, and VALSIZE are the same as in valid_args */
219 int
220 __xargmatch_internal (const char *kind, const char *arg,
221                       const char *const *arglist, 
222                       const char *vallist, size_t valsize,
223                       int sensitive)
224 {
225   int i;
226   
227   i = __argmatch_internal (arg, arglist, vallist, valsize, sensitive);
228   if (i >= 0)
229     {
230       /* Success */
231       return i;
232     }
233   else
234     {
235       /* Failure */
236       argmatch_invalid (kind, arg, i);
237       argmatch_valid (arglist, vallist, valsize);
238       exit (EXIT_BADARG);
239     }
240   return -1;    /* To please some compilers */
241 }
242
243 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
244    return the first corresponding argument in ARGLIST */
245 const char *
246 argmatch_to_argument (char * value,
247                       const char *const *arglist,
248                       const char *vallist, size_t valsize)
249 {
250   int i;
251   
252   for (i = 0 ; arglist [i] ; i++)
253     if (!memcmp (value, vallist + valsize * i, valsize))
254       return arglist [i];
255   return NULL;
256 }
257
258 #ifdef TEST
259 /*
260  * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
261  */
262 char * program_name;
263 extern const char * getenv ();
264
265 /* When to make backup files.  */
266 enum backup_type
267 {
268   /* Never make backups.  */
269   none,
270
271   /* Make simple backups of every file.  */
272   simple,
273
274   /* Make numbered backups of files that already have numbered backups,
275      and simple backups of the others.  */
276   numbered_existing,
277
278   /* Make numbered backups of every file.  */
279   numbered
280 };
281
282 /* Two tables describing arguments (keys) and their corresponding
283    values */
284 static const char *const backup_args[] =
285 {
286   "no", "none", "off", 
287   "simple", "never", 
288   "existing", "nil", 
289   "numbered", "t", 
290   0
291 };
292
293 static const enum backup_type backup_vals[] =
294 {
295   none, none, none, 
296   simple, simple, 
297   numbered_existing, numbered_existing, 
298   numbered, numbered
299 };
300
301 int
302 main (int argc, const char *const * argv)
303 {
304   const char * cp;
305   enum backup_type backup_type = none;
306
307   program_name = (char *) argv [0];
308
309   if (argc > 2)
310     {
311       fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
312       exit (1);
313     }
314
315   if ((cp = getenv ("VERSION_CONTROL")))
316     backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp, 
317                                  backup_args, backup_vals);
318
319   if (argc == 2)
320     backup_type = XARGCASEMATCH (program_name, argv [1], 
321                                  backup_args, backup_vals);
322
323   printf ("The version control is `%s'\n", 
324           ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
325
326   return 0;
327 }
328 #endif