diff options
author | Ron Yorston <rmy@pobox.com> | 2018-07-25 10:41:42 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-07-25 10:41:42 +0100 |
commit | 59873514f17cefd6ba3997dad5779f75433fd4e6 (patch) | |
tree | 1c9d0a3450ed95f0b820285b9f9fc217c902e652 /shell | |
parent | 779fd5745ac11bf752f5f4b977a274a39c192f90 (diff) | |
parent | 81de30de05beebabfa72f2a01ec4f33e9a1923e3 (diff) | |
download | busybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.tar.gz busybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.tar.bz2 busybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.zip |
Merge branch 'busybox'
Diffstat (limited to 'shell')
63 files changed, 1575 insertions, 464 deletions
diff --git a/shell/Config.src b/shell/Config.src index 81c4ec874..959d3cb42 100644 --- a/shell/Config.src +++ b/shell/Config.src | |||
@@ -1,6 +1,6 @@ | |||
1 | # | 1 | # |
2 | # For a description of the syntax of this configuration file, | 2 | # For a description of the syntax of this configuration file, |
3 | # see scripts/kbuild/config-language.txt. | 3 | # see docs/Kconfig-language.txt. |
4 | # | 4 | # |
5 | 5 | ||
6 | menu "Shells" | 6 | menu "Shells" |
diff --git a/shell/ash.c b/shell/ash.c index 7fa9dae21..4bd1c2c9d 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -8929,7 +8929,8 @@ describe_command(char *command, const char *path, int describe_command_verbose) | |||
8929 | 8929 | ||
8930 | case CMDFUNCTION: | 8930 | case CMDFUNCTION: |
8931 | if (describe_command_verbose) { | 8931 | if (describe_command_verbose) { |
8932 | out1str(" is a shell function"); | 8932 | /*out1str(" is a shell function");*/ |
8933 | out1str(" is a function"); /* bash says this */ | ||
8933 | } else { | 8934 | } else { |
8934 | out1str(command); | 8935 | out1str(command); |
8935 | } | 8936 | } |
diff --git a/shell/ash_test/ash-glob/glob_altvalue1.right b/shell/ash_test/ash-glob/glob_altvalue1.right new file mode 100644 index 000000000..bd3592229 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_altvalue1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | 1u: glob_altvalue1.tests | ||
2 | 2u: glob_altvalue1.t* | ||
3 | 3u: glob_altvalue1.t* | ||
4 | 4u: glob_altvalue1.t* | ||
5 | 1q: glob_altvalue1.t* | ||
6 | 2q: 'glob_altvalue1.t*' | ||
7 | 3q: glob_altvalue1.t* | ||
diff --git a/shell/ash_test/ash-glob/glob_altvalue1.tests b/shell/ash_test/ash-glob/glob_altvalue1.tests new file mode 100755 index 000000000..5483d63e6 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_altvalue1.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | x=x | ||
2 | |||
3 | echo 1u: ${x:+glob_altvalue1.t*} | ||
4 | echo 2u: ${x:+'glob_altvalue1.t*'} | ||
5 | echo 3u: ${x:+"glob_altvalue1.t*"} | ||
6 | echo 4u: ${x:+glob_altvalue1.t\*} | ||
7 | ##echo 5u: ${x:+"glob_altvalue1.t\*"} | ||
8 | |||
9 | echo 1q: "${x:+glob_altvalue1.t*}" | ||
10 | echo 2q: "${x:+'glob_altvalue1.t*'}" | ||
11 | echo 3q: "${x:+"glob_altvalue1.t*"}" | ||
12 | ##echo 4q: "${x:+glob_altvalue1.t\*}" | ||
13 | ##echo 5q: "${x:+"glob_altvalue1.t\*"}" | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc.right b/shell/ash_test/ash-heredoc/heredoc.right index baf115166..85d36dae9 100644 --- a/shell/ash_test/ash-heredoc/heredoc.right +++ b/shell/ash_test/ash-heredoc/heredoc.right | |||
@@ -16,6 +16,6 @@ tab 3 | |||
16 | abc | 16 | abc |
17 | def ghi | 17 | def ghi |
18 | jkl mno | 18 | jkl mno |
19 | fff is a shell function | 19 | fff is a function |
20 | hi | 20 | hi |
21 | there | 21 | there |
diff --git a/shell/ash_test/ash-heredoc/heredocA.right b/shell/ash_test/ash-heredoc/heredocA.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocA.right | |||
@@ -0,0 +1 @@ | |||
Ok | |||
diff --git a/shell/ash_test/ash-heredoc/heredocA.tests b/shell/ash_test/ash-heredoc/heredocA.tests new file mode 100755 index 000000000..440aaf906 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocA.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | { cat <<EOF ; | ||
2 | Ok | ||
3 | EOF | ||
4 | } | ||
diff --git a/shell/ash_test/ash-heredoc/heredocB.right b/shell/ash_test/ash-heredoc/heredocB.right new file mode 100644 index 000000000..43ba0b4f9 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocB.right | |||
@@ -0,0 +1,3 @@ | |||
1 | one - alpha | ||
2 | two - beta | ||
3 | three - gamma | ||
diff --git a/shell/ash_test/ash-heredoc/heredocB.tests b/shell/ash_test/ash-heredoc/heredocB.tests new file mode 100755 index 000000000..45ea4687f --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocB.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | while read line1; do | ||
2 | read line2 <&3 | ||
3 | echo $line1 - $line2 | ||
4 | done <<EOF1 3<<EOF2 | ||
5 | one | ||
6 | two | ||
7 | three | ||
8 | EOF1 | ||
9 | alpha | ||
10 | beta | ||
11 | gamma | ||
12 | EOF2 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_after_compound1.right b/shell/ash_test/ash-heredoc/heredoc_after_compound1.right new file mode 100644 index 000000000..9052f7d1f --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_after_compound1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests b/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests new file mode 100755 index 000000000..e7cfb5be1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | { cat <<EOF; }; echo Ok2 | ||
2 | Ok1 | ||
3 | EOF | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.right | |||
@@ -0,0 +1 @@ | |||
Ok1 | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.tests new file mode 100755 index 000000000..1d2a26504 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | cat <<EOF | ||
2 | Ok1 | ||
3 | EO\ | ||
4 | F | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty3.right b/shell/ash_test/ash-heredoc/heredoc_empty3.right new file mode 100644 index 000000000..0b54a9c93 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Ok | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_empty3.tests b/shell/ash_test/ash-heredoc/heredoc_empty3.tests new file mode 100755 index 000000000..828c2dd89 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_empty3.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | cat <<EOF | ||
2 | |||
3 | Ok | ||
4 | EOF | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue1.right b/shell/ash_test/ash-quoting/dollar_altvalue1.right new file mode 100644 index 000000000..5cd495d3b --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue1.right | |||
@@ -0,0 +1,16 @@ | |||
1 | Unquoted b c d | ||
2 | |b| | ||
3 | |c| | ||
4 | |d| | ||
5 | Unquoted 'b c' d | ||
6 | |b c| | ||
7 | |d| | ||
8 | Unquoted "b c" d | ||
9 | |b c| | ||
10 | |d| | ||
11 | Quoted b c d | ||
12 | |b c d| | ||
13 | Quoted 'b c' d | ||
14 | |'b c' d| | ||
15 | Quoted "b c" d | ||
16 | |b c d| | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue1.tests b/shell/ash_test/ash-quoting/dollar_altvalue1.tests new file mode 100755 index 000000000..f4dc8caec --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue1.tests | |||
@@ -0,0 +1,16 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | x=a | ||
3 | |||
4 | echo Unquoted b c d | ||
5 | f ${x:+b c d} | ||
6 | echo Unquoted "'b c' d" | ||
7 | f ${x:+'b c' d} | ||
8 | echo Unquoted '"b c" d' | ||
9 | f ${x:+"b c" d} | ||
10 | |||
11 | echo Quoted b c d | ||
12 | f "${x:+b c d}" | ||
13 | echo Quoted "'b c' d" | ||
14 | f "${x:+'b c' d}" | ||
15 | echo Quoted '"b c" d' | ||
16 | f "${x:+"b c" d}" | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue2.right b/shell/ash_test/ash-quoting/dollar_altvalue2.right new file mode 100644 index 000000000..7cf37e379 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue2.right | |||
@@ -0,0 +1,69 @@ | |||
1 | Unquoted '': | ||
2 | start: | ||
3 | || | ||
4 | end | ||
5 | start: | ||
6 | || | ||
7 | end | ||
8 | start: | ||
9 | || | ||
10 | end | ||
11 | start: | ||
12 | || | ||
13 | end | ||
14 | start: | ||
15 | || | ||
16 | || | ||
17 | end | ||
18 | |||
19 | Unquoted "": | ||
20 | start: | ||
21 | || | ||
22 | end | ||
23 | start: | ||
24 | || | ||
25 | end | ||
26 | start: | ||
27 | || | ||
28 | end | ||
29 | start: | ||
30 | || | ||
31 | end | ||
32 | start: | ||
33 | || | ||
34 | || | ||
35 | end | ||
36 | |||
37 | Quoted '': | ||
38 | start: | ||
39 | |''| | ||
40 | end | ||
41 | start: | ||
42 | |'' | | ||
43 | end | ||
44 | start: | ||
45 | | ''| | ||
46 | end | ||
47 | start: | ||
48 | | '' | | ||
49 | end | ||
50 | start: | ||
51 | |'' ''| | ||
52 | end | ||
53 | |||
54 | Quoted "": | ||
55 | start: | ||
56 | || | ||
57 | end | ||
58 | start: | ||
59 | | | | ||
60 | end | ||
61 | start: | ||
62 | | | | ||
63 | end | ||
64 | start: | ||
65 | | | | ||
66 | end | ||
67 | start: | ||
68 | | | | ||
69 | end | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue2.tests b/shell/ash_test/ash-quoting/dollar_altvalue2.tests new file mode 100755 index 000000000..3377eb27f --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue2.tests | |||
@@ -0,0 +1,33 @@ | |||
1 | f() { echo start:; for i; do echo "|$i|"; done; echo end; } | ||
2 | x=a | ||
3 | |||
4 | echo "Unquoted '':" | ||
5 | f ${x:+''} | ||
6 | f ${x:+'' } | ||
7 | f ${x:+ ''} | ||
8 | f ${x:+ '' } | ||
9 | f ${x:+'' ''} | ||
10 | |||
11 | echo | ||
12 | echo 'Unquoted "":' | ||
13 | f ${x:+""} | ||
14 | f ${x:+"" } | ||
15 | f ${x:+ ""} | ||
16 | f ${x:+ "" } | ||
17 | f ${x:+"" ""} | ||
18 | |||
19 | echo | ||
20 | echo "Quoted '':" | ||
21 | f "${x:+''}" | ||
22 | f "${x:+'' }" | ||
23 | f "${x:+ ''}" | ||
24 | f "${x:+ '' }" | ||
25 | f "${x:+'' ''}" | ||
26 | |||
27 | echo | ||
28 | echo 'Quoted "":' | ||
29 | f "${x:+""}" | ||
30 | f "${x:+"" }" | ||
31 | f "${x:+ ""}" | ||
32 | f "${x:+ "" }" | ||
33 | f "${x:+"" ""}" | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue9.right b/shell/ash_test/ash-quoting/dollar_altvalue9.right new file mode 100644 index 000000000..39342fe7c --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue9.right | |||
@@ -0,0 +1,26 @@ | |||
1 | Unquoted 1: | ||
2 | |a| | ||
3 | |x y| | ||
4 | |1| | ||
5 | |2| | ||
6 | || | ||
7 | |1 2| | ||
8 | |A| | ||
9 | |B| | ||
10 | |C D| | ||
11 | |zb| | ||
12 | Quoted 1: | ||
13 | |a 'x y' 1 2 '' 1 2 A B C D zb| | ||
14 | Unquoted 2: | ||
15 | |ax y| | ||
16 | |1| | ||
17 | |2| | ||
18 | || | ||
19 | |1 2| | ||
20 | |A| | ||
21 | |B| | ||
22 | |C D| | ||
23 | |z| | ||
24 | |b| | ||
25 | Quoted 2: | ||
26 | |a 'x y' 1 2 '' 1 2 A B C D z b| | ||
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue9.tests b/shell/ash_test/ash-quoting/dollar_altvalue9.tests new file mode 100755 index 000000000..27a6f4f3c --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_altvalue9.tests | |||
@@ -0,0 +1,17 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | |||
3 | echo Unquoted 1: | ||
4 | x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b | ||
5 | echo Quoted 1: | ||
6 | x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b" | ||
7 | |||
8 | echo Unquoted 2: | ||
9 | x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b | ||
10 | echo Quoted 2: | ||
11 | x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z }b" | ||
12 | |||
13 | #echo Unquoted 3: | ||
14 | #e= | ||
15 | #x='1 2'; f a${x:+'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b | ||
16 | #echo Quoted 3: | ||
17 | #x='1 2'; f "a${x:+ 'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b" | ||
diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash1.right b/shell/ash_test/ash-quoting/dollar_repl_bash1.right new file mode 100644 index 000000000..f5e9309f4 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_repl_bash1.right | |||
@@ -0,0 +1,14 @@ | |||
1 | |y| | ||
2 | |zx| | ||
3 | |y| | ||
4 | |zx| | ||
5 | |y zx| | ||
6 | |y zx| | ||
7 | |y| | ||
8 | |zy| | ||
9 | |z| | ||
10 | |y| | ||
11 | |zy| | ||
12 | |z| | ||
13 | |y zy z| | ||
14 | |y zy z| | ||
diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_bash1.tests new file mode 100755 index 000000000..912635925 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_repl_bash1.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | v=xx | ||
3 | |||
4 | f ${v/'x'/"y z"} | ||
5 | f ${v/"x"/'y z'} | ||
6 | f "${v/'x'/"y z"}" | ||
7 | f "${v/"x"/'y z'}" | ||
8 | |||
9 | f ${v//'x'/"y z"} | ||
10 | f ${v//"x"/'y z'} | ||
11 | f "${v//'x'/"y z"}" | ||
12 | f "${v//"x"/'y z'}" | ||
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp.right b/shell/ash_test/ash-quoting/squote_in_varexp.right index a75c0bfd6..4a457021b 100644 --- a/shell/ash_test/ash-quoting/squote_in_varexp.right +++ b/shell/ash_test/ash-quoting/squote_in_varexp.right | |||
@@ -1,5 +1,9 @@ | |||
1 | z | 1 | z |
2 | z | 2 | z |
3 | z | ||
4 | z | ||
5 | y | ||
6 | y | ||
3 | y | 7 | y |
4 | y | 8 | y |
5 | Ok:0 | 9 | Ok:0 |
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp.tests b/shell/ash_test/ash-quoting/squote_in_varexp.tests index a2d05a246..4afc52107 100755 --- a/shell/ash_test/ash-quoting/squote_in_varexp.tests +++ b/shell/ash_test/ash-quoting/squote_in_varexp.tests | |||
@@ -1,6 +1,10 @@ | |||
1 | x=yz | 1 | x=yz |
2 | echo ${x#'y'} | 2 | echo ${x#'y'} |
3 | echo "${x#'y'}" | 3 | echo "${x#'y'}" |
4 | echo ${x#"y"} | ||
5 | echo "${x#"y"}" | ||
4 | echo ${x%'z'} | 6 | echo ${x%'z'} |
5 | echo "${x%'z'}" | 7 | echo "${x%'z'}" |
8 | echo ${x%"z"} | ||
9 | echo "${x%"z"}" | ||
6 | echo Ok:$? | 10 | echo Ok:$? |
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp2.right b/shell/ash_test/ash-quoting/squote_in_varexp2.right index 9d0add3c5..d03047024 100644 --- a/shell/ash_test/ash-quoting/squote_in_varexp2.right +++ b/shell/ash_test/ash-quoting/squote_in_varexp2.right | |||
@@ -1,3 +1,5 @@ | |||
1 | Nothing: | 1 | Nothing: |
2 | Nothing: | 2 | Nothing: |
3 | Nothing: | ||
4 | Nothing: | ||
3 | Ok:0 | 5 | Ok:0 |
diff --git a/shell/ash_test/ash-quoting/squote_in_varexp2.tests b/shell/ash_test/ash-quoting/squote_in_varexp2.tests index 806ad12b9..2797725cc 100755 --- a/shell/ash_test/ash-quoting/squote_in_varexp2.tests +++ b/shell/ash_test/ash-quoting/squote_in_varexp2.tests | |||
@@ -1,4 +1,6 @@ | |||
1 | x='\\\\' | 1 | x='\\\\' |
2 | printf Nothing:'%s\n' ${x#'\\\\'} | 2 | printf Nothing:'%s\n' ${x#'\\\\'} |
3 | printf Nothing:'%s\n' "${x#'\\\\'}" | 3 | printf Nothing:'%s\n' "${x#'\\\\'}" |
4 | printf Nothing:'%s\n' ${x#"\\\\\\\\"} | ||
5 | printf Nothing:'%s\n' "${x#"\\\\\\\\"}" | ||
4 | echo Ok:$? | 6 | echo Ok:$? |
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests index 740daa461..a8d93ce4f 100755 --- a/shell/ash_test/ash-redir/redir_script.tests +++ b/shell/ash_test/ash-redir/redir_script.tests | |||
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \ | |||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | 27 | test x"$fds1" = x" 10>&- 3>&-" && \ |
28 | test x"$fds" = x" 11>&- 3>&-" \ | 28 | test x"$fds" = x" 11>&- 3>&-" \ |
29 | && { echo "Ok: script fd is not closed"; exit 0; } | 29 | && { echo "Ok: script fd is not closed"; exit 0; } |
30 | # or we see that fd 3 moved to fd 10: | ||
31 | test x"$fds1" = x" 3>&- 4>&-" && \ | ||
32 | test x"$fds" = x" 10>&- 3>&-" \ | ||
33 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
30 | 34 | ||
31 | echo "Bug: script fd is closed" | 35 | echo "Bug: script fd is closed" |
32 | echo "fds1:$fds1" | 36 | echo "fds1:$fds1" |
diff --git a/shell/ash_test/ash-z_slow/many_ifs.tests b/shell/ash_test/ash-z_slow/many_ifs.tests index 1f5b1b3a6..cf9a89874 100755 --- a/shell/ash_test/ash-z_slow/many_ifs.tests +++ b/shell/ash_test/ash-z_slow/many_ifs.tests | |||
@@ -229,8 +229,8 @@ do | |||
229 | '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; | 229 | '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; |
230 | ' ') ;; | 230 | ' ') ;; |
231 | *) x=$f2$d2$f3$d3 | 231 | *) x=$f2$d2$f3$d3 |
232 | x=${x# } #was x=${x#' '} hush needs fixing for this to work | 232 | x=${x#' '} |
233 | x=${x% } #was x=${x%' '} | 233 | x=${x%' '} |
234 | split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" | 234 | split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" |
235 | ;; | 235 | ;; |
236 | esac | 236 | esac |
diff --git a/shell/hush.c b/shell/hush.c index c77700175..e3c6e2de9 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -357,6 +357,9 @@ | |||
357 | #else | 357 | #else |
358 | # define CLEAR_RANDOM_T(rnd) ((void)0) | 358 | # define CLEAR_RANDOM_T(rnd) ((void)0) |
359 | #endif | 359 | #endif |
360 | #ifndef O_CLOEXEC | ||
361 | # define O_CLOEXEC 0 | ||
362 | #endif | ||
360 | #ifndef F_DUPFD_CLOEXEC | 363 | #ifndef F_DUPFD_CLOEXEC |
361 | # define F_DUPFD_CLOEXEC F_DUPFD | 364 | # define F_DUPFD_CLOEXEC F_DUPFD |
362 | #endif | 365 | #endif |
@@ -439,21 +442,22 @@ | |||
439 | 442 | ||
440 | /* If you comment out one of these below, it will be #defined later | 443 | /* If you comment out one of these below, it will be #defined later |
441 | * to perform debug printfs to stderr: */ | 444 | * to perform debug printfs to stderr: */ |
442 | #define debug_printf(...) do {} while (0) | 445 | #define debug_printf(...) do {} while (0) |
443 | /* Finer-grained debug switches */ | 446 | /* Finer-grained debug switches */ |
444 | #define debug_printf_parse(...) do {} while (0) | 447 | #define debug_printf_parse(...) do {} while (0) |
445 | #define debug_print_tree(a, b) do {} while (0) | 448 | #define debug_printf_heredoc(...) do {} while (0) |
446 | #define debug_printf_exec(...) do {} while (0) | 449 | #define debug_print_tree(a, b) do {} while (0) |
447 | #define debug_printf_env(...) do {} while (0) | 450 | #define debug_printf_exec(...) do {} while (0) |
448 | #define debug_printf_jobs(...) do {} while (0) | 451 | #define debug_printf_env(...) do {} while (0) |
449 | #define debug_printf_expand(...) do {} while (0) | 452 | #define debug_printf_jobs(...) do {} while (0) |
450 | #define debug_printf_varexp(...) do {} while (0) | 453 | #define debug_printf_expand(...) do {} while (0) |
451 | #define debug_printf_glob(...) do {} while (0) | 454 | #define debug_printf_varexp(...) do {} while (0) |
452 | #define debug_printf_redir(...) do {} while (0) | 455 | #define debug_printf_glob(...) do {} while (0) |
453 | #define debug_printf_list(...) do {} while (0) | 456 | #define debug_printf_redir(...) do {} while (0) |
454 | #define debug_printf_subst(...) do {} while (0) | 457 | #define debug_printf_list(...) do {} while (0) |
455 | #define debug_printf_prompt(...) do {} while (0) | 458 | #define debug_printf_subst(...) do {} while (0) |
456 | #define debug_printf_clean(...) do {} while (0) | 459 | #define debug_printf_prompt(...) do {} while (0) |
460 | #define debug_printf_clean(...) do {} while (0) | ||
457 | 461 | ||
458 | #define ERR_PTR ((void*)(long)1) | 462 | #define ERR_PTR ((void*)(long)1) |
459 | 463 | ||
@@ -534,6 +538,7 @@ typedef struct o_string { | |||
534 | * possibly empty one: word"", wo''rd etc. */ | 538 | * possibly empty one: word"", wo''rd etc. */ |
535 | smallint has_quoted_part; | 539 | smallint has_quoted_part; |
536 | smallint has_empty_slot; | 540 | smallint has_empty_slot; |
541 | smallint ended_in_ifs; | ||
537 | } o_string; | 542 | } o_string; |
538 | enum { | 543 | enum { |
539 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ | 544 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ |
@@ -554,11 +559,27 @@ static const char *const assignment_flag[] = { | |||
554 | }; | 559 | }; |
555 | #endif | 560 | #endif |
556 | 561 | ||
562 | /* We almost can use standard FILE api, but we need an ability to move | ||
563 | * its fd when redirects coincide with it. No api exists for that | ||
564 | * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902). | ||
565 | * HFILE is our internal alternative. Only supports reading. | ||
566 | * Since we now can, we incorporate linked list of all opened HFILEs | ||
567 | * into the struct (used to be a separate mini-list). | ||
568 | */ | ||
569 | typedef struct HFILE { | ||
570 | char *cur; | ||
571 | char *end; | ||
572 | struct HFILE *next_hfile; | ||
573 | int is_stdin; | ||
574 | int fd; | ||
575 | char buf[1024]; | ||
576 | } HFILE; | ||
577 | |||
557 | typedef struct in_str { | 578 | typedef struct in_str { |
558 | const char *p; | 579 | const char *p; |
559 | int peek_buf[2]; | 580 | int peek_buf[2]; |
560 | int last_char; | 581 | int last_char; |
561 | FILE *file; | 582 | HFILE *file; |
562 | } in_str; | 583 | } in_str; |
563 | 584 | ||
564 | /* The descrip member of this structure is only used to make | 585 | /* The descrip member of this structure is only used to make |
@@ -812,14 +833,6 @@ enum { | |||
812 | NUM_OPT_O | 833 | NUM_OPT_O |
813 | }; | 834 | }; |
814 | 835 | ||
815 | |||
816 | struct FILE_list { | ||
817 | struct FILE_list *next; | ||
818 | FILE *fp; | ||
819 | int fd; | ||
820 | }; | ||
821 | |||
822 | |||
823 | /* "Globals" within this file */ | 836 | /* "Globals" within this file */ |
824 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 837 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
825 | struct globals { | 838 | struct globals { |
@@ -952,7 +965,7 @@ struct globals { | |||
952 | unsigned lineno; | 965 | unsigned lineno; |
953 | char *lineno_var; | 966 | char *lineno_var; |
954 | #endif | 967 | #endif |
955 | struct FILE_list *FILE_list; | 968 | HFILE *HFILE_list; |
956 | /* Which signals have non-DFL handler (even with no traps set)? | 969 | /* Which signals have non-DFL handler (even with no traps set)? |
957 | * Set at the start to: | 970 | * Set at the start to: |
958 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | 971 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
@@ -1218,6 +1231,10 @@ static const struct built_in_command bltins2[] = { | |||
1218 | # define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__)) | 1231 | # define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__)) |
1219 | #endif | 1232 | #endif |
1220 | 1233 | ||
1234 | #ifndef debug_printf_heredoc | ||
1235 | # define debug_printf_heredoc(...) (indent(), fdprintf(2, __VA_ARGS__)) | ||
1236 | #endif | ||
1237 | |||
1221 | #ifndef debug_printf_exec | 1238 | #ifndef debug_printf_exec |
1222 | #define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__)) | 1239 | #define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__)) |
1223 | #endif | 1240 | #endif |
@@ -1557,83 +1574,115 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) | |||
1557 | } | 1574 | } |
1558 | 1575 | ||
1559 | 1576 | ||
1560 | /* Manipulating the list of open FILEs */ | 1577 | /* Manipulating HFILEs */ |
1561 | static FILE *remember_FILE(FILE *fp) | 1578 | static HFILE *hfopen(const char *name) |
1562 | { | 1579 | { |
1563 | if (fp) { | 1580 | HFILE *fp; |
1564 | struct FILE_list *n = xmalloc(sizeof(*n)); | 1581 | int fd; |
1565 | n->next = G.FILE_list; | 1582 | |
1566 | G.FILE_list = n; | 1583 | fd = STDIN_FILENO; |
1567 | n->fp = fp; | 1584 | if (name) { |
1568 | n->fd = fileno(fp); | 1585 | fd = open(name, O_RDONLY | O_CLOEXEC); |
1569 | close_on_exec_on(n->fd); | 1586 | if (fd < 0) |
1587 | return NULL; | ||
1588 | if (O_CLOEXEC == 0) /* ancient libc */ | ||
1589 | close_on_exec_on(fd); | ||
1570 | } | 1590 | } |
1591 | |||
1592 | fp = xmalloc(sizeof(*fp)); | ||
1593 | fp->is_stdin = (name == NULL); | ||
1594 | fp->fd = fd; | ||
1595 | fp->cur = fp->end = fp->buf; | ||
1596 | fp->next_hfile = G.HFILE_list; | ||
1597 | G.HFILE_list = fp; | ||
1571 | return fp; | 1598 | return fp; |
1572 | } | 1599 | } |
1573 | static void fclose_and_forget(FILE *fp) | 1600 | static void hfclose(HFILE *fp) |
1574 | { | 1601 | { |
1575 | struct FILE_list **pp = &G.FILE_list; | 1602 | HFILE **pp = &G.HFILE_list; |
1576 | while (*pp) { | 1603 | while (*pp) { |
1577 | struct FILE_list *cur = *pp; | 1604 | HFILE *cur = *pp; |
1578 | if (cur->fp == fp) { | 1605 | if (cur == fp) { |
1579 | *pp = cur->next; | 1606 | *pp = cur->next_hfile; |
1580 | free(cur); | ||
1581 | break; | 1607 | break; |
1582 | } | 1608 | } |
1583 | pp = &cur->next; | 1609 | pp = &cur->next_hfile; |
1584 | } | 1610 | } |
1585 | fclose(fp); | 1611 | if (fp->fd >= 0) |
1612 | close(fp->fd); | ||
1613 | free(fp); | ||
1586 | } | 1614 | } |
1587 | static int save_FILEs_on_redirect(int fd, int avoid_fd) | 1615 | static int refill_HFILE_and_getc(HFILE *fp) |
1588 | { | 1616 | { |
1589 | struct FILE_list *fl = G.FILE_list; | 1617 | int n; |
1618 | |||
1619 | if (fp->fd < 0) { | ||
1620 | /* Already saw EOF */ | ||
1621 | return EOF; | ||
1622 | } | ||
1623 | /* Try to buffer more input */ | ||
1624 | fp->cur = fp->buf; | ||
1625 | n = safe_read(fp->fd, fp->buf, sizeof(fp->buf)); | ||
1626 | if (n < 0) { | ||
1627 | bb_perror_msg("read error"); | ||
1628 | n = 0; | ||
1629 | } | ||
1630 | fp->end = fp->buf + n; | ||
1631 | if (n == 0) { | ||
1632 | /* EOF/error */ | ||
1633 | close(fp->fd); | ||
1634 | fp->fd = -1; | ||
1635 | return EOF; | ||
1636 | } | ||
1637 | return (unsigned char)(*fp->cur++); | ||
1638 | } | ||
1639 | /* Inlined for common case of non-empty buffer. | ||
1640 | */ | ||
1641 | static ALWAYS_INLINE int hfgetc(HFILE *fp) | ||
1642 | { | ||
1643 | if (fp->cur < fp->end) | ||
1644 | return (unsigned char)(*fp->cur++); | ||
1645 | /* Buffer empty */ | ||
1646 | return refill_HFILE_and_getc(fp); | ||
1647 | } | ||
1648 | static int move_HFILEs_on_redirect(int fd, int avoid_fd) | ||
1649 | { | ||
1650 | HFILE *fl = G.HFILE_list; | ||
1590 | while (fl) { | 1651 | while (fl) { |
1591 | if (fd == fl->fd) { | 1652 | if (fd == fl->fd) { |
1592 | /* We use it only on script files, they are all CLOEXEC */ | 1653 | /* We use it only on script files, they are all CLOEXEC */ |
1593 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); | 1654 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); |
1594 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | 1655 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); |
1595 | return 1; | 1656 | return 1; /* "found and moved" */ |
1596 | } | 1657 | } |
1597 | fl = fl->next; | 1658 | fl = fl->next_hfile; |
1598 | } | ||
1599 | return 0; | ||
1600 | } | ||
1601 | static void restore_redirected_FILEs(void) | ||
1602 | { | ||
1603 | struct FILE_list *fl = G.FILE_list; | ||
1604 | while (fl) { | ||
1605 | int should_be = fileno(fl->fp); | ||
1606 | if (fl->fd != should_be) { | ||
1607 | debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be); | ||
1608 | xmove_fd(fl->fd, should_be); | ||
1609 | fl->fd = should_be; | ||
1610 | } | ||
1611 | fl = fl->next; | ||
1612 | } | 1659 | } |
1660 | return 0; /* "not in the list" */ | ||
1613 | } | 1661 | } |
1614 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 1662 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
1615 | static void close_all_FILE_list(void) | 1663 | static void close_all_HFILE_list(void) |
1616 | { | 1664 | { |
1617 | struct FILE_list *fl = G.FILE_list; | 1665 | HFILE *fl = G.HFILE_list; |
1618 | while (fl) { | 1666 | while (fl) { |
1619 | /* fclose would also free FILE object. | 1667 | /* hfclose would also free HFILE object. |
1620 | * It is disastrous if we share memory with a vforked parent. | 1668 | * It is disastrous if we share memory with a vforked parent. |
1621 | * I'm not sure we never come here after vfork. | 1669 | * I'm not sure we never come here after vfork. |
1622 | * Therefore just close fd, nothing more. | 1670 | * Therefore just close fd, nothing more. |
1623 | */ | 1671 | */ |
1624 | /*fclose(fl->fp); - unsafe */ | 1672 | /*hfclose(fl); - unsafe */ |
1625 | close(fl->fd); | 1673 | if (fl->fd >= 0) |
1626 | fl = fl->next; | 1674 | close(fl->fd); |
1675 | fl = fl->next_hfile; | ||
1627 | } | 1676 | } |
1628 | } | 1677 | } |
1629 | #endif | 1678 | #endif |
1630 | static int fd_in_FILEs(int fd) | 1679 | static int fd_in_HFILEs(int fd) |
1631 | { | 1680 | { |
1632 | struct FILE_list *fl = G.FILE_list; | 1681 | HFILE *fl = G.HFILE_list; |
1633 | while (fl) { | 1682 | while (fl) { |
1634 | if (fl->fd == fd) | 1683 | if (fl->fd == fd) |
1635 | return 1; | 1684 | return 1; |
1636 | fl = fl->next; | 1685 | fl = fl->next_hfile; |
1637 | } | 1686 | } |
1638 | return 0; | 1687 | return 0; |
1639 | } | 1688 | } |
@@ -2333,6 +2382,7 @@ static void set_pwd_var(unsigned flag) | |||
2333 | set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag); | 2382 | set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag); |
2334 | } | 2383 | } |
2335 | 2384 | ||
2385 | #if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS | ||
2336 | static int unset_local_var_len(const char *name, int name_len) | 2386 | static int unset_local_var_len(const char *name, int name_len) |
2337 | { | 2387 | { |
2338 | struct variable *cur; | 2388 | struct variable *cur; |
@@ -2366,14 +2416,14 @@ static int unset_local_var_len(const char *name, int name_len) | |||
2366 | return EXIT_SUCCESS; | 2416 | return EXIT_SUCCESS; |
2367 | } | 2417 | } |
2368 | 2418 | ||
2369 | #if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS | ||
2370 | static int unset_local_var(const char *name) | 2419 | static int unset_local_var(const char *name) |
2371 | { | 2420 | { |
2372 | return unset_local_var_len(name, strlen(name)); | 2421 | return unset_local_var_len(name, strlen(name)); |
2373 | } | 2422 | } |
2374 | #endif | 2423 | #endif |
2375 | 2424 | ||
2376 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS | 2425 | #if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS \ |
2426 | || (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) | ||
2377 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2427 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) |
2378 | { | 2428 | { |
2379 | char *var = xasprintf("%s=%s", name, val); | 2429 | char *var = xasprintf("%s=%s", name, val); |
@@ -2422,35 +2472,33 @@ static void set_vars_and_save_old(char **strings) | |||
2422 | char *eq; | 2472 | char *eq; |
2423 | 2473 | ||
2424 | eq = strchr(*s, '='); | 2474 | eq = strchr(*s, '='); |
2425 | if (eq) { | 2475 | if (HUSH_DEBUG && !eq) |
2426 | var_pp = get_ptr_to_local_var(*s, eq - *s); | ||
2427 | if (var_pp) { | ||
2428 | var_p = *var_pp; | ||
2429 | if (var_p->flg_read_only) { | ||
2430 | char **p; | ||
2431 | bb_error_msg("%s: readonly variable", *s); | ||
2432 | /* | ||
2433 | * "VAR=V BLTIN" unsets VARs after BLTIN completes. | ||
2434 | * If VAR is readonly, leaving it in the list | ||
2435 | * after asssignment error (msg above) | ||
2436 | * causes doubled error message later, on unset. | ||
2437 | */ | ||
2438 | debug_printf_env("removing/freeing '%s' element\n", *s); | ||
2439 | free(*s); | ||
2440 | p = s; | ||
2441 | do { *p = p[1]; p++; } while (*p); | ||
2442 | goto next; | ||
2443 | } | ||
2444 | /* below, set_local_var() with nest level will | ||
2445 | * "shadow" (remove) this variable from | ||
2446 | * global linked list. | ||
2447 | */ | ||
2448 | } | ||
2449 | debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level); | ||
2450 | set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT); | ||
2451 | } else if (HUSH_DEBUG) { | ||
2452 | bb_error_msg_and_die("BUG in varexp4"); | 2476 | bb_error_msg_and_die("BUG in varexp4"); |
2477 | var_pp = get_ptr_to_local_var(*s, eq - *s); | ||
2478 | if (var_pp) { | ||
2479 | var_p = *var_pp; | ||
2480 | if (var_p->flg_read_only) { | ||
2481 | char **p; | ||
2482 | bb_error_msg("%s: readonly variable", *s); | ||
2483 | /* | ||
2484 | * "VAR=V BLTIN" unsets VARs after BLTIN completes. | ||
2485 | * If VAR is readonly, leaving it in the list | ||
2486 | * after asssignment error (msg above) | ||
2487 | * causes doubled error message later, on unset. | ||
2488 | */ | ||
2489 | debug_printf_env("removing/freeing '%s' element\n", *s); | ||
2490 | free(*s); | ||
2491 | p = s; | ||
2492 | do { *p = p[1]; p++; } while (*p); | ||
2493 | goto next; | ||
2494 | } | ||
2495 | /* below, set_local_var() with nest level will | ||
2496 | * "shadow" (remove) this variable from | ||
2497 | * global linked list. | ||
2498 | */ | ||
2453 | } | 2499 | } |
2500 | debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level); | ||
2501 | set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT); | ||
2454 | s++; | 2502 | s++; |
2455 | next: ; | 2503 | next: ; |
2456 | } | 2504 | } |
@@ -2469,7 +2517,7 @@ static void reinit_unicode_for_hush(void) | |||
2469 | */ | 2517 | */ |
2470 | if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV | 2518 | if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV |
2471 | || ENABLE_UNICODE_USING_LOCALE | 2519 | || ENABLE_UNICODE_USING_LOCALE |
2472 | ) { | 2520 | ) { |
2473 | const char *s = get_local_var_value("LC_ALL"); | 2521 | const char *s = get_local_var_value("LC_ALL"); |
2474 | if (!s) s = get_local_var_value("LC_CTYPE"); | 2522 | if (!s) s = get_local_var_value("LC_CTYPE"); |
2475 | if (!s) s = get_local_var_value("LANG"); | 2523 | if (!s) s = get_local_var_value("LANG"); |
@@ -2575,7 +2623,7 @@ static int get_user_input(struct in_str *i) | |||
2575 | } | 2623 | } |
2576 | fflush_all(); | 2624 | fflush_all(); |
2577 | //FIXME: here ^C or SIGINT will have effect only after <Enter> | 2625 | //FIXME: here ^C or SIGINT will have effect only after <Enter> |
2578 | r = fgetc(i->file); | 2626 | r = hfgetc(i->file); |
2579 | /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, | 2627 | /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, |
2580 | * no ^C masking happens during fgetc, no special code for ^C: | 2628 | * no ^C masking happens during fgetc, no special code for ^C: |
2581 | * it generates SIGINT as usual. | 2629 | * it generates SIGINT as usual. |
@@ -2595,22 +2643,22 @@ static int fgetc_interactive(struct in_str *i) | |||
2595 | { | 2643 | { |
2596 | int ch; | 2644 | int ch; |
2597 | /* If it's interactive stdin, get new line. */ | 2645 | /* If it's interactive stdin, get new line. */ |
2598 | if (G_interactive_fd && i->file == stdin) { | 2646 | if (G_interactive_fd && i->file->is_stdin) { |
2599 | /* Returns first char (or EOF), the rest is in i->p[] */ | 2647 | /* Returns first char (or EOF), the rest is in i->p[] */ |
2600 | ch = get_user_input(i); | 2648 | ch = get_user_input(i); |
2601 | G.promptmode = 1; /* PS2 */ | 2649 | G.promptmode = 1; /* PS2 */ |
2602 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 2650 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
2603 | } else { | 2651 | } else { |
2604 | /* Not stdin: script file, sourced file, etc */ | 2652 | /* Not stdin: script file, sourced file, etc */ |
2605 | do ch = fgetc(i->file); while (ch == '\0'); | 2653 | do ch = hfgetc(i->file); while (ch == '\0'); |
2606 | } | 2654 | } |
2607 | return ch; | 2655 | return ch; |
2608 | } | 2656 | } |
2609 | #else | 2657 | #else |
2610 | static inline int fgetc_interactive(struct in_str *i) | 2658 | static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) |
2611 | { | 2659 | { |
2612 | int ch; | 2660 | int ch; |
2613 | do ch = fgetc(i->file); while (ch == '\0'); | 2661 | do ch = hfgetc(i->file); while (ch == '\0'); |
2614 | return ch; | 2662 | return ch; |
2615 | } | 2663 | } |
2616 | #endif /* INTERACTIVE */ | 2664 | #endif /* INTERACTIVE */ |
@@ -2725,7 +2773,7 @@ static int i_peek2(struct in_str *i) | |||
2725 | ch = i->peek_buf[1]; | 2773 | ch = i->peek_buf[1]; |
2726 | if (ch == 0) { | 2774 | if (ch == 0) { |
2727 | /* We did not read it yet, get it now */ | 2775 | /* We did not read it yet, get it now */ |
2728 | do ch = fgetc(i->file); while (ch == '\0'); | 2776 | do ch = hfgetc(i->file); while (ch == '\0'); |
2729 | i->peek_buf[1] = ch; | 2777 | i->peek_buf[1] = ch; |
2730 | } | 2778 | } |
2731 | 2779 | ||
@@ -2769,10 +2817,10 @@ static int i_peek_and_eat_bkslash_nl(struct in_str *input) | |||
2769 | } | 2817 | } |
2770 | } | 2818 | } |
2771 | 2819 | ||
2772 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2820 | static void setup_file_in_str(struct in_str *i, HFILE *fp) |
2773 | { | 2821 | { |
2774 | memset(i, 0, sizeof(*i)); | 2822 | memset(i, 0, sizeof(*i)); |
2775 | i->file = f; | 2823 | i->file = fp; |
2776 | /* i->p = NULL; */ | 2824 | /* i->p = NULL; */ |
2777 | } | 2825 | } |
2778 | 2826 | ||
@@ -2797,13 +2845,13 @@ static void o_reset_to_empty_unquoted(o_string *o) | |||
2797 | o->data[0] = '\0'; | 2845 | o->data[0] = '\0'; |
2798 | } | 2846 | } |
2799 | 2847 | ||
2800 | static void o_free(o_string *o) | 2848 | static void o_free_and_set_NULL(o_string *o) |
2801 | { | 2849 | { |
2802 | free(o->data); | 2850 | free(o->data); |
2803 | memset(o, 0, sizeof(*o)); | 2851 | memset(o, 0, sizeof(*o)); |
2804 | } | 2852 | } |
2805 | 2853 | ||
2806 | static ALWAYS_INLINE void o_free_unsafe(o_string *o) | 2854 | static ALWAYS_INLINE void o_free(o_string *o) |
2807 | { | 2855 | { |
2808 | free(o->data); | 2856 | free(o->data); |
2809 | } | 2857 | } |
@@ -3342,7 +3390,6 @@ static char **o_finalize_list(o_string *o, int n) | |||
3342 | char **list; | 3390 | char **list; |
3343 | int string_start; | 3391 | int string_start; |
3344 | 3392 | ||
3345 | n = o_save_ptr(o, n); /* force growth for list[n] if necessary */ | ||
3346 | if (DEBUG_EXPAND) | 3393 | if (DEBUG_EXPAND) |
3347 | debug_print_list("finalized", o, n); | 3394 | debug_print_list("finalized", o, n); |
3348 | debug_printf_expand("finalized n:%d\n", n); | 3395 | debug_printf_expand("finalized n:%d\n", n); |
@@ -3530,6 +3577,8 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3530 | fdprintf(2, " '%s'", *argv); | 3577 | fdprintf(2, " '%s'", *argv); |
3531 | argv++; | 3578 | argv++; |
3532 | } | 3579 | } |
3580 | if (command->redirects) | ||
3581 | fdprintf(2, " {redir}"); | ||
3533 | fdprintf(2, "\n"); | 3582 | fdprintf(2, "\n"); |
3534 | prn++; | 3583 | prn++; |
3535 | } | 3584 | } |
@@ -3853,7 +3902,7 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) | |||
3853 | int len = old->as_string.length; | 3902 | int len = old->as_string.length; |
3854 | /* Concatenate halves */ | 3903 | /* Concatenate halves */ |
3855 | o_addstr(&old->as_string, ctx->as_string.data); | 3904 | o_addstr(&old->as_string, ctx->as_string.data); |
3856 | o_free_unsafe(&ctx->as_string); | 3905 | o_free(&ctx->as_string); |
3857 | /* Find where leading keyword starts in first half */ | 3906 | /* Find where leading keyword starts in first half */ |
3858 | str = old->as_string.data + len; | 3907 | str = old->as_string.data + len; |
3859 | if (str > old->as_string.data) | 3908 | if (str > old->as_string.data) |
@@ -4227,6 +4276,14 @@ static char *fetch_till_str(o_string *as_string, | |||
4227 | int prev = 0; /* not \ */ | 4276 | int prev = 0; /* not \ */ |
4228 | int ch; | 4277 | int ch; |
4229 | 4278 | ||
4279 | /* Starting with "" is necessary for this case: | ||
4280 | * cat <<EOF | ||
4281 | * | ||
4282 | * xxx | ||
4283 | * EOF | ||
4284 | */ | ||
4285 | heredoc.data = xzalloc(1); /* start as "", not as NULL */ | ||
4286 | |||
4230 | goto jump_in; | 4287 | goto jump_in; |
4231 | 4288 | ||
4232 | while (1) { | 4289 | while (1) { |
@@ -4236,9 +4293,10 @@ static char *fetch_till_str(o_string *as_string, | |||
4236 | if (ch == '\n' || ch == EOF) { | 4293 | if (ch == '\n' || ch == EOF) { |
4237 | check_heredoc_end: | 4294 | check_heredoc_end: |
4238 | if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') { | 4295 | if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') { |
4296 | /* End-of-line, and not a line continuation */ | ||
4239 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | 4297 | if (strcmp(heredoc.data + past_EOL, word) == 0) { |
4240 | heredoc.data[past_EOL] = '\0'; | 4298 | heredoc.data[past_EOL] = '\0'; |
4241 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | 4299 | debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data); |
4242 | return heredoc.data; | 4300 | return heredoc.data; |
4243 | } | 4301 | } |
4244 | if (ch == '\n') { | 4302 | if (ch == '\n') { |
@@ -4261,17 +4319,32 @@ static char *fetch_till_str(o_string *as_string, | |||
4261 | if (ch == '\n') | 4319 | if (ch == '\n') |
4262 | goto check_heredoc_end; | 4320 | goto check_heredoc_end; |
4263 | } | 4321 | } |
4322 | } else { | ||
4323 | /* Backslash-line continuation in an unquoted | ||
4324 | * heredoc. This does not need special handling | ||
4325 | * for heredoc body (unquoted heredocs are | ||
4326 | * expanded on "execution" and that would take | ||
4327 | * care of this case too), but not the case | ||
4328 | * of line continuation *in terminator*: | ||
4329 | * cat <<EOF | ||
4330 | * Ok1 | ||
4331 | * EO\ | ||
4332 | * F | ||
4333 | */ | ||
4334 | heredoc.data[--heredoc.length] = '\0'; | ||
4335 | prev = 0; /* not '\' */ | ||
4336 | continue; | ||
4264 | } | 4337 | } |
4265 | } | 4338 | } |
4266 | if (ch == EOF) { | 4339 | if (ch == EOF) { |
4267 | o_free_unsafe(&heredoc); | 4340 | o_free(&heredoc); |
4268 | return NULL; | 4341 | return NULL; /* error */ |
4269 | } | 4342 | } |
4270 | o_addchr(&heredoc, ch); | 4343 | o_addchr(&heredoc, ch); |
4271 | nommu_addchr(as_string, ch); | 4344 | nommu_addchr(as_string, ch); |
4272 | if (prev == '\\' && ch == '\\') | 4345 | if (prev == '\\' && ch == '\\') |
4273 | /* Correctly handle foo\\<eol> (not a line cont.) */ | 4346 | /* Correctly handle foo\\<eol> (not a line cont.) */ |
4274 | prev = 0; /* not \ */ | 4347 | prev = 0; /* not '\' */ |
4275 | else | 4348 | else |
4276 | prev = ch; | 4349 | prev = ch; |
4277 | } | 4350 | } |
@@ -4280,21 +4353,24 @@ static char *fetch_till_str(o_string *as_string, | |||
4280 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs | 4353 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs |
4281 | * and load them all. There should be exactly heredoc_cnt of them. | 4354 | * and load them all. There should be exactly heredoc_cnt of them. |
4282 | */ | 4355 | */ |
4283 | static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) | 4356 | #if BB_MMU |
4357 | #define fetch_heredocs(as_string, pi, heredoc_cnt, input) \ | ||
4358 | fetch_heredocs(pi, heredoc_cnt, input) | ||
4359 | #endif | ||
4360 | static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input) | ||
4284 | { | 4361 | { |
4285 | struct pipe *pi = ctx->list_head; | ||
4286 | |||
4287 | while (pi && heredoc_cnt) { | 4362 | while (pi && heredoc_cnt) { |
4288 | int i; | 4363 | int i; |
4289 | struct command *cmd = pi->cmds; | 4364 | struct command *cmd = pi->cmds; |
4290 | 4365 | ||
4291 | debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", | 4366 | debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", |
4292 | pi->num_cmds, | 4367 | pi->num_cmds, |
4293 | cmd->argv ? cmd->argv[0] : "NONE"); | 4368 | cmd->argv ? cmd->argv[0] : "NONE" |
4369 | ); | ||
4294 | for (i = 0; i < pi->num_cmds; i++) { | 4370 | for (i = 0; i < pi->num_cmds; i++) { |
4295 | struct redir_struct *redir = cmd->redirects; | 4371 | struct redir_struct *redir = cmd->redirects; |
4296 | 4372 | ||
4297 | debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", | 4373 | debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n", |
4298 | i, cmd->argv ? cmd->argv[0] : "NONE"); | 4374 | i, cmd->argv ? cmd->argv[0] : "NONE"); |
4299 | while (redir) { | 4375 | while (redir) { |
4300 | if (redir->rd_type == REDIRECT_HEREDOC) { | 4376 | if (redir->rd_type == REDIRECT_HEREDOC) { |
@@ -4302,11 +4378,11 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4302 | 4378 | ||
4303 | redir->rd_type = REDIRECT_HEREDOC2; | 4379 | redir->rd_type = REDIRECT_HEREDOC2; |
4304 | /* redir->rd_dup is (ab)used to indicate <<- */ | 4380 | /* redir->rd_dup is (ab)used to indicate <<- */ |
4305 | p = fetch_till_str(&ctx->as_string, input, | 4381 | p = fetch_till_str(as_string, input, |
4306 | redir->rd_filename, redir->rd_dup); | 4382 | redir->rd_filename, redir->rd_dup); |
4307 | if (!p) { | 4383 | if (!p) { |
4308 | syntax_error("unexpected EOF in here document"); | 4384 | syntax_error("unexpected EOF in here document"); |
4309 | return 1; | 4385 | return -1; |
4310 | } | 4386 | } |
4311 | free(redir->rd_filename); | 4387 | free(redir->rd_filename); |
4312 | redir->rd_filename = p; | 4388 | redir->rd_filename = p; |
@@ -4314,31 +4390,36 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4314 | } | 4390 | } |
4315 | redir = redir->next; | 4391 | redir = redir->next; |
4316 | } | 4392 | } |
4393 | if (cmd->group) { | ||
4394 | //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt); | ||
4395 | heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input); | ||
4396 | //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt); | ||
4397 | if (heredoc_cnt < 0) | ||
4398 | return heredoc_cnt; /* error */ | ||
4399 | } | ||
4317 | cmd++; | 4400 | cmd++; |
4318 | } | 4401 | } |
4319 | pi = pi->next; | 4402 | pi = pi->next; |
4320 | } | 4403 | } |
4321 | #if 0 | 4404 | return heredoc_cnt; |
4322 | /* Should be 0. If it isn't, it's a parse error */ | ||
4323 | if (heredoc_cnt) | ||
4324 | bb_error_msg_and_die("heredoc BUG 2"); | ||
4325 | #endif | ||
4326 | return 0; | ||
4327 | } | 4405 | } |
4328 | 4406 | ||
4329 | 4407 | ||
4330 | static int run_list(struct pipe *pi); | 4408 | static int run_list(struct pipe *pi); |
4331 | #if BB_MMU | 4409 | #if BB_MMU |
4332 | #define parse_stream(pstring, input, end_trigger) \ | 4410 | #define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \ |
4333 | parse_stream(input, end_trigger) | 4411 | parse_stream(heredoc_cnt_ptr, input, end_trigger) |
4334 | #endif | 4412 | #endif |
4335 | static struct pipe *parse_stream(char **pstring, | 4413 | static struct pipe *parse_stream(char **pstring, |
4414 | int *heredoc_cnt_ptr, | ||
4336 | struct in_str *input, | 4415 | struct in_str *input, |
4337 | int end_trigger); | 4416 | int end_trigger); |
4338 | 4417 | ||
4339 | 4418 | /* Returns number of heredocs not yet consumed, | |
4419 | * or -1 on error. | ||
4420 | */ | ||
4340 | static int parse_group(struct parse_context *ctx, | 4421 | static int parse_group(struct parse_context *ctx, |
4341 | struct in_str *input, int ch) | 4422 | struct in_str *input, int ch) |
4342 | { | 4423 | { |
4343 | /* ctx->word contains characters seen prior to ( or {. | 4424 | /* ctx->word contains characters seen prior to ( or {. |
4344 | * Typically it's empty, but for function defs, | 4425 | * Typically it's empty, but for function defs, |
@@ -4349,6 +4430,7 @@ static int parse_group(struct parse_context *ctx, | |||
4349 | char *as_string = NULL; | 4430 | char *as_string = NULL; |
4350 | #endif | 4431 | #endif |
4351 | struct pipe *pipe_list; | 4432 | struct pipe *pipe_list; |
4433 | int heredoc_cnt = 0; | ||
4352 | int endch; | 4434 | int endch; |
4353 | struct command *command = ctx->command; | 4435 | struct command *command = ctx->command; |
4354 | 4436 | ||
@@ -4357,12 +4439,12 @@ static int parse_group(struct parse_context *ctx, | |||
4357 | if (ch == '(' && !ctx->word.has_quoted_part) { | 4439 | if (ch == '(' && !ctx->word.has_quoted_part) { |
4358 | if (ctx->word.length) | 4440 | if (ctx->word.length) |
4359 | if (done_word(ctx)) | 4441 | if (done_word(ctx)) |
4360 | return 1; | 4442 | return -1; |
4361 | if (!command->argv) | 4443 | if (!command->argv) |
4362 | goto skip; /* (... */ | 4444 | goto skip; /* (... */ |
4363 | if (command->argv[1]) { /* word word ... (... */ | 4445 | if (command->argv[1]) { /* word word ... (... */ |
4364 | syntax_error_unexpected_ch('('); | 4446 | syntax_error_unexpected_ch('('); |
4365 | return 1; | 4447 | return -1; |
4366 | } | 4448 | } |
4367 | /* it is "word(..." or "word (..." */ | 4449 | /* it is "word(..." or "word (..." */ |
4368 | do | 4450 | do |
@@ -4370,7 +4452,7 @@ static int parse_group(struct parse_context *ctx, | |||
4370 | while (ch == ' ' || ch == '\t'); | 4452 | while (ch == ' ' || ch == '\t'); |
4371 | if (ch != ')') { | 4453 | if (ch != ')') { |
4372 | syntax_error_unexpected_ch(ch); | 4454 | syntax_error_unexpected_ch(ch); |
4373 | return 1; | 4455 | return -1; |
4374 | } | 4456 | } |
4375 | nommu_addchr(&ctx->as_string, ch); | 4457 | nommu_addchr(&ctx->as_string, ch); |
4376 | do | 4458 | do |
@@ -4378,7 +4460,7 @@ static int parse_group(struct parse_context *ctx, | |||
4378 | while (ch == ' ' || ch == '\t' || ch == '\n'); | 4460 | while (ch == ' ' || ch == '\t' || ch == '\n'); |
4379 | if (ch != '{' && ch != '(') { | 4461 | if (ch != '{' && ch != '(') { |
4380 | syntax_error_unexpected_ch(ch); | 4462 | syntax_error_unexpected_ch(ch); |
4381 | return 1; | 4463 | return -1; |
4382 | } | 4464 | } |
4383 | nommu_addchr(&ctx->as_string, ch); | 4465 | nommu_addchr(&ctx->as_string, ch); |
4384 | command->cmd_type = CMD_FUNCDEF; | 4466 | command->cmd_type = CMD_FUNCDEF; |
@@ -4392,9 +4474,9 @@ static int parse_group(struct parse_context *ctx, | |||
4392 | || ctx->word.has_quoted_part /* ""{... */ | 4474 | || ctx->word.has_quoted_part /* ""{... */ |
4393 | ) { | 4475 | ) { |
4394 | syntax_error(NULL); | 4476 | syntax_error(NULL); |
4395 | debug_printf_parse("parse_group return 1: " | 4477 | debug_printf_parse("parse_group return -1: " |
4396 | "syntax error, groups and arglists don't mix\n"); | 4478 | "syntax error, groups and arglists don't mix\n"); |
4397 | return 1; | 4479 | return -1; |
4398 | } | 4480 | } |
4399 | #endif | 4481 | #endif |
4400 | 4482 | ||
@@ -4412,7 +4494,7 @@ static int parse_group(struct parse_context *ctx, | |||
4412 | && ch != '(' /* but "{(..." is allowed (without whitespace) */ | 4494 | && ch != '(' /* but "{(..." is allowed (without whitespace) */ |
4413 | ) { | 4495 | ) { |
4414 | syntax_error_unexpected_ch(ch); | 4496 | syntax_error_unexpected_ch(ch); |
4415 | return 1; | 4497 | return -1; |
4416 | } | 4498 | } |
4417 | if (ch != '(') { | 4499 | if (ch != '(') { |
4418 | ch = i_getch(input); | 4500 | ch = i_getch(input); |
@@ -4420,7 +4502,9 @@ static int parse_group(struct parse_context *ctx, | |||
4420 | } | 4502 | } |
4421 | } | 4503 | } |
4422 | 4504 | ||
4423 | pipe_list = parse_stream(&as_string, input, endch); | 4505 | debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt); |
4506 | pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch); | ||
4507 | debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt); | ||
4424 | #if !BB_MMU | 4508 | #if !BB_MMU |
4425 | if (as_string) | 4509 | if (as_string) |
4426 | o_addstr(&ctx->as_string, as_string); | 4510 | o_addstr(&ctx->as_string, as_string); |
@@ -4431,9 +4515,9 @@ static int parse_group(struct parse_context *ctx, | |||
4431 | /* parse_stream already emitted error msg */ | 4515 | /* parse_stream already emitted error msg */ |
4432 | if (!BB_MMU) | 4516 | if (!BB_MMU) |
4433 | free(as_string); | 4517 | free(as_string); |
4434 | debug_printf_parse("parse_group return 1: " | 4518 | debug_printf_parse("parse_group return -1: " |
4435 | "parse_stream returned %p\n", pipe_list); | 4519 | "parse_stream returned %p\n", pipe_list); |
4436 | return 1; | 4520 | return -1; |
4437 | } | 4521 | } |
4438 | #if !BB_MMU | 4522 | #if !BB_MMU |
4439 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ | 4523 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ |
@@ -4464,15 +4548,14 @@ static int parse_group(struct parse_context *ctx, | |||
4464 | 4548 | ||
4465 | command->group = pipe_list; | 4549 | command->group = pipe_list; |
4466 | 4550 | ||
4467 | debug_printf_parse("parse_group return 0\n"); | 4551 | debug_printf_parse("parse_group return %d\n", heredoc_cnt); |
4468 | return 0; | 4552 | return heredoc_cnt; |
4469 | /* command remains "open", available for possible redirects */ | 4553 | /* command remains "open", available for possible redirects */ |
4470 | #undef as_string | 4554 | #undef as_string |
4471 | } | 4555 | } |
4472 | 4556 | ||
4473 | #if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS | 4557 | #if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS |
4474 | /* Subroutines for copying $(...) and `...` things */ | 4558 | /* Subroutines for copying $(...) and `...` things */ |
4475 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | ||
4476 | /* '...' */ | 4559 | /* '...' */ |
4477 | static int add_till_single_quote(o_string *dest, struct in_str *input) | 4560 | static int add_till_single_quote(o_string *dest, struct in_str *input) |
4478 | { | 4561 | { |
@@ -4487,7 +4570,21 @@ static int add_till_single_quote(o_string *dest, struct in_str *input) | |||
4487 | o_addchr(dest, ch); | 4570 | o_addchr(dest, ch); |
4488 | } | 4571 | } |
4489 | } | 4572 | } |
4573 | static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input) | ||
4574 | { | ||
4575 | while (1) { | ||
4576 | int ch = i_getch(input); | ||
4577 | if (ch == EOF) { | ||
4578 | syntax_error_unterm_ch('\''); | ||
4579 | return 0; | ||
4580 | } | ||
4581 | if (ch == '\'') | ||
4582 | return 1; | ||
4583 | o_addqchr(dest, ch); | ||
4584 | } | ||
4585 | } | ||
4490 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ | 4586 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ |
4587 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | ||
4491 | static int add_till_double_quote(o_string *dest, struct in_str *input) | 4588 | static int add_till_double_quote(o_string *dest, struct in_str *input) |
4492 | { | 4589 | { |
4493 | while (1) { | 4590 | while (1) { |
@@ -4577,7 +4674,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign | |||
4577 | # endif | 4674 | # endif |
4578 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | 4675 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); |
4579 | 4676 | ||
4677 | #if ENABLE_HUSH_INTERACTIVE | ||
4580 | G.promptmode = 1; /* PS2 */ | 4678 | G.promptmode = 1; /* PS2 */ |
4679 | #endif | ||
4581 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 4680 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
4582 | 4681 | ||
4583 | while (1) { | 4682 | while (1) { |
@@ -4885,34 +4984,15 @@ static int parse_dollar(o_string *as_string, | |||
4885 | } | 4984 | } |
4886 | 4985 | ||
4887 | #if BB_MMU | 4986 | #if BB_MMU |
4888 | # if BASH_PATTERN_SUBST | 4987 | #define encode_string(as_string, dest, input, dquote_end) \ |
4889 | #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ | ||
4890 | encode_string(dest, input, dquote_end, process_bkslash) | ||
4891 | # else | ||
4892 | /* only ${var/pattern/repl} (its pattern part) needs additional mode */ | ||
4893 | #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ | ||
4894 | encode_string(dest, input, dquote_end) | 4988 | encode_string(dest, input, dquote_end) |
4895 | # endif | ||
4896 | #define as_string NULL | 4989 | #define as_string NULL |
4897 | |||
4898 | #else /* !MMU */ | ||
4899 | |||
4900 | # if BASH_PATTERN_SUBST | ||
4901 | /* all parameters are needed, no macro tricks */ | ||
4902 | # else | ||
4903 | #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ | ||
4904 | encode_string(as_string, dest, input, dquote_end) | ||
4905 | # endif | ||
4906 | #endif | 4990 | #endif |
4907 | static int encode_string(o_string *as_string, | 4991 | static int encode_string(o_string *as_string, |
4908 | o_string *dest, | 4992 | o_string *dest, |
4909 | struct in_str *input, | 4993 | struct in_str *input, |
4910 | int dquote_end, | 4994 | int dquote_end) |
4911 | int process_bkslash) | ||
4912 | { | 4995 | { |
4913 | #if !BASH_PATTERN_SUBST | ||
4914 | const int process_bkslash = 1; | ||
4915 | #endif | ||
4916 | int ch; | 4996 | int ch; |
4917 | int next; | 4997 | int next; |
4918 | 4998 | ||
@@ -4935,7 +5015,7 @@ static int encode_string(o_string *as_string, | |||
4935 | } | 5015 | } |
4936 | debug_printf_parse("\" ch=%c (%d) escape=%d\n", | 5016 | debug_printf_parse("\" ch=%c (%d) escape=%d\n", |
4937 | ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 5017 | ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); |
4938 | if (process_bkslash && ch == '\\') { | 5018 | if (ch == '\\') { |
4939 | if (next == EOF) { | 5019 | if (next == EOF) { |
4940 | /* Testcase: in interactive shell a file with | 5020 | /* Testcase: in interactive shell a file with |
4941 | * echo "unterminated string\<eof> | 5021 | * echo "unterminated string\<eof> |
@@ -4995,6 +5075,7 @@ static int encode_string(o_string *as_string, | |||
4995 | * or return ERR_PTR. | 5075 | * or return ERR_PTR. |
4996 | */ | 5076 | */ |
4997 | static struct pipe *parse_stream(char **pstring, | 5077 | static struct pipe *parse_stream(char **pstring, |
5078 | int *heredoc_cnt_ptr, | ||
4998 | struct in_str *input, | 5079 | struct in_str *input, |
4999 | int end_trigger) | 5080 | int end_trigger) |
5000 | { | 5081 | { |
@@ -5013,8 +5094,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5013 | /* If very first arg is "" or '', ctx.word.data may end up NULL. | 5094 | /* If very first arg is "" or '', ctx.word.data may end up NULL. |
5014 | * Preventing this: | 5095 | * Preventing this: |
5015 | */ | 5096 | */ |
5016 | o_addchr(&ctx.word, '\0'); | 5097 | ctx.word.data = xzalloc(1); /* start as "", not as NULL */ |
5017 | ctx.word.length = 0; | ||
5018 | 5098 | ||
5019 | /* We used to separate words on $IFS here. This was wrong. | 5099 | /* We used to separate words on $IFS here. This was wrong. |
5020 | * $IFS is used only for word splitting when $var is expanded, | 5100 | * $IFS is used only for word splitting when $var is expanded, |
@@ -5052,7 +5132,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5052 | if (done_word(&ctx)) { | 5132 | if (done_word(&ctx)) { |
5053 | goto parse_error; | 5133 | goto parse_error; |
5054 | } | 5134 | } |
5055 | o_free(&ctx.word); | 5135 | o_free_and_set_NULL(&ctx.word); |
5056 | done_pipe(&ctx, PIPE_SEQ); | 5136 | done_pipe(&ctx, PIPE_SEQ); |
5057 | pi = ctx.list_head; | 5137 | pi = ctx.list_head; |
5058 | /* If we got nothing... */ | 5138 | /* If we got nothing... */ |
@@ -5069,9 +5149,13 @@ static struct pipe *parse_stream(char **pstring, | |||
5069 | if (pstring) | 5149 | if (pstring) |
5070 | *pstring = ctx.as_string.data; | 5150 | *pstring = ctx.as_string.data; |
5071 | else | 5151 | else |
5072 | o_free_unsafe(&ctx.as_string); | 5152 | o_free(&ctx.as_string); |
5073 | #endif | 5153 | #endif |
5154 | // heredoc_cnt must be 0 here anyway | ||
5155 | //if (heredoc_cnt_ptr) | ||
5156 | // *heredoc_cnt_ptr = heredoc_cnt; | ||
5074 | debug_leave(); | 5157 | debug_leave(); |
5158 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
5075 | debug_printf_parse("parse_stream return %p\n", pi); | 5159 | debug_printf_parse("parse_stream return %p\n", pi); |
5076 | return pi; | 5160 | return pi; |
5077 | } | 5161 | } |
@@ -5198,7 +5282,9 @@ static struct pipe *parse_stream(char **pstring, | |||
5198 | * "case ... in <newline> word) ..." | 5282 | * "case ... in <newline> word) ..." |
5199 | */ | 5283 | */ |
5200 | if (IS_NULL_CMD(ctx.command) | 5284 | if (IS_NULL_CMD(ctx.command) |
5201 | && ctx.word.length == 0 && !ctx.word.has_quoted_part | 5285 | && ctx.word.length == 0 |
5286 | && !ctx.word.has_quoted_part | ||
5287 | && heredoc_cnt == 0 | ||
5202 | ) { | 5288 | ) { |
5203 | /* This newline can be ignored. But... | 5289 | /* This newline can be ignored. But... |
5204 | * Without check #1, interactive shell | 5290 | * Without check #1, interactive shell |
@@ -5226,12 +5312,11 @@ static struct pipe *parse_stream(char **pstring, | |||
5226 | } | 5312 | } |
5227 | /* Treat newline as a command separator. */ | 5313 | /* Treat newline as a command separator. */ |
5228 | done_pipe(&ctx, PIPE_SEQ); | 5314 | done_pipe(&ctx, PIPE_SEQ); |
5229 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); | 5315 | debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); |
5230 | if (heredoc_cnt) { | 5316 | if (heredoc_cnt) { |
5231 | if (fetch_heredocs(heredoc_cnt, &ctx, input)) { | 5317 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); |
5318 | if (heredoc_cnt != 0) | ||
5232 | goto parse_error; | 5319 | goto parse_error; |
5233 | } | ||
5234 | heredoc_cnt = 0; | ||
5235 | } | 5320 | } |
5236 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 5321 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
5237 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 5322 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
@@ -5280,19 +5365,6 @@ static struct pipe *parse_stream(char **pstring, | |||
5280 | ) | 5365 | ) |
5281 | #endif | 5366 | #endif |
5282 | ) { | 5367 | ) { |
5283 | if (heredoc_cnt) { | ||
5284 | /* This is technically valid: | ||
5285 | * { cat <<HERE; }; echo Ok | ||
5286 | * heredoc | ||
5287 | * heredoc | ||
5288 | * HERE | ||
5289 | * but we don't support this. | ||
5290 | * We require heredoc to be in enclosing {}/(), | ||
5291 | * if any. | ||
5292 | */ | ||
5293 | syntax_error_unterm_str("here document"); | ||
5294 | goto parse_error; | ||
5295 | } | ||
5296 | if (done_word(&ctx)) { | 5368 | if (done_word(&ctx)) { |
5297 | goto parse_error; | 5369 | goto parse_error; |
5298 | } | 5370 | } |
@@ -5303,13 +5375,13 @@ static struct pipe *parse_stream(char **pstring, | |||
5303 | if (!HAS_KEYWORDS | 5375 | if (!HAS_KEYWORDS |
5304 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | 5376 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
5305 | ) { | 5377 | ) { |
5306 | o_free(&ctx.word); | 5378 | o_free_and_set_NULL(&ctx.word); |
5307 | #if !BB_MMU | 5379 | #if !BB_MMU |
5308 | debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data); | 5380 | debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data); |
5309 | if (pstring) | 5381 | if (pstring) |
5310 | *pstring = ctx.as_string.data; | 5382 | *pstring = ctx.as_string.data; |
5311 | else | 5383 | else |
5312 | o_free_unsafe(&ctx.as_string); | 5384 | o_free(&ctx.as_string); |
5313 | #endif | 5385 | #endif |
5314 | if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) { | 5386 | if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) { |
5315 | /* Example: bare "{ }", "()" */ | 5387 | /* Example: bare "{ }", "()" */ |
@@ -5317,6 +5389,9 @@ static struct pipe *parse_stream(char **pstring, | |||
5317 | syntax_error_unexpected_ch(ch); | 5389 | syntax_error_unexpected_ch(ch); |
5318 | goto parse_error2; | 5390 | goto parse_error2; |
5319 | } | 5391 | } |
5392 | if (heredoc_cnt_ptr) | ||
5393 | *heredoc_cnt_ptr = heredoc_cnt; | ||
5394 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
5320 | debug_printf_parse("parse_stream return %p: " | 5395 | debug_printf_parse("parse_stream return %p: " |
5321 | "end_trigger char found\n", | 5396 | "end_trigger char found\n", |
5322 | ctx.list_head); | 5397 | ctx.list_head); |
@@ -5360,7 +5435,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5360 | if (next == '<') { | 5435 | if (next == '<') { |
5361 | redir_style = REDIRECT_HEREDOC; | 5436 | redir_style = REDIRECT_HEREDOC; |
5362 | heredoc_cnt++; | 5437 | heredoc_cnt++; |
5363 | debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); | 5438 | debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt); |
5364 | ch = i_getch(input); | 5439 | ch = i_getch(input); |
5365 | nommu_addchr(&ctx.as_string, ch); | 5440 | nommu_addchr(&ctx.as_string, ch); |
5366 | } else if (next == '>') { | 5441 | } else if (next == '>') { |
@@ -5444,7 +5519,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5444 | } | 5519 | } |
5445 | if (ctx.is_assignment == NOT_ASSIGNMENT) | 5520 | if (ctx.is_assignment == NOT_ASSIGNMENT) |
5446 | ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; | 5521 | ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; |
5447 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"', /*process_bkslash:*/ 1)) | 5522 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) |
5448 | goto parse_error; | 5523 | goto parse_error; |
5449 | ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; | 5524 | ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; |
5450 | continue; /* get next char */ | 5525 | continue; /* get next char */ |
@@ -5538,16 +5613,22 @@ static struct pipe *parse_stream(char **pstring, | |||
5538 | continue; /* get next char */ | 5613 | continue; /* get next char */ |
5539 | } | 5614 | } |
5540 | #endif | 5615 | #endif |
5541 | case '{': | 5616 | /* fall through */ |
5542 | if (parse_group(&ctx, input, ch) != 0) { | 5617 | case '{': { |
5618 | int n = parse_group(&ctx, input, ch); | ||
5619 | if (n < 0) { | ||
5543 | goto parse_error; | 5620 | goto parse_error; |
5544 | } | 5621 | } |
5622 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); | ||
5623 | heredoc_cnt += n; | ||
5545 | goto new_cmd; | 5624 | goto new_cmd; |
5625 | } | ||
5546 | case ')': | 5626 | case ')': |
5547 | #if ENABLE_HUSH_CASE | 5627 | #if ENABLE_HUSH_CASE |
5548 | if (ctx.ctx_res_w == RES_MATCH) | 5628 | if (ctx.ctx_res_w == RES_MATCH) |
5549 | goto case_semi; | 5629 | goto case_semi; |
5550 | #endif | 5630 | #endif |
5631 | |||
5551 | case '}': | 5632 | case '}': |
5552 | /* proper use of this character is caught by end_trigger: | 5633 | /* proper use of this character is caught by end_trigger: |
5553 | * if we see {, we call parse_group(..., end_trigger='}') | 5634 | * if we see {, we call parse_group(..., end_trigger='}') |
@@ -5587,7 +5668,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5587 | free_pipe_list(pctx->list_head); | 5668 | free_pipe_list(pctx->list_head); |
5588 | debug_printf_clean("freed list %p\n", pctx->list_head); | 5669 | debug_printf_clean("freed list %p\n", pctx->list_head); |
5589 | #if !BB_MMU | 5670 | #if !BB_MMU |
5590 | o_free_unsafe(&pctx->as_string); | 5671 | o_free(&pctx->as_string); |
5591 | #endif | 5672 | #endif |
5592 | IF_HAS_KEYWORDS(p2 = pctx->stack;) | 5673 | IF_HAS_KEYWORDS(p2 = pctx->stack;) |
5593 | if (pctx != &ctx) { | 5674 | if (pctx != &ctx) { |
@@ -5618,6 +5699,7 @@ static char *expand_string_to_string(const char *str, int EXP_flags, int do_unba | |||
5618 | #if ENABLE_HUSH_TICK | 5699 | #if ENABLE_HUSH_TICK |
5619 | static int process_command_subs(o_string *dest, const char *s); | 5700 | static int process_command_subs(o_string *dest, const char *s); |
5620 | #endif | 5701 | #endif |
5702 | static int expand_vars_to_list(o_string *output, int n, char *arg); | ||
5621 | 5703 | ||
5622 | /* expand_strvec_to_strvec() takes a list of strings, expands | 5704 | /* expand_strvec_to_strvec() takes a list of strings, expands |
5623 | * all variable references within and returns a pointer to | 5705 | * all variable references within and returns a pointer to |
@@ -5662,10 +5744,10 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
5662 | /* Store given string, finalizing the word and starting new one whenever | 5744 | /* Store given string, finalizing the word and starting new one whenever |
5663 | * we encounter IFS char(s). This is used for expanding variable values. | 5745 | * we encounter IFS char(s). This is used for expanding variable values. |
5664 | * End-of-string does NOT finalize word: think about 'echo -$VAR-'. | 5746 | * End-of-string does NOT finalize word: think about 'echo -$VAR-'. |
5665 | * Return in *ended_with_ifs: | 5747 | * Return in output->ended_in_ifs: |
5666 | * 1 - ended with IFS char, else 0 (this includes case of empty str). | 5748 | * 1 - ended with IFS char, else 0 (this includes case of empty str). |
5667 | */ | 5749 | */ |
5668 | static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str) | 5750 | static int expand_on_ifs(o_string *output, int n, const char *str) |
5669 | { | 5751 | { |
5670 | int last_is_ifs = 0; | 5752 | int last_is_ifs = 0; |
5671 | 5753 | ||
@@ -5728,8 +5810,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha | |||
5728 | } | 5810 | } |
5729 | } | 5811 | } |
5730 | 5812 | ||
5731 | if (ended_with_ifs) | 5813 | output->ended_in_ifs = last_is_ifs; |
5732 | *ended_with_ifs = last_is_ifs; | ||
5733 | debug_print_list("expand_on_ifs[1]", output, n); | 5814 | debug_print_list("expand_on_ifs[1]", output, n); |
5734 | return n; | 5815 | return n; |
5735 | } | 5816 | } |
@@ -5741,45 +5822,288 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha | |||
5741 | * Returns malloced string. | 5822 | * Returns malloced string. |
5742 | * As an optimization, we return NULL if expansion is not needed. | 5823 | * As an optimization, we return NULL if expansion is not needed. |
5743 | */ | 5824 | */ |
5744 | #if !BASH_PATTERN_SUBST | 5825 | static char *encode_then_expand_string(const char *str) |
5745 | /* only ${var/pattern/repl} (its pattern part) needs additional mode */ | ||
5746 | #define encode_then_expand_string(str, process_bkslash, do_unbackslash) \ | ||
5747 | encode_then_expand_string(str) | ||
5748 | #endif | ||
5749 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) | ||
5750 | { | 5826 | { |
5751 | #if !BASH_PATTERN_SUBST | ||
5752 | enum { do_unbackslash = 1 }; | ||
5753 | #endif | ||
5754 | char *exp_str; | 5827 | char *exp_str; |
5755 | struct in_str input; | 5828 | struct in_str input; |
5756 | o_string dest = NULL_O_STRING; | 5829 | o_string dest = NULL_O_STRING; |
5830 | const char *cp; | ||
5757 | 5831 | ||
5758 | if (!strchr(str, '$') | 5832 | cp = str; |
5759 | && !strchr(str, '\\') | 5833 | for (;;) { |
5834 | if (!*cp) return NULL; /* string has no special chars */ | ||
5835 | if (*cp == '$') break; | ||
5836 | if (*cp == '\\') break; | ||
5760 | #if ENABLE_HUSH_TICK | 5837 | #if ENABLE_HUSH_TICK |
5761 | && !strchr(str, '`') | 5838 | if (*cp == '`') break; |
5762 | #endif | 5839 | #endif |
5763 | ) { | 5840 | cp++; |
5764 | return NULL; | ||
5765 | } | 5841 | } |
5766 | 5842 | ||
5767 | /* We need to expand. Example: | 5843 | /* We need to expand. Example: |
5768 | * echo $(($a + `echo 1`)) $((1 + $((2)) )) | 5844 | * echo $(($a + `echo 1`)) $((1 + $((2)) )) |
5769 | */ | 5845 | */ |
5770 | setup_string_in_str(&input, str); | 5846 | setup_string_in_str(&input, str); |
5771 | encode_string(NULL, &dest, &input, EOF, process_bkslash); | 5847 | encode_string(NULL, &dest, &input, EOF); |
5772 | //TODO: error check (encode_string returns 0 on error)? | 5848 | //TODO: error check (encode_string returns 0 on error)? |
5773 | //bb_error_msg("'%s' -> '%s'", str, dest.data); | 5849 | //bb_error_msg("'%s' -> '%s'", str, dest.data); |
5774 | exp_str = expand_string_to_string(dest.data, | 5850 | exp_str = expand_string_to_string(dest.data, |
5851 | EXP_FLAG_ESC_GLOB_CHARS, | ||
5852 | /*unbackslash:*/ 1 | ||
5853 | ); | ||
5854 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); | ||
5855 | o_free(&dest); | ||
5856 | return exp_str; | ||
5857 | } | ||
5858 | |||
5859 | /* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}. | ||
5860 | * These can contain single- and double-quoted strings, | ||
5861 | * and treated as if the ARG string is initially unquoted. IOW: | ||
5862 | * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be | ||
5863 | * a dquoted string: "${var#"zz"}"), the difference only comes later | ||
5864 | * (word splitting and globbing of the ${var...} result). | ||
5865 | */ | ||
5866 | #if !BASH_PATTERN_SUBST | ||
5867 | #define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \ | ||
5868 | encode_then_expand_vararg(str, handle_squotes) | ||
5869 | #endif | ||
5870 | static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash) | ||
5871 | { | ||
5872 | #if !BASH_PATTERN_SUBST | ||
5873 | const int do_unbackslash = 0; | ||
5874 | #endif | ||
5875 | char *exp_str; | ||
5876 | struct in_str input; | ||
5877 | o_string dest = NULL_O_STRING; | ||
5878 | const char *cp; | ||
5879 | |||
5880 | cp = str; | ||
5881 | for (;;) { | ||
5882 | if (!*cp) return NULL; /* string has no special chars */ | ||
5883 | if (*cp == '$') break; | ||
5884 | if (*cp == '\\') break; | ||
5885 | if (*cp == '\'') break; | ||
5886 | if (*cp == '"') break; | ||
5887 | #if ENABLE_HUSH_TICK | ||
5888 | if (*cp == '`') break; | ||
5889 | #endif | ||
5890 | cp++; | ||
5891 | } | ||
5892 | |||
5893 | setup_string_in_str(&input, str); | ||
5894 | dest.data = xzalloc(1); /* start as "", not as NULL */ | ||
5895 | exp_str = NULL; | ||
5896 | |||
5897 | for (;;) { | ||
5898 | int ch; | ||
5899 | |||
5900 | ch = i_getch(&input); | ||
5901 | debug_printf_parse("%s: ch=%c (%d) escape=%d\n", | ||
5902 | __func__, ch, ch, !!dest.o_expflags); | ||
5903 | |||
5904 | if (!dest.o_expflags) { | ||
5905 | if (ch == EOF) | ||
5906 | break; | ||
5907 | if (handle_squotes && ch == '\'') { | ||
5908 | if (!add_till_single_quote_dquoted(&dest, &input)) | ||
5909 | goto ret; /* error */ | ||
5910 | continue; | ||
5911 | } | ||
5912 | } | ||
5913 | if (ch == EOF) { | ||
5914 | syntax_error_unterm_ch('"'); | ||
5915 | goto ret; /* error */ | ||
5916 | } | ||
5917 | if (ch == '"') { | ||
5918 | dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; | ||
5919 | continue; | ||
5920 | } | ||
5921 | if (ch == '\\') { | ||
5922 | ch = i_getch(&input); | ||
5923 | if (ch == EOF) { | ||
5924 | //example? error message? syntax_error_unterm_ch('"'); | ||
5925 | debug_printf_parse("%s: error: \\<eof>\n", __func__); | ||
5926 | goto ret; | ||
5927 | } | ||
5928 | o_addqchr(&dest, ch); | ||
5929 | continue; | ||
5930 | } | ||
5931 | if (ch == '$') { | ||
5932 | if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) { | ||
5933 | debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); | ||
5934 | goto ret; | ||
5935 | } | ||
5936 | continue; | ||
5937 | } | ||
5938 | #if ENABLE_HUSH_TICK | ||
5939 | if (ch == '`') { | ||
5940 | //unsigned pos = dest->length; | ||
5941 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5942 | o_addchr(&dest, 0x80 | '`'); | ||
5943 | if (!add_till_backquote(&dest, &input, | ||
5944 | /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ | ||
5945 | ) | ||
5946 | ) { | ||
5947 | goto ret; /* error */ | ||
5948 | } | ||
5949 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
5950 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | ||
5951 | continue; | ||
5952 | } | ||
5953 | #endif | ||
5954 | o_addQchr(&dest, ch); | ||
5955 | } /* for (;;) */ | ||
5956 | |||
5957 | debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data); | ||
5958 | exp_str = expand_string_to_string(dest.data, | ||
5775 | do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, | 5959 | do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0, |
5776 | do_unbackslash | 5960 | do_unbackslash |
5777 | ); | 5961 | ); |
5778 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); | 5962 | ret: |
5779 | o_free_unsafe(&dest); | 5963 | debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str); |
5964 | o_free(&dest); | ||
5780 | return exp_str; | 5965 | return exp_str; |
5781 | } | 5966 | } |
5782 | 5967 | ||
5968 | /* Expanding ARG in ${var+ARG}, ${var-ARG} | ||
5969 | */ | ||
5970 | static int encode_then_append_var_plusminus(o_string *output, int n, | ||
5971 | const char *str, int dquoted) | ||
5972 | { | ||
5973 | struct in_str input; | ||
5974 | o_string dest = NULL_O_STRING; | ||
5975 | |||
5976 | #if 0 //todo? | ||
5977 | const char *cp; | ||
5978 | cp = str; | ||
5979 | for (;;) { | ||
5980 | if (!*cp) return NULL; /* string has no special chars */ | ||
5981 | if (*cp == '$') break; | ||
5982 | if (*cp == '\\') break; | ||
5983 | if (*cp == '\'') break; | ||
5984 | if (*cp == '"') break; | ||
5985 | #if ENABLE_HUSH_TICK | ||
5986 | if (*cp == '`') break; | ||
5987 | #endif | ||
5988 | cp++; | ||
5989 | } | ||
5990 | #endif | ||
5991 | |||
5992 | setup_string_in_str(&input, str); | ||
5993 | |||
5994 | for (;;) { | ||
5995 | int ch; | ||
5996 | |||
5997 | ch = i_getch(&input); | ||
5998 | debug_printf_parse("%s: ch=%c (%d) escape=%x\n", | ||
5999 | __func__, ch, ch, dest.o_expflags); | ||
6000 | |||
6001 | if (!dest.o_expflags) { | ||
6002 | if (ch == EOF) | ||
6003 | break; | ||
6004 | if (!dquoted && strchr(G.ifs, ch)) { | ||
6005 | /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word. | ||
6006 | * do not assume we are at the start of the word (PREFIX above). | ||
6007 | */ | ||
6008 | if (dest.data) { | ||
6009 | n = expand_vars_to_list(output, n, dest.data); | ||
6010 | o_free_and_set_NULL(&dest); | ||
6011 | o_addchr(output, '\0'); | ||
6012 | n = o_save_ptr(output, n); /* create next word */ | ||
6013 | } else | ||
6014 | if (output->length != o_get_last_ptr(output, n) | ||
6015 | || output->has_quoted_part | ||
6016 | ) { | ||
6017 | /* For these cases: | ||
6018 | * f() { for i; do echo "|$i|"; done; }; x=x | ||
6019 | * f a${x:+ }b # 1st condition | ||
6020 | * |a| | ||
6021 | * |b| | ||
6022 | * f ""${x:+ }b # 2nd condition | ||
6023 | * || | ||
6024 | * |b| | ||
6025 | */ | ||
6026 | o_addchr(output, '\0'); | ||
6027 | n = o_save_ptr(output, n); /* create next word */ | ||
6028 | } | ||
6029 | continue; | ||
6030 | } | ||
6031 | if (!dquoted && ch == '\'') { | ||
6032 | if (!add_till_single_quote_dquoted(&dest, &input)) | ||
6033 | goto ret; /* error */ | ||
6034 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6035 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6036 | continue; | ||
6037 | } | ||
6038 | } | ||
6039 | if (ch == EOF) { | ||
6040 | syntax_error_unterm_ch('"'); | ||
6041 | goto ret; /* error */ | ||
6042 | } | ||
6043 | if (ch == '"') { | ||
6044 | dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS; | ||
6045 | if (dest.o_expflags) { | ||
6046 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6047 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6048 | } | ||
6049 | continue; | ||
6050 | } | ||
6051 | if (ch == '\\') { | ||
6052 | ch = i_getch(&input); | ||
6053 | if (ch == EOF) { | ||
6054 | //example? error message? syntax_error_unterm_ch('"'); | ||
6055 | debug_printf_parse("%s: error: \\<eof>\n", __func__); | ||
6056 | goto ret; | ||
6057 | } | ||
6058 | o_addqchr(&dest, ch); | ||
6059 | continue; | ||
6060 | } | ||
6061 | if (ch == '$') { | ||
6062 | if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) { | ||
6063 | debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__); | ||
6064 | goto ret; | ||
6065 | } | ||
6066 | continue; | ||
6067 | } | ||
6068 | #if ENABLE_HUSH_TICK | ||
6069 | if (ch == '`') { | ||
6070 | //unsigned pos = dest->length; | ||
6071 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6072 | o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`'); | ||
6073 | if (!add_till_backquote(&dest, &input, | ||
6074 | /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */ | ||
6075 | ) | ||
6076 | ) { | ||
6077 | goto ret; /* error */ | ||
6078 | } | ||
6079 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
6080 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | ||
6081 | continue; | ||
6082 | } | ||
6083 | #endif | ||
6084 | if (dquoted) { | ||
6085 | /* Always glob-protect if in dquotes: | ||
6086 | * x=x; echo "${x:+/bin/c*}" - prints: /bin/c* | ||
6087 | * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c* | ||
6088 | */ | ||
6089 | o_addqchr(&dest, ch); | ||
6090 | } else { | ||
6091 | /* Glob-protect only if char is quoted: | ||
6092 | * x=x; echo ${x:+/bin/c*} - prints many filenames | ||
6093 | * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c* | ||
6094 | */ | ||
6095 | o_addQchr(&dest, ch); | ||
6096 | } | ||
6097 | } /* for (;;) */ | ||
6098 | |||
6099 | if (dest.data) { | ||
6100 | n = expand_vars_to_list(output, n, dest.data); | ||
6101 | } | ||
6102 | ret: | ||
6103 | o_free(&dest); | ||
6104 | return n; | ||
6105 | } | ||
6106 | |||
5783 | #if ENABLE_FEATURE_SH_MATH | 6107 | #if ENABLE_FEATURE_SH_MATH |
5784 | static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) | 6108 | static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) |
5785 | { | 6109 | { |
@@ -5790,7 +6114,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) | |||
5790 | math_state.lookupvar = get_local_var_value; | 6114 | math_state.lookupvar = get_local_var_value; |
5791 | math_state.setvar = set_local_var_from_halves; | 6115 | math_state.setvar = set_local_var_from_halves; |
5792 | //math_state.endofname = endofname; | 6116 | //math_state.endofname = endofname; |
5793 | exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); | 6117 | exp_str = encode_then_expand_string(arg); |
5794 | res = arith(&math_state, exp_str ? exp_str : arg); | 6118 | res = arith(&math_state, exp_str ? exp_str : arg); |
5795 | free(exp_str); | 6119 | free(exp_str); |
5796 | if (errmsg_p) | 6120 | if (errmsg_p) |
@@ -5857,16 +6181,33 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
5857 | } | 6181 | } |
5858 | #endif /* BASH_PATTERN_SUBST */ | 6182 | #endif /* BASH_PATTERN_SUBST */ |
5859 | 6183 | ||
5860 | /* Helper: | 6184 | static int append_str_maybe_ifs_split(o_string *output, int n, |
5861 | * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. | 6185 | int first_ch, const char *val) |
6186 | { | ||
6187 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | ||
6188 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, | ||
6189 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | ||
6190 | if (val && val[0]) | ||
6191 | n = expand_on_ifs(output, n, val); | ||
6192 | } else { /* quoted "$VAR" */ | ||
6193 | output->has_quoted_part = 1; | ||
6194 | debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, | ||
6195 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | ||
6196 | if (val && val[0]) | ||
6197 | o_addQstr(output, val); | ||
6198 | } | ||
6199 | return n; | ||
6200 | } | ||
6201 | |||
6202 | /* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. | ||
5862 | */ | 6203 | */ |
5863 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) | 6204 | static NOINLINE int expand_one_var(o_string *output, int n, |
6205 | int first_ch, char *arg, char **pp) | ||
5864 | { | 6206 | { |
5865 | const char *val; | 6207 | const char *val; |
5866 | char *to_be_freed; | 6208 | char *to_be_freed; |
5867 | char *p; | 6209 | char *p; |
5868 | char *var; | 6210 | char *var; |
5869 | char first_char; | ||
5870 | char exp_op; | 6211 | char exp_op; |
5871 | char exp_save = exp_save; /* for compiler */ | 6212 | char exp_save = exp_save; /* for compiler */ |
5872 | char *exp_saveptr; /* points to expansion operator */ | 6213 | char *exp_saveptr; /* points to expansion operator */ |
@@ -5880,10 +6221,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5880 | var = arg; | 6221 | var = arg; |
5881 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | 6222 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; |
5882 | arg0 = arg[0]; | 6223 | arg0 = arg[0]; |
5883 | first_char = arg[0] = arg0 & 0x7f; | 6224 | arg[0] = (arg0 & 0x7f); |
5884 | exp_op = 0; | 6225 | exp_op = 0; |
5885 | 6226 | ||
5886 | if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ | 6227 | if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */ |
5887 | && (!exp_saveptr /* and ( not(${#<op_char>...}) */ | 6228 | && (!exp_saveptr /* and ( not(${#<op_char>...}) */ |
5888 | || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ | 6229 | || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ |
5889 | ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ | 6230 | ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ |
@@ -5894,7 +6235,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5894 | } else { | 6235 | } else { |
5895 | /* Maybe handle parameter expansion */ | 6236 | /* Maybe handle parameter expansion */ |
5896 | if (exp_saveptr /* if 2nd char is one of expansion operators */ | 6237 | if (exp_saveptr /* if 2nd char is one of expansion operators */ |
5897 | && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ | 6238 | && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */ |
5898 | ) { | 6239 | ) { |
5899 | /* ${?:0}, ${#[:]%0} etc */ | 6240 | /* ${?:0}, ${#[:]%0} etc */ |
5900 | exp_saveptr = var + 1; | 6241 | exp_saveptr = var + 1; |
@@ -5923,9 +6264,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5923 | /* Look up the variable in question */ | 6264 | /* Look up the variable in question */ |
5924 | if (isdigit(var[0])) { | 6265 | if (isdigit(var[0])) { |
5925 | /* parse_dollar should have vetted var for us */ | 6266 | /* parse_dollar should have vetted var for us */ |
5926 | int n = xatoi_positive(var); | 6267 | int nn = xatoi_positive(var); |
5927 | if (n < G.global_argc) | 6268 | if (nn < G.global_argc) |
5928 | val = G.global_argv[n]; | 6269 | val = G.global_argv[nn]; |
5929 | /* else val remains NULL: $N with too big N */ | 6270 | /* else val remains NULL: $N with too big N */ |
5930 | } else { | 6271 | } else { |
5931 | switch (var[0]) { | 6272 | switch (var[0]) { |
@@ -5963,6 +6304,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5963 | * Word is expanded to produce a glob pattern. | 6304 | * Word is expanded to produce a glob pattern. |
5964 | * Then var's value is matched to it and matching part removed. | 6305 | * Then var's value is matched to it and matching part removed. |
5965 | */ | 6306 | */ |
6307 | //FIXME: ${x#...${...}...} | ||
6308 | //should evaluate inner ${...} even if x is "" and no shrinking of it is possible - | ||
6309 | //inner ${...} may have side effects! | ||
5966 | if (val && val[0]) { | 6310 | if (val && val[0]) { |
5967 | char *t; | 6311 | char *t; |
5968 | char *exp_exp_word; | 6312 | char *exp_exp_word; |
@@ -5971,20 +6315,16 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5971 | if (exp_op == *exp_word) /* ## or %% */ | 6315 | if (exp_op == *exp_word) /* ## or %% */ |
5972 | exp_word++; | 6316 | exp_word++; |
5973 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); | 6317 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); |
5974 | /* | 6318 | exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); |
5975 | * process_bkslash:1 unbackslash:1 breaks this: | ||
5976 | * a='a\\'; echo ${a%\\\\} # correct output is: a | ||
5977 | * process_bkslash:1 unbackslash:0 breaks this: | ||
5978 | * a='a}'; echo ${a%\}} # correct output is: a | ||
5979 | */ | ||
5980 | exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0); | ||
5981 | if (exp_exp_word) | 6319 | if (exp_exp_word) |
5982 | exp_word = exp_exp_word; | 6320 | exp_word = exp_exp_word; |
5983 | debug_printf_expand("expand: exp_exp_word:'%s'\n", exp_word); | 6321 | debug_printf_expand("expand: exp_word:'%s'\n", exp_word); |
5984 | /* HACK ALERT. We depend here on the fact that | 6322 | /* |
6323 | * HACK ALERT. We depend here on the fact that | ||
5985 | * G.global_argv and results of utoa and get_local_var_value | 6324 | * G.global_argv and results of utoa and get_local_var_value |
5986 | * are actually in writable memory: | 6325 | * are actually in writable memory: |
5987 | * scan_and_match momentarily stores NULs there. */ | 6326 | * scan_and_match momentarily stores NULs there. |
6327 | */ | ||
5988 | t = (char*)val; | 6328 | t = (char*)val; |
5989 | loc = scan_and_match(t, exp_word, scan_flags); | 6329 | loc = scan_and_match(t, exp_word, scan_flags); |
5990 | debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc); | 6330 | debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc); |
@@ -6017,7 +6357,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
6017 | * (note that a*z _pattern_ is never globbed!) | 6357 | * (note that a*z _pattern_ is never globbed!) |
6018 | */ | 6358 | */ |
6019 | char *pattern, *repl, *t; | 6359 | char *pattern, *repl, *t; |
6020 | pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0); | 6360 | pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0); |
6021 | if (!pattern) | 6361 | if (!pattern) |
6022 | pattern = xstrdup(exp_word); | 6362 | pattern = xstrdup(exp_word); |
6023 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); | 6363 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); |
@@ -6025,7 +6365,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
6025 | exp_word = p; | 6365 | exp_word = p; |
6026 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 6366 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
6027 | *p = '\0'; | 6367 | *p = '\0'; |
6028 | repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 1); | 6368 | repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1); |
6029 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); | 6369 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); |
6030 | /* HACK ALERT. We depend here on the fact that | 6370 | /* HACK ALERT. We depend here on the fact that |
6031 | * G.global_argv and results of utoa and get_local_var_value | 6371 | * G.global_argv and results of utoa and get_local_var_value |
@@ -6121,6 +6461,34 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
6121 | * | 6461 | * |
6122 | * Colon forms (${var:-word}, ${var:=word} etc) do the same, | 6462 | * Colon forms (${var:-word}, ${var:=word} etc) do the same, |
6123 | * but also treat null var as if it is unset. | 6463 | * but also treat null var as if it is unset. |
6464 | * | ||
6465 | * Word-splitting and single quote behavior: | ||
6466 | * | ||
6467 | * $ f() { for i; do echo "|$i|"; done; }; | ||
6468 | * | ||
6469 | * $ x=; f ${x:?'x y' z} | ||
6470 | * bash: x: x y z #BUG: does not abort, ${} results in empty expansion | ||
6471 | * $ x=; f "${x:?'x y' z}" | ||
6472 | * bash: x: x y z # dash prints: dash: x: 'x y' z #BUG: does not abort, ${} results in "" | ||
6473 | * | ||
6474 | * $ x=; f ${x:='x y' z} | ||
6475 | * |x| | ||
6476 | * |y| | ||
6477 | * |z| | ||
6478 | * $ x=; f "${x:='x y' z}" | ||
6479 | * |'x y' z| | ||
6480 | * | ||
6481 | * $ x=x; f ${x:+'x y' z} | ||
6482 | * |x y| | ||
6483 | * |z| | ||
6484 | * $ x=x; f "${x:+'x y' z}" | ||
6485 | * |'x y' z| | ||
6486 | * | ||
6487 | * $ x=; f ${x:-'x y' z} | ||
6488 | * |x y| | ||
6489 | * |z| | ||
6490 | * $ x=; f "${x:-'x y' z}" | ||
6491 | * |'x y' z| | ||
6124 | */ | 6492 | */ |
6125 | int use_word = (!val || ((exp_save == ':') && !val[0])); | 6493 | int use_word = (!val || ((exp_save == ':') && !val[0])); |
6126 | if (exp_op == '+') | 6494 | if (exp_op == '+') |
@@ -6128,33 +6496,51 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
6128 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | 6496 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, |
6129 | (exp_save == ':') ? "true" : "false", use_word); | 6497 | (exp_save == ':') ? "true" : "false", use_word); |
6130 | if (use_word) { | 6498 | if (use_word) { |
6131 | to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); | 6499 | if (exp_op == '+' || exp_op == '-') { |
6132 | if (to_be_freed) | 6500 | /* ${var+word} - use alternative value */ |
6133 | exp_word = to_be_freed; | 6501 | /* ${var-word} - use default value */ |
6134 | if (exp_op == '?') { | 6502 | n = encode_then_append_var_plusminus(output, n, exp_word, |
6135 | /* mimic bash message */ | 6503 | /*dquoted:*/ (arg0 & 0x80) |
6136 | msg_and_die_if_script("%s: %s", | ||
6137 | var, | ||
6138 | exp_word[0] | ||
6139 | ? exp_word | ||
6140 | : "parameter null or not set" | ||
6141 | /* ash has more specific messages, a-la: */ | ||
6142 | /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/ | ||
6143 | ); | 6504 | ); |
6144 | //TODO: how interactive bash aborts expansion mid-command? | 6505 | val = NULL; |
6145 | } else { | 6506 | } else { |
6146 | val = exp_word; | 6507 | /* ${var?word} - indicate error if unset */ |
6147 | } | 6508 | /* ${var=word} - assign and use default value */ |
6148 | 6509 | to_be_freed = encode_then_expand_vararg(exp_word, | |
6149 | if (exp_op == '=') { | 6510 | /*handle_squotes:*/ !(arg0 & 0x80), |
6150 | /* ${var=[word]} or ${var:=[word]} */ | 6511 | /*unbackslash:*/ 0 |
6151 | if (isdigit(var[0]) || var[0] == '#') { | 6512 | ); |
6513 | if (to_be_freed) | ||
6514 | exp_word = to_be_freed; | ||
6515 | if (exp_op == '?') { | ||
6152 | /* mimic bash message */ | 6516 | /* mimic bash message */ |
6153 | msg_and_die_if_script("$%s: cannot assign in this way", var); | 6517 | msg_and_die_if_script("%s: %s", |
6154 | val = NULL; | 6518 | var, |
6519 | exp_word[0] | ||
6520 | ? exp_word | ||
6521 | : "parameter null or not set" | ||
6522 | /* ash has more specific messages, a-la: */ | ||
6523 | /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/ | ||
6524 | ); | ||
6525 | //TODO: how interactive bash aborts expansion mid-command? | ||
6526 | //It aborts the entire line, returns to prompt: | ||
6527 | // $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO | ||
6528 | // bash: x: x y z | ||
6529 | // $ | ||
6530 | // ("echo YO" is not executed, neither the f function call) | ||
6155 | } else { | 6531 | } else { |
6156 | char *new_var = xasprintf("%s=%s", var, val); | 6532 | val = exp_word; |
6157 | set_local_var(new_var, /*flag:*/ 0); | 6533 | } |
6534 | if (exp_op == '=') { | ||
6535 | /* ${var=[word]} or ${var:=[word]} */ | ||
6536 | if (isdigit(var[0]) || var[0] == '#') { | ||
6537 | /* mimic bash message */ | ||
6538 | msg_and_die_if_script("$%s: cannot assign in this way", var); | ||
6539 | val = NULL; | ||
6540 | } else { | ||
6541 | char *new_var = xasprintf("%s=%s", var, val); | ||
6542 | set_local_var(new_var, /*flag:*/ 0); | ||
6543 | } | ||
6158 | } | 6544 | } |
6159 | } | 6545 | } |
6160 | } | 6546 | } |
@@ -6164,10 +6550,12 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
6164 | } /* if (exp_op) */ | 6550 | } /* if (exp_op) */ |
6165 | 6551 | ||
6166 | arg[0] = arg0; | 6552 | arg[0] = arg0; |
6167 | |||
6168 | *pp = p; | 6553 | *pp = p; |
6169 | *to_be_freed_pp = to_be_freed; | 6554 | |
6170 | return val; | 6555 | n = append_str_maybe_ifs_split(output, n, first_ch, val); |
6556 | |||
6557 | free(to_be_freed); | ||
6558 | return n; | ||
6171 | } | 6559 | } |
6172 | 6560 | ||
6173 | /* Expand all variable references in given string, adding words to list[] | 6561 | /* Expand all variable references in given string, adding words to list[] |
@@ -6181,30 +6569,22 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6181 | * expansion of right-hand side of assignment == 1-element expand. | 6569 | * expansion of right-hand side of assignment == 1-element expand. |
6182 | */ | 6570 | */ |
6183 | char cant_be_null = 0; /* only bit 0x80 matters */ | 6571 | char cant_be_null = 0; /* only bit 0x80 matters */ |
6184 | int ended_in_ifs = 0; /* did last unquoted expansion end with IFS chars? */ | ||
6185 | char *p; | 6572 | char *p; |
6186 | 6573 | ||
6187 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, | 6574 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, |
6188 | !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); | 6575 | !!(output->o_expflags & EXP_FLAG_SINGLEWORD)); |
6189 | debug_print_list("expand_vars_to_list", output, n); | ||
6190 | n = o_save_ptr(output, n); | ||
6191 | debug_print_list("expand_vars_to_list[0]", output, n); | 6576 | debug_print_list("expand_vars_to_list[0]", output, n); |
6192 | 6577 | ||
6193 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | 6578 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { |
6194 | char first_ch; | 6579 | char first_ch; |
6195 | char *to_be_freed = NULL; | ||
6196 | const char *val = NULL; | ||
6197 | #if ENABLE_HUSH_TICK | ||
6198 | o_string subst_result = NULL_O_STRING; | ||
6199 | #endif | ||
6200 | #if ENABLE_FEATURE_SH_MATH | 6580 | #if ENABLE_FEATURE_SH_MATH |
6201 | char arith_buf[sizeof(arith_t)*3 + 2]; | 6581 | char arith_buf[sizeof(arith_t)*3 + 2]; |
6202 | #endif | 6582 | #endif |
6203 | 6583 | ||
6204 | if (ended_in_ifs) { | 6584 | if (output->ended_in_ifs) { |
6205 | o_addchr(output, '\0'); | 6585 | o_addchr(output, '\0'); |
6206 | n = o_save_ptr(output, n); | 6586 | n = o_save_ptr(output, n); |
6207 | ended_in_ifs = 0; | 6587 | output->ended_in_ifs = 0; |
6208 | } | 6588 | } |
6209 | 6589 | ||
6210 | o_addblock(output, arg, p - arg); | 6590 | o_addblock(output, arg, p - arg); |
@@ -6235,7 +6615,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6235 | cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ | 6615 | cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ |
6236 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | 6616 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
6237 | while (G.global_argv[i]) { | 6617 | while (G.global_argv[i]) { |
6238 | n = expand_on_ifs(NULL, output, n, G.global_argv[i]); | 6618 | n = expand_on_ifs(output, n, G.global_argv[i]); |
6239 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); | 6619 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); |
6240 | if (G.global_argv[i++][0] && G.global_argv[i]) { | 6620 | if (G.global_argv[i++][0] && G.global_argv[i]) { |
6241 | /* this argv[] is not empty and not last: | 6621 | /* this argv[] is not empty and not last: |
@@ -6272,19 +6652,25 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6272 | } | 6652 | } |
6273 | break; | 6653 | break; |
6274 | } | 6654 | } |
6275 | case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ | 6655 | case SPECIAL_VAR_SYMBOL: { |
6656 | /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ | ||
6276 | /* "Empty variable", used to make "" etc to not disappear */ | 6657 | /* "Empty variable", used to make "" etc to not disappear */ |
6277 | output->has_quoted_part = 1; | 6658 | output->has_quoted_part = 1; |
6278 | arg++; | ||
6279 | cant_be_null = 0x80; | 6659 | cant_be_null = 0x80; |
6660 | arg++; | ||
6280 | break; | 6661 | break; |
6662 | } | ||
6281 | case SPECIAL_VAR_QUOTED_SVS: | 6663 | case SPECIAL_VAR_QUOTED_SVS: |
6282 | /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */ | 6664 | /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */ |
6665 | /* "^C variable", represents literal ^C char (possible in scripts) */ | ||
6666 | o_addchr(output, SPECIAL_VAR_SYMBOL); | ||
6283 | arg++; | 6667 | arg++; |
6284 | val = SPECIAL_VAR_SYMBOL_STR; | ||
6285 | break; | 6668 | break; |
6286 | #if ENABLE_HUSH_TICK | 6669 | #if ENABLE_HUSH_TICK |
6287 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 6670 | case '`': { |
6671 | /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | ||
6672 | o_string subst_result = NULL_O_STRING; | ||
6673 | |||
6288 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 6674 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
6289 | arg++; | 6675 | arg++; |
6290 | /* Can't just stuff it into output o_string, | 6676 | /* Can't just stuff it into output o_string, |
@@ -6294,11 +6680,14 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6294 | G.last_exitcode = process_command_subs(&subst_result, arg); | 6680 | G.last_exitcode = process_command_subs(&subst_result, arg); |
6295 | G.expand_exitcode = G.last_exitcode; | 6681 | G.expand_exitcode = G.last_exitcode; |
6296 | debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); | 6682 | debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); |
6297 | val = subst_result.data; | 6683 | n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data); |
6298 | goto store_val; | 6684 | o_free(&subst_result); |
6685 | break; | ||
6686 | } | ||
6299 | #endif | 6687 | #endif |
6300 | #if ENABLE_FEATURE_SH_MATH | 6688 | #if ENABLE_FEATURE_SH_MATH |
6301 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ | 6689 | case '+': { |
6690 | /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */ | ||
6302 | arith_t res; | 6691 | arith_t res; |
6303 | 6692 | ||
6304 | arg++; /* skip '+' */ | 6693 | arg++; /* skip '+' */ |
@@ -6307,62 +6696,43 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6307 | res = expand_and_evaluate_arith(arg, NULL); | 6696 | res = expand_and_evaluate_arith(arg, NULL); |
6308 | debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); | 6697 | debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); |
6309 | sprintf(arith_buf, ARITH_FMT, res); | 6698 | sprintf(arith_buf, ARITH_FMT, res); |
6310 | val = arith_buf; | 6699 | o_addstr(output, arith_buf); |
6311 | break; | 6700 | break; |
6312 | } | 6701 | } |
6313 | #endif | 6702 | #endif |
6314 | default: | 6703 | default: |
6315 | val = expand_one_var(&to_be_freed, arg, &p); | 6704 | /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */ |
6316 | IF_HUSH_TICK(store_val:) | 6705 | n = expand_one_var(output, n, first_ch, arg, &p); |
6317 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | ||
6318 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, | ||
6319 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | ||
6320 | if (val && val[0]) { | ||
6321 | n = expand_on_ifs(&ended_in_ifs, output, n, val); | ||
6322 | val = NULL; | ||
6323 | } | ||
6324 | } else { /* quoted $VAR, val will be appended below */ | ||
6325 | output->has_quoted_part = 1; | ||
6326 | debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, | ||
6327 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | ||
6328 | } | ||
6329 | break; | 6706 | break; |
6330 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ | 6707 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ |
6331 | 6708 | ||
6332 | if (val && val[0]) { | ||
6333 | o_addQstr(output, val); | ||
6334 | } | ||
6335 | free(to_be_freed); | ||
6336 | |||
6337 | /* Restore NULL'ed SPECIAL_VAR_SYMBOL. | 6709 | /* Restore NULL'ed SPECIAL_VAR_SYMBOL. |
6338 | * Do the check to avoid writing to a const string. */ | 6710 | * Do the check to avoid writing to a const string. */ |
6339 | if (*p != SPECIAL_VAR_SYMBOL) | 6711 | if (*p != SPECIAL_VAR_SYMBOL) |
6340 | *p = SPECIAL_VAR_SYMBOL; | 6712 | *p = SPECIAL_VAR_SYMBOL; |
6341 | |||
6342 | #if ENABLE_HUSH_TICK | ||
6343 | o_free(&subst_result); | ||
6344 | #endif | ||
6345 | arg = ++p; | 6713 | arg = ++p; |
6346 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ | 6714 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ |
6347 | 6715 | ||
6348 | if (arg[0]) { | 6716 | if (*arg) { |
6349 | if (ended_in_ifs) { | 6717 | /* handle trailing string */ |
6718 | if (output->ended_in_ifs) { | ||
6350 | o_addchr(output, '\0'); | 6719 | o_addchr(output, '\0'); |
6351 | n = o_save_ptr(output, n); | 6720 | n = o_save_ptr(output, n); |
6352 | } | 6721 | } |
6353 | debug_print_list("expand_vars_to_list[a]", output, n); | 6722 | debug_print_list("expand_vars_to_list[a]", output, n); |
6354 | /* this part is literal, and it was already pre-quoted | 6723 | /* this part is literal, and it was already pre-quoted |
6355 | * if needed (much earlier), do not use o_addQstr here! */ | 6724 | * if needed (much earlier), do not use o_addQstr here! |
6356 | o_addstr_with_NUL(output, arg); | 6725 | */ |
6726 | o_addstr(output, arg); | ||
6357 | debug_print_list("expand_vars_to_list[b]", output, n); | 6727 | debug_print_list("expand_vars_to_list[b]", output, n); |
6358 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ | 6728 | } else |
6359 | && !(cant_be_null & 0x80) /* and all vars were not quoted. */ | 6729 | if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ |
6730 | && !(cant_be_null & 0x80) /* and all vars were not quoted */ | ||
6731 | && !output->has_quoted_part | ||
6360 | ) { | 6732 | ) { |
6361 | n--; | 6733 | n--; |
6362 | /* allow to reuse list[n] later without re-growth */ | 6734 | /* allow to reuse list[n] later without re-growth */ |
6363 | output->has_empty_slot = 1; | 6735 | output->has_empty_slot = 1; |
6364 | } else { | ||
6365 | o_addchr(output, '\0'); | ||
6366 | } | 6736 | } |
6367 | 6737 | ||
6368 | return n; | 6738 | return n; |
@@ -6377,9 +6747,18 @@ static char **expand_variables(char **argv, unsigned expflags) | |||
6377 | output.o_expflags = expflags; | 6747 | output.o_expflags = expflags; |
6378 | 6748 | ||
6379 | n = 0; | 6749 | n = 0; |
6380 | while (*argv) { | 6750 | for (;;) { |
6381 | n = expand_vars_to_list(&output, n, *argv); | 6751 | /* go to next list[n] */ |
6382 | argv++; | 6752 | output.ended_in_ifs = 0; |
6753 | n = o_save_ptr(&output, n); | ||
6754 | |||
6755 | if (!*argv) | ||
6756 | break; | ||
6757 | |||
6758 | /* expand argv[i] */ | ||
6759 | n = expand_vars_to_list(&output, n, *argv++); | ||
6760 | /* if (!output->has_empty_slot) -- need this?? */ | ||
6761 | o_addchr(&output, '\0'); | ||
6383 | } | 6762 | } |
6384 | debug_print_list("expand_variables", &output, n); | 6763 | debug_print_list("expand_variables", &output, n); |
6385 | 6764 | ||
@@ -6429,13 +6808,20 @@ static char *expand_string_to_string(const char *str, int EXP_flags, int do_unba | |||
6429 | argv[0] = (char*)str; | 6808 | argv[0] = (char*)str; |
6430 | argv[1] = NULL; | 6809 | argv[1] = NULL; |
6431 | list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD); | 6810 | list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD); |
6432 | if (HUSH_DEBUG) | 6811 | if (!list[0]) { |
6433 | if (!list[0] || list[1]) | 6812 | /* Example where it happens: |
6434 | bb_error_msg_and_die("BUG in varexp2"); | 6813 | * x=; echo ${x:-"$@"} |
6435 | /* actually, just move string 2*sizeof(char*) bytes back */ | 6814 | */ |
6436 | overlapping_strcpy((char*)list, list[0]); | 6815 | ((char*)list)[0] = '\0'; |
6437 | if (do_unbackslash) | 6816 | } else { |
6438 | unbackslash((char*)list); | 6817 | if (HUSH_DEBUG) |
6818 | if (list[1]) | ||
6819 | bb_error_msg_and_die("BUG in varexp2"); | ||
6820 | /* actually, just move string 2*sizeof(char*) bytes back */ | ||
6821 | overlapping_strcpy((char*)list, list[0]); | ||
6822 | if (do_unbackslash) | ||
6823 | unbackslash((char*)list); | ||
6824 | } | ||
6439 | debug_printf_expand("string_to_string=>'%s'\n", (char*)list); | 6825 | debug_printf_expand("string_to_string=>'%s'\n", (char*)list); |
6440 | return (char*)list; | 6826 | return (char*)list; |
6441 | } | 6827 | } |
@@ -6722,7 +7108,7 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
6722 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 7108 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
6723 | } | 7109 | } |
6724 | #endif | 7110 | #endif |
6725 | pipe_list = parse_stream(NULL, inp, end_trigger); | 7111 | pipe_list = parse_stream(NULL, NULL, inp, end_trigger); |
6726 | if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ | 7112 | if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ |
6727 | /* If we are in "big" script | 7113 | /* If we are in "big" script |
6728 | * (not in `cmd` or something similar)... | 7114 | * (not in `cmd` or something similar)... |
@@ -6763,19 +7149,19 @@ static void parse_and_run_string(const char *s) | |||
6763 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7149 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) |
6764 | } | 7150 | } |
6765 | 7151 | ||
6766 | static void parse_and_run_file(FILE *f) | 7152 | static void parse_and_run_file(HFILE *fp) |
6767 | { | 7153 | { |
6768 | struct in_str input; | 7154 | struct in_str input; |
6769 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) | 7155 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) |
6770 | 7156 | ||
6771 | IF_HUSH_LINENO_VAR(G.lineno = 1;) | 7157 | IF_HUSH_LINENO_VAR(G.lineno = 1;) |
6772 | setup_file_in_str(&input, f); | 7158 | setup_file_in_str(&input, fp); |
6773 | parse_and_run_stream(&input, ';'); | 7159 | parse_and_run_stream(&input, ';'); |
6774 | IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7160 | IF_HUSH_LINENO_VAR(G.lineno = sv;) |
6775 | } | 7161 | } |
6776 | 7162 | ||
6777 | #if ENABLE_HUSH_TICK | 7163 | #if ENABLE_HUSH_TICK |
6778 | static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | 7164 | static int generate_stream_from_string(const char *s, pid_t *pid_p) |
6779 | { | 7165 | { |
6780 | pid_t pid; | 7166 | pid_t pid; |
6781 | int channel[2]; | 7167 | int channel[2]; |
@@ -6877,7 +7263,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
6877 | free(to_free); | 7263 | free(to_free); |
6878 | # endif | 7264 | # endif |
6879 | close(channel[1]); | 7265 | close(channel[1]); |
6880 | return remember_FILE(xfdopen_for_read(channel[0])); | 7266 | return channel[0]; |
6881 | } | 7267 | } |
6882 | 7268 | ||
6883 | /* Return code is exit status of the process that is run. */ | 7269 | /* Return code is exit status of the process that is run. */ |
@@ -6887,7 +7273,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
6887 | pid_t pid; | 7273 | pid_t pid; |
6888 | int status, ch, eol_cnt; | 7274 | int status, ch, eol_cnt; |
6889 | 7275 | ||
6890 | fp = generate_stream_from_string(s, &pid); | 7276 | fp = xfdopen_for_read(generate_stream_from_string(s, &pid)); |
6891 | 7277 | ||
6892 | /* Now send results of command back into original context */ | 7278 | /* Now send results of command back into original context */ |
6893 | eol_cnt = 0; | 7279 | eol_cnt = 0; |
@@ -6906,7 +7292,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
6906 | } | 7292 | } |
6907 | 7293 | ||
6908 | debug_printf("done reading from `cmd` pipe, closing it\n"); | 7294 | debug_printf("done reading from `cmd` pipe, closing it\n"); |
6909 | fclose_and_forget(fp); | 7295 | fclose(fp); |
6910 | /* We need to extract exitcode. Test case | 7296 | /* We need to extract exitcode. Test case |
6911 | * "true; echo `sleep 1; false` $?" | 7297 | * "true; echo `sleep 1; false` $?" |
6912 | * should print 1 */ | 7298 | * should print 1 */ |
@@ -6931,7 +7317,7 @@ static void setup_heredoc(struct redir_struct *redir) | |||
6931 | 7317 | ||
6932 | expanded = NULL; | 7318 | expanded = NULL; |
6933 | if (!(redir->rd_dup & HEREDOC_QUOTED)) { | 7319 | if (!(redir->rd_dup & HEREDOC_QUOTED)) { |
6934 | expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); | 7320 | expanded = encode_then_expand_string(heredoc); |
6935 | if (expanded) | 7321 | if (expanded) |
6936 | heredoc = expanded; | 7322 | heredoc = expanded; |
6937 | } | 7323 | } |
@@ -7084,19 +7470,54 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
7084 | return 1; /* "we closed fd" */ | 7470 | return 1; /* "we closed fd" */ |
7085 | } | 7471 | } |
7086 | #endif | 7472 | #endif |
7087 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: | 7473 | /* Are we called from setup_redirects(squirrel==NULL) |
7088 | * (1) Redirect in a forked child. No need to save FILEs' fds, | 7474 | * in redirect in a [v]forked child? |
7089 | * we aren't going to use them anymore, ok to trash. | ||
7090 | * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds, | ||
7091 | * but how are we doing to restore them? | ||
7092 | * "fileno(fd) = new_fd" can't be done. | ||
7093 | */ | 7475 | */ |
7094 | if (!sqp) | 7476 | if (sqp == NULL) { |
7477 | /* No need to move script fds. | ||
7478 | * For NOMMU case, it's actively wrong: we'd change ->fd | ||
7479 | * fields in memory for the parent, but parent's fds | ||
7480 | * aren't be moved, it would use wrong fd! | ||
7481 | * Reproducer: "cmd 3>FILE" in script. | ||
7482 | * If we would call move_HFILEs_on_redirect(), child would: | ||
7483 | * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10 | ||
7484 | * close(3) = 0 | ||
7485 | * and change ->fd to 10 if fd#3 is a script fd. WRONG. | ||
7486 | */ | ||
7487 | //bb_error_msg("sqp == NULL: [v]forked child"); | ||
7095 | return 0; | 7488 | return 0; |
7489 | } | ||
7096 | 7490 | ||
7097 | /* If this one of script's fds? */ | 7491 | /* If this one of script's fds? */ |
7098 | if (save_FILEs_on_redirect(fd, avoid_fd)) | 7492 | if (move_HFILEs_on_redirect(fd, avoid_fd)) |
7099 | return 1; /* yes. "we closed fd" */ | 7493 | return 1; /* yes. "we closed fd" (actually moved it) */ |
7494 | |||
7495 | /* Are we called for "exec 3>FILE"? Came through | ||
7496 | * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR) | ||
7497 | * This case used to fail for this script: | ||
7498 | * exec 3>FILE | ||
7499 | * echo Ok | ||
7500 | * ...100000 more lines... | ||
7501 | * echo Ok | ||
7502 | * as follows: | ||
7503 | * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024 | ||
7504 | * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4 | ||
7505 | * dup2(4, 3) = 3 | ||
7506 | * ^^^^^^^^ oops, we lost fd#3 opened to our script! | ||
7507 | * close(4) = 0 | ||
7508 | * write(1, "Ok\n", 3) = 3 | ||
7509 | * ... = 3 | ||
7510 | * write(1, "Ok\n", 3) = 3 | ||
7511 | * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor) | ||
7512 | * ^^^^^^^^ oops, wrong fd!!! | ||
7513 | * With this case separate from sqp == NULL and *after* move_HFILEs, | ||
7514 | * it now works: | ||
7515 | */ | ||
7516 | if (sqp == ERR_PTR) { | ||
7517 | /* Don't preserve redirected fds: exec is _meant_ to change these */ | ||
7518 | //bb_error_msg("sqp == ERR_PTR: exec >FILE"); | ||
7519 | return 0; | ||
7520 | } | ||
7100 | 7521 | ||
7101 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ | 7522 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ |
7102 | *sqp = add_squirrel(*sqp, fd, avoid_fd); | 7523 | *sqp = add_squirrel(*sqp, fd, avoid_fd); |
@@ -7122,8 +7543,6 @@ static void restore_redirects(struct squirrel *sq) | |||
7122 | } | 7543 | } |
7123 | 7544 | ||
7124 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ | 7545 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ |
7125 | |||
7126 | restore_redirected_FILEs(); | ||
7127 | } | 7546 | } |
7128 | 7547 | ||
7129 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 7548 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
@@ -7131,7 +7550,7 @@ static void close_saved_fds_and_FILE_fds(void) | |||
7131 | { | 7550 | { |
7132 | if (G_interactive_fd) | 7551 | if (G_interactive_fd) |
7133 | close(G_interactive_fd); | 7552 | close(G_interactive_fd); |
7134 | close_all_FILE_list(); | 7553 | close_all_HFILE_list(); |
7135 | } | 7554 | } |
7136 | #endif | 7555 | #endif |
7137 | 7556 | ||
@@ -7144,7 +7563,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq) | |||
7144 | return 1; | 7563 | return 1; |
7145 | #endif | 7564 | #endif |
7146 | /* If this one of script's fds? */ | 7565 | /* If this one of script's fds? */ |
7147 | if (fd_in_FILEs(fd)) | 7566 | if (fd_in_HFILEs(fd)) |
7148 | return 1; | 7567 | return 1; |
7149 | 7568 | ||
7150 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 7569 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { |
@@ -7169,7 +7588,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
7169 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 7588 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
7170 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 7589 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
7171 | * of the heredoc */ | 7590 | * of the heredoc */ |
7172 | debug_printf_parse("set heredoc '%s'\n", | 7591 | debug_printf_redir("set heredoc '%s'\n", |
7173 | redir->rd_filename); | 7592 | redir->rd_filename); |
7174 | setup_heredoc(redir); | 7593 | setup_heredoc(redir); |
7175 | continue; | 7594 | continue; |
@@ -7181,8 +7600,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
7181 | int mode; | 7600 | int mode; |
7182 | 7601 | ||
7183 | if (redir->rd_filename == NULL) { | 7602 | if (redir->rd_filename == NULL) { |
7184 | /* | 7603 | /* Examples: |
7185 | * Examples: | ||
7186 | * "cmd >" (no filename) | 7604 | * "cmd >" (no filename) |
7187 | * "cmd > <file" (2nd redirect starts too early) | 7605 | * "cmd > <file" (2nd redirect starts too early) |
7188 | */ | 7606 | */ |
@@ -7238,7 +7656,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
7238 | */ | 7656 | */ |
7239 | } else { | 7657 | } else { |
7240 | /* if newfd is a script fd or saved fd, simulate EBADF */ | 7658 | /* if newfd is a script fd or saved fd, simulate EBADF */ |
7241 | if (internally_opened_fd(newfd, sqp ? *sqp : NULL)) { | 7659 | if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) { |
7242 | //errno = EBADF; | 7660 | //errno = EBADF; |
7243 | //bb_perror_msg_and_die("can't duplicate file descriptor"); | 7661 | //bb_perror_msg_and_die("can't duplicate file descriptor"); |
7244 | newfd = -1; /* same effect as code above */ | 7662 | newfd = -1; /* same effect as code above */ |
@@ -7312,6 +7730,58 @@ static const struct built_in_command *find_builtin(const char *name) | |||
7312 | return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); | 7730 | return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); |
7313 | } | 7731 | } |
7314 | 7732 | ||
7733 | static void remove_nested_vars(void) | ||
7734 | { | ||
7735 | struct variable *cur; | ||
7736 | struct variable **cur_pp; | ||
7737 | |||
7738 | cur_pp = &G.top_var; | ||
7739 | while ((cur = *cur_pp) != NULL) { | ||
7740 | if (cur->var_nest_level <= G.var_nest_level) { | ||
7741 | cur_pp = &cur->next; | ||
7742 | continue; | ||
7743 | } | ||
7744 | /* Unexport */ | ||
7745 | if (cur->flg_export) { | ||
7746 | debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level); | ||
7747 | bb_unsetenv(cur->varstr); | ||
7748 | } | ||
7749 | /* Remove from global list */ | ||
7750 | *cur_pp = cur->next; | ||
7751 | /* Free */ | ||
7752 | if (!cur->max_len) { | ||
7753 | debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); | ||
7754 | free(cur->varstr); | ||
7755 | } | ||
7756 | free(cur); | ||
7757 | } | ||
7758 | } | ||
7759 | |||
7760 | static void enter_var_nest_level(void) | ||
7761 | { | ||
7762 | G.var_nest_level++; | ||
7763 | debug_printf_env("var_nest_level++ %u\n", G.var_nest_level); | ||
7764 | |||
7765 | /* Try: f() { echo -n .; f; }; f | ||
7766 | * struct variable::var_nest_level is uint16_t, | ||
7767 | * thus limiting recursion to < 2^16. | ||
7768 | * In any case, with 8 Mbyte stack SEGV happens | ||
7769 | * not too long after 2^16 recursions anyway. | ||
7770 | */ | ||
7771 | if (G.var_nest_level > 0xff00) | ||
7772 | bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level); | ||
7773 | } | ||
7774 | |||
7775 | static void leave_var_nest_level(void) | ||
7776 | { | ||
7777 | G.var_nest_level--; | ||
7778 | debug_printf_env("var_nest_level-- %u\n", G.var_nest_level); | ||
7779 | if (HUSH_DEBUG && (int)G.var_nest_level < 0) | ||
7780 | bb_error_msg_and_die("BUG: nesting underflow"); | ||
7781 | |||
7782 | remove_nested_vars(); | ||
7783 | } | ||
7784 | |||
7315 | #if ENABLE_HUSH_FUNCTIONS | 7785 | #if ENABLE_HUSH_FUNCTIONS |
7316 | static struct function **find_function_slot(const char *name) | 7786 | static struct function **find_function_slot(const char *name) |
7317 | { | 7787 | { |
@@ -7398,58 +7868,6 @@ static void unset_func(const char *name) | |||
7398 | } | 7868 | } |
7399 | # endif | 7869 | # endif |
7400 | 7870 | ||
7401 | static void remove_nested_vars(void) | ||
7402 | { | ||
7403 | struct variable *cur; | ||
7404 | struct variable **cur_pp; | ||
7405 | |||
7406 | cur_pp = &G.top_var; | ||
7407 | while ((cur = *cur_pp) != NULL) { | ||
7408 | if (cur->var_nest_level <= G.var_nest_level) { | ||
7409 | cur_pp = &cur->next; | ||
7410 | continue; | ||
7411 | } | ||
7412 | /* Unexport */ | ||
7413 | if (cur->flg_export) { | ||
7414 | debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level); | ||
7415 | bb_unsetenv(cur->varstr); | ||
7416 | } | ||
7417 | /* Remove from global list */ | ||
7418 | *cur_pp = cur->next; | ||
7419 | /* Free */ | ||
7420 | if (!cur->max_len) { | ||
7421 | debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level); | ||
7422 | free(cur->varstr); | ||
7423 | } | ||
7424 | free(cur); | ||
7425 | } | ||
7426 | } | ||
7427 | |||
7428 | static void enter_var_nest_level(void) | ||
7429 | { | ||
7430 | G.var_nest_level++; | ||
7431 | debug_printf_env("var_nest_level++ %u\n", G.var_nest_level); | ||
7432 | |||
7433 | /* Try: f() { echo -n .; f; }; f | ||
7434 | * struct variable::var_nest_level is uint16_t, | ||
7435 | * thus limiting recursion to < 2^16. | ||
7436 | * In any case, with 8 Mbyte stack SEGV happens | ||
7437 | * not too long after 2^16 recursions anyway. | ||
7438 | */ | ||
7439 | if (G.var_nest_level > 0xff00) | ||
7440 | bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level); | ||
7441 | } | ||
7442 | |||
7443 | static void leave_var_nest_level(void) | ||
7444 | { | ||
7445 | G.var_nest_level--; | ||
7446 | debug_printf_env("var_nest_level-- %u\n", G.var_nest_level); | ||
7447 | if (HUSH_DEBUG && (int)G.var_nest_level < 0) | ||
7448 | bb_error_msg_and_die("BUG: nesting underflow"); | ||
7449 | |||
7450 | remove_nested_vars(); | ||
7451 | } | ||
7452 | |||
7453 | # if BB_MMU | 7871 | # if BB_MMU |
7454 | #define exec_function(to_free, funcp, argv) \ | 7872 | #define exec_function(to_free, funcp, argv) \ |
7455 | exec_function(funcp, argv) | 7873 | exec_function(funcp, argv) |
@@ -8253,8 +8671,8 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe) | |||
8253 | * subshell: ( list ) [&] | 8671 | * subshell: ( list ) [&] |
8254 | */ | 8672 | */ |
8255 | #if !ENABLE_HUSH_MODE_X | 8673 | #if !ENABLE_HUSH_MODE_X |
8256 | #define redirect_and_varexp_helper(old_vars_p, command, squirrel, argv_expanded) \ | 8674 | #define redirect_and_varexp_helper(command, sqp, argv_expanded) \ |
8257 | redirect_and_varexp_helper(old_vars_p, command, squirrel) | 8675 | redirect_and_varexp_helper(command, sqp) |
8258 | #endif | 8676 | #endif |
8259 | static int redirect_and_varexp_helper( | 8677 | static int redirect_and_varexp_helper( |
8260 | struct command *command, | 8678 | struct command *command, |
@@ -8271,10 +8689,6 @@ static int redirect_and_varexp_helper( | |||
8271 | /* this takes ownership of new_env[i] elements, and frees new_env: */ | 8689 | /* this takes ownership of new_env[i] elements, and frees new_env: */ |
8272 | set_vars_and_save_old(new_env); | 8690 | set_vars_and_save_old(new_env); |
8273 | 8691 | ||
8274 | /* setup_redirects acts on file descriptors, not FILEs. | ||
8275 | * This is perfect for work that comes after exec(). | ||
8276 | * Is it really safe for inline use? Experimentally, | ||
8277 | * things seem to work. */ | ||
8278 | return setup_redirects(command, sqp); | 8692 | return setup_redirects(command, sqp); |
8279 | } | 8693 | } |
8280 | static NOINLINE int run_pipe(struct pipe *pi) | 8694 | static NOINLINE int run_pipe(struct pipe *pi) |
@@ -8363,7 +8777,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8363 | } | 8777 | } |
8364 | #endif | 8778 | #endif |
8365 | /* { list } */ | 8779 | /* { list } */ |
8366 | debug_printf("non-subshell group\n"); | 8780 | debug_printf_exec("non-subshell group\n"); |
8367 | rcode = 1; /* exitcode if redir failed */ | 8781 | rcode = 1; /* exitcode if redir failed */ |
8368 | if (setup_redirects(command, &squirrel) == 0) { | 8782 | if (setup_redirects(command, &squirrel) == 0) { |
8369 | debug_printf_exec(": run_list\n"); | 8783 | debug_printf_exec(": run_list\n"); |
@@ -8475,7 +8889,10 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8475 | */ | 8889 | */ |
8476 | enter_var_nest_level(); | 8890 | enter_var_nest_level(); |
8477 | G.shadowed_vars_pp = &old_vars; | 8891 | G.shadowed_vars_pp = &old_vars; |
8478 | rcode = redirect_and_varexp_helper(command, /*squirrel:*/ NULL, argv_expanded); | 8892 | rcode = redirect_and_varexp_helper(command, |
8893 | /*squirrel:*/ ERR_PTR, | ||
8894 | argv_expanded | ||
8895 | ); | ||
8479 | G.shadowed_vars_pp = sv_shadowed; | 8896 | G.shadowed_vars_pp = sv_shadowed; |
8480 | /* rcode=1 can be if redir file can't be opened */ | 8897 | /* rcode=1 can be if redir file can't be opened */ |
8481 | 8898 | ||
@@ -9528,14 +9945,13 @@ int hush_main(int argc, char **argv) | |||
9528 | 9945 | ||
9529 | /* If we are login shell... */ | 9946 | /* If we are login shell... */ |
9530 | if (flags & OPT_login) { | 9947 | if (flags & OPT_login) { |
9531 | FILE *input; | 9948 | HFILE *input; |
9532 | debug_printf("sourcing /etc/profile\n"); | 9949 | debug_printf("sourcing /etc/profile\n"); |
9533 | input = fopen_for_read("/etc/profile"); | 9950 | input = hfopen("/etc/profile"); |
9534 | if (input != NULL) { | 9951 | if (input != NULL) { |
9535 | remember_FILE(input); | ||
9536 | install_special_sighandlers(); | 9952 | install_special_sighandlers(); |
9537 | parse_and_run_file(input); | 9953 | parse_and_run_file(input); |
9538 | fclose_and_forget(input); | 9954 | hfclose(input); |
9539 | } | 9955 | } |
9540 | /* bash: after sourcing /etc/profile, | 9956 | /* bash: after sourcing /etc/profile, |
9541 | * tries to source (in the given order): | 9957 | * tries to source (in the given order): |
@@ -9548,7 +9964,7 @@ int hush_main(int argc, char **argv) | |||
9548 | 9964 | ||
9549 | /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ | 9965 | /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ |
9550 | if (!(flags & OPT_s) && G.global_argv[1]) { | 9966 | if (!(flags & OPT_s) && G.global_argv[1]) { |
9551 | FILE *input; | 9967 | HFILE *input; |
9552 | /* | 9968 | /* |
9553 | * "bash <script>" (which is never interactive (unless -i?)) | 9969 | * "bash <script>" (which is never interactive (unless -i?)) |
9554 | * sources $BASH_ENV here (without scanning $PATH). | 9970 | * sources $BASH_ENV here (without scanning $PATH). |
@@ -9559,13 +9975,15 @@ int hush_main(int argc, char **argv) | |||
9559 | G.global_argv++; | 9975 | G.global_argv++; |
9560 | debug_printf("running script '%s'\n", G.global_argv[0]); | 9976 | debug_printf("running script '%s'\n", G.global_argv[0]); |
9561 | xfunc_error_retval = 127; /* for "hush /does/not/exist" case */ | 9977 | xfunc_error_retval = 127; /* for "hush /does/not/exist" case */ |
9562 | input = xfopen_for_read(G.global_argv[0]); | 9978 | input = hfopen(G.global_argv[0]); |
9979 | if (!input) { | ||
9980 | bb_simple_perror_msg_and_die(G.global_argv[0]); | ||
9981 | } | ||
9563 | xfunc_error_retval = 1; | 9982 | xfunc_error_retval = 1; |
9564 | remember_FILE(input); | ||
9565 | install_special_sighandlers(); | 9983 | install_special_sighandlers(); |
9566 | parse_and_run_file(input); | 9984 | parse_and_run_file(input); |
9567 | #if ENABLE_FEATURE_CLEAN_UP | 9985 | #if ENABLE_FEATURE_CLEAN_UP |
9568 | fclose_and_forget(input); | 9986 | hfclose(input); |
9569 | #endif | 9987 | #endif |
9570 | goto final_return; | 9988 | goto final_return; |
9571 | } | 9989 | } |
@@ -9666,7 +10084,7 @@ int hush_main(int argc, char **argv) | |||
9666 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); | 10084 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); |
9667 | if (G_interactive_fd < 0) { | 10085 | if (G_interactive_fd < 0) { |
9668 | /* try to dup to any fd */ | 10086 | /* try to dup to any fd */ |
9669 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO); | 10087 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); |
9670 | if (G_interactive_fd < 0) | 10088 | if (G_interactive_fd < 0) |
9671 | /* give up */ | 10089 | /* give up */ |
9672 | G_interactive_fd = 0; | 10090 | G_interactive_fd = 0; |
@@ -9695,7 +10113,7 @@ int hush_main(int argc, char **argv) | |||
9695 | ); | 10113 | ); |
9696 | } | 10114 | } |
9697 | 10115 | ||
9698 | parse_and_run_file(stdin); | 10116 | parse_and_run_file(hfopen(NULL)); /* stdin */ |
9699 | 10117 | ||
9700 | final_return: | 10118 | final_return: |
9701 | hush_exit(G.last_exitcode); | 10119 | hush_exit(G.last_exitcode); |
@@ -9803,41 +10221,41 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
9803 | 10221 | ||
9804 | static int FAST_FUNC builtin_eval(char **argv) | 10222 | static int FAST_FUNC builtin_eval(char **argv) |
9805 | { | 10223 | { |
9806 | int rcode = EXIT_SUCCESS; | ||
9807 | |||
9808 | argv = skip_dash_dash(argv); | 10224 | argv = skip_dash_dash(argv); |
9809 | if (argv[0]) { | ||
9810 | char *str = NULL; | ||
9811 | 10225 | ||
9812 | if (argv[1]) { | 10226 | if (!argv[0]) |
9813 | /* "The eval utility shall construct a command by | 10227 | return EXIT_SUCCESS; |
9814 | * concatenating arguments together, separating | ||
9815 | * each with a <space> character." | ||
9816 | */ | ||
9817 | char *p; | ||
9818 | unsigned len = 0; | ||
9819 | char **pp = argv; | ||
9820 | do | ||
9821 | len += strlen(*pp) + 1; | ||
9822 | while (*++pp); | ||
9823 | str = p = xmalloc(len); | ||
9824 | pp = argv; | ||
9825 | do { | ||
9826 | p = stpcpy(p, *pp); | ||
9827 | *p++ = ' '; | ||
9828 | } while (*++pp); | ||
9829 | p[-1] = '\0'; | ||
9830 | } | ||
9831 | 10228 | ||
10229 | if (!argv[1]) { | ||
9832 | /* bash: | 10230 | /* bash: |
9833 | * eval "echo Hi; done" ("done" is syntax error): | 10231 | * eval "echo Hi; done" ("done" is syntax error): |
9834 | * "echo Hi" will not execute too. | 10232 | * "echo Hi" will not execute too. |
9835 | */ | 10233 | */ |
9836 | parse_and_run_string(str ? str : argv[0]); | 10234 | parse_and_run_string(argv[0]); |
10235 | } else { | ||
10236 | /* "The eval utility shall construct a command by | ||
10237 | * concatenating arguments together, separating | ||
10238 | * each with a <space> character." | ||
10239 | */ | ||
10240 | char *str, *p; | ||
10241 | unsigned len = 0; | ||
10242 | char **pp = argv; | ||
10243 | do | ||
10244 | len += strlen(*pp) + 1; | ||
10245 | while (*++pp); | ||
10246 | str = p = xmalloc(len); | ||
10247 | pp = argv; | ||
10248 | for (;;) { | ||
10249 | p = stpcpy(p, *pp); | ||
10250 | pp++; | ||
10251 | if (!*pp) | ||
10252 | break; | ||
10253 | *p++ = ' '; | ||
10254 | } | ||
10255 | parse_and_run_string(str); | ||
9837 | free(str); | 10256 | free(str); |
9838 | rcode = G.last_exitcode; | ||
9839 | } | 10257 | } |
9840 | return rcode; | 10258 | return G.last_exitcode; |
9841 | } | 10259 | } |
9842 | 10260 | ||
9843 | static int FAST_FUNC builtin_exec(char **argv) | 10261 | static int FAST_FUNC builtin_exec(char **argv) |
@@ -10506,7 +10924,7 @@ Test that VAR is a valid variable name? | |||
10506 | static int FAST_FUNC builtin_source(char **argv) | 10924 | static int FAST_FUNC builtin_source(char **argv) |
10507 | { | 10925 | { |
10508 | char *arg_path, *filename; | 10926 | char *arg_path, *filename; |
10509 | FILE *input; | 10927 | HFILE *input; |
10510 | save_arg_t sv; | 10928 | save_arg_t sv; |
10511 | char *args_need_save; | 10929 | char *args_need_save; |
10512 | #if ENABLE_HUSH_FUNCTIONS | 10930 | #if ENABLE_HUSH_FUNCTIONS |
@@ -10530,10 +10948,10 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10530 | return EXIT_FAILURE; | 10948 | return EXIT_FAILURE; |
10531 | } | 10949 | } |
10532 | } | 10950 | } |
10533 | input = remember_FILE(fopen_or_warn(filename, "r")); | 10951 | input = hfopen(filename); |
10534 | free(arg_path); | 10952 | free(arg_path); |
10535 | if (!input) { | 10953 | if (!input) { |
10536 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ | 10954 | bb_perror_msg("%s", filename); |
10537 | /* POSIX: non-interactive shell should abort here, | 10955 | /* POSIX: non-interactive shell should abort here, |
10538 | * not merely fail. So far no one complained :) | 10956 | * not merely fail. So far no one complained :) |
10539 | */ | 10957 | */ |
@@ -10552,7 +10970,7 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10552 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ | 10970 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ |
10553 | G.last_exitcode = 0; | 10971 | G.last_exitcode = 0; |
10554 | parse_and_run_file(input); | 10972 | parse_and_run_file(input); |
10555 | fclose_and_forget(input); | 10973 | hfclose(input); |
10556 | 10974 | ||
10557 | if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ | 10975 | if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ |
10558 | restore_G_args(&sv, argv); | 10976 | restore_G_args(&sv, argv); |
diff --git a/shell/hush_test/hush-glob/glob_altvalue1.right b/shell/hush_test/hush-glob/glob_altvalue1.right new file mode 100644 index 000000000..bd3592229 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_altvalue1.right | |||
@@ -0,0 +1,7 @@ | |||
1 | 1u: glob_altvalue1.tests | ||
2 | 2u: glob_altvalue1.t* | ||
3 | 3u: glob_altvalue1.t* | ||
4 | 4u: glob_altvalue1.t* | ||
5 | 1q: glob_altvalue1.t* | ||
6 | 2q: 'glob_altvalue1.t*' | ||
7 | 3q: glob_altvalue1.t* | ||
diff --git a/shell/hush_test/hush-glob/glob_altvalue1.tests b/shell/hush_test/hush-glob/glob_altvalue1.tests new file mode 100755 index 000000000..5483d63e6 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_altvalue1.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | x=x | ||
2 | |||
3 | echo 1u: ${x:+glob_altvalue1.t*} | ||
4 | echo 2u: ${x:+'glob_altvalue1.t*'} | ||
5 | echo 3u: ${x:+"glob_altvalue1.t*"} | ||
6 | echo 4u: ${x:+glob_altvalue1.t\*} | ||
7 | ##echo 5u: ${x:+"glob_altvalue1.t\*"} | ||
8 | |||
9 | echo 1q: "${x:+glob_altvalue1.t*}" | ||
10 | echo 2q: "${x:+'glob_altvalue1.t*'}" | ||
11 | echo 3q: "${x:+"glob_altvalue1.t*"}" | ||
12 | ##echo 4q: "${x:+glob_altvalue1.t\*}" | ||
13 | ##echo 5q: "${x:+"glob_altvalue1.t\*"}" | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc.right b/shell/hush_test/hush-heredoc/heredoc.right new file mode 100644 index 000000000..9b9e2aae9 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc.right | |||
@@ -0,0 +1,20 @@ | |||
1 | there | ||
2 | one - alpha | ||
3 | two - beta | ||
4 | three - gamma | ||
5 | hi\ | ||
6 | there$a | ||
7 | stuff | ||
8 | hi\ | ||
9 | there | ||
10 | EO\ | ||
11 | F | ||
12 | hi | ||
13 | hi | ||
14 | tab 1 | ||
15 | tab 2 | ||
16 | tab 3 | ||
17 | abc | ||
18 | def ghi | ||
19 | jkl mno | ||
20 | fff is a function | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc.tests b/shell/hush_test/hush-heredoc/heredoc.tests new file mode 100755 index 000000000..39345c51b --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc.tests | |||
@@ -0,0 +1,97 @@ | |||
1 | # check order and content of multiple here docs | ||
2 | |||
3 | cat << EOF1 << EOF2 | ||
4 | hi | ||
5 | EOF1 | ||
6 | there | ||
7 | EOF2 | ||
8 | |||
9 | while read line1; do | ||
10 | read line2 <&3 | ||
11 | echo $line1 - $line2 | ||
12 | done <<EOF1 3<<EOF2 | ||
13 | one | ||
14 | two | ||
15 | three | ||
16 | EOF1 | ||
17 | alpha | ||
18 | beta | ||
19 | gamma | ||
20 | EOF2 | ||
21 | |||
22 | |||
23 | # check quoted here-doc is protected | ||
24 | |||
25 | a=foo | ||
26 | cat << 'EOF' | ||
27 | hi\ | ||
28 | there$a | ||
29 | stuff | ||
30 | EOF | ||
31 | |||
32 | # check that quoted here-documents don't have \newline processing done | ||
33 | |||
34 | cat << 'EOF' | ||
35 | hi\ | ||
36 | there | ||
37 | EO\ | ||
38 | F | ||
39 | EOF | ||
40 | true | ||
41 | |||
42 | # check that \newline is removed at start of here-doc | ||
43 | cat << EO\ | ||
44 | F | ||
45 | hi | ||
46 | EOF | ||
47 | |||
48 | # check that \newline removal works for here-doc delimiter | ||
49 | cat << EOF | ||
50 | hi | ||
51 | EO\ | ||
52 | F | ||
53 | |||
54 | # check operation of tab removal in here documents | ||
55 | cat <<- EOF | ||
56 | tab 1 | ||
57 | tab 2 | ||
58 | tab 3 | ||
59 | EOF | ||
60 | |||
61 | # check appending of text to file from here document | ||
62 | rm -f /tmp/bash-zzz | ||
63 | cat > /tmp/bash-zzz << EOF | ||
64 | abc | ||
65 | EOF | ||
66 | cat >> /tmp/bash-zzz << EOF | ||
67 | def ghi | ||
68 | jkl mno | ||
69 | EOF | ||
70 | cat /tmp/bash-zzz | ||
71 | rm -f /tmp/bash-zzz | ||
72 | |||
73 | # make sure command printing puts the here-document as the last redirection | ||
74 | # on the line, and the function export code preserves syntactic correctness | ||
75 | fff() | ||
76 | { | ||
77 | ed /tmp/foo <<ENDOFINPUT >/dev/null | ||
78 | /^name/d | ||
79 | w | ||
80 | q | ||
81 | ENDOFINPUT | ||
82 | aa=1 | ||
83 | } | ||
84 | |||
85 | type fff | ||
86 | #ash# export -f fff | ||
87 | #ash# ${THIS_SH} -c 'type fff' | ||
88 | |||
89 | #hush# bash warns: "here-document at line N delimited by end-of-file", | ||
90 | #hush# ash allows it, | ||
91 | #hush# hush errors out for now: | ||
92 | #hush# # check that end of file delimits a here-document | ||
93 | #hush# # THIS MUST BE LAST! | ||
94 | #hush# | ||
95 | #hush# cat << EOF | ||
96 | #hush# hi | ||
97 | #hush# there | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc9.right b/shell/hush_test/hush-heredoc/heredoc9.right new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc9.right | |||
@@ -0,0 +1 @@ | |||
hello | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc9.tests b/shell/hush_test/hush-heredoc/heredoc9.tests new file mode 100755 index 000000000..96c227cc1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc9.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | echo hello >greeting | ||
2 | cat <<EOF && | ||
3 | $(cat greeting) | ||
4 | EOF | ||
5 | { | ||
6 | echo $? | ||
7 | cat greeting | ||
8 | } >/dev/null | ||
9 | rm greeting | ||
diff --git a/shell/hush_test/hush-heredoc/heredocA.right b/shell/hush_test/hush-heredoc/heredocA.right new file mode 100644 index 000000000..7326d9603 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocA.right | |||
@@ -0,0 +1 @@ | |||
Ok | |||
diff --git a/shell/hush_test/hush-heredoc/heredocA.tests b/shell/hush_test/hush-heredoc/heredocA.tests new file mode 100755 index 000000000..440aaf906 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocA.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | { cat <<EOF ; | ||
2 | Ok | ||
3 | EOF | ||
4 | } | ||
diff --git a/shell/hush_test/hush-heredoc/heredocB.right b/shell/hush_test/hush-heredoc/heredocB.right new file mode 100644 index 000000000..43ba0b4f9 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocB.right | |||
@@ -0,0 +1,3 @@ | |||
1 | one - alpha | ||
2 | two - beta | ||
3 | three - gamma | ||
diff --git a/shell/hush_test/hush-heredoc/heredocB.tests b/shell/hush_test/hush-heredoc/heredocB.tests new file mode 100755 index 000000000..45ea4687f --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocB.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | while read line1; do | ||
2 | read line2 <&3 | ||
3 | echo $line1 - $line2 | ||
4 | done <<EOF1 3<<EOF2 | ||
5 | one | ||
6 | two | ||
7 | three | ||
8 | EOF1 | ||
9 | alpha | ||
10 | beta | ||
11 | gamma | ||
12 | EOF2 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_after_compound1.right b/shell/hush_test/hush-heredoc/heredoc_after_compound1.right new file mode 100644 index 000000000..9052f7d1f --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_after_compound1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests b/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests new file mode 100755 index 000000000..e7cfb5be1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | { cat <<EOF; }; echo Ok2 | ||
2 | Ok1 | ||
3 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.right | |||
@@ -0,0 +1 @@ | |||
Ok1 | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.tests new file mode 100755 index 000000000..1d2a26504 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | cat <<EOF | ||
2 | Ok1 | ||
3 | EO\ | ||
4 | F | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_empty3.right b/shell/hush_test/hush-heredoc/heredoc_empty3.right new file mode 100644 index 000000000..0b54a9c93 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_empty3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | Ok | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_empty3.tests b/shell/hush_test/hush-heredoc/heredoc_empty3.tests new file mode 100755 index 000000000..828c2dd89 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_empty3.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | cat <<EOF | ||
2 | |||
3 | Ok | ||
4 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_var_expand1.right b/shell/hush_test/hush-heredoc/heredoc_var_expand1.right new file mode 100644 index 000000000..eb221832d --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_var_expand1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | |||
2 | Ok1:0 | ||
3 | |||
4 | Ok2:0 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests b/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests new file mode 100755 index 000000000..3b00bab7b --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | x='*' | ||
2 | |||
3 | cat <<- EOF | ||
4 | ${x#'*'} | ||
5 | EOF | ||
6 | echo Ok1:$? | ||
7 | |||
8 | cat <<EOF | ||
9 | ${x#'*'} | ||
10 | EOF | ||
11 | echo Ok2:$? | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue1.right b/shell/hush_test/hush-quoting/dollar_altvalue1.right new file mode 100644 index 000000000..5cd495d3b --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue1.right | |||
@@ -0,0 +1,16 @@ | |||
1 | Unquoted b c d | ||
2 | |b| | ||
3 | |c| | ||
4 | |d| | ||
5 | Unquoted 'b c' d | ||
6 | |b c| | ||
7 | |d| | ||
8 | Unquoted "b c" d | ||
9 | |b c| | ||
10 | |d| | ||
11 | Quoted b c d | ||
12 | |b c d| | ||
13 | Quoted 'b c' d | ||
14 | |'b c' d| | ||
15 | Quoted "b c" d | ||
16 | |b c d| | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue1.tests b/shell/hush_test/hush-quoting/dollar_altvalue1.tests new file mode 100755 index 000000000..f4dc8caec --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue1.tests | |||
@@ -0,0 +1,16 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | x=a | ||
3 | |||
4 | echo Unquoted b c d | ||
5 | f ${x:+b c d} | ||
6 | echo Unquoted "'b c' d" | ||
7 | f ${x:+'b c' d} | ||
8 | echo Unquoted '"b c" d' | ||
9 | f ${x:+"b c" d} | ||
10 | |||
11 | echo Quoted b c d | ||
12 | f "${x:+b c d}" | ||
13 | echo Quoted "'b c' d" | ||
14 | f "${x:+'b c' d}" | ||
15 | echo Quoted '"b c" d' | ||
16 | f "${x:+"b c" d}" | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue2.right b/shell/hush_test/hush-quoting/dollar_altvalue2.right new file mode 100644 index 000000000..7cf37e379 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue2.right | |||
@@ -0,0 +1,69 @@ | |||
1 | Unquoted '': | ||
2 | start: | ||
3 | || | ||
4 | end | ||
5 | start: | ||
6 | || | ||
7 | end | ||
8 | start: | ||
9 | || | ||
10 | end | ||
11 | start: | ||
12 | || | ||
13 | end | ||
14 | start: | ||
15 | || | ||
16 | || | ||
17 | end | ||
18 | |||
19 | Unquoted "": | ||
20 | start: | ||
21 | || | ||
22 | end | ||
23 | start: | ||
24 | || | ||
25 | end | ||
26 | start: | ||
27 | || | ||
28 | end | ||
29 | start: | ||
30 | || | ||
31 | end | ||
32 | start: | ||
33 | || | ||
34 | || | ||
35 | end | ||
36 | |||
37 | Quoted '': | ||
38 | start: | ||
39 | |''| | ||
40 | end | ||
41 | start: | ||
42 | |'' | | ||
43 | end | ||
44 | start: | ||
45 | | ''| | ||
46 | end | ||
47 | start: | ||
48 | | '' | | ||
49 | end | ||
50 | start: | ||
51 | |'' ''| | ||
52 | end | ||
53 | |||
54 | Quoted "": | ||
55 | start: | ||
56 | || | ||
57 | end | ||
58 | start: | ||
59 | | | | ||
60 | end | ||
61 | start: | ||
62 | | | | ||
63 | end | ||
64 | start: | ||
65 | | | | ||
66 | end | ||
67 | start: | ||
68 | | | | ||
69 | end | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue2.tests b/shell/hush_test/hush-quoting/dollar_altvalue2.tests new file mode 100755 index 000000000..3377eb27f --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue2.tests | |||
@@ -0,0 +1,33 @@ | |||
1 | f() { echo start:; for i; do echo "|$i|"; done; echo end; } | ||
2 | x=a | ||
3 | |||
4 | echo "Unquoted '':" | ||
5 | f ${x:+''} | ||
6 | f ${x:+'' } | ||
7 | f ${x:+ ''} | ||
8 | f ${x:+ '' } | ||
9 | f ${x:+'' ''} | ||
10 | |||
11 | echo | ||
12 | echo 'Unquoted "":' | ||
13 | f ${x:+""} | ||
14 | f ${x:+"" } | ||
15 | f ${x:+ ""} | ||
16 | f ${x:+ "" } | ||
17 | f ${x:+"" ""} | ||
18 | |||
19 | echo | ||
20 | echo "Quoted '':" | ||
21 | f "${x:+''}" | ||
22 | f "${x:+'' }" | ||
23 | f "${x:+ ''}" | ||
24 | f "${x:+ '' }" | ||
25 | f "${x:+'' ''}" | ||
26 | |||
27 | echo | ||
28 | echo 'Quoted "":' | ||
29 | f "${x:+""}" | ||
30 | f "${x:+"" }" | ||
31 | f "${x:+ ""}" | ||
32 | f "${x:+ "" }" | ||
33 | f "${x:+"" ""}" | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue9.right b/shell/hush_test/hush-quoting/dollar_altvalue9.right new file mode 100644 index 000000000..39342fe7c --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue9.right | |||
@@ -0,0 +1,26 @@ | |||
1 | Unquoted 1: | ||
2 | |a| | ||
3 | |x y| | ||
4 | |1| | ||
5 | |2| | ||
6 | || | ||
7 | |1 2| | ||
8 | |A| | ||
9 | |B| | ||
10 | |C D| | ||
11 | |zb| | ||
12 | Quoted 1: | ||
13 | |a 'x y' 1 2 '' 1 2 A B C D zb| | ||
14 | Unquoted 2: | ||
15 | |ax y| | ||
16 | |1| | ||
17 | |2| | ||
18 | || | ||
19 | |1 2| | ||
20 | |A| | ||
21 | |B| | ||
22 | |C D| | ||
23 | |z| | ||
24 | |b| | ||
25 | Quoted 2: | ||
26 | |a 'x y' 1 2 '' 1 2 A B C D z b| | ||
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue9.tests b/shell/hush_test/hush-quoting/dollar_altvalue9.tests new file mode 100755 index 000000000..27a6f4f3c --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_altvalue9.tests | |||
@@ -0,0 +1,17 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | |||
3 | echo Unquoted 1: | ||
4 | x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b | ||
5 | echo Quoted 1: | ||
6 | x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b" | ||
7 | |||
8 | echo Unquoted 2: | ||
9 | x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b | ||
10 | echo Quoted 2: | ||
11 | x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z }b" | ||
12 | |||
13 | #echo Unquoted 3: | ||
14 | #e= | ||
15 | #x='1 2'; f a${x:+'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b | ||
16 | #echo Quoted 3: | ||
17 | #x='1 2'; f "a${x:+ 'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b" | ||
diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash1.right b/shell/hush_test/hush-quoting/dollar_repl_bash1.right new file mode 100644 index 000000000..f5e9309f4 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_bash1.right | |||
@@ -0,0 +1,14 @@ | |||
1 | |y| | ||
2 | |zx| | ||
3 | |y| | ||
4 | |zx| | ||
5 | |y zx| | ||
6 | |y zx| | ||
7 | |y| | ||
8 | |zy| | ||
9 | |z| | ||
10 | |y| | ||
11 | |zy| | ||
12 | |z| | ||
13 | |y zy z| | ||
14 | |y zy z| | ||
diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash1.tests b/shell/hush_test/hush-quoting/dollar_repl_bash1.tests new file mode 100755 index 000000000..912635925 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_bash1.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | f() { for i; do echo "|$i|"; done; } | ||
2 | v=xx | ||
3 | |||
4 | f ${v/'x'/"y z"} | ||
5 | f ${v/"x"/'y z'} | ||
6 | f "${v/'x'/"y z"}" | ||
7 | f "${v/"x"/'y z'}" | ||
8 | |||
9 | f ${v//'x'/"y z"} | ||
10 | f ${v//"x"/'y z'} | ||
11 | f "${v//'x'/"y z"}" | ||
12 | f "${v//"x"/'y z'}" | ||
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp.right b/shell/hush_test/hush-quoting/squote_in_varexp.right index a75c0bfd6..4a457021b 100644 --- a/shell/hush_test/hush-quoting/squote_in_varexp.right +++ b/shell/hush_test/hush-quoting/squote_in_varexp.right | |||
@@ -1,5 +1,9 @@ | |||
1 | z | 1 | z |
2 | z | 2 | z |
3 | z | ||
4 | z | ||
5 | y | ||
6 | y | ||
3 | y | 7 | y |
4 | y | 8 | y |
5 | Ok:0 | 9 | Ok:0 |
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp.tests b/shell/hush_test/hush-quoting/squote_in_varexp.tests index a2d05a246..4afc52107 100755 --- a/shell/hush_test/hush-quoting/squote_in_varexp.tests +++ b/shell/hush_test/hush-quoting/squote_in_varexp.tests | |||
@@ -1,6 +1,10 @@ | |||
1 | x=yz | 1 | x=yz |
2 | echo ${x#'y'} | 2 | echo ${x#'y'} |
3 | echo "${x#'y'}" | 3 | echo "${x#'y'}" |
4 | echo ${x#"y"} | ||
5 | echo "${x#"y"}" | ||
4 | echo ${x%'z'} | 6 | echo ${x%'z'} |
5 | echo "${x%'z'}" | 7 | echo "${x%'z'}" |
8 | echo ${x%"z"} | ||
9 | echo "${x%"z"}" | ||
6 | echo Ok:$? | 10 | echo Ok:$? |
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp2.right b/shell/hush_test/hush-quoting/squote_in_varexp2.right index 9d0add3c5..d03047024 100644 --- a/shell/hush_test/hush-quoting/squote_in_varexp2.right +++ b/shell/hush_test/hush-quoting/squote_in_varexp2.right | |||
@@ -1,3 +1,5 @@ | |||
1 | Nothing: | 1 | Nothing: |
2 | Nothing: | 2 | Nothing: |
3 | Nothing: | ||
4 | Nothing: | ||
3 | Ok:0 | 5 | Ok:0 |
diff --git a/shell/hush_test/hush-quoting/squote_in_varexp2.tests b/shell/hush_test/hush-quoting/squote_in_varexp2.tests index 806ad12b9..2797725cc 100755 --- a/shell/hush_test/hush-quoting/squote_in_varexp2.tests +++ b/shell/hush_test/hush-quoting/squote_in_varexp2.tests | |||
@@ -1,4 +1,6 @@ | |||
1 | x='\\\\' | 1 | x='\\\\' |
2 | printf Nothing:'%s\n' ${x#'\\\\'} | 2 | printf Nothing:'%s\n' ${x#'\\\\'} |
3 | printf Nothing:'%s\n' "${x#'\\\\'}" | 3 | printf Nothing:'%s\n' "${x#'\\\\'}" |
4 | printf Nothing:'%s\n' ${x#"\\\\\\\\"} | ||
5 | printf Nothing:'%s\n' "${x#"\\\\\\\\"}" | ||
4 | echo Ok:$? | 6 | echo Ok:$? |
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests index 740daa461..a8d93ce4f 100755 --- a/shell/hush_test/hush-redir/redir_script.tests +++ b/shell/hush_test/hush-redir/redir_script.tests | |||
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \ | |||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | 27 | test x"$fds1" = x" 10>&- 3>&-" && \ |
28 | test x"$fds" = x" 11>&- 3>&-" \ | 28 | test x"$fds" = x" 11>&- 3>&-" \ |
29 | && { echo "Ok: script fd is not closed"; exit 0; } | 29 | && { echo "Ok: script fd is not closed"; exit 0; } |
30 | # or we see that fd 3 moved to fd 10: | ||
31 | test x"$fds1" = x" 3>&- 4>&-" && \ | ||
32 | test x"$fds" = x" 10>&- 3>&-" \ | ||
33 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
30 | 34 | ||
31 | echo "Bug: script fd is closed" | 35 | echo "Bug: script fd is closed" |
32 | echo "fds1:$fds1" | 36 | echo "fds1:$fds1" |
diff --git a/shell/hush_test/hush-z_slow/many_ifs.tests b/shell/hush_test/hush-z_slow/many_ifs.tests index 1f5b1b3a6..cf9a89874 100755 --- a/shell/hush_test/hush-z_slow/many_ifs.tests +++ b/shell/hush_test/hush-z_slow/many_ifs.tests | |||
@@ -229,8 +229,8 @@ do | |||
229 | '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; | 229 | '') split "$d0$f1$d1$f2$d2$f3$d3" "[2]($f1)($f2)" "($f1)($f2)" ;; |
230 | ' ') ;; | 230 | ' ') ;; |
231 | *) x=$f2$d2$f3$d3 | 231 | *) x=$f2$d2$f3$d3 |
232 | x=${x# } #was x=${x#' '} hush needs fixing for this to work | 232 | x=${x#' '} |
233 | x=${x% } #was x=${x%' '} | 233 | x=${x%' '} |
234 | split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" | 234 | split "$d0$f1$d1$f2$d2$f3$d3" "[3]($f1)($f2)($f3)" "($f1)($x)" |
235 | ;; | 235 | ;; |
236 | esac | 236 | esac |
diff --git a/shell/random.c b/shell/random.c index 614172279..ffe0cc937 100644 --- a/shell/random.c +++ b/shell/random.c | |||
@@ -57,11 +57,11 @@ next_random(random_t *rnd) | |||
57 | * Choices for a,b,c: 10,13,10; 8,9,22; 2,7,3; 23,3,24 | 57 | * Choices for a,b,c: 10,13,10; 8,9,22; 2,7,3; 23,3,24 |
58 | * (given by algorithm author) | 58 | * (given by algorithm author) |
59 | */ | 59 | */ |
60 | enum { | 60 | enum { |
61 | a = 2, | 61 | a = 2, |
62 | b = 7, | 62 | b = 7, |
63 | c = 3, | 63 | c = 3, |
64 | }; | 64 | }; |
65 | 65 | ||
66 | uint32_t t; | 66 | uint32_t t; |
67 | 67 | ||
@@ -165,7 +165,7 @@ int main(int argc, char **argv) | |||
165 | write(1, buf, sizeof(buf)); | 165 | write(1, buf, sizeof(buf)); |
166 | } | 166 | } |
167 | 167 | ||
168 | return 0; | 168 | return 0; |
169 | } | 169 | } |
170 | 170 | ||
171 | #endif | 171 | #endif |