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