# $NetBSD: t_here.sh,v 1.9 2021/11/22 05:21:54 kre Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # the implementation of "sh" to test : ${TEST_SH:="/bin/sh"} nl=' ' reset() { TEST_NUM=0 TEST_FAILURES='' TEST_FAIL_COUNT=0 TEST_ID="$1" } check() { fail=false TEMP_FILE=$( mktemp OUT.XXXXXX ) TEST_NUM=$(( $TEST_NUM + 1 )) # our local shell (ATF_SHELL) better do quoting correctly... # some of the tests expect us to expand $nl internally... CMD="nl='${nl}'; $1" result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )" STATUS=$? if [ "${STATUS}" -ne "$3" ]; then echo >&2 "[$TEST_NUM] expected exit code $3, got ${STATUS}" # don't actually fail just because of wrong exit code # unless we either expected, or received "good" case "$3/${STATUS}" in (*/0|0/*) fail=true;; esac fi if [ "$3" -eq 0 ]; then if [ -s "${TEMP_FILE}" ]; then echo >&2 \ "[$TEST_NUM] Messages produced on stderr unexpected..." cat "${TEMP_FILE}" >&2 fail=true fi else if ! [ -s "${TEMP_FILE}" ]; then echo >&2 \ "[$TEST_NUM] Expected messages on stderr, nothing produced" fail=true fi fi rm -f "${TEMP_FILE}" # Remove newlines (use local shell for this) result="$( IFS="$nl" set -f set -- $result IFS=' ' printf %s "$*" )" if [ "$2" != "$result" ] then echo >&2 "[$TEST_NUM] Expected output '$2', received '$result'" fail=true fi if $fail then echo >&2 "[$TEST_NUM] Full command: <<${CMD}>>" fi $fail && test -n "$TEST_ID" && { TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+ }${TEST_ID}[$TEST_NUM]: test of '$1' failed"; TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) return 0 } $fail && atf_fail "Test[$TEST_NUM] of '$1' failed" return 0 } results() { test -z "${TEST_ID}" && return 0 test -z "${TEST_FAILURES}" && return 0 echo >&2 "==========================================" echo >&2 "While testing '${TEST_ID}'" echo >&2 " - - - - - - - - - - - - - - - - -" echo >&2 "${TEST_FAILURES}" atf_fail \ "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr" } atf_test_case do_simple do_simple_head() { atf_set "descr" "Basic tests for here documents" } do_simple_body() { y=x reset 'simple' IFS=' ' check 'x=`cat < E0F ' '<'\''"'\'' \\$X\$X "'\''" \\>' 0 check 'X=!; cat <<- E0F <'\''"'\'' \\$X\$X "'\''" \\> E0F ' '<'\''"'\'' \!$X "'\''" \>' 0 check 'cat <<- END $( echo "'\''" ) $( echo '\''"'\'' ) $( echo \\ ) END ' "' \" \\" 0 check 'X=12345; Y="string1 line1?-line2"; Z=; unset W; cat <<-EOF ${#X}${Z:-${Y}}${W+junk}${Y%%l*}${Y#*\?} "$Z"'\''$W'\'' ${Y%" "*} $(( X + 54321 )) EOF ' '5string1 line1?-line2string1 -line2 ""'\'\'' string1 66666' 0 # check that \ only quotes the magic chars, otherwise is retained check 'p=A; cat <<-EOF ${p+\%$p\%} ${p+%$p%} EOF ' '\%A\% %A%' 0 # and check that " is not magic, so \ does not quote it check 'p=A; cat <<-EOF ${p+\"$p\"} ${p+"$p"} EOF ' '\"A\" "A"' 0 # except in a ${var%} word, base syntax reapplies, and # there quotes are magic again check 'p=ABCD; cat <<-EOF ${p%B?D} ${p%B\?D} ${p%"BCD"} "${p%??}" ${p#"${p%??}"} "${p#"${p%?"?"}"}" EOF ' 'A ABCD A "AB" CD ""' 0 check 'p=AB??; cat <<-EOF ${p%B?D} ${p%B\??} ${p%"B??"} "${p%??}" ${p#"${p%??}"} "${p#"${p%?"?"}"}" EOF ' 'AB?? A A "AB" ?? "??"' 0 results } # # This next test is really just testing what our shell happens to do. # There doesn't seem to be any spec on in which context expansions # in redirects are processed. Most shells do them in the parent # shell context, meaning that side effects of the expansion become # visible to the shell - a couple process redirect expansions in # a subshell, meaning that side effects are lost. # # Before PR bin/53550 was fixed, the NetBSD sh evaluated all redirect # expansions, except here documents, in the context of the shell, and # here document redirects in a subshell context. That distinction # makes no real sense (and only an old, and maybe still current, FreeBSD # shell shares that pecadillo.) Afer that fix, the NetBSD shell joins # almost all others in expanding redirects (all of them) in the shell # context, meaning that side effects of here documenty expansions become # visible in the shell. # # Before the fix, we used to get "2\n1\n" as the output from this # test, now the variable assignment in the here document persists # and we get "2\n2\n" as do most other shells. (bash is a notable # exception, but it does all redirect expansions in a subshell context) # atf_test_case side_effects side_effects_head() { atf_set "descr" "Tests how side effects in here documents are handled" } side_effects_body() { atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c ' unset X cat <<-EOF ${X=2} EOF echo "${X-1}" ' } # This is a test for the specific bug reported in PR bin/53550 # This should work in any shell. atf_test_case exit_status exit_status_head() { atf_set descr "Tests exit status of a command substitution in a heredoc" } exit_status_body() { # PR bin/53550 test atf_check -s exit:7 -o empty -e empty ${TEST_SH} -c ' <<-EOF $(exit 7) EOF ' } # The following tests a problem reported on the austin-list 2021-09-08 # by oguzismailuysal@gmail.com ... it affected all ash derived shells atf_test_case hard_cases hard_cases_head() { atf_set "descr" \ "Tests here docs in positions that have confised our parser" } hard_cases_body() { atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c ' : <<- do | for x in xxx do do echo $x done' atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c ' set -- xxx : <<- done | for x in xxx done do echo $x done' atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c ' : <<- in | case xxx in in xxx) echo xxx;; esac' } atf_test_case vicious vicious_head() { atf_set "descr" "Tests for obscure and obnoxious uses of here docs" } vicious_body() { reset cat <<- \END_SCRIPT > script cat < script prefix() { sed -e "s/^/$1:/"; } DASH_CODE() { :; } prefix A <