1# $NetBSD: t_patterns.sh,v 1.4 2018/11/18 13:41:24 kre Exp $ 2# 3# Copyright (c) 2018 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# the implementation of "sh" to test 28: ${TEST_SH:=/bin/sh} 29 30# 31# This file tests pattern matching (glob) 32# 33# Three forms: 34# standard filename expansion (echo *.c) 35# case statements (case word in (*.c) ...;;) 36# var expansions with substring matching ${var%*.c} 37# 38# Note: the emphasis here is on testing the various possible patterns, 39# not that case statements, or var expansions (etc) work in general. 40 41### Helper functions 42 43nl=' 44' 45reset() 46{ 47 TEST_NUM=0 48 TEST_FAILURES='' 49 TEST_FAIL_COUNT=0 50 TEST_ID="$1" 51} 52 53# Test run & validate. 54# 55# $1 is the command to run (via sh -c) 56# $2 is the expected output (with any \n's in output replaced by spaces) 57# $3 is the expected exit status from sh 58# 59# Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0 60# in which case some message there is expected (and nothing is a failure). 61# When non-zero exit is expected, we note a different (non-zero) value 62# observed, but do not fail the test because of that. 63 64check() 65{ 66 fail=false 67 # Note TEMP_FILE must not be in the current directory (or nearby). 68 TEMP_FILE=$( mktemp /tmp/OUT.XXXXXX ) 69 TEST_NUM=$(( $TEST_NUM + 1 )) 70 MSG= 71 72 # our local shell (ATF_SHELL) better do quoting correctly... 73 # some of the tests expect us to expand $nl internally... 74 CMD="$1" 75 76 result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )" 77 STATUS=$? 78 79 if [ "${STATUS}" -ne "$3" ]; then 80 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" 81 MSG="${MSG} expected exit code $3, got ${STATUS}" 82 83 # don't actually fail just because of wrong exit code 84 # unless we either expected, or received "good" 85 # or something else is detected as incorrect as well. 86 case "$3/${STATUS}" in 87 (*/0|0/*) fail=true;; 88 esac 89 fi 90 91 if [ "$3" -eq 0 ]; then 92 if [ -s "${TEMP_FILE}" ]; then 93 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" 94 MSG="${MSG} Messages produced on stderr unexpected..." 95 MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )" 96 fail=true 97 fi 98 else 99 if ! [ -s "${TEMP_FILE}" ]; then 100 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" 101 MSG="${MSG} Expected messages on stderr," 102 MSG="${MSG} nothing produced" 103 fail=true 104 fi 105 fi 106 rm -f "${TEMP_FILE}" 107 108 case "${result}" in 109 (*[!0-9" $nl"]*) 110 # A word of some kind: at least 1 char that is not digit or wsp 111 # Remove newlines (use local shell for this) 112 result="$( 113 set -f 114 IFS="$nl" 115 set -- $result 116 IFS=' ' 117 printf '%s' "$*" 118 )" 119 ;; 120 (*[0-9]*) 121 # a numeric result, return just the number, trim whitespace 122 result=$(( ${result} )) 123 ;; 124 (*) 125 # whitespace only, or empty string: just leave it as is 126 ;; 127 esac 128 129 if [ "$2" != "${result}" ] 130 then 131 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" 132 MSG="${MSG} Expected output '$2', received '$result'" 133 fail=true 134 fi 135 136 if $fail 137 then 138 MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" 139 MSG="${MSG} Full command: <<${CMD}>>" 140 fi 141 142 $fail && test -n "$TEST_ID" && { 143 TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}" 144 TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:" 145 TEST_FAILURES="${TEST_FAILURES} Test of '$1' failed."; 146 TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}" 147 TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) 148 return 0 149 } 150 $fail && atf_fail "Test[$TEST_NUM] failed: $( 151 # ATF does not like newlines in messages, so change them... 152 printf '%s' "${MSG}" | tr '\n' ';' 153 )" 154 return 0 155} 156 157results() 158{ 159 test -n "$1" && atf_expect_fail "$1" 160 161 test -z "${TEST_ID}" && return 0 162 test -z "${TEST_FAILURES}" && return 0 163 164 echo >&2 "==========================================" 165 echo >&2 "While testing '${TEST_ID}'" 166 echo >&2 " - - - - - - - - - - - - - - - - -" 167 echo >&2 "${TEST_FAILURES}" 168 169 atf_fail \ 170 "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr" 171} 172 173####### End helpers 174 175atf_test_case filename_expansion 176filename_expansion() { 177 atf_set descr "Test correct operation of filename expansion" 178} 179filename_expansion_body() { 180 atf_require_prog mktemp 181 atf_require_prog wc 182 atf_require_prog mv 183 atf_require_prog rm 184 atf_require_prog mkdir 185 186 reset filename_expansion 187 188 # First create a known set of filenames to match against 189 190 # Note: This creates almost 17000 files/directories, so 191 # needs at least that many free inodes (only space consumed 192 # is for the directory contents, with a 1K frag size, it 193 # should be about 1.2MiB). Switching to making links would 194 # save inodes, but would require running "ln" many times, so 195 # would be a lot slower. 196 197 # This should work on a case insensitive, but preserving, 198 # filesystem - but case sensitive filesystems are preferred. 199 200 D=$(mktemp -d "DIR.$$.XXXXXX") || atf_fail "mktemp -d failed" 201 cd "${D}" || atf_fail "cd to temp dir '$D' failed" 202 203 # we need another level of directory, so we know what 204 # files to expect in ".." (ie: in $D) - only ".D". 205 mkdir .D && cd .D || atf_fail "failed to make or enter .D in $D" 206 207 > Xx || atf_fail "Unable to make files in temporary directory" 208 case "$( printf '%s\n' *)" in 209 (Xx) rm Xx || atf_fail "Unable to delete file";; 210 (\*) atf_fail "Created file vanished";; 211 (xx|XX|xX) atf_skip "Case preserving filesystem required";; 212 (*) atf_fail "Unexpected file expansion for '*'";; 213 esac 214 215 # from here on we make files/directories that we will be 216 # using in the tests. 217 218 # CAUTION: Change *any* of this and the expected results from the 219 # tests will all need verifying and updating as well. 220 221 mkdir D || atf_fail "mkdir D failed" 222 223 for F in a b c e V W X Y 1 2 3 4 5 \\ \] \[ \* \? \- \! \^ \| \# \' \" 224 do 225 > "${F}" 226 > ".${F}" 227 > "${F}.${F}" 228 > "${F}-${F}" 229 > "${F}${F}${F}${F}" 230 > "x${F}z" 231 > ab"${F}"yz 232 > "m${F}n${F}p${F}q" 233 234 > "D/${F}" 235 > "D/.${F}" 236 > "D/${F}${F}${F}${F}" 237 238 mkdir "D${F}" || atf_fail "mkdir 'D${F}' failed" 239 mkdir ".D${F}" || atf_fail "mkdir '.D${F}' failed" 240 241 for G in a b c e W X Y 0 2 4 6 \\ \] \[ \* \? \- \! \^ \| \# 242 do 243 > "${F}${G}" 244 > "${F}${G}${G}" 245 > "${F}${G}${F}" 246 > "${G}${F}${G}" 247 > "${F}${G}${G}${F}" 248 > "${G}${F}${F}${G}" 249 > "${F}.${G}" 250 > "${F}${G}.${G}${G}" 251 > "${F}${G}${F}${G}.${G}" 252 > "x${F}${G}y" 253 > "${F}z${G}" 254 > "${G}zz${F}" 255 > "${G}+${G}" 256 257 > "D${F}/${G}" 258 > "D${F}/.${G}" 259 > "D${F}/${G}${F}${G}" 260 > "D${F}/.${G}${F}${G}" 261 262 > ".D${F}/${G}" 263 > ".D${F}/.${G}" 264 > ".D${F}/${G}${F}${G}" 265 > ".D${F}/.${G}${F}${G}" 266 267 mkdir "D${F}/D${G}" "D${F}/D${F}${G}" || 268 atf_fail \ 269 "subdir mkdirs failed D${F}/D${G} D${F}/D${F}${G}" 270 271 > "D${F}/D${G}/${G}" 272 > "D${F}/D${G}.${G}" 273 > "D${F}/D${G}/${F}${G}" 274 > "D${F}/D${G}/${G}${F}${G}" 275 > "D${F}/D${G}/.${G}${F}${G}" 276 > "D${F}/D${G}/.${G}${F}${G}" 277 278 > "D${F}/D${F}${G}/${G}" 279 > "D${F}/D${F}${G}.${G}" 280 > "D${F}/D${F}${G}/${G}${F}" 281 > "D${F}/D${F}${G}/${G}${G}${F}" 282 > "D${F}/D${F}${G}/.${F}${F}${G}" 283 > "D${F}/D${F}${G}/.${G}${F}${F}" 284 285 done 286 done 287 288 # Debug hooks ... run with environment var set to filename 289 290 case "${ATF_TEST_SAVE_FILENAMES}" in 291 '') ;; 292 /*) ls -R >"${ATF_TEST_SAVE_FILENAMES}" ;; 293 *) ls -R >"${TMPDIR:-/tmp}/${ATF_TEST_SAVE_FILENAMES}" ;; 294 esac 295 case "${ATF_TEST_SAVE_FILES}" in 296 '') ;; 297 /*) (cd ../..; tar cf "${ATF_TEST_SAVE_FILES}" D) ;; 298 *) (cd ../..; tar cf "${TMPDIR:-/tmp}/${ATF_TEST_SAVE_FILES}" D) ;; 299 esac 300 301 # Now we have lots of files, try some matching 302 303 # First just check that "printf | wc -l" works properly... 304 check 'printf "%s\n" 1 2 3 | wc -l' '3' 0 #1 305 306 # Next a whole bunch of elementary patterns 307 check 'printf "%s\n" ab* | wc -l' '31' 0 308 check 'printf "%s\n" x*y | wc -l' '525' 0 309 check 'printf "%s\n" * | wc -l' '5718' 0 310 check 'printf "%s\n" ? | wc -l' '26' 0 #5 311 check 'printf "%s\n" ?? | wc -l' '550' 0 312 check 'printf "%s\n" ??? | wc -l' '2297' 0 313 check 'printf "%s\n" ???? | wc -l' '1745' 0 314 check 'printf "%s\n" ????? | wc -l' '550' 0 315 316 check 'printf "%s\n" ?????? | wc -l' '525' 0 #10 317 check 'printf "%s\n" ??????? | wc -l' '25' 0 318 check 'printf "%s\n" ???????? | wc -l' '1' 0 319 check 'printf "%s\n" ????????' '????????' 0 320 check 'printf "%s\n" m* | wc -l' '25' 0 321 check 'printf "%s\n" -* | wc -l' '206' 0 #15 322 check 'printf "%s\n" *- | wc -l' '227' 0 323 check 'printf "%s\n" -? | wc -l' '21' 0 324 check 'printf "%s\n" ?- | wc -l' '26' 0 325 check 'printf "%s\n" [ab] | wc -l' '2' 0 326 327 check 'printf "%s\n" [ab]* | wc -l' '437' 0 #20 328 check 'printf "%s\n" [A-Z]* | wc -l' '815' 0 329 check 'printf "%s\n" [0-4]* | wc -l' '830' 0 330 check 'printf "%s\n" [-04]* | wc -l' '488' 0 331 check 'printf "%s\n" [40-]* | wc -l' '488' 0 332 check 'printf "%s\n" *[0-9] | wc -l' '1057' 0 #25 333 check 'printf "%s\n" *[0-9]* | wc -l' '2109' 0 334 check 'printf "%s\n" ?[0-9]* | wc -l' '855' 0 335 check 'printf "%s\n" ?[0-9]? | wc -l' '270' 0 336 check 'printf "%s\n" *[0-9]? | wc -l' '750' 0 337 338 check 'printf "%s\n" [a-c][0-9]? | wc -l' '33' 0 #30 339 check 'printf "%s\n" [[:alpha:]] | wc -l' '9' 0 340 check 'printf "%s\n" [[:alpha:][:digit:]] | wc -l' '14' 0 341 check 'printf "%s\n" [[:alpha:]][[:digit:]] | wc -l' '37' 0 342 check \ 343 'printf "%s\n" [[:alpha:][:digit:]][[:alpha:][:digit:]] | wc -l' \ 344 '156' 0 345 check 'printf "%s\n" D*/*a | wc -l' '152' 0 #35 346 check 'printf "%s\n" D?/*a | wc -l' '150' 0 347 check 'printf "%s\n" D*/?a | wc -l' '25' 0 348 check 'printf "%s\n" D?/?a | wc -l' '25' 0 349 check 'printf "%s\n" */*a | wc -l' '152' 0 350 351 check 'printf "%s\n" [A-Z]*/*a | wc -l' '152' 0 #40 352 check 'printf "%s\n" ??/*a | wc -l' '150' 0 353 check 'printf "%s\n" .*/*a | wc -l' '277' 0 354 check 'printf "%s\n" .?*/*a | wc -l' '50' 0 355 check 'printf "%s\n" *-/-* | wc -l' '2' 0 356 check 'printf "%s\n" *-/-*' 'D-/- D-/---' 0 #45 357 358 # now some literal magic chars 359 check 'printf "%s\n" \?* | wc -l' '206' 0 360 check 'printf "%s\n" *\?* | wc -l' '471' 0 361 check 'printf "%s\n" \*? | wc -l' '21' 0 362 check 'printf "%s\n" \** | wc -l' '206' 0 363 364 check 'printf "%s\n" *\?* | wc -l' '471' 0 #50 365 check 'printf "%s\n" \[?] | wc -l' '3' 0 366 check 'printf "%s\n" \[?]' '[.] []] [z]' 0 367 check 'printf "%s\n" *\[* | wc -l' '471' 0 368 check 'printf "%s\n" \?\?* | wc -l' '5' 0 369 check 'printf "%s\n" \?\?*' '?? ??.?? ??? ???? ????.?' 0 #55 370 check 'printf "%s\n" [A\-C]* | wc -l' '206' 0 371 check 'printf "%s\n" [-AC]* | wc -l' '206' 0 372 check 'printf "%s\n" [CA-]* | wc -l' '206' 0 373 check 'printf "%s\n" [A\]-]? | wc -l' '42' 0 374 375 check 'printf "%s\n" []A\-]? | wc -l' '42' 0 #60 376 check 'printf "%s\n" []A-]? | wc -l' '42' 0 377 check 'printf "%s\n" \\* | wc -l' '206' 0 378 check 'printf "%s\n" [[-\]]?\?* | wc -l' '12' 0 379 check 'printf "%s\n" []\\[]?\? | wc -l' '9' 0 380 check 'printf "%s\n" *\\\\ | wc -l' '52' 0 #65 381 check 'printf "%s\n" [*][?]* | wc -l' '6' 0 382 check 'printf "%s\n" "*?"* | wc -l' '6' 0 383 check "printf '%s\\n' '\\'*\\\\ | wc -l" '61' 0 384 check 'printf "%s\n" ["a-b"]* | wc -l' '643' 0 385 386 check 'printf "%s\n" ["A-C"]z[[] | wc -l' '1' 0 #70 387 check 'printf "%s\n" ["A-C"]z[[]' '-z[' 0 388 check 'printf "%s\n" ?"??"* | wc -l' '54' 0 389 check 'printf "%s\n" \??\?* | wc -l' '52' 0 390 check 'printf "%s\n" [?][\?]* | wc -l' '5' 0 391 check 'printf "%s\n" [?][\?]*' '?? ??.?? ??? ???? ????.?' 0 #75 392 check 'printf "%s\n" [!ab] | wc -l' '24' 0 393 check 'printf "%s\n" [!ab]* | wc -l' '5281' 0 394 check 'printf "%s\n" [!A-D]* | wc -l' '5692' 0 395 check 'printf "%s\n" [!0-3]* | wc -l' '5094' 0 396 397 check 'printf "%s\n" [!-03]* | wc -l' '5265' 0 #80 398 check 'printf "%s\n" [!30-]* | wc -l' '5265' 0 399 check 'printf "%s\n" [!0\-3]* | wc -l' '5265' 0 400 check 'printf "%s\n" [\!0-3]* | wc -l' '830' 0 401 check 'printf "%s\n" [0-3!]* | wc -l' '830' 0 402 check 'printf "%s\n" [0!-3]* | wc -l' '1790' 0 #85 403 check 'printf "%s\n" *[!0-3] | wc -l' '5156' 0 404 check 'printf "%s\n" *[!0-3]* | wc -l' '5680' 0 405 check 'printf "%s\n" ?[!0-3]* | wc -l' '5231' 0 406 check 'printf "%s\n" ?[!0-3]? | wc -l' '2151' 0 407 408 check 'printf "%s\n" *[!0-3]? | wc -l' '5284' 0 #90 409 check 'printf "%s\n" [!a-c][!0-3]? | wc -l' '1899' 0 410 check 'printf "%s\n" [![:alpha:]] | wc -l' '17' 0 411 check 'printf "%s\n" [![:alpha:][:digit:]] | wc -l' '12' 0 412 check 'printf "%s\n" [![:alpha:]][[:digit:]] | wc -l' '68' 0 413 check 'printf "%s\n" [[:alpha:]][![:digit:]] | wc -l' '156' 0 #95 414 check 'printf "%s\n" [![:alpha:]][![:digit:]] | wc -l' '289' 0 415 check 'printf "%s\n" [!A-Z]*/*a | wc -l' '1' 0 416 check 'printf "%s\n" [!A-Z]*/*a' '[!A-Z]*/*a' 0 417 check 'printf "%s\n" [!A\-D]* | wc -l' '5486' 0 418 419 check 'printf "%s\n" [!-AD]* | wc -l' '5486' 0 #100 420 check 'printf "%s\n" [!DA-]* | wc -l' '5486' 0 421 check 'printf "%s\n" [!A\]-]? | wc -l' '508' 0 422 check 'printf "%s\n" [!]A\-]? | wc -l' '508' 0 423 check 'printf "%s\n" [!]A-]? | wc -l' '508' 0 424 check 'printf "%s\n" [![-\]]?\?* | wc -l' '164' 0 #105 425 check 'printf "%s\n" [!]\\[]?\? | wc -l' '93' 0 426 check 'printf "%s\n" [!*][?]* | wc -l' '171' 0 427 check 'printf "%s\n" [*][!?]* | wc -l' '199' 0 428 check 'printf "%s\n" [!*][!?]* | wc -l' '5316' 0 429 430 check 'printf "%s\n" [!"a-b"]* | wc -l' '5075' 0 #110 431 check 'printf "%s\n" ["!a-b"]* | wc -l' '849' 0 432 check 'printf "%s\n" [!"A-D"]z[[] | wc -l' '24' 0 433 check 'printf "%s\n" ["!A-D"]z[[] | wc -l' '2' 0 434 check 'printf "%s\n" ["!A-D"]z[[]' '!z[ -z[' 0 435 check 'printf "%s\n" ["A-D"]z[![] | wc -l' '20' 0 #115 436 check 'printf "%s\n" [!"A-D"]z[![] | wc -l' '480' 0 437 check 'printf "%s\n" ["!A-D"]z[![] | wc -l' '40' 0 438 check 'printf "%s\n" [!?][\?]* | wc -l' '172' 0 439 check 'printf "%s\n" [?][!\?]* | wc -l' '200' 0 440 441 check 'printf "%s\n" [!?][!\?]* | wc -l' '5315' 0 #120 442 check 'printf "%s\n" [!?][?!]* | wc -l' '343' 0 443 check 'printf "%s\n" [?][\?!]* | wc -l' '11' 0 444 check "printf '%s\\n' [\']*[!#] | wc -l" '164' 0 445 check 'printf "%s\n" [\"]*[\|] | wc -l' '6' 0 446 check 'printf "%s\n" [\"]*[\|]' '".| "z| "| "|"|.| "|.|| "||' 0 #125 447 check "printf '%s\\n' '\"['* | wc -l" '6' 0 448 check "printf '%s\\n' '\"['*" '"[ "[" "["[.[ "[.[[ "[[ "[["' 0 449 450 # Now test cases where the pattern is the result of a 451 # variable expansion (will assume, for now, that cmdsub & arith 452 # work the same way, so omit tests using those) 453 # we need to check both unquoted & quoted var expansions, 454 # expansions that result from ${xxx-xxx} and ${xxx%yyy} 455 # and expansions that form just part of the eventual pattern. 456 457 check 'var="x*y";printf "%s\n" ${var} | wc -l' '525' 0 458 check 'var="[a-e]?[0-9]";printf "%s\n" ${var} | wc -l' '48' 0 459 460 check 'var="[a-e]?.*";printf "%s\n" ${var} | wc -l' '84' 0 #130 461 check 'var="[a-e]\?.*";printf "%s\n" ${var} | wc -l' '4' 0 462 check 'var="[a-e]\?.*";printf "%s\n" ${var}' 'a?.?? b?.?? c?.?? e?.??' 0 463 464 # and if you're looking for truly weird... 465 466 check 'set -- a b; IFS=\?; printf "%s\n" "$*" | wc -l' '1' 0 467 check 'set -- a b; IFS=\?; printf "%s\n" "$*"' 'a?b' 0 468 check 'set -- a b; IFS=\?; printf "%s\n" $* | wc -l' '2' 0 #boring #135 469 check 'set -- a b; IFS=\?; var=$*; unset IFS; printf "%s\n" ${var}' \ 470 'a.b abb azb' 0 471 check 'set -- a b; IFS=\?; var=$*; unset IFS; printf "%s\n" "${var}"' \ 472 'a?b' 0 473 check 'set -- a \?; IFS=\\; printf "%s\n" "$*"' 'a\?' 0 474 check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" "${var}"' \ 475 'a\?' 0 476 477 check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" ${var}' \ 478 'a?' 0 #140 479 mv 'a?' 'a@' 480 check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" ${var}' \ 481 'a\?' 0 482 mv 'a@' 'a?' 483 484 # This is unspecified by POSIX, but everyone (sane) does it this way 485 check 'printf "%s\n" D*[/*] | wc -l' '6' 0 486 check 'printf "%s\n" D*[\/*] | wc -l' '6' 0 487 check 'printf "%s\n" D*\[/*] | wc -l' '6' 0 488 check 'printf "%s\n" D*\[\/*] | wc -l' '6' 0 #145 489 check 'printf "%s\n" D*[/*]' \ 490 'D[/D[] D[/D[].] D[/D] D[/D].] D[/] D[/][]' 0 491 492 # '^' as the first char in a bracket expr is unspecified by POSIX, 493 # but for compat with REs everyone (sane) makes it the same as ! 494 495 # But just in case we are testing an insane shell ... 496 ${TEST_SH} -c 'case "^" in ([^V^]) exit 1;; (*) exit 0;; esac' && { 497 498 check 'printf "%s\n" [^ab] | wc -l' '24' 0 499 check 'printf "%s\n" [^ab]* | wc -l' '5281' 0 500 check 'printf "%s\n" [^A-D]* | wc -l' '5692' 0 501 502 check 'printf "%s\n" [^0-3]* | wc -l' '5094' 0 #150 503 check 'printf "%s\n" [^-03]* | wc -l' '5265' 0 504 check 'printf "%s\n" [^0\-3]* | wc -l' '5265' 0 505 check 'printf "%s\n" [^-a3]* | wc -l' '5110' 0 506 check 'printf "%s\n" [\^-a3]* | wc -l' '608' 0 507 check 'printf "%s\n" [\^0-3]* | wc -l' '830' 0 #155 508 check 'printf "%s\n" [0-3^]* | wc -l' '830' 0 509 check 'printf "%s\n" [0^-a]* | wc -l' '513' 0 510 check 'printf "%s\n" *[^0-3] | wc -l' '5156' 0 511 check 'printf "%s\n" [!^]? | wc -l' '529' 0 512 513 check 'printf "%s\n" [^!]? | wc -l' '529' 0 #160 514 check 'printf "%s\n" [!!^]? | wc -l' '508' 0 515 check 'printf "%s\n" [!^!]? | wc -l' '508' 0 516 check 'printf "%s\n" [^!]? | wc -l' '529' 0 517 check 'printf "%s\n" [^!^]? | wc -l' '508' 0 518 check 'printf "%s\n" [^^!]? | wc -l' '508' 0 #165 519 check 'printf "%s\n" [!^-b]? | wc -l' '487' 0 520 check 'printf "%s\n" [^!-b]? | wc -l' '63' 0 521 522 } 523 524 # No need to clean up the directory, we're in the ATF working 525 # directory, and ATF cleans up for us. 526 527 results 528} 529 530atf_test_case case_matching 531case_matching_head() { 532 atf_set descr "Test expansion of vars with embedded cmdsub" 533} 534 535# helper functions for case matching 536# 537# usage: cm word [ pattern ] [ preamble ] (expect word to match pattern) 538# cf word [ pattern ] [ preamble ] (expect word to fail to match) 539# 540# The last used (non-null) pattern, and the last used preamble, are 541# remembered and used again if only the word is given. To give a 542# new preamble while using the last pattern, give '' as the pattern. 543# 544# nb: a null (empty) pattern is a syntax error, to get '' use "''" 545# 546cm() { 547 case "$2" in 548 '') set -- "$1" "${LAST_PATTERN}" "${3:-${LAST_PFX}}";; 549 *) LAST_PATTERN="$2";; 550 esac 551 LAST_PFX="$3" 552 553 check \ 554 "${3:+${3}; }case $1 in ($2) printf M;; (*) printf X;; esac" M 0 555} 556cf() { 557 case "$2" in 558 '') set -- "$1" "${LAST_PATTERN}" "${3:-${LAST_PFX}}";; 559 *) LAST_PATTERN="$2";; 560 esac 561 LAST_PFX="$3" 562 563 check \ 564 "${3:+${3}; }case $1 in ($2) printf M;; (*) printf X;; esac" X 0 565} 566 567case_matching_body() { 568 569 # nb: we are not testing execution of case, so no ;& or alternate 570 # patterns (etc) are needed here, we just want to validate the 571 # case variant of pattern matching, so simple one word, one pattern 572 # match or not match. 573 574 reset case_matching 575 576 cm abcd 'ab*'; cf bcda; cf aabce; cm ab # 4 577 cm abcd '$var' 'var="ab*"'; cf abcd '"$var"' 'var="ab*"' # 6 578 579 cm xy 'x*y'; cm xyxy; cm '"x*y"'; cf xxyz # 10 580 581 cm '""' '*'; cm '\*'; cm '\?'; cm -; cm 12345 # 15 582 cm abcd '$var' 'var="*"'; cf abcd '"$var"' 'var="*"' # 17 583 cm '"*"' '\*'; cm '"*"' '"*"'; cm '"*"' '"$var"' 'var="*"' # 20 584 585 cm X '?'; cf XX '?'; cf X '"?"'; cm Y '$var' 'var="?"' # 24 586 cf Z '"$var"' 'var="?"'; cm '"?"' '"$var"' 'var="?"' # 26 587 588 cm XA '??'; cf X '??'; cf XX '"??"'; cm YZ '$var' 'var="??"' # 30 589 cf ZZ '"$var"' 'var="??"'; cm '"??"' '"$var"' 'var="??"' # 32 590 591 cm a '[ab]'; cm b; cf c; cf aa; cf '"[ab]"' # 37 592 cm '"[ab]"' '"[ab]"'; cm '"[ab]"' '\[ab]' # 39 593 cm a '$var' 'var="[ab]"'; cf a '"$var"' 'var="[ab]"' # 41 594 cm '"[ab]"' '"$var"' 'var="[ab]"'; cm a '["$var"]' 'var=ab' # 43 595 596 cm b '[a-c]'; cm a '[a-c]'; cm c '[a-c]'; cf d '[a-c]' # 47 597 cf '"[a-c]"' '[a-c]'; cm '"[a-c]"' '"[a-c]"' # 49 598 cm '"[a-c]"' '\[a-c]'; cm '"[a-c]"' '[a-c\]' # 51 599 cm a '$var' 'var="[a-c]"'; cf a '"$var"' 'var="[a-c]"' # 53 600 cm '"[a-c]"' '"$var"' 'var="[a-c]"'; cf b '["$var"]' 'var=a-c' # 55 601 602 cm 2 '[0-4]'; cm 0 '[0-4]'; cf - '[0-4]'; cm 0 '[-04]' # 59 603 cf 2 '[-04]'; cf 2 '[40-]'; cm 0 '[40-]'; cm - '[-04]' # 63 604 cf 2 '[0\-4]'; cm - '[0\-4]'; cf 2 '["0-4"]'; cm - '["0-4"]' # 67 605 cf 2 "[0'-'4]"; cm - "[0'-'4]"; cm 4 "[0'-'4]" # 70 606 cm 0 "['0'-'4']"; cf '"\\"' '[0\-4]'; cm '"\\"' '[\\0-\\4]' # 73 607 608 cm a '[[:alpha:]]'; cf 0; cf '"["'; cm Z; cf aa; cf .; cf '""' # 80 609 cf a '[[:digit:]]'; cm 0; cf '"["'; cm 9; cf 10; cf .; cf '""' # 87 610 cm '"["' '[][:alpha:][]'; cf a '[\[:alpha:]]'; cf a '[[\:alpha:]]' #90 611 cm a '[$var]' 'var="[:alpha:]"'; cm a '[[$var]]' 'var=":alpha:"' # 92 612 cm a '[[:$var:]]' 'var=alpha'; cm B '[[:"$var":]]' 'var=alpha' # 94 613 cf B '["$var"]' 'var="[:alpha:]"'; cf B '[["$var"]]' 'var=":alpha:"' #96 614 cm '"["' '["$var"]' 'var="[:alpha:]"' # 97 615 cm '"[]"' '[["$var"]]' 'var=":alpha:"'; # 98 616 cm A3 '[[:alpha:]][[:digit:]]'; cf '"[["' #100 617 cm 3 '[[:alpha:][:digit:]]'; cf '"["'; cm A; cf '":"' #104 618 for W in AA A7 8x 77; do 619 cm "$W" '[[:alpha:][:digit:]][[:alpha:][:digit:]]' #108 620 done 621 622 cm dir/file '*/*'; cm /dir/file; cm /dir/file '*/file' #111 623 for W in aa/bcd /x/y/z .x/.abc --/--- '\\//\\//' '[]/[][]' 624 do 625 cm "'$W'" '??/*'; cm "'$W'" '[-a/.\\[]?/??*[]dzc/-]' 626 done #123 627 628 cm '"?abc"' '\?*'; cf '"\\abc"'; cm '"?"' #126 629 630 cm '\\z' '"\\z"'; cf '\z'; cf z; cf '"\\"' #130 631 632 cm '"[x?abc"' '[[-\]]?\?*'; cm '"]x?abc"'; cm '"\\x?abc"' #133 633 cf '"-x?abc"'; cf '"[xyzabc"'; cm '"[]?"' #136 634 635 cm '"[x?"' '[]\\[]?\?'; cm '"]x?"'; cm '"\\y?"'; cm '"[]?"' #140 636 637 cm "'\z'" '"\z"'; cf z; cm '\\z'; cm '$var' '' 'var="\z"' #144 638 cm '${var}' '' "var='\z'"; cm '"${var}"' #146 639 cf '${var}' '${var}' "var='\z'"; cm '${var}' '"${var}"' "var='\z'" #148 640 cf "'${var}'"; cm "'${var}'" "'${var}'" "var='\z'" #150 641 642 cf abc '"$*"' 'IFS=?; set -- a c';cf '"a c"';cm "'a?c'";cm '"$*"' #154 643 cf abc '"$*"' 'IFS=*; set -- a c';cf '"a c"';cm "'a*c'";cm '"$*"' #158 644 cf abc '"$*"' 'IFS=\\;set -- a c';cf '"a c"';cm "'a\c'";cm '"$*"' #162 645 cf abc '"$*"' 'IFS="";set -- a c';cf '"a c"';cm "'ac'"; cm '"$*"' #166 646 647 cm a '["$*"]' 'IFS=-; set -- a c';cf b;cm c;cm '-'; cf "']'" #171 648 cm a '["$*"]' 'IFS=?; set -- a c';cf b;cm c;cm '"?"'; cf "'['" #176 649 cm a '["$*"]' 'IFS=*; set -- a c';cf b;cm c;cm '"*"'; cf - #181 650 cm a '["$*"]' 'IFS=\\;set -- a c';cf b;cm c;cm "'\\'";cf "'$'" #186 651 cm a '["$*"]' 'IFS="";set -- a c';cf b;cm c #189 652 653 654 # Now repeat the ones using bracket expressions, adding ! 655 656 cf a '[!ab]'; cf b; cm c; cf aa; cf '"[!ab]"'; cm a '[ab!]'; cm ! #196 657 cf a '$var' 'var="[!ab]"';cm x;cf a '"$var"' 'var="[!ab]"'; cf x #200 658 cm '"[!ab]"' '"$var"' 'var="[!ab]"'; cf a; cf b; cf !; cf "'['" #205 659 cf a '[!"$var"]' 'var=ab'; cm x; cm a '["!$var"]' 'var=ab' #208 660 cf x; cm !; cm a '["$var"]' 'var=!ab'; cf x #212 661 cf a '[$var]' 'var=!ab'; cm ! #214 662 663 cf b '[!a-c]'; cf a; cf c; cm d; cm !; cm -; cm _; cm '\\' #222 664 cf a '$var' 'var="[!a-c]"'; cf b; cf c; cm d; cm !; cm - #228 665 666 cf 2 '[!0-4]'; cf 0; cm -; cf 4; cm !; cm "'['"; cm "']'" #235 667 cm 2 '[!-04]'; cm 2 '[!40-]'; cf 0; cf -; cm !; #240 668 cm 2 '[!0\-4]'; cf -; cm 2 '[!"0-4"]'; cf - #244 669 670 cf a '[![:alpha:]]'; cm 0; cm '"["'; cf aa; cm .; cf '""' #250 671 cf '"["' '[!][:alpha:][!]'; cf a; cm 0; cf !; cf "']'"; cm % #256 672 cf a '[$var]' 'var="![:alpha:]"'; cm 0; cm !; cm "']'"; cm @ #261 673 674 # Next some tests of patterns containing (intended literal) '\' 675 # The first of the "set" tests pair was reported as broken bu 676 # Martijn Dekker (private mail) (Nov 2018). 677 678 cm "'\\'" "'\\'"; cf "'\\'" "'\\\\'" #263 679 cm "'\\'" '"$var"' "var='\\'"; cf "'\\'" '$var' "var='\\'" #265 680 cm '$1' '"$2"' 'set -- \\ \\'; cf '$1' '$2' 'set -- \\ \\' #267 681 cf '$1' '"$2"' 'set -- \\ \\\\'; cm '$1' '$2' 'set -- \\ \\\\' #269 682 cm "'\\'" "\$( echo '\\\\' )"; cf "'\\'" "\$( echo '\\' )" #271 683 cm "'\\'" "\"\$( echo '\\' )\"" #272 684 cf "'\\'" "\"\$( echo '\\\\' )\"" #273 685 686 if X=$( ${TEST_SH} -c 'printf %s '"\$'\\\\'" 2>/dev/null ) && 687 [ "$X" = '\' ] 688 then 689 # TEST_SH supports $'...' so we can test it as well 690 # note these are $'\\' and $'\\\\' as patterns. 691 # They should be identical to '\' and '\\' 692 cm "'\\'" "\$'\\\\'"; cf "'\\'" "\$'\\\\\\\\'" #275 693 else 694 # uncomment this if we need to keep sub-test numbering sane 695 # it isn't needed as long as this remains last. 696 # (nb: this is just a repeat of sub-test 263) 697 698 # cm "'\\'" "'\\'"; cf "'\\'" "'\\\\'" #275 699 fi 700 701 results 702} 703 704atf_test_case var_substring_matching 705var_substring_matching_head() { 706 atf_set descr 'Test pattern matching in var expansions' 707} 708 709# Helper function for var substring matching 710# $1 is the input string 711# $2 the substring matching operator (# % ## or %%) 712# $3 is the pattern to match (or reference to one) 713# $4 is the expected output (result of applying op($2) with pat($3) to $1 714# $5 (if given, and not null) is a command (or commands) to run first 715# $6 (if given, and not null) cause the var expansion to be quoted 716# (ie "${var%pattern}" instead of just ${var%pattern}) 717# any quotes needed in "pattern" should be in $3 718# Note: a variable called "var" is used (set to $1, then expanded). 719vm() 720{ 721 check "${5:+${5}; }var='$1';printf '%s\n' ${6:+\"}\${var$2$3}${6:+\"}" \ 722 "$4" 0 723} 724 725var_substring_matching_body() { 726 727 reset var_substring_matching 728 729 vm abc \# a bc; vm aaab \# a aab; vm aaab \## 'a*a' b # 3 730 vm aaab % ab aa; vm xawab %% 'a*ab' x; vm abcd \# xyz abcd 731 vm file.c % .c 'f le' IFS=i ; vm file.c % .c file IFS=i Q 732 vm file.c % ?c file ; vm file.c % '"?c"' file.c # 9 10 733 734 vm abcabcabcded \# 'a*b' cabcabcded; vm abcabcabcded \## 'a*b' cded 735 vm abcabcabcded % 'c*d' abcabcab; vm abcabcabcded %% 'c*d' ab 736 737 vm abc.jpg % '.[a-z][!0-9]?' abc # 15 738 739 vm xxxyyy \# '${P}' yyy P=xxx; vm xxxyyy \# '${P}' yyy 'P=x?x' 740 vm xxxyyy \# '${P}' yyy 'P=x?x' Q 741 vm 'x?xyyy' \# '${P}' yyy 'P=x[?]x' 742 vm xxxyyy \# '${P}' xxxyyy 'P=x[?]x' # 20 743 vm 'x?xyyy' \# '${P}' yyy 'P=x?x' Q 744 vm xxxyyy \# '${P}' yyy 'P=x?x' Q 745 vm 'x?xyyy' \# '${P}' yyy 'P="x\?x"' 746 vm 'x?xyyy' \# '${P}' yyy 'P="x\?x"' Q 747 vm 'x?xyyy' \# '${P}' yyy 'P="x?x"' # 25 748 vm 'x?xyyy' \# '${P}' yyy 'P="x?x"' Q 749 vm 'x?xyyy' \# '"${P}"' 'x?xyyy' 'P="x\?x"' 750 vm 'x?xyyy' \# '"${P}"' 'x?xyyy' 'P="x\?x"' Q 751 vm 'x?xyyy' \# '"${P}"' yyy 'P="x?x"' 752 vm 'x?xyyy' \# '"${P}"' yyy 'P="x?x"' Q # 30 753 vm 'x%xyyy' \# '${P}' 'x%xyyy' 'P="x\?x"' 754 vm 'x%xyyy' \# '${P}' 'x%xyyy' 'P="x\?x"' Q 755 vm 'x%xyyy' \# '${P}' yyy 'P="x?x"' 756 vm 'x%xyyy' \# '${P}' yyy 'P="x?x"' Q 757 vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x\?x"' # 35 758 vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x\?x"' Q 759 vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x?x"' 760 vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x?x"' Q 761 762 vm abc \# '*' abc; vm abc \# '*' abc '' Q # 39 40 763 vm abc \# '"*"' abc; vm abc \# '"*"' abc '' Q 764 vm abc \# '"a"' bc; vm abc \# '"a"' bc '' Q 765 vm abc \## '*' ''; vm abc \## '*' '' '' Q 766 vm abc % '*' abc; vm abc % '*' abc '' Q 767 vm abc %% '*' ''; vm abc %% '*' '' '' Q # 49 50 768 vm abc \# '$P' abc 'P="*"'; vm abc \# '$P' abc 'P="*"' Q 769 vm abc \# '"$P"' abc 'P="*"'; vm abc \# '"$P"' abc 'P="*"' Q 770 vm abc \# '$P' bc 'P="[a]"'; vm abc \# '$P' bc 'P="[a]"' Q 771 vm abc \# '"$P"' abc 'P="[a]"'; vm abc \# '"$P"' abc 'P="[a]"' Q 772 vm '[a]bc' \# '$P' '[a]bc' 'P="[a]"' 773 vm '[a]bc' \# '"$P"' bc 'P="[a]"' # 60 774 vm '[a]bc' \# '"$P"' bc 'P="[a]"' Q 775 776 # The following two (62 & 63) are actually the same test. 777 # The double \\ turns into a single \ when parsed. 778 vm '[a]bc' \# '$P' bc 'P="\[a]"'; vm '[a]bc' \# '$P' bc 'P="\\[a]"' 779 vm '[a]bc' \# '"$P"' '[a]bc' 'P="\[a]"' 780 vm '\[a]bc' \# '"$P"' bc 'P="\[a]"' # 65 781 782 vm ababcdabcd \# '[ab]*[ab]' abcdabcd 783 vm ababcdabcd \## '[ab]*[ab]' cd 784 vm ababcdabcd \# '$P' abcdabcd 'P="[ab]*[ab]"' 785 vm ababcdabcd \## '$P' cd "P='[ab]*[ab]'" 786 vm ababcdabcd \# '$P' 'ab dab d' 'P="[ab]*[ab]"; IFS=c' # 70 787 vm ababcdabcd \# '$P' abcdabcd 'P="[ab]*[ab]"; IFS=c' Q 788 789 vm ababcdabcd \# '[ab]*[ba]' abcdabcd 790 vm ababcdabcd \# '[ab]*[a-b]' abcdabcd 791 vm ababcdabcd \## '[ba]*[ba]' cd 792 vm ababcdabcd \## '[a-b]*[ab]' cd # 75 793 794 vm abcde \# '?[b-d]?' de; vm abcde \## '?[b-d]?' de 795 vm abcde % '?[b-d]?' ab; vm abcde %% '?[b-d]?' ab 796 797 vm .123. \# '.[0-9][1-8]' 3.; vm .123. % '[0-9][1-8].' .1 # 80 81 798 vm .123. \# '?[0-9][1-8]' 3.; vm .123. % '[0-9][1-8]?' .1 799 vm .123. \# '*[0-9][1-8]' 3.; vm .123. % '[0-9][1-8]*' .1 # 85 800 vm .123. \## '*[0-9][1-8]' .; vm .123. %% '[0-9][1-8]*' . 801 vm .123. \# '[.][1][2]' 3. ; vm .123. % '[2][3][.]' .1 802 vm .123. \# '[?]1[2]' .123. ; vm .123. % '2[3][?]' .123. # 90 91 803 vm .123. \# '\.[0-9][1-8]' 3.;vm .123. % '[0-9][1-8]\.' .1 804 805 vm '[a-c]d-f' \# '[a-c\]' d-f 806 vm '[abcd]' \# '[[:alpha:]]' '[abcd]' # 95 807 vm '[1?234' \# '[[-\]]?\?' 234 808 vm '1-2-3-\?' % '-${P}' '1-2-3-\?' 'P="\\?"' 809 vm '1-2-3-\?' % '${P}' '1-2-3-\' 'P="\\?"' 810 vm '1-2-3-\?' % '-"${P}"' 1-2-3 'P="\\?"' # 99 811 812 results 813} 814 815 816atf_init_test_cases() { 817 # Listed here in the order ATF runs them, not the order from above 818 819 atf_add_test_case filename_expansion 820 atf_add_test_case case_matching 821 atf_add_test_case var_substring_matching 822} 823