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