OLD | NEW |
(Empty) | |
| 1 /* argmatch.c -- find a match for a string in an array |
| 2 |
| 3 Copyright (C) 1990, 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007 |
| 4 Free Software 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 static void |
| 61 __argmatch_die (void) |
| 62 { |
| 63 ARGMATCH_DIE; |
| 64 } |
| 65 |
| 66 /* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h. |
| 67 Default to __argmatch_die, but allow caller to change this at run-time. */ |
| 68 argmatch_exit_fn argmatch_die = __argmatch_die; |
| 69 |
| 70 |
| 71 /* If ARG is an unambiguous match for an element of the |
| 72 NULL-terminated array ARGLIST, return the index in ARGLIST |
| 73 of the matched element, else -1 if it does not match any element |
| 74 or -2 if it is ambiguous (is a prefix of more than one element). |
| 75 |
| 76 If VALLIST is none null, use it to resolve ambiguities limited to |
| 77 synonyms, i.e., for |
| 78 "yes", "yop" -> 0 |
| 79 "no", "nope" -> 1 |
| 80 "y" is a valid argument, for `0', and "n" for `1'. */ |
| 81 |
| 82 ptrdiff_t |
| 83 argmatch (const char *arg, const char *const *arglist, |
| 84 const char *vallist, size_t valsize) |
| 85 { |
| 86 size_t i; /* Temporary index in ARGLIST. */ |
| 87 size_t arglen; /* Length of ARG. */ |
| 88 ptrdiff_t matchind = -1; /* Index of first nonexact match. */ |
| 89 bool ambiguous = false; /* If true, multiple nonexact match(es). */ |
| 90 |
| 91 arglen = strlen (arg); |
| 92 |
| 93 /* Test all elements for either exact match or abbreviated matches. */ |
| 94 for (i = 0; arglist[i]; i++) |
| 95 { |
| 96 if (!strncmp (arglist[i], arg, arglen)) |
| 97 { |
| 98 if (strlen (arglist[i]) == arglen) |
| 99 /* Exact match found. */ |
| 100 return i; |
| 101 else if (matchind == -1) |
| 102 /* First nonexact match found. */ |
| 103 matchind = i; |
| 104 else |
| 105 { |
| 106 /* Second nonexact match found. */ |
| 107 if (vallist == NULL |
| 108 || memcmp (vallist + valsize * matchind, |
| 109 vallist + valsize * i, valsize)) |
| 110 { |
| 111 /* There is a real ambiguity, or we could not |
| 112 disambiguate. */ |
| 113 ambiguous = true; |
| 114 } |
| 115 } |
| 116 } |
| 117 } |
| 118 if (ambiguous) |
| 119 return -2; |
| 120 else |
| 121 return matchind; |
| 122 } |
| 123 |
| 124 /* Error reporting for argmatch. |
| 125 CONTEXT 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 *context, const char *value, ptrdiff_t problem) |
| 131 { |
| 132 char const *format = (problem == -1 |
| 133 ? _("invalid argument %s for %s") |
| 134 : _("ambiguous argument %s for %s")); |
| 135 |
| 136 error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value), |
| 137 quote_n (1, context)); |
| 138 } |
| 139 |
| 140 /* List the valid arguments for argmatch. |
| 141 ARGLIST is the same as in argmatch. |
| 142 VALLIST is a pointer to an array of values. |
| 143 VALSIZE is the size of the elements of VALLIST */ |
| 144 void |
| 145 argmatch_valid (const char *const *arglist, |
| 146 const char *vallist, size_t valsize) |
| 147 { |
| 148 size_t i; |
| 149 const char *last_val = NULL; |
| 150 |
| 151 /* We try to put synonyms on the same line. The assumption is that |
| 152 synonyms follow each other */ |
| 153 fprintf (stderr, _("Valid arguments are:")); |
| 154 for (i = 0; arglist[i]; i++) |
| 155 if ((i == 0) |
| 156 || memcmp (last_val, vallist + valsize * i, valsize)) |
| 157 { |
| 158 fprintf (stderr, "\n - `%s'", arglist[i]); |
| 159 last_val = vallist + valsize * i; |
| 160 } |
| 161 else |
| 162 { |
| 163 fprintf (stderr, ", `%s'", arglist[i]); |
| 164 } |
| 165 putc ('\n', stderr); |
| 166 } |
| 167 |
| 168 /* Never failing versions of the previous functions. |
| 169 |
| 170 CONTEXT is the context for which argmatch is called (e.g., |
| 171 "--version-control", or "$VERSION_CONTROL" etc.). Upon failure, |
| 172 calls the (supposed never to return) function EXIT_FN. */ |
| 173 |
| 174 ptrdiff_t |
| 175 __xargmatch_internal (const char *context, |
| 176 const char *arg, const char *const *arglist, |
| 177 const char *vallist, size_t valsize, |
| 178 argmatch_exit_fn exit_fn) |
| 179 { |
| 180 ptrdiff_t res = argmatch (arg, arglist, vallist, valsize); |
| 181 if (res >= 0) |
| 182 /* Success. */ |
| 183 return res; |
| 184 |
| 185 /* We failed. Explain why. */ |
| 186 argmatch_invalid (context, arg, res); |
| 187 argmatch_valid (arglist, vallist, valsize); |
| 188 (*exit_fn) (); |
| 189 |
| 190 return -1; /* To please the compilers. */ |
| 191 } |
| 192 |
| 193 /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and |
| 194 return the first corresponding argument in ARGLIST */ |
| 195 const char * |
| 196 argmatch_to_argument (const char *value, |
| 197 const char *const *arglist, |
| 198 const char *vallist, size_t valsize) |
| 199 { |
| 200 size_t i; |
| 201 |
| 202 for (i = 0; arglist[i]; i++) |
| 203 if (!memcmp (value, vallist + valsize * i, valsize)) |
| 204 return arglist[i]; |
| 205 return NULL; |
| 206 } |
| 207 |
| 208 #ifdef TEST |
| 209 /* |
| 210 * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu> |
| 211 */ |
| 212 char *program_name; |
| 213 |
| 214 /* When to make backup files. */ |
| 215 enum backup_type |
| 216 { |
| 217 /* Never make backups. */ |
| 218 no_backups, |
| 219 |
| 220 /* Make simple backups of every file. */ |
| 221 simple_backups, |
| 222 |
| 223 /* Make numbered backups of files that already have numbered backups, |
| 224 and simple backups of the others. */ |
| 225 numbered_existing_backups, |
| 226 |
| 227 /* Make numbered backups of every file. */ |
| 228 numbered_backups |
| 229 }; |
| 230 |
| 231 /* Two tables describing arguments (keys) and their corresponding |
| 232 values */ |
| 233 static const char *const backup_args[] = |
| 234 { |
| 235 "no", "none", "off", |
| 236 "simple", "never", |
| 237 "existing", "nil", |
| 238 "numbered", "t", |
| 239 0 |
| 240 }; |
| 241 |
| 242 static const enum backup_type backup_vals[] = |
| 243 { |
| 244 no_backups, no_backups, no_backups, |
| 245 simple_backups, simple_backups, |
| 246 numbered_existing_backups, numbered_existing_backups, |
| 247 numbered_backups, numbered_backups |
| 248 }; |
| 249 |
| 250 int |
| 251 main (int argc, const char *const *argv) |
| 252 { |
| 253 const char *cp; |
| 254 enum backup_type backup_type = no_backups; |
| 255 |
| 256 program_name = (char *) argv[0]; |
| 257 |
| 258 if (argc > 2) |
| 259 { |
| 260 fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name); |
| 261 exit (1); |
| 262 } |
| 263 |
| 264 if ((cp = getenv ("VERSION_CONTROL"))) |
| 265 backup_type = XARGMATCH ("$VERSION_CONTROL", cp, |
| 266 backup_args, backup_vals); |
| 267 |
| 268 if (argc == 2) |
| 269 backup_type = XARGMATCH (program_name, argv[1], |
| 270 backup_args, backup_vals); |
| 271 |
| 272 printf ("The version control is `%s'\n", |
| 273 ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals)); |
| 274 |
| 275 return 0; |
| 276 } |
| 277 #endif |
OLD | NEW |