text: /* argmatch.c -- find a match for a string in an array
  
     Copyright (C) 1990, 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
     2009, 2010 Free Software Foundation, Inc.
  
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 3 of the License, or
     (at your option) any later version.
  
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
  
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  
  /* Written by David MacKenzie <djm@ai.mit.edu>
     Modified by Akim Demaille <demaille@inf.enst.fr> */
  
  #include <config.h>
  
  /* Specification.  */
  #include "argmatch.h"
  
  #include <stdbool.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "gettext.h"
  #define _(msgid) gettext (msgid)
  
  #include "error.h"
  #include "quotearg.h"
  #include "quote.h"
  
  #if USE_UNLOCKED_IO
  # include "unlocked-io.h"
  #endif
  
  /* When reporting an invalid argument, show nonprinting characters
     by using the quoting style ARGMATCH_QUOTING_STYLE.  Do not use
     literal_quoting_style.  */
  #ifndef ARGMATCH_QUOTING_STYLE
  # define ARGMATCH_QUOTING_STYLE locale_quoting_style
  #endif
  
  /* Non failing version of argmatch call this function after failing. */
  #ifndef ARGMATCH_DIE
  # include "exitfail.h"
  # define ARGMATCH_DIE exit (exit_failure)
  #endif
  
  #ifdef ARGMATCH_DIE_DECL
  ARGMATCH_DIE_DECL;
  #endif
  
  static void
  __argmatch_die (void)
  {
    ARGMATCH_DIE;
  }
  
  /* Used by XARGMATCH and XARGCASEMATCH.  See description in argmatch.h.
     Default to __argmatch_die, but allow caller to change this at run-time. */
  argmatch_exit_fn argmatch_die = __argmatch_die;
  
  
  /* If ARG is an unambiguous match for an element of the
     NULL-terminated array ARGLIST, return the index in ARGLIST
     of the matched element, else -1 if it does not match any element
     or -2 if it is ambiguous (is a prefix of more than one element).
  
     If VALLIST is none null, use it to resolve ambiguities limited to
     synonyms, i.e., for
       "yes", "yop" -> 0
       "no", "nope" -> 1
     "y" is a valid argument, for `0', and "n" for `1'.  */
  
  ptrdiff_t
  argmatch (const char *arg, const char *const *arglist,
            const char *vallist, size_t valsize)
  {
    size_t i;                     /* Temporary index in ARGLIST.  */
    size_t arglen;                /* Length of ARG.  */
    ptrdiff_t matchind = -1;      /* Index of first nonexact match.  */
    bool ambiguous = false;       /* If true, multiple nonexact match(es).  */
  
    arglen = strlen (arg);
  
    /* Test all elements for either exact match or abbreviated matches.  */
    for (i = 0; arglist[i]; i++)
      {
        if (!strncmp (arglist[i], arg, arglen))
          {
            if (strlen (arglist[i]) == arglen)
              /* Exact match found.  */
              return i;
            else if (matchind == -1)
              /* First nonexact match found.  */
              matchind = i;
            else
              {
                /* Second nonexact match found.  */
                if (vallist == NULL
                    || memcmp (vallist + valsize * matchind,
                               vallist + valsize * i, valsize))
                  {
                    /* There is a real ambiguity, or we could not
                       disambiguate. */
                    ambiguous = true;
                  }
              }
          }
      }
    if (ambiguous)
      return -2;
    else
      return matchind;
  }
  
  /* Error reporting for argmatch.
     CONTEXT is a description of the type of entity that was being matched.
     VALUE is the invalid value that was given.
     PROBLEM is the return value from argmatch.  */
  
  void
  argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
  {
    char const *format = (problem == -1
                          ? _("invalid argument %s for %s")
                          : _("ambiguous argument %s for %s"));
  
    error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
           quote_n (1, context));
  }
  
  /* List the valid arguments for argmatch.
     ARGLIST is the same as in argmatch.
     VALLIST is a pointer to an array of values.
     VALSIZE is the size of the elements of VALLIST */
  void
  argmatch_valid (const char *const *arglist,
                  const char *vallist, size_t valsize)
  {
    size_t i;
    const char *last_val = NULL;
  
    /* We try to put synonyms on the same line.  The assumption is that
       synonyms follow each other */
    fprintf (stderr, _("Valid arguments are:"));
    for (i = 0; arglist[i]; i++)
      if ((i == 0)
          || memcmp (last_val, vallist + valsize * i, valsize))
        {
          fprintf (stderr, "\n  - `%s'", arglist[i]);
          last_val = vallist + valsize * i;
        }
      else
        {
          fprintf (stderr, ", `%s'", arglist[i]);
        }
    putc ('\n', stderr);
  }
  
  /* Never failing versions of the previous functions.
  
     CONTEXT is the context for which argmatch is called (e.g.,
     "--version-control", or "$VERSION_CONTROL" etc.).  Upon failure,
     calls the (supposed never to return) function EXIT_FN. */
  
  ptrdiff_t
  __xargmatch_internal (const char *context,
                        const char *arg, const char *const *arglist,
                        const char *vallist, size_t valsize,
                        argmatch_exit_fn exit_fn)
  {
    ptrdiff_t res = argmatch (arg, arglist, vallist, valsize);
    if (res >= 0)
      /* Success. */
      return res;
  
    /* We failed.  Explain why. */
    argmatch_invalid (context, arg, res);
    argmatch_valid (arglist, vallist, valsize);
    (*exit_fn) ();
  
    return -1; /* To please the compilers. */
  }
  
  /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
     return the first corresponding argument in ARGLIST */
  const char *
  argmatch_to_argument (const char *value,
                        const char *const *arglist,
                        const char *vallist, size_t valsize)
  {
    size_t i;
  
    for (i = 0; arglist[i]; i++)
      if (!memcmp (value, vallist + valsize * i, valsize))
        return arglist[i];
    return NULL;
  }
  
  #ifdef TEST
  /*
   * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
   */
  char *program_name;
  
  /* When to make backup files.  */
  enum backup_type
  {
    /* Never make backups.  */
    no_backups,
  
    /* Make simple backups of every file.  */
    simple_backups,
  
    /* Make numbered backups of files that already have numbered backups,
       and simple backups of the others.  */
    numbered_existing_backups,
  
    /* Make numbered backups of every file.  */
    numbered_backups
  };
  
  /* Two tables describing arguments (keys) and their corresponding
     values */
  static const char *const backup_args[] =
  {
    "no", "none", "off",
    "simple", "never",
    "existing", "nil",
    "numbered", "t",
    0
  };
  
  static const enum backup_type backup_vals[] =
  {
    no_backups, no_backups, no_backups,
    simple_backups, simple_backups,
    numbered_existing_backups, numbered_existing_backups,
    numbered_backups, numbered_backups
  };
  
  int
  main (int argc, const char *const *argv)
  {
    const char *cp;
    enum backup_type backup_type = no_backups;
  
    program_name = (char *) argv[0];
  
    if (argc > 2)
      {
        fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
        exit (1);
      }
  
    if ((cp = getenv ("VERSION_CONTROL")))
      backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
                               backup_args, backup_vals);
  
    if (argc == 2)
      backup_type = XARGMATCH (program_name, argv[1],
                               backup_args, backup_vals);
  
    printf ("The version control is `%s'\n",
            ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
  
    return 0;
  }
  #endif
  