Home | History | Annotate | Line # | Download | only in sh
t_fsplit.sh revision 1.5
      1 # $NetBSD: t_fsplit.sh,v 1.5 2017/06/03 10:27:05 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 $#' shold give 0
     37 #
     38 
     39 # the implementation of "sh" to test
     40 : ${TEST_SH:="/bin/sh"}
     41 
     42 nl='
     43 '
     44 
     45 check()
     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 
     84 atf_test_case for
     85 for_head() {
     86 	atf_set "descr" "Checks field splitting in for loops"
     87 }
     88 for_body() {
     89 	unset x
     90 
     91 	TEST=0
     92 	# Since I managed to break this, leave the test in
     93 	check 'for f in $x; do echo x${f}y; done' ''
     94 }
     95 
     96 atf_test_case default_val
     97 default_val_head() {
     98 	atf_set "descr" "Checks field splitting in variable default values"
     99 }
    100 default_val_body() {
    101 	TEST=0
    102 	# Check that IFS is applied to text from ${x-...} unless it is inside
    103 	# any set of "..."
    104 	check 'set -- ${x-a b c};   echo $#'   3
    105 
    106 	check 'set -- ${x-"a b" c}; echo $#'   2
    107 	check 'set -- ${x-a "b c"}; echo $#'   2
    108 	check 'set -- ${x-"a b c"}; echo $#'   1
    109 
    110 	check "set -- \${x-'a b' c}; echo \$#" 2
    111 	check "set -- \${x-a 'b c'}; echo \$#" 2
    112 	check "set -- \${x-'a b c'}; echo \$#" 1
    113 
    114 	check 'set -- ${x-a\ b c};  echo $#'   2
    115 	check 'set -- ${x-a b\ c};  echo $#'   2
    116 	check 'set -- ${x-a\ b\ c}; echo $#'   1
    117 
    118 	check 'set -- ${x};        echo $#' 0
    119 	check 'set -- ${x-};       echo $#' 0
    120 	check 'set -- ${x-""};     echo $#' 1
    121 	check 'set -- ""${x};      echo $#' 1
    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 
    131 	check 'for i in ${x-a b c};            do echo "z${i}z"; done' \
    132 		'zaz zbz zcz'
    133 	check 'for i in ${x-"a b" c};          do echo "z${i}z"; done' \
    134 		'za bz zcz'
    135 	check 'for i in ${x-"a ${x-b c}" d};   do echo "z${i}z"; done' \
    136 		'za b cz zdz'
    137 	check 'for i in ${x-a ${x-b c} d};     do echo "z${i}z"; done' \
    138 		'zaz zbz zcz zdz'
    139 
    140 	# I am not sure the first of these two is correct, the rules on
    141 	# quoting word in ${var-word} are peculiar, and hard to fathom...
    142 	# It is what the NetBSD shell does, and bash, not the freebsd shell
    143 	# and not ksh93 (as of Mar 1, 2016, and still in June 2017)
    144 	# The likely correct interp of the next one is 'za bz zcz zdz'
    145 
    146 	# should be:    uuuu qqqqqq uuu q uuu   (unquoted/quoted) no nesting.
    147 	check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' \
    148 		'za b cz zdz'
    149 	check 'for i in ${x-a ${x-"b c"} d};   do echo "z${i}z"; done' \
    150 		'zaz zb cz zdz'
    151 }
    152 
    153 atf_test_case replacement_val
    154 replacement_val_head() {
    155 	atf_set "descr" "Checks field splitting in variable replacement values"
    156 }
    157 replacement_val_body() {
    158 	TEST=0
    159 
    160 	# Check that IFS is applied to text from ${x+...} unless it is inside
    161 	# any set of "...", or whole expansion is quoted, or both...
    162 
    163 	check 'x=BOGUS; set -- ${x+a b c};   echo $#'   3
    164 
    165 	check 'x=BOGUS; set -- ${x+"a b" c}; echo $#'   2
    166 	check 'x=BOGUS; set -- ${x+a "b c"}; echo $#'   2
    167 	check 'x=BOGUS; set -- ${x+"a b c"}; echo $#'   1
    168 
    169 	check "x=BOGUS; set -- \${x+'a b' c}; echo \$#" 2
    170 	check "x=BOGUS; set -- \${x+a 'b c'}; echo \$#" 2
    171 	check "x=BOGUS; set -- \${x+'a b c'}; echo \$#" 1
    172 
    173 	check 'x=BOGUS; set -- ${x+a\ b c};  echo $#'   2
    174 	check 'x=BOGUS; set -- ${x+a b\ c};  echo $#'   2
    175 	check 'x=BOGUS; set -- ${x+a\ b\ c}; echo $#'   1
    176 
    177 	check 'x=BOGUS; set -- ${x+};       echo $#' 0
    178 	check 'x=BOGUS; set -- ${x+""};     echo $#' 1
    179 	check 'x=BOGUS; set -- ""${x+};     echo $#' 1
    180 	check 'x=BOGUS; set -- ""${x+""};   echo $#' 1
    181 	check 'x=BOGUS; set -- ${x+}"";     echo $#' 1
    182 	check 'x=BOGUS; set -- ${x+""}"";   echo $#' 1
    183 	check 'x=BOGUS; set -- ""${x+}"";   echo $#' 1
    184 	check 'x=BOGUS; set -- ""${x+""}""; echo $#' 1
    185 
    186 	# verify that the value of $x does not affecty the value of ${x+...}
    187 	check 'x=BOGUS; set -- ${x+};       echo X$1' X
    188 	check 'x=BOGUS; set -- ${x+""};     echo X$1' X
    189 	check 'x=BOGUS; set -- ""${x+};     echo X$1' X
    190 	check 'x=BOGUS; set -- ""${x+""};   echo X$1' X
    191 	check 'x=BOGUS; set -- ${x+}"";     echo X$1' X
    192 	check 'x=BOGUS; set -- ${x+""}"";   echo X$1' X
    193 	check 'x=BOGUS; set -- ""${x+}"";   echo X$1' X
    194 	check 'x=BOGUS; set -- ""${x+""}""; echo X$1' X
    195 
    196 	check 'x=BOGUS; set -- ${x+};       echo X${1-:}X' X:X
    197 	check 'x=BOGUS; set -- ${x+""};     echo X${1-:}X' XX
    198 	check 'x=BOGUS; set -- ""${x+};     echo X${1-:}X' XX
    199 	check 'x=BOGUS; set -- ""${x+""};   echo X${1-:}X' XX
    200 	check 'x=BOGUS; set -- ${x+}"";     echo X${1-:}X' XX
    201 	check 'x=BOGUS; set -- ${x+""}"";   echo X${1-:}X' XX
    202 	check 'x=BOGUS; set -- ""${x+}"";   echo X${1-:}X' XX
    203 	check 'x=BOGUS; set -- ""${x+""}""; echo X${1-:}X' XX
    204 
    205 	# and validate that the replacement can be used as expected
    206 	check 'x=BOGUS; for i in ${x+a b c};            do echo "z${i}z"; done'\
    207 		'zaz zbz zcz'
    208 	check 'x=BOGUS; for i in ${x+"a b" c};          do echo "z${i}z"; done'\
    209 		'za bz zcz'
    210 	check 'x=BOGUS; for i in ${x+"a ${x+b c}" d};   do echo "z${i}z"; done'\
    211 		'za b cz zdz'
    212 	check 'x=BOGUS; for i in ${x+"a ${x+"b c"}" d}; do echo "z${i}z"; done'\
    213 		'za b cz zdz'
    214 	check 'x=BOGUS; for i in ${x+a ${x+"b c"} d};   do echo "z${i}z"; done'\
    215 		'zaz zb cz zdz'
    216 	check 'x=BOGUS; for i in ${x+a ${x+b c} d};     do echo "z${i}z"; done'\
    217 		'zaz zbz zcz zdz'
    218 }
    219 
    220 atf_test_case ifs_alpha
    221 ifs_alpha_head() {
    222 	atf_set "descr" "Checks that field splitting works with alphabetic" \
    223 	                "characters"
    224 }
    225 ifs_alpha_body() {
    226 	unset x
    227 
    228 	TEST=0
    229 	# repeat with an alphabetic in IFS
    230 	check 'IFS=q; set ${x-aqbqc}; echo $#' 3
    231 	check 'IFS=q; for i in ${x-aqbqc};            do echo "z${i}z"; done' \
    232 		'zaz zbz zcz'
    233 	check 'IFS=q; for i in ${x-"aqb"qc};          do echo "z${i}z"; done' \
    234 		'zaqbz zcz'
    235 	check 'IFS=q; for i in ${x-"aq${x-bqc}"qd};   do echo "z${i}z"; done' \
    236 		'zaqbqcz zdz'
    237 	check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' \
    238 		'zaqbqcz zdz'
    239 	check 'IFS=q; for i in ${x-aq${x-"bqc"}qd};  do echo "z${i}z"; done' \
    240 		'zaz zbqcz zdz'
    241 }
    242 
    243 atf_test_case quote
    244 quote_head() {
    245 	atf_set "descr" "Checks that field splitting works with multi-word" \
    246 	                "fields"
    247 }
    248 quote_body() {
    249 	unset x
    250 
    251 	TEST=0
    252 	# Some quote propagation checks
    253 	check 'set "${x-a b c}";   echo $#' 1
    254 	check 'set "${x-"a b" c}"; echo $1' 'a b c'
    255 	check 'for i in "${x-a b c}"; do echo "z${i}z"; done' 'za b cz'
    256 }
    257 
    258 atf_test_case dollar_at
    259 dollar_at_head() {
    260 	atf_set "descr" "Checks that field splitting works when expanding" \
    261 	                "\$@"
    262 }
    263 dollar_at_body() {
    264 	unset x
    265 
    266 	TEST=0
    267 	# Check we get "$@" right
    268 
    269 	check 'set --;        for i in x"$@"x;  do echo "z${i}z"; done' 'zxxz'
    270 	check 'set a;         for i in x"$@"x;  do echo "z${i}z"; done' 'zxaxz'
    271 	check 'set a b;       for i in x"$@"x;  do echo "z${i}z"; done' 'zxaz zbxz'
    272 
    273 	check 'set --;        for i;            do echo "z${i}z"; done' ''
    274 	check 'set --;        for i in $@;      do echo "z${i}z"; done' ''
    275 	check 'set --;        for i in "$@";    do echo "z${i}z"; done' ''
    276 	# atf_expect_fail "PR bin/50834"
    277 	check 'set --;        for i in ""$@;    do echo "z${i}z"; done' 'zz'
    278 	# atf_expect_pass
    279 	check 'set --;        for i in $@"";    do echo "z${i}z"; done' 'zz'
    280 	check 'set --;        for i in ""$@"";  do echo "z${i}z"; done' 'zz'
    281 	check 'set --;        for i in """$@";  do echo "z${i}z"; done' 'zz'
    282 	check 'set --;        for i in "$@""";  do echo "z${i}z"; done' 'zz'
    283 	check 'set --;        for i in """$@""";do echo "z${i}z"; done' 'zz'
    284 
    285 	check 'set "";        for i;            do echo "z${i}z"; done' 'zz'
    286 	check 'set "";        for i in "$@";    do echo "z${i}z"; done' 'zz'
    287 	check 'set "" "";     for i;            do echo "z${i}z"; done' 'zz zz'
    288 	check 'set "" "";     for i in "$@";    do echo "z${i}z"; done' 'zz zz'
    289 	check 'set "" "";     for i in $@;      do echo "z${i}z"; done' ''
    290 
    291 	check 'set "a b" c;   for i;            do echo "z${i}z"; done' \
    292 		'za bz zcz'
    293 	check 'set "a b" c;   for i in "$@";    do echo "z${i}z"; done' \
    294 		'za bz zcz'
    295 	check 'set "a b" c;   for i in $@;      do echo "z${i}z"; done' \
    296 		'zaz zbz zcz'
    297 	check 'set " a b " c; for i in "$@";    do echo "z${i}z"; done' \
    298 		'z a b z zcz'
    299 
    300 	check 'set a b c;     for i in "$@$@";  do echo "z${i}z"; done' \
    301 		'zaz zbz zcaz zbz zcz'
    302 	check 'set a b c;     for i in "$@""$@";do echo "z${i}z"; done' \
    303 		'zaz zbz zcaz zbz zcz'
    304 }
    305 
    306 atf_test_case ifs
    307 ifs_head() {
    308 	atf_set "descr" "Checks that IFS correctly configures field" \
    309 	                "splitting behavior"
    310 }
    311 ifs_body() {
    312 	unset x
    313 
    314 	TEST=0
    315 	# Some IFS tests
    316 	check 't="-- ";    IFS=" ";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '0'
    317 	check 't=" x";     IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
    318 	check 't=" x ";    IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
    319 	check 't=axb;      IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a:b'
    320 	check 't="a x b";  IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a : b'
    321 	check 't="a xx b"; IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a :: b'
    322 	check 't="a xx b"; IFS="x "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a::b'
    323 	# A recent 'clarification' means that a single trailing IFS non-whitespace
    324 	# doesn't generate an empty parameter
    325 	check 't="xax";  IFS="x";     set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a'
    326 	check 't="xax "; IFS="x ";   set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a'
    327 	# Verify that IFS isn't being applied where it shouldn't be.
    328 	check 'IFS="x";             set axb; IFS=":"; r="$*"; IFS=; echo $# $r' '1 axb'
    329 }
    330 
    331 atf_test_case var_length
    332 var_length_head() {
    333 	atf_set "descr" "Checks that field splitting works when expanding" \
    334 	                "a variable's length"
    335 }
    336 var_length_body() {
    337 	TEST=0
    338 
    339 	long=12345678123456781234567812345678
    340 	long=$long$long$long$long
    341 	export long
    342 	unset x
    343 
    344 	# first test that the test method works...
    345 	check 'set -u; : ${long}; echo ${#long}' '128'
    346 
    347 	# Check that we apply IFS to ${#var}
    348 	check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' \
    349 		'128 1 8 3'
    350 	check 'IFS=2; set ${x-${#long}};   IFS=" "; echo $* $#'     '1 8 2'
    351 	check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#'     '128 1'
    352 	check 'IFS=2; set "${x-${#long}}"; IFS=" "; echo $* $#'     '128 1'
    353 	check 'IFS=2; set ${x-${#long}};   :      ; echo $* $#'     '1 8 '
    354 	check 'IFS=2; set ${x-${#long}};   :      ; echo $* "$#"'   '1 8 2'
    355 	check 'IFS=2; set ${x-${#long}};   :      ; echo "$*" "$#"' '128 2'
    356 	check 'IFS=2; set ${x-${#long}};   :      ; echo "$@" "$#"' '1 8 2'
    357 }
    358 
    359 atf_init_test_cases() {
    360 	atf_add_test_case for
    361 	atf_add_test_case default_val
    362 	atf_add_test_case replacement_val
    363 	atf_add_test_case ifs_alpha
    364 	atf_add_test_case quote
    365 	atf_add_test_case dollar_at
    366 	atf_add_test_case ifs
    367 	atf_add_test_case var_length
    368 }
    369