xref: /netbsd-src/tests/bin/sh/t_syntax.sh (revision 9985dad5cbf74e75ee5375c0f79c20c06739c61d)
1# $NetBSD: t_syntax.sh,v 1.8 2017/08/19 21:18:47 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		"test \$'\\u0123' = \$'\\304\\243'"
328	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
329		"test \$'\\u0123' = \$'\\xC4\\xA3'"
330	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
331		"test \$'\\c\\\\' = \$'\\x1C'"
332	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
333		"test \$'\\c[\\c]\\c^\\c_\\c?' = \$'\\x1B\\x1D\\x1E\\x1F\\x7F'"
334
335	# all the \X sequences for a single char X (ie: not hex/octal/unicode)
336	atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
337		${TEST_SH} -c "printf '%s\\n' \$'\\a\\b\\e\\f\\n\\r\\t\\v'"
338	atf_check -s exit:0 -e empty -o inline:'\n\r\t\n' \
339	   ${TEST_SH} -c "printf '%s\\n' \$'\\cG\\cH\\x1b\\cl\\cJ\\cm\\cI\\ck'"
340	atf_check -s exit:0 -e empty -o inline:"'"'"\\\n' \
341		${TEST_SH} -c "printf '%s\\n' \$'\\'\\\"\\\\'"
342
343	# various invalid $'...' sequences
344	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
345		": \$'\\q'"
346	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
347		": \$'\\c\\q'"
348	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
349		": \$'\\uDEFF'"
350	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
351		": \$'\\u00'"
352	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
353		": \$'\\u8'"
354	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
355		": \$'abcd"
356	atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \
357		": \$'abcd\\"
358
359	# anything that generates \0 ends the $'...' immediately (\u cannot)
360	atf_check -s exit:0 -e empty -o inline:'aAa' ${TEST_SH} -c \
361		"printf '%s' \$'a\\0x'\$'A\\x00X'\$'a\\c@x'"
362
363	# \newline in a $'...' is dropped (just like in "" strings)
364	atf_check -s exit:0 -e empty -o inline:'abcdef' ${TEST_SH} -c \
365"printf '%s' \$'abc\\
366def'"
367	# but a normal newline in a $'...' is just a newline
368	atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
369"printf '%s' \$'abc
370def'"
371	# and should work when elided line wrap occurs between $ and '
372	atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \
373"printf '%s' \$\\
374'abc\\ndef'"
375
376	# $'...' only works when the $ is unquoted.
377	atf_check -s exit:0 -e empty -o inline:"abc\$'def'g" ${TEST_SH} -c \
378		"printf '%s' \"abc\$'def'g\""
379	atf_check -s exit:0 -e empty -o inline:'abc$defg' ${TEST_SH} -c \
380		"printf '%s' abc\\\$'def'g"
381	atf_check -s exit:0 -e empty -o inline:'abc$def' ${TEST_SH} -c \
382		"printf '%s' abc'\$'def"
383}
384
385atf_test_case f_redirects
386f_redirects_head() {
387	atf_set "descr" "Check parsing of redirect operators"
388}
389f_redirects_body() {
390
391	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
392		'>/dev/null'
393	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
394		'</dev/null'
395	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
396		'>>/dev/null'
397	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
398		'<>/dev/null'
399	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
400		'</dev/null>/dev/null'
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>/dev/null>/dev/null'
405
406	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
407		'echo hello >/dev/null'
408	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
409		'echo >/dev/null hello'
410	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
411		'>/dev/null echo hello'
412	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
413		'echo hello >/dev/null world'
414	atf_check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \
415		'echo hello </dev/null world'
416
417	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
418		'echo hello </dev/null'
419	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
420		'echo hello 3</dev/null'
421	atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
422		'echo hello 3 </dev/null'
423	atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
424		'echo hello \3</dev/null'
425	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
426		'echo hello</dev/null'
427	atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
428		'hello=3; echo ${hello}</dev/null'
429
430	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
431		'2>&1'
432	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
433		'2>& 1'
434	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
435		'FD=1; 2>&"${FD}"'
436	atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
437		'FD=1; echo hello 2>&"${FD}" >&2'
438
439	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
440		'2>&- 3<&- 4>&-'
441
442	return 0
443}
444
445atf_test_case g_variable_syntax
446g_variable_syntax_head() {
447	atf_set "descr" "Check that var names of all legal forms work"
448}
449g_variable_syntax_body() {
450	# don't test _ as a variable, it can be "unusual"
451	for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \
452	    A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \
453	    abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \
454	    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 \
455	    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 \
456	    xyzzy __0123454321__ _0_1_2_3_4_5_6_7_8_9_ ABBA X_ Y__ Z___ \
457	    _____________________________________________________________
458	do
459		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
460			"unset ${vname}"
461		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
462			"unset ${vname}; printf %s \${${vname}-OK}"
463		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
464			"${vname}=GOOD; printf %s \${${vname}-OK}"
465		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
466			"${vname}=GOOD; printf %s \$${vname}"
467		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
468			"unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}"
469		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
470			"${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}"
471		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
472			"${vname}=GOOD; unset ${vname}x; printf %s \$${vname}"
473		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
474			"unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x"
475		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
476			"${vname}=GOOD; ${vname}_=BAD; printf %s \$${vname}"
477
478		case "${vname}" in
479		?)	continue;;
480		esac
481
482		# The following tests do not work for 1 char var names.
483		# hence the check and "continue" above to skip the remaining
484		# tests for that case
485
486		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
487			"${vname}=GOOD; unset ${vname%?}; printf %s \$${vname}"
488
489		# (this next would work, but becomes just a duplicate of
490		# an earlier test, so is pointless for 1 ch names)
491		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
492	"${vname}=GOOD; unset ${vname}x ${vname%?}; printf %s \$${vname}"
493
494		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
495			"unset ${vname%?};${vname}=GOOD; printf %s \$${vname%?}"
496
497		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
498			"${vname}=GOOD; ${vname%?}=BAD; printf %s \$${vname}"
499
500		# all the remaining tests require the 2nd char of the
501		# variable name to be a legal first character.  That
502		# is, not a digit, so skip the rest if we have a digit
503		# second...
504		case "${vname}" in
505		?[0-9]*)	continue;;
506		esac
507
508		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
509			"${vname}=GOOD; unset ${vname#?}; printf %s \$${vname}"
510		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
511			"unset ${vname#?};${vname}=GOOD; printf %s \$${vname#?}"
512
513		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
514			"${vname}=GOOD; ${vname#?}=BAD; printf %s \$${vname}"
515
516		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
517			"unset ${vname%?} ${vname#?} ${vname}x; ${vname}=GOOD;
518			printf %s \$${vname%?}\$${vname#?}\$${vname}x"
519
520		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
521			"${vname}=GOOD; ${vname%?}=BAD; ${vname}_=BAD;
522			${vname#?}=BAD; printf %s \$${vname}"
523	done
524
525	# don't test '.' in var names, some shells permit that (in ${} anyway)
526	# this test also cannot check for embedded - + ? = : % or #
527	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';'
528	do
529		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
530			"echo \${${vname}}"
531	done
532
533	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \
534	    ';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?'
535	do
536		# failure modes will differ, but they should all fail somehow
537		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
538			"${vname}="
539	done
540
541}
542
543atf_test_case h_var_assign
544h_var_assign_head() {
545	atf_set "descr" "Check var assignments "
546}
547h_var_assign_body() {
548	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
549		'a=b'
550	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
551		'\a=b'
552	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
553		'a=b c=d'
554	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
555		'a=b c=d echo e=f'
556	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
557		'a=b 2>/dev/null c=d </dev/null echo e=f'
558
559	# We need to make sure that we do not accidentally
560	# find a command called 'a=b' ...
561
562	for d in /foo /foo/bar /not-dir /no/such/directory '/!!!' \
563		'/-/--/#' '/"/""/"""' -
564	do
565		test -d "${d}" || break
566	done
567	test "${#d}" -gt 1 || atf-skip 'Wacky directories seem to exist!'
568
569	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
570		"PATH='${d}';"'a=\b'
571	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
572		"PATH='${d}';"'a\=b'
573	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
574		"PATH='${d}';"'\a=b'
575	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
576		"PATH='${d}';"'X=; c=d ${X} a=b'
577}
578
579atf_test_case i_pipelines
580i_pipelines_head() {
581	atf_set "descr" "Check pipelines"
582}
583i_pipelines_body() {
584
585	cmd='printf "%s\n" foo'
586	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
587	do
588		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
589			"${cmd}"
590		cmd="${cmd} | cat"
591	done
592
593	cmd='printf "%s\n" foo'
594	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
595	do
596		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
597			"${cmd}"
598		cmd="${cmd} |
599		cat"
600	done
601
602	cmd='printf "%s\n" foo'
603	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
604	do
605		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
606			"${cmd}"
607		cmd="${cmd} |
608
609
610
611
612		cat"
613	done
614
615	cmd='! printf "%s\n" foo'
616	for n in 1 2 3 4 5 6 7 8 9 10
617	do
618		atf_check -s exit:1 -o inline:'foo\n' -e empty ${TEST_SH} -c \
619			"${cmd}"
620		cmd="${cmd} | cat"
621	done
622
623	cmd='exec 4>&2 3<&0; printf "%s\n" foo'
624	for n in 1 2 3
625	do
626	    pfx=
627	    for xtra in 'x=y' 'a=b' '6>&1' '5<&3'
628	    do
629		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
630			"${cmd} | ${xtra} cat"
631
632		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
633			"${cmd} | ${pfx} cat"
634
635		pfx="${pfx} ${xtra}"
636	    done
637	    cmd="${cmd} | ${pfx} cat"
638	done
639
640	# pipelines are not required to contain commands ...
641	# they don't do anything useful (at all) but the syntax is legal
642	base='4>&2'; cmd="${base}"
643	for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat'
644	do
645		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
646			"${base} | ${pipe}"
647
648		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
649			"${cmd} | ${pipe}"
650
651		cmd="${cmd} | ${pipe}"
652	done
653
654	# but the command cannot be empty, or a reserved word used improperly
655	base='printf "%s\n" foo'; cmd="${base}"
656	for pipe in '' do done then else fi esac
657	do
658		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
659			"${base} | ${pipe}"
660
661		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
662			"${pipe} | ${base}"
663
664		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
665			"${cmd} | ${pipe}"
666
667		cmd="${cmd} | ${pipe}"
668	done
669}
670
671atf_test_case j_and_or_lists
672j_and_or_lists_head() {
673	atf_set "descr" "Check && and || command lists"
674}
675j_and_or_lists_body() {
676	and=true
677	or=false
678	and_or=false
679	for i in 1 2 3 4 5 6 7 8 9 10
680	do
681		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
682			"${and}"
683
684		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
685			"${or}"
686
687		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
688			"${and_or}"
689
690		and="${and} && true"
691		or="${or} || false"
692		and_or="${and_or} || true && false"
693	done
694
695	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
696		'true &&'
697	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
698		'&& true'
699	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
700		'|| true'
701	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
702		'true ||'
703	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
704		'true || && false'
705	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
706		'false || && true'
707
708	cmd='printf "%s" foo | cat | cat>/dev/null'
709	line="${cmd}"
710	for i in 1 2 3 4
711	do
712		line="${line} && ! ${cmd} || ${cmd}"
713
714		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
715			"${line}"
716	done
717
718}
719
720atf_test_case k_lists
721k_lists_head() {
722	atf_set "descr" "Check ; command lists"
723}
724k_lists_body() {
725	line=
726	for N in 1 2 3 4
727	do
728		for cmd in \
729			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
730		do
731			line="${line}${line:+;}${cmd}"
732			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
733				-e empty ${TEST_SH} -c \
734					"echo hello; ${line}; echo world"
735			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
736				-e empty ${TEST_SH} -c \
737					"echo hello; ${line}; echo world;"
738		done
739	done
740
741	for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true'
742	do
743		atf_check -s not-exit:0 -o ignore -e not-empty \
744			${TEST_SH} -c "${cmd}"
745	done
746}
747
748atf_test_case l_async_lists
749l_async_lists_head() {
750	atf_set "descr" "Check & command lists"
751}
752l_async_lists_body() {
753	line=
754	for N in 1 2 3 4
755	do
756		for cmd in \
757			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
758		do
759			line="${line:+${line}&}${cmd}"
760			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
761				-e empty ${TEST_SH} -c \
762					"echo hello; ${line}& echo world"
763			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
764				-e empty ${TEST_SH} -c \
765					"echo hello; ${line}& echo world"
766		done
767	done
768
769	for cmd in '&' ';&' '&;'  '& true' 'false& &true'
770	do
771		atf_check -s not-exit:0 -o ignore -e not-empty \
772			${TEST_SH} -c "${cmd}"
773	done
774}
775
776atf_test_case m_compound_lists
777m_compound_lists_head() {
778	atf_set "descr" "Check subshells () and { ;} command grouping"
779}
780m_compound_lists_body() {
781	# Note: (( is an unspecified (reserved) operator, don't use it...
782	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
783		'( true )'
784	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
785		'( false )'
786	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
787		'( (:) )'
788	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
789		'( ( true ))'
790	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
791		'( ( ( ( (  true )))))'
792	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
793		'( ( ( ( (true);:));true))'
794
795	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
796		'()'
797	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
798		'(   )'
799
800	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
801		'{ true; }'
802	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
803		'{ false; }'
804	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
805		'{ { :; };}'
806	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
807		'{ { { { {  :;};};};};}'
808
809	atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \
810		'{ echo } ; }'
811	atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \
812		'{ echo { ; }'
813}
814
815atf_test_case q_for_loop
816q_for_loop_head() {
817	atf_set "descr" "Check for loop parsing"
818}
819q_for_loop_body() {
820	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
821		'for x; do : ; done'
822	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
823		'for x in ; do : ; done'
824	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
825		'for x in a b c ; do : ; done'
826
827	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
828		'for x in in;do : ; done'
829	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
830		'for x in for;do : ; done'
831	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
832		'for x in do;do : ; done'
833	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
834		'for for in in;do :;done'
835	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
836		'for for in for;do :; done'
837	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
838		'for for in do;do : ;done'
839	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
840		'for in in in;do : ; done'
841	atf_check -s exit:0 -o 'inline:do\nin\ndo\n' -e empty ${TEST_SH} -c \
842   'for in in in do in;do case $in in in)echo do;;do)echo in;;esac; done'
843	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
844		'for in in for;do : ; done'
845	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
846		'for in in do;do : ; done'
847	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
848		'for do in in;do : ; done'
849	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
850		'for do in for;do : ; done'
851	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
852		'for do in do;do : ; done'
853	atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \
854		'for do in do;do echo ${do}do ; done'
855}
856
857atf_test_case r_case
858r_case_head() {
859	atf_set "descr" "Check case statement parsing"
860}
861r_case_body() {
862	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
863		'case x in  esac'
864	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
865		'case x in x) esac'
866	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
867		'case x in (x) esac'
868	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
869		'case x in x) ;; esac'
870	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
871		'case x in (x) ;; esac'
872	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
873		'case x in x|y) ;; esac'
874	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
875		'case x in (x|y) ;; esac'
876
877	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
878		'case x in x|esac) ;; esac'
879	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
880		'case x in x|esac|y) ;; esac'
881	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
882		'case x in (x|esac) ;; esac'
883	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
884		'case x in (x|esac|y) ;; esac'
885
886	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
887		'case x in in) esac'
888	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
889		'case x in in) ;; esac'
890	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
891		'case x in x|in) ;; esac'
892	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
893		'case x in x|in|y) ;; esac'
894	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
895		'case x in (x|in) ;; esac'
896	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
897		'case x in (in|x) ;; esac'
898	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
899		'case x in (x|in|y) ;; esac'
900
901	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
902		'case case in case) esac'
903	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
904		'case in in in) esac'
905	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
906		'case esac in (in) esac'
907	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
908		'case in in esac|cat'
909	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
910		'case esac in esac|cat'
911	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
912		'case in in esac|case x in u) echo foo;; esac'
913	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
914		'case esac in esac|case x in u) echo foo;; esac'
915	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
916		'case in in esac|case x in x) echo foo;; esac'
917
918	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
919		'case in in (esac|cat'
920
921	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
922		'case x in x );;esac'
923	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
924		'case x in ( x );;esac'
925	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
926		'case x in x | y );;esac'
927	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
928		'case x in ( x | y );;esac'
929
930	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
931		'case x
932		 in
933		 (    x    |    y    )
934
935			;;
936
937
938		esac
939		'
940}
941
942atf_test_case s_if
943s_if_head() {
944	atf_set "descr" "Check if statement parsing"
945}
946s_if_body() {
947	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
948		'if :; then :; fi'
949	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
950		'if :; then :; else :; fi'
951	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
952		'if :; then :; elif :; then :; else :; fi'
953	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
954		'if :; then :; elif :; then :; elif :; then :; else :; fi'
955
956	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
957		'if :; then : else :; fi'
958	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
959		'if : then :; then :; fi'
960
961	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
962		'if if :;then :;fi then :;fi'
963	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
964		'if if :;then if :;then :;fi fi;then :;fi'
965	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
966		'if if :;then :;fi;then :;else if :;then :;fi fi'
967
968	for a in true false; do
969		for b in true false; do
970			for c in true false; do
971
972				$a && out=a || {
973				$b && out=b || {
974				$c && out=c || {
975				out=d; };};}
976
977				atf_check -s exit:0 -e empty \
978					-o "inline:${out}"'\n' \
979					${TEST_SH} -c \
980						"if $a; then echo a
981						elif $b; then echo b
982						elif $c; then echo c
983						else echo d
984						fi"
985			done
986		done
987	done
988}
989
990atf_test_case t_loops
991t_loops_head() {
992	atf_set "descr" "Check while/until loop parsing"
993}
994t_loops_body() {
995	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
996		'while false; do :; done'
997	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
998		'while false; do \done; done'
999	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1000		'until :; do :; done'
1001	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1002		'until :; do \done; done'
1003
1004	atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \
1005		':; while (exit) do echo x; false; done; echo $?'
1006	atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \
1007		'false; until (exit) do echo x; done; echo $?'
1008}
1009
1010atf_test_case u_case_cont
1011u_case_cont_head() {
1012	atf_set "descr" "Check case stmt parsing using ;& [optional]"
1013}
1014u_case_cont_body() {
1015
1016	${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null ||
1017		atf_skip ";& case list terminator unsupported in ${TEST_SH}"
1018
1019	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1020		'case x in x) ;& esac'
1021	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1022		'case x in (x) ;& esac'
1023	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1024		'case x in x|y) ;& esac'
1025	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1026		'case x in (x|y) ;& esac'
1027
1028	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1029		'case x in x );&esac'
1030	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1031		'case x in ( x );&esac'
1032	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1033		'case x in x | y );&esac'
1034	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1035		'case x in ( x | y );&esac'
1036
1037	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1038		'case x in x) ;& (y) esac'
1039	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1040		'case x in (x) ;& esac'
1041	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1042		'case x in x|y) ;& esac'
1043	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1044		'case x in (x|y) ;& esac'
1045}
1046
1047atf_test_case x_functions
1048x_functions_head() {
1049	atf_set "descr" "Check function definition parsing"
1050}
1051x_functions_body() {
1052	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1053		'func() { :; }'
1054	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1055		'func() { :; }; func'
1056	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1057		'func()(:)'
1058	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1059		'func()if :; then false; else true; fi'
1060	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1061		'func()while false; do :;done'
1062	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1063		'func () for a in b c; do :; done'
1064	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1065		'func() case x in (y) esac'
1066
1067	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1068		'f1() { f2() { :; }; }; f1; f2'
1069
1070	# f2 should be not found, but f1 clears $?
1071	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
1072		'f1() { f2() { :; }; }; f2; f1'
1073
1074	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1075		'f1() { eval "$1() { :; }"; }; f1 f2; f2'
1076}
1077
1078atf_test_case z_PR_48498
1079z_PR_48498_head() {
1080	atf_set "descr" "Check for detecting the syntax error from PR bin/48498"
1081}
1082z_PR_48498_body() {
1083
1084	# reserved words/operators that end things,
1085	# were completely ignored after a ';' or '&'
1086	# many of these tests lifted directly from the PR
1087
1088	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1089		'true; fi'
1090	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1091		'false; fi'
1092	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1093		'false; then echo wut'
1094	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1095		'true; then echo wut'
1096	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1097		'true; do echo wut'
1098	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1099		'true; then'
1100	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1101		'true; else'
1102	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1103		'true; do'
1104	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1105		'true; done'
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		':;)'
1112
1113	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1114		'true& fi'
1115	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1116		'false& fi'
1117	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1118		'false& then echo wut'
1119	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1120		'true& then echo wut'
1121	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1122		'true& do echo wut'
1123	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1124		'true& then'
1125	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1126		'true& else'
1127	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1128		'true& do'
1129	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1130		'true&done'
1131		# {
1132	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1133		':&}'
1134		# (
1135	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1136		':&)'
1137}
1138
1139atf_test_case z_PR_52426
1140z_PR_52426_head() {
1141	atf_set "descr" "Check for detecting the syntax error from PR bin/52426"
1142}
1143z_PR_52426_body() {
1144	# Absoluely anything was permitted as a pattern of a case
1145	# statement, any token (except 'esac') would serve
1146	# What follows are a few "pretty" examples that were accepted.
1147	# The first is the example from the PR
1148
1149	# Note we use only ;; type case lists, ;& should do the same, but
1150	# only for shells that support it, we do not want the shell to
1151	# object to any of these just because it does not support ;&
1152
1153	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1154		'case x in <|() ;; esac'
1155
1156	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1157		'case x in ((|)) ;; esac'
1158	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1159		'case x in _|() ;; esac'
1160	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1161		'case x in ()|() ;; esac'
1162	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1163		'case x in -|;) ;; esac'
1164	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
1165		'case x in (;|-) ;; esac'
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 <> |
1182		) ;; esac'
1183
1184	# now check some similar looking cases that are supposed to work
1185	# That is, make sure the fix for the PR does not break valid cases.
1186
1187	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1188		'case fi in ({|}) ;; (!) ;; esac'
1189	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1190		'case esac in ([|]);; (][);; !!!|!!!|!!!|!!!);; esac'
1191	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1192		'case then in ({[]]}) ;; (^^);; (^|^);; ([!]);; (-);; esac'
1193	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1194		'case while in while   );;(if|then|elif|fi);;(do|done);; esac'
1195	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1196		'case until in($);;($$);;($4.50);;(1/2);;0.3333);;esac'
1197	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1198		'case return in !);; !$);; $!);; !#);; (@);; esac'
1199	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
1200		'case break in (/);; (\/);; (/\|/\));; (\\//);; esac'
1201}
1202
1203atf_init_test_cases() {
1204	atf_add_test_case a_basic_tokenisation
1205	atf_add_test_case b_comments
1206	atf_add_test_case c_line_wrapping
1207	atf_add_test_case d_cstrings
1208	atf_add_test_case f_redirects
1209	atf_add_test_case g_variable_syntax
1210	atf_add_test_case h_var_assign
1211	atf_add_test_case i_pipelines
1212	atf_add_test_case j_and_or_lists
1213	atf_add_test_case k_lists
1214	atf_add_test_case l_async_lists
1215	atf_add_test_case m_compound_lists
1216	atf_add_test_case q_for_loop
1217	atf_add_test_case r_case
1218	atf_add_test_case s_if
1219	atf_add_test_case t_loops
1220	atf_add_test_case u_case_cont
1221	atf_add_test_case x_functions
1222
1223	atf_add_test_case z_PR_48498
1224	atf_add_test_case z_PR_52426
1225}
1226