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