Blame lib/argmatch.c

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