Blame lib/argmatch.c

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