public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Sam James" <sam@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/gentoo-functions:master commit in: functions/, /
Date: Sun, 11 Aug 2024 10:11:10 +0000 (UTC)	[thread overview]
Message-ID: <1723371063.6cf0940b8d336eb35a970af2ffc819f55e3ab429.sam@gentoo> (raw)

commit:     6cf0940b8d336eb35a970af2ffc819f55e3ab429
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Sat Aug 10 05:12:15 2024 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sun Aug 11 10:11:03 2024 +0000
URL:        https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=6cf0940b

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 <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>

 functions.sh    | 157 +++++++++++++++++++++++++++++++++-----------------------
 functions/rc.sh |  25 +++++++--
 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
 }
 
 #
@@ -675,34 +751,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.
 #
@@ -734,25 +782,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

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
 }
 
 #


             reply	other threads:[~2024-08-11 10:11 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-11 10:11 Sam James [this message]
  -- strict thread matches above, loose matches on Subject: below --
2025-10-14 12:59 [gentoo-commits] proj/gentoo-functions:master commit in: functions/, / Sam James
2024-08-11 10:11 Sam James
2024-08-02 23:14 Sam James
2024-06-25  4:06 Sam James

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1723371063.6cf0940b8d336eb35a970af2ffc819f55e3ab429.sam@gentoo \
    --to=sam@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox