commit:     6b1ea77cf5befc19999701a73c64eb39f0dafacd
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Sun Jun  2 20:57:49 2024 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Wed Jun 12 07:06:42 2024 +0000
URL:        
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=6b1ea77c

Add the oldest() and newest() functions

It compares potentially existing pathnames, printing the oldest and
newest among them by modification time, respectively.

$ . /lib/gentoo/functions.sh
$ newest /etc/*
/etc/environment.d
$ printf '%s\0' /etc/* | newest
/etc/environment.d

Support for pathnames containing <newline> characters may be added once
read -d becomes broadly supported.

https://austingroupbugs.net/view.php?id=245

Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>

 functions.sh   | 74 ++++++++++++++++++++++++++++++++++++++++++----------------
 test-functions | 39 +++++++++++++++++++++++++++++++
 2 files changed, 93 insertions(+), 20 deletions(-)

diff --git a/functions.sh b/functions.sh
index 0c35b2d..1548bd0 100644
--- a/functions.sh
+++ b/functions.sh
@@ -1,6 +1,6 @@
 # Copyright 1999-2023 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
-# shellcheck shell=sh disable=3043
+# shellcheck shell=sh disable=2209,3043
 
 # This file contains a series of function declarations followed by some
 # initialisation code. Functions intended for internal use shall be prefixed
@@ -401,7 +401,7 @@ is_int()
 #
 is_older_than()
 {
-       local ref has_gfind
+       local ref
 
        if [ "$#" -eq 0 ]; then
                warn "is_older_than: too few arguments (got $#, expected at 
least 1)"
@@ -412,26 +412,39 @@ is_older_than()
                ref=
        fi
        shift
+       { test "$#" -gt 0 && printf '%s\0' "$@"; } \
+       | "${genfun_bin_find}" -L -files0-from - ${ref:+-newermm} 
${ref:+"${ref}"} -printf '\n' -quit \
+       | read -r _
+}
 
-       # Check whether GNU find is installed by the name of "gfind". So as to
-       # avoid repeated PATH lookups, run the hash builtin in the present
-       # shell, prior to forking.
-       hash gfind 2>/dev/null; has_gfind=$(( $? == 0 ))
+#
+# 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.
+#
+newest()
+{
+       _select_by_mtime -r "$@"
+}
 
-       for path; do
-               if [ -e "${path}" ]; then
-                       printf '%s\0' "${path}"
-               fi
-       done |
-       {
-               set -- -L -files0-from - ${ref:+-newermm} ${ref:+"${ref}"} 
-printf '\n' -quit
-               if [ "${has_gfind}" -eq 1 ]; then
-                       gfind "$@"
-               else
-                       find "$@"
-               fi
-       } |
-       read -r _
+#
+# 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.
+#
+oldest()
+{
+       _select_by_mtime -- "$@"
 }
 
 #
@@ -726,6 +739,24 @@ _print_args()
        EOF
 }
 
+#
+# See the definitions of oldest() and newest().
+#
+_select_by_mtime() {
+       local sort_opt
+
+       sort_opt=$1
+       shift
+       if [ "$#" -ge 0 ]; then
+               printf '%s\0' "$@"
+       else
+               cat
+       fi \
+       | "${genfun_bin_find}" -files0-from - -maxdepth 0 ! -path 
"*${genfun_newline}*" -printf '%T+ %p\n' \
+       | sort "${sort_opt}" \
+       | { IFS= read -r line && printf '%s\n' "${line#* }"; }
+}
+
 #
 # Determines whether the terminal on STDIN is able to report its dimensions.
 # Upon success, the number of columns shall be stored in genfun_cols.
@@ -805,6 +836,9 @@ if [ "${BASH}" ]; then
        genfun_bin_true=$(type -P true)
 fi
 
+# Store the name of the GNU find binary. Some platforms may have it as "gfind".
+hash gfind 2>/dev/null && genfun_bin_find=gfind || genfun_bin_find=find
+
 # Determine whether the use of color is to be wilfully avoided.
 if [ -n "${NO_COLOR}" ]; then
        # See https://no-color.org/.

diff --git a/test-functions b/test-functions
index 0c4a222..400ddb2 100755
--- a/test-functions
+++ b/test-functions
@@ -418,6 +418,44 @@ test_srandom() {
        iterate_tests 2 "$@"
 }
 
+test_newest() {
+       set -- \
+               ge  1  non-existent  non-existent  \
+               ge  1  N/A           N/A           \
+                                                  \
+               eq  0  newer/file    N/A           \
+               eq  0  newer/file    newer/file    \
+               eq  0  newer/file    non-existent  \
+               eq  0  newer/file    older/file    \
+               eq  0  non-existent  newer/file    \
+               eq  0  older/file    newer/file    \
+                                                  \
+               eq  0  older/file    N/A           \
+               eq  0  older/file    older/file    \
+               eq  0  older/file    non-existent  \
+               ge  1  newer/file    older/file    \
+               eq  0  non-existent  older/file    \
+               ge  1  older/file    newer/file
+
+       row=0
+
+       callback() {
+               shift
+               test_description="newest $(_print_args "$@")"
+               row=$(( row + 1 ))
+               true |
+               if [ "${row}" -le 2 ]; then
+                       newest "$@"
+               elif [ "${row}" -le 8 ]; then
+                       test "$(newest "$@")" = "newer/file"
+               else
+                       test "$(newest "$@")" = "older/file"
+               fi
+       }
+
+       iterate_tests 4 "$@"
+}
+
 iterate_tests() {
        slice_width=$1
        shift
@@ -479,6 +517,7 @@ test_yesno || rc=1
 test_die || rc=1
 test_edo || rc=1
 test_srandom || rc=1
+test_newest || rc=1
 
 cleanup_tmpdir
 

Reply via email to