198038Sache/* argmatch.c -- find a match for a string in an array 298038Sache 3131447Stjr Copyright (C) 1990, 1998, 1999, 2001, 2002, 2003, 2004 Free 4131447Stjr Software Foundation, Inc. 5131447Stjr 698038Sache This program is free software; you can redistribute it and/or modify 798038Sache it under the terms of the GNU General Public License as published by 898038Sache the Free Software Foundation; either version 2, or (at your option) 998038Sache any later version. 1098038Sache 1198038Sache This program is distributed in the hope that it will be useful, 1298038Sache but WITHOUT ANY WARRANTY; without even the implied warranty of 1398038Sache MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1498038Sache GNU General Public License for more details. 1598038Sache 1698038Sache You should have received a copy of the GNU General Public License 1798038Sache along with this program; if not, write to the Free Software Foundation, 1898038Sache Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 1998038Sache 2098038Sache/* Written by David MacKenzie <djm@ai.mit.edu> 2198038Sache Modified by Akim Demaille <demaille@inf.enst.fr> */ 2298038Sache 23131447Stjr#if HAVE_CONFIG_H 24131447Stjr# include <config.h> 25131447Stjr#endif 26131447Stjr 27131447Stjr/* Specification. */ 2898038Sache#include "argmatch.h" 2998038Sache 30133543Stjr#include <stdbool.h> 3198038Sache#include <stdio.h> 32131447Stjr#include <stdlib.h> 33131447Stjr#include <string.h> 3498038Sache 35131447Stjr#include "gettext.h" 36131447Stjr#define _(msgid) gettext (msgid) 3798038Sache 3898038Sache#include "error.h" 39131447Stjr#include "exit.h" 4098038Sache#include "quotearg.h" 4198038Sache#include "quote.h" 4298038Sache#include "unlocked-io.h" 4398038Sache 4498038Sache/* When reporting an invalid argument, show nonprinting characters 4598038Sache by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use 4698038Sache literal_quoting_style. */ 4798038Sache#ifndef ARGMATCH_QUOTING_STYLE 4898038Sache# define ARGMATCH_QUOTING_STYLE locale_quoting_style 4998038Sache#endif 5098038Sache 5198038Sache/* Non failing version of argmatch call this function after failing. */ 5298038Sache#ifndef ARGMATCH_DIE 53131447Stjr# include "exitfail.h" 54131447Stjr# define ARGMATCH_DIE exit (exit_failure) 5598038Sache#endif 5698038Sache 5798038Sache#ifdef ARGMATCH_DIE_DECL 5898038SacheARGMATCH_DIE_DECL; 5998038Sache#endif 6098038Sache 6198038Sachestatic void 6298038Sache__argmatch_die (void) 6398038Sache{ 6498038Sache ARGMATCH_DIE; 6598038Sache} 6698038Sache 6798038Sache/* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h. 6898038Sache Default to __argmatch_die, but allow caller to change this at run-time. */ 6998038Sacheargmatch_exit_fn argmatch_die = __argmatch_die; 7098038Sache 7198038Sache 7298038Sache/* If ARG is an unambiguous match for an element of the 7398038Sache null-terminated array ARGLIST, return the index in ARGLIST 7498038Sache of the matched element, else -1 if it does not match any element 7598038Sache or -2 if it is ambiguous (is a prefix of more than one element). 7698038Sache 7798038Sache If VALLIST is none null, use it to resolve ambiguities limited to 7898038Sache synonyms, i.e., for 7998038Sache "yes", "yop" -> 0 8098038Sache "no", "nope" -> 1 8198038Sache "y" is a valid argument, for `0', and "n" for `1'. */ 8298038Sache 83133543Stjrptrdiff_t 84131447Stjrargmatch (const char *arg, const char *const *arglist, 85131447Stjr const char *vallist, size_t valsize) 8698038Sache{ 87133543Stjr size_t i; /* Temporary index in ARGLIST. */ 8898038Sache size_t arglen; /* Length of ARG. */ 89133543Stjr ptrdiff_t matchind = -1; /* Index of first nonexact match. */ 90133543Stjr bool ambiguous = false; /* If true, multiple nonexact match(es). */ 9198038Sache 9298038Sache arglen = strlen (arg); 9398038Sache 9498038Sache /* Test all elements for either exact match or abbreviated matches. */ 9598038Sache for (i = 0; arglist[i]; i++) 9698038Sache { 97131447Stjr if (!strncmp (arglist[i], arg, arglen)) 9898038Sache { 9998038Sache if (strlen (arglist[i]) == arglen) 10098038Sache /* Exact match found. */ 10198038Sache return i; 10298038Sache else if (matchind == -1) 10398038Sache /* First nonexact match found. */ 10498038Sache matchind = i; 10598038Sache else 10698038Sache { 10798038Sache /* Second nonexact match found. */ 10898038Sache if (vallist == NULL 10998038Sache || memcmp (vallist + valsize * matchind, 11098038Sache vallist + valsize * i, valsize)) 11198038Sache { 11298038Sache /* There is a real ambiguity, or we could not 11398038Sache disambiguate. */ 114133543Stjr ambiguous = true; 11598038Sache } 11698038Sache } 11798038Sache } 11898038Sache } 11998038Sache if (ambiguous) 12098038Sache return -2; 12198038Sache else 12298038Sache return matchind; 12398038Sache} 12498038Sache 12598038Sache/* Error reporting for argmatch. 12698038Sache CONTEXT is a description of the type of entity that was being matched. 12798038Sache VALUE is the invalid value that was given. 12898038Sache PROBLEM is the return value from argmatch. */ 12998038Sache 13098038Sachevoid 131133543Stjrargmatch_invalid (const char *context, const char *value, ptrdiff_t problem) 13298038Sache{ 13398038Sache char const *format = (problem == -1 13498038Sache ? _("invalid argument %s for %s") 13598038Sache : _("ambiguous argument %s for %s")); 13698038Sache 13798038Sache error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value), 13898038Sache quote_n (1, context)); 13998038Sache} 14098038Sache 14198038Sache/* List the valid arguments for argmatch. 14298038Sache ARGLIST is the same as in argmatch. 14398038Sache VALLIST is a pointer to an array of values. 14498038Sache VALSIZE is the size of the elements of VALLIST */ 14598038Sachevoid 14698038Sacheargmatch_valid (const char *const *arglist, 14798038Sache const char *vallist, size_t valsize) 14898038Sache{ 149133543Stjr size_t i; 15098038Sache const char *last_val = NULL; 15198038Sache 15298038Sache /* We try to put synonyms on the same line. The assumption is that 15398038Sache synonyms follow each other */ 15498038Sache fprintf (stderr, _("Valid arguments are:")); 15598038Sache for (i = 0; arglist[i]; i++) 15698038Sache if ((i == 0) 15798038Sache || memcmp (last_val, vallist + valsize * i, valsize)) 15898038Sache { 15998038Sache fprintf (stderr, "\n - `%s'", arglist[i]); 16098038Sache last_val = vallist + valsize * i; 16198038Sache } 16298038Sache else 16398038Sache { 16498038Sache fprintf (stderr, ", `%s'", arglist[i]); 16598038Sache } 16698038Sache putc ('\n', stderr); 16798038Sache} 16898038Sache 16998038Sache/* Never failing versions of the previous functions. 17098038Sache 17198038Sache CONTEXT is the context for which argmatch is called (e.g., 17298038Sache "--version-control", or "$VERSION_CONTROL" etc.). Upon failure, 17398038Sache calls the (supposed never to return) function EXIT_FN. */ 17498038Sache 175133543Stjrptrdiff_t 17698038Sache__xargmatch_internal (const char *context, 17798038Sache const char *arg, const char *const *arglist, 17898038Sache const char *vallist, size_t valsize, 17998038Sache argmatch_exit_fn exit_fn) 18098038Sache{ 181133543Stjr ptrdiff_t res = argmatch (arg, arglist, vallist, valsize); 18298038Sache if (res >= 0) 18398038Sache /* Success. */ 18498038Sache return res; 18598038Sache 18698038Sache /* We failed. Explain why. */ 18798038Sache argmatch_invalid (context, arg, res); 18898038Sache argmatch_valid (arglist, vallist, valsize); 18998038Sache (*exit_fn) (); 19098038Sache 19198038Sache return -1; /* To please the compilers. */ 19298038Sache} 19398038Sache 19498038Sache/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and 19598038Sache return the first corresponding argument in ARGLIST */ 19698038Sacheconst char * 19798038Sacheargmatch_to_argument (const char *value, 19898038Sache const char *const *arglist, 19998038Sache const char *vallist, size_t valsize) 20098038Sache{ 201133543Stjr size_t i; 20298038Sache 20398038Sache for (i = 0; arglist[i]; i++) 20498038Sache if (!memcmp (value, vallist + valsize * i, valsize)) 20598038Sache return arglist[i]; 20698038Sache return NULL; 20798038Sache} 20898038Sache 20998038Sache#ifdef TEST 21098038Sache/* 21198038Sache * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu> 21298038Sache */ 21398038Sachechar *program_name; 21498038Sache 21598038Sache/* When to make backup files. */ 21698038Sacheenum backup_type 21798038Sache{ 21898038Sache /* Never make backups. */ 21998038Sache none, 22098038Sache 22198038Sache /* Make simple backups of every file. */ 22298038Sache simple, 22398038Sache 22498038Sache /* Make numbered backups of files that already have numbered backups, 22598038Sache and simple backups of the others. */ 22698038Sache numbered_existing, 22798038Sache 22898038Sache /* Make numbered backups of every file. */ 22998038Sache numbered 23098038Sache}; 23198038Sache 23298038Sache/* Two tables describing arguments (keys) and their corresponding 23398038Sache values */ 23498038Sachestatic const char *const backup_args[] = 23598038Sache{ 23698038Sache "no", "none", "off", 23798038Sache "simple", "never", 23898038Sache "existing", "nil", 23998038Sache "numbered", "t", 24098038Sache 0 24198038Sache}; 24298038Sache 24398038Sachestatic const enum backup_type backup_vals[] = 24498038Sache{ 24598038Sache none, none, none, 24698038Sache simple, simple, 24798038Sache numbered_existing, numbered_existing, 24898038Sache numbered, numbered 24998038Sache}; 25098038Sache 25198038Sacheint 25298038Sachemain (int argc, const char *const *argv) 25398038Sache{ 25498038Sache const char *cp; 25598038Sache enum backup_type backup_type = none; 25698038Sache 25798038Sache program_name = (char *) argv[0]; 25898038Sache 25998038Sache if (argc > 2) 26098038Sache { 26198038Sache fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name); 26298038Sache exit (1); 26398038Sache } 26498038Sache 26598038Sache if ((cp = getenv ("VERSION_CONTROL"))) 266131447Stjr backup_type = XARGMATCH ("$VERSION_CONTROL", cp, 267131447Stjr backup_args, backup_vals); 26898038Sache 26998038Sache if (argc == 2) 270131447Stjr backup_type = XARGMATCH (program_name, argv[1], 271131447Stjr backup_args, backup_vals); 27298038Sache 27398038Sache printf ("The version control is `%s'\n", 27498038Sache ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals)); 27598038Sache 27698038Sache return 0; 27798038Sache} 27898038Sache#endif 279