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