1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Fix bugs in testing if a parameter is set

This fixes three related bugs:
1. Expansions like ${var+set} remained static when used within a
   'for', 'while' or 'until' loop; the expansions din't change
   along with the state of the variable, so they could not be used
   to check whether a variable is set within a loop if the state of
   that variable changed in the course of the loop. (BUG_ISSETLOOP)
2. ${IFS+s} always yielded 's', and [[ -v IFS ]] always yielded
   true, even if IFS is unset. (BUG_IFSISSET)
3. IFS was incorrectly exempt from '-u' ('-o nounset') checks.

src/cmd/ksh93/sh/macro.c: varsub():
- When getting a node pointer (np) to the parameter to test,
  special-case IFS by checking if it has a value and not setting
  the pointer if not. The node to IFS always exists, even after
  'unset -v IFS', so before this fix it always followed the code
  path for a parameter that is set. This fixes BUG_IFSISSET for
  ${IFS+s} and also fixes set -u (-o nounset) with IFS.
- Before using the 'nv_isnull' macro to check if a regular variable
  is set, call nv_optimize() if needed. This fixes BUG_ISSETLOOP.
  Idea from Kurtis Rader: https://github.com/att/ast/issues/1090
  Of course this only works if SHOPT_OPTIMIZE==1 (the default),
  but if not, then this bug is not triggered in the first place.
- Add some comments for future reference.

src/cmd/ksh93/bltins/test.c: test_unop():
- Fix BUG_IFSISSET for [[ -v IFS ]]. The nv_optimize() method
  doesn't seem to have any effect here, so the only way that I can
  figure out is to special-case IFS, nv_getval()'ing it to check if
  IFS has a value in the current scope.

src/cmd/ksh93/tests/variables.sh:
- Add regression tests for checking if a varariable is set within a
  loop, within and outside a function with that variable made local
  (to check if the scope is honoured). Repeat these tests for a
  regular variable and for IFS, for ${foo+set} and [[ -v foo ]].

(cherry picked from commit a2cf79cb98fa3e47eca85d9049d1d831636c9b16)
This commit is contained in:
Martijn Dekker 2020-05-20 15:50:43 +01:00
parent 2887378ae3
commit 952944197f
6 changed files with 92 additions and 11 deletions

View file

@ -680,4 +680,62 @@ level=$($SHELL -c $'$SHELL -c \'print -r "$SHLVL"\'')
$SHELL -c 'unset .sh' 2> /dev/null
[[ $? == 1 ]] || err_exit 'unset .sh should return 1'
#'
# ======
# ${var+set} within a loop.
_test_isset() { eval "
$1=initial_value
function _$1_test {
typeset $1 # make local and initially unset
for i in 1 2 3 4 5; do
case \${$1+s} in
( s ) print -n 'S'; unset -v $1 ;;
( '' ) print -n 'U'; $1='' ;;
esac
done
}
_$1_test
[[ -n \${$1+s} && \${$1} == initial_value ]] || exit
for i in 1 2 3 4 5; do
case \${$1+s} in
( s ) print -n 's'; unset -v $1 ;;
( '' ) print -n 'u'; $1='' ;;
esac
done
"; }
expect='USUSUsusus'
actual=$(_test_isset var)
[[ "$actual" = "$expect" ]] || err_exit "\${var+s} expansion fails in loops (expected '$expect', got '$actual')"
actual=$(_test_isset IFS)
[[ "$actual" = "$expect" ]] || err_exit "\${IFS+s} expansion fails in loops (expected '$expect', got '$actual')"
# [[ -v var ]] within a loop.
_test_v() { eval "
$1=initial_value
function _$1_test {
typeset $1 # make local and initially unset
for i in 1 2 3 4 5; do
if [[ -v $1 ]]
then print -n 'S'; unset -v $1
else print -n 'U'; $1=''
fi
done
}
_$1_test
[[ -v $1 && \${$1} == initial_value ]] || exit
for i in 1 2 3 4 5; do
if [[ -v $1 ]]
then print -n 's'; unset -v $1
else print -n 'u'; $1=''
fi
done
"; }
expect='USUSUsusus'
actual=$(_test_v var)
[[ "$actual" = "$expect" ]] || err_exit "[[ -v var ]] expansion fails in loops (expected '$expect', got '$actual')"
actual=$(_test_v IFS)
[[ "$actual" = "$expect" ]] || err_exit "[[ -v IFS ]] expansion fails in loops (expected '$expect', got '$actual')"
# ======
exit $((Errors<125?Errors:125))