(program_name): Remove dcl.
[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 "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 escape_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   enum quoting_style saved_quoting_style;
159   char const *format;
160
161   /* Make sure to have a good quoting style to report errors.
162      literal is insane here. */
163   saved_quoting_style = get_quoting_style (NULL);
164   set_quoting_style (NULL, ARGMATCH_QUOTING_STYLE);
165
166   format = (problem == -1
167             ? _("invalid argument `%s' for `%s'")
168             : _("ambiguous argument `%s' for `%s'"));
169
170   error (0, 0, format, quotearg (value), context);
171
172   set_quoting_style (NULL, saved_quoting_style);
173 }
174
175 /* List the valid arguments for argmatch.
176    ARGLIST is the same as in argmatch.
177    VALLIST is a pointer to an array of values.
178    VALSIZE is the size of the elements of VALLIST */
179 void
180 argmatch_valid (const char *const *arglist,
181                 const char *vallist, size_t valsize)
182 {
183   int i;
184   const char *last_val = NULL;
185
186   /* We try to put synonyms on the same line.  The assumption is that
187      synonyms follow each other */
188   fprintf (stderr, _("Valid arguments are:"));
189   for (i = 0; arglist[i]; i++)
190     if ((i == 0)
191         || memcmp (last_val, vallist + valsize * i, valsize))
192       {
193         fprintf (stderr, "\n  - `%s'", arglist[i]);
194         last_val = vallist + valsize * i;
195       }
196     else
197       {
198         fprintf (stderr, ", `%s'", arglist[i]);
199       }
200   putc ('\n', stderr);
201 }
202
203 /* Never failing versions of the previous functions.
204
205    CONTEXT is the context for which argmatch is called (e.g.,
206    "--version-control", or "$VERSION_CONTROL" etc.).  Upon failure,
207    calls the (supposed never to return) function EXIT_FN. */
208
209 int
210 __xargmatch_internal (const char *context,
211                       const char *arg, const char *const *arglist,
212                       const char *vallist, size_t valsize,
213                       int case_sensitive,
214                       argmatch_exit_fn exit_fn)
215 {
216   int res = __argmatch_internal (arg, arglist,
217                                  vallist, valsize,
218                                  case_sensitive);
219   if (res >= 0)
220     /* Success. */
221     return res;
222
223   /* We failed.  Explain why. */
224   argmatch_invalid (context, arg, res);
225   argmatch_valid (arglist, vallist, valsize);
226   (*exit_fn) ();
227
228   return -1; /* To please the compilers. */
229 }
230
231 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
232    return the first corresponding argument in ARGLIST */
233 const char *
234 argmatch_to_argument (const char *value,
235                       const char *const *arglist,
236                       const char *vallist, size_t valsize)
237 {
238   int i;
239
240   for (i = 0; arglist[i]; i++)
241     if (!memcmp (value, vallist + valsize * i, valsize))
242       return arglist[i];
243   return NULL;
244 }
245
246 #ifdef TEST
247 /*
248  * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
249  */
250 char *program_name;
251 extern const char *getenv ();
252
253 /* When to make backup files.  */
254 enum backup_type
255 {
256   /* Never make backups.  */
257   none,
258
259   /* Make simple backups of every file.  */
260   simple,
261
262   /* Make numbered backups of files that already have numbered backups,
263      and simple backups of the others.  */
264   numbered_existing,
265
266   /* Make numbered backups of every file.  */
267   numbered
268 };
269
270 /* Two tables describing arguments (keys) and their corresponding
271    values */
272 static const char *const backup_args[] =
273 {
274   "no", "none", "off",
275   "simple", "never",
276   "existing", "nil",
277   "numbered", "t",
278   0
279 };
280
281 static const enum backup_type backup_vals[] =
282 {
283   none, none, none,
284   simple, simple,
285   numbered_existing, numbered_existing,
286   numbered, numbered
287 };
288
289 int
290 main (int argc, const char *const *argv)
291 {
292   const char *cp;
293   enum backup_type backup_type = none;
294
295   program_name = (char *) argv[0];
296
297   if (argc > 2)
298     {
299       fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
300       exit (1);
301     }
302
303   if ((cp = getenv ("VERSION_CONTROL")))
304     backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp,
305                                  backup_args, backup_vals);
306
307   if (argc == 2)
308     backup_type = XARGCASEMATCH (program_name, argv[1],
309                                  backup_args, backup_vals);
310
311   printf ("The version control is `%s'\n",
312           ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
313
314   return 0;
315 }
316 #endif