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