mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
arithmetic: Fix the octal leading zero mess (#337)
In C/POSIX arithmetic, a leading 0 denotes an octal number, e.g.
010 == 8. But this is not a desirable feature as it can cause
problems with processing things like dates with a leading zero.
In ksh, you should use 8#10 instead ("10" with base 8).
It would be tolerable if ksh at least implemented it consistently.
But AT&T made an incredible mess of it. For anyone who is not
intimately familiar with ksh internals, it is inscrutable where
arithmetic evaluation special-cases a leading 0 and where it
doesn't. Here are just some of the surprises/inconsistencies:
1. The AT&T maintainers tried to honour a leading 0 inside of
((...)) and $((...)) and not for arithmetic contexts outside it,
but even that inconsistency was never quite consistent.
2. Since 2010-12-12, $((x)) and $(($x)) are different:
$ /bin/ksh -c 'x=010; echo $((x)) $(($x))'
10 8
That's a clear violation of both POSIX and the principle of
least astonishment. $((x)) and $(($x)) should be the same in
all cases.
3. 'let' with '-o letoctal' acts in this bizarre way:
$ set -o letoctal; x=010; let "y1=$x" "y2=010"; echo $y1 $y2
10 8
That's right, 'let y=$x' is different from 'let y=010' even
when $x contains the same string value '010'! This violates
established shell grammar on the most basic level.
This commit introduces consistency. By default, ksh now acts like
mksh and zsh: the octal leading zero is disabled in all arithmetic
contexts equally. In POSIX mode, it is enabled equally.
The one exception is the 'let' built-in, where this can still be
controlled independently with the letoctal option as before (but,
because letoctal is synched with posix when switching that on/off,
it's consistent by default).
We're also removing the hackery that causes variable expansions for
the 'let' builtin to be quietly altered, so that 'x=010; let y=$x'
now does the same as 'let y=010' even with letoctal on.
Various files:
- Get rid of now-redundant sh.inarith (shp->inarith) flag, as we're
no longer distinguishing between being inside or outside ((...)).
src/cmd/ksh93/sh/arith.c:
- arith(): Let disabling POSIX octal constants by skipping leading
zeros depend on either the letoctal option being off (if we're
running the "let" built-in") or the posix option being off.
- sh_strnum(): Preset a base of 10 for strtonll(3) depending on the
posix or letoctal option being off, not on the sh.inarith flag.
src/cmd/ksh93/include/argnod.h,
src/cmd/ksh93/sh/args.c,
src/cmd/ksh93/sh/macro.c:
- Remove astonishing hackery that violated shell grammar for 'let'.
src/cmd/ksh93/sh/name.c (nv_getnum()),
src/cmd/ksh93/sh/nvdisc.c (nv_getn()):
- Remove loops for skipping leading zeroes that included a broken
check for justify/zerofill attributes, thereby fixing this bug:
$ typeset -Z x=0x15; echo $((x))
-ksh: x15: parameter not set
Even if this code wasn't redundant before, it is now: sh_arith()
is called immediately after the removed code and it ignores
leading zeroes via sh_strnum() and strtonll(3).
Resolves: https://github.com/ksh93/ksh/issues/334
This commit is contained in:
parent
257eea612a
commit
c734568b02
14 changed files with 86 additions and 93 deletions
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
||||
|
||||
integer hasposix=0
|
||||
(set -o posix) 2>/dev/null && ((hasposix++)) # not using [[ -o ?posix ]] as it's broken on 93v-
|
||||
|
||||
trap '' FPE # NOTE: osf.alpha requires this (no ieee math)
|
||||
|
||||
integer x=1 y=2 z=3
|
||||
|
|
@ -346,8 +349,14 @@ do (( ipx = ip % 256 ))
|
|||
done
|
||||
unset x
|
||||
x=010
|
||||
(( x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with $((x))'
|
||||
(( $x == 8 )) || err_exit 'leading zeros not treated as octal arithmetic with $x'
|
||||
(( x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with ((x))'
|
||||
(( $x == 10 )) || err_exit 'leading zeros in x treated as octal arithmetic with (($x))'
|
||||
if ((hasposix))
|
||||
then set --posix
|
||||
((x == 8)) || err_exit 'posix: leading zeros in x not treated as octal arithmetic with ((x))'
|
||||
(($x == 8)) || err_exit 'posix: leading zeros in x not treated as octal arithmetic with (($x))'
|
||||
set --noposix
|
||||
fi
|
||||
unset x
|
||||
typeset -Z x=010
|
||||
(( x == 10 )) || err_exit 'leading zeros not ignored for arithmetic'
|
||||
|
|
@ -728,15 +737,26 @@ unset A
|
|||
unset r x
|
||||
integer x
|
||||
r=020
|
||||
(($r == 16)) || err_exit 'leading 0 not treated as octal inside ((...))'
|
||||
(($r == 20)) || err_exit 'leading 0 treated as octal inside ((...))'
|
||||
x=$(($r))
|
||||
(( x == 16 )) || err_exit 'leading 0 not treated as octal inside $((...))'
|
||||
((x == 20)) || err_exit 'leading 0 treated as octal inside $((...))'
|
||||
x=$r
|
||||
((x == 20 )) || err_exit 'leading 0 should not be treated as octal outside ((...))'
|
||||
((x == 20)) || err_exit 'leading 0 treated as octal outside ((...))'
|
||||
print -- -020 | read x
|
||||
((x == -20)) || err_exit 'numbers with leading -0 should not be treated as octal outside ((...))'
|
||||
((x == -20)) || err_exit 'numbers with leading -0 treated as octal outside ((...))'
|
||||
print -- -8#20 | read x
|
||||
((x == -16)) || err_exit 'numbers with leading -8# should be treated as octal'
|
||||
if ((hasposix))
|
||||
then set --posix
|
||||
(($r == 16)) || err_exit 'posix: leading 0 not treated as octal inside ((...))'
|
||||
x=$(($r))
|
||||
(( x == 16 )) || err_exit 'posix: leading 0 not treated as octal inside $((...))'
|
||||
x=$r
|
||||
((x == 16)) || err_exit 'posix: leading 0 not as octal outside ((...))'
|
||||
print -- -020 | read x
|
||||
((x == -16)) || err_exit 'posix: numbers with leading -0 should be treated as octal outside ((...))'
|
||||
set --noposix
|
||||
fi
|
||||
|
||||
unset x
|
||||
x=0x1
|
||||
|
|
@ -750,8 +770,13 @@ let "$x==10" || err_exit 'arithmetic with $x where $x is 010 should be decimal i
|
|||
(( 9.$x == 9.01 )) || err_exit 'arithmetic with 9.$x where x=010 should be 9.01'
|
||||
(( 9$x == 9010 )) || err_exit 'arithmetic with 9$x where x=010 should be 9010'
|
||||
x010=99
|
||||
((x$x == 99 )) || err_exit 'arithtmetic with x$x where x=010 should be $x010'
|
||||
(( 3+$x == 11 )) || err_exit '3+$x where x=010 should be 11 in ((...))'
|
||||
((x$x == 99 )) || err_exit 'arithmetic with x$x where x=010 should be $x010'
|
||||
(( 3+$x == 13 )) || err_exit '3+$x where x=010 should be 13 in ((...))'
|
||||
if ((hasposix))
|
||||
then set --posix
|
||||
(( 3+$x == 11 )) || err_exit 'posix: 3+$x where x=010 should be 11 in ((...))'
|
||||
set --noposix
|
||||
fi
|
||||
let "(3+$x)==13" || err_exit 'let should not recognize leading 0 as octal'
|
||||
unset x
|
||||
typeset -RZ3 x=10
|
||||
|
|
@ -878,8 +903,9 @@ unset got
|
|||
|
||||
# ======
|
||||
# https://github.com/ksh93/ksh/issues/326
|
||||
for m in u d i o x X
|
||||
((hasposix)) && for m in u d i o x X
|
||||
do
|
||||
set --posix
|
||||
case $m in
|
||||
o) exp="10;21;32;" ;;
|
||||
x) exp="8;11;1a;" ;;
|
||||
|
|
@ -887,22 +913,16 @@ do
|
|||
*) exp="8;17;26;" ;;
|
||||
esac
|
||||
got=${ printf "%$m;" 010 021 032; }
|
||||
[[ $got == "$exp" ]] || err_exit "printf %$m does not recognize octal arguments" \
|
||||
[[ $got == "$exp" ]] || err_exit "posix: printf %$m does not recognize octal arguments" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
set --noposix
|
||||
done
|
||||
|
||||
# https://github.com/ksh93/ksh/issues/326#issuecomment-917707463
|
||||
exp=18
|
||||
got=$(( $(integer x; x=010; echo $x) + 010 ))
|
||||
# ^^^ decimal ^^^ octal
|
||||
[[ $got == "$exp" ]] || err_exit 'Integer with leading zero incorrectly interpreted as octal in non-POSIX arith context' \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# BUG_ARITHNAN: In ksh <= 93u+m 2021-11-15 and zsh 5.6 - 5.8, the case-insensitive
|
||||
# floating point constants Inf and NaN are recognised in arithmetic evaluation,
|
||||
# overriding any variables with the names Inf, NaN, INF, nan, etc.
|
||||
if (set --posix) 2>/dev/null
|
||||
if ((hasposix))
|
||||
then set --posix
|
||||
Inf=42 NaN=13
|
||||
inf=421 nan=137
|
||||
|
|
@ -920,5 +940,12 @@ then set --posix
|
|||
set --noposix
|
||||
fi
|
||||
|
||||
# ======
|
||||
# https://github.com/ksh93/ksh/issues/334#issuecomment-968603087
|
||||
exp=21
|
||||
got=$(typeset -Z x=0x15; { echo $((x)); } 2>&1)
|
||||
[[ $got == "$exp" ]] || err_exit "typeset -Z corrupts hexadecimal number in arithmetic context" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue