commit:     d6b03b9ae75d0e76a2620e227cf24d9bcdb5e321
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Mon Jun  3 10:58:44 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=d6b03b9a

Add the whenceforth() function as a type -P alternative

It acts much as type -P does in bash. I would have liked to name it
whence but ksh and zsh already have builtins by that name.

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

 functions.sh   | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
 test-functions | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/functions.sh b/functions.sh
index c077290..504ee9e 100644
--- a/functions.sh
+++ b/functions.sh
@@ -565,6 +565,51 @@ warn()
        printf '%s: %s\n' "${0##*/}" "$*" >&2
 }
 
+#
+# Considers the first parameter as the potential name of an executable regular
+# file before attempting to locate it. If not specifed as an absolute pathname,
+# a PATH search shall be performed in accordance with the Environment Variables
+# section of the Base Definitions. If an executable is found, its path shall be
+# printed. Otherwise, the return value shall be 1. This function is intended as
+# an alternative to type -P in bash. That is, it is useful for determining the
+# existence and location of an external utility without potentially matching
+# against aliases, builtins and functions (as command -v can).
+#
+whenceforth()
+(
+       local bin path prefix
+
+       case $1 in
+               /*)
+                       # Absolute command paths must be directly checked.
+                       [ -f "$1" ] && [ -x "$1" ] && bin=$1
+                       ;;
+               *)
+                       # Relative command paths must be searched for in PATH.
+                       # 
https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
+                       case ${PATH} in
+                               ''|*:)
+                                       path=${PATH}:
+                                       ;;
+                               *)
+                                       path=${PATH}
+                       esac
+                       IFS=:
+                       set -f
+                       for prefix in ${path}; do
+                               case ${prefix} in
+                                       */)
+                                               bin=${prefix}$1
+                                               ;;
+                                       *)
+                                               bin=${prefix:-.}/$1
+                               esac
+                               [ -f "${bin}" ] && [ -x "${bin}" ] && break
+                       done
+       esac \
+       && printf '%s\n' "${bin}"
+)
+
 #
 # Determines whether the first parameter is truthy. The values taken to be true
 # are "yes", "true", "on" and "1", whereas their opposites are taken to be
@@ -876,8 +921,7 @@ fi
 
 # Store the path to the true binary. It is potentially used by _update_columns.
 if [ "${BASH}" ]; then
-       # shellcheck disable=3045
-       genfun_bin_true=$(type -P true)
+       genfun_bin_true=$(whenceforth true)
 fi
 
 # Store the name of the GNU find binary. Some platforms may have it as "gfind".

diff --git a/test-functions b/test-functions
index d90462e..813d524 100755
--- a/test-functions
+++ b/test-functions
@@ -507,6 +507,44 @@ test_hr() {
        iterate_tests 5 "$@"
 }
 
+test_whenceforth() {
+       set -- \
+               ge  1  PATH                        N/A                       \
+               ge  1  PATH                        .                         \
+               ge  1  PATH                        rather-unlikely-to-exist  \
+               ge  1  PATH                        /var/empty                \
+               ge  1  PATH                        /var/empty/nofile         \
+               eq  0  PATH                        /bin/sh                   \
+               eq  0  PATH                        sh                        \
+               eq  0  ''                          newer/file                \
+               eq  0  .                           newer/file                \
+               eq  0  :/var/empty/x               newer/file                \
+               eq  0  /var/empty/x:               newer/file                \
+               eq  0  /var/empty/x::/var/empty/y  newer/file                \
+               ge  1  ''                          older/file                \
+               ge  1  .                           older/file                \
+               ge  1  :/var/empty/x               older/file                \
+               ge  1  /var/empty/x:               older/file                \
+               ge  1  /var/empty/x::/var/empty/y  older/file
+
+       chmod +x newer/file
+
+       callback() {
+               shift
+               path=$1
+               shift
+               test_description="whenceforth $(_print_args "$@")"
+               if [ "${path}" = PATH ]; then
+                       whenceforth "$@" >/dev/null
+               else
+                       PATH=${path} whenceforth "$@" >/dev/null
+               fi
+       }
+
+       iterate_tests 4 "$@"
+}
+
+
 iterate_tests() {
        slice_width=$1
        shift
@@ -571,6 +609,7 @@ test_srandom || rc=1
 test_newest || rc=1
 test_trim || rc=1
 test_hr || rc=1
+test_whenceforth || rc=1
 
 cleanup_tmpdir
 

Reply via email to