1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-24 23:14:14 +00:00
cde/src/cmd/ksh93/tests/basic.sh
Martijn Dekker cefe087d23 Fix argv rewrite on invoking hashbangless script (rhbz#1047506)
The fixargs() function is invoked when ksh needs to run a script
without a #!/hashbang/path. Instead of letting the kernel invoke a
shell, ksh exfile()s the script itself from sh_main(). In the
forked child, it calls fixargs() to set the argument list in the
environment to the args of the new script, so that 'ps' and
/proc/PID/cmdline show the expected output.

But fixargs() is broken because, on systems other than HP-UX (on
which ksh uses pstat(2)), ksh simply inserts a terminating zero.
The arguments list is not a zero-terminated C string. Unix systems
expect the entire arguments buffer to be zeroed out, otherwise 'ps'
and /proc/*/cmdline will have fragments of previous command lines
in the output.

The Red Hat patch for this bug is:
https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20120801-argvfix.patch

However, that fix is incomplete because 'command_len' was also
hardcoded to be limited to 64 characters (!), which still gave
invalid 'ps' output if the erased command line was longer.

src/cmd/ksh93/sh/main.c: fixargs():

- Remove CMD_LENGTH macro which was defined as 64.

- Remove code that limited the erasure of the arguments buffer to
  CMD_LENGTH characters. That code also had quite a dodgy strdup()
  call -- it copies arguments to the heap, but they are never freed
  (or even used), so it's a memory leak. Also, none of this is
  ever done if the length is calculated using pstat(2) on HP-UX,
  which is a clear indication that it's unnecessary.
  (I think this code block must have been some experiment they
  forgot to remove. One reason why I think so is that a 64 byte
  arguments limit never made sense, even in the 1980s when they
  wrote ksh on 80-column CRT displays. Another indication of this
  is that fixing it didn't require adding anything; the code to do
  the right thing was already there, it was just being overridden.)

- Zero out the full arguments length as in the Red Hat patch.

src/cmd/ksh93/tests/basic.sh:

- Add test. It's sort of involved because 'ps' is one of the least
  portable commands in practice, in spite of standardisation.
2020-09-25 15:02:51 +02:00

720 lines
22 KiB
Bash
Executable file

########################################################################
# #
# This software is part of the ast package #
# Copyright (c) 1982-2012 AT&T Intellectual Property #
# and is licensed under the #
# Eclipse Public License, Version 1.0 #
# by AT&T Intellectual Property #
# #
# A copy of the License is available at #
# http://www.eclipse.org/org/documents/epl-v10.html #
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
# #
# Information and Software Systems Research #
# AT&T Research #
# Florham Park NJ #
# #
# David Korn <dgk@research.att.com> #
# #
########################################################################
function err_exit
{
print -u2 -n "\t"
print -u2 -r ${Command}[$1]: "${@:2}"
let Errors+=1
}
alias err_exit='err_exit $LINENO'
Command=${0##*/}
integer Errors=0
[[ -d $tmp && -w $tmp && $tmp == "$PWD" ]] || { err\_exit "$LINENO" '$tmp not set; run this from shtests. Aborting.'; exit 1; }
bincat=$(whence -p cat)
binecho=$(whence -p echo)
binfalse=$(whence -p false)
# make an external 'sleep' command that supports fractional seconds
binsleep=$tmp/.sleep.sh # hide to exclude from simple wildcard expansion
cat >"$binsleep" <<EOF
#!$SHELL
sleep "\$@"
EOF
chmod +x "$binsleep"
# test basic file operations like redirection, pipes, file expansion
set -- \
go+r 0000 \
go-r 0044 \
ug=r 0330 \
go+w 0000 \
go-w 0022 \
ug=w 0550 \
go+x 0000 \
go-x 0011 \
ug=x 0660 \
go-rx 0055 \
uo-wx 0303 \
ug-rw 0660 \
o= 0007
while (( $# >= 2 ))
do umask 0
umask $1
g=$(umask)
[[ $g == $2 ]] || err_exit "umask 0; umask $1 failed -- expected $2, got $g"
shift 2
done
umask u=rwx,go=rx || err_exit "umask u=rws,go=rx failed"
if [[ $(umask -S) != u=rwx,g=rx,o=rx ]]
then err_exit 'umask -S incorrect'
fi
um=$(umask -S)
( umask 0777; > foobar )
rm -f foobar
> foobar
[[ -r foobar ]] || err_exit 'umask not being restored after subshell'
umask "$um"
rm -f foobar
# optimizer bug test
> foobar
for i in 1 2
do print foobar*
rm -f foobar
done > out
if [[ "$(<out)" != "foobar"$'\n'"foobar*" ]]
then print -u2 "optimizer bug with file expansion"
fi
rm -f out foobar
mkdir dir
if [[ $(print */) != dir/ ]]
then err_exit 'file expansion with trailing / not working'
fi
if [[ $(print *) != dir ]]
then err_exit 'file expansion with single file not working'
fi
print hi > .foo
if [[ $(print *) != dir ]]
then err_exit 'file expansion leading . not working'
fi
date > dat1 || err_exit "date > dat1 failed"
test -r dat1 || err_exit "dat1 is not readable"
x=dat1
cat <$x > dat2 || err_exit "cat < $x > dat2 failed"
cat dat1 dat2 | cat | cat | cat > dat3 || err_exit "cat pipe failed"
cat > dat4 <<!
$(date)
!
cat dat1 dat2 | cat | cat | cat > dat5 &
wait $!
set -- dat*
if (( $# != 5 ))
then err_exit "dat* matches only $# files"
fi
if (command > foo\\abc) 2> /dev/null
then set -- foo*
if [[ $1 != 'foo\abc' ]]
then err_exit 'foo* does not match foo\abc'
fi
fi
if ( : > TT* && : > TTfoo ) 2>/dev/null
then set -- TT*
if (( $# < 2 ))
then err_exit 'TT* not expanding when file TT* exists'
fi
fi
cd ~- || err_exit "cd back failed"
cat > $tmp/script <<- !
#! $SHELL
print -r -- \$0
!
chmod 755 $tmp/script
if [[ $($tmp/script) != "$tmp/script" ]]
then err_exit '$0 not correct for #! script'
fi
bar=foo
eval foo=\$bar
if [[ $foo != foo ]]
then err_exit 'eval foo=\$bar not working'
fi
bar='foo=foo\ bar'
eval $bar
if [[ $foo != 'foo bar' ]]
then err_exit 'eval foo=\$bar, with bar="foo\ bar" not working'
fi
cd /tmp
cd ../../tmp || err_exit "cd ../../tmp failed"
if [[ $PWD != /tmp ]]
then err_exit 'cd ../../tmp is not /tmp'
fi
( sleep .2; cat <<!
foobar
!
) | cat > $tmp/foobar &
wait $!
foobar=$( < $tmp/foobar)
if [[ $foobar != foobar ]]
then err_exit "$foobar is not foobar"
fi
{
print foo
"$binecho" bar
print bam
} > $tmp/foobar
if [[ $( < $tmp/foobar) != $'foo\nbar\nbam' ]]
then err_exit "output file pointer not shared correctly"
fi
cat > $tmp/foobar <<\!
print foo
"$binecho" bar
print bam
!
chmod +x $tmp/foobar
if [[ $(export binecho; $tmp/foobar) != $'foo\nbar\nbam' ]]
then err_exit "script not working"
fi
if [[ $(export binecho; $tmp/foobar | "$bincat") != $'foo\nbar\nbam' ]]
then err_exit "script | cat not working"
fi
if [[ $(export binecho; $tmp/foobar) != $'foo\nbar\nbam' ]]
then err_exit "output file pointer not shared correctly"
fi
rm -f $tmp/foobar
x=$( (print foo) ; (print bar) )
if [[ $x != $'foo\nbar' ]]
then err_exit " ( (print foo);(print bar ) failed"
fi
x=$( ("$binecho" foo) ; (print bar) )
if [[ $x != $'foo\nbar' ]]
then err_exit " ( ("$binecho");(print bar ) failed"
fi
x=$( ("$binecho" foo) ; ("$binecho" bar) )
if [[ $x != $'foo\nbar' ]]
then err_exit " ( ("$binecho");("$binecho" bar ) failed"
fi
cat > $tmp/script <<\!
if [[ -p /dev/fd/0 ]]
then builtin cat
cat - > /dev/null
[[ -p /dev/fd/0 ]] && print ok
else print no
fi
!
chmod +x $tmp/script
case $( (print) | $tmp/script;:) in
ok) ;;
no) err_exit "[[ -p /dev/fd/0 ]] fails for standard input pipe" ;;
*) err_exit "builtin replaces standard input pipe" ;;
esac
print 'print $0' > $tmp/script
print ". $tmp/script" > $tmp/scriptx
chmod +x $tmp/scriptx
if [[ $($tmp/scriptx) != $tmp/scriptx ]]
then err_exit '$0 not correct for . script'
fi
cd $tmp || { err_exit "cd $tmp failed"; exit 1; }
print ./b > ./a; print ./c > b; print ./d > c; print ./e > d; print "echo \"hello there\"" > e
chmod 755 a b c d e
x=$(./a)
if [[ $x != "hello there" ]]
then err_exit "nested scripts failed"
fi
x=$( (./a) | cat)
if [[ $x != "hello there" ]]
then err_exit "scripts in subshells fail"
fi
cd ~- || err_exit "cd back failed"
x=$( ("$binecho" foo) 2> /dev/null )
if [[ $x != foo ]]
then err_exit "subshell in command substitution fails"
fi
exec 9>& 1
exec 1>&-
x=$(print hello)
if [[ $x != hello ]]
then err_exit "command substitution with stdout closed failed"
fi
exec >& 9
x=$(export binecho bincat; cat <<\! | $SHELL
"$binecho" | "$bincat"
"$binecho" hello
!
)
if [[ $x != $'\n'hello ]]
then err_exit "$SHELL not working when standard input is a pipe"
fi
x=$( ("$binecho" hello) 2> /dev/null )
if [[ $x != hello ]]
then err_exit "subshell in command substitution with 1 closed fails"
fi
cat > $tmp/script <<- \!
read line 2> /dev/null
print done
!
if [[ $($SHELL $tmp/script <&-) != done ]]
then err_exit "executing script with 0 closed fails"
fi
trap '' INT
cat > $tmp/script <<- \!
trap 'print bad' INT
kill -s INT $$
print good
!
chmod +x $tmp/script
if [[ $($SHELL $tmp/script) != good ]]
then err_exit "traps ignored by parent not ignored"
fi
trap - INT
cat > $tmp/script <<- \!
read line
"$bincat"
!
if [[ $(export bincat; $SHELL $tmp/script <<!
one
two
!
) != two ]]
then err_exit "standard input not positioned correctly"
fi
word=$(print $'foo\nbar' | { read line; "$bincat";})
if [[ $word != bar ]]
then err_exit "pipe to { read line; $bincat;} not working"
fi
word=$(print $'foo\nbar' | ( read line; "$bincat") )
if [[ $word != bar ]]
then err_exit "pipe to ( read line; $bincat) not working"
fi
if [[ $(print x{a,b}y) != 'xay xby' ]]
then err_exit 'brace expansion not working'
fi
if [[ $(for i in foo bar
do ( tgz=$(print $i)
print $tgz)
done) != $'foo\nbar' ]]
then err_exit 'for loop subshell optimizer bug'
fi
unset a1
function optbug
{
set -A a1 foo bar bam
integer i
for ((i=0; i < 3; i++))
do
(( ${#a1[@]} < 2 )) && return 0
set -- "${a1[@]}"
shift
set -A a1 -- "$@"
done
return 1
}
optbug || err_exit 'array size optimization bug'
wait # not running --pipefail which would interfere with subsequent tests
: $(jobs -p) # required to clear jobs for next jobs -p (interactive side effect)
sleep 2 &
pids=$!
if [[ $(jobs -p) != $! ]]
then err_exit 'jobs -p not reporting a background job'
fi
sleep 2 &
pids="$pids $!"
foo()
{
set -- $(jobs -p)
(( $# == 2 )) || err_exit "$# jobs not reported -- 2 expected"
}
foo
kill $pids
[[ $( (trap 'print alarm' ALRM; sleep .4) & sleep .2; kill -ALRM $!; sleep .2; wait) == alarm ]] || err_exit 'ALRM signal not working'
[[ $($SHELL -c 'trap "" HUP; $SHELL -c "(sleep .2;kill -HUP $$)& sleep .4;print done"') != done ]] && err_exit 'ignored traps not being ignored'
[[ $($SHELL -c 'o=foobar; for x in foo bar; do (o=save);print $o;done' 2> /dev/null ) == $'foobar\nfoobar' ]] || err_exit 'for loop optimization subshell bug'
command exec 3<> /dev/null
if cat /dev/fd/3 >/dev/null 2>&1 || whence mkfifo > /dev/null
then [[ $($SHELL -c 'cat <(print foo)' 2> /dev/null) == foo ]] || err_exit 'process substitution not working'
[[ $($SHELL -c $'tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<- \!!!
line0
line1
line2
!!!
wait
cat '$tmp/scriptx 2> /dev/null) == line1 ]] || err_exit '>() process substitution fails'
> $tmp/scriptx
[[ $($SHELL -c $'
for i in 1
do tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<- \!!!
line0
line1
line2
!!!
done
wait
cat '$tmp/scriptx 2>> /dev/null) == line1 ]] || err_exit '>() process substitution fails in for loop'
[[ $({ $SHELL -c 'cat <(for i in x y z; do print $i; done)';} 2> /dev/null) == $'x\ny\nz' ]] ||
err_exit 'process substitution of compound commands not working'
fi
[[ $($SHELL -r 'command -p :' 2>&1) == *restricted* ]] || err_exit 'command -p not restricted'
print cat > $tmp/scriptx
chmod +x $tmp/scriptx
[[ $($SHELL -c "print foo | $tmp/scriptx ;:" 2> /dev/null ) == foo ]] || err_exit 'piping into script fails'
[[ $($SHELL -c 'X=1;print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 1 ]] || err_exit 'x=1;${x:=$(..."...")} failure'
[[ $($SHELL -c 'print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 0 ]] || err_exit '${x:=$(..."...")} failure'
if cat /dev/fd/3 >/dev/null 2>&1 || whence mkfifo > /dev/null
then [[ $(cat <(print hello) ) == hello ]] || err_exit "process substitution not working outside for or while loop"
$SHELL -c '[[ $(for i in 1;do cat <(print hello);done ) == hello ]]' 2> /dev/null|| err_exit "process substitution not working in for or while loop"
fi
exec 3> /dev/null
print 'print foo "$@"' > $tmp/scriptx
[[ $( print "($tmp/scriptx bar)" | $SHELL 2>/dev/null) == 'foo bar' ]] || err_exit 'script pipe to shell fails'
print "#! $SHELL" > $tmp/scriptx
print 'print -- $0' >> $tmp/scriptx
chmod +x $tmp/scriptx
[[ $($tmp/scriptx) == $tmp/scriptx ]] || err_exit "\$0 is $0 instead of $tmp/scriptx"
cat > $tmp/scriptx <<- \EOF
myfilter() { x=$(print ok | cat); print -r -- $SECONDS;}
set -o pipefail
sleep .3 | myfilter
EOF
(( $($SHELL $tmp/scriptx) > .2 )) && err_exit 'command substitution causes pipefail option to hang'
exec 3<&-
( typeset -r foo=bar) 2> /dev/null || err_exit 'readonly variables set in a subshell cannot unset'
$SHELL -c 'x=${ print hello;}; [[ $x == hello ]]' 2> /dev/null || err_exit '${ command;} not supported'
$SHELL 2> /dev/null <<- \EOF || err_exit 'multiline ${...} command substitution not supported'
x=${
print hello
}
[[ $x == hello ]]
EOF
$SHELL 2> /dev/null <<- \EOF || err_exit '${...} command substitution with side effects not supported '
y=bye
x=${
y=hello
print hello
}
[[ $y == $x ]]
EOF
$SHELL 2> /dev/null <<- \EOF || err_exit 'nested ${...} command substitution not supported'
x=${
print ${ print hello;} $(print world)
}
[[ $x == 'hello world' ]]
EOF
$SHELL 2> /dev/null <<- \EOF || err_exit 'terminating } is not a reserved word with ${ command }'
x=${ { print -n } ; print -n hello ; } ; print ' world' }
[[ $x == '}hello world' ]]
EOF
$SHELL 2> /dev/null <<- \EOF || err_exit '${ command;}xxx not working'
f()
{
print foo
}
[[ ${ f;}bar == foobar ]]
EOF
unset foo
[[ ! ${foo[@]} ]] || err_exit '${foo[@]} is not empty when foo is unset'
[[ ! ${foo[3]} ]] || err_exit '${foo[3]} is not empty when foo is unset'
[[ $(print "[${ print foo }]") == '[foo]' ]] || err_exit '${...} not working when } is followed by ]'
[[ $(print "${ print "[${ print foo }]" }") == '[foo]' ]] || err_exit 'nested ${...} not working when } is followed by ]'
unset foo
foo=$(false) > /dev/null && err_exit 'failed command substitution with redirection not returning false'
expected=foreback
got=$(print -n fore; (sleep .2;print back)&)
[[ $got == $expected ]] || err_exit "command substitution background process output error -- got '$got', expected '$expected'"
for false in false $binfalse
do x=$($false) && err_exit "x=\$($false) should fail"
$($false) && err_exit "\$($false) should fail"
$($false) > /dev/null && err_exit "\$($false) > /dev/null should fail"
done
if env x-a=y >/dev/null 2>&1
then [[ $(env 'x-a=y' $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved'
fi
foo() { return; }
false
foo && err_exit "'return' within function does not preserve exit status"
false
foo & wait "$!" && err_exit "'return' within function does not preserve exit status (bg job)"
foo() { false; return || :; }
foo && err_exit "'return ||' does not preserve exit status"
foo() { false; return && :; }
foo && err_exit "'return &&' does not preserve exit status"
foo() { false; while return; do true; done; }
foo && err_exit "'while return' does not preserve exit status"
foo() { false; while return; do true; done 2>&1; }
foo && err_exit "'while return' with redirection does not preserve exit status"
foo() { false; until return; do true; done; }
foo && err_exit "'until return' does not preserve exit status"
foo() { false; until return; do true; done 2>&1; }
foo && err_exit "'until return' with redirection does not preserve exit status"
foo() { false; for i in 1; do return; done; }
foo && err_exit "'return' within 'for' does not preserve exit status"
foo() { false; for i in 1; do return; done 2>&1; }
foo && err_exit "'return' within 'for' with redirection does not preserve exit status"
foo() { false; { return; } 2>&1; }
foo && err_exit "'return' within { block; } with redirection does not preserve exit status"
foo() ( exit )
false
foo && err_exit "'exit' within function does not preserve exit status"
false
foo & wait "$!" && err_exit "'exit' within function does not preserve exit status (bg job)"
foo() ( false; exit || : )
foo && err_exit "'exit ||' does not preserve exit status"
foo() ( false; exit && : )
foo && err_exit "'exit &&' does not preserve exit status"
foo() ( false; while exit; do true; done )
foo && err_exit "'while exit' does not preserve exit status"
foo() ( false; while exit; do true; done 2>&1 )
foo && err_exit "'while exit' with redirection does not preserve exit status"
foo() ( false; until exit; do true; done )
foo && err_exit "'until exit' does not preserve exit status"
foo() ( false; until exit; do true; done 2>&1 )
foo && err_exit "'until exit' with redirection does not preserve exit status"
foo() ( false; for i in 1; do exit; done )
foo && err_exit "'exit' within 'for' does not preserve exit status"
foo() ( false; for i in 1; do exit; done 2>&1 )
foo && err_exit "'exit' within 'for' with redirection does not preserve exit status"
foo() ( false; { exit; } 2>&1 )
foo && err_exit "'exit' within { block; } with redirection does not preserve exit status"
set --
false
for i in "$@"; do :; done || err_exit 'empty loop does not reset exit status ("$@")'
false
for i do :; done || err_exit 'empty loop does not reset exit status ("$@" shorthand)'
false
for i in; do :; done || err_exit "empty loop does not reset exit status (empty 'in' list)"
false
case 1 in 1) ;; esac || err_exit "empty case does not reset exit status"
false
case 1 in 2) echo whoa ;; esac || err_exit "non-matching case does not reset exit status"
false
2>&1 || err_exit "lone redirection does not reset exit status"
float s=SECONDS
for i in .1 .2
do print $i
done | while read sec; do ( "$binsleep" "$sec"; "$binsleep" "$sec") done
(( (SECONDS-s) < .4)) && err_exit '"command | while read...done" finishing too fast'
s=SECONDS
set -o pipefail
for ((i=0; i < 30; i++))
do print hello
sleep .01
done | "$binsleep" .1
(( (SECONDS-s) < .2 )) || err_exit 'early termination not causing broken pipe'
[[ $({ trap 'print trap' 0; print -n | $(whence -p cat); } & wait $!) == trap ]] || err_exit 'trap on exit not getting triggered'
var=$({ trap 'print trap' ERR; print -n | $binfalse; } & wait $!)
[[ $var == trap ]] || err_exit 'trap on ERR not getting triggered'
exp=
got=$(
function fun
{
$binfalse && echo FAILED
}
: works if this line deleted : |
fun
: works if this line deleted :
)
[[ $got == $exp ]] || err_exit "pipe to function with conditional fails -- expected '$exp', got '$got'"
got=$(
: works if this line deleted : |
{ $binfalse && echo FAILED; }
: works if this line deleted :
)
[[ $got == $exp ]] || err_exit "pipe to { ... } with conditional fails -- expected '$exp', got '$got'"
got=$(
: works if this line deleted : |
( $binfalse && echo FAILED )
: works if this line deleted :
)
[[ $got == $exp ]] || err_exit "pipe to ( ... ) with conditional fails -- expected '$exp', got '$got'"
( $SHELL -c 'trap : DEBUG; x=( $foo); exit 0') 2> /dev/null || err_exit 'trap DEBUG fails'
bintrue=$(whence -p true)
set -o pipefail
float start=$SECONDS end
for ((i=0; i < 2; i++))
do print foo
sleep .15
done | { read; $bintrue; end=$SECONDS ;}
(( (SECONDS-start) < .1 )) && err_exit "pipefail not waiting for pipe to finish"
set +o pipefail
(( (SECONDS-end) > .2 )) && err_exit "pipefail causing $bintrue to wait for other end of pipe"
{ env A__z=C+SHLVL $SHELL -c : ;} 2> /dev/null || err_exit "SHLVL with wrong attribute fails"
if [[ $bintrue ]]
then float t0=SECONDS
{ time sleep .15 | $bintrue ;} 2> /dev/null
(( (SECONDS-t0) < .1 )) && err_exit 'time not waiting for pipeline to complete'
fi
cat > $tmp/foo.sh <<- \EOF
eval "cat > /dev/null < /dev/null"
sleep .1
EOF
float sec=SECONDS
. $tmp/foo.sh | cat > /dev/null
(( (SECONDS-sec) < .07 )) && err_exit '. script does not restore output redirection with eval'
file=$tmp/foobar
builtin cat
for ((n=0; n < 1000; n++))
do
> $file
{ sleep .001;echo $? >$file;} | cat > /dev/null
if [[ ! -s $file ]]
then err_exit 'output from pipe is lost with pipe to builtin'
break;
fi
done
$SHELL -c 'kill -0 123456789123456789123456789' 2> /dev/null && err_exit 'kill not catching process id overflows'
[[ $($SHELL -c '{ cd..; print ok;}' 2> /dev/null) == ok ]] || err_exit 'command name ending in .. causes shell to abort'
$SHELL -xc '$(LD_LIBRARY_PATH=$LD_LIBRARY_PATH exec $SHELL -c :)' > /dev/null 2>&1 || err_exit "ksh -xc '(name=value exec ksh)' fails with err=$?"
$SHELL 2> /dev/null -c $'for i;\ndo :;done' || err_exit 'for i ; <newline> not vaid'
# ======
# Crash on syntax error when dotting/sourcing multiple files
# Ref.: https://www.mail-archive.com/ast-developers@lists.research.att.com/msg01943.html
(
mkdir "$tmp/dotcrash" || exit
cd "$tmp/dotcrash" || exit
cat >functions.ksh <<-EOF
function f1
{
echo "f1"
}
function f2
{
if [[ $1 -eq 1 ]]: # deliberate syntax error
then echo "f2"
fi
}
EOF
cat >sub1.ksh <<-EOF
. ./functions.ksh
echo "sub1" >tmp.out
EOF
cat >main.ksh <<-EOF
. ./sub1.ksh
EOF
"$SHELL" main.ksh 2>/dev/null
) || err_exit "crash when sourcing multiple files (exit status $?)"
# ======
# The time keyword
# A single '%' after a format specifier should not be a syntax
# error (it should be treated as a literal '%').
expect='0%'
actual=$(
TIMEFORMAT=$'%0S%'
redirect 2>&1
set +x
time :
)
[[ $actual == "$expect" ]] || err_exit "'%' is not treated literally when placed after a format specifier" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# The locale's radix point shouldn't be ignored
us=$(
LC_ALL='C.UTF-8' # radix point '.'
TIMEFORMAT='%1U' # catch -1.99 bug as well by getting user time
redirect 2>&1
set +x
time sleep 0
)
eu=$(
LC_ALL='C_EU.UTF-8' # radix point ','
TIMEFORMAT='%1U'
redirect 2>&1
set +x
time sleep 0
)
[[ ${us:1:1} == ${eu:1:1} ]] && err_exit "The time keyword ignores the locale's radix point (both are ${eu:1:1})"
# ======
# Test for bug in ksh binaries that use posix_spawn() while job control is active.
# See discussion at: https://github.com/ksh93/ksh/issues/79
if test -t 1 2>/dev/null 1>/dev/tty # this test only works if we have a tty
then actual=$(
"$SHELL" -i <<-\EOF 2>/dev/tty
printf '%s\n' 1 2 3 4 5 | while read
do ls /dev/null
done 2>&1
exit # suppress extra newline
EOF
)
expect=$'/dev/null\n/dev/null\n/dev/null\n/dev/null\n/dev/null'
[[ $actual == "$expect" ]] || err_exit 'Race condition while launching external commands' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
fi
# ======
# Expansion of multibyte characters after expansion of single-character names $1..$9, $?, $!, $-, etc.
function exptest
{
print -r "$1テスト"
print -r "$?テスト"
print -r "$#テスト"
}
expect=$'fooテスト\n0テスト\n1テスト'
actual=$(exptest foo)
[[ $actual == "$expect" ]] || err_exit 'Corruption of multibyte char following expansion of single-char name' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# ======
# ksh didn't rewrite argv correctly (rhbz#1047506)
# When running a script without a #! hashbang path, ksh attempts to replace argv with the arguments
# of the script. However, fixargs() didn't wipe out the rest of previous arguments after the last
# \0. This caused an erroneous record in /proc/<PID>/cmdline and the output of the ps command.
if actual=$(UNIX95=1 ps -o args= -p "$$" 2>&1) # UNIX95=1 makes this work on HP-UX
# Some 'ps' implementations add leading and/or trailing whitespace. Remove.
while [[ $actual == [[:space:]]* ]]; do actual=${actual#?}; done
while [[ $actual == *[[:space:]] ]]; do actual=${actual%?}; done
[[ $actual == "$SHELL $0" ]] # this is how shtests invokes this script
then expect='./atest 1 2'
echo 'sleep 10; exit 0' >atest
chmod 755 atest
./atest 1 2 &
actual=$(UNIX95=1 ps -o args= -p "$!")
kill "$!"
while [[ $actual == [[:space:]]* ]]; do actual=${actual#?}; done
while [[ $actual == *[[:space:]] ]]; do actual=${actual%?}; done
[[ $actual == "$expect" ]] || err_exit "ksh didn't rewrite argv correctly" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
else err_exit "warning: skipping argv rewrite test due to noncompliant 'ps' utility (got $(printf %q "$actual"))"
let Errors--
fi
# ======
exit $((Errors<125?Errors:125))