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