From 449fed1c13525c8420b4366014ba34616cb20239 Mon Sep 17 00:00:00 2001 From: Kerin Millar Date: Tue, 11 Jun 2024 06:26:41 +0100 Subject: Add the is_anyof() and is_subset() functions Examples follow. is_anyof y x y z # returns 0 is_anoyf y x w z # returns 1 is_subset x y -- x y z # returns 0 is_subset x y z -- z y x # returns 0 is_subset x y -- x w z # returns 1 Signed-off-by: Kerin Millar --- functions.sh | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test-functions | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/functions.sh b/functions.sh index 7d146c8..166f184 100644 --- a/functions.sh +++ b/functions.sh @@ -446,6 +446,28 @@ is_int() esac } +# +# Determines whether the first parameter matches any of the parameters that +# follow it. +# +is_anyof() +{ + local arg needle + + if [ "$#" -eq 0 ]; then + warn "is_anyof: too few arguments (got $#, expected at least 1)" + else + needle=$1 + shift + for arg; do + if [ "${arg}" = "${needle}" ]; then + return + fi + done + fi + false +} + # # Takes the first parameter as a reference file/directory then determines # whether any of the following parameters refer to newer files/directories. @@ -468,6 +490,46 @@ is_older_than() | read -r _ } +# +# Collects the intersection of the parameters up to - but not including - a +# sentinel value then determines whether the resulting set is a subset of the +# interection of the remaining parameters. If the SENTINEL variable is set and +# non-empty, it shall be taken as the value of the sentinel. Otherwise, the +# value of the sentinel shall be defined as . If the +# sentinel value is not encountered or if either set is empty then the returm +# value shall be greater than 1. +# +is_subset() +{ + SENTINEL=${SENTINEL:-'--'} awk -f - -- "$@" <<-'EOF' + BEGIN { + argc = ARGC + ARGC = 1 + for (i = 1; i < argc; i++) { + word = ARGV[i] + if (word == ENVIRON["SENTINEL"]) { + break + } else { + set1[word] = "" + } + } + if (i == 1 || argc - i < 2) { + exit 1 + } + for (i++; i < argc; i++) { + word = ARGV[i] + set2[word] = "" + } + for (word in set2) { + delete set1[word] + } + for (word in set1) { + exit 1 + } + } + EOF +} + # # Considers one or more pathnames and prints the one having the newest # modification time. If at least one parameter is provided, all parameters shall diff --git a/test-functions b/test-functions index 1a2eb81..0b987ab 100755 --- a/test-functions +++ b/test-functions @@ -575,6 +575,53 @@ test_parallel_run() { iterate_tests 4 "$@" } +test_is_anyof() { + set -- \ + ge 1 N/A N/A N/A \ + ge 1 x N/A N/A \ + ge 1 x y N/A \ + ge 1 x y z \ + eq 0 x x N/A \ + eq 0 x x y \ + eq 0 x y x + + callback() { + shift + test_description="is_anyof $(quote_args "$@")" + is_anyof "$@" + } + + iterate_tests 5 "$@" +} + +test_is_subset() { + set -- \ + ge 1 N/A N/A N/A N/A N/A \ + ge 1 -- N/A N/A N/A N/A \ + ge 1 -- -- N/A N/A N/A \ + ge 1 -- x N/A N/A N/A \ + ge 1 x -- N/A N/A N/A \ + ge 1 x y N/A N/A N/A \ + ge 1 x y x N/A N/A \ + eq 0 x -- x N/A N/A \ + eq 0 x -- x y N/A \ + eq 0 x -- y x N/A \ + eq 0 x y -- x y \ + eq 0 x y -- y x \ + ge 1 x y -- x z \ + ge 1 y x -- z x \ + ge 1 x z -- x y \ + ge 1 z x -- y x + + callback() { + shift + test_description="is_subset $(quote_args "$@")" + is_subset "$@" + } + + iterate_tests 7 "$@" +} + iterate_tests() { slice_width=$1 shift @@ -641,6 +688,8 @@ test_trim || rc=1 test_hr || rc=1 test_whenceforth || rc=1 test_parallel_run || rc=1 +test_is_anyof || rc=1 +test_is_subset || rc=1 cleanup_tmpdir -- cgit v1.2.3-65-gdbad