* lib/argmatch.h (argmatch_exact, [X]ARGMATCH_EXACT): New interfaces that don't support abbreviations. * lib/argmatch.c (argmatch_exact, __xargmatch_exact_internal): Likewise. * tests/test-argmatch.c: Add tests. --- ChangeLog | 8 ++++++++ lib/argmatch.c | 37 +++++++++++++++++++++++++++++++++++++ lib/argmatch.h | 17 +++++++++++++++++ tests/test-argmatch.c | 23 +++++++++++++++++++++++ 4 files changed, 85 insertions(+)
diff --git a/ChangeLog b/ChangeLog index 4c9a1e7989..e51a00190a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2022-01-30 Pádraig Brady <p...@draigbrady.com> + + argmatch: add variants that only match full argument + * lib/argmatch.h (argmatch_exact, [X]ARGMATCH_EXACT): New interfaces + that don't support abbreviations. + * lib/argmatch.c (argmatch_exact, __xargmatch_exact_internal): Likewise. + * tests/test-argmatch.c: Add tests. + 2022-01-30 Bruno Haible <br...@clisp.org> terminfo: Add tests. diff --git a/lib/argmatch.c b/lib/argmatch.c index 9e3232f947..71059e8f7e 100644 --- a/lib/argmatch.c +++ b/lib/argmatch.c @@ -120,6 +120,24 @@ argmatch (const char *arg, const char *const *arglist, return matchind; } +ptrdiff_t +argmatch_exact (const char *arg, const char *const *arglist) +{ + size_t i; + + /* Test elements for exact match. */ + for (i = 0; arglist[i]; i++) + { + if (!strcmp (arglist[i], arg)) + { + /* Exact match found. */ + return i; + } + } + + return -1; +} + /* Error reporting for argmatch. CONTEXT is a description of the type of entity that was being matched. VALUE is the invalid value that was given. @@ -189,6 +207,25 @@ __xargmatch_internal (const char *context, return -1; /* To please the compilers. */ } +ptrdiff_t +__xargmatch_exact_internal (const char *context, + const char *arg, const char *const *arglist, + const void *vallist, size_t valsize, + argmatch_exit_fn exit_fn) +{ + ptrdiff_t res = argmatch_exact (arg, arglist); + if (res >= 0) + /* Success. */ + return res; + + /* We failed. Explain why. */ + argmatch_invalid (context, arg, res); + argmatch_valid (arglist, vallist, valsize); + (*exit_fn) (); + + return -1; /* To please the compilers. */ +} + /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and return the first corresponding argument in ARGLIST */ const char * diff --git a/lib/argmatch.h b/lib/argmatch.h index a2364b5a2e..c6d24d981c 100644 --- a/lib/argmatch.h +++ b/lib/argmatch.h @@ -52,9 +52,15 @@ extern "C" { ptrdiff_t argmatch (char const *arg, char const *const *arglist, void const *vallist, size_t valsize) _GL_ATTRIBUTE_PURE; +ptrdiff_t argmatch_exact (char const *arg, char const *const *arglist) + _GL_ATTRIBUTE_PURE; + # define ARGMATCH(Arg, Arglist, Vallist) \ argmatch (Arg, Arglist, (void const *) (Vallist), sizeof *(Vallist)) +# define ARGMATCH_EXACT(Arg, Arglist) \ + argmatch_exact (Arg, Arglist) + /* xargmatch calls this function when it fails. This function should not return. By default, this is a function that calls ARGMATCH_DIE which in turn defaults to 'exit (exit_failure)'. */ @@ -91,6 +97,11 @@ ptrdiff_t __xargmatch_internal (char const *context, void const *vallist, size_t valsize, argmatch_exit_fn exit_fn); +ptrdiff_t __xargmatch_exact_internal (char const *context, + char const *arg, char const *const *arglist, + void const *vallist, size_t valsize, + argmatch_exit_fn exit_fn); + /* Programmer friendly interface to __xargmatch_internal. */ # define XARGMATCH(Context, Arg, Arglist, Vallist) \ @@ -99,6 +110,12 @@ ptrdiff_t __xargmatch_internal (char const *context, sizeof *(Vallist), \ argmatch_die)]) +# define XARGMATCH_EXACT(Context, Arg, Arglist, Vallist) \ + ((Vallist) [__xargmatch_exact_internal (Context, Arg, Arglist,\ + (void const *) (Vallist), \ + sizeof *(Vallist), \ + argmatch_die)]) + /* Convert a value into a corresponding argument. */ char const *argmatch_to_argument (void const *value, diff --git a/tests/test-argmatch.c b/tests/test-argmatch.c index 8345150002..46a7f07153 100644 --- a/tests/test-argmatch.c +++ b/tests/test-argmatch.c @@ -125,37 +125,60 @@ main (int argc, char *argv[]) } \ } while (0) +#define CHECK_EXACT(Input, Output) \ + do { \ + ASSERT (ARGMATCH_EXACT (Input, backup_args) == Output); \ + } while (0) + /* Not found. */ CHECK ("klingon", -1); + CHECK_EXACT ("klingon", -1); /* Exact match. */ CHECK ("none", 1); + CHECK_EXACT ("none", 1); CHECK ("nil", 7); + CHECK_EXACT ("nil", 7); /* Too long. */ CHECK ("nilpotent", -1); + CHECK_EXACT ("nilpotent", -1); /* Abbreviated. */ CHECK ("simpl", 3); + CHECK_EXACT ("simpl", -1); CHECK ("simp", 3); + CHECK_EXACT ("simp", -1); CHECK ("sim", 3); + CHECK_EXACT ("sim", -1); /* Exact match and abbreviated. */ CHECK ("numbered", 9); + CHECK_EXACT ("numbered", 9); CHECK ("numbere", -2); + CHECK_EXACT ("numbere", -1); CHECK ("number", -2); + CHECK_EXACT ("number", -1); CHECK ("numbe", -2); + CHECK_EXACT ("numbe", -1); CHECK ("numb", -2); + CHECK_EXACT ("numb", -1); CHECK ("num", -2); + CHECK_EXACT ("num", -1); CHECK ("nu", -2); + CHECK_EXACT ("nu", -1); CHECK ("n", -2); + CHECK_EXACT ("n", -1); /* Ambiguous abbreviated. */ CHECK ("ne", -2); + CHECK_EXACT ("ne", -1); /* Ambiguous abbreviated, but same value ("single" and "simple"). */ CHECK ("si", 3); + CHECK_EXACT ("si", -1); CHECK ("s", 3); + CHECK_EXACT ("s", -1); #undef CHECK argmatch_backup_usage (stdout); -- 2.26.2