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