195b7b453SJohn Marino /* argmatch.c -- find a match for a string in an array
295b7b453SJohn Marino
3*09d4459fSDaniel Fojt Copyright (C) 1990, 1998-1999, 2001-2007, 2009-2020 Free Software
4200fbe8dSJohn Marino Foundation, Inc.
595b7b453SJohn Marino
695b7b453SJohn Marino This program is free software: you can redistribute it and/or modify
795b7b453SJohn Marino it under the terms of the GNU General Public License as published by
895b7b453SJohn Marino the Free Software Foundation; either version 3 of the License, or
995b7b453SJohn Marino (at your option) any later version.
1095b7b453SJohn Marino
1195b7b453SJohn Marino This program is distributed in the hope that it will be useful,
1295b7b453SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
1395b7b453SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495b7b453SJohn Marino GNU General Public License for more details.
1595b7b453SJohn Marino
1695b7b453SJohn Marino You should have received a copy of the GNU General Public License
17*09d4459fSDaniel Fojt along with this program. If not, see <https://www.gnu.org/licenses/>. */
1895b7b453SJohn Marino
1995b7b453SJohn Marino /* Written by David MacKenzie <djm@ai.mit.edu>
2095b7b453SJohn Marino Modified by Akim Demaille <demaille@inf.enst.fr> */
2195b7b453SJohn Marino
2295b7b453SJohn Marino #include <config.h>
2395b7b453SJohn Marino
2495b7b453SJohn Marino /* Specification. */
2595b7b453SJohn Marino #include "argmatch.h"
2695b7b453SJohn Marino
2795b7b453SJohn Marino #include <stdbool.h>
2895b7b453SJohn Marino #include <stdio.h>
2995b7b453SJohn Marino #include <stdlib.h>
3095b7b453SJohn Marino #include <string.h>
3195b7b453SJohn Marino
3295b7b453SJohn Marino #define _(msgid) gettext (msgid)
3395b7b453SJohn Marino
3495b7b453SJohn Marino #include "error.h"
3595b7b453SJohn Marino #include "quotearg.h"
36*09d4459fSDaniel Fojt #include "getprogname.h"
3795b7b453SJohn Marino
3895b7b453SJohn Marino #if USE_UNLOCKED_IO
3995b7b453SJohn Marino # include "unlocked-io.h"
4095b7b453SJohn Marino #endif
4195b7b453SJohn Marino
4295b7b453SJohn Marino /* When reporting an invalid argument, show nonprinting characters
4395b7b453SJohn Marino by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use
4495b7b453SJohn Marino literal_quoting_style. */
4595b7b453SJohn Marino #ifndef ARGMATCH_QUOTING_STYLE
4695b7b453SJohn Marino # define ARGMATCH_QUOTING_STYLE locale_quoting_style
4795b7b453SJohn Marino #endif
4895b7b453SJohn Marino
4995b7b453SJohn Marino /* Non failing version of argmatch call this function after failing. */
5095b7b453SJohn Marino #ifndef ARGMATCH_DIE
5195b7b453SJohn Marino # include "exitfail.h"
5295b7b453SJohn Marino # define ARGMATCH_DIE exit (exit_failure)
5395b7b453SJohn Marino #endif
5495b7b453SJohn Marino
5595b7b453SJohn Marino #ifdef ARGMATCH_DIE_DECL
5695b7b453SJohn Marino ARGMATCH_DIE_DECL;
5795b7b453SJohn Marino #endif
5895b7b453SJohn Marino
5995b7b453SJohn Marino static void
__argmatch_die(void)6095b7b453SJohn Marino __argmatch_die (void)
6195b7b453SJohn Marino {
6295b7b453SJohn Marino ARGMATCH_DIE;
6395b7b453SJohn Marino }
6495b7b453SJohn Marino
6595b7b453SJohn Marino /* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h.
6695b7b453SJohn Marino Default to __argmatch_die, but allow caller to change this at run-time. */
6795b7b453SJohn Marino argmatch_exit_fn argmatch_die = __argmatch_die;
6895b7b453SJohn Marino
6995b7b453SJohn Marino
7095b7b453SJohn Marino /* If ARG is an unambiguous match for an element of the
7195b7b453SJohn Marino NULL-terminated array ARGLIST, return the index in ARGLIST
7295b7b453SJohn Marino of the matched element, else -1 if it does not match any element
7395b7b453SJohn Marino or -2 if it is ambiguous (is a prefix of more than one element).
7495b7b453SJohn Marino
7595b7b453SJohn Marino If VALLIST is none null, use it to resolve ambiguities limited to
7695b7b453SJohn Marino synonyms, i.e., for
7795b7b453SJohn Marino "yes", "yop" -> 0
7895b7b453SJohn Marino "no", "nope" -> 1
79cf28ed85SJohn Marino "y" is a valid argument, for 0, and "n" for 1. */
8095b7b453SJohn Marino
8195b7b453SJohn Marino ptrdiff_t
argmatch(const char * arg,const char * const * arglist,const void * vallist,size_t valsize)8295b7b453SJohn Marino argmatch (const char *arg, const char *const *arglist,
83*09d4459fSDaniel Fojt const void *vallist, size_t valsize)
8495b7b453SJohn Marino {
8595b7b453SJohn Marino size_t i; /* Temporary index in ARGLIST. */
8695b7b453SJohn Marino size_t arglen; /* Length of ARG. */
8795b7b453SJohn Marino ptrdiff_t matchind = -1; /* Index of first nonexact match. */
8895b7b453SJohn Marino bool ambiguous = false; /* If true, multiple nonexact match(es). */
8995b7b453SJohn Marino
9095b7b453SJohn Marino arglen = strlen (arg);
9195b7b453SJohn Marino
9295b7b453SJohn Marino /* Test all elements for either exact match or abbreviated matches. */
9395b7b453SJohn Marino for (i = 0; arglist[i]; i++)
9495b7b453SJohn Marino {
9595b7b453SJohn Marino if (!strncmp (arglist[i], arg, arglen))
9695b7b453SJohn Marino {
9795b7b453SJohn Marino if (strlen (arglist[i]) == arglen)
9895b7b453SJohn Marino /* Exact match found. */
9995b7b453SJohn Marino return i;
10095b7b453SJohn Marino else if (matchind == -1)
10195b7b453SJohn Marino /* First nonexact match found. */
10295b7b453SJohn Marino matchind = i;
10395b7b453SJohn Marino else
10495b7b453SJohn Marino {
10595b7b453SJohn Marino /* Second nonexact match found. */
10695b7b453SJohn Marino if (vallist == NULL
107*09d4459fSDaniel Fojt || memcmp ((char const *) vallist + valsize * matchind,
108*09d4459fSDaniel Fojt (char const *) vallist + valsize * i, valsize))
10995b7b453SJohn Marino {
11095b7b453SJohn Marino /* There is a real ambiguity, or we could not
11195b7b453SJohn Marino disambiguate. */
11295b7b453SJohn Marino ambiguous = true;
11395b7b453SJohn Marino }
11495b7b453SJohn Marino }
11595b7b453SJohn Marino }
11695b7b453SJohn Marino }
11795b7b453SJohn Marino if (ambiguous)
11895b7b453SJohn Marino return -2;
11995b7b453SJohn Marino else
12095b7b453SJohn Marino return matchind;
12195b7b453SJohn Marino }
12295b7b453SJohn Marino
12395b7b453SJohn Marino /* Error reporting for argmatch.
12495b7b453SJohn Marino CONTEXT is a description of the type of entity that was being matched.
12595b7b453SJohn Marino VALUE is the invalid value that was given.
12695b7b453SJohn Marino PROBLEM is the return value from argmatch. */
12795b7b453SJohn Marino
12895b7b453SJohn Marino void
argmatch_invalid(const char * context,const char * value,ptrdiff_t problem)12995b7b453SJohn Marino argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
13095b7b453SJohn Marino {
13195b7b453SJohn Marino char const *format = (problem == -1
13295b7b453SJohn Marino ? _("invalid argument %s for %s")
13395b7b453SJohn Marino : _("ambiguous argument %s for %s"));
13495b7b453SJohn Marino
13595b7b453SJohn Marino error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
13695b7b453SJohn Marino quote_n (1, context));
13795b7b453SJohn Marino }
13895b7b453SJohn Marino
13995b7b453SJohn Marino /* List the valid arguments for argmatch.
14095b7b453SJohn Marino ARGLIST is the same as in argmatch.
14195b7b453SJohn Marino VALLIST is a pointer to an array of values.
14295b7b453SJohn Marino VALSIZE is the size of the elements of VALLIST */
14395b7b453SJohn Marino void
argmatch_valid(const char * const * arglist,const void * vallist,size_t valsize)14495b7b453SJohn Marino argmatch_valid (const char *const *arglist,
145*09d4459fSDaniel Fojt const void *vallist, size_t valsize)
14695b7b453SJohn Marino {
14795b7b453SJohn Marino size_t i;
14895b7b453SJohn Marino const char *last_val = NULL;
14995b7b453SJohn Marino
15095b7b453SJohn Marino /* We try to put synonyms on the same line. The assumption is that
15195b7b453SJohn Marino synonyms follow each other */
152cf28ed85SJohn Marino fputs (_("Valid arguments are:"), stderr);
15395b7b453SJohn Marino for (i = 0; arglist[i]; i++)
15495b7b453SJohn Marino if ((i == 0)
155*09d4459fSDaniel Fojt || memcmp (last_val, (char const *) vallist + valsize * i, valsize))
15695b7b453SJohn Marino {
157cf28ed85SJohn Marino fprintf (stderr, "\n - %s", quote (arglist[i]));
158*09d4459fSDaniel Fojt last_val = (char const *) vallist + valsize * i;
15995b7b453SJohn Marino }
16095b7b453SJohn Marino else
16195b7b453SJohn Marino {
162cf28ed85SJohn Marino fprintf (stderr, ", %s", quote (arglist[i]));
16395b7b453SJohn Marino }
16495b7b453SJohn Marino putc ('\n', stderr);
16595b7b453SJohn Marino }
16695b7b453SJohn Marino
16795b7b453SJohn Marino /* Never failing versions of the previous functions.
16895b7b453SJohn Marino
16995b7b453SJohn Marino CONTEXT is the context for which argmatch is called (e.g.,
17095b7b453SJohn Marino "--version-control", or "$VERSION_CONTROL" etc.). Upon failure,
17195b7b453SJohn Marino calls the (supposed never to return) function EXIT_FN. */
17295b7b453SJohn Marino
17395b7b453SJohn Marino ptrdiff_t
__xargmatch_internal(const char * context,const char * arg,const char * const * arglist,const void * vallist,size_t valsize,argmatch_exit_fn exit_fn)17495b7b453SJohn Marino __xargmatch_internal (const char *context,
17595b7b453SJohn Marino const char *arg, const char *const *arglist,
176*09d4459fSDaniel Fojt const void *vallist, size_t valsize,
17795b7b453SJohn Marino argmatch_exit_fn exit_fn)
17895b7b453SJohn Marino {
17995b7b453SJohn Marino ptrdiff_t res = argmatch (arg, arglist, vallist, valsize);
18095b7b453SJohn Marino if (res >= 0)
18195b7b453SJohn Marino /* Success. */
18295b7b453SJohn Marino return res;
18395b7b453SJohn Marino
18495b7b453SJohn Marino /* We failed. Explain why. */
18595b7b453SJohn Marino argmatch_invalid (context, arg, res);
18695b7b453SJohn Marino argmatch_valid (arglist, vallist, valsize);
18795b7b453SJohn Marino (*exit_fn) ();
18895b7b453SJohn Marino
18995b7b453SJohn Marino return -1; /* To please the compilers. */
19095b7b453SJohn Marino }
19195b7b453SJohn Marino
19295b7b453SJohn Marino /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
19395b7b453SJohn Marino return the first corresponding argument in ARGLIST */
19495b7b453SJohn Marino const char *
argmatch_to_argument(const void * value,const char * const * arglist,const void * vallist,size_t valsize)195*09d4459fSDaniel Fojt argmatch_to_argument (const void *value,
19695b7b453SJohn Marino const char *const *arglist,
197*09d4459fSDaniel Fojt const void *vallist, size_t valsize)
19895b7b453SJohn Marino {
19995b7b453SJohn Marino size_t i;
20095b7b453SJohn Marino
20195b7b453SJohn Marino for (i = 0; arglist[i]; i++)
202*09d4459fSDaniel Fojt if (!memcmp (value, (char const *) vallist + valsize * i, valsize))
20395b7b453SJohn Marino return arglist[i];
20495b7b453SJohn Marino return NULL;
20595b7b453SJohn Marino }
20695b7b453SJohn Marino
20795b7b453SJohn Marino #ifdef TEST
20895b7b453SJohn Marino /*
20995b7b453SJohn Marino * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
21095b7b453SJohn Marino */
21195b7b453SJohn Marino
21295b7b453SJohn Marino /* When to make backup files. */
21395b7b453SJohn Marino enum backup_type
21495b7b453SJohn Marino {
21595b7b453SJohn Marino /* Never make backups. */
21695b7b453SJohn Marino no_backups,
21795b7b453SJohn Marino
21895b7b453SJohn Marino /* Make simple backups of every file. */
21995b7b453SJohn Marino simple_backups,
22095b7b453SJohn Marino
22195b7b453SJohn Marino /* Make numbered backups of files that already have numbered backups,
22295b7b453SJohn Marino and simple backups of the others. */
22395b7b453SJohn Marino numbered_existing_backups,
22495b7b453SJohn Marino
22595b7b453SJohn Marino /* Make numbered backups of every file. */
22695b7b453SJohn Marino numbered_backups
22795b7b453SJohn Marino };
22895b7b453SJohn Marino
22995b7b453SJohn Marino /* Two tables describing arguments (keys) and their corresponding
23095b7b453SJohn Marino values */
23195b7b453SJohn Marino static const char *const backup_args[] =
23295b7b453SJohn Marino {
23395b7b453SJohn Marino "no", "none", "off",
23495b7b453SJohn Marino "simple", "never",
23595b7b453SJohn Marino "existing", "nil",
23695b7b453SJohn Marino "numbered", "t",
23795b7b453SJohn Marino 0
23895b7b453SJohn Marino };
23995b7b453SJohn Marino
24095b7b453SJohn Marino static const enum backup_type backup_vals[] =
24195b7b453SJohn Marino {
24295b7b453SJohn Marino no_backups, no_backups, no_backups,
24395b7b453SJohn Marino simple_backups, simple_backups,
24495b7b453SJohn Marino numbered_existing_backups, numbered_existing_backups,
24595b7b453SJohn Marino numbered_backups, numbered_backups
24695b7b453SJohn Marino };
24795b7b453SJohn Marino
24895b7b453SJohn Marino int
main(int argc,const char * const * argv)24995b7b453SJohn Marino main (int argc, const char *const *argv)
25095b7b453SJohn Marino {
25195b7b453SJohn Marino const char *cp;
25295b7b453SJohn Marino enum backup_type backup_type = no_backups;
25395b7b453SJohn Marino
25495b7b453SJohn Marino if (argc > 2)
25595b7b453SJohn Marino {
256*09d4459fSDaniel Fojt fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", getprogname ());
25795b7b453SJohn Marino exit (1);
25895b7b453SJohn Marino }
25995b7b453SJohn Marino
26095b7b453SJohn Marino if ((cp = getenv ("VERSION_CONTROL")))
26195b7b453SJohn Marino backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
26295b7b453SJohn Marino backup_args, backup_vals);
26395b7b453SJohn Marino
26495b7b453SJohn Marino if (argc == 2)
265*09d4459fSDaniel Fojt backup_type = XARGMATCH (getprogname (), argv[1],
26695b7b453SJohn Marino backup_args, backup_vals);
26795b7b453SJohn Marino
268cf28ed85SJohn Marino printf ("The version control is '%s'\n",
26995b7b453SJohn Marino ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
27095b7b453SJohn Marino
27195b7b453SJohn Marino return 0;
27295b7b453SJohn Marino }
27395b7b453SJohn Marino #endif
274