From 6ef3d783333346333f35bb181ba90f5bc46c0b29 Mon Sep 17 00:00:00 2001 From: =?utf8?q?P=C3=A1draig=20Brady?= Date: Sun, 30 Jan 2022 16:50:27 +0000 Subject: [PATCH] argmatch: add variants that only match full argument * lib/argmatch.h (argmatch_exact, [X]ARGMATCH_EXACT): New interfaces that don't allow abbreviations. * lib/argmatch.c (argmatch_exact): Likewise. (__xargmatch_internal): Add a bool parameter to disable abbreviations. * tests/test-argmatch.c: Add tests. --- ChangeLog | 9 +++++++++ lib/argmatch.c | 26 ++++++++++++++++++++++++-- lib/argmatch.h | 23 +++++++++++++++++++---- tests/test-argmatch.c | 25 +++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index fedc7dc777..a202950bd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2022-01-30 Pádraig Brady + + argmatch: add variants that only match full argument + * lib/argmatch.h (argmatch_exact, [X]ARGMATCH_EXACT): New interfaces + that don't allow abbreviations. + * lib/argmatch.c (argmatch_exact): Likewise. + (__xargmatch_internal): Add a bool parameter to disable abbreviations. + * tests/test-argmatch.c: Add tests. + 2022-01-30 Bruno Haible tests: Fix interpretation of setupterm's return code. diff --git a/lib/argmatch.c b/lib/argmatch.c index 9e3232f947..2a28900a4e 100644 --- a/lib/argmatch.c +++ b/lib/argmatch.c @@ -120,6 +120,21 @@ 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)) + 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. @@ -174,9 +189,16 @@ 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) + argmatch_exit_fn exit_fn, + bool allow_abbreviation) { - ptrdiff_t res = argmatch (arg, arglist, vallist, valsize); + ptrdiff_t res; + + if (allow_abbreviation) + res = argmatch (arg, arglist, vallist, valsize); + else + res = argmatch_exact (arg, arglist); + if (res >= 0) /* Success. */ return res; diff --git a/lib/argmatch.h b/lib/argmatch.h index a2364b5a2e..52f2bb727b 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)'. */ @@ -83,13 +89,14 @@ void argmatch_valid (char const *const *arglist, -/* Same as argmatch, but upon failure, report an explanation of the - failure, and exit using the function EXIT_FN. */ +/* Like argmatch/argmatch_exact, but upon failure, report an explanation + of the failure, and exit using the function EXIT_FN. */ ptrdiff_t __xargmatch_internal (char const *context, char const *arg, char const *const *arglist, void const *vallist, size_t valsize, - argmatch_exit_fn exit_fn); + argmatch_exit_fn exit_fn, + bool allow_abbreviation); /* Programmer friendly interface to __xargmatch_internal. */ @@ -97,7 +104,15 @@ ptrdiff_t __xargmatch_internal (char const *context, ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \ (void const *) (Vallist), \ sizeof *(Vallist), \ - argmatch_die)]) + argmatch_die, \ + true)]) + +# define XARGMATCH_EXACT(Context, Arg, Arglist, Vallist) \ + ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \ + (void const *) (Vallist), \ + sizeof *(Vallist), \ + argmatch_die, \ + false)]) /* Convert a value into a corresponding argument. */ diff --git a/tests/test-argmatch.c b/tests/test-argmatch.c index 8345150002..b05e431266 100644 --- a/tests/test-argmatch.c +++ b/tests/test-argmatch.c @@ -125,38 +125,63 @@ 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 +#undef CHECK_EXACT argmatch_backup_usage (stdout); -- 2.39.5