aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/Config.src45
-rw-r--r--shell/Kbuild.src2
-rw-r--r--shell/ash.c304
-rw-r--r--shell/ash_test/ash-misc/exitcode2.right4
-rwxr-xr-xshell/ash_test/ash-misc/exitcode2.tests12
-rw-r--r--shell/ash_test/ash-redir/redir_leak.right6
-rwxr-xr-xshell/ash_test/ash-redir/redir_leak.tests10
-rw-r--r--shell/hush.c130
-rw-r--r--shell/hush_test/hush-misc/exitcode2.right4
-rwxr-xr-xshell/hush_test/hush-misc/exitcode2.tests12
-rw-r--r--shell/hush_test/hush-redir/redir_leak.right6
-rwxr-xr-xshell/hush_test/hush-redir/redir_leak.tests10
-rw-r--r--shell/math.h2
13 files changed, 314 insertions, 233 deletions
diff --git a/shell/Config.src b/shell/Config.src
index e4df35973..7f5f67050 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -5,34 +5,30 @@
5 5
6menu "Shells" 6menu "Shells"
7 7
8INSERT
9
10 8
11choice 9choice
12 prompt "Choose which shell is aliased to 'sh' name" 10 prompt "Choose which shell is aliased to 'sh' name"
13 default FEATURE_SH_IS_ASH 11 default SH_IS_ASH
14 help 12 help
15 Choose which shell you want to be executed by 'sh' alias. 13 Choose which shell you want to be executed by 'sh' alias.
16 The ash shell is the most bash compatible and full featured one. 14 The ash shell is the most bash compatible and full featured one.
17 15
18# note: cannot use "select ASH" here, it breaks "make allnoconfig" 16# note: cannot use "select ASH" here, it breaks "make allnoconfig"
19config FEATURE_SH_IS_ASH 17config SH_IS_ASH
20 depends on ASH
21 bool "ash"
22 depends on !NOMMU 18 depends on !NOMMU
19 bool "ash"
23 20
24config FEATURE_SH_IS_HUSH 21config SH_IS_HUSH
25 depends on HUSH
26 bool "hush" 22 bool "hush"
27 23
28config FEATURE_SH_IS_NONE 24config SH_IS_NONE
29 bool "none" 25 bool "none"
30 26
31endchoice 27endchoice
32 28
33choice 29choice
34 prompt "Choose which shell is aliased to 'bash' name" 30 prompt "Choose which shell is aliased to 'bash' name"
35 default FEATURE_BASH_IS_NONE 31 default BASH_IS_NONE
36 help 32 help
37 Choose which shell you want to be executed by 'bash' alias. 33 Choose which shell you want to be executed by 'bash' alias.
38 The ash shell is the most bash compatible and full featured one. 34 The ash shell is the most bash compatible and full featured one.
@@ -47,32 +43,33 @@ choice
47 can't be used for running them because it won't recongnize 43 can't be used for running them because it won't recongnize
48 "bash" as a supported applet name. 44 "bash" as a supported applet name.
49 45
50config FEATURE_BASH_IS_ASH 46config BASH_IS_ASH
51 depends on ASH
52 bool "ash"
53 depends on !NOMMU 47 depends on !NOMMU
48 bool "ash"
54 49
55config FEATURE_BASH_IS_HUSH 50config BASH_IS_HUSH
56 depends on HUSH
57 bool "hush" 51 bool "hush"
58 52
59config FEATURE_BASH_IS_NONE 53config BASH_IS_NONE
60 bool "none" 54 bool "none"
61 55
62endchoice 56endchoice
63 57
64 58
65config SH_MATH_SUPPORT 59INSERT
60
61
62config FEATURE_SH_MATH
66 bool "POSIX math support" 63 bool "POSIX math support"
67 default y 64 default y
68 depends on ASH || HUSH 65 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
69 help 66 help
70 Enable math support in the shell via $((...)) syntax. 67 Enable math support in the shell via $((...)) syntax.
71 68
72config SH_MATH_SUPPORT_64 69config FEATURE_SH_MATH_64
73 bool "Extend POSIX math support to 64 bit" 70 bool "Extend POSIX math support to 64 bit"
74 default y 71 default y
75 depends on SH_MATH_SUPPORT 72 depends on FEATURE_SH_MATH
76 help 73 help
77 Enable 64-bit math support in the shell. This will make the shell 74 Enable 64-bit math support in the shell. This will make the shell
78 slightly larger, but will allow computation with very large numbers. 75 slightly larger, but will allow computation with very large numbers.
@@ -81,14 +78,14 @@ config SH_MATH_SUPPORT_64
81config FEATURE_SH_EXTRA_QUIET 78config FEATURE_SH_EXTRA_QUIET
82 bool "Hide message on interactive shell startup" 79 bool "Hide message on interactive shell startup"
83 default y 80 default y
84 depends on HUSH || ASH 81 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
85 help 82 help
86 Remove the busybox introduction when starting a shell. 83 Remove the busybox introduction when starting a shell.
87 84
88config FEATURE_SH_STANDALONE 85config FEATURE_SH_STANDALONE
89 bool "Standalone shell" 86 bool "Standalone shell"
90 default n 87 default n
91 depends on (HUSH || ASH) 88 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
92 help 89 help
93 This option causes busybox shells to use busybox applets 90 This option causes busybox shells to use busybox applets
94 in preference to executables in the PATH whenever possible. For 91 in preference to executables in the PATH whenever possible. For
@@ -121,7 +118,7 @@ config FEATURE_SH_STANDALONE
121config FEATURE_SH_NOFORK 118config FEATURE_SH_NOFORK
122 bool "Run 'nofork' applets directly" 119 bool "Run 'nofork' applets directly"
123 default n 120 default n
124 depends on (HUSH || ASH) 121 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
125 help 122 help
126 This option causes busybox shells to not execute typical 123 This option causes busybox shells to not execute typical
127 fork/exec/wait sequence, but call <applet>_main directly, 124 fork/exec/wait sequence, but call <applet>_main directly,
@@ -139,7 +136,7 @@ config FEATURE_SH_NOFORK
139config FEATURE_SH_HISTFILESIZE 136config FEATURE_SH_HISTFILESIZE
140 bool "Use $HISTFILESIZE" 137 bool "Use $HISTFILESIZE"
141 default y 138 default y
142 depends on HUSH || ASH 139 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
143 help 140 help
144 This option makes busybox shells to use $HISTFILESIZE variable 141 This option makes busybox shells to use $HISTFILESIZE variable
145 to set shell history size. Note that its max value is capped 142 to set shell history size. Note that its max value is capped
diff --git a/shell/Kbuild.src b/shell/Kbuild.src
index c00aec92a..6bba4989f 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -8,4 +8,4 @@ lib-y:=
8 8
9INSERT 9INSERT
10 10
11lib-$(CONFIG_SH_MATH_SUPPORT) += math.o 11lib-$(CONFIG_FEATURE_SH_MATH) += math.o
diff --git a/shell/ash.c b/shell/ash.c
index 2e7f68c05..af1157709 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -30,80 +30,6 @@
30 * - fake $PPID 30 * - fake $PPID
31 */ 31 */
32 32
33/*
34 * The following should be set to reflect the type of system you have:
35 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
36 * define SYSV if you are running under System V.
37 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
38 * define DEBUG=2 to compile in and turn on debugging.
39 *
40 * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
41 * debugging info will be written to ./trace and a quit signal
42 * will generate a core dump.
43 */
44#define DEBUG 0
45/* Tweak debug output verbosity here */
46#define DEBUG_TIME 0
47#define DEBUG_PID 1
48#define DEBUG_SIG 1
49#define DEBUG_INTONOFF 0
50
51#define PROFILE 0
52
53#define JOBS ENABLE_ASH_JOB_CONTROL
54
55#include <setjmp.h>
56#include <fnmatch.h>
57#include <sys/times.h>
58#include <sys/utsname.h> /* for setting $HOSTNAME */
59
60#include "busybox.h" /* for applet_names */
61
62#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
63/* Bionic at least up to version 24 has no glob() */
64# undef ENABLE_ASH_INTERNAL_GLOB
65# define ENABLE_ASH_INTERNAL_GLOB 1
66#endif
67
68#if !ENABLE_ASH_INTERNAL_GLOB
69# include <glob.h>
70#endif
71
72#include "unicode.h"
73#include "shell_common.h"
74#if ENABLE_SH_MATH_SUPPORT
75# include "math.h"
76#endif
77#if ENABLE_ASH_RANDOM_SUPPORT
78# include "random.h"
79#else
80# define CLEAR_RANDOM_T(rnd) ((void)0)
81#endif
82
83#include "NUM_APPLETS.h"
84#if NUM_APPLETS == 1
85/* STANDALONE does not make sense, and won't compile */
86# undef CONFIG_FEATURE_SH_STANDALONE
87# undef ENABLE_FEATURE_SH_STANDALONE
88# undef IF_FEATURE_SH_STANDALONE
89# undef IF_NOT_FEATURE_SH_STANDALONE
90# define ENABLE_FEATURE_SH_STANDALONE 0
91# define IF_FEATURE_SH_STANDALONE(...)
92# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
93#endif
94
95#ifndef PIPE_BUF
96# define PIPE_BUF 4096 /* amount of buffering in a pipe */
97#endif
98
99#if !ENABLE_PLATFORM_MINGW32
100# define is_absolute_path(path) ((path)[0] == '/')
101#endif
102
103#if !BB_MMU
104# error "Do not even bother, ash will not run on NOMMU machine"
105#endif
106
107//config:config ASH 33//config:config ASH
108//config: bool "ash" 34//config: bool "ash"
109//config: default y 35//config: default y
@@ -118,14 +44,14 @@
118//config:config ASH_OPTIMIZE_FOR_SIZE 44//config:config ASH_OPTIMIZE_FOR_SIZE
119//config: bool "Optimize for size instead of speed" 45//config: bool "Optimize for size instead of speed"
120//config: default y 46//config: default y
121//config: depends on ASH 47//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
122//config: help 48//config: help
123//config: Compile ash for reduced size at the price of speed. 49//config: Compile ash for reduced size at the price of speed.
124//config: 50//config:
125//config:config ASH_INTERNAL_GLOB 51//config:config ASH_INTERNAL_GLOB
126//config: bool "Use internal glob() implementation" 52//config: bool "Use internal glob() implementation"
127//config: default n 53//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
128//config: depends on ASH 54//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
129//config: help 55//config: help
130//config: Do not use glob() function from libc, use internal implementation. 56//config: Do not use glob() function from libc, use internal implementation.
131//config: Use this if you are getting "glob.h: No such file or directory" 57//config: Use this if you are getting "glob.h: No such file or directory"
@@ -134,7 +60,7 @@
134//config:config ASH_RANDOM_SUPPORT 60//config:config ASH_RANDOM_SUPPORT
135//config: bool "Pseudorandom generator and $RANDOM variable" 61//config: bool "Pseudorandom generator and $RANDOM variable"
136//config: default y 62//config: default y
137//config: depends on ASH 63//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
138//config: help 64//config: help
139//config: Enable pseudorandom generator and dynamic variable "$RANDOM". 65//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
140//config: Each read of "$RANDOM" will generate a new pseudorandom value. 66//config: Each read of "$RANDOM" will generate a new pseudorandom value.
@@ -145,7 +71,7 @@
145//config:config ASH_EXPAND_PRMT 71//config:config ASH_EXPAND_PRMT
146//config: bool "Expand prompt string" 72//config: bool "Expand prompt string"
147//config: default y 73//config: default y
148//config: depends on ASH 74//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
149//config: help 75//config: help
150//config: "PS#" may contain volatile content, such as backquote commands. 76//config: "PS#" may contain volatile content, such as backquote commands.
151//config: This option recreates the prompt string from the environment 77//config: This option recreates the prompt string from the environment
@@ -154,70 +80,70 @@
154//config:config ASH_BASH_COMPAT 80//config:config ASH_BASH_COMPAT
155//config: bool "bash-compatible extensions" 81//config: bool "bash-compatible extensions"
156//config: default y 82//config: default y
157//config: depends on ASH 83//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
158//config: help 84//config: help
159//config: Enable bash-compatible extensions. 85//config: Enable bash-compatible extensions.
160//config: 86//config:
161//config:config ASH_IDLE_TIMEOUT 87//config:config ASH_IDLE_TIMEOUT
162//config: bool "Idle timeout variable" 88//config: bool "Idle timeout variable"
163//config: default n 89//config: default n
164//config: depends on ASH 90//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
165//config: help 91//config: help
166//config: Enables bash-like auto-logout after $TMOUT seconds of idle time. 92//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
167//config: 93//config:
168//config:config ASH_JOB_CONTROL 94//config:config ASH_JOB_CONTROL
169//config: bool "Job control" 95//config: bool "Job control"
170//config: default y 96//config: default y
171//config: depends on ASH 97//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
172//config: help 98//config: help
173//config: Enable job control in the ash shell. 99//config: Enable job control in the ash shell.
174//config: 100//config:
175//config:config ASH_ALIAS 101//config:config ASH_ALIAS
176//config: bool "Alias support" 102//config: bool "Alias support"
177//config: default y 103//config: default y
178//config: depends on ASH 104//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
179//config: help 105//config: help
180//config: Enable alias support in the ash shell. 106//config: Enable alias support in the ash shell.
181//config: 107//config:
182//config:config ASH_GETOPTS 108//config:config ASH_GETOPTS
183//config: bool "Builtin getopt to parse positional parameters" 109//config: bool "Builtin getopt to parse positional parameters"
184//config: default y 110//config: default y
185//config: depends on ASH 111//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
186//config: help 112//config: help
187//config: Enable support for getopts builtin in ash. 113//config: Enable support for getopts builtin in ash.
188//config: 114//config:
189//config:config ASH_BUILTIN_ECHO 115//config:config ASH_BUILTIN_ECHO
190//config: bool "Builtin version of 'echo'" 116//config: bool "Builtin version of 'echo'"
191//config: default y 117//config: default y
192//config: depends on ASH 118//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
193//config: help 119//config: help
194//config: Enable support for echo builtin in ash. 120//config: Enable support for echo builtin in ash.
195//config: 121//config:
196//config:config ASH_BUILTIN_PRINTF 122//config:config ASH_BUILTIN_PRINTF
197//config: bool "Builtin version of 'printf'" 123//config: bool "Builtin version of 'printf'"
198//config: default y 124//config: default y
199//config: depends on ASH 125//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
200//config: help 126//config: help
201//config: Enable support for printf builtin in ash. 127//config: Enable support for printf builtin in ash.
202//config: 128//config:
203//config:config ASH_BUILTIN_TEST 129//config:config ASH_BUILTIN_TEST
204//config: bool "Builtin version of 'test'" 130//config: bool "Builtin version of 'test'"
205//config: default y 131//config: default y
206//config: depends on ASH 132//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
207//config: help 133//config: help
208//config: Enable support for test builtin in ash. 134//config: Enable support for test builtin in ash.
209//config: 135//config:
210//config:config ASH_HELP 136//config:config ASH_HELP
211//config: bool "help builtin" 137//config: bool "help builtin"
212//config: default y 138//config: default y
213//config: depends on ASH 139//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
214//config: help 140//config: help
215//config: Enable help builtin in ash. 141//config: Enable help builtin in ash.
216//config: 142//config:
217//config:config ASH_CMDCMD 143//config:config ASH_CMDCMD
218//config: bool "'command' command to override shell builtins" 144//config: bool "'command' command to override shell builtins"
219//config: default y 145//config: default y
220//config: depends on ASH 146//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
221//config: help 147//config: help
222//config: Enable support for the ash 'command' builtin, which allows 148//config: Enable support for the ash 'command' builtin, which allows
223//config: you to run the specified command with the specified arguments, 149//config: you to run the specified command with the specified arguments,
@@ -225,19 +151,105 @@
225//config: 151//config:
226//config:config ASH_MAIL 152//config:config ASH_MAIL
227//config: bool "Check for new mail on interactive shells" 153//config: bool "Check for new mail on interactive shells"
228//config: default n 154//config: default y
229//config: depends on ASH 155//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
230//config: help 156//config: help
231//config: Enable "check for new mail" function in the ash shell. 157//config: Enable "check for new mail" function in the ash shell.
232//config:
233 158
234//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 159//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
235//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) 160//applet:IF_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
236//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash)) 161//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
237 162
238//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o 163//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
164//kbuild:lib-$(CONFIG_SH_IS_ASH) += ash.o ash_ptr_hack.o shell_common.o
165//kbuild:lib-$(CONFIG_BASH_IS_ASH) += ash.o ash_ptr_hack.o shell_common.o
239//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o 166//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
240 167
168/*
169 * The following should be set to reflect the type of system you have:
170 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
171 * define SYSV if you are running under System V.
172 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
173 * define DEBUG=2 to compile in and turn on debugging.
174 *
175 * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
176 * debugging info will be written to ./trace and a quit signal
177 * will generate a core dump.
178 */
179#define DEBUG 0
180/* Tweak debug output verbosity here */
181#define DEBUG_TIME 0
182#define DEBUG_PID 1
183#define DEBUG_SIG 1
184#define DEBUG_INTONOFF 0
185
186#define PROFILE 0
187
188#define JOBS ENABLE_ASH_JOB_CONTROL
189
190#include <setjmp.h>
191#include <fnmatch.h>
192#include <sys/times.h>
193#include <sys/utsname.h> /* for setting $HOSTNAME */
194
195#include "busybox.h" /* for applet_names */
196
197#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
198/* Bionic at least up to version 24 has no glob() */
199# undef ENABLE_ASH_INTERNAL_GLOB
200# define ENABLE_ASH_INTERNAL_GLOB 1
201#endif
202
203#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
204# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
205# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
206# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
207# error glob() should unbackslash them and match. uClibc does not unbackslash,
208# error fails to match dirname, subsequently not expanding <pattern> in it.
209// Testcase:
210// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
211// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
212#endif
213
214#if !ENABLE_ASH_INTERNAL_GLOB
215# include <glob.h>
216#endif
217
218#include "unicode.h"
219#include "shell_common.h"
220#if CONFIG_FEATURE_SH_MATH
221# include "math.h"
222#endif
223#if ENABLE_ASH_RANDOM_SUPPORT
224# include "random.h"
225#else
226# define CLEAR_RANDOM_T(rnd) ((void)0)
227#endif
228
229#include "NUM_APPLETS.h"
230#if NUM_APPLETS == 1
231/* STANDALONE does not make sense, and won't compile */
232# undef CONFIG_FEATURE_SH_STANDALONE
233# undef ENABLE_FEATURE_SH_STANDALONE
234# undef IF_FEATURE_SH_STANDALONE
235# undef IF_NOT_FEATURE_SH_STANDALONE
236# define ENABLE_FEATURE_SH_STANDALONE 0
237# define IF_FEATURE_SH_STANDALONE(...)
238# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
239#endif
240
241#ifndef PIPE_BUF
242# define PIPE_BUF 4096 /* amount of buffering in a pipe */
243#endif
244
245#if !ENABLE_PLATFORM_MINGW32
246# define is_absolute_path(path) ((path)[0] == '/')
247#endif
248
249#if !BB_MMU
250# error "Do not even bother, ash will not run on NOMMU machine"
251#endif
252
241#if ENABLE_PLATFORM_MINGW32 253#if ENABLE_PLATFORM_MINGW32
242union node; 254union node;
243struct strlist; 255struct strlist;
@@ -1343,6 +1355,8 @@ ash_msg_and_raise_error(const char *msg, ...)
1343{ 1355{
1344 va_list ap; 1356 va_list ap;
1345 1357
1358 exitstatus = 2;
1359
1346 va_start(ap, msg); 1360 va_start(ap, msg);
1347 ash_vmsg_and_raise(EXERROR, msg, ap); 1361 ash_vmsg_and_raise(EXERROR, msg, ap);
1348 /* NOTREACHED */ 1362 /* NOTREACHED */
@@ -2205,6 +2219,7 @@ lookupvar(const char *name)
2205 return NULL; 2219 return NULL;
2206} 2220}
2207 2221
2222#if ENABLE_UNICODE_SUPPORT
2208static void 2223static void
2209reinit_unicode_for_ash(void) 2224reinit_unicode_for_ash(void)
2210{ 2225{
@@ -2221,6 +2236,9 @@ reinit_unicode_for_ash(void)
2221 reinit_unicode(s); 2236 reinit_unicode(s);
2222 } 2237 }
2223} 2238}
2239#else
2240# define reinit_unicode_for_ash() ((void)0)
2241#endif
2224 2242
2225/* 2243/*
2226 * Search the environment of a builtin command. 2244 * Search the environment of a builtin command.
@@ -2924,7 +2942,7 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2924 2942
2925#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE 2943#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
2926 2944
2927#if ENABLE_SH_MATH_SUPPORT 2945#if ENABLE_FEATURE_SH_MATH
2928# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12)) 2946# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
2929#else 2947#else
2930# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) 2948# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
@@ -3305,7 +3323,18 @@ static const uint8_t syntax_index_table[] ALIGN1 = {
3305# endif 3323# endif
3306}; 3324};
3307 3325
3326#if 1
3308# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf) 3327# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
3328#else /* debug version, caught one signed char bug */
3329# define SIT(c, syntax) \
3330 ({ \
3331 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3332 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3333 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
3334 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3335 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3336 })
3337#endif
3309 3338
3310#endif /* !USE_SIT_FUNCTION */ 3339#endif /* !USE_SIT_FUNCTION */
3311 3340
@@ -4765,7 +4794,7 @@ cmdputs(const char *s)
4765 case CTLBACKQ: 4794 case CTLBACKQ:
4766 str = "$(...)"; 4795 str = "$(...)";
4767 goto dostr; 4796 goto dostr;
4768#if ENABLE_SH_MATH_SUPPORT 4797#if ENABLE_FEATURE_SH_MATH
4769 case CTLARI: 4798 case CTLARI:
4770 str = "$(("; 4799 str = "$((";
4771 goto dostr; 4800 goto dostr;
@@ -5749,11 +5778,11 @@ redirect(union node *redir, int flags)
5749 /* Careful to not accidentally "save" 5778 /* Careful to not accidentally "save"
5750 * to the same fd as right side fd in N>&M */ 5779 * to the same fd as right side fd in N>&M */
5751 int minfd = right_fd < 10 ? 10 : right_fd + 1; 5780 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5781#if defined(F_DUPFD_CLOEXEC)
5782 i = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
5783#else
5752 i = fcntl(fd, F_DUPFD, minfd); 5784 i = fcntl(fd, F_DUPFD, minfd);
5753/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds 5785#endif
5754 * are closed in popredir() in the child, preventing them from leaking
5755 * into child. (popredir() also cleans up the mess in case of failures)
5756 */
5757 if (i == -1) { 5786 if (i == -1) {
5758 i = errno; 5787 i = errno;
5759 if (i != EBADF) { 5788 if (i != EBADF) {
@@ -5768,6 +5797,9 @@ redirect(union node *redir, int flags)
5768 remember_to_close: 5797 remember_to_close:
5769 i = CLOSED; 5798 i = CLOSED;
5770 } else { /* fd is open, save its copy */ 5799 } else { /* fd is open, save its copy */
5800#if !defined(F_DUPFD_CLOEXEC)
5801 fcntl(i, F_SETFD, FD_CLOEXEC);
5802#endif
5771 /* "exec fd>&-" should not close fds 5803 /* "exec fd>&-" should not close fds
5772 * which point to script file(s). 5804 * which point to script file(s).
5773 * Force them to be restored afterwards */ 5805 * Force them to be restored afterwards */
@@ -5878,7 +5910,7 @@ redirectsafe(union node *redir, int flags)
5878 * We have to deal with backquotes, shell variables, and file metacharacters. 5910 * We have to deal with backquotes, shell variables, and file metacharacters.
5879 */ 5911 */
5880 5912
5881#if ENABLE_SH_MATH_SUPPORT 5913#if ENABLE_FEATURE_SH_MATH
5882static arith_t 5914static arith_t
5883ash_arith(const char *s) 5915ash_arith(const char *s)
5884{ 5916{
@@ -5966,7 +5998,7 @@ static struct arglist exparg;
5966/* 5998/*
5967 * Our own itoa(). 5999 * Our own itoa().
5968 */ 6000 */
5969#if !ENABLE_SH_MATH_SUPPORT 6001#if !ENABLE_FEATURE_SH_MATH
5970/* cvtnum() is used even if math support is off (to prepare $? values and such) */ 6002/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5971typedef long arith_t; 6003typedef long arith_t;
5972# define ARITH_FMT "%ld" 6004# define ARITH_FMT "%ld"
@@ -6220,14 +6252,15 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
6220 do { 6252 do {
6221 unsigned char c = *p++; 6253 unsigned char c = *p++;
6222 if (c) { 6254 if (c) {
6223 int n = SIT(c, syntax); 6255 if (quotes & QUOTES_ESC) {
6224 if ((quotes & QUOTES_ESC) 6256 int n = SIT(c, syntax);
6225 && ((n == CCTL) 6257 if (n == CCTL
6226 || (((quotes & EXP_FULL) || syntax != BASESYNTAX) 6258 || (((quotes & EXP_FULL) || syntax != BASESYNTAX)
6227 && n == CBACK) 6259 && n == CBACK
6228 ) 6260 )
6229 ) { 6261 ) {
6230 USTPUTC(CTLESC, q); 6262 USTPUTC(CTLESC, q);
6263 }
6231 } 6264 }
6232 } else if (!(quotes & QUOTES_KEEPNUL)) 6265 } else if (!(quotes & QUOTES_KEEPNUL))
6233 continue; 6266 continue;
@@ -6490,7 +6523,7 @@ expbackq(union node *cmd, int flag)
6490 stackblock() + startloc)); 6523 stackblock() + startloc));
6491} 6524}
6492 6525
6493#if ENABLE_SH_MATH_SUPPORT 6526#if ENABLE_FEATURE_SH_MATH
6494/* 6527/*
6495 * Expand arithmetic expression. Backup to start of expression, 6528 * Expand arithmetic expression. Backup to start of expression,
6496 * evaluate, place result in (backed up) result, adjust string position. 6529 * evaluate, place result in (backed up) result, adjust string position.
@@ -6572,7 +6605,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6572 CTLESC, 6605 CTLESC,
6573 CTLVAR, 6606 CTLVAR,
6574 CTLBACKQ, 6607 CTLBACKQ,
6575#if ENABLE_SH_MATH_SUPPORT 6608#if ENABLE_FEATURE_SH_MATH
6576 CTLENDARI, 6609 CTLENDARI,
6577#endif 6610#endif
6578 '\0' 6611 '\0'
@@ -6608,7 +6641,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6608 c = p[length]; 6641 c = p[length];
6609 if (c) { 6642 if (c) {
6610 if (!(c & 0x80) 6643 if (!(c & 0x80)
6611 IF_SH_MATH_SUPPORT(|| c == CTLENDARI) 6644 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6612 ) { 6645 ) {
6613 /* c == '=' || c == ':' || c == CTLENDARI */ 6646 /* c == '=' || c == ':' || c == CTLENDARI */
6614 length++; 6647 length++;
@@ -6688,7 +6721,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6688 expbackq(argbackq->n, flags | inquotes); 6721 expbackq(argbackq->n, flags | inquotes);
6689 argbackq = argbackq->next; 6722 argbackq = argbackq->next;
6690 goto start; 6723 goto start;
6691#if ENABLE_SH_MATH_SUPPORT 6724#if ENABLE_FEATURE_SH_MATH
6692 case CTLENDARI: 6725 case CTLENDARI:
6693 p--; 6726 p--;
6694 expari(flags | inquotes); 6727 expari(flags | inquotes);
@@ -9686,7 +9719,7 @@ static int helpcmd(int, char **) FAST_FUNC;
9686#if MAX_HISTORY 9719#if MAX_HISTORY
9687static int historycmd(int, char **) FAST_FUNC; 9720static int historycmd(int, char **) FAST_FUNC;
9688#endif 9721#endif
9689#if ENABLE_SH_MATH_SUPPORT 9722#if ENABLE_FEATURE_SH_MATH
9690static int letcmd(int, char **) FAST_FUNC; 9723static int letcmd(int, char **) FAST_FUNC;
9691#endif 9724#endif
9692static int readcmd(int, char **) FAST_FUNC; 9725static int readcmd(int, char **) FAST_FUNC;
@@ -9766,7 +9799,7 @@ static const struct builtincmd builtintab[] = {
9766 { BUILTIN_REGULAR "jobs" , jobscmd }, 9799 { BUILTIN_REGULAR "jobs" , jobscmd },
9767 { BUILTIN_REGULAR "kill" , killcmd }, 9800 { BUILTIN_REGULAR "kill" , killcmd },
9768#endif 9801#endif
9769#if ENABLE_SH_MATH_SUPPORT 9802#if ENABLE_FEATURE_SH_MATH
9770 { BUILTIN_NOSPEC "let" , letcmd }, 9803 { BUILTIN_NOSPEC "let" , letcmd },
9771#endif 9804#endif
9772 { BUILTIN_ASSIGN "local" , localcmd }, 9805 { BUILTIN_ASSIGN "local" , localcmd },
@@ -10004,11 +10037,13 @@ evalcommand(union node *cmd, int flags)
10004 } 10037 }
10005 10038
10006 if (status) { 10039 if (status) {
10040 bail:
10041 exitstatus = status;
10042
10007 /* We have a redirection error. */ 10043 /* We have a redirection error. */
10008 if (spclbltin > 0) 10044 if (spclbltin > 0)
10009 raise_exception(EXERROR); 10045 raise_exception(EXERROR);
10010 bail: 10046
10011 exitstatus = status;
10012 goto out; 10047 goto out;
10013 } 10048 }
10014 10049
@@ -10516,7 +10551,7 @@ pgetc(void)
10516 return g_parsefile->lastc[--g_parsefile->unget]; 10551 return g_parsefile->lastc[--g_parsefile->unget];
10517 10552
10518 if (--g_parsefile->left_in_line >= 0) 10553 if (--g_parsefile->left_in_line >= 0)
10519 c = (signed char)*g_parsefile->next_to_pgetc++; 10554 c = (unsigned char)*g_parsefile->next_to_pgetc++;
10520 else 10555 else
10521 c = preadbuffer(); 10556 c = preadbuffer();
10522 10557
@@ -11840,13 +11875,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11840 smallint quotef; 11875 smallint quotef;
11841 smallint dblquote; 11876 smallint dblquote;
11842 smallint oldstyle; 11877 smallint oldstyle;
11843 smallint prevsyntax; /* syntax before arithmetic */ 11878 IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */
11844#if ENABLE_ASH_EXPAND_PRMT 11879#if ENABLE_ASH_EXPAND_PRMT
11845 smallint pssyntax; /* we are expanding a prompt string */ 11880 smallint pssyntax; /* we are expanding a prompt string */
11846#endif 11881#endif
11847 int varnest; /* levels of variables expansion */ 11882 int varnest; /* levels of variables expansion */
11848 int arinest; /* levels of arithmetic expansion */ 11883 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
11849 int parenlevel; /* levels of parens in arithmetic */ 11884 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
11850 int dqvarnest; /* levels of variables expansion within double quotes */ 11885 int dqvarnest; /* levels of variables expansion within double quotes */
11851 11886
11852 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) 11887 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
@@ -11854,7 +11889,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11854 startlinno = g_parsefile->linno; 11889 startlinno = g_parsefile->linno;
11855 bqlist = NULL; 11890 bqlist = NULL;
11856 quotef = 0; 11891 quotef = 0;
11857 prevsyntax = 0; 11892 IF_FEATURE_SH_MATH(prevsyntax = 0;)
11858#if ENABLE_ASH_EXPAND_PRMT 11893#if ENABLE_ASH_EXPAND_PRMT
11859 pssyntax = (syntax == PSSYNTAX); 11894 pssyntax = (syntax == PSSYNTAX);
11860 if (pssyntax) 11895 if (pssyntax)
@@ -11862,8 +11897,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11862#endif 11897#endif
11863 dblquote = (syntax == DQSYNTAX); 11898 dblquote = (syntax == DQSYNTAX);
11864 varnest = 0; 11899 varnest = 0;
11865 arinest = 0; 11900 IF_FEATURE_SH_MATH(arinest = 0;)
11866 parenlevel = 0; 11901 IF_FEATURE_SH_MATH(parenlevel = 0;)
11867 dqvarnest = 0; 11902 dqvarnest = 0;
11868 11903
11869 STARTSTACKSTR(out); 11904 STARTSTACKSTR(out);
@@ -11970,7 +12005,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11970 } 12005 }
11971 USTPUTC(c, out); 12006 USTPUTC(c, out);
11972 break; 12007 break;
11973#if ENABLE_SH_MATH_SUPPORT 12008#if ENABLE_FEATURE_SH_MATH
11974 case CLP: /* '(' in arithmetic */ 12009 case CLP: /* '(' in arithmetic */
11975 parenlevel++; 12010 parenlevel++;
11976 USTPUTC(c, out); 12011 USTPUTC(c, out);
@@ -12021,7 +12056,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12021 } /* for (;;) */ 12056 } /* for (;;) */
12022 endword: 12057 endword:
12023 12058
12024#if ENABLE_SH_MATH_SUPPORT 12059#if ENABLE_FEATURE_SH_MATH
12025 if (syntax == ARISYNTAX) 12060 if (syntax == ARISYNTAX)
12026 raise_error_syntax("missing '))'"); 12061 raise_error_syntax("missing '))'");
12027#endif 12062#endif
@@ -12200,7 +12235,7 @@ parsesub: {
12200 } else if (c == '(') { 12235 } else if (c == '(') {
12201 /* $(command) or $((arith)) */ 12236 /* $(command) or $((arith)) */
12202 if (pgetc_eatbnl() == '(') { 12237 if (pgetc_eatbnl() == '(') {
12203#if ENABLE_SH_MATH_SUPPORT 12238#if ENABLE_FEATURE_SH_MATH
12204 PARSEARITH(); 12239 PARSEARITH();
12205#else 12240#else
12206 raise_error_syntax("you disabled math support for $((arith)) syntax"); 12241 raise_error_syntax("you disabled math support for $((arith)) syntax");
@@ -12455,7 +12490,7 @@ parsebackq: {
12455 goto parsebackq_newreturn; 12490 goto parsebackq_newreturn;
12456} 12491}
12457 12492
12458#if ENABLE_SH_MATH_SUPPORT 12493#if ENABLE_FEATURE_SH_MATH
12459/* 12494/*
12460 * Parse an arithmetic expansion (indicate start of one and set state) 12495 * Parse an arithmetic expansion (indicate start of one and set state)
12461 */ 12496 */
@@ -13532,7 +13567,7 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13532 return 0; 13567 return 0;
13533} 13568}
13534 13569
13535#if ENABLE_SH_MATH_SUPPORT 13570#if ENABLE_FEATURE_SH_MATH
13536/* 13571/*
13537 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. 13572 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
13538 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. 13573 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
@@ -13871,15 +13906,6 @@ init(void)
13871//usage:#define ash_full_usage "\n\n" 13906//usage:#define ash_full_usage "\n\n"
13872//usage: "Unix shell interpreter" 13907//usage: "Unix shell interpreter"
13873 13908
13874//usage:#if ENABLE_FEATURE_SH_IS_ASH
13875//usage:# define sh_trivial_usage ash_trivial_usage
13876//usage:# define sh_full_usage ash_full_usage
13877//usage:#endif
13878//usage:#if ENABLE_FEATURE_BASH_IS_ASH
13879//usage:# define bash_trivial_usage ash_trivial_usage
13880//usage:# define bash_full_usage ash_full_usage
13881//usage:#endif
13882
13883/* 13909/*
13884 * Process the shell command line arguments. 13910 * Process the shell command line arguments.
13885 */ 13911 */
diff --git a/shell/ash_test/ash-misc/exitcode2.right b/shell/ash_test/ash-misc/exitcode2.right
new file mode 100644
index 000000000..f7cb983c6
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode2.right
@@ -0,0 +1,4 @@
1./test.sh: line 1: syntax error: unexpected ")"
2Done:2
3./exitcode2.tests: line 11: can't open does_not_exist: no such file
4Done:1
diff --git a/shell/ash_test/ash-misc/exitcode2.tests b/shell/ash_test/ash-misc/exitcode2.tests
new file mode 100755
index 000000000..79a6ebd50
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode2.tests
@@ -0,0 +1,12 @@
1# syntax error should return status 2
2cat >test.sh <<EOF
3)
4EOF
5chmod +x test.sh
6$THIS_SH ./test.sh
7echo Done:$?
8rm -f test.sh
9
10# redirection error with special builtin should return status 1
11(eval cat <does_not_exist)
12echo Done:$?
diff --git a/shell/ash_test/ash-redir/redir_leak.right b/shell/ash_test/ash-redir/redir_leak.right
new file mode 100644
index 000000000..b1c48292b
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_leak.right
@@ -0,0 +1,6 @@
14
24
34
44
54
64
diff --git a/shell/ash_test/ash-redir/redir_leak.tests b/shell/ash_test/ash-redir/redir_leak.tests
new file mode 100755
index 000000000..c8a9c6343
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_leak.tests
@@ -0,0 +1,10 @@
1# Each of these should show only four lines:
2# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls.
3# This test detects bugs where redirects leave stray open fds.
4
5ls -1 /proc/self/fd | wc -l
6ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l
7ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l
8echo "`ls -1 /proc/self/fd `" | wc -l
9echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l
10echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l
diff --git a/shell/hush.c b/shell/hush.c
index bcd4dffee..a56d3b280 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -82,35 +82,6 @@
82 * $ "export" i=`echo 'aaa bbb'`; echo "$i" 82 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
83 * aaa 83 * aaa
84 */ 84 */
85#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
86 || defined(__APPLE__) \
87 )
88# include <malloc.h> /* for malloc_trim */
89#endif
90#include <glob.h>
91/* #include <dmalloc.h> */
92#if ENABLE_HUSH_CASE
93# include <fnmatch.h>
94#endif
95#include <sys/utsname.h> /* for setting $HOSTNAME */
96
97#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
98#include "unicode.h"
99#include "shell_common.h"
100#include "math.h"
101#include "match.h"
102#if ENABLE_HUSH_RANDOM_SUPPORT
103# include "random.h"
104#else
105# define CLEAR_RANDOM_T(rnd) ((void)0)
106#endif
107#ifndef F_DUPFD_CLOEXEC
108# define F_DUPFD_CLOEXEC F_DUPFD
109#endif
110#ifndef PIPE_BUF
111# define PIPE_BUF 4096 /* amount of buffering in a pipe */
112#endif
113
114//config:config HUSH 85//config:config HUSH
115//config: bool "hush" 86//config: bool "hush"
116//config: default y 87//config: default y
@@ -128,7 +99,7 @@
128//config:config HUSH_BASH_COMPAT 99//config:config HUSH_BASH_COMPAT
129//config: bool "bash-compatible extensions" 100//config: bool "bash-compatible extensions"
130//config: default y 101//config: default y
131//config: depends on HUSH 102//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
132//config: help 103//config: help
133//config: Enable bash-compatible extensions. 104//config: Enable bash-compatible extensions.
134//config: 105//config:
@@ -142,14 +113,14 @@
142//config:config HUSH_HELP 113//config:config HUSH_HELP
143//config: bool "help builtin" 114//config: bool "help builtin"
144//config: default y 115//config: default y
145//config: depends on HUSH 116//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
146//config: help 117//config: help
147//config: Enable help builtin in hush. Code size + ~1 kbyte. 118//config: Enable help builtin in hush. Code size + ~1 kbyte.
148//config: 119//config:
149//config:config HUSH_INTERACTIVE 120//config:config HUSH_INTERACTIVE
150//config: bool "Interactive mode" 121//config: bool "Interactive mode"
151//config: default y 122//config: default y
152//config: depends on HUSH 123//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
153//config: help 124//config: help
154//config: Enable interactive mode (prompt and command editing). 125//config: Enable interactive mode (prompt and command editing).
155//config: Without this, hush simply reads and executes commands 126//config: Without this, hush simply reads and executes commands
@@ -177,35 +148,35 @@
177//config:config HUSH_TICK 148//config:config HUSH_TICK
178//config: bool "Process substitution" 149//config: bool "Process substitution"
179//config: default y 150//config: default y
180//config: depends on HUSH 151//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
181//config: help 152//config: help
182//config: Enable process substitution `command` and $(command) in hush. 153//config: Enable process substitution `command` and $(command) in hush.
183//config: 154//config:
184//config:config HUSH_IF 155//config:config HUSH_IF
185//config: bool "Support if/then/elif/else/fi" 156//config: bool "Support if/then/elif/else/fi"
186//config: default y 157//config: default y
187//config: depends on HUSH 158//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
188//config: help 159//config: help
189//config: Enable if/then/elif/else/fi in hush. 160//config: Enable if/then/elif/else/fi in hush.
190//config: 161//config:
191//config:config HUSH_LOOPS 162//config:config HUSH_LOOPS
192//config: bool "Support for, while and until loops" 163//config: bool "Support for, while and until loops"
193//config: default y 164//config: default y
194//config: depends on HUSH 165//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
195//config: help 166//config: help
196//config: Enable for, while and until loops in hush. 167//config: Enable for, while and until loops in hush.
197//config: 168//config:
198//config:config HUSH_CASE 169//config:config HUSH_CASE
199//config: bool "Support case ... esac statement" 170//config: bool "Support case ... esac statement"
200//config: default y 171//config: default y
201//config: depends on HUSH 172//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
202//config: help 173//config: help
203//config: Enable case ... esac statement in hush. +400 bytes. 174//config: Enable case ... esac statement in hush. +400 bytes.
204//config: 175//config:
205//config:config HUSH_FUNCTIONS 176//config:config HUSH_FUNCTIONS
206//config: bool "Support funcname() { commands; } syntax" 177//config: bool "Support funcname() { commands; } syntax"
207//config: default y 178//config: default y
208//config: depends on HUSH 179//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
209//config: help 180//config: help
210//config: Enable support for shell functions in hush. +800 bytes. 181//config: Enable support for shell functions in hush. +800 bytes.
211//config: 182//config:
@@ -219,7 +190,7 @@
219//config:config HUSH_RANDOM_SUPPORT 190//config:config HUSH_RANDOM_SUPPORT
220//config: bool "Pseudorandom generator and $RANDOM variable" 191//config: bool "Pseudorandom generator and $RANDOM variable"
221//config: default y 192//config: default y
222//config: depends on HUSH 193//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
223//config: help 194//config: help
224//config: Enable pseudorandom generator and dynamic variable "$RANDOM". 195//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
225//config: Each read of "$RANDOM" will generate a new pseudorandom value. 196//config: Each read of "$RANDOM" will generate a new pseudorandom value.
@@ -227,14 +198,14 @@
227//config:config HUSH_EXPORT_N 198//config:config HUSH_EXPORT_N
228//config: bool "Support 'export -n' option" 199//config: bool "Support 'export -n' option"
229//config: default y 200//config: default y
230//config: depends on HUSH 201//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
231//config: help 202//config: help
232//config: export -n unexports variables. It is a bash extension. 203//config: export -n unexports variables. It is a bash extension.
233//config: 204//config:
234//config:config HUSH_MODE_X 205//config:config HUSH_MODE_X
235//config: bool "Support 'hush -x' option and 'set -x' command" 206//config: bool "Support 'hush -x' option and 'set -x' command"
236//config: default y 207//config: default y
237//config: depends on HUSH 208//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
238//config: help 209//config: help
239//config: This instructs hush to print commands before execution. 210//config: This instructs hush to print commands before execution.
240//config: Adds ~300 bytes. 211//config: Adds ~300 bytes.
@@ -245,14 +216,15 @@
245//config: select HUSH 216//config: select HUSH
246//config: help 217//config: help
247//config: msh is deprecated and will be removed, please migrate to hush. 218//config: msh is deprecated and will be removed, please migrate to hush.
248//config:
249 219
250//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) 220//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
251//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP)) 221//applet:IF_MSH(APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
252//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh)) 222//applet:IF_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
253//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash)) 223//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
254 224
255//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o 225//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
226//kbuild:lib-$(CONFIG_SH_IS_HUSH) += hush.o match.o shell_common.o
227//kbuild:lib-$(CONFIG_BASH_IS_HUSH) += hush.o match.o shell_common.o
256//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o 228//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
257 229
258/* -i (interactive) and -s (read stdin) are also accepted, 230/* -i (interactive) and -s (read stdin) are also accepted,
@@ -265,17 +237,34 @@
265//usage:#define hush_full_usage "\n\n" 237//usage:#define hush_full_usage "\n\n"
266//usage: "Unix shell interpreter" 238//usage: "Unix shell interpreter"
267 239
268//usage:#define msh_trivial_usage hush_trivial_usage 240#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
269//usage:#define msh_full_usage hush_full_usage 241 || defined(__APPLE__) \
242 )
243# include <malloc.h> /* for malloc_trim */
244#endif
245#include <glob.h>
246/* #include <dmalloc.h> */
247#if ENABLE_HUSH_CASE
248# include <fnmatch.h>
249#endif
250#include <sys/utsname.h> /* for setting $HOSTNAME */
270 251
271//usage:#if ENABLE_FEATURE_SH_IS_HUSH 252#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
272//usage:# define sh_trivial_usage hush_trivial_usage 253#include "unicode.h"
273//usage:# define sh_full_usage hush_full_usage 254#include "shell_common.h"
274//usage:#endif 255#include "math.h"
275//usage:#if ENABLE_FEATURE_BASH_IS_HUSH 256#include "match.h"
276//usage:# define bash_trivial_usage hush_trivial_usage 257#if ENABLE_HUSH_RANDOM_SUPPORT
277//usage:# define bash_full_usage hush_full_usage 258# include "random.h"
278//usage:#endif 259#else
260# define CLEAR_RANDOM_T(rnd) ((void)0)
261#endif
262#ifndef F_DUPFD_CLOEXEC
263# define F_DUPFD_CLOEXEC F_DUPFD
264#endif
265#ifndef PIPE_BUF
266# define PIPE_BUF 4096 /* amount of buffering in a pipe */
267#endif
279 268
280 269
281/* Build knobs */ 270/* Build knobs */
@@ -1148,6 +1137,9 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1148 char msg[2]; 1137 char msg[2];
1149 msg[0] = ch; 1138 msg[0] = ch;
1150 msg[1] = '\0'; 1139 msg[1] = '\0';
1140#if HUSH_DEBUG >= 2
1141 bb_error_msg("hush.c:%u", lineno);
1142#endif
1151 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); 1143 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1152} 1144}
1153 1145
@@ -1574,9 +1566,8 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
1574} 1566}
1575 1567
1576static void hush_exit(int exitcode) NORETURN; 1568static void hush_exit(int exitcode) NORETURN;
1577static void fflush_and__exit(void) NORETURN;
1578static void restore_ttypgrp_and__exit(void) NORETURN;
1579 1569
1570static void restore_ttypgrp_and__exit(void) NORETURN;
1580static void restore_ttypgrp_and__exit(void) 1571static void restore_ttypgrp_and__exit(void)
1581{ 1572{
1582 /* xfunc has failed! die die die */ 1573 /* xfunc has failed! die die die */
@@ -1585,6 +1576,8 @@ static void restore_ttypgrp_and__exit(void)
1585 hush_exit(xfunc_error_retval); 1576 hush_exit(xfunc_error_retval);
1586} 1577}
1587 1578
1579#if ENABLE_HUSH_JOB
1580
1588/* Needed only on some libc: 1581/* Needed only on some libc:
1589 * It was observed that on exit(), fgetc'ed buffered data 1582 * It was observed that on exit(), fgetc'ed buffered data
1590 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 1583 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -1598,14 +1591,13 @@ static void restore_ttypgrp_and__exit(void)
1598 * and in `cmd` handling. 1591 * and in `cmd` handling.
1599 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 1592 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
1600 */ 1593 */
1594static void fflush_and__exit(void) NORETURN;
1601static void fflush_and__exit(void) 1595static void fflush_and__exit(void)
1602{ 1596{
1603 fflush_all(); 1597 fflush_all();
1604 _exit(xfunc_error_retval); 1598 _exit(xfunc_error_retval);
1605} 1599}
1606 1600
1607#if ENABLE_HUSH_JOB
1608
1609/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 1601/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
1610# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit) 1602# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
1611/* After [v]fork, in parent: restore tty pgrp on xfunc death */ 1603/* After [v]fork, in parent: restore tty pgrp on xfunc death */
@@ -4011,7 +4003,7 @@ static int i_peek_and_eat_bkslash_nl(struct in_str *input)
4011 } 4003 }
4012} 4004}
4013 4005
4014#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS 4006#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
4015/* Subroutines for copying $(...) and `...` things */ 4007/* Subroutines for copying $(...) and `...` things */
4016static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); 4008static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
4017/* '...' */ 4009/* '...' */
@@ -4179,7 +4171,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4179 } 4171 }
4180 return ch; 4172 return ch;
4181} 4173}
4182#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */ 4174#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
4183 4175
4184/* Return code: 0 for OK, 1 for syntax error */ 4176/* Return code: 0 for OK, 1 for syntax error */
4185#if BB_MMU 4177#if BB_MMU
@@ -4333,13 +4325,13 @@ static int parse_dollar(o_string *as_string,
4333 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4325 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4334 break; 4326 break;
4335 } 4327 }
4336#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK 4328#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
4337 case '(': { 4329 case '(': {
4338 unsigned pos; 4330 unsigned pos;
4339 4331
4340 ch = i_getch(input); 4332 ch = i_getch(input);
4341 nommu_addchr(as_string, ch); 4333 nommu_addchr(as_string, ch);
4342# if ENABLE_SH_MATH_SUPPORT 4334# if ENABLE_FEATURE_SH_MATH
4343 if (i_peek_and_eat_bkslash_nl(input) == '(') { 4335 if (i_peek_and_eat_bkslash_nl(input) == '(') {
4344 ch = i_getch(input); 4336 ch = i_getch(input);
4345 nommu_addchr(as_string, ch); 4337 nommu_addchr(as_string, ch);
@@ -5008,7 +5000,8 @@ static struct pipe *parse_stream(char **pstring,
5008 * if we see {, we call parse_group(..., end_trigger='}') 5000 * if we see {, we call parse_group(..., end_trigger='}')
5009 * and it will match } earlier (not here). */ 5001 * and it will match } earlier (not here). */
5010 syntax_error_unexpected_ch(ch); 5002 syntax_error_unexpected_ch(ch);
5011 goto parse_error; 5003 G.last_exitcode = 2;
5004 goto parse_error1;
5012 default: 5005 default:
5013 if (HUSH_DEBUG) 5006 if (HUSH_DEBUG)
5014 bb_error_msg_and_die("BUG: unexpected %c\n", ch); 5007 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
@@ -5016,6 +5009,8 @@ static struct pipe *parse_stream(char **pstring,
5016 } /* while (1) */ 5009 } /* while (1) */
5017 5010
5018 parse_error: 5011 parse_error:
5012 G.last_exitcode = 1;
5013 parse_error1:
5019 { 5014 {
5020 struct parse_context *pctx; 5015 struct parse_context *pctx;
5021 IF_HAS_KEYWORDS(struct parse_context *p2;) 5016 IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -5049,7 +5044,6 @@ static struct pipe *parse_stream(char **pstring,
5049 } while (HAS_KEYWORDS && pctx); 5044 } while (HAS_KEYWORDS && pctx);
5050 5045
5051 o_free(&dest); 5046 o_free(&dest);
5052 G.last_exitcode = 1;
5053#if !BB_MMU 5047#if !BB_MMU
5054 if (pstring) 5048 if (pstring)
5055 *pstring = NULL; 5049 *pstring = NULL;
@@ -5217,7 +5211,7 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int
5217 return exp_str; 5211 return exp_str;
5218} 5212}
5219 5213
5220#if ENABLE_SH_MATH_SUPPORT 5214#if ENABLE_FEATURE_SH_MATH
5221static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) 5215static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5222{ 5216{
5223 arith_state_t math_state; 5217 arith_state_t math_state;
@@ -5469,7 +5463,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5469 } 5463 }
5470#endif 5464#endif
5471 else if (exp_op == ':') { 5465 else if (exp_op == ':') {
5472#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT 5466#if ENABLE_HUSH_BASH_COMPAT && ENABLE_FEATURE_SH_MATH
5473 /* It's ${var:N[:M]} bashism. 5467 /* It's ${var:N[:M]} bashism.
5474 * Note that in encoded form it has TWO parts: 5468 * Note that in encoded form it has TWO parts:
5475 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 5469 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -5604,7 +5598,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
5604#if ENABLE_HUSH_TICK 5598#if ENABLE_HUSH_TICK
5605 o_string subst_result = NULL_O_STRING; 5599 o_string subst_result = NULL_O_STRING;
5606#endif 5600#endif
5607#if ENABLE_SH_MATH_SUPPORT 5601#if ENABLE_FEATURE_SH_MATH
5608 char arith_buf[sizeof(arith_t)*3 + 2]; 5602 char arith_buf[sizeof(arith_t)*3 + 2];
5609#endif 5603#endif
5610 5604
@@ -5698,7 +5692,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
5698 val = subst_result.data; 5692 val = subst_result.data;
5699 goto store_val; 5693 goto store_val;
5700#endif 5694#endif
5701#if ENABLE_SH_MATH_SUPPORT 5695#if ENABLE_FEATURE_SH_MATH
5702 case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ 5696 case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
5703 arith_t res; 5697 arith_t res;
5704 5698
diff --git a/shell/hush_test/hush-misc/exitcode2.right b/shell/hush_test/hush-misc/exitcode2.right
new file mode 100644
index 000000000..0a57b9b1b
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode2.right
@@ -0,0 +1,4 @@
1hush: syntax error: unexpected )
2Done:2
3hush: can't open 'does_not_exist': No such file or directory
4Done:1
diff --git a/shell/hush_test/hush-misc/exitcode2.tests b/shell/hush_test/hush-misc/exitcode2.tests
new file mode 100755
index 000000000..79a6ebd50
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode2.tests
@@ -0,0 +1,12 @@
1# syntax error should return status 2
2cat >test.sh <<EOF
3)
4EOF
5chmod +x test.sh
6$THIS_SH ./test.sh
7echo Done:$?
8rm -f test.sh
9
10# redirection error with special builtin should return status 1
11(eval cat <does_not_exist)
12echo Done:$?
diff --git a/shell/hush_test/hush-redir/redir_leak.right b/shell/hush_test/hush-redir/redir_leak.right
new file mode 100644
index 000000000..b1c48292b
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_leak.right
@@ -0,0 +1,6 @@
14
24
34
44
54
64
diff --git a/shell/hush_test/hush-redir/redir_leak.tests b/shell/hush_test/hush-redir/redir_leak.tests
new file mode 100755
index 000000000..c8a9c6343
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_leak.tests
@@ -0,0 +1,10 @@
1# Each of these should show only four lines:
2# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls.
3# This test detects bugs where redirects leave stray open fds.
4
5ls -1 /proc/self/fd | wc -l
6ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l
7ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l
8echo "`ls -1 /proc/self/fd `" | wc -l
9echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l
10echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l
diff --git a/shell/math.h b/shell/math.h
index 864bee691..32e1ffe35 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -63,7 +63,7 @@
63 63
64PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 64PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
65 65
66#if ENABLE_SH_MATH_SUPPORT_64 66#if ENABLE_FEATURE_SH_MATH_64
67typedef long long arith_t; 67typedef long long arith_t;
68#define ARITH_FMT "%lld" 68#define ARITH_FMT "%lld"
69#define strto_arith_t strtoull 69#define strto_arith_t strtoull