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