aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2018-07-25 10:41:42 +0100
committerRon Yorston <rmy@pobox.com>2018-07-25 10:41:42 +0100
commit59873514f17cefd6ba3997dad5779f75433fd4e6 (patch)
tree1c9d0a3450ed95f0b820285b9f9fc217c902e652 /shell
parent779fd5745ac11bf752f5f4b977a274a39c192f90 (diff)
parent81de30de05beebabfa72f2a01ec4f33e9a1923e3 (diff)
downloadbusybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.tar.gz
busybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.tar.bz2
busybox-w32-59873514f17cefd6ba3997dad5779f75433fd4e6.zip
Merge branch 'busybox'
Diffstat (limited to 'shell')
-rw-r--r--shell/Config.src2
-rw-r--r--shell/ash.c3
-rw-r--r--shell/ash_test/ash-glob/glob_altvalue1.right7
-rwxr-xr-xshell/ash_test/ash-glob/glob_altvalue1.tests13
-rw-r--r--shell/ash_test/ash-heredoc/heredoc.right2
-rw-r--r--shell/ash_test/ash-heredoc/heredocA.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredocA.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredocB.right3
-rwxr-xr-xshell/ash_test/ash-heredoc/heredocB.tests12
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_after_compound1.right2
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_after_compound1.tests3
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_bkslash_newline2.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_bkslash_newline2.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_empty3.right2
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_empty3.tests4
-rw-r--r--shell/ash_test/ash-quoting/dollar_altvalue1.right16
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_altvalue1.tests16
-rw-r--r--shell/ash_test/ash-quoting/dollar_altvalue2.right69
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_altvalue2.tests33
-rw-r--r--shell/ash_test/ash-quoting/dollar_altvalue9.right26
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_altvalue9.tests17
-rw-r--r--shell/ash_test/ash-quoting/dollar_repl_bash1.right14
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_bash1.tests12
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp.right4
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp.tests4
-rw-r--r--shell/ash_test/ash-quoting/squote_in_varexp2.right2
-rwxr-xr-xshell/ash_test/ash-quoting/squote_in_varexp2.tests2
-rwxr-xr-xshell/ash_test/ash-redir/redir_script.tests4
-rwxr-xr-xshell/ash_test/ash-z_slow/many_ifs.tests4
-rw-r--r--shell/hush.c1320
-rw-r--r--shell/hush_test/hush-glob/glob_altvalue1.right7
-rwxr-xr-xshell/hush_test/hush-glob/glob_altvalue1.tests13
-rw-r--r--shell/hush_test/hush-heredoc/heredoc.right20
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc.tests97
-rw-r--r--shell/hush_test/hush-heredoc/heredoc9.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc9.tests9
-rw-r--r--shell/hush_test/hush-heredoc/heredocA.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredocA.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredocB.right3
-rwxr-xr-xshell/hush_test/hush-heredoc/heredocB.tests12
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_after_compound1.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_after_compound1.tests3
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_bkslash_newline2.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_bkslash_newline2.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_empty3.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_empty3.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_var_expand1.right4
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_var_expand1.tests11
-rw-r--r--shell/hush_test/hush-quoting/dollar_altvalue1.right16
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_altvalue1.tests16
-rw-r--r--shell/hush_test/hush-quoting/dollar_altvalue2.right69
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_altvalue2.tests33
-rw-r--r--shell/hush_test/hush-quoting/dollar_altvalue9.right26
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_altvalue9.tests17
-rw-r--r--shell/hush_test/hush-quoting/dollar_repl_bash1.right14
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_repl_bash1.tests12
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp.right4
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp.tests4
-rw-r--r--shell/hush_test/hush-quoting/squote_in_varexp2.right2
-rwxr-xr-xshell/hush_test/hush-quoting/squote_in_varexp2.tests2
-rwxr-xr-xshell/hush_test/hush-redir/redir_script.tests4
-rwxr-xr-xshell/hush_test/hush-z_slow/many_ifs.tests4
-rw-r--r--shell/random.c12
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
6menu "Shells" 6menu "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 @@
11u: glob_altvalue1.tests
22u: glob_altvalue1.t*
33u: glob_altvalue1.t*
44u: glob_altvalue1.t*
51q: glob_altvalue1.t*
62q: 'glob_altvalue1.t*'
73q: 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 @@
1x=x
2
3echo 1u: ${x:+glob_altvalue1.t*}
4echo 2u: ${x:+'glob_altvalue1.t*'}
5echo 3u: ${x:+"glob_altvalue1.t*"}
6echo 4u: ${x:+glob_altvalue1.t\*}
7##echo 5u: ${x:+"glob_altvalue1.t\*"}
8
9echo 1q: "${x:+glob_altvalue1.t*}"
10echo 2q: "${x:+'glob_altvalue1.t*'}"
11echo 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
16abc 16abc
17def ghi 17def ghi
18jkl mno 18jkl mno
19fff is a shell function 19fff is a function
20hi 20hi
21there 21there
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 ;
2Ok
3EOF
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 @@
1one - alpha
2two - beta
3three - 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 @@
1while read line1; do
2 read line2 <&3
3 echo $line1 - $line2
4done <<EOF1 3<<EOF2
5one
6two
7three
8EOF1
9alpha
10beta
11gamma
12EOF2
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 @@
1Ok1
2Ok2
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
2Ok1
3EOF
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 @@
1cat <<EOF
2Ok1
3EO\
4F
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
2Ok
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 @@
1cat <<EOF
2
3Ok
4EOF
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 @@
1Unquoted b c d
2|b|
3|c|
4|d|
5Unquoted 'b c' d
6|b c|
7|d|
8Unquoted "b c" d
9|b c|
10|d|
11Quoted b c d
12|b c d|
13Quoted 'b c' d
14|'b c' d|
15Quoted "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 @@
1f() { for i; do echo "|$i|"; done; }
2x=a
3
4echo Unquoted b c d
5f ${x:+b c d}
6echo Unquoted "'b c' d"
7f ${x:+'b c' d}
8echo Unquoted '"b c" d'
9f ${x:+"b c" d}
10
11echo Quoted b c d
12f "${x:+b c d}"
13echo Quoted "'b c' d"
14f "${x:+'b c' d}"
15echo Quoted '"b c" d'
16f "${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 @@
1Unquoted '':
2start:
3||
4end
5start:
6||
7end
8start:
9||
10end
11start:
12||
13end
14start:
15||
16||
17end
18
19Unquoted "":
20start:
21||
22end
23start:
24||
25end
26start:
27||
28end
29start:
30||
31end
32start:
33||
34||
35end
36
37Quoted '':
38start:
39|''|
40end
41start:
42|'' |
43end
44start:
45| ''|
46end
47start:
48| '' |
49end
50start:
51|'' ''|
52end
53
54Quoted "":
55start:
56||
57end
58start:
59| |
60end
61start:
62| |
63end
64start:
65| |
66end
67start:
68| |
69end
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 @@
1f() { echo start:; for i; do echo "|$i|"; done; echo end; }
2x=a
3
4echo "Unquoted '':"
5f ${x:+''}
6f ${x:+'' }
7f ${x:+ ''}
8f ${x:+ '' }
9f ${x:+'' ''}
10
11echo
12echo 'Unquoted "":'
13f ${x:+""}
14f ${x:+"" }
15f ${x:+ ""}
16f ${x:+ "" }
17f ${x:+"" ""}
18
19echo
20echo "Quoted '':"
21f "${x:+''}"
22f "${x:+'' }"
23f "${x:+ ''}"
24f "${x:+ '' }"
25f "${x:+'' ''}"
26
27echo
28echo 'Quoted "":'
29f "${x:+""}"
30f "${x:+"" }"
31f "${x:+ ""}"
32f "${x:+ "" }"
33f "${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 @@
1Unquoted 1:
2|a|
3|x y|
4|1|
5|2|
6||
7|1 2|
8|A|
9|B|
10|C D|
11|zb|
12Quoted 1:
13|a 'x y' 1 2 '' 1 2 A B C D zb|
14Unquoted 2:
15|ax y|
16|1|
17|2|
18||
19|1 2|
20|A|
21|B|
22|C D|
23|z|
24|b|
25Quoted 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 @@
1f() { for i; do echo "|$i|"; done; }
2
3echo Unquoted 1:
4x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b
5echo Quoted 1:
6x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b"
7
8echo Unquoted 2:
9x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b
10echo Quoted 2:
11x='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 @@
1f() { for i; do echo "|$i|"; done; }
2v=xx
3
4f ${v/'x'/"y z"}
5f ${v/"x"/'y z'}
6f "${v/'x'/"y z"}"
7f "${v/"x"/'y z'}"
8
9f ${v//'x'/"y z"}
10f ${v//"x"/'y z'}
11f "${v//'x'/"y z"}"
12f "${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 @@
1z 1z
2z 2z
3z
4z
5y
6y
3y 7y
4y 8y
5Ok:0 9Ok: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 @@
1x=yz 1x=yz
2echo ${x#'y'} 2echo ${x#'y'}
3echo "${x#'y'}" 3echo "${x#'y'}"
4echo ${x#"y"}
5echo "${x#"y"}"
4echo ${x%'z'} 6echo ${x%'z'}
5echo "${x%'z'}" 7echo "${x%'z'}"
8echo ${x%"z"}
9echo "${x%"z"}"
6echo Ok:$? 10echo 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 @@
1Nothing: 1Nothing:
2Nothing: 2Nothing:
3Nothing:
4Nothing:
3Ok:0 5Ok: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 @@
1x='\\\\' 1x='\\\\'
2printf Nothing:'%s\n' ${x#'\\\\'} 2printf Nothing:'%s\n' ${x#'\\\\'}
3printf Nothing:'%s\n' "${x#'\\\\'}" 3printf Nothing:'%s\n' "${x#'\\\\'}"
4printf Nothing:'%s\n' ${x#"\\\\\\\\"}
5printf Nothing:'%s\n' "${x#"\\\\\\\\"}"
4echo Ok:$? 6echo 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" \
27test x"$fds1" = x" 10>&- 3>&-" && \ 27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \ 28test 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:
31test x"$fds1" = x" 3>&- 4>&-" && \
32test x"$fds" = x" 10>&- 3>&-" \
33&& { echo "Ok: script fd is not closed"; exit 0; }
30 34
31echo "Bug: script fd is closed" 35echo "Bug: script fd is closed"
32echo "fds1:$fds1" 36echo "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;
538enum { 543enum {
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 */
569typedef 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
557typedef struct in_str { 578typedef 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
816struct 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) */
825struct globals { 838struct 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 */
1561static FILE *remember_FILE(FILE *fp) 1578static 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}
1573static void fclose_and_forget(FILE *fp) 1600static 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}
1587static int save_FILEs_on_redirect(int fd, int avoid_fd) 1615static 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 */
1641static 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}
1648static 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}
1601static 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
1615static void close_all_FILE_list(void) 1663static 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
1630static int fd_in_FILEs(int fd) 1679static 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
2336static int unset_local_var_len(const char *name, int name_len) 2386static 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
2370static int unset_local_var(const char *name) 2419static 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)
2377static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2427static 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
2610static inline int fgetc_interactive(struct in_str *i) 2658static 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
2772static void setup_file_in_str(struct in_str *i, FILE *f) 2820static 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
2800static void o_free(o_string *o) 2848static 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
2806static ALWAYS_INLINE void o_free_unsafe(o_string *o) 2854static 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 */
4283static 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
4360static 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
4330static int run_list(struct pipe *pi); 4408static 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
4335static struct pipe *parse_stream(char **pstring, 4413static 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 */
4340static int parse_group(struct parse_context *ctx, 4421static 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 */
4475static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
4476/* '...' */ 4559/* '...' */
4477static int add_till_single_quote(o_string *dest, struct in_str *input) 4560static 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}
4573static 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? */
4587static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
4491static int add_till_double_quote(o_string *dest, struct in_str *input) 4588static 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
4907static int encode_string(o_string *as_string, 4991static 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 */
4997static struct pipe *parse_stream(char **pstring, 5077static 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
5619static int process_command_subs(o_string *dest, const char *s); 5700static int process_command_subs(o_string *dest, const char *s);
5620#endif 5701#endif
5702static 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 */
5668static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str) 5750static 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 5825static 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
5749static 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
5870static 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 */
5970static 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
5784static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) 6108static 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: 6184static 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 */
5863static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp) 6204static 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
6766static void parse_and_run_file(FILE *f) 7152static 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
6778static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) 7164static 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
7733static 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
7760static 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
7775static 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
7316static struct function **find_function_slot(const char *name) 7786static 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
7401static 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
7428static 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
7443static 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
8259static int redirect_and_varexp_helper( 8677static 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}
8280static NOINLINE int run_pipe(struct pipe *pi) 8694static 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
9804static int FAST_FUNC builtin_eval(char **argv) 10222static 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
9843static int FAST_FUNC builtin_exec(char **argv) 10261static int FAST_FUNC builtin_exec(char **argv)
@@ -10506,7 +10924,7 @@ Test that VAR is a valid variable name?
10506static int FAST_FUNC builtin_source(char **argv) 10924static 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 @@
11u: glob_altvalue1.tests
22u: glob_altvalue1.t*
33u: glob_altvalue1.t*
44u: glob_altvalue1.t*
51q: glob_altvalue1.t*
62q: 'glob_altvalue1.t*'
73q: 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 @@
1x=x
2
3echo 1u: ${x:+glob_altvalue1.t*}
4echo 2u: ${x:+'glob_altvalue1.t*'}
5echo 3u: ${x:+"glob_altvalue1.t*"}
6echo 4u: ${x:+glob_altvalue1.t\*}
7##echo 5u: ${x:+"glob_altvalue1.t\*"}
8
9echo 1q: "${x:+glob_altvalue1.t*}"
10echo 2q: "${x:+'glob_altvalue1.t*'}"
11echo 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 @@
1there
2one - alpha
3two - beta
4three - gamma
5hi\
6there$a
7stuff
8hi\
9there
10EO\
11F
12hi
13hi
14tab 1
15tab 2
16tab 3
17abc
18def ghi
19jkl mno
20fff 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
3cat << EOF1 << EOF2
4hi
5EOF1
6there
7EOF2
8
9while read line1; do
10 read line2 <&3
11 echo $line1 - $line2
12done <<EOF1 3<<EOF2
13one
14two
15three
16EOF1
17alpha
18beta
19gamma
20EOF2
21
22
23# check quoted here-doc is protected
24
25a=foo
26cat << 'EOF'
27hi\
28there$a
29stuff
30EOF
31
32# check that quoted here-documents don't have \newline processing done
33
34cat << 'EOF'
35hi\
36there
37EO\
38F
39EOF
40true
41
42# check that \newline is removed at start of here-doc
43cat << EO\
44F
45hi
46EOF
47
48# check that \newline removal works for here-doc delimiter
49cat << EOF
50hi
51EO\
52F
53
54# check operation of tab removal in here documents
55cat <<- EOF
56 tab 1
57 tab 2
58 tab 3
59 EOF
60
61# check appending of text to file from here document
62rm -f /tmp/bash-zzz
63cat > /tmp/bash-zzz << EOF
64abc
65EOF
66cat >> /tmp/bash-zzz << EOF
67def ghi
68jkl mno
69EOF
70cat /tmp/bash-zzz
71rm -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
75fff()
76{
77 ed /tmp/foo <<ENDOFINPUT >/dev/null
78/^name/d
79w
80q
81ENDOFINPUT
82aa=1
83}
84
85type 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 @@
1echo hello >greeting
2cat <<EOF &&
3$(cat greeting)
4EOF
5{
6 echo $?
7 cat greeting
8} >/dev/null
9rm 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 ;
2Ok
3EOF
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 @@
1one - alpha
2two - beta
3three - 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 @@
1while read line1; do
2 read line2 <&3
3 echo $line1 - $line2
4done <<EOF1 3<<EOF2
5one
6two
7three
8EOF1
9alpha
10beta
11gamma
12EOF2
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 @@
1Ok1
2Ok2
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
2Ok1
3EOF
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 @@
1cat <<EOF
2Ok1
3EO\
4F
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
2Ok
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 @@
1cat <<EOF
2
3Ok
4EOF
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
2Ok1:0
3
4Ok2: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 @@
1x='*'
2
3cat <<- EOF
4 ${x#'*'}
5EOF
6echo Ok1:$?
7
8cat <<EOF
9${x#'*'}
10EOF
11echo 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 @@
1Unquoted b c d
2|b|
3|c|
4|d|
5Unquoted 'b c' d
6|b c|
7|d|
8Unquoted "b c" d
9|b c|
10|d|
11Quoted b c d
12|b c d|
13Quoted 'b c' d
14|'b c' d|
15Quoted "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 @@
1f() { for i; do echo "|$i|"; done; }
2x=a
3
4echo Unquoted b c d
5f ${x:+b c d}
6echo Unquoted "'b c' d"
7f ${x:+'b c' d}
8echo Unquoted '"b c" d'
9f ${x:+"b c" d}
10
11echo Quoted b c d
12f "${x:+b c d}"
13echo Quoted "'b c' d"
14f "${x:+'b c' d}"
15echo Quoted '"b c" d'
16f "${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 @@
1Unquoted '':
2start:
3||
4end
5start:
6||
7end
8start:
9||
10end
11start:
12||
13end
14start:
15||
16||
17end
18
19Unquoted "":
20start:
21||
22end
23start:
24||
25end
26start:
27||
28end
29start:
30||
31end
32start:
33||
34||
35end
36
37Quoted '':
38start:
39|''|
40end
41start:
42|'' |
43end
44start:
45| ''|
46end
47start:
48| '' |
49end
50start:
51|'' ''|
52end
53
54Quoted "":
55start:
56||
57end
58start:
59| |
60end
61start:
62| |
63end
64start:
65| |
66end
67start:
68| |
69end
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 @@
1f() { echo start:; for i; do echo "|$i|"; done; echo end; }
2x=a
3
4echo "Unquoted '':"
5f ${x:+''}
6f ${x:+'' }
7f ${x:+ ''}
8f ${x:+ '' }
9f ${x:+'' ''}
10
11echo
12echo 'Unquoted "":'
13f ${x:+""}
14f ${x:+"" }
15f ${x:+ ""}
16f ${x:+ "" }
17f ${x:+"" ""}
18
19echo
20echo "Quoted '':"
21f "${x:+''}"
22f "${x:+'' }"
23f "${x:+ ''}"
24f "${x:+ '' }"
25f "${x:+'' ''}"
26
27echo
28echo 'Quoted "":'
29f "${x:+""}"
30f "${x:+"" }"
31f "${x:+ ""}"
32f "${x:+ "" }"
33f "${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 @@
1Unquoted 1:
2|a|
3|x y|
4|1|
5|2|
6||
7|1 2|
8|A|
9|B|
10|C D|
11|zb|
12Quoted 1:
13|a 'x y' 1 2 '' 1 2 A B C D zb|
14Unquoted 2:
15|ax y|
16|1|
17|2|
18||
19|1 2|
20|A|
21|B|
22|C D|
23|z|
24|b|
25Quoted 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 @@
1f() { for i; do echo "|$i|"; done; }
2
3echo Unquoted 1:
4x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b
5echo Quoted 1:
6x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b"
7
8echo Unquoted 2:
9x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b
10echo Quoted 2:
11x='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 @@
1f() { for i; do echo "|$i|"; done; }
2v=xx
3
4f ${v/'x'/"y z"}
5f ${v/"x"/'y z'}
6f "${v/'x'/"y z"}"
7f "${v/"x"/'y z'}"
8
9f ${v//'x'/"y z"}
10f ${v//"x"/'y z'}
11f "${v//'x'/"y z"}"
12f "${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 @@
1z 1z
2z 2z
3z
4z
5y
6y
3y 7y
4y 8y
5Ok:0 9Ok: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 @@
1x=yz 1x=yz
2echo ${x#'y'} 2echo ${x#'y'}
3echo "${x#'y'}" 3echo "${x#'y'}"
4echo ${x#"y"}
5echo "${x#"y"}"
4echo ${x%'z'} 6echo ${x%'z'}
5echo "${x%'z'}" 7echo "${x%'z'}"
8echo ${x%"z"}
9echo "${x%"z"}"
6echo Ok:$? 10echo 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 @@
1Nothing: 1Nothing:
2Nothing: 2Nothing:
3Nothing:
4Nothing:
3Ok:0 5Ok: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 @@
1x='\\\\' 1x='\\\\'
2printf Nothing:'%s\n' ${x#'\\\\'} 2printf Nothing:'%s\n' ${x#'\\\\'}
3printf Nothing:'%s\n' "${x#'\\\\'}" 3printf Nothing:'%s\n' "${x#'\\\\'}"
4printf Nothing:'%s\n' ${x#"\\\\\\\\"}
5printf Nothing:'%s\n' "${x#"\\\\\\\\"}"
4echo Ok:$? 6echo 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" \
27test x"$fds1" = x" 10>&- 3>&-" && \ 27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \ 28test 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:
31test x"$fds1" = x" 3>&- 4>&-" && \
32test x"$fds" = x" 10>&- 3>&-" \
33&& { echo "Ok: script fd is not closed"; exit 0; }
30 34
31echo "Bug: script fd is closed" 35echo "Bug: script fd is closed"
32echo "fds1:$fds1" 36echo "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