(argmatch_invalid): Remove double quotes from the
[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 "quotearg.h"
40
41 /* When reporting a failing argument, make sure to show invisible
42    characters hidden using the quoting style
43    ARGMATCH_QUOTING_STYLE. literal_quoting_style is not good.  */
44
45 #ifndef ARGMATCH_QUOTING_STYLE
46 # define ARGMATCH_QUOTING_STYLE c_quoting_style
47 #endif
48
49 extern char *program_name;
50
51 /* If ARG is an unambiguous match for an element of the
52    null-terminated array ARGLIST, return the index in ARGLIST
53    of the matched element, else -1 if it does not match any element
54    or -2 if it is ambiguous (is a prefix of more than one element).
55    If SENSITIVE, comparison is case sensitive.
56
57    If VALLIST is none null, use it to resolve ambiguities limited to
58    synonyms, i.e., for
59      "yes", "yop" -> 0
60      "no", "nope" -> 1
61    "y" is a valid argument, for `0', and "n" for `1'.  */
62
63 static int
64 __argmatch_internal (const char *arg, const char *const *arglist,
65                      const char *vallist, size_t valsize,
66                      int case_sensitive)
67 {
68   int i;                        /* Temporary index in ARGLIST.  */
69   size_t arglen;                /* Length of ARG.  */
70   int matchind = -1;            /* Index of first nonexact match.  */
71   int ambiguous = 0;            /* If nonzero, multiple nonexact match(es).  */
72
73   arglen = strlen (arg);
74
75   /* Test all elements for either exact match or abbreviated matches.  */
76   for (i = 0; arglist[i]; i++)
77     {
78       if (case_sensitive
79           ? !strncmp (arglist[i], arg, arglen)
80           : !strncasecmp (arglist[i], arg, arglen))
81         {
82           if (strlen (arglist[i]) == arglen)
83             /* Exact match found.  */
84             return i;
85           else if (matchind == -1)
86             /* First nonexact match found.  */
87             matchind = i;
88           else
89             {
90               /* Second nonexact match found.  */
91               if (vallist == NULL
92                   || memcmp (vallist + valsize * matchind,
93                              vallist + valsize * i, valsize))
94                 {
95                   /* There is a real ambiguity, or we could not
96                      disambiguate. */
97                   ambiguous = 1;
98                 }
99             }
100         }
101     }
102   if (ambiguous)
103     return -2;
104   else
105     return matchind;
106 }
107
108 /* argmatch - case sensitive version */
109 int
110 argmatch (const char *arg, const char *const *arglist,
111           const char *vallist, size_t valsize)
112 {
113   return __argmatch_internal (arg, arglist, vallist, valsize, 1);
114 }
115
116 /* argcasematch - case insensitive version */
117 int
118 argcasematch (const char *arg, const char *const *arglist,
119               const char *vallist, size_t valsize)
120 {
121   return __argmatch_internal (arg, arglist, vallist, valsize, 0);
122 }
123
124 /* Error reporting for argmatch.
125    KIND is a description of the type of entity that was being matched.
126    VALUE is the invalid value that was given.
127    PROBLEM is the return value from argmatch.  */
128
129 void
130 argmatch_invalid (const char *kind, const char *value, int problem)
131 {
132   enum quoting_style saved_quoting_style;
133   char const *format;
134   char *quoted_arg;
135
136   /* Make sure to have a good quoting style to report errors.
137      literal is insane here. */
138   saved_quoting_style = get_quoting_style (NULL);
139   set_quoting_style (NULL, ARGMATCH_QUOTING_STYLE);
140
141   /* There is an error */
142   quoted_arg = quotearg (value);
143
144   /* Skip over the first quote character, and overwrite the last one.  */
145   ++quoted_arg;
146   quoted_arg[strlen (quoted_arg) - 1] = '\0';
147
148   format = (problem == -1
149             ? _("%s: invalid argument `%s' for `%s'\n")
150             : _("%s: ambiguous argument `%s' for `%s'\n"));
151
152   fprintf (stderr, format, program_name, quoted_arg, kind);
153
154   set_quoting_style (NULL, saved_quoting_style);
155 }
156
157 /* List the valid arguments for argmatch.
158    ARGLIST is the same as in argmatch.
159    VALLIST is a pointer to an array of values.
160    VALSIZE is the size of the elements of VALLIST */
161 void
162 argmatch_valid (const char *const *arglist,
163                 const char *vallist, size_t valsize)
164 {
165   int i;
166   const char *last_val = NULL;
167
168   /* We try to put synonyms on the same line.  The assumption is that
169      synonyms follow each other */
170   fprintf (stderr, _("Valid arguments are:"));
171   for (i = 0; arglist[i]; i++)
172     if ((i == 0)
173         || memcmp (last_val, vallist + valsize * i, valsize))
174       {
175         fprintf (stderr, "\n  - `%s'", arglist[i]);
176         last_val = vallist + valsize * i;
177       }
178     else
179       {
180         fprintf (stderr, ", `%s'", arglist[i]);
181       }
182   putc ('\n', stderr);
183 }
184
185 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
186    return the first corresponding argument in ARGLIST */
187 const char *
188 argmatch_to_argument (const char *value,
189                       const char *const *arglist,
190                       const char *vallist, size_t valsize)
191 {
192   int i;
193
194   for (i = 0; arglist[i]; i++)
195     if (!memcmp (value, vallist + valsize * i, valsize))
196       return arglist[i];
197   return NULL;
198 }
199
200 #ifdef TEST
201 /*
202  * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
203  */
204 char *rogram_name;
205 extern const char *getenv ();
206
207 /* When to make backup files.  */
208 enum backup_type
209 {
210   /* Never make backups.  */
211   none,
212
213   /* Make simple backups of every file.  */
214   simple,
215
216   /* Make numbered backups of files that already have numbered backups,
217      and simple backups of the others.  */
218   numbered_existing,
219
220   /* Make numbered backups of every file.  */
221   numbered
222 };
223
224 /* Two tables describing arguments (keys) and their corresponding
225    values */
226 static const char *const backup_args[] =
227 {
228   "no", "none", "off",
229   "simple", "never",
230   "existing", "nil",
231   "numbered", "t",
232   0
233 };
234
235 static const enum backup_type backup_vals[] =
236 {
237   none, none, none,
238   simple, simple,
239   numbered_existing, numbered_existing,
240   numbered, numbered
241 };
242
243 int
244 main (int argc, const char *const *argv)
245 {
246   const char *cp;
247   enum backup_type backup_type = none;
248
249   program_name = (char *) argv[0];
250
251   if (argc > 2)
252     {
253       fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
254       exit (1);
255     }
256
257   if ((cp = getenv ("VERSION_CONTROL")))
258     backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp,
259                                  backup_args, backup_vals);
260
261   if (argc == 2)
262     backup_type = XARGCASEMATCH (program_name, argv[1],
263                                  backup_args, backup_vals);
264
265   printf ("The version control is `%s'\n",
266           ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
267
268   return 0;
269 }
270 #endif