Bash · 35243 bytes Raw Blame History
1 #!/bin/sh
2 # =====================================
3 # POSIX Compliance Quoting Test Suite for rush
4 # =====================================
5 # Tests quoting and escaping per IEEE Std 1003.1-2017
6 # Section: Shell Command Language - Quoting
7
8 # Colors (POSIX-compliant way)
9 RED='\033[0;31m'
10 GREEN='\033[0;32m'
11 YELLOW='\033[1;33m'
12 BLUE='\033[0;34m'
13 NC='\033[0m'
14
15 # Test identification
16 TEST_PREFIX="[posix-quoting]"
17 CURRENT_SECTION=""
18 TEST_NUM=0
19
20 PASSED=0
21 FAILED=0
22 SKIPPED=0
23 FAILED_TESTS_LIST=""
24
25 # Get script directory (POSIX way)
26 SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
27 RUSH_BIN="${RUSH_BIN:-$SCRIPT_DIR/../target/release/rush}"
28
29 # Check if fortsh exists
30 if [ ! -x "$RUSH_BIN" ]; then
31 printf "${RED}ERROR${NC}: rush binary not found at $RUSH_BIN\n"
32 printf "Please run 'make' first or set RUSH_BIN environment variable\n"
33 exit 1
34 fi
35
36 # Test result trackers
37 pass() {
38 TEST_NUM=$((TEST_NUM + 1))
39 printf "${GREEN}✓ PASS${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s\n" "$1"
40 PASSED=$((PASSED + 1))
41 }
42
43 fail() {
44 TEST_NUM=$((TEST_NUM + 1))
45 TEST_ID="${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}"
46 printf "${RED}✗ FAIL${NC} ${TEST_ID}: %s\n" "$1"
47 FAILED_TESTS_LIST="${FAILED_TESTS_LIST} ${TEST_ID}: $1\n"
48 if [ -n "$2" ]; then
49 printf " expected: %s\n" "$2"
50 fi
51 if [ -n "$3" ]; then
52 printf " got: %s\n" "$3"
53 fi
54 FAILED=$((FAILED + 1))
55 }
56
57 skip() {
58 TEST_NUM=$((TEST_NUM + 1))
59 printf "${YELLOW}⊘ SKIP${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s - %s\n" "$1" "$2"
60 SKIPPED=$((SKIPPED + 1))
61 }
62
63 section() {
64 CURRENT_SECTION=$(echo "$1" | grep -oE '^[0-9]+' || echo "0")
65 TEST_NUM=0
66 printf "\n"
67 printf "${BLUE}==========================================\n"
68 printf "%s\n" "$1"
69 printf "==========================================${NC}\n"
70 }
71
72 # =====================================
73 section "446. SINGLE QUOTES"
74 # =====================================
75
76 result=$("$RUSH_BIN" -c "echo 'hello world'" 2>&1)
77 if [ "$result" = "hello world" ]; then
78 pass "Single quotes preserve spaces"
79 else
80 fail "Single quotes preserve spaces" "hello world" "$result"
81 fi
82
83 result=$("$RUSH_BIN" -c "x=test; echo '\$x'" 2>&1)
84 if [ "$result" = '$x' ]; then
85 pass "Single quotes prevent variable expansion"
86 else
87 fail "Single quotes prevent variable expansion" '$x' "$result"
88 fi
89
90 result=$("$RUSH_BIN" -c "echo 'back\\slash'" 2>&1)
91 if [ "$result" = 'back\slash' ]; then
92 pass "Single quotes preserve backslash"
93 else
94 fail "Single quotes preserve backslash" 'back\slash' "$result"
95 fi
96
97 result=$("$RUSH_BIN" -c "echo 'has \"double\" quotes'" 2>&1)
98 if [ "$result" = 'has "double" quotes' ]; then
99 pass "Single quotes preserve double quotes"
100 else
101 fail "Single quotes preserve double quotes" 'has "double" quotes' "$result"
102 fi
103
104 result=$("$RUSH_BIN" -c "echo '\$(echo no)'" 2>&1)
105 if [ "$result" = '$(echo no)' ]; then
106 pass "Single quotes prevent command substitution"
107 else
108 fail "Single quotes prevent command substitution" '$(echo no)' "$result"
109 fi
110
111 # =====================================
112 section "447. DOUBLE QUOTES"
113 # =====================================
114
115 result=$("$RUSH_BIN" -c 'echo "hello world"' 2>&1)
116 if [ "$result" = "hello world" ]; then
117 pass "Double quotes preserve spaces"
118 else
119 fail "Double quotes preserve spaces" "hello world" "$result"
120 fi
121
122 result=$("$RUSH_BIN" -c 'x=test; echo "$x"' 2>&1)
123 if [ "$result" = "test" ]; then
124 pass "Double quotes allow variable expansion"
125 else
126 fail "Double quotes allow variable expansion" "test" "$result"
127 fi
128
129 result=$("$RUSH_BIN" -c 'echo "$(echo hello)"' 2>&1)
130 if [ "$result" = "hello" ]; then
131 pass "Double quotes allow command substitution"
132 else
133 fail "Double quotes allow command substitution" "hello" "$result"
134 fi
135
136 result=$("$RUSH_BIN" -c 'echo "has '\''single'\'' quotes"' 2>&1)
137 if [ "$result" = "has 'single' quotes" ]; then
138 pass "Double quotes preserve single quotes"
139 else
140 fail "Double quotes preserve single quotes" "has 'single' quotes" "$result"
141 fi
142
143 result=$("$RUSH_BIN" -c 'echo "escaped \"quote\""' 2>&1)
144 if [ "$result" = 'escaped "quote"' ]; then
145 pass "Double quotes with escaped quotes"
146 else
147 fail "Double quotes with escaped quotes" 'escaped "quote"' "$result"
148 fi
149
150 result=$("$RUSH_BIN" -c 'echo "back\\slash"' 2>&1)
151 if [ "$result" = 'back\slash' ]; then
152 pass "Double quotes with escaped backslash"
153 else
154 fail "Double quotes with escaped backslash" 'back\slash' "$result"
155 fi
156
157 result=$("$RUSH_BIN" -c 'echo "dollar\$sign"' 2>&1)
158 if [ "$result" = 'dollar$sign' ]; then
159 pass "Double quotes with escaped dollar"
160 else
161 fail "Double quotes with escaped dollar" 'dollar$sign' "$result"
162 fi
163
164 # =====================================
165 section "448. BACKSLASH ESCAPING"
166 # =====================================
167
168 result=$("$RUSH_BIN" -c 'echo hello\ world' 2>&1)
169 if [ "$result" = "hello world" ]; then
170 pass "Backslash escapes space"
171 else
172 fail "Backslash escapes space" "hello world" "$result"
173 fi
174
175 result=$("$RUSH_BIN" -c 'x=test; echo \$x' 2>&1)
176 if [ "$result" = '$x' ]; then
177 pass "Backslash escapes dollar sign"
178 else
179 fail "Backslash escapes dollar sign" '$x' "$result"
180 fi
181
182 result=$("$RUSH_BIN" -c 'echo back\\slash' 2>&1)
183 if [ "$result" = 'back\slash' ]; then
184 pass "Backslash escapes backslash"
185 else
186 fail "Backslash escapes backslash" 'back\slash' "$result"
187 fi
188
189 result=$("$RUSH_BIN" -c 'echo hello\
190 world' 2>&1)
191 if [ "$result" = "helloworld" ]; then
192 pass "Backslash-newline line continuation"
193 else
194 fail "Backslash-newline line continuation" "helloworld" "$result"
195 fi
196
197 # =====================================
198 section "449. MIXED QUOTING"
199 # =====================================
200
201 result=$("$RUSH_BIN" -c 'echo "hello"'\''world'\''' 2>&1)
202 if [ "$result" = "hello'world'" ]; then
203 pass "Adjacent double and single quotes"
204 else
205 fail "Adjacent double and single quotes" "hello'world'" "$result"
206 fi
207
208 result=$("$RUSH_BIN" -c 'x=val; echo "$x"'\''$x'\''' 2>&1)
209 if [ "$result" = 'val$x' ]; then
210 pass "Mixed expansion and literal"
211 else
212 fail "Mixed expansion and literal" 'val$x' "$result"
213 fi
214
215 result=$("$RUSH_BIN" -c "echo 'don'\\''t'" 2>&1)
216 if [ "$result" = "don't" ]; then
217 pass "Single quote in single quoted string"
218 else
219 fail "Single quote in single quoted string" "don't" "$result"
220 fi
221
222 # =====================================
223 section "450. QUOTING SPECIAL CHARACTERS"
224 # =====================================
225
226 result=$("$RUSH_BIN" -c 'echo "semi;colon"' 2>&1)
227 if [ "$result" = "semi;colon" ]; then
228 pass "Semicolon in double quotes"
229 else
230 fail "Semicolon in double quotes" "semi;colon" "$result"
231 fi
232
233 result=$("$RUSH_BIN" -c "echo 'pipe|char'" 2>&1)
234 if [ "$result" = "pipe|char" ]; then
235 pass "Pipe in single quotes"
236 else
237 fail "Pipe in single quotes" "pipe|char" "$result"
238 fi
239
240 result=$("$RUSH_BIN" -c 'echo "ampersand&here"' 2>&1)
241 if [ "$result" = "ampersand&here" ]; then
242 pass "Ampersand in double quotes"
243 else
244 fail "Ampersand in double quotes" "ampersand&here" "$result"
245 fi
246
247 result=$("$RUSH_BIN" -c "echo 'less<greater>'" 2>&1)
248 if [ "$result" = "less<greater>" ]; then
249 pass "Angle brackets in single quotes"
250 else
251 fail "Angle brackets in single quotes" "less<greater>" "$result"
252 fi
253
254 result=$("$RUSH_BIN" -c 'echo "paren(here)"' 2>&1)
255 if [ "$result" = "paren(here)" ]; then
256 pass "Parentheses in double quotes"
257 else
258 fail "Parentheses in double quotes" "paren(here)" "$result"
259 fi
260
261 result=$("$RUSH_BIN" -c "echo 'star*glob?'" 2>&1)
262 if [ "$result" = "star*glob?" ]; then
263 pass "Glob chars in single quotes"
264 else
265 fail "Glob chars in single quotes" "star*glob?" "$result"
266 fi
267
268 result=$("$RUSH_BIN" -c 'echo "hash#comment"' 2>&1)
269 if [ "$result" = "hash#comment" ]; then
270 pass "Hash in double quotes"
271 else
272 fail "Hash in double quotes" "hash#comment" "$result"
273 fi
274
275 # =====================================
276 section "451. EMPTY STRINGS"
277 # =====================================
278
279 result=$("$RUSH_BIN" -c 'echo ""' 2>&1)
280 if [ "$result" = "" ]; then
281 pass "Empty double-quoted string"
282 else
283 fail "Empty double-quoted string" "(empty)" "$result"
284 fi
285
286 result=$("$RUSH_BIN" -c "echo ''" 2>&1)
287 if [ "$result" = "" ]; then
288 pass "Empty single-quoted string"
289 else
290 fail "Empty single-quoted string" "(empty)" "$result"
291 fi
292
293 result=$("$RUSH_BIN" -c 'x=""; echo "[$x]"' 2>&1)
294 if [ "$result" = "[]" ]; then
295 pass "Empty variable in quotes"
296 else
297 fail "Empty variable in quotes" "[]" "$result"
298 fi
299
300 result=$("$RUSH_BIN" -c 'echo a "" b' 2>&1)
301 if [ "$result" = "a b" ]; then
302 pass "Empty string preserves word"
303 else
304 fail "Empty string preserves word" "a b" "$result"
305 fi
306
307 # =====================================
308 section "452. WORD SPLITTING"
309 # =====================================
310
311 result=$("$RUSH_BIN" -c 'x="a b c"; for w in $x; do echo "[$w]"; done' 2>&1)
312 expected=$(printf "[a]\n[b]\n[c]")
313 if [ "$result" = "$expected" ]; then
314 pass "Unquoted variable splits on whitespace"
315 else
316 fail "Unquoted variable splits on whitespace" "$expected" "$result"
317 fi
318
319 result=$("$RUSH_BIN" -c 'x="a b c"; for w in "$x"; do echo "[$w]"; done' 2>&1)
320 if [ "$result" = "[a b c]" ]; then
321 pass "Quoted variable prevents splitting"
322 else
323 fail "Quoted variable prevents splitting" "[a b c]" "$result"
324 fi
325
326 result=$("$RUSH_BIN" -c 'x="a b"; echo $x' 2>&1)
327 if [ "$result" = "a b" ]; then
328 pass "Word splitting collapses spaces"
329 else
330 fail "Word splitting collapses spaces" "a b" "$result"
331 fi
332
333 result=$("$RUSH_BIN" -c 'x="a b"; echo "$x"' 2>&1)
334 if [ "$result" = "a b" ]; then
335 pass "Quoting preserves multiple spaces"
336 else
337 fail "Quoting preserves multiple spaces" "a b" "$result"
338 fi
339
340 # =====================================
341 section "453. GLOB PREVENTION"
342 # =====================================
343
344 result=$("$RUSH_BIN" -c 'echo "*"' 2>&1)
345 if [ "$result" = "*" ]; then
346 pass "Double quotes prevent glob expansion"
347 else
348 fail "Double quotes prevent glob expansion" "*" "$result"
349 fi
350
351 result=$("$RUSH_BIN" -c "echo '[a-z]*'" 2>&1)
352 if [ "$result" = "[a-z]*" ]; then
353 pass "Single quotes prevent bracket expansion"
354 else
355 fail "Single quotes prevent bracket expansion" "[a-z]*" "$result"
356 fi
357
358 result=$("$RUSH_BIN" -c 'echo "?"' 2>&1)
359 if [ "$result" = "?" ]; then
360 pass "Double quotes prevent ? expansion"
361 else
362 fail "Double quotes prevent ? expansion" "?" "$result"
363 fi
364
365 # =====================================
366 section "454. QUOTE IN VARIABLE"
367 # =====================================
368
369 result=$("$RUSH_BIN" -c "x=\"has 'quotes'\"; echo \"\$x\"" 2>&1)
370 if [ "$result" = "has 'quotes'" ]; then
371 pass "Single quotes inside double-quoted variable"
372 else
373 fail "Single quotes inside double-quoted variable" "has 'quotes'" "$result"
374 fi
375
376 result=$("$RUSH_BIN" -c 'x="has \"quotes\""; echo "$x"' 2>&1)
377 if [ "$result" = 'has "quotes"' ]; then
378 pass "Double quotes inside variable"
379 else
380 fail "Double quotes inside variable" 'has "quotes"' "$result"
381 fi
382
383 # =====================================
384 section "455. QUOTING IN ASSIGNMENTS"
385 # =====================================
386
387 result=$("$RUSH_BIN" -c 'x="hello world"; echo $x' 2>&1)
388 if [ "$result" = "hello world" ]; then
389 pass "Quoted assignment with spaces"
390 else
391 fail "Quoted assignment with spaces" "hello world" "$result"
392 fi
393
394 result=$("$RUSH_BIN" -c "x='no \$expansion'; echo \"\$x\"" 2>&1)
395 if [ "$result" = 'no $expansion' ]; then
396 pass "Single-quoted assignment"
397 else
398 fail "Single-quoted assignment" 'no $expansion' "$result"
399 fi
400
401 result=$("$RUSH_BIN" -c 'y=value; x="with $y"; echo "$x"' 2>&1)
402 if [ "$result" = "with value" ]; then
403 pass "Variable expansion in assignment"
404 else
405 fail "Variable expansion in assignment" "with value" "$result"
406 fi
407
408 # =====================================
409 section "456. QUOTING IN COMMAND SUBSTITUTION"
410 # =====================================
411
412 result=$("$RUSH_BIN" -c 'x=$(echo "hello world"); echo "$x"' 2>&1)
413 if [ "$result" = "hello world" ]; then
414 pass "Quotes inside command substitution"
415 else
416 fail "Quotes inside command substitution" "hello world" "$result"
417 fi
418
419 result=$("$RUSH_BIN" -c 'x="$(echo "nested quotes")"; echo "$x"' 2>&1)
420 if [ "$result" = "nested quotes" ]; then
421 pass "Nested quotes in command substitution"
422 else
423 fail "Nested quotes in command substitution" "nested quotes" "$result"
424 fi
425
426 # =====================================
427 section "457. BACKSLASH IN DOUBLE QUOTES"
428 # =====================================
429
430 result=$("$RUSH_BIN" -c 'echo "back\\\\slash"' 2>&1)
431 if [ "$result" = 'back\slash' ]; then
432 pass "Backslash-backslash in double quotes"
433 else
434 fail "Backslash-backslash in double quotes" 'back\slash' "$result"
435 fi
436
437 result=$("$RUSH_BIN" -c 'echo "newline\\n"' 2>&1)
438 if [ "$result" = 'newline\n' ]; then
439 pass "Backslash-n in double quotes (literal)"
440 else
441 fail "Backslash-n in double quotes (literal)" 'newline\n' "$result"
442 fi
443
444 result=$("$RUSH_BIN" -c 'echo "tab\\t"' 2>&1)
445 if [ "$result" = 'tab\t' ]; then
446 pass "Backslash-t in double quotes (literal)"
447 else
448 fail "Backslash-t in double quotes (literal)" 'tab\t' "$result"
449 fi
450
451 # =====================================
452 section "458. DOLLAR IN QUOTES"
453 # =====================================
454
455 result=$("$RUSH_BIN" -c 'echo "cost: \$5"' 2>&1)
456 if [ "$result" = 'cost: $5' ]; then
457 pass "Escaped dollar in double quotes"
458 else
459 fail "Escaped dollar in double quotes" 'cost: $5' "$result"
460 fi
461
462 result=$("$RUSH_BIN" -c "echo 'cost: \$5'" 2>&1)
463 if [ "$result" = 'cost: $5' ]; then
464 pass "Dollar in single quotes (literal)"
465 else
466 fail "Dollar in single quotes (literal)" 'cost: $5' "$result"
467 fi
468
469 # =====================================
470 section "459. NEWLINE IN QUOTES"
471 # =====================================
472
473 result=$("$RUSH_BIN" -c 'echo "line1
474 line2"' 2>&1)
475 expected=$(printf "line1\nline2")
476 if [ "$result" = "$expected" ]; then
477 pass "Literal newline in double quotes"
478 else
479 fail "Literal newline in double quotes" "$expected" "$result"
480 fi
481
482 result=$("$RUSH_BIN" -c "echo 'line1
483 line2'" 2>&1)
484 expected=$(printf "line1\nline2")
485 if [ "$result" = "$expected" ]; then
486 pass "Literal newline in single quotes"
487 else
488 fail "Literal newline in single quotes" "$expected" "$result"
489 fi
490
491 # =====================================
492 section "460. TAB AND SPACE IN QUOTES"
493 # =====================================
494
495 result=$("$RUSH_BIN" -c 'echo " tab"' 2>&1)
496 if printf "%s" "$result" | grep -q " "; then
497 pass "Literal tab in double quotes"
498 else
499 fail "Literal tab in double quotes" "string with tab" "$result"
500 fi
501
502 result=$("$RUSH_BIN" -c 'echo " spaces "' 2>&1)
503 if [ "$result" = " spaces " ]; then
504 pass "Multiple spaces in double quotes"
505 else
506 fail "Multiple spaces in double quotes" " spaces " "$result"
507 fi
508
509 # =====================================
510 section "461. QUOTING IN FOR LOOP"
511 # =====================================
512
513 result=$("$RUSH_BIN" -c 'for x in "a b" c; do echo "[$x]"; done' 2>&1)
514 expected=$(printf "[a b]\n[c]")
515 if [ "$result" = "$expected" ]; then
516 pass "Quoted string in for list"
517 else
518 fail "Quoted string in for list" "$expected" "$result"
519 fi
520
521 result=$("$RUSH_BIN" -c 'list="a b c"; for x in $list; do echo "[$x]"; done' 2>&1)
522 expected=$(printf "[a]\n[b]\n[c]")
523 if [ "$result" = "$expected" ]; then
524 pass "Unquoted var splits in for"
525 else
526 fail "Unquoted var splits in for" "$expected" "$result"
527 fi
528
529 result=$("$RUSH_BIN" -c 'list="a b c"; for x in "$list"; do echo "[$x]"; done' 2>&1)
530 if [ "$result" = "[a b c]" ]; then
531 pass "Quoted var no split in for"
532 else
533 fail "Quoted var no split in for" "[a b c]" "$result"
534 fi
535
536 # =====================================
537 section "462. QUOTING IN CASE STATEMENT"
538 # =====================================
539
540 result=$("$RUSH_BIN" -c 'x="hello world"; case "$x" in "hello world") echo match;; esac' 2>&1)
541 if [ "$result" = "match" ]; then
542 pass "Quoted string in case word"
543 else
544 fail "Quoted string in case word" "match" "$result"
545 fi
546
547 result=$("$RUSH_BIN" -c 'case "a b" in "a b") echo yes;; *) echo no;; esac' 2>&1)
548 if [ "$result" = "yes" ]; then
549 pass "Quoted pattern in case"
550 else
551 fail "Quoted pattern in case" "yes" "$result"
552 fi
553
554 # =====================================
555 section "463. QUOTING SPECIAL SHELL CHARS"
556 # =====================================
557
558 result=$("$RUSH_BIN" -c 'echo "hello; world"' 2>&1)
559 if [ "$result" = "hello; world" ]; then
560 pass "Semicolon in double quotes"
561 else
562 fail "Semicolon in double quotes" "hello; world" "$result"
563 fi
564
565 result=$("$RUSH_BIN" -c "echo 'a && b'" 2>&1)
566 if [ "$result" = "a && b" ]; then
567 pass "Double ampersand in single quotes"
568 else
569 fail "Double ampersand in single quotes" "a && b" "$result"
570 fi
571
572 result=$("$RUSH_BIN" -c 'echo "a || b"' 2>&1)
573 if [ "$result" = "a || b" ]; then
574 pass "Double pipe in double quotes"
575 else
576 fail "Double pipe in double quotes" "a || b" "$result"
577 fi
578
579 result=$("$RUSH_BIN" -c "echo 'back\`tick'" 2>&1)
580 if [ "$result" = 'back`tick' ]; then
581 pass "Backtick in single quotes"
582 else
583 fail "Backtick in single quotes" 'back`tick' "$result"
584 fi
585
586 # =====================================
587 section "464. QUOTING IN ARITHMETIC"
588 # =====================================
589
590 result=$("$RUSH_BIN" -c 'x=5; echo $((x + 3))' 2>&1)
591 if [ "$result" = "8" ]; then
592 pass "Unquoted var in arithmetic"
593 else
594 fail "Unquoted var in arithmetic" "8" "$result"
595 fi
596
597 result=$("$RUSH_BIN" -c 'x="5"; echo $((x + 3))' 2>&1)
598 if [ "$result" = "8" ]; then
599 pass "Quoted assignment used in arithmetic"
600 else
601 fail "Quoted assignment used in arithmetic" "8" "$result"
602 fi
603
604 # =====================================
605 section "465. ESCAPE SEQUENCES"
606 # =====================================
607
608 result=$("$RUSH_BIN" -c 'echo "hello\tworld"' 2>&1)
609 if echo "$result" | grep -q "hello"; then
610 pass "Backslash-t in double quotes"
611 else
612 fail "Backslash-t in double quotes"
613 fi
614
615 result=$("$RUSH_BIN" -c "echo 'hello\nworld'" 2>&1)
616 if echo "$result" | grep -q 'hello\\nworld'; then
617 pass "Backslash-n literal in single quotes"
618 else
619 fail "Backslash-n literal in single quotes"
620 fi
621
622 # =====================================
623 section "466. QUOTING AND WORD SPLITTING"
624 # =====================================
625
626 result=$("$RUSH_BIN" -c 'x="a b c"; set -- $x; echo $#' 2>&1)
627 if [ "$result" = "3" ]; then
628 pass "Unquoted var splits on spaces"
629 else
630 fail "Unquoted var splits on spaces" "3" "$result"
631 fi
632
633 result=$("$RUSH_BIN" -c 'x="a b c"; set -- "$x"; echo $#' 2>&1)
634 if [ "$result" = "1" ]; then
635 pass "Quoted var prevents splitting"
636 else
637 fail "Quoted var prevents splitting" "1" "$result"
638 fi
639
640 result=$("$RUSH_BIN" -c 'x=""; set -- $x; echo $#' 2>&1)
641 if [ "$result" = "0" ]; then
642 pass "Empty unquoted var produces no args"
643 else
644 fail "Empty unquoted var produces no args" "0" "$result"
645 fi
646
647 result=$("$RUSH_BIN" -c 'x=""; set -- "$x"; echo $#' 2>&1)
648 if [ "$result" = "1" ]; then
649 pass "Empty quoted var produces one empty arg"
650 else
651 fail "Empty quoted var produces one empty arg" "1" "$result"
652 fi
653
654 # =====================================
655 section "467. QUOTING SPECIAL PARAMETERS"
656 # =====================================
657
658 result=$("$RUSH_BIN" -c 'set -- a b c; echo "$@" | wc -w' 2>&1)
659 if [ "$result" = "3" ]; then
660 pass 'Quoted $@ preserves args'
661 else
662 fail 'Quoted $@ preserves args' "3" "$result"
663 fi
664
665 result=$("$RUSH_BIN" -c 'set -- a b c; echo "$*"' 2>&1)
666 if [ "$result" = "a b c" ]; then
667 pass 'Quoted $* joins args'
668 else
669 fail 'Quoted $* joins args' "a b c" "$result"
670 fi
671
672 result=$("$RUSH_BIN" -c 'set -- a "b c" d; for x in "$@"; do echo "[$x]"; done' 2>&1)
673 expected=$(printf "[a]\n[b c]\n[d]")
674 if [ "$result" = "$expected" ]; then
675 pass 'Quoted $@ preserves quoting in original args'
676 else
677 fail 'Quoted $@ preserves quoting in original args'
678 fi
679
680 # =====================================
681 section "468. NESTED QUOTING"
682 # =====================================
683
684 result=$("$RUSH_BIN" -c "echo 'single \"with\" double'" 2>&1)
685 if [ "$result" = 'single "with" double' ]; then
686 pass "Double quotes inside single quotes"
687 else
688 fail "Double quotes inside single quotes"
689 fi
690
691 result=$("$RUSH_BIN" -c 'echo "double '\''with'\'' single"' 2>&1)
692 if [ "$result" = "double 'with' single" ]; then
693 pass "Single quotes inside double quotes (escaped)"
694 else
695 fail "Single quotes inside double quotes (escaped)"
696 fi
697
698 # =====================================
699 section "469. QUOTING IN ASSIGNMENTS"
700 # =====================================
701
702 result=$("$RUSH_BIN" -c 'x="hello world"; echo $x' 2>&1)
703 if [ "$result" = "hello world" ]; then
704 pass "Quoted assignment with spaces"
705 else
706 fail "Quoted assignment with spaces" "hello world" "$result"
707 fi
708
709 result=$("$RUSH_BIN" -c "x='hello world'; echo \$x" 2>&1)
710 if [ "$result" = "hello world" ]; then
711 pass "Single-quoted assignment with spaces"
712 else
713 fail "Single-quoted assignment with spaces" "hello world" "$result"
714 fi
715
716 result=$("$RUSH_BIN" -c 'x=hello\ world; echo $x' 2>&1)
717 if [ "$result" = "hello world" ]; then
718 pass "Escaped space in assignment"
719 else
720 fail "Escaped space in assignment" "hello world" "$result"
721 fi
722
723 # =====================================
724 section "470. QUOTING IN COMMAND SUBSTITUTION"
725 # =====================================
726
727 result=$("$RUSH_BIN" -c 'x=$(echo "hello world"); echo "$x"' 2>&1)
728 if [ "$result" = "hello world" ]; then
729 pass "Quoted string in command substitution"
730 else
731 fail "Quoted string in command substitution" "hello world" "$result"
732 fi
733
734 result=$("$RUSH_BIN" -c 'x=`echo "hello world"`; echo "$x"' 2>&1)
735 if [ "$result" = "hello world" ]; then
736 pass "Quoted string in backtick substitution"
737 else
738 fail "Quoted string in backtick substitution" "hello world" "$result"
739 fi
740
741 # =====================================
742 section "471. QUOTING AND GLOB PREVENTION"
743 # =====================================
744
745 result=$("$RUSH_BIN" -c 'echo "*"' 2>&1)
746 if [ "$result" = "*" ]; then
747 pass "Quoted asterisk is literal"
748 else
749 fail "Quoted asterisk is literal" "*" "$result"
750 fi
751
752 result=$("$RUSH_BIN" -c 'echo "?"' 2>&1)
753 if [ "$result" = "?" ]; then
754 pass "Quoted question mark is literal"
755 else
756 fail "Quoted question mark is literal" "?" "$result"
757 fi
758
759 result=$("$RUSH_BIN" -c 'echo "[abc]"' 2>&1)
760 if [ "$result" = "[abc]" ]; then
761 pass "Quoted brackets are literal"
762 else
763 fail "Quoted brackets are literal" "[abc]" "$result"
764 fi
765
766 # =====================================
767 section "472. QUOTING IN TESTS"
768 # =====================================
769
770 result=$("$RUSH_BIN" -c 'x=""; [ -z "$x" ] && echo empty' 2>&1)
771 if [ "$result" = "empty" ]; then
772 pass "Quoted empty var in test -z"
773 else
774 fail "Quoted empty var in test -z" "empty" "$result"
775 fi
776
777 result=$("$RUSH_BIN" -c 'x="hello"; [ -n "$x" ] && echo nonempty' 2>&1)
778 if [ "$result" = "nonempty" ]; then
779 pass "Quoted var in test -n"
780 else
781 fail "Quoted var in test -n" "nonempty" "$result"
782 fi
783
784 result=$("$RUSH_BIN" -c 'x="a b"; [ "$x" = "a b" ] && echo match' 2>&1)
785 if [ "$result" = "match" ]; then
786 pass "Quoted var with spaces in test ="
787 else
788 fail "Quoted var with spaces in test =" "match" "$result"
789 fi
790
791 # =====================================
792 section "473. QUOTING IN CASE PATTERNS"
793 # =====================================
794
795 result=$("$RUSH_BIN" -c 'x="*"; case "$x" in "*") echo literal;; esac' 2>&1)
796 if [ "$result" = "literal" ]; then
797 pass "Quoted asterisk matches literally in case"
798 else
799 fail "Quoted asterisk matches literally in case" "literal" "$result"
800 fi
801
802 result=$("$RUSH_BIN" -c 'x="a b"; case "$x" in "a b") echo match;; esac' 2>&1)
803 if [ "$result" = "match" ]; then
804 pass "Quoted pattern with space in case"
805 else
806 fail "Quoted pattern with space in case" "match" "$result"
807 fi
808
809 # =====================================
810 section "474. QUOTING IN FUNCTION ARGS"
811 # =====================================
812
813 result=$("$RUSH_BIN" -c 'f() { echo "[$1]"; }; f "hello world"' 2>&1)
814 if [ "$result" = "[hello world]" ]; then
815 pass "Quoted arg to function"
816 else
817 fail "Quoted arg to function" "[hello world]" "$result"
818 fi
819
820 result=$("$RUSH_BIN" -c 'f() { echo $#; }; f "a b" "c d"' 2>&1)
821 if [ "$result" = "2" ]; then
822 pass "Quoted args count in function"
823 else
824 fail "Quoted args count in function" "2" "$result"
825 fi
826
827 # =====================================
828 section "475. DOLLAR IN QUOTES"
829 # =====================================
830
831 result=$("$RUSH_BIN" -c 'echo "\$HOME"' 2>&1)
832 if [ "$result" = '$HOME' ]; then
833 pass "Escaped dollar in double quotes"
834 else
835 fail "Escaped dollar in double quotes" "\$HOME" "$result"
836 fi
837
838 result=$("$RUSH_BIN" -c "echo '\$HOME'" 2>&1)
839 if [ "$result" = '$HOME' ]; then
840 pass "Dollar in single quotes"
841 else
842 fail "Dollar in single quotes" "\$HOME" "$result"
843 fi
844
845 # =====================================
846 section "476. BACKSLASH IN QUOTES"
847 # =====================================
848
849 result=$("$RUSH_BIN" -c 'echo "\\"' 2>&1)
850 if [ "$result" = '\' ]; then
851 pass "Escaped backslash in double quotes"
852 else
853 fail "Escaped backslash in double quotes" "\\" "$result"
854 fi
855
856 result=$("$RUSH_BIN" -c "echo '\\'" 2>&1)
857 if [ "$result" = '\' ]; then
858 pass "Backslash in single quotes"
859 else
860 fail "Backslash in single quotes" "\\" "$result"
861 fi
862
863 # =====================================
864 section "477. QUOTE REMOVAL"
865 # =====================================
866
867 result=$("$RUSH_BIN" -c 'echo ""hello""' 2>&1)
868 if [ "$result" = "hello" ]; then
869 pass "Empty quotes around word"
870 else
871 fail "Empty quotes around word" "hello" "$result"
872 fi
873
874 result=$("$RUSH_BIN" -c 'echo "a""b""c"' 2>&1)
875 if [ "$result" = "abc" ]; then
876 pass "Adjacent quoted strings concatenate"
877 else
878 fail "Adjacent quoted strings concatenate" "abc" "$result"
879 fi
880
881 # =====================================
882 section "478. MIXED QUOTE STYLES"
883 # =====================================
884
885 result=$("$RUSH_BIN" -c "x=val; echo 'a'\"\$x\"'b'" 2>&1)
886 if [ "$result" = "avalb" ]; then
887 pass "Mixed single and double quotes"
888 else
889 fail "Mixed single and double quotes" "avalb" "$result"
890 fi
891
892 result=$("$RUSH_BIN" -c "echo 'single'\"double\"'single'" 2>&1)
893 if [ "$result" = "singledoublesingle" ]; then
894 pass "Alternating quote styles"
895 else
896 fail "Alternating quote styles" "singledoublesingle" "$result"
897 fi
898
899 # =====================================
900 section "479. QUOTING IN REDIRECTIONS"
901 # =====================================
902
903 result=$("$RUSH_BIN" -c 'echo test > "/tmp/quote space test.txt"; cat "/tmp/quote space test.txt"; rm "/tmp/quote space test.txt"' 2>&1)
904 if [ "$result" = "test" ]; then
905 pass "Quoted filename with space in redirect"
906 else
907 fail "Quoted filename with space in redirect" "test" "$result"
908 fi
909
910 # =====================================
911 section "480. EMPTY QUOTES"
912 # =====================================
913
914 result=$("$RUSH_BIN" -c 'echo ""' 2>&1)
915 if [ -z "$result" ]; then
916 pass "Empty double quotes produce empty output"
917 else
918 fail "Empty double quotes produce empty output" "empty" "$result"
919 fi
920
921 result=$("$RUSH_BIN" -c "echo ''" 2>&1)
922 if [ -z "$result" ]; then
923 pass "Empty single quotes produce empty output"
924 else
925 fail "Empty single quotes produce empty output" "empty" "$result"
926 fi
927
928 result=$("$RUSH_BIN" -c 'x=""; echo "[$x]"' 2>&1)
929 if [ "$result" = "[]" ]; then
930 pass "Empty var in quotes"
931 else
932 fail "Empty var in quotes" "[]" "$result"
933 fi
934
935 # =====================================
936 section "481. ADDITIONAL QUOTING TESTS"
937 # =====================================
938
939 result=$("$RUSH_BIN" -c 'echo "a\"b"' 2>&1)
940 if [ "$result" = 'a"b' ]; then
941 pass "Escaped quote in double quotes"
942 else
943 fail "Escaped quote in double quotes"
944 fi
945
946 result=$("$RUSH_BIN" -c "echo 'a'\"'\"'b'" 2>&1)
947 if [ "$result" = "a'b" ]; then
948 pass "Single quote via concatenation"
949 else
950 fail "Single quote via concatenation"
951 fi
952
953 result=$("$RUSH_BIN" -c 'X=test; echo "${X}"' 2>&1)
954 if [ "$result" = "test" ]; then
955 pass "Braces in double quotes"
956 else
957 fail "Braces in double quotes" "test" "$result"
958 fi
959
960 result=$("$RUSH_BIN" -c 'echo "$(echo test)"' 2>&1)
961 if [ "$result" = "test" ]; then
962 pass "Command sub in double quotes"
963 else
964 fail "Command sub in double quotes" "test" "$result"
965 fi
966
967 result=$("$RUSH_BIN" -c 'echo "$((2+2))"' 2>&1)
968 if [ "$result" = "4" ]; then
969 pass "Arithmetic in double quotes"
970 else
971 fail "Arithmetic in double quotes" "4" "$result"
972 fi
973
974 # =====================================
975 section "482. ESCAPE SEQUENCES IN CONTEXT"
976 # =====================================
977
978 result=$("$RUSH_BIN" -c 'printf "tab:\there\n"' 2>&1)
979 if echo "$result" | grep -q "tab:"; then
980 pass "Tab escape in printf"
981 else
982 fail "Tab escape in printf"
983 fi
984
985 result=$("$RUSH_BIN" -c 'printf "line1\nline2\n"' 2>&1)
986 if echo "$result" | wc -l | grep -q "2"; then
987 pass "Newline escape in printf"
988 else
989 fail "Newline escape in printf"
990 fi
991
992 result=$("$RUSH_BIN" -c 'echo "back\\slash"' 2>&1)
993 if echo "$result" | grep -q "back"; then
994 pass "Backslash in double quotes"
995 else
996 fail "Backslash in double quotes"
997 fi
998
999 # =====================================
1000 section "483. QUOTING IN LOOPS"
1001 # =====================================
1002
1003 result=$("$RUSH_BIN" -c 'for x in "a b" "c d"; do echo "[$x]"; done' 2>&1)
1004 expected=$(printf "[a b]\n[c d]")
1005 if [ "$result" = "$expected" ]; then
1006 pass "Quoted items in for loop"
1007 else
1008 fail "Quoted items in for loop"
1009 fi
1010
1011 result=$("$RUSH_BIN" -c 'X="1 2 3"; for i in $X; do echo $i; done | wc -l' 2>&1)
1012 if [ "$result" = "3" ]; then
1013 pass "Unquoted var splits in for"
1014 else
1015 fail "Unquoted var splits in for" "3" "$result"
1016 fi
1017
1018 result=$("$RUSH_BIN" -c 'X="1 2 3"; for i in "$X"; do echo $i; done | wc -l' 2>&1)
1019 if [ "$result" = "1" ]; then
1020 pass "Quoted var no split in for"
1021 else
1022 fail "Quoted var no split in for" "1" "$result"
1023 fi
1024
1025 # =====================================
1026 section "484. QUOTING IN CONDITIONALS"
1027 # =====================================
1028
1029 result=$("$RUSH_BIN" -c 'X=""; if [ -z "$X" ]; then echo empty; fi' 2>&1)
1030 if [ "$result" = "empty" ]; then
1031 pass "Quoted empty var in test -z"
1032 else
1033 fail "Quoted empty var in test -z" "empty" "$result"
1034 fi
1035
1036 result=$("$RUSH_BIN" -c 'X="a b"; if [ "$X" = "a b" ]; then echo match; fi' 2>&1)
1037 if [ "$result" = "match" ]; then
1038 pass "Quoted var with spaces in test"
1039 else
1040 fail "Quoted var with spaces in test" "match" "$result"
1041 fi
1042
1043 result=$("$RUSH_BIN" -c 'X=""; if [ "$X" = "" ]; then echo yes; fi' 2>&1)
1044 if [ "$result" = "yes" ]; then
1045 pass "Empty var equals empty string"
1046 else
1047 fail "Empty var equals empty string" "yes" "$result"
1048 fi
1049
1050 # =====================================
1051 section "485. QUOTING IN CASE"
1052 # =====================================
1053
1054 result=$("$RUSH_BIN" -c 'X="a b"; case "$X" in "a b") echo match;; esac' 2>&1)
1055 if [ "$result" = "match" ]; then
1056 pass "Quoted pattern in case"
1057 else
1058 fail "Quoted pattern in case" "match" "$result"
1059 fi
1060
1061 result=$("$RUSH_BIN" -c 'X="*"; case "$X" in "*") echo literal;; esac' 2>&1)
1062 if [ "$result" = "literal" ]; then
1063 pass "Quoted asterisk in case"
1064 else
1065 fail "Quoted asterisk in case" "literal" "$result"
1066 fi
1067
1068 # =====================================
1069 section "486. QUOTING IN FUNCTIONS"
1070 # =====================================
1071
1072 result=$("$RUSH_BIN" -c 'f() { echo "arg: $1"; }; f "hello world"' 2>&1)
1073 if [ "$result" = "arg: hello world" ]; then
1074 pass "Quoted arg to function"
1075 else
1076 fail "Quoted arg to function" "arg: hello world" "$result"
1077 fi
1078
1079 result=$("$RUSH_BIN" -c 'f() { echo "$#"; }; f "a b" "c d"' 2>&1)
1080 if [ "$result" = "2" ]; then
1081 pass "Quoted args count correct"
1082 else
1083 fail "Quoted args count correct" "2" "$result"
1084 fi
1085
1086 result=$("$RUSH_BIN" -c 'f() { for x in "$@"; do echo "[$x]"; done; }; f "a b" "c"' 2>&1)
1087 expected=$(printf "[a b]\n[c]")
1088 if [ "$result" = "$expected" ]; then
1089 pass "Quoted \$@ in function"
1090 else
1091 fail "Quoted \$@ in function"
1092 fi
1093
1094 # =====================================
1095 section "487. SPECIAL QUOTING CASES"
1096 # =====================================
1097
1098 result=$("$RUSH_BIN" -c 'echo "hello""world"' 2>&1)
1099 if [ "$result" = "helloworld" ]; then
1100 pass "Adjacent double quotes concatenate"
1101 else
1102 fail "Adjacent double quotes concatenate" "helloworld" "$result"
1103 fi
1104
1105 result=$("$RUSH_BIN" -c "echo 'hello''world'" 2>&1)
1106 if [ "$result" = "helloworld" ]; then
1107 pass "Adjacent single quotes concatenate"
1108 else
1109 fail "Adjacent single quotes concatenate" "helloworld" "$result"
1110 fi
1111
1112 result=$("$RUSH_BIN" -c 'echo "a"b"c"' 2>&1)
1113 if [ "$result" = "abc" ]; then
1114 pass "Mixed quoted/unquoted"
1115 else
1116 fail "Mixed quoted/unquoted" "abc" "$result"
1117 fi
1118
1119 # =====================================
1120 section "488. QUOTING EDGE CASES"
1121 # =====================================
1122
1123 result=$("$RUSH_BIN" -c 'X=""; [ -z "$X" ] && echo yes' 2>&1)
1124 if [ "$result" = "yes" ]; then
1125 pass "Empty quoted var is zero length"
1126 else
1127 fail "Empty quoted var is zero length" "yes" "$result"
1128 fi
1129
1130 result=$("$RUSH_BIN" -c 'X=" "; [ -n "$X" ] && echo yes' 2>&1)
1131 if [ "$result" = "yes" ]; then
1132 pass "Whitespace-only quoted var is non-empty"
1133 else
1134 fail "Whitespace-only quoted var is non-empty" "yes" "$result"
1135 fi
1136
1137 result=$("$RUSH_BIN" -c 'echo "start
1138 middle
1139 end" | wc -l' 2>&1)
1140 if [ "$result" = "3" ]; then
1141 pass "Multiline quoted string"
1142 else
1143 fail "Multiline quoted string" "3" "$result"
1144 fi
1145
1146 # =====================================
1147 section "489. QUOTE REMOVAL"
1148 # =====================================
1149
1150 result=$("$RUSH_BIN" -c 'echo hello' 2>&1)
1151 if [ "$result" = "hello" ]; then
1152 pass "No quotes needed for simple word"
1153 else
1154 fail "No quotes needed for simple word" "hello" "$result"
1155 fi
1156
1157 result=$("$RUSH_BIN" -c 'echo "hello"' 2>&1)
1158 if [ "$result" = "hello" ]; then
1159 pass "Double quotes removed"
1160 else
1161 fail "Double quotes removed" "hello" "$result"
1162 fi
1163
1164 result=$("$RUSH_BIN" -c "echo 'hello'" 2>&1)
1165 if [ "$result" = "hello" ]; then
1166 pass "Single quotes removed"
1167 else
1168 fail "Single quotes removed" "hello" "$result"
1169 fi
1170
1171 # =====================================
1172 section "490. COMPLEX QUOTING COMBINATIONS"
1173 # =====================================
1174
1175 result=$("$RUSH_BIN" -c 'X=val; echo "pre${X}post"' 2>&1)
1176 if [ "$result" = "prevalpost" ]; then
1177 pass "Var in middle of double quoted string"
1178 else
1179 fail "Var in middle of double quoted string" "prevalpost" "$result"
1180 fi
1181
1182 result=$("$RUSH_BIN" -c 'echo "$(echo "inner")"' 2>&1)
1183 if [ "$result" = "inner" ]; then
1184 pass "Nested quotes in command sub"
1185 else
1186 fail "Nested quotes in command sub" "inner" "$result"
1187 fi
1188
1189 result=$("$RUSH_BIN" -c 'X="a b c"; set -- $X; echo $2' 2>&1)
1190 if [ "$result" = "b" ]; then
1191 pass "Word splitting after unquoted expansion"
1192 else
1193 fail "Word splitting after unquoted expansion" "b" "$result"
1194 fi
1195
1196 # =====================================
1197 # Summary
1198 # =====================================
1199 printf "\n"
1200 printf "${BLUE}==========================================\n"
1201 printf "POSIX Quoting Test Summary\n"
1202 printf "==========================================${NC}\n"
1203 printf "Passed: ${GREEN}%d${NC}\n" "$PASSED"
1204 printf "Failed: ${RED}%d${NC}\n" "$FAILED"
1205 printf "Skipped: ${YELLOW}%d${NC}\n" "$SKIPPED"
1206 printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))"
1207
1208 if [ -n "$FAILED_TESTS_LIST" ]; then
1209 printf "\n${RED}Failed tests:${NC}\n"
1210 printf "%b" "$FAILED_TESTS_LIST"
1211 fi
1212
1213 if [ "$FAILED" -gt 0 ]; then
1214 exit 1
1215 fi
1216 exit 0