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