xref: /netbsd-src/tests/bin/sh/t_syntax.sh (revision 82d56013d7b633d116a93943de88e08335357a7c)
1# $NetBSD: t_syntax.sh,v 1.10 2018/11/14 02:37:51 kre Exp $
2#
3# Copyright (c) 2017 The NetBSD Foundation, Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26#
27: ${TEST_SH:=/bin/sh}
28
29# This set of tests verifies various requirementgs relating to correct
30# (and incorrect) syntax of shell input
31#
32# There is no intent in these tests to verify correct operation
33# (though that sometimes cannot be separated from correct parsing.)
34# That is (or should be) verified elsewhere.
35#
36# Also, some very basic syntax is tested in almost every test
37# (they cannot work without basic parsing of elementary commands)
38# so that is also not explicitly tested here.
39#
40# Similarly word expansion, field splitting, redirection, all have
41# tests of their own (though we do test parsing of redirect ops).
42#
43# Note that in order to test the basic facilities, other shell operations
44# are used - a test failure here does not necessarily mean that the
45# operation intended to be tested is faulty, just that something is.
46
47atf_test_case a_basic_tokenisation
48a_basic_tokenisation_head() {
49	atf_set "descr" "Test the shell correctly finds various tokens"
50}
51a_basic_tokenisation_body() {
52	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
53		'set -- a b c; echo $#'
54	atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
55		'set -- a""b c; echo $#'
56	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
57		'set -- a"" b c; echo $#'
58	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
59		'set -- ""a b c\;; echo $#'
60
61	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
62		'set -- set -- c; echo $#'
63	atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
64		'set --;set -- c; echo $#'
65	atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
66		'set --&set -- c; echo $#'
67	atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
68		'set -- a b&&set -- c; echo $#'
69	atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
70		'set -- a b||set -- c; echo $#'
71}
72
73atf_test_case b_comments
74b_comments_head() {
75	atf_set "descr" "Test the shell correctly handles comments"
76}
77b_comments_body() {
78
79	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '#'
80	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '# exit 1'
81	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'true # exit 1'
82	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c 'false # exit 0'
83
84	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
85		'echo foo # bar'
86	atf_check -s exit:0 -o 'inline:foo # bar\n' -e empty ${TEST_SH} -c \
87		'echo foo \# bar'
88	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
89		'echo foo; # echo bar'
90	atf_check -s exit:0 -o 'inline:foo#bar\n' -e empty ${TEST_SH} -c \
91		'echo foo#bar'
92	atf_check -s exit:0 -o 'inline:foo# bar\n' -e empty ${TEST_SH} -c \
93		'echo foo# bar'
94	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
95		'x=foo; echo ${x#bar}'
96
97	atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
98		'echo "#"'
99	atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
100		"echo '#'"
101	atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
102		'echo \#'
103	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
104		'echo "#"#'
105	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
106		"echo '#'#"
107	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
108		'echo \##'
109	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
110		'echo "#"# #"#"'
111	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
112		"echo '#'# #'#'"
113	atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
114		'echo \## #\#'
115
116	cat <<-'DONE'|atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH}
117		# test comments do not provoke synax errors !\
118		echo foo # ( { " hello
119		while : # that's forever
120		do	# the following command list
121			# starting with nothing ${unset?error}
122			break	# done loop terminate $( echo bar; exit 1 )
123		done #####################################################
124		# "hello
125		exit 0
126	DONE
127}
128
129atf_test_case c_line_wrapping
130c_line_wrapping_head() {
131	atf_set "descr" "check aspects of command line wrapping"
132}
133c_line_wrapping_body() {
134	atf_require_prog ls
135	atf_require_prog printf
136
137	cat <<- 'DONE' | atf_check -s exit:0 -o ignore -e empty ${TEST_SH} -e
138		l\
139		s
140	DONE
141
142	cat <<- 'DONE' | atf_check -s exit:7 -o empty -e empty ${TEST_SH}
143		e\
144		x\
145		it \
146		7
147	DONE
148
149	# Have to do this twice as cannot say "any exit code but 0 or 7" ...
150	cat <<- 'DONE' | atf_check -s not-exit:0 -o empty -e not-empty \
151	    ${TEST_SH}
152		e\
153		x\
154		it\
155		7
156	DONE
157	cat <<- 'DONE' | atf_check -s not-exit:7 -o empty -e not-empty \
158	    ${TEST_SH}
159		e\
160		x\
161		it\
162		7
163	DONE
164
165	cat <<- 'DONE' | atf_check -s exit:0 -o empty -e empty  ${TEST_SH}
166		wh\
167		il\
168		e \
169		f\a\
170		\l\s\e
171		do
172		:\
173		;
174		done
175	DONE
176
177	cat <<- 'DONE' | atf_check -s exit:0 -o inline:'hellohellohellohello' \
178	    -e empty ${TEST_SH}
179		V\
180		AR=hel\
181		lo
182		unset U V1
183		pri\
184		ntf '%s' ${\
185		VAR\
186		}
187		p\
188		r\
189		i\
190		n\
191		t\
192		f\
193		 \
194		'%s' \
195		$\
196		{\
197		V\
198		A\
199		R}
200		printf '%s' ${U\
201		-\
202		"$\
203		{V\
204		1:\
205		=$\
206		{V\
207		AR+\
208		${V\
209		AR}\
210		}\
211		}"}
212		printf '%s' ${V\
213		1?V1\
214		 \
215		FAIL}
216	DONE
217
218	cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH}
219		l\
220		s=7 bi\
221		n\
222		=\
223		3
224		echo $(\
225		( ls /bin )\
226		)
227	DONE
228
229	# Inspired by src/etc/MAKEDEV.tmpl failure with (broken)
230	# sh LINENO code...  avoid it happening again...
231	for VARS in 1:0:0:0 0:1:0:0 0:0:1:0 0:0:0:1 \
232		    1:0:0:1 1:0:1:0 1:1:0:0 0:1:1:0 \
233		    0:0:0:0 1:1:0:1 0:1:1:1 1:1:1:1
234	do
235		eval $(
236			IFS=:
237			set -- $VARS
238			test $(( $1 + $2 + $3 + $4 )) -eq 1 &&
239				R=OK || R=BAD
240			printf "R=%s;" $R
241			for v in a b c d
242			do
243				case $1 in
244				(0)	printf "export %s=false;" $v;;
245				(1)	printf "export %s=true;"  $v;;
246				esac
247				shift
248			done
249		)
250
251		cat <<- 'DONE' | atf_check -s exit:0 -o inline:"${R}" ${TEST_SH}
252			case $(( $($a && echo 1 || echo 0) + \
253				 $($b && echo 1 || echo 0) + \
254				 $($c && echo 1 || echo 0) + \
255				 $($d && echo 1 || echo 0) ))
256			in
257			(1)	printf OK ;;
258			(*)	printf BAD ;;
259			esac
260		DONE
261	done
262
263	# inspired by pkgsrc/pkgtools/cwrappers :: libnbcompat/configure
264	# failure with (broken) sh LINENO code .. avoid recurrence
265	# This test would have failed.
266	cat <<- 'DONE' | atf_check -s exit:0 -o inline:'/tmp\n' ${TEST_SH}
267		dn=/tmp/foo
268
269		D=`dirname -- "${dn}" ||
270		expr	X"${dn}" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
271			X"${dn}" : 'X\(//\)[^/]' \| \
272			X"${dn}" : 'X\(//\)$' \| \
273			X"${dn}" : 'X\(/\)' \| . 2>/dev/null ||
274		echo X"${dn}" |
275		    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
276			    s//\1/
277			    q
278			  }
279			  /^X\(\/\/\)[^/].*/{
280			    s//\1/
281			    q
282			  }
283			  /^X\(\/\/\)$/{
284			    s//\1/
285			    q
286			  }
287			  /^X\(\/\).*/{
288			    s//\1/
289			    q
290			  }
291			  s/.*/./; q'`
292
293		echo "${D}"
294	DONE
295	return 0
296}
297
298atf_test_case d_cstrings
299d_cstrings_head() {
300	atf_set "descr" "Check processing of $' ' quoting (C style strings)"
301}
302d_cstrings_body() {
303	unset ENV
304
305	if ! ${TEST_SH} -c ": \$'abc'" ||
306	     test $( ${TEST_SH} -c "printf %s \$'abc'" ) != abc
307	then
308		atf_skip "\$'...' (C style quoted strings) not supported"
309	fi
310
311	# simple stuff
312	atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
313		"printf '%s\\n' \$'abc\tdef'"
314	atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
315		"printf '%s\\n' \$'abc\011def'"
316	atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \
317		"printf '%s\\n' \$'abc\x09'def"
318	atf_check -s exit:0 -e empty -o inline:'abc$def\n' ${TEST_SH} -c \
319		"def=xyz; printf '%s\\n' \$'abc\$def'"
320
321	# control chars (\c) and unicode \u
322	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
323		"test \$'\\1-\\2-\\3' = \$'\\ca-\\cb-\\cc'"
324	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
325		"test \$'\\r-\\n-\\f' = \$'\\cm-\\cj-\\cl'"
326	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
327		"unset LC_ALL; export LC_CTYPE=en_AU.UTF-8;
328		test \$'\\u0123' = \$'\\304\\243'"
329	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
330		"test \$'\\u0123' = \$'\\xC4\\xA3'"
331	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
332		"test \$'\\c\\\\' = \$'\\x1C'"
333	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
334		"test \$'\\c[\\c]\\c^\\c_\\c?' = \$'\\x1B\\x1D\\x1E\\x1F\\x7F'"
335
336	# all the \X sequences for a single char X (ie: not hex/octal/unicode)
337	atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
338		${TEST_SH} -c "printf '%s\\n' \$'\\a\\b\\e\\f\\n\\r\\t\\v'"
339	atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
340	   ${TEST_SH} -c "printf '%s\\n' \$'\\cG\\cH\\x1b\\cl\\cJ\\cm\\cI\\ck'"
341	atf_check -s exit:0 -e empty -o inline:"'"'"\\\n' \
342		${TEST_SH} -c "printf '%s\\n' \$'\\'\\\"\\\\'"
343
344	# various invalid $'...' sequences
345	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
346		": \$'\\q'"
347	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
348		": \$'\\c\\q'"
349	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
350		": \$'\\uDEFF'"
351	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
352		": \$'abcd"
353	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
354		": \$'abcd\\"
355
356	# anything that generates \0 ends the $'...' immediately
357	atf_check -s exit:0 -e empty -o inline:'aAaA' ${TEST_SH} -c \
358		"printf '%s' \$'a\\0x'\$'A\\x00X'\$'a\\c@x'\$'A\\u0000X'"
359
360	# \newline in a $'...' is dropped (just like in "" strings)
361	atf_check -s exit:0 -e empty -o inline:'abcdef' ${TEST_SH} -c \
362"printf '%s' \$'abc\\
363def'"
364	# but a normal newline in a $'...' is just a newline
365	atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
366"printf '%s' \$'abc
367def'"
368	# and should work when elided line wrap occurs between $ and '
369	atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
370"printf '%s' \$\\
371'abc\\ndef'"
372
373	# $'...' only works when the $ is unquoted.
374	atf_check -s exit:0 -e empty -o inline:"abc\$'def'g" ${TEST_SH} -c \
375		"printf '%s' \"abc\$'def'g\""
376	atf_check -s exit:0 -e empty -o inline:'abc$defg' ${TEST_SH} -c \
377		"printf '%s' abc\\\$'def'g"
378	atf_check -s exit:0 -e empty -o inline:'abc$def' ${TEST_SH} -c \
379		"printf '%s' abc'\$'def"
380}
381
382atf_test_case f_redirects
383f_redirects_head() {
384	atf_set "descr" "Check parsing of redirect operators"
385}
386f_redirects_body() {
387
388	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
389		'>/dev/null'
390	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
391		'</dev/null'
392	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
393		'>>/dev/null'
394	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
395		'<>/dev/null'
396	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
397		'</dev/null>/dev/null'
398	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
399		'>|/dev/null'
400	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
401		'>/dev/null>/dev/null>/dev/null'
402
403	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
404		'echo hello >/dev/null'
405	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
406		'echo >/dev/null hello'
407	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
408		'>/dev/null echo hello'
409	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
410		'echo hello >/dev/null world'
411	atf_check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \
412		'echo hello </dev/null world'
413
414	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
415		'echo hello </dev/null'
416	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
417		'echo hello 3</dev/null'
418	atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
419		'echo hello 3 </dev/null'
420	atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
421		'echo hello \3</dev/null'
422	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
423		'echo hello</dev/null'
424	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
425		'hello=3; echo ${hello}</dev/null'
426
427	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
428		'2>&1'
429	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
430		'2>& 1'
431	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
432		'FD=1; 2>&"${FD}"'
433	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
434		'FD=1; echo hello 2>&"${FD}" >&2'
435
436	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
437		'2>&- 3<&- 4>&-'
438
439	return 0
440}
441
442atf_test_case g_variable_syntax
443g_variable_syntax_head() {
444	atf_set "descr" "Check that var names of all legal forms work"
445}
446g_variable_syntax_body() {
447	# don't test _ as a variable, it can be "unusual"
448	for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \
449	    A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \
450	    abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \
451	    A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
452	    Then_Make_it_Even_Longer_by_Multiplying_it___A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
453	    xyzzy __0123454321__ _0_1_2_3_4_5_6_7_8_9_ ABBA X_ Y__ Z___ \
454	    _____________________________________________________________
455	do
456		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
457			"unset ${vname}"
458		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
459			"unset ${vname}; printf %s \${${vname}-OK}"
460		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
461			"${vname}=GOOD; printf %s \${${vname}-OK}"
462		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
463			"${vname}=GOOD; printf %s \$${vname}"
464		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
465			"unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}"
466		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
467			"${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}"
468		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
469			"${vname}=GOOD; unset ${vname}x; printf %s \$${vname}"
470		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
471			"unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x"
472		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
473			"${vname}=GOOD; ${vname}_=BAD; printf %s \$${vname}"
474
475		case "${vname}" in
476		?)	continue;;
477		esac
478
479		# The following tests do not work for 1 char var names.
480		# hence the check and "continue" above to skip the remaining
481		# tests for that case
482
483		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
484			"${vname}=GOOD; unset ${vname%?}; printf %s \$${vname}"
485
486		# (this next would work, but becomes just a duplicate of
487		# an earlier test, so is pointless for 1 ch names)
488		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
489	"${vname}=GOOD; unset ${vname}x ${vname%?}; printf %s \$${vname}"
490
491		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
492			"unset ${vname%?};${vname}=GOOD; printf %s \$${vname%?}"
493
494		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
495			"${vname}=GOOD; ${vname%?}=BAD; printf %s \$${vname}"
496
497		# all the remaining tests require the 2nd char of the
498		# variable name to be a legal first character.  That
499		# is, not a digit, so skip the rest if we have a digit
500		# second...
501		case "${vname}" in
502		?[0-9]*)	continue;;
503		esac
504
505		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
506			"${vname}=GOOD; unset ${vname#?}; printf %s \$${vname}"
507		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
508			"unset ${vname#?};${vname}=GOOD; printf %s \$${vname#?}"
509
510		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
511			"${vname}=GOOD; ${vname#?}=BAD; printf %s \$${vname}"
512
513		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
514			"unset ${vname%?} ${vname#?} ${vname}x; ${vname}=GOOD;
515			printf %s \$${vname%?}\$${vname#?}\$${vname}x"
516
517		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
518			"${vname}=GOOD; ${vname%?}=BAD; ${vname}_=BAD;
519			${vname#?}=BAD; printf %s \$${vname}"
520	done
521
522	# don't test '.' in var names, some shells permit that (in ${} anyway)
523	# this test also cannot check for embedded - + ? = : % or #
524	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';'
525	do
526		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
527			"echo \${${vname}}"
528	done
529
530	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \
531	    ';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?'
532	do
533		# failure modes will differ, but they should all fail somehow
534		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
535			"${vname}="
536	done
537
538}
539
540atf_test_case h_var_assign
541h_var_assign_head() {
542	atf_set "descr" "Check var assignments "
543}
544h_var_assign_body() {
545	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
546		'a=b'
547	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
548		'\a=b'
549	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
550		'a=b c=d'
551	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
552		'a=b c=d echo e=f'
553	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
554		'a=b 2>/dev/null c=d </dev/null echo e=f'
555
556	# We need to make sure that we do not accidentally
557	# find a command called 'a=b' ...
558
559	for d in /foo /foo/bar /not-dir /no/such/directory '/!!!' \
560		'/-/--/#' '/"/""/"""' -
561	do
562		test -d "${d}" || break
563	done
564	test "${#d}" -gt 1 || atf-skip 'Wacky directories seem to exist!'
565
566	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
567		"PATH='${d}';"'a=\b'
568	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
569		"PATH='${d}';"'a\=b'
570	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
571		"PATH='${d}';"'\a=b'
572	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
573		"PATH='${d}';"'X=; c=d ${X} a=b'
574}
575
576atf_test_case i_pipelines
577i_pipelines_head() {
578	atf_set "descr" "Check pipelines"
579}
580i_pipelines_body() {
581
582	cmd='printf "%s\n" foo'
583	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
584	do
585		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
586			"${cmd}"
587		cmd="${cmd} | cat"
588	done
589
590	cmd='printf "%s\n" foo'
591	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
592	do
593		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
594			"${cmd}"
595		cmd="${cmd} |
596		cat"
597	done
598
599	cmd='printf "%s\n" foo'
600	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
601	do
602		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
603			"${cmd}"
604		cmd="${cmd} |
605
606
607
608
609		cat"
610	done
611
612	cmd='! printf "%s\n" foo'
613	for n in 1 2 3 4 5 6 7 8 9 10
614	do
615		atf_check -s exit:1 -o inline:'foo\n' -e empty ${TEST_SH} -c \
616			"${cmd}"
617		cmd="${cmd} | cat"
618	done
619
620	cmd='exec 4>&2 3<&0; printf "%s\n" foo'
621	for n in 1 2 3
622	do
623	    pfx=
624	    for xtra in 'x=y' 'a=b' '6>&1' '5<&3'
625	    do
626		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
627			"${cmd} | ${xtra} cat"
628
629		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
630			"${cmd} | ${pfx} cat"
631
632		pfx="${pfx} ${xtra}"
633	    done
634	    cmd="${cmd} | ${pfx} cat"
635	done
636
637	# pipelines are not required to contain commands ...
638	# they don't do anything useful (at all) but the syntax is legal
639	base='4>&2'; cmd="${base}"
640	for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat'
641	do
642		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
643			"${base} | ${pipe}"
644
645		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
646			"${cmd} | ${pipe}"
647
648		cmd="${cmd} | ${pipe}"
649	done
650
651	# but the command cannot be empty, or a reserved word used improperly
652	base='printf "%s\n" foo'; cmd="${base}"
653	for pipe in '' do done then else fi esac
654	do
655		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
656			"${base} | ${pipe}"
657
658		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
659			"${pipe} | ${base}"
660
661		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
662			"${cmd} | ${pipe}"
663
664		cmd="${cmd} | ${pipe}"
665	done
666}
667
668atf_test_case j_and_or_lists
669j_and_or_lists_head() {
670	atf_set "descr" "Check && and || command lists"
671}
672j_and_or_lists_body() {
673	and=true
674	or=false
675	and_or=false
676	for i in 1 2 3 4 5 6 7 8 9 10
677	do
678		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
679			"${and}"
680
681		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
682			"${or}"
683
684		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
685			"${and_or}"
686
687		and="${and} && true"
688		or="${or} || false"
689		and_or="${and_or} || true && false"
690	done
691
692	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
693		'true &&'
694	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
695		'&& true'
696	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
697		'|| true'
698	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
699		'true ||'
700	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
701		'true || && false'
702	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
703		'false || && true'
704
705	cmd='printf "%s" foo | cat | cat>/dev/null'
706	line="${cmd}"
707	for i in 1 2 3 4
708	do
709		line="${line} && ! ${cmd} || ${cmd}"
710
711		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
712			"${line}"
713	done
714
715}
716
717atf_test_case k_lists
718k_lists_head() {
719	atf_set "descr" "Check ; command lists"
720}
721k_lists_body() {
722	line=
723	for N in 1 2 3 4
724	do
725		for cmd in \
726			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
727		do
728			line="${line}${line:+;}${cmd}"
729			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
730				-e empty ${TEST_SH} -c \
731					"echo hello; ${line}; echo world"
732			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
733				-e empty ${TEST_SH} -c \
734					"echo hello; ${line}; echo world;"
735		done
736	done
737
738	for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true'
739	do
740		atf_check -s not-exit:0 -o ignore -e not-empty \
741			${TEST_SH} -c "${cmd}"
742	done
743}
744
745atf_test_case l_async_lists
746l_async_lists_head() {
747	atf_set "descr" "Check & command lists"
748}
749l_async_lists_body() {
750	line=
751	for N in 1 2 3 4
752	do
753		for cmd in \
754			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
755		do
756			line="${line:+${line}&}${cmd}"
757			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
758				-e empty ${TEST_SH} -c \
759					"echo hello; ${line}& echo world"
760			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
761				-e empty ${TEST_SH} -c \
762					"echo hello; ${line}& echo world"
763		done
764	done
765
766	for cmd in '&' ';&' '&;'  '& true' 'false& &true'
767	do
768		atf_check -s not-exit:0 -o ignore -e not-empty \
769			${TEST_SH} -c "${cmd}"
770	done
771}
772
773atf_test_case m_compound_lists
774m_compound_lists_head() {
775	atf_set "descr" "Check subshells () and { ;} command grouping"
776}
777m_compound_lists_body() {
778	# Note: (( is an unspecified (reserved) operator, don't use it...
779	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
780		'( true )'
781	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
782		'( false )'
783	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
784		'( (:) )'
785	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
786		'( ( true ))'
787	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
788		'( ( ( ( (  true )))))'
789	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
790		'( ( ( ( (true);:));true))'
791
792	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
793		'()'
794	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
795		'(   )'
796
797	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
798		'{ true; }'
799	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
800		'{ false; }'
801	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
802		'{ { :; };}'
803	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
804		'{ { { { {  :;};};};};}'
805
806	atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \
807		'{ echo } ; }'
808	atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \
809		'{ echo { ; }'
810}
811
812atf_test_case q_for_loop
813q_for_loop_head() {
814	atf_set "descr" "Check for loop parsing"
815}
816q_for_loop_body() {
817	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
818		'for x; do : ; done'
819	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
820		'for x in ; do : ; done'
821	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
822		'for x in a b c ; do : ; done'
823
824	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
825		'for x in in;do : ; done'
826	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
827		'for x in for;do : ; done'
828	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
829		'for x in do;do : ; done'
830	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
831		'for for in in;do :;done'
832	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
833		'for for in for;do :; done'
834	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
835		'for for in do;do : ;done'
836	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
837		'for in in in;do : ; done'
838	atf_check -s exit:0 -o 'inline:do\nin\ndo\n' -e empty ${TEST_SH} -c \
839   'for in in in do in;do case $in in in)echo do;;do)echo in;;esac; done'
840	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
841		'for in in for;do : ; done'
842	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
843		'for in in do;do : ; done'
844	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
845		'for do in in;do : ; done'
846	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
847		'for do in for;do : ; done'
848	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
849		'for do in do;do : ; done'
850	atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \
851		'for do in do;do echo ${do}do ; done'
852}
853
854atf_test_case r_case
855r_case_head() {
856	atf_set "descr" "Check case statement parsing"
857}
858r_case_body() {
859	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
860		'case x in  esac'
861	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
862		'case x in x) esac'
863	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
864		'case x in (x) esac'
865	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
866		'case x in x) ;; esac'
867	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
868		'case x in (x) ;; esac'
869	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
870		'case x in x|y) ;; esac'
871	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
872		'case x in (x|y) ;; esac'
873
874	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
875		'case x in x|esac) ;; esac'
876	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
877		'case x in x|esac|y) ;; esac'
878	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
879		'case x in (x|esac) ;; esac'
880	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
881		'case x in (x|esac|y) ;; esac'
882
883	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
884		'case x in in) esac'
885	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
886		'case x in in) ;; esac'
887	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
888		'case x in x|in) ;; esac'
889	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
890		'case x in x|in|y) ;; esac'
891	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
892		'case x in (x|in) ;; esac'
893	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
894		'case x in (in|x) ;; esac'
895	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
896		'case x in (x|in|y) ;; esac'
897
898	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
899		'case case in case) esac'
900	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
901		'case in in in) esac'
902	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
903		'case esac in (in) esac'
904	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
905		'case in in esac|cat'
906	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
907		'case esac in esac|cat'
908	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
909		'case in in esac|case x in u) echo foo;; esac'
910	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
911		'case esac in esac|case x in u) echo foo;; esac'
912	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
913		'case in in esac|case x in x) echo foo;; esac'
914
915	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
916		'case in in (esac|cat'
917
918	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
919		'case x in x );;esac'
920	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
921		'case x in ( x );;esac'
922	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
923		'case x in x | y );;esac'
924	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
925		'case x in ( x | y );;esac'
926
927	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
928		'case x
929		 in
930		 (    x    |    y    )
931
932			;;
933
934
935		esac
936		'
937}
938
939atf_test_case s_if
940s_if_head() {
941	atf_set "descr" "Check if statement parsing"
942}
943s_if_body() {
944	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
945		'if :; then :; fi'
946	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
947		'if :; then :; else :; fi'
948	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
949		'if :; then :; elif :; then :; else :; fi'
950	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
951		'if :; then :; elif :; then :; elif :; then :; else :; fi'
952
953	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
954		'if :; then : else :; fi'
955	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
956		'if : then :; then :; fi'
957
958	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
959		'if if :;then :;fi then :;fi'
960	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
961		'if if :;then if :;then :;fi fi;then :;fi'
962	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
963		'if if :;then :;fi;then :;else if :;then :;fi fi'
964
965	for a in true false; do
966		for b in true false; do
967			for c in true false; do
968
969				$a && out=a || {
970				$b && out=b || {
971				$c && out=c || {
972				out=d; };};}
973
974				atf_check -s exit:0 -e empty \
975					-o "inline:${out}"'\n' \
976					${TEST_SH} -c \
977						"if $a; then echo a
978						elif $b; then echo b
979						elif $c; then echo c
980						else echo d
981						fi"
982			done
983		done
984	done
985}
986
987atf_test_case t_loops
988t_loops_head() {
989	atf_set "descr" "Check while/until loop parsing"
990}
991t_loops_body() {
992	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
993		'while false; do :; done'
994	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
995		'while false; do \done; done'
996	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
997		'until :; do :; done'
998	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
999		'until :; do \done; done'
1000
1001	atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \
1002		':; while (exit) do echo x; false; done; echo $?'
1003	atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \
1004		'false; until (exit) do echo x; done; echo $?'
1005}
1006
1007atf_test_case u_case_cont
1008u_case_cont_head() {
1009	atf_set "descr" "Check case stmt parsing using ;& [optional]"
1010}
1011u_case_cont_body() {
1012
1013	${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null ||
1014		atf_skip ";& case list terminator unsupported in ${TEST_SH}"
1015
1016	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1017		'case x in x) ;& esac'
1018	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1019		'case x in (x) ;& esac'
1020	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1021		'case x in x|y) ;& esac'
1022	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1023		'case x in (x|y) ;& esac'
1024
1025	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1026		'case x in x );&esac'
1027	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1028		'case x in ( x );&esac'
1029	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1030		'case x in x | y );&esac'
1031	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1032		'case x in ( x | y );&esac'
1033
1034	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1035		'case x in x) ;& (y) esac'
1036	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1037		'case x in (x) ;& esac'
1038	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1039		'case x in x|y) ;& esac'
1040	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1041		'case x in (x|y) ;& esac'
1042}
1043
1044atf_test_case x_functions
1045x_functions_head() {
1046	atf_set "descr" "Check function definition parsing"
1047}
1048x_functions_body() {
1049	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1050		'func() { :; }'
1051	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1052		'func() { :; }; func'
1053	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1054		'func()(:)'
1055	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1056		'func()if :; then false; else true; fi'
1057	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1058		'func()while false; do :;done'
1059	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1060		'func () for a in b c; do :; done'
1061	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1062		'func() case x in (y) esac'
1063
1064	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1065		'f1() { f2() { :; }; }; f1; f2'
1066
1067	# f2 should be not found, but f1 clears $?
1068	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
1069		'f1() { f2() { :; }; }; f2; f1'
1070
1071	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1072		'f1() { eval "$1() { :; }"; }; f1 f2; f2'
1073}
1074
1075atf_test_case z_PR_48498
1076z_PR_48498_head() {
1077	atf_set "descr" "Check for detecting the syntax error from PR bin/48498"
1078}
1079z_PR_48498_body() {
1080
1081	# reserved words/operators that end things,
1082	# were completely ignored after a ';' or '&'
1083	# many of these tests lifted directly from the PR
1084
1085	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1086		'true; fi'
1087	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1088		'false; fi'
1089	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1090		'false; then echo wut'
1091	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1092		'true; then echo wut'
1093	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1094		'true; do echo wut'
1095	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1096		'true; then'
1097	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1098		'true; else'
1099	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1100		'true; do'
1101	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1102		'true; done'
1103		# {
1104	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1105		': ; }'
1106		# (
1107	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1108		':;)'
1109
1110	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1111		'true& fi'
1112	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1113		'false& fi'
1114	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1115		'false& then echo wut'
1116	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1117		'true& then echo wut'
1118	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1119		'true& do echo wut'
1120	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1121		'true& then'
1122	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1123		'true& else'
1124	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1125		'true& do'
1126	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1127		'true&done'
1128		# {
1129	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1130		':&}'
1131		# (
1132	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1133		':&)'
1134}
1135
1136atf_test_case z_PR_52426
1137z_PR_52426_head() {
1138	atf_set "descr" "Check for detecting the syntax error from PR bin/52426"
1139}
1140z_PR_52426_body() {
1141	# Absoluely anything was permitted as a pattern of a case
1142	# statement, any token (except 'esac') would serve
1143	# What follows are a few "pretty" examples that were accepted.
1144	# The first is the example from the PR
1145
1146	# Note we use only ;; type case lists, ;& should do the same, but
1147	# only for shells that support it, we do not want the shell to
1148	# object to any of these just because it does not support ;&
1149
1150	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1151		'case x in <|() ;; esac'
1152
1153	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1154		'case x in ((|)) ;; esac'
1155	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1156		'case x in _|() ;; esac'
1157	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1158		'case x in ()|() ;; esac'
1159	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1160		'case x in -|;) ;; esac'
1161	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1162		'case x in (;|-) ;; esac'
1163	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1164		'case x in ;;|;) ;; esac'
1165	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1166		'case x in (|| | ||) ;; esac'
1167	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1168		'case x in (<<|>>) ;; esac'
1169	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1170		'case x in (&&|&) ;; (|||>&) ;; &) esac'
1171	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1172		'case x in (>||<) ;; esac'
1173	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1174		'case x in( || | || | || | || | || );; esac'
1175	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1176		'case x in (||| ||| ||| ||| ||) ;; esac'
1177	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1178		'case x in <> |
1179		) ;; esac'
1180
1181	# now check some similar looking cases that are supposed to work
1182	# That is, make sure the fix for the PR does not break valid cases.
1183
1184	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1185		'case fi in ({|}) ;; (!) ;; esac'
1186	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1187		'case esac in ([|]);; (][);; !!!|!!!|!!!|!!!);; esac'
1188	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1189		'case then in ({[]]}) ;; (^^);; (^|^);; ([!]);; (-);; esac'
1190	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1191		'case while in while   );;(if|then|elif|fi);;(do|done);; esac'
1192	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1193		'case until in($);;($$);;($4.50);;(1/2);;0.3333);;esac'
1194	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1195		'case return in !);; !$);; $!);; !#);; (@);; esac'
1196	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1197		'case break in (/);; (\/);; (/\|/\));; (\\//);; esac'
1198}
1199
1200atf_test_case z_PR_53712
1201z_PR_53712_head() {
1202	atf_set "descr" "Check for avoiding the core dump from PR bin/53712"
1203}
1204z_PR_53712_body() {
1205	atf_require_prog sysctl
1206	atf_require_prog rm
1207
1208	# Don't want to have to deal with all the possible ways
1209	# that the systcm might be configured to drop core files...
1210	sysctl -w proc.$$.corename=core ||
1211		atf_skip "Unable to set file name for core dump file"
1212	rm -f core
1213
1214	${TEST_SH} -c '{ } > out'; S=$?
1215	test -f core &&
1216		atf_fail "PR bin/53712: ${TEST_SH} dumps core: status=$S"
1217	test "$S" -lt 128 ||
1218		atf_fail "PR bin/53712: ${TEST_SH} reported status $S (core?)"
1219
1220	# It doesn't matter here whether or not there was an error
1221	# from the empty compound, or whether "out" was created
1222	# just that no core dump appeared, and the shell did not
1223	# exit because of a signal.
1224
1225	return 0
1226}
1227
1228atf_init_test_cases() {
1229	atf_add_test_case a_basic_tokenisation
1230	atf_add_test_case b_comments
1231	atf_add_test_case c_line_wrapping
1232	atf_add_test_case d_cstrings
1233	atf_add_test_case f_redirects
1234	atf_add_test_case g_variable_syntax
1235	atf_add_test_case h_var_assign
1236	atf_add_test_case i_pipelines
1237	atf_add_test_case j_and_or_lists
1238	atf_add_test_case k_lists
1239	atf_add_test_case l_async_lists
1240	atf_add_test_case m_compound_lists
1241	atf_add_test_case q_for_loop
1242	atf_add_test_case r_case
1243	atf_add_test_case s_if
1244	atf_add_test_case t_loops
1245	atf_add_test_case u_case_cont
1246	atf_add_test_case x_functions
1247
1248	atf_add_test_case z_PR_48498
1249	atf_add_test_case z_PR_52426
1250	atf_add_test_case z_PR_53712
1251}
1252