summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKerin Millar <kfm@plushkava.net>2024-08-10 06:12:15 +0100
committerSam James <sam@gentoo.org>2024-08-11 11:11:03 +0100
commit6cf0940b8d336eb35a970af2ffc819f55e3ab429 (patch)
tree6b034ac5dd1dc1d9e3eca852c31645fd52cba394
parenttest-functions: comment as to the implications of test_local() failing (diff)
downloadgentoo-functions-6cf0940b8d336eb35a970af2ffc819f55e3ab429.tar.gz
gentoo-functions-6cf0940b8d336eb35a970af2ffc819f55e3ab429.tar.bz2
gentoo-functions-6cf0940b8d336eb35a970af2ffc819f55e3ab429.zip
Use the -nt and -ot test primaries again rather than depend on GNU find
As regards the test(1) utility, the POSIX.1-2024 specification defines the -nt and -ot primaries as standard features. Given that the specification in question was only recently published, this would not normally be an adequate reason for using them in gentoo-functions, in and as of itself. However, I was already aware that the these primaries are commonly implemented and have been so for years. So, I decided to evaluate a number of shells and see how things stand now. Here is a list of the ones that I tested: - ash (busybox 1.36.1) - dash 0.5.12 - bash 5.2.26 - ksh 93u+ - loksh 7.5 - mksh 59c - oksh 7.5 - sh (FreeBSD 14.1) - sh (NetBSD 10.0) - sh (OpenBSD 7.5) - yash 2.56.1 Of these, bash, ksh93, loksh, mksh, oksh, OpenBSD sh and yash appear to conform with the POSIX-1.2024 specification. The remaining four fail to conform in one particular respect, which is as follows. $ touch existent $ set -- existent nonexistent $ [ "$1" -nt "$2" ]; echo "$?" # should be 0 1 $ [ "$2" -ot "$1" ]; echo "$?" # should be 0 1 To address this, I discerned a reasonably straightforward workaround that involves testing both whether the file under consideration exists and whether the variable keeping track of the newest/oldest file has yet been assigned to. As far as I am concerned, the coverage is more than adequate for both primaries to be used by gentoo-functions. As such, this commit adjusts the following three functions so as to do exactly that. - is_older_than() - newest() - oldest() It also removes the following functions, since they are no longer used. - _find0() - _select_by_mtime() With this, GNU findutils is no longer a required runtime dependency. Of course, should a newly introduced feature of gentoo-functions benefit from the presence of findutils in the future, there is no reason that it cannot be brought back in that capacity. Signed-off-by: Kerin Millar <kfm@plushkava.net> Signed-off-by: Sam James <sam@gentoo.org>
-rw-r--r--functions.sh157
-rw-r--r--functions/rc.sh25
2 files changed, 113 insertions, 69 deletions
diff --git a/functions.sh b/functions.sh
index 641deb6..43ea385 100644
--- a/functions.sh
+++ b/functions.sh
@@ -1,6 +1,6 @@
# Copyright 1999-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
-# shellcheck shell=sh disable=2209,3043
+# shellcheck shell=sh disable=2209,3013,3043
# This file contains a series of function declarations followed by some
# initialisation code. Functions intended for internal use shall be prefixed
@@ -294,17 +294,55 @@ is_anyof()
#
# Considers one or more pathnames and prints the one having the newest
-# modification time. If at least one parameter is provided, all parameters shall
-# be considered as pathnames to be compared to one another. Otherwise, the
-# pathnames to be compared shall be read from the standard input as
-# NUL-delimited records. If no pathnames are given, or those specified do not
-# exist, the return value shall be greater than 0. In the case that two or more
-# pathnames are candidates, the one having the lexicographically greatest value
-# shall be selected. Pathnames containing newline characters shall be ignored.
+# modification time. If at least one parameter is provided, all parameters
+# shall be considered as pathnames to be compared to one another. Otherwise,
+# the pathnames to be compared shall be read from the standard input as
+# null-terminated records. In the case that two or more pathnames are
+# candidates, whichever was first specified shall take precedence over the
+# other. If no pathnames are given, or those specified do not exist, the return
+# value shall be greater than 0.
+#
+# Pathnames containing <newline> characters shall be handled correctly if
+# conveyed as positional parameters. Otherwise, the behaviour for such
+# pathnames is unspecified. Users of the function are duly expected to refrain
+# from conveying such pathnames for consumption from the standard input; for
+# example, by specifying a predicate of ! -path $'*\n*' to the find utility.
+# This constraint is expected to be eliminated by a future amendment to the
+# function, once support for read -d becomes sufficiently widespread.
+#
+# The test utility is required to support the -nt primary, per POSIX-1.2024.
+# However, measures are in place to to achieve compatibility with shells that
+# implement the primary without yet fully adhering to the specification.
#
newest()
{
- _select_by_mtime -r "$@"
+ local path newest
+
+ newest=
+ if [ "$#" -gt 0 ]; then
+ for path; do
+ # The tests within curly braces address a conformance
+ # issue whereby [ existent -nt nonexistent ] is
+ # incorrectly false. As of August 2024, busybox ash,
+ # dash, FreeBSD sh and NetBSD sh are known to be
+ # non-conforming in this respect.
+ if { [ ! "${newest}" ] && [ -e "${path}" ]; } || [ "${path}" -nt "${newest}" ]; then
+ newest=$path
+ fi
+ done
+ test "${newest}" && printf '%s\n' "${newest}"
+ else
+ # Support for read -d '' is not yet sufficiently widespread.
+ tr '\0' '\n' |
+ {
+ while IFS= read -r path; do
+ if { [ ! "${newest}" ] && [ -e "${path}" ]; } || [ "${path}" -nt "${newest}" ]; then
+ newest=$path
+ fi
+ done
+ test "${newest}" && printf '%s\n' "${newest}"
+ }
+ fi
}
#
@@ -330,17 +368,55 @@ get_nprocs()
#
# Considers one or more pathnames and prints the one having the oldest
-# modification time. If at least one parameter is provided, all parameters shall
-# be considered as pathnames to be compared to one another. Otherwise, the
-# pathnames to be compared shall be read from the standard input as
-# NUL-delimited records. If no pathnames are given, or those specified do not
-# exist, the return value shall be greater than 0. In the case that two or more
-# pathnames are candidates, the one having the lexicographically lesser value
-# shall be selected. Pathnames containing newline characters shall be ignored.
+# modification time. If at least one parameter is provided, all parameters
+# shall be considered as pathnames to be compared to one another. Otherwise,
+# the pathnames to be compared shall be read from the standard input as
+# null-terminated records. In the case that two or more pathnames are
+# candidates, whichever was first specified shall take precedence over the
+# other. If no pathnames are given, or those specified do not exist, the return
+# value shall be greater than 0.
+#
+# Pathnames containing <newline> characters shall be handled correctly if
+# conveyed as positional parameters. Otherwise, the behaviour for such
+# pathnames is unspecified. Users of the function are duly expected to refrain
+# from conveying such pathnames for consumption from the standard input; for
+# example, by specifying a predicate of ! -path $'*\n*' to the find utility.
+# This constraint is expected to be eliminated by a future amendment to the
+# function, once support for read -d becomes sufficiently widespread.
+#
+# The test utility is required to support the -ot primary, per POSIX-1.2024.
#
oldest()
{
- _select_by_mtime -- "$@"
+ local path oldest
+
+ oldest=
+ if [ "$#" -gt 0 ]; then
+ for path; do
+ # The specification has [ nonexistent -ot existent ] as
+ # being true. Such is a nuisance in this case but the
+ # preceding tests suffice as a workaround.
+ if [ ! -e "${path}" ]; then
+ continue
+ elif [ ! "${oldest}" ] || [ "${path}" -ot "${oldest}" ]; then
+ oldest=$path
+ fi
+ done
+ test "${oldest}" && printf '%s\n' "${oldest}"
+ else
+ # Support for read -d '' is not yet sufficiently widespread.
+ tr '\0' '\n' |
+ {
+ while IFS= read -r path; do
+ if [ ! -e "${path}" ]; then
+ continue
+ elif [ ! "${oldest}" ] || [ "${path}" -ot "${oldest}" ]; then
+ oldest=$path
+ fi
+ done
+ test "${oldest}" && printf '%s\n' "${oldest}"
+ }
+ fi
}
#
@@ -676,34 +752,6 @@ whenceforth()
#------------------------------------------------------------------------------#
#
-# See the definitions of _select_by_mtime() and is_older_than(). This function
-# requires that GNU findutils >=4.9 be installed.
-#
-_find0()
-{
- # Store the name of the GNU find binary, which may be "gfind".
- hash gfind 2>/dev/null && genfun_bin_find=gfind || genfun_bin_find=find
-
- _find0()
- {
- local opt
-
- case $1 in
- -[HL])
- opt=$1
- shift
- set -- "${opt}" -files0-from - "$@"
- ;;
- *)
- set -- -files0-from - "$@"
- esac
- "${genfun_bin_find}" "$@"
- }
-
- _find0 "$@"
-}
-
-#
# Determines whether the terminal is a dumb one.
#
_has_dumb_terminal()
@@ -735,25 +783,6 @@ if [ "${BASH_VERSINFO-0}" -ge 5 ]; then
fi
#
-# See the definitions of oldest() and newest().
-#
-_select_by_mtime()
-{
- local sort_opt
-
- sort_opt=$1
- shift
- if [ "$#" -gt 0 ]; then
- printf '%s\0' "$@"
- else
- cat
- fi \
- | _find0 -maxdepth 0 ! -path "*${genfun_newline}*" -printf '%T+ %p\n' \
- | sort "${sort_opt}" \
- | { IFS= read -r line && printf '%s\n' "${line#* }"; }
-}
-
-#
# Considers the first parameter as a number of centiseconds and determines
# whether fewer have elapsed since the last occasion on which the function was
# called, or whether the last genfun_time update resulted in integer overflow.
diff --git a/functions/rc.sh b/functions/rc.sh
index 0c14035..4eff3c8 100644
--- a/functions/rc.sh
+++ b/functions/rc.sh
@@ -1,6 +1,6 @@
# Copyright 1999-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
-# shellcheck shell=sh disable=3043
+# shellcheck shell=sh disable=3013,3043
# This file contains alternative implementations for some of the functions and
# utilities provided by OpenRC. Please refer to ../functions.sh for coding
@@ -205,9 +205,13 @@ get_bootparam()
# Takes the first parameter as a reference file/directory then determines
# whether any of the following parameters refer to newer files/directories.
#
+# The test utility is required to support the -nt primary, per POSIX-1.2024.
+# However, measures are in place to to achieve compatibility with shells that
+# implement the primary without yet fully adhering to the specification.
+#
is_older_than()
{
- local ref
+ local path ref
if [ "$#" -eq 0 ]; then
warn "is_older_than: too few arguments (got $#, expected at least 1)"
@@ -218,9 +222,20 @@ is_older_than()
ref=
fi
shift
- { test "$#" -gt 0 && printf '%s\0' "$@"; } \
- | _find0 -L ${ref:+-newermm} ${ref:+"${ref}"} -printf '\n' -quit \
- | read -r _
+ for path; do
+ # The first branch addresses a conformance issue whereby
+ # [ existent -nt nonexistent ] is incorrectly false. As of
+ # August 2024, busybox ash, dash, FreeBSD sh and NetBSD sh are
+ # known to be non-conforming in this respect.
+ if [ ! "${ref}" ] && [ -e "${path}" ]; then
+ return
+ elif [ "${path}" -nt "${ref}" ]; then
+ return
+ elif [ -d "${path}" ] && is_older_than "${ref}" "${path}"/*; then
+ return
+ fi
+ done
+ false
}
#