Home | History | Annotate | Line # | Download | only in sh
t_fsplit.sh revision 1.2
      1 # $NetBSD: t_fsplit.sh,v 1.2 2016/03/08 14:26:54 christos 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 nl='
     40 '
     41 
     42 check()
     43 {
     44 	TEST=$((${TEST} + 1))
     45 
     46 	case "$#" in
     47 	(2)	;;
     48 	(*)	atf_fail "Internal test error, $# args to check test ${TEST}";;
     49 	esac
     50 
     51 	result=$( ${TEST_SH} -c "unset x; $1" )
     52 	STATUS="$?"
     53 
     54 	# Remove newlines
     55 	oifs="$IFS"
     56 	IFS="$nl"
     57 	result="$(echo $result)"
     58 	IFS="$oifs"
     59 
     60 	# trim the test text in case we use it in a message below
     61 	case "$1" in
     62 	????????????????*)
     63 		set -- "$(expr "$1" : '\(............\).*')..." "$2" ;;
     64 	esac
     65 
     66 	if [ "$2" != "$result" ]
     67 	then
     68 		if [ "${STATUS}" = "0" ]
     69 		then
     70 		  atf_fail "Test ${TEST} '$1': expected [$2], found [$result]"
     71 		else
     72 		  atf_fail \
     73 	  "TEST ${TEST} '$1' failed ($STATUS): expected [$2], found [$result]"
     74 		fi
     75 	elif [ "${STATUS}" != 0 ]
     76 	then
     77 		  atf_fail "TEST ${TEST} '$1' failed ($STATUS)"
     78 	fi
     79 }
     80 
     81 atf_test_case for
     82 for_head() {
     83 	atf_set "descr" "Checks field splitting in for loops"
     84 }
     85 for_body() {
     86 	unset x
     87 
     88 	TEST=0
     89 	# Since I managed to break this, leave the test in
     90 	check 'for f in $x; do echo x${f}y; done' ''
     91 }
     92 
     93 atf_test_case default_val
     94 default_val_head() {
     95 	atf_set "descr" "Checks field splitting in variable default values"
     96 }
     97 default_val_body() {
     98 	TEST=0
     99 	# Check that IFS is applied to text from ${x-...} unless it is inside
    100 	# any set of "..."
    101 	check 'set -- ${x-a b c};   echo $#'   3
    102 
    103 	check 'set -- ${x-"a b" c}; echo $#'   2
    104 	check 'set -- ${x-a "b c"}; echo $#'   2
    105 	check 'set -- ${x-"a b c"}; echo $#'   1
    106 
    107 	check "set -- \${x-'a b' c}; echo \$#" 2
    108 	check "set -- \${x-a 'b c'}; echo \$#" 2
    109 	check "set -- \${x-'a b c'}; echo \$#" 1
    110 
    111 	check 'set -- ${x-a\ b c};  echo $#'   2
    112 	check 'set -- ${x-a b\ c};  echo $#'   2
    113 	check 'set -- ${x-a\ b\ c}; echo $#'   1
    114 
    115 	check 'set -- ${x};        echo $#' 0
    116 	check 'set -- ${x-};       echo $#' 0
    117 	check 'set -- ${x-""};     echo $#' 1
    118 	check 'set -- ""${x};      echo $#' 1
    119 	check 'set -- ""${x-};     echo $#' 1
    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 
    128 	check 'for i in ${x-a b c};            do echo "z${i}z"; done' \
    129 		'zaz zbz zcz'
    130 	check 'for i in ${x-"a b" c};          do echo "z${i}z"; done' \
    131 		'za bz zcz'
    132 	check 'for i in ${x-"a ${x-b c}" d};   do echo "z${i}z"; done' \
    133 		'za b cz zdz'
    134 	check 'for i in ${x-a ${x-b c} d};     do echo "z${i}z"; done' \
    135 		'zaz zbz zcz zdz'
    136 
    137 	# I am not sure these two are correct, the rules on quoting word
    138 	# in ${var-word} are peculiar, and hard to fathom...
    139 	# They are what the NetBSD shell does, and bash, not the freebsd shell
    140 	# (as of Mar 1, 2016)
    141 
    142 	check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' \
    143 		'za b cz zdz'
    144 	check 'for i in ${x-a ${x-"b c"} d};   do echo "z${i}z"; done' \
    145 		'zaz zb cz zdz'
    146 }
    147 
    148 atf_test_case replacement_val
    149 replacement_val_head() {
    150 	atf_set "descr" "Checks field splitting in variable replacement values"
    151 }
    152 replacement_val_body() {
    153 	TEST=0
    154 
    155 	# Check that IFS is applied to text from ${x+...} unless it is inside
    156 	# any set of "...", or whole expansion is quoted, or both...
    157 
    158 	check 'x=BOGUS; set -- ${x+a b c};   echo $#'   3
    159 
    160 	check 'x=BOGUS; set -- ${x+"a b" c}; echo $#'   2
    161 	check 'x=BOGUS; set -- ${x+a "b c"}; echo $#'   2
    162 	check 'x=BOGUS; set -- ${x+"a b c"}; echo $#'   1
    163 
    164 	check "x=BOGUS; set -- \${x+'a b' c}; echo \$#" 2
    165 	check "x=BOGUS; set -- \${x+a 'b c'}; echo \$#" 2
    166 	check "x=BOGUS; set -- \${x+'a b c'}; echo \$#" 1
    167 
    168 	check 'x=BOGUS; set -- ${x+a\ b c};  echo $#'   2
    169 	check 'x=BOGUS; set -- ${x+a b\ c};  echo $#'   2
    170 	check 'x=BOGUS; set -- ${x+a\ b\ c}; echo $#'   1
    171 
    172 	check 'x=BOGUS; set -- ${x+};       echo $#' 0
    173 	check 'x=BOGUS; set -- ${x+""};     echo $#' 1
    174 	check 'x=BOGUS; set -- ""${x+};     echo $#' 1
    175 	check 'x=BOGUS; set -- ""${x+""};   echo $#' 1
    176 	check 'x=BOGUS; set -- ${x+}"";     echo $#' 1
    177 	check 'x=BOGUS; set -- ${x+""}"";   echo $#' 1
    178 	check 'x=BOGUS; set -- ""${x+}"";   echo $#' 1
    179 	check 'x=BOGUS; set -- ""${x+""}""; echo $#' 1
    180 
    181 	# verify that the value of $x does not affecty the value of ${x+...}
    182 	check 'x=BOGUS; set -- ${x+};       echo X$1' X
    183 	check 'x=BOGUS; set -- ${x+""};     echo X$1' X
    184 	check 'x=BOGUS; set -- ""${x+};     echo X$1' X
    185 	check 'x=BOGUS; set -- ""${x+""};   echo X$1' X
    186 	check 'x=BOGUS; set -- ${x+}"";     echo X$1' 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 
    191 	check 'x=BOGUS; set -- ${x+};       echo X${1-:}X' X:X
    192 	check 'x=BOGUS; set -- ${x+""};     echo X${1-:}X' XX
    193 	check 'x=BOGUS; set -- ""${x+};     echo X${1-:}X' XX
    194 	check 'x=BOGUS; set -- ""${x+""};   echo X${1-:}X' XX
    195 	check 'x=BOGUS; set -- ${x+}"";     echo X${1-:}X' XX
    196 	check 'x=BOGUS; set -- ${x+""}"";   echo X${1-:}X' XX
    197 	check 'x=BOGUS; set -- ""${x+}"";   echo X${1-:}X' XX
    198 	check 'x=BOGUS; set -- ""${x+""}""; echo X${1-:}X' XX
    199 
    200 	# and validate that the replacement can be used as expected
    201 	check 'x=BOGUS; for i in ${x+a b c};            do echo "z${i}z"; done'\
    202 		'zaz zbz zcz'
    203 	check 'x=BOGUS; for i in ${x+"a b" c};          do echo "z${i}z"; done'\
    204 		'za bz zcz'
    205 	check 'x=BOGUS; for i in ${x+"a ${x+b c}" d};   do echo "z${i}z"; done'\
    206 		'za b cz zdz'
    207 	check 'x=BOGUS; for i in ${x+"a ${x+"b c"}" d}; do echo "z${i}z"; done'\
    208 		'za b cz zdz'
    209 	check 'x=BOGUS; for i in ${x+a ${x+"b c"} d};   do echo "z${i}z"; done'\
    210 		'zaz zb cz zdz'
    211 	check 'x=BOGUS; for i in ${x+a ${x+b c} d};     do echo "z${i}z"; done'\
    212 		'zaz zbz zcz zdz'
    213 }
    214 
    215 atf_test_case ifs_alpha
    216 ifs_alpha_head() {
    217 	atf_set "descr" "Checks that field splitting works with alphabetic" \
    218 	                "characters"
    219 }
    220 ifs_alpha_body() {
    221 	unset x
    222 
    223 	TEST=0
    224 	# repeat with an alphabetic in IFS
    225 	check 'IFS=q; set ${x-aqbqc}; echo $#' 3
    226 	check 'IFS=q; for i in ${x-aqbqc};            do echo "z${i}z"; done' \
    227 		'zaz zbz zcz'
    228 	check 'IFS=q; for i in ${x-"aqb"qc};          do echo "z${i}z"; done' \
    229 		'zaqbz zcz'
    230 	check 'IFS=q; for i in ${x-"aq${x-bqc}"qd};   do echo "z${i}z"; done' \
    231 		'zaqbqcz zdz'
    232 	check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' \
    233 		'zaqbqcz zdz'
    234 	check 'IFS=q; for i in ${x-aq${x-"bqc"}qd};  do echo "z${i}z"; done' \
    235 		'zaz zbqcz zdz'
    236 }
    237 
    238 atf_test_case quote
    239 quote_head() {
    240 	atf_set "descr" "Checks that field splitting works with multi-word" \
    241 	                "fields"
    242 }
    243 quote_body() {
    244 	unset x
    245 
    246 	TEST=0
    247 	# Some quote propagation checks
    248 	check 'set "${x-a b c}";   echo $#' 1
    249 	check 'set "${x-"a b" c}"; echo $1' 'a b c'
    250 	check 'for i in "${x-a b c}"; do echo "z${i}z"; done' 'za b cz'
    251 }
    252 
    253 atf_test_case dollar_at
    254 dollar_at_head() {
    255 	atf_set "descr" "Checks that field splitting works when expanding" \
    256 	                "\$@"
    257 }
    258 dollar_at_body() {
    259 	unset x
    260 
    261 	TEST=0
    262 	# Check we get "$@" right
    263 
    264 	check 'set --;        for i in x"$@"x;  do echo "z${i}z"; done' 'zxxz'
    265 	check 'set a;         for i in x"$@"x;  do echo "z${i}z"; done' 'zxaxz'
    266 	check 'set a b;       for i in x"$@"x;  do echo "z${i}z"; done' 'zxaz zbxz'
    267 
    268 	check 'set --;        for i;            do echo "z${i}z"; done' ''
    269 	check 'set --;        for i in $@;      do echo "z${i}z"; done' ''
    270 	check 'set --;        for i in "$@";    do echo "z${i}z"; done' ''
    271 	# atf_expect_fail "PR bin/50834"
    272 	check 'set --;        for i in ""$@;    do echo "z${i}z"; done' 'zz'
    273 	# atf_expect_pass
    274 	check 'set --;        for i in $@"";    do echo "z${i}z"; done' 'zz'
    275 	check 'set --;        for i in ""$@"";  do echo "z${i}z"; done' 'zz'
    276 	check 'set --;        for i in """$@";  do echo "z${i}z"; done' 'zz'
    277 	check 'set --;        for i in "$@""";  do echo "z${i}z"; done' 'zz'
    278 	check 'set --;        for i in """$@""";do echo "z${i}z"; done' 'zz'
    279 
    280 	check 'set "";        for i;            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;            do echo "z${i}z"; done' 'zz zz'
    283 	check 'set "" "";     for i in "$@";    do echo "z${i}z"; done' 'zz zz'
    284 	check 'set "" "";     for i in $@;      do echo "z${i}z"; done' ''
    285 
    286 	check 'set "a b" c;   for i;            do echo "z${i}z"; done' \
    287 		'za bz zcz'
    288 	check 'set "a b" c;   for i in "$@";    do echo "z${i}z"; done' \
    289 		'za bz zcz'
    290 	check 'set "a b" c;   for i in $@;      do echo "z${i}z"; done' \
    291 		'zaz zbz zcz'
    292 	check 'set " a b " c; for i in "$@";    do echo "z${i}z"; done' \
    293 		'z a b z zcz'
    294 }
    295 
    296 atf_test_case ifs
    297 ifs_head() {
    298 	atf_set "descr" "Checks that IFS correctly configures field" \
    299 	                "splitting behavior"
    300 }
    301 ifs_body() {
    302 	unset x
    303 
    304 	TEST=0
    305 	# Some IFS tests
    306 	check 't="-- ";    IFS=" ";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '0'
    307 	check 't=" x";     IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
    308 	check 't=" x ";    IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
    309 	check 't=axb;      IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a:b'
    310 	check 't="a x b";  IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 a : b'
    311 	check 't="a xx b"; IFS="x";  set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a :: b'
    312 	check 't="a xx b"; IFS="x "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '3 a::b'
    313 	# A recent 'clarification' means that a single trailing IFS non-whitespace
    314 	# doesn't generate an empty parameter
    315 	check 't="xax";  IFS="x";     set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a'
    316 	check 't="xax "; IFS="x ";   set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '2 :a'
    317 	# Verify that IFS isn't being applied where it shouldn't be.
    318 	check 'IFS="x";             set axb; IFS=":"; r="$*"; IFS=; echo $# $r' '1 axb'
    319 }
    320 
    321 atf_test_case var_length
    322 var_length_head() {
    323 	atf_set "descr" "Checks that field splitting works when expanding" \
    324 	                "a variable's length"
    325 }
    326 var_length_body() {
    327 	TEST=0
    328 
    329 	long=12345678123456781234567812345678
    330 	long=$long$long$long$long
    331 	export long
    332 
    333 	# first test that the test method works...
    334 	check 'set -u; : ${long}; echo ${#long}' '128'
    335 
    336 	# Check that we apply IFS to ${#var}
    337 	check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' \
    338 		'128 1 8 3'
    339 	check 'IFS=2; set ${x-${#long}};   IFS=" "; echo $* $#'   '1 8 2'
    340 	check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#'   '128 1'
    341 }
    342 
    343 atf_init_test_cases() {
    344 	atf_add_test_case for
    345 	atf_add_test_case default_val
    346 	atf_add_test_case replacement_val
    347 	atf_add_test_case ifs_alpha
    348 	atf_add_test_case quote
    349 	atf_add_test_case dollar_at
    350 	atf_add_test_case ifs
    351 	atf_add_test_case var_length
    352 }
    353