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