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