Blame lib/argmatch.c

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