1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 11:42:21 +00:00

Fix ${x=y} and ${x:=y} for numeric types of x

These POSIX expansions first assign y to x if x is unset or empty,
respectively, and then they yield the value of x. This was not
working on any ksh93 version if x was typeset as numeric (integer
or float) but still unset, as in not assigned a value.

$ unset a; typeset -i a; printf '%q\n' "${a:=42}" "$a"
0
''

Expected output:
42
42

src/cmd/ksh93/sh/macro.c:
- Fix the test for set/unset variable. It was broken because it
  only checked for the existence of the node, which exists after
  'typeset', but did not check if a value had been assigned. This
  additional check needs to be done with the nv_isnull() macro, but
  only for expansions of the regular M_BRACE type. Special
  expansions cannot have an unset state.
- As of commit 95294419, we know that an nv_optimize() call may be
  needed before using nv_isnull() if the shell is compiled with
  SHOPT_OPTIMIZE. Move the nv_optimize() call from that commit
  forward to before the new check that calls nv_isnull(), and only
  bother with it if the type is M_BRACE.

src/cmd/ksh93/tests/variables.sh:
- Add tests for this bug. Test float and integer, and also check
  that ${a=b} and ${a:=b} correctly treat the value of 'b' as an
  arithmetic expression of which the result is assigned to 'a' if
  'a' was typeset as numeric.

src/cmd/ksh93/tests/attributes.sh,
src/cmd/ksh93/tests/comvar.sh,
src/cmd/ksh93/tests/nameref.sh,
src/cmd/ksh93/tests/types.sh:
- Fix a number of tests to report failures correctly.

Resolves: https://github.com/ksh93/ksh/issues/157
This commit is contained in:
Martijn Dekker 2021-03-06 03:56:52 +00:00
parent f8f2c4b608
commit 9f2389ed93
8 changed files with 60 additions and 19 deletions

6
NEWS
View file

@ -3,6 +3,12 @@ For full details, see the git log at: https://github.com/ksh93/ksh
Any uppercase BUG_* names are modernish shell bug IDs. Any uppercase BUG_* names are modernish shell bug IDs.
2021-03-06:
- Fixed an old expansion bug: expansions of type ${var=value} and ${var:=value}
did not perform an assignment and yielded the value 0 if 'var' was typeset as
numeric (integer or float) but had not yet been assigned a value.
2021-03-05: 2021-03-05:
- Unbalanced quotes and backticks now correctly produce a syntax error - Unbalanced quotes and backticks now correctly produce a syntax error

View file

@ -20,7 +20,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */ #define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2021-03-05" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_DATE "2021-03-06" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK #define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */

View file

