use _GL_ATTRIBUTE_CONST and _GL_ATTRIBUTE_PURE
[gnulib.git] / lib / argmatch.c
1 /* argmatch.c -- find a match for a string in an array
2
3    Copyright (C) 1990, 1998-1999, 2001-2007, 2009-2011 Free Software
4    Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by David MacKenzie <djm@ai.mit.edu>
20    Modified by Akim Demaille <demaille@inf.enst.fr> */
21
22 #include <config.h>
23
24 /* Specification.  */
25 #include "argmatch.h"
26
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 #include "error.h"
36 #include "quotearg.h"
37 #include "quote.h"
38
39 #if USE_UNLOCKED_IO
40 # include "unlocked-io.h"
41 #endif
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 /* Non failing version of argmatch call this function after failing. */
51 #ifndef ARGMATCH_DIE
52 # include "exitfail.h"
53 # define ARGMATCH_DIE exit (exit_failure)
54 #endif
55
56 #ifdef ARGMATCH_DIE_DECL
57 ARGMATCH_DIE_DECL;
58 #endif
59
60 /* The attribute __pure__ was added in gcc 2.96.  */
61 #undef _GL_ATTRIBUTE_PURE
62 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
63 # define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
64 #else
65 # define _GL_ATTRIBUTE_PURE /* empty */
66 #endif
67
68 static void
69 __argmatch_die (void)
70 {
71   ARGMATCH_DIE;
72 }
73
74 /* Used by XARGMATCH and XARGCASEMATCH.  See description in argmatch.h.
75    Default to __argmatch_die, but allow caller to change this at run-time. */
76 argmatch_exit_fn argmatch_die = __argmatch_die;
77
78 \f
79 /* If ARG is an unambiguous match for an element of the
80    NULL-terminated array ARGLIST, return the index in ARGLIST
81    of the matched element, else -1 if it does not match any element
82    or -2 if it is ambiguous (is a prefix of more than one element).
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 ptrdiff_t _GL_ATTRIBUTE_PURE
91 argmatch (const char *arg, const char *const *arglist,
92           const char *vallist, size_t valsize)
93 {
94   size_t i;                     /* Temporary index in ARGLIST.  */
95   size_t arglen;                /* Length of ARG.  */
96   ptrdiff_t matchind = -1;      /* Index of first nonexact match.  */
97   bool ambiguous = false;       /* If true, multiple nonexact match(es).  */
98
99   arglen = strlen (arg);
100
101   /* Test all elements for either exact match or abbreviated matches.  */
102   for (i = 0; arglist[i]; i++)
103     {
104       if (!strncmp (arglist[i], arg, arglen))
105         {
106           if (strlen (arglist[i]) == arglen)
107             /* Exact match found.  */
108             return i;
109           else if (matchind == -1)
110             /* First nonexact match found.  */
111             matchind = i;
112           else
113             {
114               /* Second nonexact match found.  */
115               if (vallist == NULL
116                   || memcmp (vallist + valsize * matchind,
117                              vallist + valsize * i, valsize))
118                 {
119                   /* There is a real ambiguity, or we could not
120                      disambiguate. */
121                   ambiguous = true;
122                 }
123             }
124         }
125     }
126   if (ambiguous)
127     return -2;
128   else
129     return matchind;
130 }
131
132 /* Error reporting for argmatch.
133    CONTEXT is a description of the type of entity that was being matched.
134    VALUE is the invalid value that was given.
135    PROBLEM is the return value from argmatch.  */
136
137 void
138 argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
139 {
140   char const *format = (problem == -1
141                         ? _("invalid argument %s for %s")
142                         : _("ambiguous argument %s for %s"));
143
144   error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
145          quote_n (1, context));
146 }
147
148 /* List the valid arguments for argmatch.
149    ARGLIST is the same as in argmatch.
150    VALLIST is a pointer to an array of values.
151    VALSIZE is the size of the elements of VALLIST */
152 void
153 argmatch_valid (const char *const *arglist,
154                 const char *vallist, size_t valsize)
155 {
156   size_t i;
157   const char *last_val = NULL;
158
159   /* We try to put synonyms on the same line.  The assumption is that
160      synonyms follow each other */
161   fprintf (stderr, _("Valid arguments are:"));
162   for (i = 0; arglist[i]; i++)
163     if ((i == 0)
164         || memcmp (last_val, vallist + valsize * i, valsize))
165       {
166         fprintf (stderr, "\n  - `%s'", arglist[i]);
167         last_val = vallist + valsize * i;
168       }
169     else
170       {
171         fprintf (stderr, ", `%s'", arglist[i]);
172       }
173   putc ('\n', stderr);
174 }
175
176 /* Never failing versions of the previous functions.
177
178    CONTEXT is the context for which argmatch is called (e.g.,
179    "--version-control", or "$VERSION_CONTROL" etc.).  Upon failure,
180    calls the (supposed never to return) function EXIT_FN. */
181
182 ptrdiff_t
183 __xargmatch_internal (const char *context,
184                       const char *arg, const char *const *arglist,
185                       const char *vallist, size_t valsize,
186                       argmatch_exit_fn exit_fn)
187 {
188   ptrdiff_t res = argmatch (arg, arglist, vallist, valsize);
189   if (res >= 0)
190     /* Success. */
191     return res;
192
193   /* We failed.  Explain why. */
194   argmatch_invalid (context, arg, res);
195   argmatch_valid (arglist, vallist, valsize);
196   (*exit_fn) ();
197
198   return -1; /* To please the compilers. */
199 }
200
201 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
202    return the first corresponding argument in ARGLIST */
203 const char *
204 argmatch_to_argument (const char *value,
205                       const char *const *arglist,
206                       const char *vallist, size_t valsize)
207 {
208   size_t i;
209
210   for (i = 0; arglist[i]; i++)
211     if (!memcmp (value, vallist + valsize * i, valsize))
212       return arglist[i];
213   return NULL;
214 }
215
216 #ifdef TEST
217 /*
218  * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
219  */
220 char *program_name;
221
222 /* When to make backup files.  */
223 enum backup_type
224 {
225   /* Never make backups.  */
226   no_backups,
227
228   /* Make simple backups of every file.  */
229   simple_backups,
230
231   /* Make numbered backups of files that already have numbered backups,
232      and simple backups of the others.  */
233   numbered_existing_backups,
234
235   /* Make numbered backups of every file.  */
236   numbered_backups
237 };
238
239 /* Two tables describing arguments (keys) and their corresponding
240    values */
241 static const char *const backup_args[] =
242 {
243   "no", "none", "off",
244   "simple", "never",
245   "existing", "nil",
246   "numbered", "t",
247   0
248 };
249
250 static const enum backup_type backup_vals[] =
251 {
252   no_backups, no_backups, no_backups,
253   simple_backups, simple_backups,
254   numbered_existing_backups, numbered_existing_backups,
255   numbered_backups, numbered_backups
256 };
257
258 int
259 main (int argc, const char *const *argv)
260 {
261   const char *cp;
262   enum backup_type backup_type = no_backups;
263
264   program_name = (char *) argv[0];
265
266   if (argc > 2)
267     {
268       fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
269       exit (1);
270     }
271
272   if ((cp = getenv ("VERSION_CONTROL")))
273     backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
274                              backup_args, backup_vals);
275
276   if (argc == 2)
277     backup_type = XARGMATCH (program_name, argv[1],
278                              backup_args, backup_vals);
279
280   printf ("The version control is `%s'\n",
281           ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
282
283   return 0;
284 }
285 #endif