134f9b3eeSRoland Mainz#
234f9b3eeSRoland Mainz# CDDL HEADER START
334f9b3eeSRoland Mainz#
434f9b3eeSRoland Mainz# The contents of this file are subject to the terms of the
534f9b3eeSRoland Mainz# Common Development and Distribution License (the "License").
634f9b3eeSRoland Mainz# You may not use this file except in compliance with the License.
734f9b3eeSRoland Mainz#
834f9b3eeSRoland Mainz# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
934f9b3eeSRoland Mainz# or http://www.opensolaris.org/os/licensing.
1034f9b3eeSRoland Mainz# See the License for the specific language governing permissions
1134f9b3eeSRoland Mainz# and limitations under the License.
1234f9b3eeSRoland Mainz#
1334f9b3eeSRoland Mainz# When distributing Covered Code, include this CDDL HEADER in each
1434f9b3eeSRoland Mainz# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1534f9b3eeSRoland Mainz# If applicable, add the following below this CDDL HEADER, with the
1634f9b3eeSRoland Mainz# fields enclosed by brackets "[]" replaced with your own identifying
1734f9b3eeSRoland Mainz# information: Portions Copyright [yyyy] [name of copyright owner]
1834f9b3eeSRoland Mainz#
1934f9b3eeSRoland Mainz# CDDL HEADER END
2034f9b3eeSRoland Mainz#
2134f9b3eeSRoland Mainz
2234f9b3eeSRoland Mainz#
233e14f97fSRoger A. Faulkner# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2434f9b3eeSRoland Mainz#
2534f9b3eeSRoland Mainz
2634f9b3eeSRoland Mainz#
2734f9b3eeSRoland Mainz# Written by Roland Mainz <roland.mainz@nrubsig.org>
2834f9b3eeSRoland Mainz#
2934f9b3eeSRoland Mainz
3034f9b3eeSRoland Mainz# test setup
3134f9b3eeSRoland Mainzfunction err_exit
3234f9b3eeSRoland Mainz{
3334f9b3eeSRoland Mainz	print -u2 -n "\t"
3434f9b3eeSRoland Mainz	print -u2 -r ${Command}[$1]: "${@:2}"
353e14f97fSRoger A. Faulkner	(( Errors < 127 && Errors++ ))
3634f9b3eeSRoland Mainz}
3734f9b3eeSRoland Mainzalias err_exit='err_exit $LINENO'
3834f9b3eeSRoland Mainz
3934f9b3eeSRoland Mainzset -o nounset
4034f9b3eeSRoland MainzCommand=${0##*/}
4134f9b3eeSRoland Mainzinteger Errors=0
4234f9b3eeSRoland Mainz
4334f9b3eeSRoland Mainz
4434f9b3eeSRoland Mainzfunction isvalidpid
4534f9b3eeSRoland Mainz{
4634f9b3eeSRoland Mainz	kill -0 ${1} 2>/dev/null && return 0
4734f9b3eeSRoland Mainz	return 1
4834f9b3eeSRoland Mainz}
4934f9b3eeSRoland Mainzinteger testfilesize i maxwait
5034f9b3eeSRoland Mainztypeset tmpfile
5134f9b3eeSRoland Mainzinteger testid
5234f9b3eeSRoland Mainz
5334f9b3eeSRoland Mainz
5434f9b3eeSRoland Mainz########################################################################
5534f9b3eeSRoland Mainz#### test set 001:
5634f9b3eeSRoland Mainz# run loop and check various temp filesizes
5734f9b3eeSRoland Mainz# (Please keep this test syncted with sun_solaris_cr_6800929_large_command_substitution_hang.sh)
5834f9b3eeSRoland Mainz
5934f9b3eeSRoland Mainz# test 1: run loop and check various temp filesizes
603e14f97fSRoger A. Faulknertmpfile="$(mktemp -t "ksh93_tests_command_substitution.${PPID}.$$.XXXXXX")" || err_exit "Cannot create temporary file."
6134f9b3eeSRoland Mainz
6234f9b3eeSRoland Mainzcompound test1=(
6334f9b3eeSRoland Mainz	compound -a testcases=(
6434f9b3eeSRoland Mainz		# test 1a: Run test child for $(...)
6534f9b3eeSRoland Mainz		# (note the pipe chain has to end in a builtin command, an external command may not trigger the bug)
6634f9b3eeSRoland Mainz		( name="test1a" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
6734f9b3eeSRoland Mainz		# test 1b: Same as test1a but uses ${... ; } instead if $(...)
6834f9b3eeSRoland Mainz		( name="test1b" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
6934f9b3eeSRoland Mainz		# test 1c: Same as test1a but does not use a pipe
7034f9b3eeSRoland Mainz		( name="test1c" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
7134f9b3eeSRoland Mainz		# test 1d: Same as test1a but does not use a pipe
7234f9b3eeSRoland Mainz		( name="test1d" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
7334f9b3eeSRoland Mainz
7434f9b3eeSRoland Mainz		# test 1e: Same as test1a but uses an external "cat" command
7534f9b3eeSRoland Mainz		( name="test1e" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
7634f9b3eeSRoland Mainz		# test 1f: Same as test1a but uses an external "cat" command
7734f9b3eeSRoland Mainz		( name="test1f" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
7834f9b3eeSRoland Mainz		# test 1g: Same as test1a but uses an external "cat" command
7934f9b3eeSRoland Mainz		( name="test1g" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
8034f9b3eeSRoland Mainz		# test 1h: Same as test1a but uses an external "cat" command
8134f9b3eeSRoland Mainz		( name="test1h" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
8234f9b3eeSRoland Mainz	)
8334f9b3eeSRoland Mainz)
8434f9b3eeSRoland Mainz
8534f9b3eeSRoland Mainzfor (( testfilesize=1*1024 ; testfilesize <= 1024*1024 ; testfilesize*=2 )) ; do
8634f9b3eeSRoland Mainz	# Create temp file
8734f9b3eeSRoland Mainz	{
8834f9b3eeSRoland Mainz		for (( i=0 ; i < testfilesize ; i+=64 )) ; do
8934f9b3eeSRoland Mainz			print "0123456789abcdef01234567890ABCDEF0123456789abcdef01234567890ABCDE"
9034f9b3eeSRoland Mainz		done
9134f9b3eeSRoland Mainz	} >"${tmpfile}"
9234f9b3eeSRoland Mainz
9334f9b3eeSRoland Mainz	# wait up to log2(i) seconds for the child to terminate
9434f9b3eeSRoland Mainz	# (this is 10 seconds for 1KB and 19 seconds for 512KB)
9534f9b3eeSRoland Mainz	(( maxwait=log2(testfilesize) ))
96*b30d1939SAndy Fiddaman
9734f9b3eeSRoland Mainz	for testid in "${!test1.testcases[@]}" ; do
9834f9b3eeSRoland Mainz		nameref currtst=test1.testcases[testid]
9934f9b3eeSRoland Mainz		${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" &
10034f9b3eeSRoland Mainz		(( childpid=$! ))
10134f9b3eeSRoland Mainz
10234f9b3eeSRoland Mainz		for (( i=0 ; i < maxwait ; i++ )) ; do
10334f9b3eeSRoland Mainz			isvalidpid ${childpid} || break
10434f9b3eeSRoland Mainz			sleep 0.25
10534f9b3eeSRoland Mainz		done
10634f9b3eeSRoland Mainz
10734f9b3eeSRoland Mainz		if isvalidpid ${childpid} ; then
10834f9b3eeSRoland Mainz			err_exit "${currtst.name}: child (pid=${childpid}) still busy, filesize=${testfilesize}."
10934f9b3eeSRoland Mainz			kill -KILL ${childpid} 2>/dev/null
11034f9b3eeSRoland Mainz		fi
11134f9b3eeSRoland Mainz		wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
11234f9b3eeSRoland Mainz
11334f9b3eeSRoland Mainz		# compare input/output
11434f9b3eeSRoland Mainz		cmp -s "${tmpfile}" "${tmpfile}.out" || err_exit "${currtst.name}: ${tmpfile} and ${tmpfile}.out differ, filesize=${testfilesize}."
11534f9b3eeSRoland Mainz		rm "${tmpfile}.out"
11634f9b3eeSRoland Mainz	done
11734f9b3eeSRoland Mainz
11834f9b3eeSRoland Mainz	# Cleanup
11934f9b3eeSRoland Mainz	rm "${tmpfile}"
12034f9b3eeSRoland Mainzdone
12134f9b3eeSRoland Mainz
12234f9b3eeSRoland Mainz
12334f9b3eeSRoland Mainz########################################################################
12434f9b3eeSRoland Mainz#### test set 002:
12534f9b3eeSRoland Mainz# If a command substitution calls a function and that function contains
12634f9b3eeSRoland Mainz# a command substitution which contains a piped command, the original
12734f9b3eeSRoland Mainz# command substitution calling the function will return 127 instead of 0.
12834f9b3eeSRoland Mainz# This is causing problems in several VSC tests.
12934f9b3eeSRoland Mainz# If we remove the piped command from the simple
13034f9b3eeSRoland Mainz# case in the attached script, it returns 0.
13134f9b3eeSRoland Mainz
13234f9b3eeSRoland Mainztypeset str
13334f9b3eeSRoland Mainztypeset testbody
13434f9b3eeSRoland Mainztypeset testout
13534f9b3eeSRoland Mainz
13634f9b3eeSRoland Mainztestbody=$(
13734f9b3eeSRoland Mainz# <CS> means command substitution start, <CE> means command substitution end
13834f9b3eeSRoland Mainzcat <<EOF
13934f9b3eeSRoland Mainzmyfunc ()
140*b30d1939SAndy Fiddaman{
14134f9b3eeSRoland Mainz	pipedcmd=<CS> printf "hi" | tr "h" "H" <CE>
14234f9b3eeSRoland Mainz	echo \$pipedcmd
14334f9b3eeSRoland Mainz
14434f9b3eeSRoland Mainz	return 0
14534f9b3eeSRoland Mainz}
14634f9b3eeSRoland Mainz
14734f9b3eeSRoland Mainzfoo=<CS>myfunc<CE>
14834f9b3eeSRoland Mainzretval=\$?
14934f9b3eeSRoland Mainz
15034f9b3eeSRoland Mainzif [ "\$foo"X != "HiX" ]; then
15134f9b3eeSRoland Mainz	echo "myfunc returned '\${foo}'; expected 'Hi'"
15234f9b3eeSRoland Mainzfi
15334f9b3eeSRoland Mainz
15434f9b3eeSRoland Mainzif [ \$retval -ne 0 ]; then
15534f9b3eeSRoland Mainz	echo "command substitution calling myfunc returned \"\${retval}\"; expected 0"
15634f9b3eeSRoland Mainzelse
15734f9b3eeSRoland Mainz	echo "command substitution calling myfunc successfully returned 0"
15834f9b3eeSRoland Mainzfi
15934f9b3eeSRoland MainzEOF
16034f9b3eeSRoland Mainz)
16134f9b3eeSRoland Mainz
16234f9b3eeSRoland Mainz
16334f9b3eeSRoland Mainz# Test 002/a: Plain test
16434f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
16534f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
16634f9b3eeSRoland Mainz
16734f9b3eeSRoland Mainz# Test 002/b: Same as test002/a but replaces "$(" with "${"
16834f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
16934f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
17034f9b3eeSRoland Mainz
17134f9b3eeSRoland Mainz# Test 002/c: Same as test002/a but forces |fork()| for a subshell via "ulimit -c 0"
17234f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
17334f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
17434f9b3eeSRoland Mainz
17534f9b3eeSRoland Mainz# Test 002/d: Same as test002/a but uses extra subshell
17634f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
17734f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
17834f9b3eeSRoland Mainz
179*b30d1939SAndy Fiddaman# Test 002/e: Same as test002/b but uses extra subshell after "${ "
18034f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
18134f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}"
18234f9b3eeSRoland Mainz
18334f9b3eeSRoland Mainz
18434f9b3eeSRoland Mainz
18534f9b3eeSRoland Mainz
18634f9b3eeSRoland Mainz########################################################################
18734f9b3eeSRoland Mainz#### test set 003:
18834f9b3eeSRoland Mainz# An expression within backticks which should return false, instead
18934f9b3eeSRoland Mainz# returns true (0).
19034f9b3eeSRoland Mainz
19134f9b3eeSRoland Mainztypeset str
19234f9b3eeSRoland Mainztypeset testbody
19334f9b3eeSRoland Mainztypeset testout
19434f9b3eeSRoland Mainz
19534f9b3eeSRoland Mainztestbody=$(
19634f9b3eeSRoland Mainz# <CS> means command substitution start, <CE> means command substitution end
19734f9b3eeSRoland Mainzcat <<EOF
19834f9b3eeSRoland Mainzif <CS>expr "NOMATCH" : ".*Z" > /dev/null<CE> ; then
19934f9b3eeSRoland Mainz        echo "xerror"
20034f9b3eeSRoland Mainzelse
20134f9b3eeSRoland Mainz        echo "xok"
20234f9b3eeSRoland Mainzfi
20334f9b3eeSRoland MainzEOF
20434f9b3eeSRoland Mainz)
20534f9b3eeSRoland Mainz
20634f9b3eeSRoland Mainz
20734f9b3eeSRoland Mainz# Test 003/a: Plain test
20834f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
20934f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}"
21034f9b3eeSRoland Mainz
21134f9b3eeSRoland Mainz# Test 003/b: Same as test003/a but replaces "$(" with "${"
21234f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
21334f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}"
21434f9b3eeSRoland Mainz
21534f9b3eeSRoland Mainz# Test 003/c: Same as test003/a but forces |fork()| for a subshell via "ulimit -c 0"
21634f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
21734f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}"
21834f9b3eeSRoland Mainz
21934f9b3eeSRoland Mainz# Test 003/d: Same as test003/a but uses extra subshell
22034f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
22134f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}"
22234f9b3eeSRoland Mainz
223*b30d1939SAndy Fiddaman# Test 003/e: Same as test003/b but uses extra subshell after "${ "
22434f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
22534f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}"
22634f9b3eeSRoland Mainz
22734f9b3eeSRoland Mainz
22834f9b3eeSRoland Mainz########################################################################
22934f9b3eeSRoland Mainz#### test set 004:
23034f9b3eeSRoland Mainz# test pipe within ${... ; } command subtitution ending in a
23134f9b3eeSRoland Mainz# non-builtin command (therefore we use "/bin/cat" instead of "cat" below
23234f9b3eeSRoland Mainz# to force the use of the external "cat" command). ast-ksh.2009-01-20
23334f9b3eeSRoland Mainz# had a bug which caused this test to fail.
23434f9b3eeSRoland Mainztestout=$( ${SHELL} -c 'pipedcmd=${ printf "hi" | /bin/cat ; } ; print $pipedcmd' )
23534f9b3eeSRoland Mainz[[ "${testout}" == "hi" ]] || err_exit "test004: Expected 'hi', got '${testout}'"
23634f9b3eeSRoland Mainz
23734f9b3eeSRoland Mainz
23834f9b3eeSRoland Mainz########################################################################
23934f9b3eeSRoland Mainz#### test set 005:
24034f9b3eeSRoland Mainz# Test whether the shell may hang in a
24134f9b3eeSRoland Mainz# 'exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)'
24234f9b3eeSRoland Mainz# Originally discovered with ast-ksh.2009-05-05 which hung in
243*b30d1939SAndy Fiddaman# the "configure" script of postgresql-8.3.7.tar.gz (e.g.
24434f9b3eeSRoland Mainz# configure --enable-thread-safety --without-readline)
24534f9b3eeSRoland Mainzcompound test5=(
24634f9b3eeSRoland Mainz	compound -a testcases=(
24734f9b3eeSRoland Mainz		# gsf's reduced testcase
24834f9b3eeSRoland Mainz		( name="test5_a" cmd='exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)done' )
24934f9b3eeSRoland Mainz		# gisburn's reduced testcase
25034f9b3eeSRoland Mainz		( name="test5_b" cmd='exec 5>/dev/null; print $(eval "/bin/printf hello\n" 2>&1 1>&5)done' )
25134f9b3eeSRoland Mainz
25234f9b3eeSRoland Mainz		## The following tests do not trigger the problem but are included here for completeness
25334f9b3eeSRoland Mainz		## and to make sure we don't get other incarnations of the same problem later...
25434f9b3eeSRoland Mainz
25534f9b3eeSRoland Mainz		# same as test5_a but uses ${ ... ; } instead of $(...)
25634f9b3eeSRoland Mainz		( name="test5_c" cmd='exec 5>/dev/null; print "${ eval ls -d . 2>&1 1>&5 ;}done"' )
25734f9b3eeSRoland Mainz		# same as test5_b but uses ${ ... ; } instead of $(...)
25834f9b3eeSRoland Mainz		( name="test5_d" cmd='exec 5>/dev/null; print "${ eval "/bin/printf hello\n" 2>&1 1>&5 ;}done"' )
25934f9b3eeSRoland Mainz		# same as test5_a but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
26034f9b3eeSRoland Mainz		( name="test5_e" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval ls -d . 2>&1 1>&5)done' )
26134f9b3eeSRoland Mainz		# same as test5_b but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
26234f9b3eeSRoland Mainz		( name="test5_f" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval "/bin/printf hello\n" 2>&1 1>&5)done' )
26334f9b3eeSRoland Mainz	)
26434f9b3eeSRoland Mainz)
26534f9b3eeSRoland Mainz
26634f9b3eeSRoland Mainzmaxwait=5
26734f9b3eeSRoland Mainzfor testid in "${!test5.testcases[@]}" ; do
26834f9b3eeSRoland Mainz	nameref currtst=test5.testcases[testid]
26934f9b3eeSRoland Mainz	${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" &
27034f9b3eeSRoland Mainz	(( childpid=$! ))
27134f9b3eeSRoland Mainz
27234f9b3eeSRoland Mainz	for (( i=0 ; i < maxwait ; i++ )) ; do
27334f9b3eeSRoland Mainz		isvalidpid ${childpid} || break
27434f9b3eeSRoland Mainz		sleep 0.25
27534f9b3eeSRoland Mainz	done
27634f9b3eeSRoland Mainz
27734f9b3eeSRoland Mainz	if isvalidpid ${childpid} ; then
27834f9b3eeSRoland Mainz		err_exit "${currtst.name}: child (pid=${childpid}) still busy."
27934f9b3eeSRoland Mainz		kill -KILL ${childpid} 2>/dev/null
28034f9b3eeSRoland Mainz	fi
28134f9b3eeSRoland Mainz	wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
28234f9b3eeSRoland Mainz
28334f9b3eeSRoland Mainz	testout="$( < "${tmpfile}.out")"
28434f9b3eeSRoland Mainz	rm "${tmpfile}.out" || err_exit "File '${tmpfile}.out' could not be removed."
28534f9b3eeSRoland Mainz	[[ "${testout}" == "done" ]] || err_exit "test '${currtst.name}' failed, expected 'done', got '${testout}'"
28634f9b3eeSRoland Mainzdone
28734f9b3eeSRoland Mainz
28834f9b3eeSRoland Mainz
28934f9b3eeSRoland Mainz# tests done
29034f9b3eeSRoland Mainzexit $((Errors))
291