Bash · 28117 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 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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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=$("$FORTSH_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 # Summary
937 # =====================================
938 printf "\n"
939 printf "${BLUE}==========================================\n"
940 printf "POSIX Quoting Test Summary\n"
941 printf "==========================================${NC}\n"
942 printf "Passed: ${GREEN}%d${NC}\n" "$PASSED"
943 printf "Failed: ${RED}%d${NC}\n" "$FAILED"
944 printf "Skipped: ${YELLOW}%d${NC}\n" "$SKIPPED"
945 printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))"
946
947 if [ -n "$FAILED_TESTS_LIST" ]; then
948 printf "\n${RED}Failed tests:${NC}\n"
949 printf "%b" "$FAILED_TESTS_LIST"
950 fi
951
952 if [ "$FAILED" -gt 0 ]; then
953 exit 1
954 fi
955 exit 0