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