@ -1314,7 +1314,7 @@ retry1:
* a value; otherwise it always follows the code path for a set parameter, so is * a value; otherwise it always follows the code path for a set parameter, so is
* not subject to 'set -u', and may test as set even after 'unset -v IFS'. * not subject to 'set -u', and may test as set even after 'unset -v IFS'.
*/ */
if(!(*id=='I' && strcmp(id,"IFS")==0 && nv_getval(sh_scoped(mp->shp,IFSNOD)) == NULL)) if(!(*id=='I' && strcmp(id,"IFS")==0 && nv_getval(sh_scoped(mp->shp,IFSNOD)) == NIL(char*)))
np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL); np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL);
if(!np) if(!np)
{ {
@ -1405,9 +1405,13 @@ retry1:
/* /*
* Check if the parameter is set or unset. * Check if the parameter is set or unset.
*/ */
if(np && (type==M_TREE || !c || !ap)) #if SHOPT_OPTIMIZE
if(np && type==M_BRACE && mp->shp->argaddr)
nv_optimize(np); /* needed before calling nv_isnull() */
#endif /* SHOPT_OPTIMIZE */
if(np && (type==M_BRACE ? !nv_isnull(np) : (type==M_TREE || !c || !ap)))
{ {
/* The parameter is set. */ /* Either the parameter is set, or it's a special type of expansion where 'unset' doesn't apply. */
char *savptr; char *savptr;
c = *((unsigned char*)stkptr(stkp,offset-1)); c = *((unsigned char*)stkptr(stkp,offset-1));
savptr = stkfreeze(stkp,0); savptr = stkfreeze(stkp,0);
@ -1443,14 +1447,8 @@ retry1:
if(ap) if(ap)
v = nv_arrayisset(np,ap)?(char*)"x":0; v = nv_arrayisset(np,ap)?(char*)"x":0;
else else
{
#if SHOPT_OPTIMIZE
if(mp->shp->argaddr)
nv_optimize(np); /* avoid BUG_ISSETLOOP */
#endif /* SHOPT_OPTIMIZE */
v = nv_isnull(np)?0:(char*)"x"; v = nv_isnull(np)?0:(char*)"x";
} }
}
else else
v = nv_getval(np); v = nv_getval(np);
mp->atmode = (v && mp->quoted && mode=='@'); mp->atmode = (v && mp->quoted && mode=='@');

View file

@ -204,7 +204,7 @@ else b1=iIWTk5ZAppaZk4Q=
fi fi
z=$b1 z=$b1
typeset -b x=$b1 typeset -b x=$b1
[[ $x == "$z" ]] || print -u2 'binary variable not expanding correctly' [[ $x == "$z" ]] || err_exit "binary variable not expanding correctly ($(printf %q "$x") != $(printf %q "$z"))"
[[ $(printf "%B" x) == $t1 ]] || err_exit 'typeset -b not working' [[ $(printf "%B" x) == $t1 ]] || err_exit 'typeset -b not working'
typeset -b -Z5 a=$b1 typeset -b -Z5 a=$b1
[[ $(printf "%B" a) == $w1 ]] || err_exit 'typeset -b -Z5 not working' [[ $(printf "%B" a) == $w1 ]] || err_exit 'typeset -b -Z5 not working'

View file

@ -482,9 +482,11 @@ typeset -C -A hello19=(
) )
) )
expected="typeset -C -A hello19=([19]=(one='xone 19';two='xtwo 19') [23]=(one='xone 23';two='xtwo 23'))" expected="typeset -C -A hello19=([19]=(one='xone 19';two='xtwo 19') [23]=(one='xone 23';two='xtwo 23'))"
[[ $(typeset -p hello19) == "$expected" ]] || print -u2 'typeset -p hello19 incorrect' got=$(typeset -p hello19)
[[ $got == "$expected" ]] || err_exit "typeset -p hello19 incorrect (expected $(printf %q "$expected"), got $(printf %q "$got"))"
expected=$'(\n\tone=\'xone 19\'\n\ttwo=\'xtwo 19\'\n) (\n\tone=\'xone 23\'\n\ttwo=\'xtwo 23\'\n)' expected=$'(\n\tone=\'xone 19\'\n\ttwo=\'xtwo 19\'\n) (\n\tone=\'xone 23\'\n\ttwo=\'xtwo 23\'\n)'
[[ ${hello19[@]} == "$expected" ]] || print -u2 '${hello19[@]} incorrect' got=${hello19[@]}
[[ $got == "$expected" ]] || err_exit "\${hello19[@]} incorrect (expected $(printf %q "$expected"), got $(printf %q "$got"))"
typeset -C -A foo1=( abc="alphabet" ) foo2=( abc="alphabet" ) typeset -C -A foo1=( abc="alphabet" ) foo2=( abc="alphabet" )
function add_one function add_one

View file

