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