#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
#
# Written by Roland Mainz <roland.mainz@nrubsig.org>
#
# test setup
function err_exit
{
print -u2 -n "\t"
}
alias err_exit='err_exit $LINENO'
set -o nounset
integer Errors=0
function isvalidpid
{
return 1
}
integer testfilesize i maxwait
typeset tmpfile
integer testid
########################################################################
#### test set 001:
# run loop and check various temp filesizes
# (Please keep this test syncted with sun_solaris_cr_6800929_large_command_substitution_hang.sh)
# test 1: run loop and check various temp filesizes
tmpfile="$(mktemp -t "ksh93_tests_command_substitution.${PPID}.$$.XXXXXX")" || err_exit "Cannot create temporary file."
compound test1=(
compound -a testcases=(
# test 1a: Run test child for $(...)
# (note the pipe chain has to end in a builtin command, an external command may not trigger the bug)
# test 1b: Same as test1a but uses ${... ; } instead if $(...)
# test 1c: Same as test1a but does not use a pipe
# test 1d: Same as test1a but does not use a pipe
# test 1e: Same as test1a but uses an external "cat" command
# test 1f: Same as test1a but uses an external "cat" command
( name="test1f" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
# test 1g: Same as test1a but uses an external "cat" command
# test 1h: Same as test1a but uses an external "cat" command
( name="test1h" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
)
)
# Create temp file
{
print "0123456789abcdef01234567890ABCDEF0123456789abcdef01234567890ABCDE"
done
} >"${tmpfile}"
# wait up to log2(i) seconds for the child to terminate
# (this is 10 seconds for 1KB and 19 seconds for 512KB)
isvalidpid ${childpid} || break
sleep 0.25
done
if isvalidpid ${childpid} ; then
err_exit "${currtst.name}: child (pid=${childpid}) still busy, filesize=${testfilesize}."
fi
wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
cmp -s "${tmpfile}" "${tmpfile}.out" || err_exit "${currtst.name}: ${tmpfile} and ${tmpfile}.out differ, filesize=${testfilesize}."
rm "${tmpfile}.out"
done
# Cleanup
rm "${tmpfile}"
done
########################################################################
#### test set 002:
# If a command substitution calls a function and that function contains
# a command substitution which contains a piped command, the original
# command substitution calling the function will return 127 instead of 0.
# This is causing problems in several VSC tests.
# If we remove the piped command from the simple
# case in the attached script, it returns 0.
typeset str
typeset testbody
typeset testout
testbody=$(
# <CS> means command substitution start, <CE> means command substitution end
cat <<EOF
myfunc ()
{
pipedcmd=<CS> printf "hi" | tr "h" "H" <CE>
echo \$pipedcmd
return 0
}
foo=<CS>myfunc<CE>
retval=\$?
if [ "\$foo"X != "HiX" ]; then
echo "myfunc returned '\${foo}'; expected 'Hi'"
fi
if [ \$retval -ne 0 ]; then
echo "command substitution calling myfunc returned \"\${retval}\"; expected 0"
else
echo "command substitution calling myfunc successfully returned 0"
fi
EOF
)
# Test 002/a: Plain test
testout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
# Test 002/b: Same as test002/a but replaces "$(" with "${"
testout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
# Test 002/c: Same as test002/a but forces |fork()| for a subshell via "ulimit -c 0"
testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
# Test 002/d: Same as test002/a but uses extra subshell
testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
# Test 002/e: Same as test002/b but uses extra subshell after "${ "
testout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
########################################################################
#### test set 003:
# An expression within backticks which should return false, instead
# returns true (0).
typeset str
typeset testbody
typeset testout
testbody=$(
# <CS> means command substitution start, <CE> means command substitution end
cat <<EOF
if <CS>expr "NOMATCH" : ".*Z" > /dev/null<CE> ; then
echo "xerror"
else
echo "xok"
fi
EOF
)
# Test 003/a: Plain test
testout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
# Test 003/b: Same as test003/a but replaces "$(" with "${"
testout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
# Test 003/c: Same as test003/a but forces |fork()| for a subshell via "ulimit -c 0"
testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
# Test 003/d: Same as test003/a but uses extra subshell
testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
# Test 003/e: Same as test003/b but uses extra subshell after "${ "
testout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
########################################################################
#### test set 004:
# test pipe within ${... ; } command subtitution ending in a
# to force the use of the external "cat" command). ast-ksh.2009-01-20
# had a bug which caused this test to fail.
########################################################################
#### test set 005:
# Test whether the shell may hang in a
# Originally discovered with ast-ksh.2009-05-05 which hung in
# the "configure" script of postgresql-8.3.7.tar.gz (e.g.
# configure --enable-thread-safety --without-readline)
compound test5=(
compound -a testcases=(
# gsf's reduced testcase
# gisburn's reduced testcase
## The following tests do not trigger the problem but are included here for completeness
## and to make sure we don't get other incarnations of the same problem later...
# same as test5_a but uses ${ ... ; } instead of $(...)
# same as test5_b but uses ${ ... ; } instead of $(...)
# same as test5_a but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
# same as test5_b but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
( name="test5_f" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval "/bin/printf hello\n" 2>&1 1>&5)done' )
)
)
maxwait=5
nameref currtst=test5.testcases[testid]
isvalidpid ${childpid} || break
sleep 0.25
done
if isvalidpid ${childpid} ; then
err_exit "${currtst.name}: child (pid=${childpid}) still busy."
fi
wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
[[ "${testout}" == "done" ]] || err_exit "test '${currtst.name}' failed, expected 'done', got '${testout}'"
done
# tests done