Home | History | Annotate | Line # | Download | only in sh
t_syntax.sh revision 1.1
      1 # $NetBSD: t_syntax.sh,v 1.1 2017/05/20 16:35:55 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 
     47 atf_test_case a_basic_tokenisation
     48 a_basic_tokenisation_head() {
     49 	atf_set "descr" "Test the shell correctly finds various tokens"
     50 }
     51 a_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 
     73 atf_test_case b_comments
     74 b_comments_head() {
     75 	atf_set "descr" "Test the shell correctly handles comments"
     76 }
     77 b_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 
    129 atf_test_case c_line_wrapping
    130 c_line_wrapping_head() {
    131 	atf_set "descr" "check aspects of command line wrapping"
    132 }
    133 c_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 	cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH}
    218 		l\
    219 		s=7 bi\
    220 		n\
    221 		=\
    222 		3
    223 		echo $(\
    224 		( ls /bin )\
    225 		)
    226 	DONE
    227 }
    228 
    229 atf_test_case d_redirects
    230 d_redirects_head() {
    231 	atf_set "descr" "Check parsing of redirect operators"
    232 }
    233 d_redirects_body() {
    234 
    235 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    236 		'>/dev/null'
    237 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    238 		'</dev/null'
    239 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    240 		'>>/dev/null'
    241 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    242 		'<>/dev/null'
    243 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    244 		'</dev/null>/dev/null'
    245 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    246 		'>|/dev/null'
    247 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    248 		'>/dev/null>/dev/null>/dev/null'
    249 
    250 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    251 		'echo hello >/dev/null'
    252 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    253 		'echo >/dev/null hello'
    254 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    255 		'>/dev/null echo hello'
    256 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    257 		'echo hello >/dev/null world'
    258 	atf-check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \
    259 		'echo hello </dev/null world'
    260 
    261 	atf-check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
    262 		'echo hello </dev/null'
    263 	atf-check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
    264 		'echo hello 3</dev/null'
    265 	atf-check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
    266 		'echo hello 3 </dev/null'
    267 	atf-check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
    268 		'echo hello \3</dev/null'
    269 	atf-check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
    270 		'echo hello</dev/null'
    271 	atf-check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
    272 		'hello=3; echo ${hello}</dev/null'
    273 
    274 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    275 		'2>&1'
    276 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    277 		'2>& 1'
    278 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    279 		'FD=1; 2>&"${FD}"'
    280 	atf-check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
    281 		'FD=1; echo hello 2>&"${FD}" >&2'
    282 
    283 	atf-check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    284 		'2>&- 3<&- 4>&-'
    285 
    286 	return 0
    287 }
    288 
    289 atf_test_case f_variable_syntax
    290 f_variable_syntax_head() {
    291 	atf_set "descr" "Check that var names of all legal forms work"
    292 }
    293 f_variable_syntax_body() {
    294 	# don't test _ as a variable, it can be "unusual"
    295 	for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \
    296 	    A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \
    297 	    abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \
    298 	    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 xyzzy \
    299 	    _____________________________________________________________
    300 	do
    301 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    302 			"unset ${vname}"
    303 		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
    304 			"unset ${vname}; printf %s \${${vname}-OK}"
    305 		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
    306 			"${vname}=GOOD; printf %s \${${vname}-OK}"
    307 		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
    308 			"${vname}=GOOD; printf %s \$${vname}"
    309 		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
    310 			"unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}"
    311 		atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
    312 			"${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}"
    313 		atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
    314 			"${vname}=GOOD; unset ${vname}x; printf %s \$${vname}"
    315 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    316 			"unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x"
    317 	done
    318 
    319 	# don't test '.' in var names, some shells permit that (in ${} anyway)
    320 	# this test also cannot check for embedded - + ? = : % or #
    321 	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';'
    322 	do
    323 		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
    324 			"echo \${${vname}}"
    325 	done
    326 
    327 	for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \
    328 	    ';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?'
    329 	do
    330 		# failure modes will differ, but they should all fail somehow
    331 		atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
    332 			"${vname}="
    333 	done
    334 
    335 }
    336 
    337 atf_test_case g_var_assign
    338 g_var_assign_head() {
    339 	atf_set "descr" "Check var assignments "
    340 }
    341 g_var_assign_body() {
    342 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
    343 		'a=b'
    344 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    345 		'\a=b'
    346 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
    347 		'a=b c=d'
    348 	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
    349 		'a=b c=d echo e=f'
    350 	atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
    351 		'a=b 2>/dev/null c=d </dev/null echo e=f'
    352 
    353 	# We need to make sure that we do not accidentally
    354 	# find a command called 'a=b' ...
    355 
    356 	for d in /foo /foo/bar /not-dir /no/such/directory '/!!!' \
    357 		'/-/--/#' '/"/""/"""' - 
    358 	do
    359 		test -d "${d}" || break
    360 	done
    361 	test "${#d}" -gt 1 || atf-skip 'Wacky directories seem to exist!'
    362 
    363 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
    364 		"PATH='${d}';"'a=\b'
    365 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    366 		"PATH='${d}';"'a\=b'
    367 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    368 		"PATH='${d}';"'\a=b'
    369 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    370 		"PATH='${d}';"'X=; c=d ${X} a=b'
    371 }
    372 
    373 atf_test_case i_pipelines
    374 i_pipelines_head() {
    375 	atf_set "descr" "Check pipelines"
    376 }
    377 i_pipelines_body() {
    378 
    379 	cmd='printf "%s\n" foo'
    380 	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    381 	do
    382 		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    383 			"${cmd}"
    384 		cmd="${cmd} | cat"
    385 	done
    386 
    387 	cmd='printf "%s\n" foo'
    388 	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    389 	do
    390 		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    391 			"${cmd}"
    392 		cmd="${cmd} |
    393 		cat"
    394 	done
    395 
    396 	cmd='printf "%s\n" foo'
    397 	for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    398 	do
    399 		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    400 			"${cmd}"
    401 		cmd="${cmd} |
    402 
    403 
    404 
    405 
    406 		cat"
    407 	done
    408 
    409 	cmd='! printf "%s\n" foo'
    410 	for n in 1 2 3 4 5 6 7 8 9 10
    411 	do
    412 		atf_check -s exit:1 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    413 			"${cmd}"
    414 		cmd="${cmd} | cat"
    415 	done
    416 
    417 	cmd='exec 4>&2 3<&0; printf "%s\n" foo'
    418 	for n in 1 2 3
    419 	do
    420 	    pfx=
    421 	    for xtra in 'x=y' 'a=b' '6>&1' '5<&3'
    422 	    do
    423 		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    424 			"${cmd} | ${xtra} cat"
    425 
    426 		atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
    427 			"${cmd} | ${pfx} cat"
    428 
    429 		pfx="${pfx} ${xtra}"
    430 	    done
    431 	    cmd="${cmd} | ${pfx} cat"
    432 	done
    433 
    434 	# pipelines are not required to contain commands ...
    435 	# they don't do anything useful (at all) but the syntax is legal
    436 	base='4>&2'; cmd="${base}"
    437 	for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat'
    438 	do
    439 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    440 			"${base} | ${pipe}"
    441 
    442 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    443 			"${cmd} | ${pipe}"
    444 
    445 		cmd="${cmd} | ${pipe}"
    446 	done
    447 
    448 	# but the command cannot be empty, or a reserved word used improperly
    449 	base='printf "%s\n" foo'; cmd="${base}"
    450 	for pipe in '' do done then else fi esac
    451 	do
    452 		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    453 			"${base} | ${pipe}"
    454 
    455 		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    456 			"${pipe} | ${base}"
    457 
    458 		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    459 			"${cmd} | ${pipe}"
    460 
    461 		cmd="${cmd} | ${pipe}"
    462 	done
    463 }
    464 
    465 atf_test_case j_and_or_lists
    466 j_and_or_lists_head() {
    467 	atf_set "descr" "Check && and || command lists"
    468 }
    469 j_and_or_lists_body() {
    470 	and=true
    471 	or=false
    472 	and_or=false
    473 	for i in 1 2 3 4 5 6 7 8 9 10
    474 	do
    475 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    476 			"${and}"
    477 
    478 		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    479 			"${or}"
    480 
    481 		atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    482 			"${and_or}"
    483 
    484 		and="${and} && true"
    485 		or="${or} || false"
    486 		and_or="${and_or} || true && false"
    487 	done
    488 
    489 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    490 		'true &&'
    491 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    492 		'&& true'
    493 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    494 		'|| true'
    495 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    496 		'true ||'
    497 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    498 		'true || && false'
    499 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    500 		'false || && true'
    501 
    502 	cmd='printf "%s" foo | cat | cat>/dev/null'
    503 	line="${cmd}"
    504 	for i in 1 2 3 4
    505 	do
    506 		line="${line} && ! ${cmd} || ${cmd}"
    507 
    508 		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    509 			"${line}"
    510 	done
    511 
    512 }
    513 
    514 atf_test_case k_lists
    515 k_lists_head() {
    516 	atf_set "descr" "Check ; command lists"
    517 }
    518 k_lists_body() {
    519 	line=
    520 	for N in 1 2 3 4
    521 	do
    522 		for cmd in \
    523 			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
    524 		do
    525 			line="${line}${line:+;}${cmd}"
    526 			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
    527 				-e empty ${TEST_SH} -c \
    528 					"echo hello; ${line}; echo world"
    529 			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
    530 				-e empty ${TEST_SH} -c \
    531 					"echo hello; ${line}; echo world;"
    532 		done
    533 	done
    534 
    535 	for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true'
    536 	do
    537 		atf_check -s not-exit:0 -o ignore -e not-empty \
    538 			${TEST_SH} -c "${cmd}"
    539 	done
    540 }
    541 
    542 atf_test_case l_async_lists
    543 l_async_lists_head() {
    544 	atf_set "descr" "Check & command lists"
    545 }
    546 l_async_lists_body() {
    547 	line=
    548 	for N in 1 2 3 4
    549 	do
    550 		for cmd in \
    551 			true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
    552 		do
    553 			line="${line:+${line}&}${cmd}"
    554 			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
    555 				-e empty ${TEST_SH} -c \
    556 					"echo hello; ${line}& echo world"
    557 			atf_check -s exit:0 -o 'inline:hello\nworld\n' \
    558 				-e empty ${TEST_SH} -c \
    559 					"echo hello; ${line}& echo world"
    560 		done
    561 	done
    562 
    563 	for cmd in '&' ';&' '&;'  '& true' 'false& &true'
    564 	do
    565 		atf_check -s not-exit:0 -o ignore -e not-empty \
    566 			${TEST_SH} -c "${cmd}"
    567 	done
    568 }
    569 
    570 atf_test_case m_compound_lists
    571 m_compound_lists_head() {
    572 	atf_set "descr" "Check subshells () and { ;} command grouping"
    573 }
    574 m_compound_lists_body() {
    575 	# Note: (( is an unspecified (reserved) operator, don't use it...
    576 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    577 		'( true )'
    578 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    579 		'( false )'
    580 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    581 		'( (:) )'
    582 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    583 		'( ( true ))'
    584 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    585 		'( ( ( ( (  true )))))'
    586 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    587 		'( ( ( ( (true);:));true))'
    588 
    589 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    590 		'()'
    591 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    592 		'(   )'
    593 
    594 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    595 		'{ true; }'
    596 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    597 		'{ false; }'
    598 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    599 		'{ { :; };}'
    600 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    601 		'{ { { { {  :;};};};};}'
    602 
    603 	atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \
    604 		'{ echo } ; }'
    605 	atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \
    606 		'{ echo { ; }'
    607 }
    608 
    609 atf_test_case q_for_loop
    610 q_for_loop_head() {
    611 	atf_set "descr" "Check for loop parsing"
    612 }
    613 q_for_loop_body() {
    614 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    615 		'for x; do : ; done'
    616 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    617 		'for x in ; do : ; done'
    618 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    619 		'for x in a b c ; do : ; done'
    620 
    621 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    622 		'for x in in;do : ; done'
    623 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    624 		'for x in for;do : ; done'
    625 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    626 		'for x in do;do : ; done'
    627 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    628 		'for for in in;do :;done'
    629 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    630 		'for for in for;do :; done'
    631 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    632 		'for for in do;do : ;done'
    633 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    634 		'for in in in;do : ; done'
    635 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    636 		'for in in for;do : ; done'
    637 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    638 		'for in in do;do : ; done'
    639 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    640 		'for do in in;do : ; done'
    641 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    642 		'for do in for;do : ; done'
    643 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    644 		'for do in do;do : ; done'
    645 	atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \
    646 		'for do in do;do echo ${do}do ; done'
    647 }
    648 
    649 atf_test_case r_case
    650 r_case_head() {
    651 	atf_set "descr" "Check case statement parsing"
    652 }
    653 r_case_body() {
    654 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    655 		'case x in  esac'
    656 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    657 		'case x in x) esac'
    658 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    659 		'case x in (x) esac'
    660 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    661 		'case x in x) ;; esac'
    662 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    663 		'case x in (x) ;; esac'
    664 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    665 		'case x in x|y) ;; esac'
    666 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    667 		'case x in (x|y) ;; esac'
    668 
    669 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    670 		'case x in x|esac) ;; esac'
    671 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    672 		'case x in x|esac|y) ;; esac'
    673 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    674 		'case x in (x|esac) ;; esac'
    675 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    676 		'case x in (x|esac|y) ;; esac'
    677 
    678 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    679 		'case x in in) esac'
    680 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    681 		'case x in in) ;; esac'
    682 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    683 		'case x in x|in) ;; esac'
    684 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    685 		'case x in x|in|y) ;; esac'
    686 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    687 		'case x in (x|in) ;; esac'
    688 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    689 		'case x in (in|x) ;; esac'
    690 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    691 		'case x in (x|in|y) ;; esac'
    692 
    693 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    694 		'case case in case) esac'
    695 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    696 		'case in in in) esac'
    697 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    698 		'case esac in (in) esac'
    699 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    700 		'case in in esac|cat'
    701 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    702 		'case esac in esac|cat'
    703 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    704 		'case in in esac|case x in u) echo foo;; esac'
    705 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    706 		'case esac in esac|case x in u) echo foo;; esac'
    707 	atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
    708 		'case in in esac|case x in x) echo foo;; esac'
    709 
    710 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    711 		'case in in (esac|cat'
    712 
    713 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    714 		'case x in x );;esac'
    715 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    716 		'case x in ( x );;esac'
    717 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    718 		'case x in x | y );;esac'
    719 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    720 		'case x in ( x | y );;esac'
    721 
    722 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    723 		'case x
    724 		 in
    725 		 (    x    |    y    )
    726 
    727 			;;
    728 
    729 
    730 		esac
    731 		'
    732 }
    733 
    734 atf_test_case s_if
    735 s_if_head() {
    736 	atf_set "descr" "Check if statement parsing"
    737 }
    738 s_if_body() {
    739 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    740 		'if :; then :; fi'
    741 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    742 		'if :; then :; else :; fi'
    743 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    744 		'if :; then :; elif :; then :; else :; fi'
    745 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    746 		'if :; then :; elif :; then :; elif :; then :; else :; fi'
    747 
    748 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    749 		'if :; then : else :; fi'
    750 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    751 		'if : then :; then :; fi'
    752 
    753 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    754 		'if if :;then :;fi then :;fi'
    755 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    756 		'if if :;then if :;then :;fi fi;then :;fi'
    757 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    758 		'if if :;then :;fi;then :;else if :;then :;fi fi'
    759 
    760 	for a in true false; do
    761 		for b in true false; do
    762 			for c in true false; do
    763 
    764 				$a && out=a || {
    765 				$b && out=b || {
    766 				$c && out=c || {
    767 				out=d; };};}
    768 
    769 				atf_check -s exit:0 -e empty \
    770 					-o "inline:${out}"'\n' \
    771 					${TEST_SH} -c \
    772 						"if $a; then echo a
    773 						elif $b; then echo b
    774 						elif $c; then echo c
    775 						else echo d
    776 						fi"
    777 			done
    778 		done
    779 	done
    780 }
    781 
    782 atf_test_case t_loops
    783 t_loops_head() {
    784 	atf_set "descr" "Check while/until loop parsing"
    785 }
    786 t_loops_body() {
    787 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    788 		'while false; do :; done'
    789 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    790 		'while false; do \done; done'
    791 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    792 		'until :; do :; done'
    793 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    794 		'until :; do \done; done'
    795 
    796 	atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \
    797 		':; while (exit) do echo x; false; done; echo $?'
    798 	atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \
    799 		'false; until (exit) do echo x; done; echo $?'
    800 }
    801 
    802 atf_test_case u_case_cont
    803 u_case_cont_head() {
    804 	atf_set "descr" "Check case stmt parsing using ;& [optional]"
    805 }
    806 u_case_cont_body() {
    807 
    808 	${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null ||
    809 		atf_skip ";& case list terminator unsupported in ${TEST_SH}"
    810 
    811 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    812 		'case x in x) ;& esac'
    813 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    814 		'case x in (x) ;& esac'
    815 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    816 		'case x in x|y) ;& esac'
    817 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    818 		'case x in (x|y) ;& esac'
    819 
    820 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    821 		'case x in x );&esac'
    822 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    823 		'case x in ( x );&esac'
    824 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    825 		'case x in x | y );&esac'
    826 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    827 		'case x in ( x | y );&esac'
    828 
    829 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    830 		'case x in x) ;& (y) esac'
    831 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    832 		'case x in (x) ;& esac'
    833 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    834 		'case x in x|y) ;& esac'
    835 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    836 		'case x in (x|y) ;& esac'
    837 }
    838 
    839 atf_test_case x_functions
    840 x_functions_head() {
    841 	atf_set "descr" "Check function definition parsing"
    842 }
    843 x_functions_body() {
    844 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    845 		'func() { :; }'
    846 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    847 		'func() { :; }; func'
    848 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    849 		'func()(:)'
    850 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    851 		'func()if :; then false; else true; fi'
    852 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    853 		'func()while false; do :;done'
    854 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    855 		'func () for a in b c; do :; done'
    856 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    857 		'func() case x in (y) esac'
    858 
    859 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    860 		'f1() { f2() { :; }; }; f1; f2'
    861 
    862 	# f2 should be not found, but f1 clears $?
    863 	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
    864 		'f1() { f2() { :; }; }; f2; f1'
    865 
    866 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    867 		'f1() { eval "$1() { :; }"; }; f1 f2; f2'
    868 }
    869 
    870 atf_init_test_cases() {
    871 	atf_add_test_case a_basic_tokenisation
    872 	atf_add_test_case b_comments
    873 	atf_add_test_case c_line_wrapping
    874 	atf_add_test_case d_redirects
    875 	atf_add_test_case f_variable_syntax
    876 	atf_add_test_case g_var_assign
    877 	atf_add_test_case i_pipelines
    878 	atf_add_test_case j_and_or_lists
    879 	atf_add_test_case k_lists
    880 	atf_add_test_case l_async_lists
    881 	atf_add_test_case m_compound_lists
    882 	atf_add_test_case q_for_loop
    883 	atf_add_test_case r_case
    884 	atf_add_test_case s_if
    885 	atf_add_test_case t_loops
    886 	atf_add_test_case u_case_cont
    887 	atf_add_test_case x_functions
    888 }
    889