From d25dbcc1efa58ac99cd9bce35a4c81af0578d889 Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Thu, 3 Jun 2021 15:47:25 +0200 Subject: [PATCH] [[ ... ]]: fix '!' to negate another '!' Bug: [[ ! ! 1 -eq 1 ]] returns false, but should return true. This bug was reported for bash, but ksh has it too: https://lists.gnu.org/archive/html/bug-bash/2021-06/msg00006.html Op 24-05-21 om 17:47 schreef Chet Ramey: > On 5/22/21 2:45 PM, Vincent Menegaux wrote: >> Previously, these commands: >> >> [[ ! 1 -eq 1 ]]; echo $? >> [[ ! ! 1 -eq 1 ]]; echo $? >> >> would both result in `1', since parsing `!' set CMD_INVERT_RETURN >> instead of toggling it. > > Interestingly, ksh93 produces the same result as bash. I agree > that it's more intuitive to toggle it. Also interesting is that '!' as an argument to the simple 'test'/'[' command does work as expected (on both bash and ksh93): 'test ! ! 1 -eq 1' and '[ ! ! 1 -eq 1 ]' return 0/true. Even the man page for [[ is identical for bash and ksh93: | ! expression | True if expression is false. This suggests it's supposed to be a logical negation operator, i.e. '!' is implicitly documented to negate another '!'. Bolsky & Korn's 1995 ksh book, p. 167, is slightly more explicit about it: "! test-expression. Logical negation of test-expression." I also note that multiple '!' negators in '[[' work as expected on mksh, yash and zsh. src/cmd/ksh93/sh/parse.c: test_primary(): - Fix bitwise logic for '!': xor the TNEGATE bit into tretyp instead of or'ing it, which has the effect of toggling it. --- NEWS | 6 ++++++ src/cmd/ksh93/COMPATIBILITY | 4 ++++ src/cmd/ksh93/include/version.h | 2 +- src/cmd/ksh93/sh/parse.c | 2 +- src/cmd/ksh93/tests/bracket.sh | 12 ++++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 3fea6662b..b6a7282ee 100644 --- a/NEWS +++ b/NEWS @@ -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. +2021-06-03: + +- Fixed a bug in the [[ compound command: the '!' logical negation operator + now correctly negates another '!', e.g., [[ ! ! 1 -eq 1 ]] now returns + 0/true. Note that this has always been the case for 'test'/'['. + 2021-05-18: - Fixed SHLVL so that replacing ksh by itself (exec ksh) will not increase it. diff --git a/src/cmd/ksh93/COMPATIBILITY b/src/cmd/ksh93/COMPATIBILITY index b6b35809b..94154d6b6 100644 --- a/src/cmd/ksh93/COMPATIBILITY +++ b/src/cmd/ksh93/COMPATIBILITY @@ -147,6 +147,10 @@ For more details, see the NEWS file and for complete details, see the git log. zero instead of negative infinity. Previously, int() was an alias to floor(), but now it behaves like trunc(). +27. The '!' logical negation operator in the '[[' compound command now + correctly negates another '!', e.g., [[ ! ! 1 -eq 1 ]] now returns + 0/true. Note that this has always been the case for 'test'/'['. + ____________________________________________________________________________ KSH-93 VS. KSH-88 diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 420bc1910..ff36c716e 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -21,7 +21,7 @@ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */ -#define SH_RELEASE_DATE "2021-05-18" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2021-06-03" /* must be in this format for $((.sh.version)) */ #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. */ diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c index bfeb477b3..d63ea13c2 100644 --- a/src/cmd/ksh93/sh/parse.c +++ b/src/cmd/ksh93/sh/parse.c @@ -1916,7 +1916,7 @@ static Shnode_t *test_primary(Lex_t *lexp) case '!': if(!(t = test_primary(lexp))) sh_syntax(lexp); - t->tre.tretyp |= TNEGATE; + t->tre.tretyp ^= TNEGATE; /* xor it, so that a '!' negates another '!' */ return(t); case TESTUNOP: if(sh_lex(lexp)) diff --git a/src/cmd/ksh93/tests/bracket.sh b/src/cmd/ksh93/tests/bracket.sh index 675c8d6b8..479468fcc 100755 --- a/src/cmd/ksh93/tests/bracket.sh +++ b/src/cmd/ksh93/tests/bracket.sh @@ -411,5 +411,17 @@ unset foo foo=10 ([[ foo -eq 10 ]]) || err_exit 'foo -eq 10 fails in [[ ... ]] with foo=10' +# ====== +# The negator should negate the negator +# This bug was shared with bash: +# https://lists.gnu.org/archive/html/bug-bash/2021-06/msg00006.html +[[ ! ! -n x ]] && ! [[ ! ! ! -n x ]] && [[ ! ! ! ! -n x ]] && ! [[ ! ! ! ! ! -n x ]] \ +&& [[ ! ! -n x && ! ! ! ! -n x && ! ! ! ! ! ! -n x ]] \ +|| err_exit '! does not negate ! in [[ ... ]]' +# The bug did not exist in 'test'/'[', but check for it anyway +[ ! ! -n x ] && ! [ ! ! ! -n x ] && [ ! ! ! ! -n x ] && ! [ ! ! ! ! ! -n x ] \ +&& [ ! ! -n x -a ! ! ! ! -n x -a ! ! ! ! ! ! -n x ] \ +|| err_exit '! does not negate ! in [ ... ]' + # ====== exit $((Errors<125?Errors:125))