4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
23 # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 # Written by Roland Mainz <roland.mainz@nrubsig.org>
34 print
-u2 -r ${Command}[$1]: "${@:2}"
35 (( Errors
< 127 && Errors
++ ))
37 alias err_exit
='err_exit $LINENO'
46 kill -0 ${1} 2>/dev
/null
&& return 0
49 integer testfilesize i maxwait
54 ########################################################################
56 # run loop and check various temp filesizes
57 # (Please keep this test syncted with sun_solaris_cr_6800929_large_command_substitution_hang.sh)
59 # test 1: run loop and check various temp filesizes
60 tmpfile
="$(mktemp -t "ksh93_tests_command_substitution.
${PPID}.$$.XXXXXX
")" || err_exit
"Cannot create temporary file."
63 compound
-a testcases
=(
64 # test 1a: Run test child for $(...)
65 # (note the pipe chain has to end in a builtin command, an external command may not trigger the bug)
66 ( name
="test1a" cmd
="builtin cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
67 # test 1b: Same as test1a but uses ${... ; } instead if $(...)
68 ( name
="test1b" cmd
="builtin cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
69 # test 1c: Same as test1a but does not use a pipe
70 ( name
="test1c" cmd
="builtin cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
71 # test 1d: Same as test1a but does not use a pipe
72 ( name
="test1d" cmd
="builtin cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
74 # test 1e: Same as test1a but uses an external "cat" command
75 ( name
="test1e" cmd
="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
76 # test 1f: Same as test1a but uses an external "cat" command
77 ( name
="test1f" cmd
="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
78 # test 1g: Same as test1a but uses an external "cat" command
79 ( name
="test1g" cmd
="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
80 # test 1h: Same as test1a but uses an external "cat" command
81 ( name
="test1h" cmd
="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
85 for (( testfilesize
=1*1024 ; testfilesize
<= 1024*1024 ; testfilesize
*=2 )) ; do
88 for (( i
=0 ; i
< testfilesize
; i
+=64 )) ; do
89 print
"0123456789abcdef01234567890ABCDEF0123456789abcdef01234567890ABCDE"
93 # wait up to log2(i) seconds for the child to terminate
94 # (this is 10 seconds for 1KB and 19 seconds for 512KB)
95 (( maxwait
=log2
(testfilesize
) ))
97 for testid
in "${!test1.testcases[@]}" ; do
98 nameref currtst
=test1.testcases
[testid
]
99 ${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out
" &
102 for (( i=0 ; i < maxwait ; i++ )) ; do
103 isvalidpid ${childpid} || break
107 if isvalidpid ${childpid} ; then
108 err_exit "${currtst.name}: child (pid=${childpid}) still busy, filesize=${testfilesize}.
"
109 kill -KILL ${childpid} 2>/dev/null
111 wait || err_exit "${currtst.name}: Child returned non-zero
exit code.
" # wait for child (and/or avoid zombies/slime)
113 # compare input/output
114 cmp -s "${tmpfile}" "${tmpfile}.out" || err_exit "${currtst.name}: ${tmpfile} and ${tmpfile}.out differ, filesize=${testfilesize}.
"
123 ########################################################################
125 # If a command substitution calls a function and that function contains
126 # a command substitution which contains a piped command, the original
127 # command substitution calling the function will return 127 instead of 0.
128 # This is causing problems in several VSC tests.
129 # If we remove the piped command from the simple
130 # case in the attached script, it returns 0.
137 # <CS> means command substitution start, <CE> means command substitution end
141 pipedcmd=<CS> printf "hi
" | tr "h
" "H
" <CE>
150 if [ "\
$foo"X != "HiX
" ]; then
151 echo "myfunc returned
'\${foo}'; expected
'Hi'"
154 if [ \$retval -ne 0 ]; then
155 echo "command substitution calling myfunc returned
\"\
${retval}\"; expected
0"
157 echo "command substitution calling myfunc successfully returned
0"
163 # Test 002/a: Plain test
164 testout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
165 [[ "${testout}" == "command substitution calling myfunc successfully returned
0" ]] || err_exit "Expected
'command substitution calling myfunc successfully returned 0', got
${testout}"
167 # Test 002/b: Same as test002/a but replaces "$
(" with "$
{"
168 testout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
169 [[ "${testout}" == "command substitution calling myfunc successfully returned
0" ]] || err_exit "Expected
'command substitution calling myfunc successfully returned 0', got
${testout}"
171 # Test 002/c: Same as test002/a but forces |fork()| for a subshell via "ulimit -c 0"
172 testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
173 [[ "${testout}" == "command substitution calling myfunc successfully returned
0" ]] || err_exit "Expected
'command substitution calling myfunc successfully returned 0', got
${testout}"
175 # Test 002/d: Same as test002/a but uses extra subshell
176 testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
177 [[ "${testout}" == "command substitution calling myfunc successfully returned
0" ]] || err_exit "Expected
'command substitution calling myfunc successfully returned 0', got
${testout}"
179 # Test 002/e: Same as test002/b but uses extra subshell after "$
{ "
180 testout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
181 [[ "${testout}" == "command substitution calling myfunc successfully returned
0" ]] || err_exit "Expected
'command substitution calling myfunc successfully returned 0', got
${testout}"
186 ########################################################################
188 # An expression within backticks which should return false, instead
196 # <CS> means command substitution start, <CE> means command substitution end
198 if <CS>expr "NOMATCH
" : ".
*Z
" > /dev/null<CE> ; then
207 # Test 003/a: Plain test
208 testout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
209 [[ "${testout}" == "xok
" ]] || err_exit "Expected
'xok', got
${testout}"
211 # Test 003/b: Same as test003/a but replaces "$
(" with "$
{"
212 testout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
213 [[ "${testout}" == "xok
" ]] || err_exit "Expected
'xok', got
${testout}"
215 # Test 003/c: Same as test003/a but forces |fork()| for a subshell via "ulimit -c 0"
216 testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
217 [[ "${testout}" == "xok
" ]] || err_exit "Expected
'xok', got
${testout}"
219 # Test 003/d: Same as test003/a but uses extra subshell
220 testout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
221 [[ "${testout}" == "xok
" ]] || err_exit "Expected
'xok', got
${testout}"
223 # Test 003/e: Same as test003/b but uses extra subshell after "$
{ "
224 testout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" }
225 [[ "${testout}" == "xok
" ]] || err_exit "Expected
'xok', got
${testout}"
228 ########################################################################
230 # test pipe within ${... ; } command subtitution ending in a
231 # non-builtin command (therefore we use "/bin
/cat" instead of "cat" below
232 # to force the use of the external "cat" command). ast-ksh.2009-01-20
233 # had a bug which caused this test to fail.
234 testout=$( ${SHELL} -c 'pipedcmd=${ printf "hi" | /bin/cat ; } ; print $pipedcmd' )
235 [[ "${testout}" == "hi
" ]] || err_exit "test004
: Expected
'hi', got
'${testout}'"
238 ########################################################################
240 # Test whether the shell may hang in a
241 # 'exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)'
242 # Originally discovered with ast-ksh.2009-05-05 which hung in
243 # the "configure
" script of postgresql-8.3.7.tar.gz (e.g.
244 # configure --enable-thread-safety --without-readline)
246 compound -a testcases=(
247 # gsf's reduced testcase
248 ( name="test5_a
" cmd='exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)done' )
249 # gisburn's reduced testcase
250 ( name="test5_b
" cmd='exec 5>/dev/null; print $(eval "/bin
/printf hello
\n" 2>&1 1>&5)done' )
252 ## The following tests do not trigger the problem but are included here for completeness
253 ## and to make sure we don't get other incarnations of the same problem later...
255 # same as test5_a but uses ${ ... ; } instead of $(...)
256 ( name="test5_c
" cmd='exec 5>/dev/null; print "${ eval ls -d . 2>&1 1>&5 ;}done"' )
257 # same as test5_b but uses ${ ... ; } instead of $(...)
258 ( name="test5_d
" cmd='exec 5>/dev/null; print "${ eval "/bin/printf hello\n" 2>&1 1>&5 ;}done"' )
259 # same as test5_a but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
260 ( name="test5_e
" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval ls -d . 2>&1 1>&5)done' )
261 # same as test5_b but uses "ulimit -c 0" to force the shell to use a seperare process for $(...)
262 ( name="test5_f
" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval "/bin
/printf hello
\n" 2>&1 1>&5)done' )
267 for testid in "${!test5.testcases[@]}" ; do
268 nameref currtst=test5.testcases[testid]
269 ${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" &
272 for (( i
=0 ; i
< maxwait
; i
++ )) ; do
273 isvalidpid
${childpid} ||
break
277 if isvalidpid
${childpid} ; then
278 err_exit
"${currtst.name}: child (pid=${childpid}) still busy."
279 kill -KILL ${childpid} 2>/dev
/null
281 wait || err_exit
"${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
283 testout
="$( < "${tmpfile}.out
")"
284 rm "${tmpfile}.out" || err_exit
"File '${tmpfile}.out' could not be removed."
285 [[ "${testout}" == "done" ]] || err_exit "test '${currtst.name}' failed, expected 'done', got '${testout}'"