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