Blame lib/argmatch.c

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