Blame lib/argmatch.c

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