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

Fix test -v for numeric types & set/unset state for short int

This commit fixes two interrelated problems.

1. The -v unary test/[/[[ operator is documented to test if a
   variable is set. However, it always returns true for variable
   names with a numeric attribute, even if the variable has not
   been given a value. Reproducer:
	$ ksh -o nounset -c 'typeset -i n; [[ -v n ]] && echo $n'
	ksh: n: parameter not set
   That is clearly wrong; 'echo $n' should never be reached and the
   error should not occur, and does not occur on mksh or bash.

2. Fixing the previous problem revealed serious breakage in short
   integer type variables that was being masked. After applying
   that fix and then executing 'typeset -si var=0':
   - The conditional assignment expansions ${var=123} and
     ${var:=123} assigned 123 to var, even though it was set to 0.
   - The expansions ${var+s} and ${var:+n} incorrectly acted as if
     the variable was unset and empty, respectively.
   - '[[ -v var ]]' and 'test -v var' incorrectly returned false.
   The problems were caused by a different storage method for short
   ints. Their values were stored directly in the 'union Value'
   member of the Namval_t struct, instead of allocated on the stack
   and referred to by a pointer, as regular integers and all other
   types do. This inherently broke nv_isnull() as this leaves no
   way to distinguish between a zero value and no value at all.
   (I'm also pretty sure it's undefined behaviour in C to check for
   a null pointer at the address where a short int is stored.)
   The fix is to store short ints like other variables and refer
   to them by pointers. The NV_INT16P combined bit mask already
   existed for this, but nv_putval() did not yet support it.

src/cmd/ksh93/bltins/test.c: test_unop():
- Fix problem 1. For -v, only check nv_isnull() and do not check
  for the NV_INTEGER attribute (which, by the way, is also used
  for float variables by combining it with other bits).
  See also 5aba0c72 where we recently fixed nv_isnull() to
  work properly for all variable types including short ints.

src/cmd/ksh93/sh/name.c: nv_putval():
- Fix problem 2, part 1. Add support for NV_INT16P. The code is
  simply copied and adapted from the code for regular integers, a
  few lines further on. The regular NV_SHORT code is kept as this
  is still used for some special variables like ${.sh.level}.

src/cmd/ksh93/bltins/typeset.c: b_typeset():
- Fix problem 2, part 2. Use NV_INT16P instead of NV_SHORT.

src/cmd/ksh93/tests/attributes.sh:
- Add set/unset/empty/nonempty tests for all numeric types.

src/cmd/ksh93/tests/bracket.sh,
src/cmd/ksh93/tests/comvar.sh:
- Update a couple of existing tests.
- Add test for [[ -v var ]] and [[ -n ${var+s} ]] on unset
  and empty variables with many attributes.

src/cmd/ksh93/COMPATIBILITY:
- Add a note detailing the change to test -v.

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/sh.1:
- Correct 'typeset -C' documentation. Variables declared as
  compound are *not* initially unset, but initially have the empty
  compound value. 'typeset' outputs them as:
	typeset -C foo=()
  and not:
	typeset -C foo
  and nv_isnull() is never true for them. This may or may not
  technically be a bug. I don't think it's worth changing, but
  it should at least be documented correctly.
This commit is contained in:
Martijn Dekker 2021-03-10 00:38:00 +00:00
parent 4a8072e826
commit d4adc8fcf9
10 changed files with 79 additions and 11 deletions

View file

@ -667,5 +667,33 @@ exp=$'00000\n000\n0000000'
[[ $(typeset -Z4 x; typeset -rZ x; typeset -p x) == 'typeset -r -Z 0 -R 0 x' ]] || err_exit "typeset -rZ failed to set new size."
[[ $(typeset -bZ4 x; typeset -rbZ x; typeset -p x) == 'typeset -r -b -Z 0 -R 0 x' ]] || err_exit "typeset -rbZ failed to set new size."
# ======
# set/unset tests for numeric types
for flag in i s si ui us usi uli E F X lX
do
unset var
typeset "-$flag" var
[[ -v var ]] && err_exit "[[ -v var ]] should return false after typeset -$flag var"
[[ -z ${var+s} ]] || err_exit "\${var+s} should be empty after typeset -$flag var (got '${var+s}')"
[[ -z ${var:+n} ]] || err_exit "\${var:+n} should be empty after typeset -$flag var (got '${var+n}')"
[[ ${var-u} == 'u' ]] || err_exit "\${var-u} should be 'u' after typeset -$flag var (got '${var-u}')"
[[ ${var:-e} == 'e' ]] || err_exit "\${var:-e} should be 'e' after typeset -$flag var (got '${var:-e}')"
(( ${var=1} == 1 )) || err_exit "\${var=1} should yield 1 after typeset -$flag var (got '$var')"
unset var
typeset "-$flag" var
(( ${var:=1} == 1 )) || err_exit "\${var:=1} should yield 1 after typeset -$flag var (got '$var')"
unset var
typeset "-$flag" var=0
[[ -v var ]] || err_exit "[[ -v var ]] should return true after typeset -$flag var=0"
[[ ${var+s} == 's' ]] || err_exit "\${var+s} should be 's' after typeset -$flag var=0"
[[ ${var:+n} == 'n' ]] || err_exit "\${var:+n} should be 'n' after typeset -$flag var=0"
[[ ${var-u} != 'u' ]] || err_exit "\${var-u} should be 0 after typeset -$flag var (got '${var-u}')"
[[ ${var:-e} != 'e' ]] || err_exit "\${var:-e} should be 0 after typeset -$flag var (got '${var:-e}')"
(( ${var=1} == 0 )) || err_exit "\${var=1} should yield 0 after typeset -$flag var=0 (got '$var')"
unset var
typeset "-$flag" var=0
(( ${var:=1} == 0 )) || err_exit "\${var:=1} should yield 0 after typeset -$flag var=0 (got '$var')"
done
# ======
exit $((Errors<125?Errors:125))

View file

@ -354,7 +354,9 @@ $SHELL 2> /dev/null -c '[[ "]" == ~(E)[]] ]]' || err_exit 'pattern "~(E)[]]" doe
unset var
[[ -v var ]] && err_exit '[[ -v var ]] should be false after unset var'
float var
[[ -v var ]] || err_exit '[[ -v var ]] should be true after float var'
[[ -v var ]] && err_exit '[[ -v var ]] should be false after float var'
unset var; float var=
[[ -v var ]] || err_exit '[[ -v var ]] should be true after float var='
unset var
[[ -v var ]] && err_exit '[[ -v var ]] should be false after unset var again'
@ -389,5 +391,22 @@ error=$(set +x; "$SHELL" -c '[[ AATAAT =~ (AAT){2} ]]' 2>&1) \
error=$(set +x; "$SHELL" -c '[[ AATAATCCCAATAAT =~ (AAT){2}CCC(AAT){2} ]]' 2>&1) \
|| err_exit "[[ AATAATCCCAATAAT =~ (AAT){2}CCC(AAT){2} ]] does not match${error:+ (got $(printf %q "$error"))}"
# ======
# The -v unary operator should work for names with all type attributes.
empty=
for flag in a b i l n s si u ui usi uli E F H L Mtolower Mtoupper R X lX S Z
do unset var
typeset "-$flag" var
[[ -v var ]] && err_exit "[[ -v var ]] should be false for unset var with attribute -$flag"
[[ -n ${var+s} ]] && err_exit "[[ -n \${var+s} ]] should be false for unset var with attribute -$flag"
unset var
case $flag in
n) typeset -n var=empty ;;
*) typeset "-$flag" var= ;;
esac
[[ -v var ]] || err_exit "[[ -v var ]] should be true for empty var with attribute -$flag"
[[ -n ${var+s} ]] || err_exit "[[ -n \${var+s} ]] should be true for empty var with attribute -$flag"
done
# ======
exit $((Errors<125?Errors:125))

View file

@ -466,7 +466,7 @@ expected=$'typeset -C -a mica01=([4]=(a_string=\'foo bar\';some_stuff=hello))'
[[ $(typeset -p mica01) == "$expected" ]] || err_exit 'appened to indexed array compound variable not working'
unset x
compound x=( integer x ; )
compound x=( integer x= ; )
[[ ! -v x.x ]] && err_exit 'x.x should be set'
expected=$'(\n\ttypeset -l -i x=0\n)'
[[ $(print -v x) == "$expected" ]] || err_exit "'print -v x' should be $expected"