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