4e879691f4ce7799af51c512862223f584f9f8b0
[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
134   /* Make sure to have a good quoting style to report errors.
135      literal is insane here. */
136   saved_quoting_style = get_quoting_style (NULL);
137   set_quoting_style (NULL, ARGMATCH_QUOTING_STYLE);
138
139   /* There is an error */
140   fprintf (stderr, "%s: ", program_name);
141   if (problem == -1)
142     fprintf (stderr, _("invalid argument %s for `%s'"),
143              quotearg (value), kind);
144   else                          /* Assume -2.  */
145     fprintf (stderr, _("ambiguous argument %s for `%s'"),
146              quotearg (value), kind);
147   putc ('\n', stderr);
148
149   set_quoting_style (NULL, saved_quoting_style);
150 }
151
152 /* List the valid arguments for argmatch.
153    ARGLIST is the same as in argmatch.
154    VALLIST is a pointer to an array of values.
155    VALSIZE is the size of the elements of VALLIST */
156 void
157 argmatch_valid (const char *const *arglist,
158                 const char *vallist, size_t valsize)
159 {
160   int i;
161   const char *last_val = NULL;
162
163   /* We try to put synonyms on the same line.  The assumption is that
164      synonyms follow each other */
165   fprintf (stderr, _("Valid arguments are:"));
166   for (i = 0; arglist[i]; i++)
167     if ((i == 0)
168         || memcmp (last_val, vallist + valsize * i, valsize))
169       {
170         fprintf (stderr, "\n  - `%s'", arglist[i]);
171         last_val = vallist + valsize * i;
172       }
173     else
174       {
175         fprintf (stderr, ", `%s'", arglist[i]);
176       }
177   putc ('\n', stderr);
178 }
179
180 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
181    return the first corresponding argument in ARGLIST */
182 const char *
183 argmatch_to_argument (const char *value,
184                       const char *const *arglist,
185                       const char *vallist, size_t valsize)
186 {
187   int i;
188
189   for (i = 0; arglist[i]; i++)
190     if (!memcmp (value, vallist + valsize * i, valsize))
191       return arglist[i];
192   return NULL;
193 }
194
195 #ifdef TEST
196 /*
197  * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
198  */
199 char *rogram_name;
200 extern const char *getenv ();
201
202 /* When to make backup files.  */
203 enum backup_type
204 {
205   /* Never make backups.  */
206   none,
207
208   /* Make simple backups of every file.  */
209   simple,
210
211   /* Make numbered backups of files that already have numbered backups,
212      and simple backups of the others.  */
213   numbered_existing,
214
215   /* Make numbered backups of every file.  */
216   numbered
217 };
218
219 /* Two tables describing arguments (keys) and their corresponding
220    values */
221 static const char *const backup_args[] =
222 {
223   "no", "none", "off",
224   "simple", "never",
225   "existing", "nil",
226   "numbered", "t",
227   0
228 };
229
230 static const enum backup_type backup_vals[] =
231 {
232   none, none, none,
233   simple, simple,
234   numbered_existing, numbered_existing,
235   numbered, numbered
236 };
237
238 int
239 main (int argc, const char *const *argv)
240 {
241   const char *cp;
242   enum backup_type backup_type = none;
243
244   program_name = (char *) argv[0];
245
246   if (argc > 2)
247     {
248       fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
249       exit (1);
250     }
251
252   if ((cp = getenv ("VERSION_CONTROL")))
253     backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp,
254                                  backup_args, backup_vals);
255
256   if (argc == 2)
257     backup_type = XARGCASEMATCH (program_name, argv[1],
258                                  backup_args, backup_vals);
259
260   printf ("The version control is `%s'\n",
261           ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
262
263   return 0;
264 }
265 #endif