Bash · 28439 bytes Raw Blame History
1 #!/bin/sh
2 # =====================================
3 # POSIX Compliance Quoting Test Suite for fortsh
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 FORTSH_BIN="${FORTSH_BIN:-$SCRIPT_DIR/../bin/fortsh}"
28
29 # Check if fortsh exists
30 if [ ! -x "$FORTSH_BIN" ]; then
31 printf "${RED}ERROR${NC}: fortsh binary not found at $FORTSH_BIN\n"
32 printf "Please run 'make' first or set FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_BIN" -c 'echo "hello"'\''world'\''' 2>&1)
202 # Adjacent quoted strings concatenate: "hello" + 'world' = helloworld
203 if [ "$result" = "helloworld" ]; then
204 pass "Adjacent double and single quotes"
205 else
206 fail "Adjacent double and single quotes" "helloworld" "$result"
207 fi
208
209 result=$("$FORTSH_BIN" -c 'x=val; echo "$x"'\''$x'\''' 2>&1)
210 if [ "$result" = 'val$x' ]; then
211 pass "Mixed expansion and literal"
212 else
213 fail "Mixed expansion and literal" 'val$x' "$result"
214 fi
215
216 result=$("$FORTSH_BIN" -c "echo 'don'\\''t'" 2>&1)
217 if [ "$result" = "don't" ]; then
218 pass "Single quote in single quoted string"
219 else
220 fail "Single quote in single quoted string" "don't" "$result"
221 fi
222
223 # =====================================
224 section "450. QUOTING SPECIAL CHARACTERS"
225 # =====================================
226
227 result=$("$FORTSH_BIN" -c 'echo "semi;colon"' 2>&1)
228 if [ "$result" = "semi;colon" ]; then
229 pass "Semicolon in double quotes"
230 else
231 fail "Semicolon in double quotes" "semi;colon" "$result"
232 fi
233
234 result=$("$FORTSH_BIN" -c "echo 'pipe|char'" 2>&1)
235 if [ "$result" = "pipe|char" ]; then
236 pass "Pipe in single quotes"
237 else
238 fail "Pipe in single quotes" "pipe|char" "$result"
239 fi
240
241 result=$("$FORTSH_BIN" -c 'echo "ampersand&here"' 2>&1)
242 if [ "$result" = "ampersand&here" ]; then
243 pass "Ampersand in double quotes"
244 else
245 fail "Ampersand in double quotes" "ampersand&here" "$result"
246 fi
247
248 result=$("$FORTSH_BIN" -c "echo 'less<greater>'" 2>&1)
249 if [ "$result" = "less<greater>" ]; then
250 pass "Angle brackets in single quotes"
251 else
252 fail "Angle brackets in single quotes" "less<greater>" "$result"
253 fi
254
255 result=$("$FORTSH_BIN" -c 'echo "paren(here)"' 2>&1)
256 if [ "$result" = "paren(here)" ]; then
257 pass "Parentheses in double quotes"
258 else
259 fail "Parentheses in double quotes" "paren(here)" "$result"
260 fi
261
262 result=$("$FORTSH_BIN" -c "echo 'star*glob?'" 2>&1)
263 if [ "$result" = "star*glob?" ]; then
264 pass "Glob chars in single quotes"
265 else
266 fail "Glob chars in single quotes" "star*glob?" "$result"
267 fi
268
269 result=$("$FORTSH_BIN" -c 'echo "hash#comment"' 2>&1)
270 if [ "$result" = "hash#comment" ]; then
271 pass "Hash in double quotes"
272 else
273 fail "Hash in double quotes" "hash#comment" "$result"
274 fi
275
276 # =====================================
277 section "451. EMPTY STRINGS"
278 # =====================================
279
280 result=$("$FORTSH_BIN" -c 'echo ""' 2>&1)
281 if [ "$result" = "" ]; then
282 pass "Empty double-quoted string"
283 else
284 fail "Empty double-quoted string" "(empty)" "$result"
285 fi
286
287 result=$("$FORTSH_BIN" -c "echo ''" 2>&1)
288 if [ "$result" = "" ]; then
289 pass "Empty single-quoted string"
290 else
291 fail "Empty single-quoted string" "(empty)" "$result"
292 fi
293
294 result=$("$FORTSH_BIN" -c 'x=""; echo "[$x]"' 2>&1)
295 if [ "$result" = "[]" ]; then
296 pass "Empty variable in quotes"
297 else
298 fail "Empty variable in quotes" "[]" "$result"
299 fi
300
301 result=$("$FORTSH_BIN" -c 'echo a "" b' 2>&1)
302 if [ "$result" = "a b" ]; then
303 pass "Empty string preserves word"
304 else
305 fail "Empty string preserves word" "a b" "$result"
306 fi
307
308 # =====================================
309 section "452. WORD SPLITTING"
310 # =====================================
311
312 result=$("$FORTSH_BIN" -c 'x="a b c"; for w in $x; do echo "[$w]"; done' 2>&1)
313 expected=$(printf "[a]\n[b]\n[c]")
314 if [ "$result" = "$expected" ]; then
315 pass "Unquoted variable splits on whitespace"
316 else
317 fail "Unquoted variable splits on whitespace" "$expected" "$result"
318 fi
319
320 result=$("$FORTSH_BIN" -c 'x="a b c"; for w in "$x"; do echo "[$w]"; done' 2>&1)
321 if [ "$result" = "[a b c]" ]; then
322 pass "Quoted variable prevents splitting"
323 else
324 fail "Quoted variable prevents splitting" "[a b c]" "$result"
325 fi
326
327 result=$("$FORTSH_BIN" -c 'x="a b"; echo $x' 2>&1)
328 if [ "$result" = "a b" ]; then
329 pass "Word splitting collapses spaces"
330 else
331 fail "Word splitting collapses spaces" "a b" "$result"
332 fi
333
334 result=$("$FORTSH_BIN" -c 'x="a b"; echo "$x"' 2>&1)
335 if [ "$result" = "a b" ]; then
336 pass "Quoting preserves multiple spaces"
337 else
338 fail "Quoting preserves multiple spaces" "a b" "$result"
339 fi
340
341 # =====================================
342 section "453. GLOB PREVENTION"
343 # =====================================
344
345 result=$("$FORTSH_BIN" -c 'echo "*"' 2>&1)
346 if [ "$result" = "*" ]; then
347 pass "Double quotes prevent glob expansion"
348 else
349 fail "Double quotes prevent glob expansion" "*" "$result"
350 fi
351
352 result=$("$FORTSH_BIN" -c "echo '[a-z]*'" 2>&1)
353 if [ "$result" = "[a-z]*" ]; then
354 pass "Single quotes prevent bracket expansion"
355 else
356 fail "Single quotes prevent bracket expansion" "[a-z]*" "$result"
357 fi
358
359 result=$("$FORTSH_BIN" -c 'echo "?"' 2>&1)
360 if [ "$result" = "?" ]; then
361 pass "Double quotes prevent ? expansion"
362 else
363 fail "Double quotes prevent ? expansion" "?" "$result"
364 fi
365
366 # =====================================
367 section "454. QUOTE IN VARIABLE"
368 # =====================================
369
370 result=$("$FORTSH_BIN" -c "x=\"has 'quotes'\"; echo \"\$x\"" 2>&1)
371 if [ "$result" = "has 'quotes'" ]; then
372 pass "Single quotes inside double-quoted variable"
373 else
374 fail "Single quotes inside double-quoted variable" "has 'quotes'" "$result"
375 fi
376
377 result=$("$FORTSH_BIN" -c 'x="has \"quotes\""; echo "$x"' 2>&1)
378 if [ "$result" = 'has "quotes"' ]; then
379 pass "Double quotes inside variable"
380 else
381 fail "Double quotes inside variable" 'has "quotes"' "$result"
382 fi
383
384 # =====================================
385 section "455. QUOTING IN ASSIGNMENTS"
386 # =====================================
387
388 result=$("$FORTSH_BIN" -c 'x="hello world"; echo $x' 2>&1)
389 if [ "$result" = "hello world" ]; then
390 pass "Quoted assignment with spaces"
391 else
392 fail "Quoted assignment with spaces" "hello world" "$result"
393 fi
394
395 result=$("$FORTSH_BIN" -c "x='no \$expansion'; echo \"\$x\"" 2>&1)
396 if [ "$result" = 'no $expansion' ]; then
397 pass "Single-quoted assignment"
398 else
399 fail "Single-quoted assignment" 'no $expansion' "$result"
400 fi
401
402 result=$("$FORTSH_BIN" -c 'y=value; x="with $y"; echo "$x"' 2>&1)
403 if [ "$result" = "with value" ]; then
404 pass "Variable expansion in assignment"
405 else
406 fail "Variable expansion in assignment" "with value" "$result"
407 fi
408
409 # =====================================
410 section "456. QUOTING IN COMMAND SUBSTITUTION"
411 # =====================================
412
413 result=$("$FORTSH_BIN" -c 'x=$(echo "hello world"); echo "$x"' 2>&1)
414 if [ "$result" = "hello world" ]; then
415 pass "Quotes inside command substitution"
416 else
417 fail "Quotes inside command substitution" "hello world" "$result"
418 fi
419
420 result=$("$FORTSH_BIN" -c 'x="$(echo "nested quotes")"; echo "$x"' 2>&1)
421 if [ "$result" = "nested quotes" ]; then
422 pass "Nested quotes in command substitution"
423 else
424 fail "Nested quotes in command substitution" "nested quotes" "$result"
425 fi
426
427 # =====================================
428 section "457. BACKSLASH IN DOUBLE QUOTES"
429 # =====================================
430
431 # POSIX: In double quotes, \\\\ (4 backslashes) becomes \\ (2 backslashes)
432 # Each pair of \\ in double quotes produces one literal backslash
433 result=$("$FORTSH_BIN" -c 'echo "back\\\\slash"' 2>&1)
434 if [ "$result" = 'back\\slash' ]; then
435 pass "Backslash-backslash in double quotes"
436 else
437 fail "Backslash-backslash in double quotes" 'back\\slash' "$result"
438 fi
439
440 result=$("$FORTSH_BIN" -c 'echo "newline\\n"' 2>&1)
441 if [ "$result" = 'newline\n' ]; then
442 pass "Backslash-n in double quotes (literal)"
443 else
444 fail "Backslash-n in double quotes (literal)" 'newline\n' "$result"
445 fi
446
447 result=$("$FORTSH_BIN" -c 'echo "tab\\t"' 2>&1)
448 if [ "$result" = 'tab\t' ]; then
449 pass "Backslash-t in double quotes (literal)"
450 else
451 fail "Backslash-t in double quotes (literal)" 'tab\t' "$result"
452 fi
453
454 # =====================================
455 section "458. DOLLAR IN QUOTES"
456 # =====================================
457
458 result=$("$FORTSH_BIN" -c 'echo "cost: \$5"' 2>&1)
459 if [ "$result" = 'cost: $5' ]; then
460 pass "Escaped dollar in double quotes"
461 else
462 fail "Escaped dollar in double quotes" 'cost: $5' "$result"
463 fi
464
465 result=$("$FORTSH_BIN" -c "echo 'cost: \$5'" 2>&1)
466 if [ "$result" = 'cost: $5' ]; then
467 pass "Dollar in single quotes (literal)"
468 else
469 fail "Dollar in single quotes (literal)" 'cost: $5' "$result"
470 fi
471
472 # =====================================
473 section "459. NEWLINE IN QUOTES"
474 # =====================================
475
476 result=$("$FORTSH_BIN" -c 'echo "line1
477 line2"' 2>&1)
478 expected=$(printf "line1\nline2")
479 if [ "$result" = "$expected" ]; then
480 pass "Literal newline in double quotes"
481 else
482 fail "Literal newline in double quotes" "$expected" "$result"
483 fi
484
485 result=$("$FORTSH_BIN" -c "echo 'line1
486 line2'" 2>&1)
487 expected=$(printf "line1\nline2")
488 if [ "$result" = "$expected" ]; then
489 pass "Literal newline in single quotes"
490 else
491 fail "Literal newline in single quotes" "$expected" "$result"
492 fi
493
494 # =====================================
495 section "460. TAB AND SPACE IN QUOTES"
496 # =====================================
497
498 result=$("$FORTSH_BIN" -c 'echo " tab"' 2>&1)
499 if printf "%s" "$result" | grep -q " "; then
500 pass "Literal tab in double quotes"
501 else
502 fail "Literal tab in double quotes" "string with tab" "$result"
503 fi
504
505 result=$("$FORTSH_BIN" -c 'echo " spaces "' 2>&1)
506 if [ "$result" = " spaces " ]; then
507 pass "Multiple spaces in double quotes"
508 else
509 fail "Multiple spaces in double quotes" " spaces " "$result"
510 fi
511
512 # =====================================
513 section "461. QUOTING IN FOR LOOP"
514 # =====================================
515
516 result=$("$FORTSH_BIN" -c 'for x in "a b" c; do echo "[$x]"; done' 2>&1)
517 expected=$(printf "[a b]\n[c]")
518 if [ "$result" = "$expected" ]; then
519 pass "Quoted string in for list"
520 else
521 fail "Quoted string in for list" "$expected" "$result"
522 fi
523
524 result=$("$FORTSH_BIN" -c 'list="a b c"; for x in $list; do echo "[$x]"; done' 2>&1)
525 expected=$(printf "[a]\n[b]\n[c]")
526 if [ "$result" = "$expected" ]; then
527 pass "Unquoted var splits in for"
528 else
529 fail "Unquoted var splits in for" "$expected" "$result"
530 fi
531
532 result=$("$FORTSH_BIN" -c 'list="a b c"; for x in "$list"; do echo "[$x]"; done' 2>&1)
533 if [ "$result" = "[a b c]" ]; then
534 pass "Quoted var no split in for"
535 else
536 fail "Quoted var no split in for" "[a b c]" "$result"
537 fi
538
539 # =====================================
540 section "462. QUOTING IN CASE STATEMENT"
541 # =====================================
542
543 result=$("$FORTSH_BIN" -c 'x="hello world"; case "$x" in "hello world") echo match;; esac' 2>&1)
544 if [ "$result" = "match" ]; then
545 pass "Quoted string in case word"
546 else
547 fail "Quoted string in case word" "match" "$result"
548 fi
549
550 result=$("$FORTSH_BIN" -c 'case "a b" in "a b") echo yes;; *) echo no;; esac' 2>&1)
551 if [ "$result" = "yes" ]; then
552 pass "Quoted pattern in case"
553 else
554 fail "Quoted pattern in case" "yes" "$result"
555 fi
556
557 # =====================================
558 section "463. QUOTING SPECIAL SHELL CHARS"
559 # =====================================
560
561 result=$("$FORTSH_BIN" -c 'echo "hello; world"' 2>&1)
562 if [ "$result" = "hello; world" ]; then
563 pass "Semicolon in double quotes"
564 else
565 fail "Semicolon in double quotes" "hello; world" "$result"
566 fi
567
568 result=$("$FORTSH_BIN" -c "echo 'a && b'" 2>&1)
569 if [ "$result" = "a && b" ]; then
570 pass "Double ampersand in single quotes"
571 else
572 fail "Double ampersand in single quotes" "a && b" "$result"
573 fi
574
575 result=$("$FORTSH_BIN" -c 'echo "a || b"' 2>&1)
576 if [ "$result" = "a || b" ]; then
577 pass "Double pipe in double quotes"
578 else
579 fail "Double pipe in double quotes" "a || b" "$result"
580 fi
581
582 result=$("$FORTSH_BIN" -c "echo 'back\`tick'" 2>&1)
583 if [ "$result" = 'back`tick' ]; then
584 pass "Backtick in single quotes"
585 else
586 fail "Backtick in single quotes" 'back`tick' "$result"
587 fi
588
589 # =====================================
590 section "464. QUOTING IN ARITHMETIC"
591 # =====================================
592
593 result=$("$FORTSH_BIN" -c 'x=5; echo $((x + 3))' 2>&1)
594 if [ "$result" = "8" ]; then
595 pass "Unquoted var in arithmetic"
596 else
597 fail "Unquoted var in arithmetic" "8" "$result"
598 fi
599
600 result=$("$FORTSH_BIN" -c 'x="5"; echo $((x + 3))' 2>&1)
601 if [ "$result" = "8" ]; then
602 pass "Quoted assignment used in arithmetic"
603 else
604 fail "Quoted assignment used in arithmetic" "8" "$result"
605 fi
606
607 # =====================================
608 section "465. ESCAPE SEQUENCES"
609 # =====================================
610
611 result=$("$FORTSH_BIN" -c 'echo "hello\tworld"' 2>&1)
612 if echo "$result" | grep -q "hello"; then
613 pass "Backslash-t in double quotes"
614 else
615 fail "Backslash-t in double quotes"
616 fi
617
618 # Test backslash-n literal in single quotes (use double backslash for POSIX sh compatibility)
619 result=$("$FORTSH_BIN" -c "echo 'hello\\nworld'" 2>&1)
620 if printf '%s' "$result" | grep -q 'hello.nworld'; then
621 pass "Backslash-n literal in single quotes"
622 else
623 fail "Backslash-n literal in single quotes"
624 fi
625
626 # =====================================
627 section "466. QUOTING AND WORD SPLITTING"
628 # =====================================
629
630 result=$("$FORTSH_BIN" -c 'x="a b c"; set -- $x; echo $#' 2>&1)
631 if [ "$result" = "3" ]; then
632 pass "Unquoted var splits on spaces"
633 else
634 fail "Unquoted var splits on spaces" "3" "$result"
635 fi
636
637 result=$("$FORTSH_BIN" -c 'x="a b c"; set -- "$x"; echo $#' 2>&1)
638 if [ "$result" = "1" ]; then
639 pass "Quoted var prevents splitting"
640 else
641 fail "Quoted var prevents splitting" "1" "$result"
642 fi
643
644 result=$("$FORTSH_BIN" -c 'x=""; set -- $x; echo $#' 2>&1)
645 if [ "$result" = "0" ]; then
646 pass "Empty unquoted var produces no args"
647 else
648 fail "Empty unquoted var produces no args" "0" "$result"
649 fi
650
651 result=$("$FORTSH_BIN" -c 'x=""; set -- "$x"; echo $#' 2>&1)
652 if [ "$result" = "1" ]; then
653 pass "Empty quoted var produces one empty arg"
654 else
655 fail "Empty quoted var produces one empty arg" "1" "$result"
656 fi
657
658 # =====================================
659 section "467. QUOTING SPECIAL PARAMETERS"
660 # =====================================
661
662 result=$("$FORTSH_BIN" -c 'set -- a b c; echo "$@" | wc -w' 2>&1)
663 if [ "$result" -eq 3 ] 2>/dev/null; then
664 pass 'Quoted $@ preserves args'
665 else
666 fail 'Quoted $@ preserves args' "3" "$result"
667 fi
668
669 result=$("$FORTSH_BIN" -c 'set -- a b c; echo "$*"' 2>&1)
670 if [ "$result" = "a b c" ]; then
671 pass 'Quoted $* joins args'
672 else
673 fail 'Quoted $* joins args' "a b c" "$result"
674 fi
675
676 result=$("$FORTSH_BIN" -c 'set -- a "b c" d; for x in "$@"; do echo "[$x]"; done' 2>&1)
677 expected=$(printf "[a]\n[b c]\n[d]")
678 if [ "$result" = "$expected" ]; then
679 pass 'Quoted $@ preserves quoting in original args'
680 else
681 fail 'Quoted $@ preserves quoting in original args'
682 fi
683
684 # =====================================
685 section "468. NESTED QUOTING"
686 # =====================================
687
688 result=$("$FORTSH_BIN" -c "echo 'single \"with\" double'" 2>&1)
689 if [ "$result" = 'single "with" double' ]; then
690 pass "Double quotes inside single quotes"
691 else
692 fail "Double quotes inside single quotes"
693 fi
694
695 result=$("$FORTSH_BIN" -c 'echo "double '\''with'\'' single"' 2>&1)
696 if [ "$result" = "double 'with' single" ]; then
697 pass "Single quotes inside double quotes (escaped)"
698 else
699 fail "Single quotes inside double quotes (escaped)"
700 fi
701
702 # =====================================
703 section "469. QUOTING IN ASSIGNMENTS"
704 # =====================================
705
706 result=$("$FORTSH_BIN" -c 'x="hello world"; echo $x' 2>&1)
707 if [ "$result" = "hello world" ]; then
708 pass "Quoted assignment with spaces"
709 else
710 fail "Quoted assignment with spaces" "hello world" "$result"
711 fi
712
713 result=$("$FORTSH_BIN" -c "x='hello world'; echo \$x" 2>&1)
714 if [ "$result" = "hello world" ]; then
715 pass "Single-quoted assignment with spaces"
716 else
717 fail "Single-quoted assignment with spaces" "hello world" "$result"
718 fi
719
720 result=$("$FORTSH_BIN" -c 'x=hello\ world; echo $x' 2>&1)
721 if [ "$result" = "hello world" ]; then
722 pass "Escaped space in assignment"
723 else
724 fail "Escaped space in assignment" "hello world" "$result"
725 fi
726
727 # =====================================
728 section "470. QUOTING IN COMMAND SUBSTITUTION"
729 # =====================================
730
731 result=$("$FORTSH_BIN" -c 'x=$(echo "hello world"); echo "$x"' 2>&1)
732 if [ "$result" = "hello world" ]; then
733 pass "Quoted string in command substitution"
734 else
735 fail "Quoted string in command substitution" "hello world" "$result"
736 fi
737
738 result=$("$FORTSH_BIN" -c 'x=`echo "hello world"`; echo "$x"' 2>&1)
739 if [ "$result" = "hello world" ]; then
740 pass "Quoted string in backtick substitution"
741 else
742 fail "Quoted string in backtick substitution" "hello world" "$result"
743 fi
744
745 # =====================================
746 section "471. QUOTING AND GLOB PREVENTION"
747 # =====================================
748
749 result=$("$FORTSH_BIN" -c 'echo "*"' 2>&1)
750 if [ "$result" = "*" ]; then
751 pass "Quoted asterisk is literal"
752 else
753 fail "Quoted asterisk is literal" "*" "$result"
754 fi
755
756 result=$("$FORTSH_BIN" -c 'echo "?"' 2>&1)
757 if [ "$result" = "?" ]; then
758 pass "Quoted question mark is literal"
759 else
760 fail "Quoted question mark is literal" "?" "$result"
761 fi
762
763 result=$("$FORTSH_BIN" -c 'echo "[abc]"' 2>&1)
764 if [ "$result" = "[abc]" ]; then
765 pass "Quoted brackets are literal"
766 else
767 fail "Quoted brackets are literal" "[abc]" "$result"
768 fi
769
770 # =====================================
771 section "472. QUOTING IN TESTS"
772 # =====================================
773
774 result=$("$FORTSH_BIN" -c 'x=""; [ -z "$x" ] && echo empty' 2>&1)
775 if [ "$result" = "empty" ]; then
776 pass "Quoted empty var in test -z"
777 else
778 fail "Quoted empty var in test -z" "empty" "$result"
779 fi
780
781 result=$("$FORTSH_BIN" -c 'x="hello"; [ -n "$x" ] && echo nonempty' 2>&1)
782 if [ "$result" = "nonempty" ]; then
783 pass "Quoted var in test -n"
784 else
785 fail "Quoted var in test -n" "nonempty" "$result"
786 fi
787
788 result=$("$FORTSH_BIN" -c 'x="a b"; [ "$x" = "a b" ] && echo match' 2>&1)
789 if [ "$result" = "match" ]; then
790 pass "Quoted var with spaces in test ="
791 else
792 fail "Quoted var with spaces in test =" "match" "$result"
793 fi
794
795 # =====================================
796 section "473. QUOTING IN CASE PATTERNS"
797 # =====================================
798
799 result=$("$FORTSH_BIN" -c 'x="*"; case "$x" in "*") echo literal;; esac' 2>&1)
800 if [ "$result" = "literal" ]; then
801 pass "Quoted asterisk matches literally in case"
802 else
803 fail "Quoted asterisk matches literally in case" "literal" "$result"
804 fi
805
806 result=$("$FORTSH_BIN" -c 'x="a b"; case "$x" in "a b") echo match;; esac' 2>&1)
807 if [ "$result" = "match" ]; then
808 pass "Quoted pattern with space in case"
809 else
810 fail "Quoted pattern with space in case" "match" "$result"
811 fi
812
813 # =====================================
814 section "474. QUOTING IN FUNCTION ARGS"
815 # =====================================
816
817 result=$("$FORTSH_BIN" -c 'f() { echo "[$1]"; }; f "hello world"' 2>&1)
818 if [ "$result" = "[hello world]" ]; then
819 pass "Quoted arg to function"
820 else
821 fail "Quoted arg to function" "[hello world]" "$result"
822 fi
823
824 result=$("$FORTSH_BIN" -c 'f() { echo $#; }; f "a b" "c d"' 2>&1)
825 if [ "$result" = "2" ]; then
826 pass "Quoted args count in function"
827 else
828 fail "Quoted args count in function" "2" "$result"
829 fi
830
831 # =====================================
832 section "475. DOLLAR IN QUOTES"
833 # =====================================
834
835 result=$("$FORTSH_BIN" -c 'echo "\$HOME"' 2>&1)
836 if [ "$result" = '$HOME' ]; then
837 pass "Escaped dollar in double quotes"
838 else
839 fail "Escaped dollar in double quotes" "\$HOME" "$result"
840 fi
841
842 result=$("$FORTSH_BIN" -c "echo '\$HOME'" 2>&1)
843 if [ "$result" = '$HOME' ]; then
844 pass "Dollar in single quotes"
845 else
846 fail "Dollar in single quotes" "\$HOME" "$result"
847 fi
848
849 # =====================================
850 section "476. BACKSLASH IN QUOTES"
851 # =====================================
852
853 result=$("$FORTSH_BIN" -c 'echo "\\"' 2>&1)
854 if [ "$result" = '\' ]; then
855 pass "Escaped backslash in double quotes"
856 else
857 fail "Escaped backslash in double quotes" "\\" "$result"
858 fi
859
860 result=$("$FORTSH_BIN" -c "echo '\\'" 2>&1)
861 if [ "$result" = '\' ]; then
862 pass "Backslash in single quotes"
863 else
864 fail "Backslash in single quotes" "\\" "$result"
865 fi
866
867 # =====================================
868 section "477. QUOTE REMOVAL"
869 # =====================================
870
871 result=$("$FORTSH_BIN" -c 'echo ""hello""' 2>&1)
872 if [ "$result" = "hello" ]; then
873 pass "Empty quotes around word"
874 else
875 fail "Empty quotes around word" "hello" "$result"
876 fi
877
878 result=$("$FORTSH_BIN" -c 'echo "a""b""c"' 2>&1)
879 if [ "$result" = "abc" ]; then
880 pass "Adjacent quoted strings concatenate"
881 else
882 fail "Adjacent quoted strings concatenate" "abc" "$result"
883 fi
884
885 # =====================================
886 section "478. MIXED QUOTE STYLES"
887 # =====================================
888
889 result=$("$FORTSH_BIN" -c "x=val; echo 'a'\"\$x\"'b'" 2>&1)
890 if [ "$result" = "avalb" ]; then
891 pass "Mixed single and double quotes"
892 else
893 fail "Mixed single and double quotes" "avalb" "$result"
894 fi
895
896 result=$("$FORTSH_BIN" -c "echo 'single'\"double\"'single'" 2>&1)
897 if [ "$result" = "singledoublesingle" ]; then
898 pass "Alternating quote styles"
899 else
900 fail "Alternating quote styles" "singledoublesingle" "$result"
901 fi
902
903 # =====================================
904 section "479. QUOTING IN REDIRECTIONS"
905 # =====================================
906
907 result=$("$FORTSH_BIN" -c 'echo test > "/tmp/quote space test.txt"; cat "/tmp/quote space test.txt"; rm "/tmp/quote space test.txt"' 2>&1)
908 if [ "$result" = "test" ]; then
909 pass "Quoted filename with space in redirect"
910 else
911 fail "Quoted filename with space in redirect" "test" "$result"
912 fi
913
914 # =====================================
915 section "480. EMPTY QUOTES"
916 # =====================================
917
918 result=$("$FORTSH_BIN" -c 'echo ""' 2>&1)
919 if [ -z "$result" ]; then
920 pass "Empty double quotes produce empty output"
921 else
922 fail "Empty double quotes produce empty output" "empty" "$result"
923 fi
924
925 result=$("$FORTSH_BIN" -c "echo ''" 2>&1)
926 if [ -z "$result" ]; then
927 pass "Empty single quotes produce empty output"
928 else
929 fail "Empty single quotes produce empty output" "empty" "$result"
930 fi
931
932 result=$("$FORTSH_BIN" -c 'x=""; echo "[$x]"' 2>&1)
933 if [ "$result" = "[]" ]; then
934 pass "Empty var in quotes"
935 else
936 fail "Empty var in quotes" "[]" "$result"
937 fi
938
939 # =====================================
940 # Summary
941 # =====================================
942 printf "\n"
943 printf "${BLUE}==========================================\n"
944 printf "POSIX Quoting Test Summary\n"
945 printf "==========================================${NC}\n"
946 printf "Passed: ${GREEN}%d${NC}\n" "$PASSED"
947 printf "Failed: ${RED}%d${NC}\n" "$FAILED"
948 printf "Skipped: ${YELLOW}%d${NC}\n" "$SKIPPED"
949 printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))"
950
951 if [ -n "$FAILED_TESTS_LIST" ]; then
952 printf "\n${RED}Failed tests:${NC}\n"
953 printf "%b" "$FAILED_TESTS_LIST"
954 fi
955
956 if [ "$FAILED" -gt 0 ]; then
957 exit 1
958 fi
959 exit 0