mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-15 04:32:24 +00:00
Fix another test/[ corner case bug; add --posix test script
This fixes another corner case bug in the horror show that is the test/[ comand. Reproducer: $ ksh --posix -c 'test X -a -n' ksh: test: argument expected Every other shell returns 0 (success) as, POSIXly, this is a test for the strings 'X' and '-n' both being non-empty, combined with the binary -a (logical and) operator. Instead, '-n' was taken as a unary primary operator with a missing argument, which is incorrect. POSIX reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html > 3 arguments: > * If $2 is a binary primary, perform the binary test of $1 and $3. src/cmd/ksh93/bltins/test.c: - e3(): If the final argument begins with a dash, always treat it as a test for a non-empty string, therefore return true. Do not limit this to "new flags" only. src/cmd/ksh93/tests/posix.sh: - Added. These are tests for every aspect of the POSIX mode.
This commit is contained in:
parent
9e2a8c6925
commit
fd28da31da
6 changed files with 232 additions and 34 deletions
4
NEWS
4
NEWS
|
@ -3,6 +3,10 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
|
|||
|
||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||
|
||||
2022-03-10:
|
||||
|
||||
- Fixed another corner case bug in the 'test'/'[' command.
|
||||
|
||||
2022-03-05:
|
||||
|
||||
- The 'enum' command can now create more than one type per invocation.
|
||||
|
|
|
@ -334,14 +334,8 @@ static int e3(struct test *tp)
|
|||
if(*arg=='-' && arg[2]==0)
|
||||
{
|
||||
op = arg[1];
|
||||
if(!cp)
|
||||
{
|
||||
/* for backward compatibility with new flags */
|
||||
if(op==0 || !strchr(test_opchars+10,op))
|
||||
return(1);
|
||||
errormsg(SH_DICT,ERROR_exit(2),e_argument);
|
||||
UNREACHABLE();
|
||||
}
|
||||
if(!cp) /* no further argument: */
|
||||
return(1); /* treat as nonempty string instead of unary op, so return true */
|
||||
if(strchr(test_opchars,op))
|
||||
return(test_unop(op,cp));
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ const Shtable_t shtab_testops[] =
|
|||
};
|
||||
|
||||
const char sh_opttest[] =
|
||||
"[-1c?\n@(#)$Id: test (ksh 93u+m) 2021-11-14 $\n]"
|
||||
"[-1c?\n@(#)$Id: test (ksh 93u+m) 2022-03-10 $\n]"
|
||||
"[--catalog?" SH_DICT "]"
|
||||
"[+NAME?test, [ - evaluate expression]"
|
||||
"[+DESCRIPTION?\btest\b evaluates expressions and returns its result using the "
|
||||
|
|
|
@ -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 "2022-03-05" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_DATE "2022-03-10" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
|
||||
|
||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||
|
|
|
@ -358,19 +358,6 @@ test ! ! '' 2> /dev/null && err_exit 'test ! ! "" should return non-zero'
|
|||
x=10
|
||||
([[ x -eq 10 ]]) 2> /dev/null || err_exit 'x -eq 10 fails in [[...]] with x=10'
|
||||
|
||||
# ======
|
||||
# The POSIX mode should disable the ancient 'test -t' compatibility hack.
|
||||
if [[ -o ?posix ]]
|
||||
then # need 'eval' in first test, as it's a parser hack, and ksh normally parses ahead of execution
|
||||
(set -o posix; eval '[ -t ] && test -t') >/dev/null \
|
||||
|| err_exit "posix mode fails to disable 'test -t' compat hack"
|
||||
# the compound command variant of the hack is in the 'test' builtin itself; no 'eval' needed
|
||||
expect=$'*: argument expected\n*: argument expected'
|
||||
actual=$(set -o posix; [ -n X -a -t ] 2>&1; test -n X -a -t 2>&1)
|
||||
[[ $actual == $expect ]] || err_exit "posix mode fails to disable 'test -t' compat hack (compound expression)" \
|
||||
"(expected output matching $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||
fi
|
||||
|
||||
# ======
|
||||
# POSIX specifies that on error, test builtin should always return status > 1
|
||||
expect=$': test: 123x: arithmetic syntax error\nExit status: 2'
|
||||
|
@ -426,17 +413,6 @@ foo=10
|
|||
&& [ ! ! -n x -a ! ! ! ! -n x -a ! ! ! ! ! ! -n x ] \
|
||||
|| err_exit '! does not negate ! in [ ... ]'
|
||||
|
||||
# ======
|
||||
# https://github.com/ksh93/ksh/issues/330
|
||||
if (set -o posix) 2>/dev/null
|
||||
then set -o posix -o trackall
|
||||
test ! -a "" && err_exit "POSIX test/[: binary -a operator does not work with '!' as left-hand expression"
|
||||
test \( -a \) 2>/dev/null || err_exit "POSIX test/[: binary -a operator does not work with '(' as left-hand expression"
|
||||
test ! -o trackall || err_exit "POSIX test/[: binary -o operator does not work with '!' as left-hand expression"
|
||||
test \( -o \) 2>/dev/null || err_exit "POSIX test/[: binary -o operator does not work with '(' as left-hand expression"
|
||||
set +o posix
|
||||
fi
|
||||
|
||||
# ======
|
||||
# test should support '<' as well as '>'; before 2021-11-13, ksh supported
|
||||
# only '>' due to '<' being missorted in shtab_testops[] in data/testops.c
|
||||
|
|
224
src/cmd/ksh93/tests/posix.sh
Normal file
224
src/cmd/ksh93/tests/posix.sh
Normal file
|
@ -0,0 +1,224 @@
|
|||
########################################################################
|
||||
# #
|
||||
# This file is part of the ksh 93u+m package #
|
||||
# Copyright (c) 2020-2022 Contributors to ksh 93u+m #
|
||||
# <https://github.com/ksh93/ksh> #
|
||||
# and is licensed under the #
|
||||
# Eclipse Public License, Version 1.0 #
|
||||
# #
|
||||
# A copy of the License is available at #
|
||||
# http://www.eclipse.org/org/documents/epl-v10.html #
|
||||
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
|
||||
# #
|
||||
# Martijn Dekker <martijn@inlv.org> #
|
||||
# #
|
||||
########################################################################
|
||||
|
||||
. "${SHTESTS_COMMON:-${0%/*}/_common}"
|
||||
|
||||
# Tests for the --posix compatibility mode. The comments are quotes from the manual page.
|
||||
# Some of these tests need to use 'eval' to avoid the parser parsing ahead and/or to work with shcomp.
|
||||
|
||||
set -o noglob
|
||||
typeset -xi testint=123
|
||||
|
||||
if ! (set -o posix) 2>/dev/null
|
||||
then
|
||||
warning "$SHELL does not have a posix option -- skipping tests"
|
||||
exit 0
|
||||
fi
|
||||
command set --noposix 2>/dev/null && [[ ! -o posix ]] || err_exit "set --noposix does not work"
|
||||
command set --posix 2>/dev/null && [[ -o posix ]] || err_exit "set --posix does not work"
|
||||
((Errors)) && exit "$Errors" # no sense in continuing if the above fail
|
||||
|
||||
# The --posix option enables the POSIX standard mode for maximum compatibility with other compliant shells. At the
|
||||
# moment that the posix option is turned on, it also turns on letoctal and turns off -B/braceexpand; the reverse
|
||||
# is done when posix is turned back off. (These options can still be controlled independently in between.)
|
||||
let "017 == 15" || err_exit "--posix does not turn on letoctal"
|
||||
(set --noletoctal; let "017 == 17") || err_exit "--posix does not allow independent control of letoctal"
|
||||
(set --noposix; let "017 == 17") || err_exit "--noposix does not turn off letoctal"
|
||||
(set --noposix; set --letoctal; let "017 == 15") || err_exit "--noposix does not allow independent control of letoctal"
|
||||
(set {1..4}; let "$# == 1") || err_exit "--posix does not turn off braceexpand"
|
||||
if ((SHOPT_BRACEPAT)); then
|
||||
(set -B; eval 'set {1..4}'; let "$# == 4") || err_exit "--posix does not allow independent control of braceexpand"
|
||||
(set --noposix; eval 'set {1..4}'; let "$# == 4") || err_exit "--noposix does not turn on braceexpand"
|
||||
fi
|
||||
(set --noposix; ((SHOPT_BRACEPAT)) && set +B; eval 'set {1..4}'; let "$# == 1") || err_exit "--noposix does not allow independent control of braceexpand"
|
||||
|
||||
# Furthermore, the posix option is automatically turned on upon invocation if the shell is invoked as sh or rsh,
|
||||
# or if -o posix or --posix is specified on the shell invocation command line, or when executing scripts
|
||||
# without a #! path with this option active in the invoking shell.
|
||||
# In that case, the invoked shell will not set the preset aliases even if interactive, and will not import
|
||||
# type attributes for variables (such as integer or left/right justify) from the environment.
|
||||
set --noposix
|
||||
ln -s "$SHELL" sh
|
||||
ln -s "$SHELL" rsh
|
||||
script=$'[[ -o posix ]]\necho $?\ntypeset -p testint'
|
||||
exp=$'0\ntypeset -x testint=123'
|
||||
got=$(./sh -c "$script")
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invocation as sh" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(./rsh -c "$script")
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invocation as rsh" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
echo "$script" >script
|
||||
chmod +x script
|
||||
set --posix
|
||||
./script >|out
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (direct)" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
: <<\DISABLED # TODO: these do not pass yet, unless SHOPT_SPAWN is disabled.
|
||||
(./script) >|out
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (subshell)" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(./script)
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from posix shell (comsub)" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
DISABLED
|
||||
|
||||
got=$("$SHELL" --posix -c "$(<script)")
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking -c script from posix shell" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
set --noposix
|
||||
exp=$'1\ntypeset -x -i testint=123'
|
||||
got=$(./script)
|
||||
[[ $got == "$exp" ]] || err_exit "incorrect --posix settings on invoking hashbangless script from noposix shell" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
set --posix
|
||||
|
||||
# In addition, while on, the posix option:
|
||||
#
|
||||
# disables exporting variable type attributes to the environment for other ksh processes to import;
|
||||
exp='typeset -x testint=123'
|
||||
got=$("$SHELL" -c 'typeset -p testint')
|
||||
[[ $got == "$exp" ]] || err_exit "variable attributes incorrectly exported in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
set --noposix
|
||||
exp='typeset -x -i testint=123'
|
||||
got=$("$SHELL" -c 'typeset -p testint')
|
||||
[[ $got == "$exp" ]] || err_exit "variable attributes not exported in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
set --posix
|
||||
|
||||
# disables the special handling of repeated isspace class characters in the IFS variable;
|
||||
IFS=$'x\t\ty' val=$'\tun\t\tduo\ttres\t'
|
||||
got=$(set $val; echo "$#")
|
||||
exp=3
|
||||
[[ $got == "$exp" ]] || err_exit "repeated IFS whitespace char (posix): incorrect number of fields" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(set --noposix; set $val; echo "$#")
|
||||
exp=5
|
||||
[[ $got == "$exp" ]] || err_exit "repeated IFS whitespace char (noposix): incorrect number of fields" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
IFS=$' \t\n' # default
|
||||
|
||||
# causes file descriptors > 2 to be left open when invoking another program;
|
||||
exp='ok'
|
||||
got=$(redirect 3>&1; "$SHELL" -c 'echo ok >&3' 2>/dev/null)
|
||||
[[ $got == "$exp" ]] || err_exit "file descriptor 3 not left open in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
exp=''
|
||||
got=$(set --noposix; redirect 3>&1; "$SHELL" -c 'echo ok >&3' 2>/dev/null)
|
||||
[[ $got == "$exp" ]] || err_exit "file descriptor 3 left open in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# disables the &> redirection shorthand;
|
||||
exp=''
|
||||
(set --posix; eval 'echo output &>out') >/dev/null
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "&> redirection shorthand not disabled in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
(set --noposix; eval 'echo output &>out') >/dev/null
|
||||
exp='output'
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "&> redirection shorthand disabled in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# disables fast filescan loops of type 'while inputredirection; do list; done';
|
||||
printf '%s\n' "un duo tres" >out
|
||||
set --
|
||||
exp='0'
|
||||
got=$(while <out; do echo "$#" "$@"; break; done)
|
||||
[[ $got == "$exp" ]] || err_exit "filescan loop not disabled in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
if ((SHOPT_FILESCAN))
|
||||
then
|
||||
exp='3 un duo tres'
|
||||
got=$(set --noposix; while <out; do echo "$#" "$@"; break; done)
|
||||
[[ $got == "$exp" ]] || err_exit "filescan loop disabled in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
fi
|
||||
|
||||
# makes the <> redirection operator default to redirecting standard input if no file descriptor number precedes it;
|
||||
: >|out
|
||||
(set --posix; eval 'echo foo <>out') >/dev/null
|
||||
exp=''
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "<> redirects standard output in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
: >|out
|
||||
(set --noposix; eval 'echo foo <>out') >/dev/null
|
||||
exp='foo'
|
||||
got=$(<out)
|
||||
[[ $got == "$exp" ]] || err_exit "<> does not redirect standard output in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# disables the special floating point constants Inf and NaN in arithmetic evaluation so that, e.g., $((inf))
|
||||
# and $((nan)) refer to the variables by those names;
|
||||
inf=1 nan=2
|
||||
let "inf==1 && nan==2" || err_exit "inf and nan are not variables in arithmetic expressions in --posix mode"
|
||||
(set --noposix; let "inf==1 || nan==2") && err_exit "inf and nan are variables in arithmetic expressions in --noposix mode"
|
||||
|
||||
# enables the recognition of a leading zero as introducing an octal number in all arithmetic evaluation
|
||||
# contexts, except in the let built-in while letoctal is off;
|
||||
let "017 == 15" || err_exit "leading octal zero not recognised in 'let' in --posix"
|
||||
(set --noletoctal; let "017 == 17") || err_exit "leading octal zero erroneously recognised in --posix --noletoctal"
|
||||
(set --noposix; let "017 == 17") || err_exit "leading octal zero erroneously recognised in --noposix"
|
||||
(set --noposix --letoctal; let "017 == 15") || err_exit "leading octal zero not recognised in --noposix --letoctal (1)"
|
||||
(set --noposix; set --letoctal; let "017 == 15") || err_exit "leading octal zero not recognised in --noposix --letoctal (2)"
|
||||
|
||||
# stops the . command (but not source) from looking up functions defined with the function syntax;
|
||||
echo 'echo SCRIPT' >scrunction
|
||||
function scrunction { echo FUNCTION; }
|
||||
got=$(PATH=.:$PATH; . scrunction)
|
||||
exp='SCRIPT'
|
||||
[[ $got == "$exp" ]] || err_exit "'.' finds ksh function in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(set --noposix; PATH=.:$PATH; . scrunction)
|
||||
exp='FUNCTION'
|
||||
[[ $got == "$exp" ]] || err_exit "'.' does not find ksh function in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(PATH=.:$PATH; source scrunction)
|
||||
[[ $got == "$exp" ]] || err_exit "'source' does not find ksh function in --posix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
got=$(set --noposix; PATH=.:$PATH; source scrunction)
|
||||
[[ $got == "$exp" ]] || err_exit "'source' does not find ksh function in --noposix mode" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# changes the test/[ built-in command to make its deprecated expr1 -a expr2 and expr1 -o expr2 operators work
|
||||
# even if expr1 equals "!" or "(" (which means the nonstandard unary -a file and -o option operators cannot
|
||||
# be directly negated using ! or wrapped in parentheses);
|
||||
# https://github.com/ksh93/ksh/issues/330
|
||||
test ! -a "" && err_exit "POSIX test/[: binary -a operator does not work with '!' as left-hand expression"
|
||||
test \( -a \) 2>/dev/null || err_exit "POSIX test/[: binary -a operator does not work with '(' as left-hand expression"
|
||||
(set --trackall; test ! -o trackall) || err_exit "POSIX test/[: binary -o operator does not work with '!' as left-hand expression"
|
||||
(set --noposix --trackall; test ! -o trackall) && err_exit "ksh test/[: unary -o operator does not work with '!' negator"
|
||||
test \( -o \) 2>/dev/null || err_exit "POSIX test/[: binary -o operator does not work with '(' as left-hand expression"
|
||||
|
||||
# disables a hack that makes test -t ([ -t ]) equivalent to test -t 1 ([ -t 1 ]).
|
||||
# ...simple 'test -t' is hacked in the parser (so we need 'eval')...
|
||||
eval 'test -t' >/dev/null 2>&1 || err_exit "'test -t' does not test for nonemptiness of string '-t' in --posix mode"
|
||||
eval '[ -t ]' >/dev/null 2>&1 || err_exit "'[ -t ]' does not test for nonemptiness of string '-t' in --posix mode"
|
||||
(set --noposix; eval 'test -t') >/dev/null 2>&1 && err_exit "'test -t' is not equivalent to 'test -t 1' in --noposix mode"
|
||||
(set --noposix; eval '[ -t ]') >/dev/null 2>&1 && err_exit "'[ -t ]' is not equivalent to '[ -t 1 ]' in --noposix mode"
|
||||
# ...whereas complex expressions with bare '-t' are hacked in the test builtin itself
|
||||
test X -a -t >/dev/null 2>&1 || err_exit "'test X -a -t' does not test for nonemptiness of string '-t' in --posix mode"
|
||||
[ X -a -t ] >/dev/null 2>&1 || err_exit "'[ X -a -t ]' does not test for nonemptiness of string '-t' in --posix mode"
|
||||
(set --noposix; test X -a -t) >/dev/null 2>&1 && err_exit "'test X -a -t' is not equivalent to 'test X -a -t 1' in --noposix mode"
|
||||
(set --noposix; [ X -a -t ]) >/dev/null 2>&1 && err_exit "'[ X -a -t ]' is not equivalent to '[ X -a -t 1 ]' in --noposix mode"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
Loading…
Reference in a new issue