1# $NetBSD: t_fsplit.sh,v 1.7 2017/06/24 11:06:17 kre Exp $ 2# 3# Copyright (c) 2007-2016 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 28# The standard 29# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html 30# explains (section 2.6) that Field splitting should be performed on the 31# result of variable expansions. 32# In particular this means that in ${x-word}, 'word' must be expanded as if 33# the "${x-" and "}" were absent from the input line. 34# 35# So: sh -c 'set ${x-a b c}; echo $#' should give 3. 36# and: sh -c 'set -- ${x-}' echo $#' should give 0 37# 38 39# the implementation of "sh" to test 40: ${TEST_SH:="/bin/sh"} 41 42nl=' 43' 44 45check() 46{ 47 TEST=$((${TEST} + 1)) 48 49 case "$#" in 50 (2) ;; 51 (*) atf_fail "Internal test error, $# args to check test ${TEST}";; 52 esac 53 54 result=$( ${TEST_SH} -c "unset x; $1" ) 55 STATUS="$?" 56 57 # Remove newlines 58 oifs="$IFS" 59 IFS="$nl" 60 result="$(echo $result)" 61 IFS="$oifs" 62 63 # trim the test text in case we use it in a message below 64 case "$1" in 65 ????????????????*) 66 set -- "$(expr "$1" : '\(............\).*')..." "$2" ;; 67 esac 68 69 if [ "$2" != "$result" ] 70 then 71 if [ "${STATUS}" = "0" ] 72 then 73 atf_fail "Test ${TEST} '$1': expected [$2], found [$result]" 74 else 75 atf_fail \ 76 "TEST ${TEST} '$1' failed ($STATUS): expected [$2], found [$result]" 77 fi 78 elif [ "${STATUS}" != 0 ] 79 then 80 atf_fail "TEST ${TEST} '$1' failed ($STATUS)" 81 fi 82 83 return 0 84} 85 86atf_test_case for 87for_head() { 88 atf_set "descr" "Checks field splitting in for loops" 89} 90for_body() { 91 unset x 92 93 TEST=0 94 # Since I managed to break this, leave the test in 95 check 'for f in $x; do echo x${f}y; done' '' 96} 97 98atf_test_case default_val 99default_val_head() { 100 atf_set "descr" "Checks field splitting in variable default values" 101} 102default_val_body() { 103 TEST=0 104 # Check that IFS is applied to text from ${x-...} unless it is inside 105 # any set of "..." 106 check 'set -- ${x-a b c}; echo $#' 3 107 108 check 'set -- ${x-"a b" c}; echo $#' 2 109 check 'set -- ${x-a "b c"}; echo $#' 2 110 check 'set -- ${x-"a b c"}; echo $#' 1 111 112 check "set -- \${x-'a b' c}; echo \$#" 2 113 check "set -- \${x-a 'b c'}; echo \$#" 2 114 check "set -- \${x-'a b c'}; echo \$#" 1 115 116 check 'set -- ${x-a\ b c}; echo $#' 2 117 check 'set -- ${x-a b\ c}; echo $#' 2 118 check 'set -- ${x-a\ b\ c}; echo $#' 1 119 120 check 'set -- ${x}; echo $#' 0 121 check 'set -- ${x-}; echo $#' 0 122 check 'set -- ${x-""}; echo $#' 1 123 check 'set -- ""${x}; echo $#' 1 124 check 'set -- ""${x-}; echo $#' 1 125 check 'set -- ""${x-""}; echo $#' 1 126 check 'set -- ${x}""; echo $#' 1 127 check 'set -- ${x-}""; echo $#' 1 128 check 'set -- ${x-""}""; echo $#' 1 129 check 'set -- ""${x}""; echo $#' 1 130 check 'set -- ""${x-}""; echo $#' 1 131 check 'set -- ""${x-""}""; echo $#' 1 132 133 check 'for i in ${x-a b c}; do echo "z${i}z"; done' \ 134 'zaz zbz zcz' 135 check 'for i in ${x-"a b" c}; do echo "z${i}z"; done' \ 136 'za bz zcz' 137 check 'for i in ${x-"a ${x-b c}" d}; do echo "z${i}z"; done' \ 138 'za b cz zdz' 139 check 'for i in ${x-a ${x-b c} d}; do echo "z${i}z"; done' \ 140 'zaz zbz zcz zdz' 141 142 # I am not sure the first of these two is correct, the rules on 143 # quoting word in ${var-word} are peculiar, and hard to fathom... 144 # It is what the NetBSD shell does, and bash, not the freebsd shell 145 # and not ksh93 (as of Mar 1, 2016, and still in June 2017) 146 # The likely correct interp of the next one is 'za bz zcz zdz' 147 148 # That and the "should be" below are correct as of POSIX 7 TC2 149 # But this is going to change to "unspecified" in POSIX 8 150 # (resolution of bug 221) so instead of being incorrect (as now) 151 # the NetBSD shell will simply be implementing is version 152 # of unspecified behaviour. Just beware that shells differ, 153 # a shell that fails this test is not incorrect because of it. 154 155 # should be: uuuu qqqqqq uuu q uuu (unquoted/quoted) no nesting. 156 check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' \ 157 'za b cz zdz' 158 check 'for i in ${x-a ${x-"b c"} d}; do echo "z${i}z"; done' \ 159 'zaz zb cz zdz' 160} 161 162atf_test_case replacement_val 163replacement_val_head() { 164 atf_set "descr" "Checks field splitting in variable replacement values" 165} 166replacement_val_body() { 167 TEST=0 168 169 # Check that IFS is applied to text from ${x+...} unless it is inside 170 # any set of "...", or whole expansion is quoted, or both... 171 172 check 'x=BOGUS; set -- ${x+a b c}; echo $#' 3 173 174 check 'x=BOGUS; set -- ${x+"a b" c}; echo $#' 2 175 check 'x=BOGUS; set -- ${x+a "b c"}; echo $#' 2 176 check 'x=BOGUS; set -- ${x+"a b c"}; echo $#' 1 177 178 check "x=BOGUS; set -- \${x+'a b' c}; echo \$#" 2 179 check "x=BOGUS; set -- \${x+a 'b c'}; echo \$#" 2 180 check "x=BOGUS; set -- \${x+'a b c'}; echo \$#" 1 181 182 check 'x=BOGUS; set -- ${x+a\ b c}; echo $#' 2 183 check 'x=BOGUS; set -- ${x+a b\ c}; echo $#' 2 184 check 'x=BOGUS; set -- ${x+a\ b\ c}; echo $#' 1 185 186 check 'x=BOGUS; set -- ${x+}; echo $#' 0 187 check 'x=BOGUS; set -- ${x+""}; echo $#' 1 188 check 'x=BOGUS; set -- ""${x+}; echo $#' 1 189 check 'x=BOGUS; set -- ""${x+""}; echo $#' 1 190 check 'x=BOGUS; set -- ${x+}""; echo $#' 1 191 check 'x=BOGUS; set -- ${x+""}""; echo $#' 1 192 check 'x=BOGUS; set -- ""${x+}""; echo $#' 1 193 check 'x=BOGUS; set -- ""${x+""}""; echo $#' 1 194 195 # verify that the value of $x does not affecty the value of ${x+...} 196 check 'x=BOGUS; set -- ${x+}; echo X$1' X 197 check 'x=BOGUS; set -- ${x+""}; echo X$1' X 198 check 'x=BOGUS; set -- ""${x+}; echo X$1' X 199 check 'x=BOGUS; set -- ""${x+""}; echo X$1' X 200 check 'x=BOGUS; set -- ${x+}""; echo X$1' X 201 check 'x=BOGUS; set -- ${x+""}""; echo X$1' X 202 check 'x=BOGUS; set -- ""${x+}""; echo X$1' X 203 check 'x=BOGUS; set -- ""${x+""}""; echo X$1' X 204 205 check 'x=BOGUS; set -- ${x+}; echo X${1-:}X' X:X 206 check 'x=BOGUS; set -- ${x+""}; echo X${1-:}X' XX 207 check 'x=BOGUS; set -- ""${x+}; echo X${1-:}X' XX 208 check 'x=BOGUS; set -- ""${x+""}; echo X${1-:}X' XX 209 check 'x=BOGUS; set -- ${x+}""; echo X${1-:}X' XX 210 check 'x=BOGUS; set -- ${x+""}""; echo X${1-:}X' XX 211 check 'x=BOGUS; set -- ""${x+}""; echo X${1-:}X' XX 212 check 'x=BOGUS; set -- ""${x+""}""; echo X${1-:}X' XX 213 214 # and validate that the replacement can be used as expected 215 check 'x=BOGUS; for i in ${x+a b c}; do echo "z${i}z"; done'\ 216 'zaz zbz zcz' 217 check 'x=BOGUS; for i in ${x+"a b" c}; do echo "z${i}z"; done'\ 218 'za bz zcz' 219 check 'x=BOGUS; for i in ${x+"a ${x+b c}" d}; do echo "z${i}z"; done'\ 220 'za b cz zdz' 221 222 # see the (extended) comment in the default_val test. This will be 223 # unspecified, hence we are OK (will be) but expect differences. 224 # also incorrect: uuuu qqqqqq uuu q uuu 225 check 'x=BOGUS; for i in ${x+"a ${x+"b c"}" d}; do echo "z${i}z"; done'\ 226 'za b cz zdz' 227 228 check 'x=BOGUS; for i in ${x+a ${x+"b c"} d}; do echo "z${i}z"; done'\ 229 'zaz zb cz zdz' 230 check 'x=BOGUS; for i in ${x+a ${x+b c} d}; do echo "z${i}z"; done'\ 231 'zaz zbz zcz zdz' 232} 233 234atf_test_case ifs_alpha 235ifs_alpha_head() { 236 atf_set "descr" "Checks that field splitting works with alphabetic" \ 237 "characters" 238} 239ifs_alpha_body() { 240 unset x 241 242 TEST=0 243 # repeat with an alphabetic in IFS 244 check 'IFS=q; set ${x-aqbqc}; echo $#' 3 245 check 'IFS=q; for i in ${x-aqbqc}; do echo "z${i}z"; done' \ 246 'zaz zbz zcz' 247 check 'IFS=q; for i in ${x-"aqb"qc}; do echo "z${i}z"; done' \ 248 'zaqbz zcz' 249 check 'IFS=q; for i in ${x-"aq${x-bqc}"qd}; do echo "z${i}z"; done' \ 250 'zaqbqcz zdz' 251 252 # this is another almost certainly incorrect expectation 253 # (but again, see comment in default_val test - becoming unspecified.) 254 # uu qqqqqq uuu q uu (quoted/unquoted) 255 check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' \ 256 'zaqbqcz zdz' 257 258 check 'IFS=q; for i in ${x-aq${x-"bqc"}qd}; do echo "z${i}z"; done' \ 259 'zaz zbqcz zdz' 260} 261 262atf_test_case quote 263quote_head() { 264 atf_set "descr" "Checks that field splitting works with multi-word" \ 265 "fields" 266} 267quote_body() { 268 unset x 269 270 TEST=0 271 # Some quote propagation checks 272 check 'set "${x-a b c}"; echo $#' 1 273 274 # this is another almost certainly incorrect expectation 275 # (but again, see comment in default_val test - becoming unspecified.) 276 # qqqq uuu qqq (quoted/unquoted) $1 is a $# is 2 277 check 'set "${x-"a b" c}"; echo $1' 'a b c' 278 279 check 'for i in "${x-a b c}"; do echo "z${i}z"; done' 'za b cz' 280} 281 282atf_test_case dollar_at 283dollar_at_head() { 284 atf_set "descr" "Checks that field splitting works when expanding" \ 285 "\$@" 286} 287dollar_at_body() { 288 unset x 289 290 TEST=0 291 # Check we get "$@" right 292 293 check 'set --; for i in x"$@"x; do echo "z${i}z"; done' 'zxxz' 294 check 'set a; for i in x"$@"x; do echo "z${i}z"; done' 'zxaxz' 295 check 'set a b; for i in x"$@"x; do echo "z${i}z"; done' 'zxaz zbxz' 296 297 check 'set --; for i; do echo "z${i}z"; done' '' 298 check 'set --; for i in $@; do echo "z${i}z"; done' '' 299 check 'set --; for i in "$@"; do echo "z${i}z"; done' '' 300 # atf_expect_fail "PR bin/50834" 301 check 'set --; for i in ""$@; do echo "z${i}z"; done' 'zz' 302 # atf_expect_pass 303 check 'set --; for i in $@""; do echo "z${i}z"; done' 'zz' 304 check 'set --; for i in ""$@""; do echo "z${i}z"; done' 'zz' 305 check 'set --; for i in """$@"; do echo "z${i}z"; done' 'zz' 306 check 'set --; for i in "$@"""; do echo "z${i}z"; done' 'zz' 307 check 'set --; for i in """$@""";do echo "z${i}z"; done' 'zz' 308 309 check 'set ""; for i; do echo "z${i}z"; done' 'zz' 310 check 'set ""; for i in "$@"; do echo "z${i}z"; done' 'zz' 311 check 'set "" ""; for i; do echo "z${i}z"; done' 'zz zz' 312 check 'set "" ""; for i in "$@"; do echo "z${i}z"; done' 'zz zz' 313 check 'set "" ""; for i in $@; do echo "z${i}z"; done' '' 314 315 check 'set "a b" c; for i; do echo "z${i}z"; done' \ 316 'za bz zcz' 317 check 'set "a b" c; for i in "$@"; do echo "z${i}z"; done' \ 318 'za bz zcz' 319 check 'set "a b" c; for i in $@; do echo "z${i}z"; done' \ 320 'zaz zbz zcz' 321 check 'set " a b " c; for i in "$@"; do echo "z${i}z"; done' \ 322 'z a b z zcz' 323 324 check 'set a b c; for i in "$@$@"; do echo "z${i}z"; done' \ 325 'zaz zbz zcaz zbz zcz' 326 check 'set a b c; for i in "$@""$@";do echo "z${i}z"; done' \ 327 'zaz zbz zcaz zbz zcz' 328} 329 330atf_test_case ifs 331ifs_head() { 332 atf_set "descr" "Checks that IFS correctly configures field" \ 333 "splitting behavior" 334} 335ifs_body() { 336 unset x 337 338 TEST=0 339 # Some IFS tests 340 check 't="-- "; IFS=" "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '0' 341 check 't=" x"; IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1' 342 check 't=" x "; IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1' 343 check 't=axb; IFS="x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a:b' 344 check 't="a x b"; IFS="x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a : b' 345 check 't="a xx b"; IFS="x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a :: b' 346 check 't="a xx b"; IFS="x "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a::b' 347 # A recent 'clarification' means that a single trailing IFS non-whitespace 348 # doesn't generate an empty parameter 349 check 't="xax"; IFS="x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a' 350 check 't="xax "; IFS="x "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a' 351 # Verify that IFS isn't being applied where it shouldn't be. 352 check 'IFS="x"; set axb; IFS=":"; r="$*"; IFS=; echo $# $r' '1 axb' 353} 354 355atf_test_case var_length 356var_length_head() { 357 atf_set "descr" "Checks that field splitting works when expanding" \ 358 "a variable's length" 359} 360var_length_body() { 361 TEST=0 362 363 long=12345678123456781234567812345678 364 long=$long$long$long$long 365 export long 366 unset x 367 368 # first test that the test method works... 369 check 'set -u; : ${long}; echo ${#long}' '128' 370 371 # Check that we apply IFS to ${#var} 372 check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' \ 373 '128 1 8 3' 374 check 'IFS=2; set ${x-${#long}}; IFS=" "; echo $* $#' '1 8 2' 375 check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#' '128 1' 376 check 'IFS=2; set "${x-${#long}}"; IFS=" "; echo $* $#' '128 1' 377 check 'IFS=2; set ${x-${#long}}; : ; echo $* $#' '1 8 ' 378 check 'IFS=2; set ${x-${#long}}; : ; echo $* "$#"' '1 8 2' 379 check 'IFS=2; set ${x-${#long}}; : ; echo "$*" "$#"' '128 2' 380 check 'IFS=2; set ${x-${#long}}; : ; echo "$@" "$#"' '1 8 2' 381} 382 383atf_test_case split_arith 384split_arith_head() { 385 atf_set "descr" "Checks that field splitting works when expanding" \ 386 "the results from arithmetic" 387} 388split_arith_body() { 389 TEST=0 390 391 # Check that we apply IFS to $(( expr )) 392 393 # Note: we do not check the actual arithmetic operations here 394 # (there is a separate test just for that) so we just enter 395 # the "answer" inside $(( )) ... also makes it easier to visualise 396 397 check 'IFS=5; echo $(( 123456789 ))' '1234 6789' 398 check 'IFS=5; echo "$(( 123456789 ))"' '123456789' 399 check 'IFS=37; echo $(( 123456789 ))' '12 456 89' 400 check 'IFS=37; echo "$(( 123456789 ))"' '123456789' 401 check 'IFS=159; echo $(( 123456789 ))' ' 234 678' 402 403 check 'IFS=5; set -- $(( 123456789 )); echo $#: $1 $2 $3 $4' \ 404 '2: 1234 6789' 405 check 'IFS=5; set -- "$(( 123456789 ))"; echo $#: $1 $2 $3 $4' \ 406 '1: 1234 6789' # go ahead: explain it! 407 check 'IFS=5; set -- "$(( 123456789 ))"; echo "$#: $1 $2 $3 $4"' \ 408 '1: 123456789 ' # ah! 409 410 check 'IFS=37; set -- $(( 123456789 )); echo $#: $1 $2 $3 $4' \ 411 ' : 12 456 89' # Tricky! 412 check 'IFS=5; set -- $(( 123456789 )); echo $#: $*' \ 413 '2: 1234 6789' 414 check 'IFS=47; set -- $(( 123456789 )); echo $#: $*' \ 415 '3: 123 56 89' 416 check 'IFS=5; set -- $(( 123456789 )); echo "$#: $*"' \ 417 '2: 123456789' 418 check 'IFS=37; set -- $(( 123456789 )); echo "$#: $*"' \ 419 '3: 123456389' # [sic] 420 check 'IFS=5; set -- $(( 123456789 )); echo $#: $@' \ 421 '2: 1234 6789' 422 check 'IFS=47; set -- $(( 123456789 )); echo $#: $@' \ 423 '3: 123 56 89' 424 check 'IFS=5; set -- $(( 123456789 )); echo "$#: $@"' \ 425 '2: 1234 6789' 426 check 'IFS=37; set -- $(( 123456789 )); echo "$#: $*"' \ 427 '3: 123456389' # [sic] 428 429 check 'IFS=1; set -- $(( 1111 )); echo "$#:" $*' '4: ' 430 check 'IFS=" 1"; set -- $(( 1231231231 )); echo "$#: $*"' \ 431 '4: 23 23 23' 432 check 'IFS="1 "; set -- $(( 1231231231 )); echo "$#: $*"' \ 433 '4: 123123123' 434 435 check 'IFS=5; echo 5$(( 123456789 ))5' '51234 67895' 436 check 'IFS=37; echo 73$(( 123456789 ))37' '7312 456 8937' 437 check 'IFS=159; echo 11$(( 123456789 ))95' '11 234 678 95' 438 check 'IFS="159 "; echo 11$(( 123456789 ))95' '11 234 678 95' 439 check 'IFS="159 "; echo 11$(( 11234567899 ))95' '11 234 678 95' 440} 441 442atf_init_test_cases() { 443 atf_add_test_case for 444 atf_add_test_case default_val 445 atf_add_test_case replacement_val 446 atf_add_test_case ifs_alpha 447 atf_add_test_case quote 448 atf_add_test_case dollar_at 449 atf_add_test_case ifs 450 atf_add_test_case var_length 451 atf_add_test_case split_arith 452} 453