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