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