commit:     2a78a15de6e5d90da0657bf74929993b1b51a337
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Thu Jun  8 05:48:02 2023 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Fri Jun  9 06:57:10 2023 +0000
URL:        
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=2a78a15d

Add a chdir() function to act as a safer alternative to the cd builtin

To run cd "$dir" is problematic because:

1) it may consider its operand as an option
2) it will search CDPATH for an operand not beginning with ./, ../ or /
3) it will switch to OLDPWD if the operand is -
4) cdable_vars causes bash to treat the operand as a potential varname

This commit introduces a chdir() function that addresses all of these
pitfalls.

Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>

 functions.sh   | 18 +++++++++++++++++
 test-functions | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 78 insertions(+), 3 deletions(-)

diff --git a/functions.sh b/functions.sh
index 7838c90..154c8a4 100644
--- a/functions.sh
+++ b/functions.sh
@@ -534,6 +534,24 @@ is_int() {
        esac
 }
 
+#
+#   A safe wrapper for the cd builtin. To run cd "$dir" is problematic because:
+#
+#   1) it may consider its operand as an option
+#   2) it will search CDPATH for an operand not beginning with ./, ../ or /
+#   3) it will switch to OLDPWD if the operand is -
+#   4) cdable_vars causes bash to treat the operand as a potential variable 
name
+#
+chdir() {
+       if [ "$BASH" ]; then
+               shopt -u cdable_vars
+       fi
+       if [ "$1" = - ]; then
+               set -- ./-
+       fi
+       CDPATH= cd -- "$@"
+}
+
 #
 #   Determine whether the first operand contains any visible characters.
 #

diff --git a/test-functions b/test-functions
index b80587b..5a6b23b 100755
--- a/test-functions
+++ b/test-functions
@@ -14,7 +14,7 @@ bailout() {
 assign_tmpdir() {
        # shellcheck disable=1007
        dir=$(mktemp -d) \
-       && CDPATH= cd -- "${dir}" \
+       && chdir "${dir}" \
        || bailout "Couldn't create or change to the temp dir"
 }
 
@@ -24,6 +24,49 @@ cleanup_tmpdir() {
        fi
 }
 
+test_chdir() {
+       set -- \
+               1  grandchild  \
+               1         var  \
+               0          -L  \
+               0          -p  \
+               0          -e  \
+               0          -@  \
+               0           -  \
+               0       child
+
+       if ! mkdir -p -- -L -p -e -@ - child child/grandchild; then
+               bailout "Couldn't set up all test directories"
+       fi
+
+       callback() {
+               shift
+               test_description="chdir $(print_args "$@")"
+               if [ "$BASH" ]; then
+                       shopt -s cdable_vars
+               fi
+               CDPATH=child var=$CDPATH chdir "$@" \
+               && test "$PWD" != "$OLDPWD" \
+               && cd - >/dev/null
+       }
+
+       iterate_tests 2 "$@"
+}
+
+test_chdir_noop() {
+       set -- 0 ''
+
+       callback() {
+               shift
+               test_description="chdir $(print_args "$@")"
+               chdir "$@" \
+               && test "$PWD" = "$OLDPWD" \
+               || { cd - >/dev/null; false; }
+       }
+
+       iterate_tests 2 "$@"
+}
+
 test_is_older_than() {
        set -- \
                1  N/A           N/A \
@@ -317,12 +360,24 @@ iterate_tests() {
                        fi
                done
                eval "${code}"
-               if [ "$?" -eq "$1" ]; then
+               case $? in
+                       0)
+                               test "$?" -eq "$1"
+                               ;;
+                       *)
+                               test "$?" -ge "$1"
+               esac
+               if [ "$?" -eq 0 ]; then
                        passed=$((passed + 1))
                else
                        printf 'not '
                fi
-               printf 'ok %d - %s (expecting %d)\n' "${i}" 
"${test_description}" "$1"
+               if [ "$1" -eq 0 ]; then
+                       expected=$1
+               else
+                       expected=">=$1"
+               fi
+               printf 'ok %d - %s (expecting %s)\n' "${i}" 
"${test_description}" "${expected}"
                shift "${slice_width}"
        done
        return "$(( passed < total ))"
@@ -359,6 +414,8 @@ export TEST_GENFUNCS=1
 export TZ=UTC
 
 rc=0
+test_chdir || rc=1
+test_chdir_noop || rc=1
 ( ewarn() { true; }; test_is_older_than ) || rc=1
 test_get_bootparam || rc=1
 test_esyslog || rc=1

Reply via email to