From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 609C31581C1 for ; Sun, 7 Jul 2024 05:55:44 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id D725AE2A6C; Sun, 7 Jul 2024 05:55:42 +0000 (UTC) Received: from smtp.gentoo.org (woodpecker.gentoo.org [140.211.166.183]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id B71E5E2A6C for ; Sun, 7 Jul 2024 05:55:42 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 87033340CC9 for ; Sun, 7 Jul 2024 05:55:41 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id EEE3E1DDF for ; Sun, 7 Jul 2024 05:55:38 +0000 (UTC) From: "Sam James" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Sam James" Message-ID: <1719801147.2a0c3ce54dda9e72310745d04960bcea7071fc4e.sam@gentoo> Subject: [gentoo-commits] proj/gentoo-functions:master commit in: / X-VCS-Repository: proj/gentoo-functions X-VCS-Files: functions.sh test-functions X-VCS-Directories: / X-VCS-Committer: sam X-VCS-Committer-Name: Sam James X-VCS-Revision: 2a0c3ce54dda9e72310745d04960bcea7071fc4e X-VCS-Branch: master Date: Sun, 7 Jul 2024 05:55:38 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 011b5fff-4602-4d33-80a9-f924a26d13dc X-Archives-Hash: 3a3b5bda37ccb6dcc395d799f09faf71 commit: 2a0c3ce54dda9e72310745d04960bcea7071fc4e Author: Kerin Millar plushkava net> AuthorDate: Mon Jul 1 01:34:25 2024 +0000 Commit: Sam James gentoo org> CommitDate: Mon Jul 1 02:32:27 2024 +0000 URL: https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=2a0c3ce5 Add the contains_all() and contains_any() functions Here are some examples which presume the default value of IFS. contains_all " cat mat " cat dog # returns 1 contains_all " cat mat " mat cat # returns 0 contains_any " cat mat " cat dog # returns 0 contains_any " cat mat " dog # returns 1 Here are some examples showing that IFS is taken into account. IFS=, contains_all "cat,mat" cat dog # returns 1 IFS=, contains_all "cat,mat" mat cat # returns 0 IFS=, contains_any "cat,mat" cat dog # returns 0 IFS=, contains_any "cat,mat" dog # returns 1 Signed-off-by: Kerin Millar plushkava.net> functions.sh | 117 +++++++++++++++++++++++++++++++++++++++++++++------------ test-functions | 80 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 25 deletions(-) diff --git a/functions.sh b/functions.sh index 1926c40..a4fa946 100644 --- a/functions.sh +++ b/functions.sh @@ -17,7 +17,7 @@ # COLUMNS : may be used by _update_columns() to get the column count # EPOCHREALTIME : potentially used by _update_time() to get the time # GENFUN_MODULES : which of the optional function collections must be sourced -# IFS : multiple warn() operands are joined by its first character +# IFS : affects contains_all(), contains_any() and warn() # INVOCATION_ID : used by from_unit() # PORTAGE_BIN_PATH : used by from_portage() # RC_OPENRC_PID : used by from_runscript() @@ -48,6 +48,96 @@ chdir() CDPATH= cd -- "$@" } +# +# Takes the first parameter as a string comprising zero or more words, composes +# a set consisting of the intersection of those words, then determines whether +# the intersection of the remaining parameters forms a subset thereof. The +# words shall be collected by splitting the string into individual fields, in +# accordance with section 2.6.5 of the Shell Command Language specification. +# Therefore, the value of IFS shall be taken into account. If fewer than two +# parameters are provided, or if the first parameter yields no fields, or if the +# second set is disjoint from - or a superset of - the first, the return value +# shall be greater than 0. +# +contains_all() +{ + [ "$#" -ge 2 ] && IFS=${IFS} awk -f - -- "$@" <<-'EOF' + BEGIN { + ifs = ENVIRON["IFS"] + haystack = ARGV[1] + argc = ARGC + ARGC = 1 + if (length(ifs) == 0) { + FS = "^" + } else if (length(ifs) != 3 || ifs ~ /[^ \t\n]/) { + # Split by the first character of IFS. + FS = "[" substr(ifs, 1, 1) "]" + } else { + # Mimic default field splitting behaviour, per section 2.6.5. + FS = "[ \t\n]+" + sub("^" FS, "", haystack) + } + # In sh, fields are terminated, not separated. + sub(FS "$", "", haystack) + len = split(haystack, words) + for (i = 1; i <= len; i++) { + set2[words[i]] + } + for (i = 2; i < argc; i++) { + set1[ARGV[i]] + } + for (word in set2) { + delete set1[word] + } + for (word in set1) { + exit 1 + } + } + EOF +} + +# +# Takes the first parameter as a string comprising zero or more words then +# determines whether at least one of the remaining parameters can be matched +# against any of those words. The words shall be collected by splitting the +# string into individual fields, in accordance with section 2.6.5 of the Shell +# Command Language specification. Therefore, the value of IFS shall be taken +# into account. If fewer than two parameters are provided, or if the first +# parameter yields no fields, or if none of the following parameters can be +# matched, the return value shall be greater than 0. +# +contains_any() +{ + local had_noglob haystack i item needle retval + + [ "$#" -ge 2 ] || return + haystack=$1 + shift + i=0 + case $- in + *f*) + had_noglob=1 + ;; + *) + had_noglob=0 + esac + set -f + for needle; do + if [ "$(( i += 1 ))" -eq 1 ]; then + # shellcheck disable=2086 + set -- ${haystack} + fi + for item; do + [ "${item}" = "${needle}" ] && break 2 + done + done + retval=$? + if [ "${had_noglob}" -eq 0 ]; then + set +f + fi + return "${retval}" +} + # # Considers the first parameter as an URL then attempts to fetch it with either # curl(1) or wget(1). If the URL does not contain a scheme then the https:// @@ -585,29 +675,6 @@ whenceforth() #------------------------------------------------------------------------------# -# -# Considers the first parameter as containing zero or more blank-separated words -# then determines whether any of the remaining parameters can be matched in -# their capacity as discrete words. -# -_contains_word() -{ - local word wordlist - - wordlist=$1 word=$2 - case ${word} in - ''|*[[:blank:]]*) - ;; - *) - case " ${wordlist} " in - *[[:blank:]]"${word}"[[:blank:]]*) - return - ;; - esac - esac - false -} - # # Determines whether the terminal is a dumb one. # @@ -761,7 +828,7 @@ _want_module() local basename basename=${1##*/} - _contains_word "${GENFUN_MODULES}" "${basename%.sh}" + contains_any "${GENFUN_MODULES}" "${basename%.sh}" } # diff --git a/test-functions b/test-functions index 34ff54a..59c0b29 100755 --- a/test-functions +++ b/test-functions @@ -719,6 +719,84 @@ test_substr() { iterate_tests 6 "$@" } +test_contains_all() { + set -- \ + ge 1 N/A N/A N/A N/A \ + ge 1 'foo bar' '' N/A N/A \ + ge 1 'foo bar' '' ' ' N/A \ + ge 1 'foo bar' '' ' bar' N/A \ + ge 1 'foo bar' '' ' bar' N/A \ + ge 1 'foo bar' '' 'foo ' N/A \ + ge 1 'foo bar' '' 'foo bar' N/A \ + ge 1 'foo bar' ' ' '' N/A \ + ge 1 'foo bar' ' ' ' ' N/A \ + ge 1 'foo bar' ' ' N/A N/A \ + ge 1 'foo bar' ' bar' '' N/A \ + ge 1 'foo bar' ' bar' N/A N/A \ + ge 1 'foo bar' 'foo ' '' N/A \ + ge 1 'foo bar' 'foo ' ' bar' N/A \ + ge 1 'foo bar' 'foo ' N/A N/A \ + ge 1 'foo bar' 'foo bar' '' N/A \ + ge 1 'foo bar' 'foo bar' N/A N/A \ + ge 1 'foo bar' N/A N/A N/A \ + ge 1 'foo bar' bar foo '' \ + ge 1 'foo bar' bar foo ' ' \ + ge 1 'foo bar' baz bar foo \ + ge 1 'foo bar' fo ba N/A \ + ge 1 'foo bar' foo bar '' \ + ge 1 'foo bar' foo bar ' ' \ + ge 1 'foo bar' foo bar baz \ + ge 1 'foo bar' o a N/A \ + ge 1 'foo bar' oo ar N/A \ + eq 0 'foo bar' foo bar N/A \ + eq 0 'foo bar' bar foo N/A + + callback() { + shift + test_description="contains_all $(quote_args "$@")" + contains_all "$@" + } + + iterate_tests 6 "$@" +} + +test_contains_any() { + set -- \ + ge 1 N/A N/A N/A \ + ge 1 'foo bar' N/A N/A \ + ge 1 'foo bar' fo ba \ + ge 1 'foo bar' oo ar \ + ge 1 'foo bar' o a \ + ge 1 'foo bar' 'foo bar' 'foo bar' \ + ge 1 'foo bar' 'foo bar' _ \ + ge 1 'foo bar' _ 'foo bar' \ + ge 1 'foo bar' 'foo ' ' bar' \ + ge 1 'foo bar' 'foo ' _ \ + ge 1 'foo bar' _ ' bar' \ + ge 1 'foo bar' ' bar' _ \ + ge 1 'foo bar' _ 'foo ' \ + ge 1 'foo bar' '' '' \ + ge 1 'foo bar' '' _ \ + ge 1 'foo bar' _ '' \ + ge 1 'foo bar' ' ' ' ' \ + ge 1 'foo bar' ' ' _ \ + ge 1 'foo bar' _ ' ' \ + eq 0 'foo bar' foo bar \ + eq 0 'foo bar' bar foo \ + eq 0 'foo bar' foo _ \ + eq 0 'foo bar' _ bar \ + eq 0 'foo bar' bar _ \ + eq 0 'foo bar' _ foo + + callback() { + shift + test_description="contains_any $(quote_args "$@")" + contains_any "$@" + } + + iterate_tests 5 "$@" +} + iterate_tests() { slice_width=$1 shift @@ -794,6 +872,8 @@ test_is_subset || rc=1 test_trueof_all || rc=1 test_trueof_any || rc=1 #test_substr || rc=1 +test_contains_all || rc=1 +test_contains_any || rc=1 cleanup_tmpdir