@ -161,9 +161,10 @@ then err_exit 'for loop nameref optimization test2 error'
fi fi
unset -n x foo bar unset -n x foo bar
if [[ $(nameref x=foo;for x in foo bar;do print ${!x};done) != $'foo\nbar' ]] exp=$'foo\nbar'
then err_exit 'for loop optimization with namerefs not working' got=$(nameref x=foo;for x in foo bar;do print ${!x};done)
fi [[ $got == "$exp" ]] || err_exit 'for loop optimization with namerefs not working' \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
if [[ $( if [[ $(
p=(x=(r=3) y=(r=4)) p=(x=(r=3) y=(r=4))
for i in x y for i in x y
@ -465,9 +466,9 @@ EOF
} 2> /dev/null #|| print -u2 bad } 2> /dev/null #|| print -u2 bad
exitval=$? exitval=$?
if [[ $(kill -l $exitval) == SEGV ]] if [[ $(kill -l $exitval) == SEGV ]]
then print -u2 'name reference to unset type instance causes segmentation violation' then err_exit 'name reference to unset type instance causes segmentation violation'
else if((exitval)) else if((exitval))
then print -u2 'name reference to unset type instance not redirected to .deleted' then err_exit 'name reference to unset type instance not redirected to .deleted'
fi fi
fi fi

View file

@ -84,7 +84,7 @@ Frame_t frame
[[ $(typeset -p frame) == 'Frame_t frame=(typeset file;typeset lineno)' ]] || err_exit 'empty fields in type not displayed' [[ $(typeset -p frame) == 'Frame_t frame=(typeset file;typeset lineno)' ]] || err_exit 'empty fields in type not displayed'
x=( typeset -a arr=([2]=abc [4]=(x=1 y=def));zz=abc) x=( typeset -a arr=([2]=abc [4]=(x=1 y=def));zz=abc)
typeset -C y=x typeset -C y=x
[[ "$x" == "$y" ]] || print -u2 'y is not equal to x' [[ "$x" == "$y" ]] || err_exit "y is not equal to x (y == $(printf %q "$y"); x == $(printf %q "$x"))"
Type_t z=(y=(xa=bb xq=cc)) Type_t z=(y=(xa=bb xq=cc))
typeset -A arr=([foo]=one [bar]=2) typeset -A arr=([foo]=one [bar]=2)
typeset -A brr=([foo]=one [bar]=2) typeset -A brr=([foo]=one [bar]=2)

View file

@ -1198,5 +1198,39 @@ unset .sh.fun
got=$(some_func() { :; }; trap some_func DEBUG; trap - DEBUG; print -r "${.sh.fun}") got=$(some_func() { :; }; trap some_func DEBUG; trap - DEBUG; print -r "${.sh.fun}")
[[ -z $got ]] || err_exit "\${.sh.fun} leaks out of DEBUG trap (got $(printf %q "$got"))" [[ -z $got ]] || err_exit "\${.sh.fun} leaks out of DEBUG trap (got $(printf %q "$got"))"
# =====
# Before 2021-03-06, ${foo=bar} and ${foo:=bar} did not work if `foo` had a numeric type
# https://github.com/ksh93/ksh/issues/157
unset a b
typeset -i a
b=3+39
got=${a=b}
[[ $got == 42 ]] || err_exit "\${a=b}: expansion not working for integer type (expected '42', got '$got')"
[[ $a == 42 ]] || err_exit "\${a=b}: a was not assigned the correct integer value (expected '42', got '$a')"
unset a b
typeset -F a
b=3.75+38.25
got=${a=b}
exp=42.0000000000
[[ $got == "$exp" ]] || err_exit "\${a=b}: expansion not working for float type (expected '$exp', got '$got')"
[[ $a == "$exp" ]] || err_exit "\${a=b}: a was not assigned the correct float value (expected '$exp', got '$a')"
unset a b
typeset -i a
b=3+39
got=${a:=b}
[[ $got == 42 ]] || err_exit "\${a:=b}: expansion not working for integer type (expected '42', got '$got')"
[[ $a == 42 ]] || err_exit "\${a:=b}: a was not assigned the correct integer value (expected '42', got '$a')"
unset a b
typeset -F a
b=3.75+38.25
got=${a:=b}
exp=42.0000000000
[[ $got == "$exp" ]] || err_exit "\${a:=b}: expansion not working for float type (expected '$exp', got '$got')"
[[ $a == "$exp" ]] || err_exit "\${a:=b}: a was not assigned the correct float value (expected '$exp', got '$a')"
# ====== # ======
exit $((Errors<125?Errors:125)) exit $((Errors<125?Errors:125))