aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-02-08 20:09:29 +0000
committerRon Yorston <rmy@pobox.com>2017-02-08 20:09:29 +0000
commit373275a708bafb88fa4f0519de2166154f44fed9 (patch)
tree4587b4fd3f695e0f3705b2a217e199f3144df931 /shell
parentb74b2619779b1deb903b7766261807df1e9b1f7f (diff)
parentc2b18583a3df06aeecf535c3cea6856aa1f2e205 (diff)
downloadbusybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.tar.gz
busybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.tar.bz2
busybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/Config.src22
-rw-r--r--shell/ash.c465
-rw-r--r--shell/ash_test/ash-misc/source_argv_and_shift.right4
-rwxr-xr-xshell/ash_test/ash-misc/source_argv_and_shift.tests12
-rw-r--r--shell/hush.c1092
-rw-r--r--shell/hush_test/hush-misc/source_argv_and_shift.right4
-rwxr-xr-xshell/hush_test/hush-misc/source_argv_and_shift.tests12
-rw-r--r--shell/shell_common.c2
8 files changed, 1010 insertions, 603 deletions
diff --git a/shell/Config.src b/shell/Config.src
index 3545f05dd..ccb1b15fe 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -121,23 +121,11 @@ config FEATURE_SH_STANDALONE
121 for use as a rescue shell, in the event that you screw up your system. 121 for use as a rescue shell, in the event that you screw up your system.
122 122
123 This is implemented by re-execing /proc/self/exe (typically) 123 This is implemented by re-execing /proc/self/exe (typically)
124 with right parameters. Some selected applets ("NOFORK" applets) 124 with right parameters.
125 can even be executed without creating new process. 125
126 Instead, busybox will call <applet>_main() internally. 126 However, there are drawbacks: it is problematic in chroot jails
127 127 without mounted /proc, and ps/top may show command name as 'exe'
128 However, this causes problems in chroot jails without mounted /proc 128 for applets started this way.
129 and with ps/top (command name can be shown as 'exe' for applets
130 started this way).
131# untrue?
132# Note that this will *also* cause applets to take precedence
133# over shell builtins of the same name. So turning this on will
134# eliminate any performance gained by turning on the builtin "echo"
135# and "test" commands in ash.
136# untrue?
137# Note that when using this option, the shell will attempt to directly
138# run '/bin/busybox'. If you do not have the busybox binary sitting in
139# that exact location with that exact name, this option will not work at
140# all.
141 129
142config FEATURE_SH_NOFORK 130config FEATURE_SH_NOFORK
143 bool "Run 'nofork' applets directly" 131 bool "Run 'nofork' applets directly"
diff --git a/shell/ash.c b/shell/ash.c
index e21c4433d..0325a325c 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -50,8 +50,6 @@
50//config: bool "Optimize for size instead of speed" 50//config: bool "Optimize for size instead of speed"
51//config: default y 51//config: default y
52//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 52//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
53//config: help
54//config: Compile ash for reduced size at the price of speed.
55//config: 53//config:
56//config:config ASH_INTERNAL_GLOB 54//config:config ASH_INTERNAL_GLOB
57//config: bool "Use internal glob() implementation" 55//config: bool "Use internal glob() implementation"
@@ -61,6 +59,23 @@
61//config: Do not use glob() function from libc, use internal implementation. 59//config: Do not use glob() function from libc, use internal implementation.
62//config: Use this if you are getting "glob.h: No such file or directory" 60//config: Use this if you are getting "glob.h: No such file or directory"
63//config: or similar build errors. 61//config: or similar build errors.
62//config: Note that as of now (2017-01), uclibc and musl glob() both have bugs
63//config: which would break ash if you select N here.
64//config:
65//config:config ASH_BASH_COMPAT
66//config: bool "bash-compatible extensions"
67//config: default y
68//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
69//config:
70//config:config ASH_JOB_CONTROL
71//config: bool "Job control"
72//config: default y
73//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
74//config:
75//config:config ASH_ALIAS
76//config: bool "Alias support"
77//config: default y
78//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
64//config: 79//config:
65//config:config ASH_RANDOM_SUPPORT 80//config:config ASH_RANDOM_SUPPORT
66//config: bool "Pseudorandom generator and $RANDOM variable" 81//config: bool "Pseudorandom generator and $RANDOM variable"
@@ -78,88 +93,60 @@
78//config: default y 93//config: default y
79//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 94//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
80//config: help 95//config: help
81//config: "PS#" may contain volatile content, such as backquote commands. 96//config: $PS# may contain volatile content, such as backquote commands.
82//config: This option recreates the prompt string from the environment 97//config: This option recreates the prompt string from the environment
83//config: variable each time it is displayed. 98//config: variable each time it is displayed.
84//config: 99//config:
85//config:config ASH_BASH_COMPAT
86//config: bool "bash-compatible extensions"
87//config: default y
88//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
89//config: help
90//config: Enable bash-compatible extensions.
91//config:
92//config:config ASH_IDLE_TIMEOUT 100//config:config ASH_IDLE_TIMEOUT
93//config: bool "Idle timeout variable" 101//config: bool "Idle timeout variable $TMOUT"
94//config: default n
95//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
96//config: help
97//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
98//config:
99//config:config ASH_JOB_CONTROL
100//config: bool "Job control"
101//config: default y
102//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
103//config: help
104//config: Enable job control in the ash shell.
105//config:
106//config:config ASH_ALIAS
107//config: bool "Alias support"
108//config: default y 102//config: default y
109//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 103//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
110//config: help 104//config: help
111//config: Enable alias support in the ash shell. 105//config: Enable bash-like auto-logout after $TMOUT seconds of idle time.
112//config: 106//config:
113//config:config ASH_GETOPTS 107//config:config ASH_MAIL
114//config: bool "Builtin getopt to parse positional parameters" 108//config: bool "Check for new mail in interactive shell"
115//config: default y 109//config: default y
116//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 110//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
117//config: help 111//config: help
118//config: Enable support for getopts builtin in ash. 112//config: Enable "check for new mail" function:
113//config: if set, $MAIL file and $MAILPATH list of files
114//config: are checked for mtime changes, and "you have mail"
115//config: message is printed if change is detected.
119//config: 116//config:
120//config:config ASH_BUILTIN_ECHO 117//config:config ASH_ECHO
121//config: bool "Builtin version of 'echo'" 118//config: bool "echo builtin"
122//config: default y 119//config: default y
123//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 120//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
124//config: help
125//config: Enable support for echo builtin in ash.
126//config: 121//config:
127//config:config ASH_BUILTIN_PRINTF 122//config:config ASH_PRINTF
128//config: bool "Builtin version of 'printf'" 123//config: bool "printf builtin"
129//config: default y 124//config: default y
130//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 125//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
131//config: help
132//config: Enable support for printf builtin in ash.
133//config: 126//config:
134//config:config ASH_BUILTIN_TEST 127//config:config ASH_TEST
135//config: bool "Builtin version of 'test'" 128//config: bool "test builtin"
136//config: default y 129//config: default y
137//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 130//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
138//config: help
139//config: Enable support for test builtin in ash.
140//config: 131//config:
141//config:config ASH_HELP 132//config:config ASH_HELP
142//config: bool "help builtin" 133//config: bool "help builtin"
143//config: default y 134//config: default y
144//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 135//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
145//config: help
146//config: Enable help builtin in ash.
147//config: 136//config:
148//config:config ASH_CMDCMD 137//config:config ASH_GETOPTS
149//config: bool "'command' command to override shell builtins" 138//config: bool "getopts builtin"
150//config: default y 139//config: default y
151//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 140//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
152//config: help
153//config: Enable support for the ash 'command' builtin, which allows
154//config: you to run the specified command with the specified arguments,
155//config: even when there is an ash builtin command with the same name.
156//config: 141//config:
157//config:config ASH_MAIL 142//config:config ASH_CMDCMD
158//config: bool "Check for new mail on interactive shells" 143//config: bool "command builtin"
159//config: default y 144//config: default y
160//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH 145//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
161//config: help 146//config: help
162//config: Enable "check for new mail" function in the ash shell. 147//config: Enable support for the 'command' builtin, which allows
148//config: you to run the specified command or builtin,
149//config: even when there is a function with the same name.
163//config: 150//config:
164//config: 151//config:
165//config:config ASH_NOCONSOLE 152//config:config ASH_NOCONSOLE
@@ -176,7 +163,8 @@
176//config:endif # ash options 163//config:endif # ash options
177 164
178//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 165//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
179//applet:IF_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 166// APPLET_ODDNAME:name main location suid_type help
167//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
180//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 168//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
181 169
182//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o 170//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
@@ -185,15 +173,10 @@
185//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o 173//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
186 174
187/* 175/*
188 * The following should be set to reflect the type of system you have: 176 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
189 * JOBS -> 1 if you have Berkeley job control, 0 otherwise. 177 * DEBUG=2 to compile in and turn on debugging.
190 * define SYSV if you are running under System V. 178 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
191 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) 179 * debugging info is written to ./trace, quit signal generates core dump.
192 * define DEBUG=2 to compile in and turn on debugging.
193 *
194 * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
195 * debugging info will be written to ./trace and a quit signal
196 * will generate a core dump.
197 */ 180 */
198#define DEBUG 0 181#define DEBUG 0
199/* Tweak debug output verbosity here */ 182/* Tweak debug output verbosity here */
@@ -210,9 +193,30 @@
210#include <fnmatch.h> 193#include <fnmatch.h>
211#include <sys/times.h> 194#include <sys/times.h>
212#include <sys/utsname.h> /* for setting $HOSTNAME */ 195#include <sys/utsname.h> /* for setting $HOSTNAME */
213
214#include "busybox.h" /* for applet_names */ 196#include "busybox.h" /* for applet_names */
215 197
198/* So far, all bash compat is controlled by one config option */
199/* Separate defines document which part of code implements what */
200/* function keyword */
201#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
202#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
203/* &>file */
204#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
206/* $'...' */
207#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
208#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
209#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
210#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
211#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
212#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
213/* [[ EXPR ]] */
214#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
215#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
216#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
217#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
218#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
219
216#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 220#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
217/* Bionic at least up to version 24 has no glob() */ 221/* Bionic at least up to version 24 has no glob() */
218# undef ENABLE_ASH_INTERNAL_GLOB 222# undef ENABLE_ASH_INTERNAL_GLOB
@@ -338,7 +342,7 @@ static const char *const optletters_optnames[] = {
338 "b" "notify", 342 "b" "notify",
339 "u" "nounset", 343 "u" "nounset",
340 "\0" "vi" 344 "\0" "vi"
341#if ENABLE_ASH_BASH_COMPAT 345#if BASH_PIPEFAIL
342 ,"\0" "pipefail" 346 ,"\0" "pipefail"
343#endif 347#endif
344#if DEBUG 348#if DEBUG
@@ -421,14 +425,14 @@ struct globals_misc {
421#define bflag optlist[11] 425#define bflag optlist[11]
422#define uflag optlist[12] 426#define uflag optlist[12]
423#define viflag optlist[13] 427#define viflag optlist[13]
424#if ENABLE_ASH_BASH_COMPAT 428#if BASH_PIPEFAIL
425# define pipefail optlist[14] 429# define pipefail optlist[14]
426#else 430#else
427# define pipefail 0 431# define pipefail 0
428#endif 432#endif
429#if DEBUG 433#if DEBUG
430# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT] 434# define nolog optlist[14 + BASH_PIPEFAIL]
431# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT] 435# define debug optlist[15 + BASH_PIPEFAIL]
432#endif 436#endif
433#if ENABLE_PLATFORM_MINGW32 437#if ENABLE_PLATFORM_MINGW32
434# define winxp optlist[14 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG] 438# define winxp optlist[14 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG]
@@ -755,8 +759,10 @@ out2str(const char *p)
755#define VSTRIMLEFT 0x8 /* ${var#pattern} */ 759#define VSTRIMLEFT 0x8 /* ${var#pattern} */
756#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ 760#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
757#define VSLENGTH 0xa /* ${#var} */ 761#define VSLENGTH 0xa /* ${#var} */
758#if ENABLE_ASH_BASH_COMPAT 762#if BASH_SUBSTR
759#define VSSUBSTR 0xc /* ${var:position:length} */ 763#define VSSUBSTR 0xc /* ${var:position:length} */
764#endif
765#if BASH_PATTERN_SUBST
760#define VSREPLACE 0xd /* ${var/pattern/replacement} */ 766#define VSREPLACE 0xd /* ${var/pattern/replacement} */
761#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ 767#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
762#endif 768#endif
@@ -783,7 +789,7 @@ static const char dolatstr[] ALIGN1 = {
783#define NDEFUN 14 789#define NDEFUN 14
784#define NARG 15 790#define NARG 15
785#define NTO 16 791#define NTO 16
786#if ENABLE_ASH_BASH_COMPAT 792#if BASH_REDIR_OUTPUT
787#define NTO2 17 793#define NTO2 17
788#endif 794#endif
789#define NCLOBBER 18 795#define NCLOBBER 18
@@ -1193,7 +1199,7 @@ shcmd(union node *cmd, FILE *fp)
1193 case NTO: s = ">>"+1; dftfd = 1; break; 1199 case NTO: s = ">>"+1; dftfd = 1; break;
1194 case NCLOBBER: s = ">|"; dftfd = 1; break; 1200 case NCLOBBER: s = ">|"; dftfd = 1; break;
1195 case NAPPEND: s = ">>"; dftfd = 1; break; 1201 case NAPPEND: s = ">>"; dftfd = 1; break;
1196#if ENABLE_ASH_BASH_COMPAT 1202#if BASH_REDIR_OUTPUT
1197 case NTO2: 1203 case NTO2:
1198#endif 1204#endif
1199 case NTOFD: s = ">&"; dftfd = 1; break; 1205 case NTOFD: s = ">&"; dftfd = 1; break;
@@ -3599,12 +3605,13 @@ struct job {
3599#if JOBS 3605#if JOBS
3600 int stopstatus; /* status of a stopped job */ 3606 int stopstatus; /* status of a stopped job */
3601#endif 3607#endif
3602 uint32_t 3608 unsigned nprocs; /* number of processes */
3603 nprocs: 16, /* number of processes */ 3609
3604 state: 8,
3605#define JOBRUNNING 0 /* at least one proc running */ 3610#define JOBRUNNING 0 /* at least one proc running */
3606#define JOBSTOPPED 1 /* all procs are stopped */ 3611#define JOBSTOPPED 1 /* all procs are stopped */
3607#define JOBDONE 2 /* all procs are completed */ 3612#define JOBDONE 2 /* all procs are completed */
3613 unsigned
3614 state: 8,
3608#if JOBS 3615#if JOBS
3609 sigint: 1, /* job was killed by SIGINT */ 3616 sigint: 1, /* job was killed by SIGINT */
3610 jobctl: 1, /* job running under job control */ 3617 jobctl: 1, /* job running under job control */
@@ -3791,6 +3798,72 @@ static struct job *curjob; //lots
3791/* number of presumed living untracked jobs */ 3798/* number of presumed living untracked jobs */
3792static int jobless; //4 3799static int jobless; //4
3793 3800
3801#if 0
3802/* Bash has a feature: it restores termios after a successful wait for
3803 * a foreground job which had at least one stopped or sigkilled member.
3804 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3805 * properly restoring tty state. Should we do this too?
3806 * A reproducer: ^Z an interactive python:
3807 *
3808 * # python
3809 * Python 2.7.12 (...)
3810 * >>> ^Z
3811 * { python leaves tty in -icanon -echo state. We do survive that... }
3812 * [1]+ Stopped python
3813 * { ...however, next program (python #2) does not survive it well: }
3814 * # python
3815 * Python 2.7.12 (...)
3816 * >>> Traceback (most recent call last):
3817 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3818 * File "<stdin>", line 1, in <module>
3819 * NameError: name 'qwerty' is not defined
3820 *
3821 * The implementation below is modeled on bash code and seems to work.
3822 * However, I'm not sure we should do this. For one: what if I'd fg
3823 * the stopped python instead? It'll be confused by "restored" tty state.
3824 */
3825static struct termios shell_tty_info;
3826static void
3827get_tty_state(void)
3828{
3829 if (rootshell && ttyfd >= 0)
3830 tcgetattr(ttyfd, &shell_tty_info);
3831}
3832static void
3833set_tty_state(void)
3834{
3835 /* if (rootshell) - caller ensures this */
3836 if (ttyfd >= 0)
3837 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3838}
3839static int
3840job_signal_status(struct job *jp)
3841{
3842 int status;
3843 unsigned i;
3844 struct procstat *ps = jp->ps;
3845 for (i = 0; i < jp->nprocs; i++) {
3846 status = ps[i].ps_status;
3847 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3848 return status;
3849 }
3850 return 0;
3851}
3852static void
3853restore_tty_if_stopped_or_signaled(struct job *jp)
3854{
3855//TODO: check what happens if we come from waitforjob() in expbackq()
3856 if (rootshell) {
3857 int s = job_signal_status(jp);
3858 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3859 set_tty_state();
3860 }
3861}
3862#else
3863# define get_tty_state() ((void)0)
3864# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3865#endif
3866
3794static void 3867static void
3795set_curjob(struct job *jp, unsigned mode) 3868set_curjob(struct job *jp, unsigned mode)
3796{ 3869{
@@ -4122,8 +4195,10 @@ restartjob(struct job *jp, int mode)
4122 goto out; 4195 goto out;
4123 jp->state = JOBRUNNING; 4196 jp->state = JOBRUNNING;
4124 pgid = jp->ps[0].ps_pid; 4197 pgid = jp->ps[0].ps_pid;
4125 if (mode == FORK_FG) 4198 if (mode == FORK_FG) {
4199 get_tty_state();
4126 xtcsetpgrp(ttyfd, pgid); 4200 xtcsetpgrp(ttyfd, pgid);
4201 }
4127 killpg(pgid, SIGCONT); 4202 killpg(pgid, SIGCONT);
4128 ps = jp->ps; 4203 ps = jp->ps;
4129 i = jp->nprocs; 4204 i = jp->nprocs;
@@ -4754,7 +4829,7 @@ makejob(/*union node *node,*/ int nprocs)
4754 memset(jp, 0, sizeof(*jp)); 4829 memset(jp, 0, sizeof(*jp));
4755#if JOBS 4830#if JOBS
4756 /* jp->jobctl is a bitfield. 4831 /* jp->jobctl is a bitfield.
4757 * "jp->jobctl |= jobctl" likely to give awful code */ 4832 * "jp->jobctl |= doing_jobctl" likely to give awful code */
4758 if (doing_jobctl) 4833 if (doing_jobctl)
4759 jp->jobctl = 1; 4834 jp->jobctl = 1;
4760#endif 4835#endif
@@ -4783,7 +4858,8 @@ cmdputs(const char *s)
4783 static const char vstype[VSTYPE + 1][3] = { 4858 static const char vstype[VSTYPE + 1][3] = {
4784 "", "}", "-", "+", "?", "=", 4859 "", "}", "-", "+", "?", "=",
4785 "%", "%%", "#", "##" 4860 "%", "%%", "#", "##"
4786 IF_ASH_BASH_COMPAT(, ":", "/", "//") 4861 IF_BASH_SUBSTR(, ":")
4862 IF_BASH_PATTERN_SUBST(, "/", "//")
4787 }; 4863 };
4788 4864
4789 const char *p, *str; 4865 const char *p, *str;
@@ -5010,7 +5086,7 @@ cmdtxt(union node *n)
5010 case NAPPEND: 5086 case NAPPEND:
5011 p = ">>"; 5087 p = ">>";
5012 goto redir; 5088 goto redir;
5013#if ENABLE_ASH_BASH_COMPAT 5089#if BASH_REDIR_OUTPUT
5014 case NTO2: 5090 case NTO2:
5015#endif 5091#endif
5016 case NTOFD: 5092 case NTOFD:
@@ -5362,6 +5438,8 @@ waitforjob(struct job *jp)
5362#if JOBS 5438#if JOBS
5363 if (jp->jobctl) { 5439 if (jp->jobctl) {
5364 xtcsetpgrp(ttyfd, rootpid); 5440 xtcsetpgrp(ttyfd, rootpid);
5441 restore_tty_if_stopped_or_signaled(jp);
5442
5365 /* 5443 /*
5366 * This is truly gross. 5444 * This is truly gross.
5367 * If we're doing job control, then we did a TIOCSPGRP which 5445 * If we're doing job control, then we did a TIOCSPGRP which
@@ -5587,7 +5665,7 @@ openredirect(union node *redir)
5587 goto ecreate; 5665 goto ecreate;
5588 break; 5666 break;
5589 case NTO: 5667 case NTO:
5590#if ENABLE_ASH_BASH_COMPAT 5668#if BASH_REDIR_OUTPUT
5591 case NTO2: 5669 case NTO2:
5592#endif 5670#endif
5593 /* Take care of noclobber mode. */ 5671 /* Take care of noclobber mode. */
@@ -5751,7 +5829,7 @@ redirect(union node *redir, int flags)
5751 union node *tmp = redir; 5829 union node *tmp = redir;
5752 do { 5830 do {
5753 sv_pos++; 5831 sv_pos++;
5754#if ENABLE_ASH_BASH_COMPAT 5832#if BASH_REDIR_OUTPUT
5755 if (tmp->nfile.type == NTO2) 5833 if (tmp->nfile.type == NTO2)
5756 sv_pos++; 5834 sv_pos++;
5757#endif 5835#endif
@@ -5793,7 +5871,7 @@ redirect(union node *redir, int flags)
5793 continue; 5871 continue;
5794 } 5872 }
5795 } 5873 }
5796#if ENABLE_ASH_BASH_COMPAT 5874#if BASH_REDIR_OUTPUT
5797 redirect_more: 5875 redirect_more:
5798#endif 5876#endif
5799 if (need_to_remember(sv, fd)) { 5877 if (need_to_remember(sv, fd)) {
@@ -5846,12 +5924,12 @@ redirect(union node *redir, int flags)
5846 } 5924 }
5847 } else if (fd != newfd) { /* move newfd to fd */ 5925 } else if (fd != newfd) { /* move newfd to fd */
5848 dup2_or_raise(newfd, fd); 5926 dup2_or_raise(newfd, fd);
5849#if ENABLE_ASH_BASH_COMPAT 5927#if BASH_REDIR_OUTPUT
5850 if (!(redir->nfile.type == NTO2 && fd == 2)) 5928 if (!(redir->nfile.type == NTO2 && fd == 2))
5851#endif 5929#endif
5852 close(newfd); 5930 close(newfd);
5853 } 5931 }
5854#if ENABLE_ASH_BASH_COMPAT 5932#if BASH_REDIR_OUTPUT
5855 if (redir->nfile.type == NTO2 && fd == 1) { 5933 if (redir->nfile.type == NTO2 && fd == 1) {
5856 /* We already redirected it to fd 1, now copy it to 2 */ 5934 /* We already redirected it to fd 1, now copy it to 2 */
5857 newfd = 1; 5935 newfd = 1;
@@ -6168,15 +6246,15 @@ static char *
6168rmescapes(char *str, int flag) 6246rmescapes(char *str, int flag)
6169{ 6247{
6170 static const char qchars[] ALIGN1 = { 6248 static const char qchars[] ALIGN1 = {
6171 IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' }; 6249 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
6172 6250
6173 char *p, *q, *r; 6251 char *p, *q, *r;
6174 unsigned inquotes; 6252 unsigned inquotes;
6175 unsigned protect_against_glob; 6253 unsigned protect_against_glob;
6176 unsigned globbing; 6254 unsigned globbing;
6177 IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;) 6255 IF_BASH_PATTERN_SUBST(unsigned slash = flag & RMESCAPE_SLASH;)
6178 6256
6179 p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash)); 6257 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash));
6180 if (!p) 6258 if (!p)
6181 return str; 6259 return str;
6182 6260
@@ -6228,7 +6306,7 @@ rmescapes(char *str, int flag)
6228 protect_against_glob = 0; 6306 protect_against_glob = 0;
6229 goto copy; 6307 goto copy;
6230 } 6308 }
6231#if ENABLE_ASH_BASH_COMPAT 6309#if BASH_PATTERN_SUBST
6232 else if (*p == '/' && slash) { 6310 else if (*p == '/' && slash) {
6233 /* stop handling globbing and mark location of slash */ 6311 /* stop handling globbing and mark location of slash */
6234 globbing = slash = 0; 6312 globbing = slash = 0;
@@ -6887,10 +6965,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6887 char *loc; 6965 char *loc;
6888 char *rmesc, *rmescend; 6966 char *rmesc, *rmescend;
6889 char *str; 6967 char *str;
6890 IF_ASH_BASH_COMPAT(char *repl = NULL;) 6968 IF_BASH_SUBSTR(int pos, len, orig_len;)
6891 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
6892 int amount, resetloc; 6969 int amount, resetloc;
6893 IF_ASH_BASH_COMPAT(int workloc;) 6970 IF_BASH_PATTERN_SUBST(int workloc;)
6971 IF_BASH_PATTERN_SUBST(char *repl = NULL;)
6894 int zero; 6972 int zero;
6895 char *(*scan)(char*, char*, char*, char*, int, int); 6973 char *(*scan)(char*, char*, char*, char*, int, int);
6896 6974
@@ -6915,7 +6993,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6915 varunset(p, varname, startp, varflags); 6993 varunset(p, varname, startp, varflags);
6916 /* NOTREACHED */ 6994 /* NOTREACHED */
6917 6995
6918#if ENABLE_ASH_BASH_COMPAT 6996#if BASH_SUBSTR
6919 case VSSUBSTR: 6997 case VSSUBSTR:
6920//TODO: support more general format ${v:EXPR:EXPR}, 6998//TODO: support more general format ${v:EXPR:EXPR},
6921// where EXPR follows $(()) rules 6999// where EXPR follows $(()) rules
@@ -6984,17 +7062,19 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6984 amount = loc - expdest; 7062 amount = loc - expdest;
6985 STADJUST(amount, expdest); 7063 STADJUST(amount, expdest);
6986 return loc; 7064 return loc;
6987#endif 7065#endif /* BASH_SUBSTR */
6988 } 7066 }
6989 7067
6990 resetloc = expdest - (char *)stackblock(); 7068 resetloc = expdest - (char *)stackblock();
6991 7069
7070#if BASH_PATTERN_SUBST
6992 /* We'll comeback here if we grow the stack while handling 7071 /* We'll comeback here if we grow the stack while handling
6993 * a VSREPLACE or VSREPLACEALL, since our pointers into the 7072 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6994 * stack will need rebasing, and we'll need to remove our work 7073 * stack will need rebasing, and we'll need to remove our work
6995 * areas each time 7074 * areas each time
6996 */ 7075 */
6997 IF_ASH_BASH_COMPAT(restart:) 7076 restart:
7077#endif
6998 7078
6999 amount = expdest - ((char *)stackblock() + resetloc); 7079 amount = expdest - ((char *)stackblock() + resetloc);
7000 STADJUST(-amount, expdest); 7080 STADJUST(-amount, expdest);
@@ -7019,11 +7099,11 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7019 * RMESCAPE_SLASH causes preglob to work differently on the pattern 7099 * RMESCAPE_SLASH causes preglob to work differently on the pattern
7020 * and string. It's only used on the first call. 7100 * and string. It's only used on the first call.
7021 */ 7101 */
7022 preglob(str, IF_ASH_BASH_COMPAT( 7102 preglob(str, IF_BASH_PATTERN_SUBST(
7023 (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? 7103 (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ?
7024 RMESCAPE_SLASH :) 0); 7104 RMESCAPE_SLASH : ) 0);
7025 7105
7026#if ENABLE_ASH_BASH_COMPAT 7106#if BASH_PATTERN_SUBST
7027 workloc = expdest - (char *)stackblock(); 7107 workloc = expdest - (char *)stackblock();
7028 if (subtype == VSREPLACE || subtype == VSREPLACEALL) { 7108 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7029 char *idx, *end; 7109 char *idx, *end;
@@ -7124,7 +7204,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7124 STADJUST(-amount, expdest); 7204 STADJUST(-amount, expdest);
7125 return startp; 7205 return startp;
7126 } 7206 }
7127#endif /* ENABLE_ASH_BASH_COMPAT */ 7207#endif /* BASH_PATTERN_SUBST */
7128 7208
7129 subtype -= VSTRIMRIGHT; 7209 subtype -= VSTRIMRIGHT;
7130#if DEBUG 7210#if DEBUG
@@ -7392,8 +7472,10 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
7392 case VSTRIMLEFTMAX: 7472 case VSTRIMLEFTMAX:
7393 case VSTRIMRIGHT: 7473 case VSTRIMRIGHT:
7394 case VSTRIMRIGHTMAX: 7474 case VSTRIMRIGHTMAX:
7395#if ENABLE_ASH_BASH_COMPAT 7475#if BASH_SUBSTR
7396 case VSSUBSTR: 7476 case VSSUBSTR:
7477#endif
7478#if BASH_PATTERN_SUBST
7397 case VSREPLACE: 7479 case VSREPLACE:
7398 case VSREPLACEALL: 7480 case VSREPLACEALL:
7399#endif 7481#endif
@@ -7458,6 +7540,57 @@ addfname(const char *name)
7458 exparg.lastp = &sp->next; 7540 exparg.lastp = &sp->next;
7459} 7541}
7460 7542
7543/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7544static int
7545hasmeta(const char *p)
7546{
7547 static const char chars[] ALIGN1 = {
7548 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7549 };
7550
7551 for (;;) {
7552 p = strpbrk(p, chars);
7553 if (!p)
7554 break;
7555 switch ((unsigned char) *p) {
7556 case CTLQUOTEMARK:
7557 for (;;) {
7558 p++;
7559 if (*p == CTLQUOTEMARK)
7560 break;
7561 if (*p == CTLESC)
7562 p++;
7563 if (*p == '\0') /* huh? */
7564 return 0;
7565 }
7566 break;
7567 case '\\':
7568 case CTLESC:
7569 p++;
7570 if (*p == '\0')
7571 return 0;
7572 break;
7573 case '[':
7574 if (!strchr(p + 1, ']')) {
7575 /* It's not a properly closed [] pattern,
7576 * but other metas may follow. Continue checking.
7577 * my[file* _is_ globbed by bash
7578 * and matches filenames like "my[file1".
7579 */
7580 break;
7581 }
7582 /* fallthrough */
7583 default:
7584 /* case '*': */
7585 /* case '?': */
7586 return 1;
7587 }
7588 p++;
7589 }
7590
7591 return 0;
7592}
7593
7461/* If we want to use glob() from libc... */ 7594/* If we want to use glob() from libc... */
7462#if !ENABLE_ASH_INTERNAL_GLOB 7595#if !ENABLE_ASH_INTERNAL_GLOB
7463 7596
@@ -7484,20 +7617,9 @@ expandmeta(struct strlist *str /*, int flag*/)
7484 if (fflag) 7617 if (fflag)
7485 goto nometa; 7618 goto nometa;
7486 7619
7487 /* Avoid glob() (and thus, stat() et al) for words like "echo" */ 7620 if (!hasmeta(str->text))
7488 p = str->text; 7621 goto nometa;
7489 while (*p) {
7490 if (*p == '*')
7491 goto need_glob;
7492 if (*p == '?')
7493 goto need_glob;
7494 if (*p == '[')
7495 goto need_glob;
7496 p++;
7497 }
7498 goto nometa;
7499 7622
7500 need_glob:
7501 INT_OFF; 7623 INT_OFF;
7502 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 7624 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7503// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match 7625// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
@@ -7734,9 +7856,6 @@ expsort(struct strlist *str)
7734static void 7856static void
7735expandmeta(struct strlist *str /*, int flag*/) 7857expandmeta(struct strlist *str /*, int flag*/)
7736{ 7858{
7737 static const char metachars[] ALIGN1 = {
7738 '*', '?', '[', 0
7739 };
7740 /* TODO - EXP_REDIR */ 7859 /* TODO - EXP_REDIR */
7741 7860
7742 while (str) { 7861 while (str) {
@@ -7747,7 +7866,7 @@ expandmeta(struct strlist *str /*, int flag*/)
7747 7866
7748 if (fflag) 7867 if (fflag)
7749 goto nometa; 7868 goto nometa;
7750 if (!strpbrk(str->text, metachars)) 7869 if (!hasmeta(str->text))
7751 goto nometa; 7870 goto nometa;
7752 savelastp = exparg.lastp; 7871 savelastp = exparg.lastp;
7753 7872
@@ -8322,7 +8441,7 @@ enum {
8322 TESAC, 8441 TESAC,
8323 TFI, 8442 TFI,
8324 TFOR, 8443 TFOR,
8325#if ENABLE_ASH_BASH_COMPAT 8444#if BASH_FUNCTION
8326 TFUNCTION, 8445 TFUNCTION,
8327#endif 8446#endif
8328 TIF, 8447 TIF,
@@ -8360,7 +8479,7 @@ enum {
8360 /* 19 */ | (1u << TESAC) 8479 /* 19 */ | (1u << TESAC)
8361 /* 20 */ | (1u << TFI) 8480 /* 20 */ | (1u << TFI)
8362 /* 21 */ | (0u << TFOR) 8481 /* 21 */ | (0u << TFOR)
8363#if ENABLE_ASH_BASH_COMPAT 8482#if BASH_FUNCTION
8364 /* 22 */ | (0u << TFUNCTION) 8483 /* 22 */ | (0u << TFUNCTION)
8365#endif 8484#endif
8366 /* 23 */ | (0u << TIF) 8485 /* 23 */ | (0u << TIF)
@@ -8398,7 +8517,7 @@ static const char *const tokname_array[] = {
8398 "esac", 8517 "esac",
8399 "fi", 8518 "fi",
8400 "for", 8519 "for",
8401#if ENABLE_ASH_BASH_COMPAT 8520#if BASH_FUNCTION
8402 "function", 8521 "function",
8403#endif 8522#endif
8404 "if", 8523 "if",
@@ -8646,7 +8765,7 @@ static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8646 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)), 8765 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8647 [NARG ] = SHELL_ALIGN(sizeof(struct narg)), 8766 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8648 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)), 8767 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8649#if ENABLE_ASH_BASH_COMPAT 8768#if BASH_REDIR_OUTPUT
8650 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)), 8769 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
8651#endif 8770#endif
8652 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)), 8771 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
@@ -8737,7 +8856,7 @@ calcsize(union node *n)
8737 IF_PLATFORM_MINGW32(nodeptrsize += 3); 8856 IF_PLATFORM_MINGW32(nodeptrsize += 3);
8738 break; 8857 break;
8739 case NTO: 8858 case NTO:
8740#if ENABLE_ASH_BASH_COMPAT 8859#if BASH_REDIR_OUTPUT
8741 case NTO2: 8860 case NTO2:
8742#endif 8861#endif
8743 case NCLOBBER: 8862 case NCLOBBER:
@@ -8881,7 +9000,7 @@ copynode(union node *n)
8881 SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next); 9000 SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next);
8882 break; 9001 break;
8883 case NTO: 9002 case NTO:
8884#if ENABLE_ASH_BASH_COMPAT 9003#if BASH_REDIR_OUTPUT
8885 case NTO2: 9004 case NTO2:
8886#endif 9005#endif
8887 case NCLOBBER: 9006 case NCLOBBER:
@@ -9277,13 +9396,15 @@ evalsubshell(union node *n, int flags)
9277{ 9396{
9278 IF_PLATFORM_MINGW32(struct forkshell fs;) 9397 IF_PLATFORM_MINGW32(struct forkshell fs;)
9279 struct job *jp; 9398 struct job *jp;
9280 int backgnd = (n->type == NBACKGND); 9399 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9281 int status; 9400 int status;
9282 9401
9283 expredir(n->nredir.redirect); 9402 expredir(n->nredir.redirect);
9284 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 9403 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9285 goto nofork; 9404 goto nofork;
9286 INT_OFF; 9405 INT_OFF;
9406 if (backgnd == FORK_FG)
9407 get_tty_state();
9287 jp = makejob(/*n,*/ 1); 9408 jp = makejob(/*n,*/ 1);
9288#if ENABLE_PLATFORM_MINGW32 9409#if ENABLE_PLATFORM_MINGW32
9289 memset(&fs, 0, sizeof(fs)); 9410 memset(&fs, 0, sizeof(fs));
@@ -9308,7 +9429,7 @@ evalsubshell(union node *n, int flags)
9308 } 9429 }
9309 /* parent */ 9430 /* parent */
9310 status = 0; 9431 status = 0;
9311 if (!backgnd) 9432 if (backgnd == FORK_FG)
9312 status = waitforjob(jp); 9433 status = waitforjob(jp);
9313 INT_ON; 9434 INT_ON;
9314 return status; 9435 return status;
@@ -9332,14 +9453,14 @@ expredir(union node *n)
9332 case NFROMTO: 9453 case NFROMTO:
9333 case NFROM: 9454 case NFROM:
9334 case NTO: 9455 case NTO:
9335#if ENABLE_ASH_BASH_COMPAT 9456#if BASH_REDIR_OUTPUT
9336 case NTO2: 9457 case NTO2:
9337#endif 9458#endif
9338 case NCLOBBER: 9459 case NCLOBBER:
9339 case NAPPEND: 9460 case NAPPEND:
9340 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 9461 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
9341 TRACE(("expredir expanded to '%s'\n", fn.list->text)); 9462 TRACE(("expredir expanded to '%s'\n", fn.list->text));
9342#if ENABLE_ASH_BASH_COMPAT 9463#if BASH_REDIR_OUTPUT
9343 store_expfname: 9464 store_expfname:
9344#endif 9465#endif
9345#if 0 9466#if 0
@@ -9361,7 +9482,7 @@ expredir(union node *n)
9361 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); 9482 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
9362 if (fn.list == NULL) 9483 if (fn.list == NULL)
9363 ash_msg_and_raise_error("redir error"); 9484 ash_msg_and_raise_error("redir error");
9364#if ENABLE_ASH_BASH_COMPAT 9485#if BASH_REDIR_OUTPUT
9365//FIXME: we used expandarg with different args! 9486//FIXME: we used expandarg with different args!
9366 if (!isdigit_str9(fn.list->text)) { 9487 if (!isdigit_str9(fn.list->text)) {
9367 /* >&file, not >&fd */ 9488 /* >&file, not >&fd */
@@ -9401,6 +9522,8 @@ evalpipe(union node *n, int flags)
9401 pipelen++; 9522 pipelen++;
9402 flags |= EV_EXIT; 9523 flags |= EV_EXIT;
9403 INT_OFF; 9524 INT_OFF;
9525 if (n->npipe.pipe_backgnd == 0)
9526 get_tty_state();
9404 jp = makejob(/*n,*/ pipelen); 9527 jp = makejob(/*n,*/ pipelen);
9405 prevfd = -1; 9528 prevfd = -1;
9406 for (lp = n->npipe.cmdlist; lp; lp = lp->next) { 9529 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
@@ -9764,13 +9887,13 @@ static int ulimitcmd(int, char **) FAST_FUNC;
9764#define BUILTIN_SPEC_REG_ASSG "7" 9887#define BUILTIN_SPEC_REG_ASSG "7"
9765 9888
9766/* Stubs for calling non-FAST_FUNC's */ 9889/* Stubs for calling non-FAST_FUNC's */
9767#if ENABLE_ASH_BUILTIN_ECHO 9890#if ENABLE_ASH_ECHO
9768static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } 9891static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
9769#endif 9892#endif
9770#if ENABLE_ASH_BUILTIN_PRINTF 9893#if ENABLE_ASH_PRINTF
9771static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } 9894static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
9772#endif 9895#endif
9773#if ENABLE_ASH_BUILTIN_TEST 9896#if ENABLE_ASH_TEST || BASH_TEST2
9774static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } 9897static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
9775#endif 9898#endif
9776 9899
@@ -9778,11 +9901,11 @@ static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, a
9778static const struct builtincmd builtintab[] = { 9901static const struct builtincmd builtintab[] = {
9779 { BUILTIN_SPEC_REG "." , dotcmd }, 9902 { BUILTIN_SPEC_REG "." , dotcmd },
9780 { BUILTIN_SPEC_REG ":" , truecmd }, 9903 { BUILTIN_SPEC_REG ":" , truecmd },
9781#if ENABLE_ASH_BUILTIN_TEST 9904#if ENABLE_ASH_TEST
9782 { BUILTIN_REGULAR "[" , testcmd }, 9905 { BUILTIN_REGULAR "[" , testcmd },
9783# if ENABLE_ASH_BASH_COMPAT 9906#endif
9907#if BASH_TEST2
9784 { BUILTIN_REGULAR "[[" , testcmd }, 9908 { BUILTIN_REGULAR "[[" , testcmd },
9785# endif
9786#endif 9909#endif
9787#if ENABLE_ASH_ALIAS 9910#if ENABLE_ASH_ALIAS
9788 { BUILTIN_REG_ASSG "alias" , aliascmd }, 9911 { BUILTIN_REG_ASSG "alias" , aliascmd },
@@ -9797,7 +9920,7 @@ static const struct builtincmd builtintab[] = {
9797 { BUILTIN_REGULAR "command" , commandcmd }, 9920 { BUILTIN_REGULAR "command" , commandcmd },
9798#endif 9921#endif
9799 { BUILTIN_SPEC_REG "continue", breakcmd }, 9922 { BUILTIN_SPEC_REG "continue", breakcmd },
9800#if ENABLE_ASH_BUILTIN_ECHO 9923#if ENABLE_ASH_ECHO
9801 { BUILTIN_REGULAR "echo" , echocmd }, 9924 { BUILTIN_REGULAR "echo" , echocmd },
9802#endif 9925#endif
9803 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/ 9926 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
@@ -9826,7 +9949,7 @@ static const struct builtincmd builtintab[] = {
9826 { BUILTIN_NOSPEC "let" , letcmd }, 9949 { BUILTIN_NOSPEC "let" , letcmd },
9827#endif 9950#endif
9828 { BUILTIN_ASSIGN "local" , localcmd }, 9951 { BUILTIN_ASSIGN "local" , localcmd },
9829#if ENABLE_ASH_BUILTIN_PRINTF 9952#if ENABLE_ASH_PRINTF
9830 { BUILTIN_REGULAR "printf" , printfcmd }, 9953 { BUILTIN_REGULAR "printf" , printfcmd },
9831#endif 9954#endif
9832 { BUILTIN_NOSPEC "pwd" , pwdcmd }, 9955 { BUILTIN_NOSPEC "pwd" , pwdcmd },
@@ -9835,10 +9958,10 @@ static const struct builtincmd builtintab[] = {
9835 { BUILTIN_SPEC_REG "return" , returncmd }, 9958 { BUILTIN_SPEC_REG "return" , returncmd },
9836 { BUILTIN_SPEC_REG "set" , setcmd }, 9959 { BUILTIN_SPEC_REG "set" , setcmd },
9837 { BUILTIN_SPEC_REG "shift" , shiftcmd }, 9960 { BUILTIN_SPEC_REG "shift" , shiftcmd },
9838#if ENABLE_ASH_BASH_COMPAT 9961#if BASH_SOURCE
9839 { BUILTIN_SPEC_REG "source" , dotcmd }, 9962 { BUILTIN_SPEC_REG "source" , dotcmd },
9840#endif 9963#endif
9841#if ENABLE_ASH_BUILTIN_TEST 9964#if ENABLE_ASH_TEST
9842 { BUILTIN_REGULAR "test" , testcmd }, 9965 { BUILTIN_REGULAR "test" , testcmd },
9843#endif 9966#endif
9844 { BUILTIN_SPEC_REG "times" , timescmd }, 9967 { BUILTIN_SPEC_REG "times" , timescmd },
@@ -9857,15 +9980,15 @@ static const struct builtincmd builtintab[] = {
9857/* Should match the above table! */ 9980/* Should match the above table! */
9858#define COMMANDCMD (builtintab + \ 9981#define COMMANDCMD (builtintab + \
9859 /* . : */ 2 + \ 9982 /* . : */ 2 + \
9860 /* [ */ 1 * ENABLE_ASH_BUILTIN_TEST + \ 9983 /* [ */ 1 * ENABLE_ASH_TEST + \
9861 /* [[ */ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ 9984 /* [[ */ 1 * BASH_TEST2 + \
9862 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 9985 /* alias */ 1 * ENABLE_ASH_ALIAS + \
9863 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 9986 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
9864 /* break cd cddir */ 3) 9987 /* break cd cddir */ 3)
9865#define EVALCMD (COMMANDCMD + \ 9988#define EVALCMD (COMMANDCMD + \
9866 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 9989 /* command */ 1 * ENABLE_ASH_CMDCMD + \
9867 /* continue */ 1 + \ 9990 /* continue */ 1 + \
9868 /* echo */ 1 * ENABLE_ASH_BUILTIN_ECHO + \ 9991 /* echo */ 1 * ENABLE_ASH_ECHO + \
9869 0) 9992 0)
9870#define EXECCMD (EVALCMD + \ 9993#define EXECCMD (EVALCMD + \
9871 /* eval */ 1) 9994 /* eval */ 1)
@@ -10117,6 +10240,7 @@ evalcommand(union node *cmd, int flags)
10117 if (!(flags & EV_EXIT) || may_have_traps) { 10240 if (!(flags & EV_EXIT) || may_have_traps) {
10118 /* No, forking off a child is necessary */ 10241 /* No, forking off a child is necessary */
10119 INT_OFF; 10242 INT_OFF;
10243 get_tty_state();
10120 jp = makejob(/*cmd,*/ 1); 10244 jp = makejob(/*cmd,*/ 1);
10121 if (forkshell(jp, cmd, FORK_FG) != 0) { 10245 if (forkshell(jp, cmd, FORK_FG) != 0) {
10122 /* parent */ 10246 /* parent */
@@ -11505,10 +11629,10 @@ simplecmd(void)
11505 union node *vars, **vpp; 11629 union node *vars, **vpp;
11506 union node **rpp, *redir; 11630 union node **rpp, *redir;
11507 int savecheckkwd; 11631 int savecheckkwd;
11508#if ENABLE_ASH_BASH_COMPAT 11632#if BASH_TEST2
11509 smallint double_brackets_flag = 0; 11633 smallint double_brackets_flag = 0;
11510 smallint function_flag = 0;
11511#endif 11634#endif
11635 IF_BASH_FUNCTION(smallint function_flag = 0;)
11512 11636
11513 args = NULL; 11637 args = NULL;
11514 app = &args; 11638 app = &args;
@@ -11523,12 +11647,14 @@ simplecmd(void)
11523 checkkwd = savecheckkwd; 11647 checkkwd = savecheckkwd;
11524 t = readtoken(); 11648 t = readtoken();
11525 switch (t) { 11649 switch (t) {
11526#if ENABLE_ASH_BASH_COMPAT 11650#if BASH_FUNCTION
11527 case TFUNCTION: 11651 case TFUNCTION:
11528 if (peektoken() != TWORD) 11652 if (peektoken() != TWORD)
11529 raise_error_unexpected_syntax(TWORD); 11653 raise_error_unexpected_syntax(TWORD);
11530 function_flag = 1; 11654 function_flag = 1;
11531 break; 11655 break;
11656#endif
11657#if BASH_TEST2
11532 case TAND: /* "&&" */ 11658 case TAND: /* "&&" */
11533 case TOR: /* "||" */ 11659 case TOR: /* "||" */
11534 if (!double_brackets_flag) { 11660 if (!double_brackets_flag) {
@@ -11542,7 +11668,7 @@ simplecmd(void)
11542 n->type = NARG; 11668 n->type = NARG;
11543 /*n->narg.next = NULL; - stzalloc did it */ 11669 /*n->narg.next = NULL; - stzalloc did it */
11544 n->narg.text = wordtext; 11670 n->narg.text = wordtext;
11545#if ENABLE_ASH_BASH_COMPAT 11671#if BASH_TEST2
11546 if (strcmp("[[", wordtext) == 0) 11672 if (strcmp("[[", wordtext) == 0)
11547 double_brackets_flag = 1; 11673 double_brackets_flag = 1;
11548 else if (strcmp("]]", wordtext) == 0) 11674 else if (strcmp("]]", wordtext) == 0)
@@ -11557,7 +11683,7 @@ simplecmd(void)
11557 app = &n->narg.next; 11683 app = &n->narg.next;
11558 savecheckkwd = 0; 11684 savecheckkwd = 0;
11559 } 11685 }
11560#if ENABLE_ASH_BASH_COMPAT 11686#if BASH_FUNCTION
11561 if (function_flag) { 11687 if (function_flag) {
11562 checkkwd = CHKNL | CHKKWD; 11688 checkkwd = CHKNL | CHKKWD;
11563 switch (peektoken()) { 11689 switch (peektoken()) {
@@ -11587,7 +11713,7 @@ simplecmd(void)
11587 parsefname(); /* read name of redirection file */ 11713 parsefname(); /* read name of redirection file */
11588 break; 11714 break;
11589 case TLP: 11715 case TLP:
11590 IF_ASH_BASH_COMPAT(do_func:) 11716 IF_BASH_FUNCTION(do_func:)
11591 if (args && app == &args->narg.next 11717 if (args && app == &args->narg.next
11592 && !vars && !redir 11718 && !vars && !redir
11593 ) { 11719 ) {
@@ -11595,7 +11721,7 @@ simplecmd(void)
11595 const char *name; 11721 const char *name;
11596 11722
11597 /* We have a function */ 11723 /* We have a function */
11598 if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP) 11724 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
11599 raise_error_unexpected_syntax(TRP); 11725 raise_error_unexpected_syntax(TRP);
11600 name = n->narg.text; 11726 name = n->narg.text;
11601 if (!goodname(name) 11727 if (!goodname(name)
@@ -11608,7 +11734,7 @@ simplecmd(void)
11608 n->narg.next = parse_command(); 11734 n->narg.next = parse_command();
11609 return n; 11735 return n;
11610 } 11736 }
11611 IF_ASH_BASH_COMPAT(function_flag = 0;) 11737 IF_BASH_FUNCTION(function_flag = 0;)
11612 /* fall through */ 11738 /* fall through */
11613 default: 11739 default:
11614 tokpushback = 1; 11740 tokpushback = 1;
@@ -11789,7 +11915,7 @@ parse_command(void)
11789 n1 = list(0); 11915 n1 = list(0);
11790 t = TEND; 11916 t = TEND;
11791 break; 11917 break;
11792 IF_ASH_BASH_COMPAT(case TFUNCTION:) 11918 IF_BASH_FUNCTION(case TFUNCTION:)
11793 case TWORD: 11919 case TWORD:
11794 case TREDIR: 11920 case TREDIR:
11795 tokpushback = 1; 11921 tokpushback = 1;
@@ -11822,7 +11948,7 @@ parse_command(void)
11822 return n1; 11948 return n1;
11823} 11949}
11824 11950
11825#if ENABLE_ASH_BASH_COMPAT 11951#if BASH_DOLLAR_SQUOTE
11826static int 11952static int
11827decode_dollar_squote(void) 11953decode_dollar_squote(void)
11828{ 11954{
@@ -11907,7 +12033,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11907 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ 12033 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
11908 int dqvarnest; /* levels of variables expansion within double quotes */ 12034 int dqvarnest; /* levels of variables expansion within double quotes */
11909 12035
11910 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) 12036 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
11911 12037
11912 startlinno = g_parsefile->linno; 12038 startlinno = g_parsefile->linno;
11913 bqlist = NULL; 12039 bqlist = NULL;
@@ -11942,7 +12068,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11942 USTPUTC(c, out); 12068 USTPUTC(c, out);
11943 break; 12069 break;
11944 case CCTL: 12070 case CCTL:
11945#if ENABLE_ASH_BASH_COMPAT 12071#if BASH_DOLLAR_SQUOTE
11946 if (c == '\\' && bash_dollar_squote) { 12072 if (c == '\\' && bash_dollar_squote) {
11947 c = decode_dollar_squote(); 12073 c = decode_dollar_squote();
11948 if (c == '\0') { 12074 if (c == '\0') {
@@ -12003,7 +12129,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12003 dblquote = 1; 12129 dblquote = 1;
12004 goto quotemark; 12130 goto quotemark;
12005 case CENDQUOTE: 12131 case CENDQUOTE:
12006 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) 12132 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
12007 if (eofmark != NULL && varnest == 0) { 12133 if (eofmark != NULL && varnest == 0) {
12008 USTPUTC(c, out); 12134 USTPUTC(c, out);
12009 } else { 12135 } else {
@@ -12062,7 +12188,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12062 break; 12188 break;
12063 default: 12189 default:
12064 if (varnest == 0) { 12190 if (varnest == 0) {
12065#if ENABLE_ASH_BASH_COMPAT 12191#if BASH_REDIR_OUTPUT
12066 if (c == '&') { 12192 if (c == '&') {
12067//Can't call pgetc_eatbnl() here, this requires three-deep pungetc() 12193//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
12068 if (pgetc() == '>') 12194 if (pgetc() == '>')
@@ -12094,7 +12220,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12094 len = out - (char *)stackblock(); 12220 len = out - (char *)stackblock();
12095 out = stackblock(); 12221 out = stackblock();
12096 if (eofmark == NULL) { 12222 if (eofmark == NULL) {
12097 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>')) 12223 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
12098 && quotef == 0 12224 && quotef == 0
12099 ) { 12225 ) {
12100 if (isdigit_str9(out)) { 12226 if (isdigit_str9(out)) {
@@ -12182,7 +12308,7 @@ parseredir: {
12182 pungetc(); 12308 pungetc();
12183 } 12309 }
12184 } 12310 }
12185#if ENABLE_ASH_BASH_COMPAT 12311#if BASH_REDIR_OUTPUT
12186 else if (c == 0x100 + '>') { /* this flags &> redirection */ 12312 else if (c == 0x100 + '>') { /* this flags &> redirection */
12187 np->nfile.fd = 1; 12313 np->nfile.fd = 1;
12188 pgetc(); /* this is '>', no need to check */ 12314 pgetc(); /* this is '>', no need to check */
@@ -12248,7 +12374,7 @@ parsesub: {
12248 if (c > 255 /* PEOA or PEOF */ 12374 if (c > 255 /* PEOA or PEOF */
12249 || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) 12375 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
12250 ) { 12376 ) {
12251#if ENABLE_ASH_BASH_COMPAT 12377#if BASH_DOLLAR_SQUOTE
12252 if (syntax != DQSYNTAX && c == '\'') 12378 if (syntax != DQSYNTAX && c == '\'')
12253 bash_dollar_squote = 1; 12379 bash_dollar_squote = 1;
12254 else 12380 else
@@ -12324,7 +12450,7 @@ parsesub: {
12324 switch (c) { 12450 switch (c) {
12325 case ':': 12451 case ':':
12326 c = pgetc_eatbnl(); 12452 c = pgetc_eatbnl();
12327#if ENABLE_ASH_BASH_COMPAT 12453#if BASH_SUBSTR
12328 /* This check is only needed to not misinterpret 12454 /* This check is only needed to not misinterpret
12329 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} 12455 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12330 * constructs. 12456 * constructs.
@@ -12354,7 +12480,7 @@ parsesub: {
12354 subtype++; 12480 subtype++;
12355 break; 12481 break;
12356 } 12482 }
12357#if ENABLE_ASH_BASH_COMPAT 12483#if BASH_PATTERN_SUBST
12358 case '/': 12484 case '/':
12359 /* ${v/[/]pattern/repl} */ 12485 /* ${v/[/]pattern/repl} */
12360//TODO: encode pattern and repl separately. 12486//TODO: encode pattern and repl separately.
@@ -12609,7 +12735,7 @@ xxreadtoken(void)
12609 p += xxreadtoken_doubles + 1; 12735 p += xxreadtoken_doubles + 1;
12610 } else { 12736 } else {
12611 pungetc(); 12737 pungetc();
12612#if ENABLE_ASH_BASH_COMPAT 12738#if BASH_REDIR_OUTPUT
12613 if (c == '&' && cc == '>') /* &> */ 12739 if (c == '&' && cc == '>') /* &> */
12614 break; /* return readtoken1(...) */ 12740 break; /* return readtoken1(...) */
12615#endif 12741#endif
@@ -12999,16 +13125,7 @@ find_dot_file(char *name)
12999 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) 13125 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\')))
13000 return name; 13126 return name;
13001 13127
13002 /* IIRC standards do not say whether . is to be searched.
13003 * And it is even smaller this way, making it unconditional for now:
13004 */
13005 if (1) { /* ENABLE_ASH_BASH_COMPAT */
13006 fullname = name;
13007 goto try_cur_dir;
13008 }
13009
13010 while ((fullname = path_advance(&path, name)) != NULL) { 13128 while ((fullname = path_advance(&path, name)) != NULL) {
13011 try_cur_dir:
13012 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { 13129 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
13013 /* 13130 /*
13014 * Don't bother freeing here, since it will 13131 * Don't bother freeing here, since it will
@@ -13032,6 +13149,7 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
13032 int status = 0; 13149 int status = 0;
13033 char *fullname; 13150 char *fullname;
13034 char **argv; 13151 char **argv;
13152 char *args_need_save;
13035 struct strlist *sp; 13153 struct strlist *sp;
13036 volatile struct shparam saveparam; 13154 volatile struct shparam saveparam;
13037 13155
@@ -13051,7 +13169,8 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
13051 */ 13169 */
13052 fullname = find_dot_file(argv[0]); 13170 fullname = find_dot_file(argv[0]);
13053 argv++; 13171 argv++;
13054 if (argv[0]) { /* . FILE ARGS, ARGS exist */ 13172 args_need_save = argv[0];
13173 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
13055 int argc; 13174 int argc;
13056 saveparam = shellparam; 13175 saveparam = shellparam;
13057 shellparam.malloced = 0; 13176 shellparam.malloced = 0;
@@ -13070,7 +13189,7 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
13070 status = cmdloop(0); 13189 status = cmdloop(0);
13071 popfile(); 13190 popfile();
13072 13191
13073 if (argv[0]) { 13192 if (args_need_save) {
13074 freeparam(&shellparam); 13193 freeparam(&shellparam);
13075 shellparam = saveparam; 13194 shellparam = saveparam;
13076 }; 13195 };
@@ -13902,9 +14021,11 @@ init(void)
13902 setvareq((char*)defoptindvar, VTEXTFIXED); 14021 setvareq((char*)defoptindvar, VTEXTFIXED);
13903 14022
13904 setvar0("PPID", utoa(getppid())); 14023 setvar0("PPID", utoa(getppid()));
13905#if ENABLE_ASH_BASH_COMPAT 14024#if BASH_SHLVL_VAR
13906 p = lookupvar("SHLVL"); 14025 p = lookupvar("SHLVL");
13907 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); 14026 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
14027#endif
14028#if BASH_HOSTNAME_VAR
13908 if (!lookupvar("HOSTNAME")) { 14029 if (!lookupvar("HOSTNAME")) {
13909 struct utsname uts; 14030 struct utsname uts;
13910 uname(&uts); 14031 uname(&uts);
@@ -13967,7 +14088,7 @@ procargs(char **argv)
13967#if DEBUG == 2 14088#if DEBUG == 2
13968 debug = 1; 14089 debug = 1;
13969#endif 14090#endif
13970 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 14091 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
13971 if (xminusc) { 14092 if (xminusc) {
13972 minusc = *xargv++; 14093 minusc = *xargv++;
13973 if (*xargv) 14094 if (*xargv)
@@ -14174,9 +14295,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14174 if (!hp) { 14295 if (!hp) {
14175 hp = lookupvar("HOME"); 14296 hp = lookupvar("HOME");
14176 if (hp) { 14297 if (hp) {
14298 INT_OFF;
14177 hp = concat_path_file(hp, ".ash_history"); 14299 hp = concat_path_file(hp, ".ash_history");
14178 setvar0("HISTFILE", hp); 14300 setvar0("HISTFILE", hp);
14179 free((char*)hp); 14301 free((char*)hp);
14302 INT_ON;
14180 hp = lookupvar("HISTFILE"); 14303 hp = lookupvar("HISTFILE");
14181 } 14304 }
14182 } 14305 }
diff --git a/shell/ash_test/ash-misc/source_argv_and_shift.right b/shell/ash_test/ash-misc/source_argv_and_shift.right
new file mode 100644
index 000000000..b15cc96e7
--- /dev/null
+++ b/shell/ash_test/ash-misc/source_argv_and_shift.right
@@ -0,0 +1,4 @@
1sourced_arg1:1
2arg1:
3sourced_arg1:a
4arg1:1
diff --git a/shell/ash_test/ash-misc/source_argv_and_shift.tests b/shell/ash_test/ash-misc/source_argv_and_shift.tests
new file mode 100755
index 000000000..66353f3d7
--- /dev/null
+++ b/shell/ash_test/ash-misc/source_argv_and_shift.tests
@@ -0,0 +1,12 @@
1echo 'echo sourced_arg1:$1' >sourced1
2echo 'shift' >>sourced1
3
4set -- 1
5. ./sourced1
6echo arg1:$1
7
8set -- 1
9. ./sourced1 a
10echo arg1:$1
11
12rm sourced1
diff --git a/shell/hush.c b/shell/hush.c
index a56d3b280..4123cc19e 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,7 +44,6 @@
44 * special variables (done: PWD, PPID, RANDOM) 44 * special variables (done: PWD, PPID, RANDOM)
45 * tilde expansion 45 * tilde expansion
46 * aliases 46 * aliases
47 * kill %jobspec
48 * follow IFS rules more precisely, including update semantics 47 * follow IFS rules more precisely, including update semantics
49 * builtins mandated by standards we don't support: 48 * builtins mandated by standards we don't support:
50 * [un]alias, command, fc, getopts, newgrp, readonly, times 49 * [un]alias, command, fc, getopts, newgrp, readonly, times
@@ -100,8 +99,6 @@
100//config: bool "bash-compatible extensions" 99//config: bool "bash-compatible extensions"
101//config: default y 100//config: default y
102//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 101//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
103//config: help
104//config: Enable bash-compatible extensions.
105//config: 102//config:
106//config:config HUSH_BRACE_EXPANSION 103//config:config HUSH_BRACE_EXPANSION
107//config: bool "Brace expansion" 104//config: bool "Brace expansion"
@@ -110,13 +107,6 @@
110//config: help 107//config: help
111//config: Enable {abc,def} extension. 108//config: Enable {abc,def} extension.
112//config: 109//config:
113//config:config HUSH_HELP
114//config: bool "help builtin"
115//config: default y
116//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config: help
118//config: Enable help builtin in hush. Code size + ~1 kbyte.
119//config:
120//config:config HUSH_INTERACTIVE 110//config:config HUSH_INTERACTIVE
121//config: bool "Interactive mode" 111//config: bool "Interactive mode"
122//config: default y 112//config: default y
@@ -131,8 +121,6 @@
131//config: bool "Save command history to .hush_history" 121//config: bool "Save command history to .hush_history"
132//config: default y 122//config: default y
133//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY 123//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
134//config: help
135//config: Enable history saving in hush.
136//config: 124//config:
137//config:config HUSH_JOB 125//config:config HUSH_JOB
138//config: bool "Job control" 126//config: bool "Job control"
@@ -146,42 +134,38 @@
146//config: but no separate process group is formed. 134//config: but no separate process group is formed.
147//config: 135//config:
148//config:config HUSH_TICK 136//config:config HUSH_TICK
149//config: bool "Process substitution" 137//config: bool "Support process substitution"
150//config: default y 138//config: default y
151//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 139//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
152//config: help 140//config: help
153//config: Enable process substitution `command` and $(command) in hush. 141//config: Enable `command` and $(command).
154//config: 142//config:
155//config:config HUSH_IF 143//config:config HUSH_IF
156//config: bool "Support if/then/elif/else/fi" 144//config: bool "Support if/then/elif/else/fi"
157//config: default y 145//config: default y
158//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 146//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
159//config: help
160//config: Enable if/then/elif/else/fi in hush.
161//config: 147//config:
162//config:config HUSH_LOOPS 148//config:config HUSH_LOOPS
163//config: bool "Support for, while and until loops" 149//config: bool "Support for, while and until loops"
164//config: default y 150//config: default y
165//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 151//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
166//config: help
167//config: Enable for, while and until loops in hush.
168//config: 152//config:
169//config:config HUSH_CASE 153//config:config HUSH_CASE
170//config: bool "Support case ... esac statement" 154//config: bool "Support case ... esac statement"
171//config: default y 155//config: default y
172//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 156//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
173//config: help 157//config: help
174//config: Enable case ... esac statement in hush. +400 bytes. 158//config: Enable case ... esac statement. +400 bytes.
175//config: 159//config:
176//config:config HUSH_FUNCTIONS 160//config:config HUSH_FUNCTIONS
177//config: bool "Support funcname() { commands; } syntax" 161//config: bool "Support funcname() { commands; } syntax"
178//config: default y 162//config: default y
179//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 163//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
180//config: help 164//config: help
181//config: Enable support for shell functions in hush. +800 bytes. 165//config: Enable support for shell functions. +800 bytes.
182//config: 166//config:
183//config:config HUSH_LOCAL 167//config:config HUSH_LOCAL
184//config: bool "Support local builtin" 168//config: bool "local builtin"
185//config: default y 169//config: default y
186//config: depends on HUSH_FUNCTIONS 170//config: depends on HUSH_FUNCTIONS
187//config: help 171//config: help
@@ -195,20 +179,95 @@
195//config: Enable pseudorandom generator and dynamic variable "$RANDOM". 179//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
196//config: Each read of "$RANDOM" will generate a new pseudorandom value. 180//config: Each read of "$RANDOM" will generate a new pseudorandom value.
197//config: 181//config:
182//config:config HUSH_MODE_X
183//config: bool "Support 'hush -x' option and 'set -x' command"
184//config: default y
185//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
186//config: help
187//config: This instructs hush to print commands before execution.
188//config: Adds ~300 bytes.
189//config:
190//config:config HUSH_ECHO
191//config: bool "echo builtin"
192//config: default y
193//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
194//config:
195//config:config HUSH_PRINTF
196//config: bool "printf builtin"
197//config: default y
198//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
199//config:
200//config:config HUSH_TEST
201//config: bool "test builtin"
202//config: default y
203//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
204//config:
205//config:config HUSH_HELP
206//config: bool "help builtin"
207//config: default y
208//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
209//config:
210//config:config HUSH_EXPORT
211//config: bool "export builtin"
212//config: default y
213//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
214//config:
198//config:config HUSH_EXPORT_N 215//config:config HUSH_EXPORT_N
199//config: bool "Support 'export -n' option" 216//config: bool "Support 'export -n' option"
200//config: default y 217//config: default y
201//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 218//config: depends on HUSH_EXPORT
202//config: help 219//config: help
203//config: export -n unexports variables. It is a bash extension. 220//config: export -n unexports variables. It is a bash extension.
204//config: 221//config:
205//config:config HUSH_MODE_X 222//config:config HUSH_KILL
206//config: bool "Support 'hush -x' option and 'set -x' command" 223//config: bool "kill builtin (supports kill %jobspec)"
224//config: default y
225//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
226//config:
227//config:config HUSH_WAIT
228//config: bool "wait builtin"
229//config: default y
230//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
231//config:
232//config:config HUSH_TRAP
233//config: bool "trap builtin"
234//config: default y
235//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
236//config:
237//config:config HUSH_TYPE
238//config: bool "type builtin"
239//config: default y
240//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
241//config:
242//config:config HUSH_READ
243//config: bool "read builtin"
244//config: default y
245//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
246//config:
247//config:config HUSH_SET
248//config: bool "set builtin"
207//config: default y 249//config: default y
208//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 250//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
209//config: help 251//config:
210//config: This instructs hush to print commands before execution. 252//config:config HUSH_UNSET
211//config: Adds ~300 bytes. 253//config: bool "unset builtin"
254//config: default y
255//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
256//config:
257//config:config HUSH_ULIMIT
258//config: bool "ulimit builtin"
259//config: default y
260//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
261//config:
262//config:config HUSH_UMASK
263//config: bool "umask builtin"
264//config: default y
265//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
266//config:
267//config:config HUSH_MEMLEAK
268//config: bool "memleak builtin (debugging)"
269//config: default n
270//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
212//config: 271//config:
213//config:config MSH 272//config:config MSH
214//config: bool "msh (deprecated: aliased to hush)" 273//config: bool "msh (deprecated: aliased to hush)"
@@ -218,8 +277,9 @@
218//config: msh is deprecated and will be removed, please migrate to hush. 277//config: msh is deprecated and will be removed, please migrate to hush.
219 278
220//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) 279//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
221//applet:IF_MSH(APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 280// APPLET_ODDNAME:name main location suid_type help
222//applet:IF_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 281//applet:IF_MSH( APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
282//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
223//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 283//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
224 284
225//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o 285//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
@@ -267,6 +327,15 @@
267#endif 327#endif
268 328
269 329
330/* So far, all bash compat is controlled by one config option */
331/* Separate defines document which part of code implements what */
332#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
333#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
334#define BASH_TEST2 ENABLE_HUSH_BASH_COMPAT
335#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
336#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
337
338
270/* Build knobs */ 339/* Build knobs */
271#define LEAK_HUNTING 0 340#define LEAK_HUNTING 0
272#define BUILD_AS_NOMMU 0 341#define BUILD_AS_NOMMU 0
@@ -347,12 +416,12 @@
347 416
348#define ERR_PTR ((void*)(long)1) 417#define ERR_PTR ((void*)(long)1)
349 418
350#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 419#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
351 420
352#define _SPECIAL_VARS_STR "_*@$!?#" 421#define _SPECIAL_VARS_STR "_*@$!?#"
353#define SPECIAL_VARS_STR ("_*@$!?#" + 1) 422#define SPECIAL_VARS_STR ("_*@$!?#" + 1)
354#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) 423#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
355#if ENABLE_HUSH_BASH_COMPAT 424#if BASH_PATTERN_SUBST
356/* Support / and // replace ops */ 425/* Support / and // replace ops */
357/* Note that // is stored as \ in "encoded" string representation */ 426/* Note that // is stored as \ in "encoded" string representation */
358# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?" 427# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
@@ -513,7 +582,7 @@ struct command {
513 smallint cmd_type; /* CMD_xxx */ 582 smallint cmd_type; /* CMD_xxx */
514#define CMD_NORMAL 0 583#define CMD_NORMAL 0
515#define CMD_SUBSHELL 1 584#define CMD_SUBSHELL 1
516#if ENABLE_HUSH_BASH_COMPAT 585#if BASH_TEST2
517/* used for "[[ EXPR ]]" */ 586/* used for "[[ EXPR ]]" */
518# define CMD_SINGLEWORD_NOGLOB 2 587# define CMD_SINGLEWORD_NOGLOB 2
519#endif 588#endif
@@ -563,7 +632,7 @@ struct pipe {
563 int alive_cmds; /* number of commands running (not exited) */ 632 int alive_cmds; /* number of commands running (not exited) */
564 int stopped_cmds; /* number of commands alive, but stopped */ 633 int stopped_cmds; /* number of commands alive, but stopped */
565#if ENABLE_HUSH_JOB 634#if ENABLE_HUSH_JOB
566 int jobid; /* job number */ 635 unsigned jobid; /* job number */
567 pid_t pgrp; /* process group ID for the job */ 636 pid_t pgrp; /* process group ID for the job */
568 char *cmdtext; /* name of job */ 637 char *cmdtext; /* name of job */
569#endif 638#endif
@@ -740,7 +809,7 @@ struct globals {
740#endif 809#endif
741#if ENABLE_HUSH_JOB 810#if ENABLE_HUSH_JOB
742 int run_list_level; 811 int run_list_level;
743 int last_jobid; 812 unsigned last_jobid;
744 pid_t saved_tty_pgrp; 813 pid_t saved_tty_pgrp;
745 struct pipe *job_list; 814 struct pipe *job_list;
746# define G_saved_tty_pgrp (G.saved_tty_pgrp) 815# define G_saved_tty_pgrp (G.saved_tty_pgrp)
@@ -770,8 +839,13 @@ struct globals {
770 smallint exiting; /* used to prevent EXIT trap recursion */ 839 smallint exiting; /* used to prevent EXIT trap recursion */
771 /* These four support $?, $#, and $1 */ 840 /* These four support $?, $#, and $1 */
772 smalluint last_exitcode; 841 smalluint last_exitcode;
842#if ENABLE_HUSH_SET
773 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 843 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
774 smalluint global_args_malloced; 844 smalluint global_args_malloced;
845# define G_global_args_malloced (G.global_args_malloced)
846#else
847# define G_global_args_malloced 0
848#endif
775 /* how many non-NULL argv's we have. NB: $# + 1 */ 849 /* how many non-NULL argv's we have. NB: $# + 1 */
776 int global_argc; 850 int global_argc;
777 char **global_argv; 851 char **global_argv;
@@ -810,14 +884,21 @@ struct globals {
810 unsigned special_sig_mask; 884 unsigned special_sig_mask;
811#if ENABLE_HUSH_JOB 885#if ENABLE_HUSH_JOB
812 unsigned fatal_sig_mask; 886 unsigned fatal_sig_mask;
813# define G_fatal_sig_mask G.fatal_sig_mask 887# define G_fatal_sig_mask (G.fatal_sig_mask)
814#else 888#else
815# define G_fatal_sig_mask 0 889# define G_fatal_sig_mask 0
816#endif 890#endif
891#if ENABLE_HUSH_TRAP
817 char **traps; /* char *traps[NSIG] */ 892 char **traps; /* char *traps[NSIG] */
893# define G_traps G.traps
894#else
895# define G_traps ((char**)NULL)
896#endif
818 sigset_t pending_set; 897 sigset_t pending_set;
819#if HUSH_DEBUG 898#if ENABLE_HUSH_MEMLEAK
820 unsigned long memleak_value; 899 unsigned long memleak_value;
900#endif
901#if HUSH_DEBUG
821 int debug_indent; 902 int debug_indent;
822#endif 903#endif
823 struct sigaction sa; 904 struct sigaction sa;
@@ -839,11 +920,15 @@ struct globals {
839 920
840/* Function prototypes for builtins */ 921/* Function prototypes for builtins */
841static int builtin_cd(char **argv) FAST_FUNC; 922static int builtin_cd(char **argv) FAST_FUNC;
923#if ENABLE_HUSH_ECHO
842static int builtin_echo(char **argv) FAST_FUNC; 924static int builtin_echo(char **argv) FAST_FUNC;
925#endif
843static int builtin_eval(char **argv) FAST_FUNC; 926static int builtin_eval(char **argv) FAST_FUNC;
844static int builtin_exec(char **argv) FAST_FUNC; 927static int builtin_exec(char **argv) FAST_FUNC;
845static int builtin_exit(char **argv) FAST_FUNC; 928static int builtin_exit(char **argv) FAST_FUNC;
929#if ENABLE_HUSH_EXPORT
846static int builtin_export(char **argv) FAST_FUNC; 930static int builtin_export(char **argv) FAST_FUNC;
931#endif
847#if ENABLE_HUSH_JOB 932#if ENABLE_HUSH_JOB
848static int builtin_fg_bg(char **argv) FAST_FUNC; 933static int builtin_fg_bg(char **argv) FAST_FUNC;
849static int builtin_jobs(char **argv) FAST_FUNC; 934static int builtin_jobs(char **argv) FAST_FUNC;
@@ -857,24 +942,43 @@ static int builtin_history(char **argv) FAST_FUNC;
857#if ENABLE_HUSH_LOCAL 942#if ENABLE_HUSH_LOCAL
858static int builtin_local(char **argv) FAST_FUNC; 943static int builtin_local(char **argv) FAST_FUNC;
859#endif 944#endif
860#if HUSH_DEBUG 945#if ENABLE_HUSH_MEMLEAK
861static int builtin_memleak(char **argv) FAST_FUNC; 946static int builtin_memleak(char **argv) FAST_FUNC;
862#endif 947#endif
863#if ENABLE_PRINTF 948#if ENABLE_HUSH_PRINTF
864static int builtin_printf(char **argv) FAST_FUNC; 949static int builtin_printf(char **argv) FAST_FUNC;
865#endif 950#endif
866static int builtin_pwd(char **argv) FAST_FUNC; 951static int builtin_pwd(char **argv) FAST_FUNC;
952#if ENABLE_HUSH_READ
867static int builtin_read(char **argv) FAST_FUNC; 953static int builtin_read(char **argv) FAST_FUNC;
954#endif
955#if ENABLE_HUSH_SET
868static int builtin_set(char **argv) FAST_FUNC; 956static int builtin_set(char **argv) FAST_FUNC;
957#endif
869static int builtin_shift(char **argv) FAST_FUNC; 958static int builtin_shift(char **argv) FAST_FUNC;
870static int builtin_source(char **argv) FAST_FUNC; 959static int builtin_source(char **argv) FAST_FUNC;
960#if ENABLE_HUSH_TEST || BASH_TEST2
871static int builtin_test(char **argv) FAST_FUNC; 961static int builtin_test(char **argv) FAST_FUNC;
962#endif
963#if ENABLE_HUSH_TRAP
872static int builtin_trap(char **argv) FAST_FUNC; 964static int builtin_trap(char **argv) FAST_FUNC;
965#endif
966#if ENABLE_HUSH_TYPE
873static int builtin_type(char **argv) FAST_FUNC; 967static int builtin_type(char **argv) FAST_FUNC;
968#endif
874static int builtin_true(char **argv) FAST_FUNC; 969static int builtin_true(char **argv) FAST_FUNC;
970#if ENABLE_HUSH_UMASK
875static int builtin_umask(char **argv) FAST_FUNC; 971static int builtin_umask(char **argv) FAST_FUNC;
972#endif
973#if ENABLE_HUSH_UNSET
876static int builtin_unset(char **argv) FAST_FUNC; 974static int builtin_unset(char **argv) FAST_FUNC;
975#endif
976#if ENABLE_HUSH_KILL
977static int builtin_kill(char **argv) FAST_FUNC;
978#endif
979#if ENABLE_HUSH_WAIT
877static int builtin_wait(char **argv) FAST_FUNC; 980static int builtin_wait(char **argv) FAST_FUNC;
981#endif
878#if ENABLE_HUSH_LOOPS 982#if ENABLE_HUSH_LOOPS
879static int builtin_break(char **argv) FAST_FUNC; 983static int builtin_break(char **argv) FAST_FUNC;
880static int builtin_continue(char **argv) FAST_FUNC; 984static int builtin_continue(char **argv) FAST_FUNC;
@@ -901,13 +1005,13 @@ struct built_in_command {
901}; 1005};
902 1006
903static const struct built_in_command bltins1[] = { 1007static const struct built_in_command bltins1[] = {
904 BLTIN("." , builtin_source , "Run commands in a file"), 1008 BLTIN("." , builtin_source , "Run commands in file"),
905 BLTIN(":" , builtin_true , NULL), 1009 BLTIN(":" , builtin_true , NULL),
906#if ENABLE_HUSH_JOB 1010#if ENABLE_HUSH_JOB
907 BLTIN("bg" , builtin_fg_bg , "Resume a job in the background"), 1011 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
908#endif 1012#endif
909#if ENABLE_HUSH_LOOPS 1013#if ENABLE_HUSH_LOOPS
910 BLTIN("break" , builtin_break , "Exit from a loop"), 1014 BLTIN("break" , builtin_break , "Exit loop"),
911#endif 1015#endif
912 BLTIN("cd" , builtin_cd , "Change directory"), 1016 BLTIN("cd" , builtin_cd , "Change directory"),
913#if ENABLE_HUSH_LOOPS 1017#if ENABLE_HUSH_LOOPS
@@ -915,53 +1019,84 @@ static const struct built_in_command bltins1[] = {
915#endif 1019#endif
916 BLTIN("eval" , builtin_eval , "Construct and run shell command"), 1020 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
917 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"), 1021 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
918 BLTIN("exit" , builtin_exit , "Exit"), 1022 BLTIN("exit" , builtin_exit , NULL),
1023#if ENABLE_HUSH_EXPORT
919 BLTIN("export" , builtin_export , "Set environment variables"), 1024 BLTIN("export" , builtin_export , "Set environment variables"),
1025#endif
920#if ENABLE_HUSH_JOB 1026#if ENABLE_HUSH_JOB
921 BLTIN("fg" , builtin_fg_bg , "Bring job into the foreground"), 1027 BLTIN("fg" , builtin_fg_bg , "Bring job into foreground"),
922#endif 1028#endif
923#if ENABLE_HUSH_HELP 1029#if ENABLE_HUSH_HELP
924 BLTIN("help" , builtin_help , NULL), 1030 BLTIN("help" , builtin_help , NULL),
925#endif 1031#endif
926#if MAX_HISTORY && ENABLE_FEATURE_EDITING 1032#if MAX_HISTORY && ENABLE_FEATURE_EDITING
927 BLTIN("history" , builtin_history , "Show command history"), 1033 BLTIN("history" , builtin_history , "Show history"),
928#endif 1034#endif
929#if ENABLE_HUSH_JOB 1035#if ENABLE_HUSH_JOB
930 BLTIN("jobs" , builtin_jobs , "List jobs"), 1036 BLTIN("jobs" , builtin_jobs , "List jobs"),
931#endif 1037#endif
1038#if ENABLE_HUSH_KILL
1039 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1040#endif
932#if ENABLE_HUSH_LOCAL 1041#if ENABLE_HUSH_LOCAL
933 BLTIN("local" , builtin_local , "Set local variables"), 1042 BLTIN("local" , builtin_local , "Set local variables"),
934#endif 1043#endif
935#if HUSH_DEBUG 1044#if ENABLE_HUSH_MEMLEAK
936 BLTIN("memleak" , builtin_memleak , NULL), 1045 BLTIN("memleak" , builtin_memleak , NULL),
937#endif 1046#endif
1047#if ENABLE_HUSH_READ
938 BLTIN("read" , builtin_read , "Input into variable"), 1048 BLTIN("read" , builtin_read , "Input into variable"),
1049#endif
939#if ENABLE_HUSH_FUNCTIONS 1050#if ENABLE_HUSH_FUNCTIONS
940 BLTIN("return" , builtin_return , "Return from a function"), 1051 BLTIN("return" , builtin_return , "Return from function"),
1052#endif
1053#if ENABLE_HUSH_SET
1054 BLTIN("set" , builtin_set , "Set positional parameters"),
941#endif 1055#endif
942 BLTIN("set" , builtin_set , "Set/unset positional parameters"),
943 BLTIN("shift" , builtin_shift , "Shift positional parameters"), 1056 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
944#if ENABLE_HUSH_BASH_COMPAT 1057#if BASH_SOURCE
945 BLTIN("source" , builtin_source , "Run commands in a file"), 1058 BLTIN("source" , builtin_source , NULL),
946#endif 1059#endif
1060#if ENABLE_HUSH_TRAP
947 BLTIN("trap" , builtin_trap , "Trap signals"), 1061 BLTIN("trap" , builtin_trap , "Trap signals"),
1062#endif
948 BLTIN("true" , builtin_true , NULL), 1063 BLTIN("true" , builtin_true , NULL),
1064#if ENABLE_HUSH_TYPE
949 BLTIN("type" , builtin_type , "Show command type"), 1065 BLTIN("type" , builtin_type , "Show command type"),
950 BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), 1066#endif
1067#if ENABLE_HUSH_ULIMIT
1068 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1069#endif
1070#if ENABLE_HUSH_UMASK
951 BLTIN("umask" , builtin_umask , "Set file creation mask"), 1071 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1072#endif
1073#if ENABLE_HUSH_UNSET
952 BLTIN("unset" , builtin_unset , "Unset variables"), 1074 BLTIN("unset" , builtin_unset , "Unset variables"),
1075#endif
1076#if ENABLE_HUSH_WAIT
953 BLTIN("wait" , builtin_wait , "Wait for process"), 1077 BLTIN("wait" , builtin_wait , "Wait for process"),
1078#endif
954}; 1079};
955/* For now, echo and test are unconditionally enabled. 1080/* These builtins won't be used if we are on NOMMU and need to re-exec
956 * Maybe make it configurable? */ 1081 * (it's cheaper to run an external program in this case):
1082 */
957static const struct built_in_command bltins2[] = { 1083static const struct built_in_command bltins2[] = {
1084#if ENABLE_HUSH_TEST
958 BLTIN("[" , builtin_test , NULL), 1085 BLTIN("[" , builtin_test , NULL),
1086#endif
1087#if BASH_TEST2
1088 BLTIN("[[" , builtin_test , NULL),
1089#endif
1090#if ENABLE_HUSH_ECHO
959 BLTIN("echo" , builtin_echo , NULL), 1091 BLTIN("echo" , builtin_echo , NULL),
960#if ENABLE_PRINTF 1092#endif
1093#if ENABLE_HUSH_PRINTF
961 BLTIN("printf" , builtin_printf , NULL), 1094 BLTIN("printf" , builtin_printf , NULL),
962#endif 1095#endif
963 BLTIN("pwd" , builtin_pwd , NULL), 1096 BLTIN("pwd" , builtin_pwd , NULL),
1097#if ENABLE_HUSH_TEST
964 BLTIN("test" , builtin_test , NULL), 1098 BLTIN("test" , builtin_test , NULL),
1099#endif
965}; 1100};
966 1101
967 1102
@@ -1346,7 +1481,7 @@ typedef struct save_arg_t {
1346 char *sv_argv0; 1481 char *sv_argv0;
1347 char **sv_g_argv; 1482 char **sv_g_argv;
1348 int sv_g_argc; 1483 int sv_g_argc;
1349 smallint sv_g_malloced; 1484 IF_HUSH_SET(smallint sv_g_malloced;)
1350} save_arg_t; 1485} save_arg_t;
1351 1486
1352static void save_and_replace_G_args(save_arg_t *sv, char **argv) 1487static void save_and_replace_G_args(save_arg_t *sv, char **argv)
@@ -1356,11 +1491,11 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1356 sv->sv_argv0 = argv[0]; 1491 sv->sv_argv0 = argv[0];
1357 sv->sv_g_argv = G.global_argv; 1492 sv->sv_g_argv = G.global_argv;
1358 sv->sv_g_argc = G.global_argc; 1493 sv->sv_g_argc = G.global_argc;
1359 sv->sv_g_malloced = G.global_args_malloced; 1494 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1360 1495
1361 argv[0] = G.global_argv[0]; /* retain $0 */ 1496 argv[0] = G.global_argv[0]; /* retain $0 */
1362 G.global_argv = argv; 1497 G.global_argv = argv;
1363 G.global_args_malloced = 0; 1498 IF_HUSH_SET(G.global_args_malloced = 0;)
1364 1499
1365 n = 1; 1500 n = 1;
1366 while (*++argv) 1501 while (*++argv)
@@ -1370,19 +1505,19 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1370 1505
1371static void restore_G_args(save_arg_t *sv, char **argv) 1506static void restore_G_args(save_arg_t *sv, char **argv)
1372{ 1507{
1373 char **pp; 1508#if ENABLE_HUSH_SET
1374
1375 if (G.global_args_malloced) { 1509 if (G.global_args_malloced) {
1376 /* someone ran "set -- arg1 arg2 ...", undo */ 1510 /* someone ran "set -- arg1 arg2 ...", undo */
1377 pp = G.global_argv; 1511 char **pp = G.global_argv;
1378 while (*++pp) /* note: does not free $0 */ 1512 while (*++pp) /* note: does not free $0 */
1379 free(*pp); 1513 free(*pp);
1380 free(G.global_argv); 1514 free(G.global_argv);
1381 } 1515 }
1516#endif
1382 argv[0] = sv->sv_argv0; 1517 argv[0] = sv->sv_argv0;
1383 G.global_argv = sv->sv_g_argv; 1518 G.global_argv = sv->sv_g_argv;
1384 G.global_argc = sv->sv_g_argc; 1519 G.global_argc = sv->sv_g_argc;
1385 G.global_args_malloced = sv->sv_g_malloced; 1520 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1386} 1521}
1387 1522
1388 1523
@@ -1670,13 +1805,13 @@ static void hush_exit(int exitcode)
1670#endif 1805#endif
1671 1806
1672 fflush_all(); 1807 fflush_all();
1673 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { 1808 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
1674 char *argv[3]; 1809 char *argv[3];
1675 /* argv[0] is unused */ 1810 /* argv[0] is unused */
1676 argv[1] = G.traps[0]; 1811 argv[1] = G_traps[0];
1677 argv[2] = NULL; 1812 argv[2] = NULL;
1678 G.exiting = 1; /* prevent EXIT trap recursion */ 1813 G.exiting = 1; /* prevent EXIT trap recursion */
1679 /* Note: G.traps[0] is not cleared! 1814 /* Note: G_traps[0] is not cleared!
1680 * "trap" will still show it, if executed 1815 * "trap" will still show it, if executed
1681 * in the handler */ 1816 * in the handler */
1682 builtin_eval(argv); 1817 builtin_eval(argv);
@@ -1727,14 +1862,14 @@ static int check_and_run_traps(void)
1727 } while (sig < NSIG); 1862 } while (sig < NSIG);
1728 break; 1863 break;
1729 got_sig: 1864 got_sig:
1730 if (G.traps && G.traps[sig]) { 1865 if (G_traps && G_traps[sig]) {
1731 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); 1866 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
1732 if (G.traps[sig][0]) { 1867 if (G_traps[sig][0]) {
1733 /* We have user-defined handler */ 1868 /* We have user-defined handler */
1734 smalluint save_rcode; 1869 smalluint save_rcode;
1735 char *argv[3]; 1870 char *argv[3];
1736 /* argv[0] is unused */ 1871 /* argv[0] is unused */
1737 argv[1] = G.traps[sig]; 1872 argv[1] = G_traps[sig];
1738 argv[2] = NULL; 1873 argv[2] = NULL;
1739 save_rcode = G.last_exitcode; 1874 save_rcode = G.last_exitcode;
1740 builtin_eval(argv); 1875 builtin_eval(argv);
@@ -2030,10 +2165,12 @@ static int unset_local_var_len(const char *name, int name_len)
2030 return EXIT_SUCCESS; 2165 return EXIT_SUCCESS;
2031} 2166}
2032 2167
2168#if ENABLE_HUSH_UNSET
2033static int unset_local_var(const char *name) 2169static int unset_local_var(const char *name)
2034{ 2170{
2035 return unset_local_var_len(name, strlen(name)); 2171 return unset_local_var_len(name, strlen(name));
2036} 2172}
2173#endif
2037 2174
2038static void unset_vars(char **strings) 2175static void unset_vars(char **strings)
2039{ 2176{
@@ -2050,11 +2187,13 @@ static void unset_vars(char **strings)
2050 free(strings); 2187 free(strings);
2051} 2188}
2052 2189
2190#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ
2053static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2191static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2054{ 2192{
2055 char *var = xasprintf("%s=%s", name, val); 2193 char *var = xasprintf("%s=%s", name, val);
2056 set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 2194 set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
2057} 2195}
2196#endif
2058 2197
2059 2198
2060/* 2199/*
@@ -3507,7 +3646,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
3507 (ctx->ctx_res_w == RES_SNTX)); 3646 (ctx->ctx_res_w == RES_SNTX));
3508 return (ctx->ctx_res_w == RES_SNTX); 3647 return (ctx->ctx_res_w == RES_SNTX);
3509 } 3648 }
3510# if ENABLE_HUSH_BASH_COMPAT 3649# if BASH_TEST2
3511 if (strcmp(word->data, "[[") == 0) { 3650 if (strcmp(word->data, "[[") == 0) {
3512 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 3651 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
3513 } 3652 }
@@ -4105,7 +4244,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4105{ 4244{
4106 int ch; 4245 int ch;
4107 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; 4246 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
4108# if ENABLE_HUSH_BASH_COMPAT 4247# if BASH_SUBSTR || BASH_PATTERN_SUBST
4109 char end_char2 = end_ch >> 8; 4248 char end_char2 = end_ch >> 8;
4110# endif 4249# endif
4111 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 4250 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
@@ -4116,7 +4255,11 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4116 syntax_error_unterm_ch(end_ch); 4255 syntax_error_unterm_ch(end_ch);
4117 return 0; 4256 return 0;
4118 } 4257 }
4119 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { 4258 if (ch == end_ch
4259# if BASH_SUBSTR || BASH_PATTERN_SUBST
4260 || ch == end_char2
4261# endif
4262 ) {
4120 if (!dbl) 4263 if (!dbl)
4121 break; 4264 break;
4122 /* we look for closing )) of $((EXPR)) */ 4265 /* we look for closing )) of $((EXPR)) */
@@ -4269,14 +4412,14 @@ static int parse_dollar(o_string *as_string,
4269 4412
4270 /* Eat everything until closing '}' (or ':') */ 4413 /* Eat everything until closing '}' (or ':') */
4271 end_ch = '}'; 4414 end_ch = '}';
4272 if (ENABLE_HUSH_BASH_COMPAT 4415 if (BASH_SUBSTR
4273 && ch == ':' 4416 && ch == ':'
4274 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) 4417 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
4275 ) { 4418 ) {
4276 /* It's ${var:N[:M]} thing */ 4419 /* It's ${var:N[:M]} thing */
4277 end_ch = '}' * 0x100 + ':'; 4420 end_ch = '}' * 0x100 + ':';
4278 } 4421 }
4279 if (ENABLE_HUSH_BASH_COMPAT 4422 if (BASH_PATTERN_SUBST
4280 && ch == '/' 4423 && ch == '/'
4281 ) { 4424 ) {
4282 /* It's ${var/[/]pattern[/repl]} thing */ 4425 /* It's ${var/[/]pattern[/repl]} thing */
@@ -4303,7 +4446,9 @@ static int parse_dollar(o_string *as_string,
4303 o_addchr(as_string, last_ch); 4446 o_addchr(as_string, last_ch);
4304 } 4447 }
4305 4448
4306 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { 4449 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
4450 && (end_ch & 0xff00)
4451 ) {
4307 /* close the first block: */ 4452 /* close the first block: */
4308 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4453 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4309 /* while parsing N from ${var:N[:M]} 4454 /* while parsing N from ${var:N[:M]}
@@ -4314,7 +4459,7 @@ static int parse_dollar(o_string *as_string,
4314 goto again; 4459 goto again;
4315 } 4460 }
4316 /* got '}' */ 4461 /* got '}' */
4317 if (end_ch == '}' * 0x100 + ':') { 4462 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
4318 /* it's ${var:N} - emulate :999999999 */ 4463 /* it's ${var:N} - emulate :999999999 */
4319 o_addstr(dest, "999999999"); 4464 o_addstr(dest, "999999999");
4320 } /* else: it's ${var/[/]pattern} */ 4465 } /* else: it's ${var/[/]pattern} */
@@ -4389,7 +4534,7 @@ static int parse_dollar(o_string *as_string,
4389} 4534}
4390 4535
4391#if BB_MMU 4536#if BB_MMU
4392# if ENABLE_HUSH_BASH_COMPAT 4537# if BASH_PATTERN_SUBST
4393#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ 4538#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
4394 encode_string(dest, input, dquote_end, process_bkslash) 4539 encode_string(dest, input, dquote_end, process_bkslash)
4395# else 4540# else
@@ -4401,7 +4546,7 @@ static int parse_dollar(o_string *as_string,
4401 4546
4402#else /* !MMU */ 4547#else /* !MMU */
4403 4548
4404# if ENABLE_HUSH_BASH_COMPAT 4549# if BASH_PATTERN_SUBST
4405/* all parameters are needed, no macro tricks */ 4550/* all parameters are needed, no macro tricks */
4406# else 4551# else
4407#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ 4552#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
@@ -4414,7 +4559,7 @@ static int encode_string(o_string *as_string,
4414 int dquote_end, 4559 int dquote_end,
4415 int process_bkslash) 4560 int process_bkslash)
4416{ 4561{
4417#if !ENABLE_HUSH_BASH_COMPAT 4562#if !BASH_PATTERN_SUBST
4418 const int process_bkslash = 1; 4563 const int process_bkslash = 1;
4419#endif 4564#endif
4420 int ch; 4565 int ch;
@@ -5057,7 +5202,7 @@ static struct pipe *parse_stream(char **pstring,
5057/*** Execution routines ***/ 5202/*** Execution routines ***/
5058 5203
5059/* Expansion can recurse, need forward decls: */ 5204/* Expansion can recurse, need forward decls: */
5060#if !ENABLE_HUSH_BASH_COMPAT 5205#if !BASH_PATTERN_SUBST
5061/* only ${var/pattern/repl} (its pattern part) needs additional mode */ 5206/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5062#define expand_string_to_string(str, do_unbackslash) \ 5207#define expand_string_to_string(str, do_unbackslash) \
5063 expand_string_to_string(str) 5208 expand_string_to_string(str)
@@ -5178,7 +5323,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha
5178 * Returns malloced string. 5323 * Returns malloced string.
5179 * As an optimization, we return NULL if expansion is not needed. 5324 * As an optimization, we return NULL if expansion is not needed.
5180 */ 5325 */
5181#if !ENABLE_HUSH_BASH_COMPAT 5326#if !BASH_PATTERN_SUBST
5182/* only ${var/pattern/repl} (its pattern part) needs additional mode */ 5327/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5183#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \ 5328#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
5184 encode_then_expand_string(str) 5329 encode_then_expand_string(str)
@@ -5232,7 +5377,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5232} 5377}
5233#endif 5378#endif
5234 5379
5235#if ENABLE_HUSH_BASH_COMPAT 5380#if BASH_PATTERN_SUBST
5236/* ${var/[/]pattern[/repl]} helpers */ 5381/* ${var/[/]pattern[/repl]} helpers */
5237static char *strstr_pattern(char *val, const char *pattern, int *size) 5382static char *strstr_pattern(char *val, const char *pattern, int *size)
5238{ 5383{
@@ -5284,7 +5429,7 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
5284 debug_printf_varexp("result:'%s'\n", result); 5429 debug_printf_varexp("result:'%s'\n", result);
5285 return result; 5430 return result;
5286} 5431}
5287#endif 5432#endif /* BASH_PATTERN_SUBST */
5288 5433
5289/* Helper: 5434/* Helper:
5290 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. 5435 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
@@ -5332,7 +5477,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5332 if (exp_op == ':') { 5477 if (exp_op == ':') {
5333 exp_op = *exp_word++; 5478 exp_op = *exp_word++;
5334//TODO: try ${var:} and ${var:bogus} in non-bash config 5479//TODO: try ${var:} and ${var:bogus} in non-bash config
5335 if (ENABLE_HUSH_BASH_COMPAT 5480 if (BASH_SUBSTR
5336 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) 5481 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
5337 ) { 5482 ) {
5338 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ 5483 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
@@ -5414,7 +5559,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5414 } 5559 }
5415 } 5560 }
5416 } 5561 }
5417#if ENABLE_HUSH_BASH_COMPAT 5562#if BASH_PATTERN_SUBST
5418 else if (exp_op == '/' || exp_op == '\\') { 5563 else if (exp_op == '/' || exp_op == '\\') {
5419 /* It's ${var/[/]pattern[/repl]} thing. 5564 /* It's ${var/[/]pattern[/repl]} thing.
5420 * Note that in encoded form it has TWO parts: 5565 * Note that in encoded form it has TWO parts:
@@ -5461,9 +5606,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5461 free(repl); 5606 free(repl);
5462 } 5607 }
5463 } 5608 }
5464#endif 5609#endif /* BASH_PATTERN_SUBST */
5465 else if (exp_op == ':') { 5610 else if (exp_op == ':') {
5466#if ENABLE_HUSH_BASH_COMPAT && ENABLE_FEATURE_SH_MATH 5611#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
5467 /* It's ${var:N[:M]} bashism. 5612 /* It's ${var:N[:M]} bashism.
5468 * Note that in encoded form it has TWO parts: 5613 * Note that in encoded form it has TWO parts:
5469 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 5614 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -5499,7 +5644,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5499 } 5644 }
5500 debug_printf_varexp("val:'%s'\n", val); 5645 debug_printf_varexp("val:'%s'\n", val);
5501 } else 5646 } else
5502#endif 5647#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */
5503 { 5648 {
5504 die_if_script("malformed ${%s:...}", var); 5649 die_if_script("malformed ${%s:...}", var);
5505 val = NULL; 5650 val = NULL;
@@ -5789,7 +5934,7 @@ static char **expand_strvec_to_strvec(char **argv)
5789 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 5934 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
5790} 5935}
5791 5936
5792#if ENABLE_HUSH_BASH_COMPAT 5937#if BASH_TEST2
5793static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 5938static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
5794{ 5939{
5795 return expand_variables(argv, EXP_FLAG_SINGLEWORD); 5940 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
@@ -5804,7 +5949,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
5804 */ 5949 */
5805static char *expand_string_to_string(const char *str, int do_unbackslash) 5950static char *expand_string_to_string(const char *str, int do_unbackslash)
5806{ 5951{
5807#if !ENABLE_HUSH_BASH_COMPAT 5952#if !BASH_PATTERN_SUBST
5808 const int do_unbackslash = 1; 5953 const int do_unbackslash = 1;
5809#endif 5954#endif
5810 char *argv[2], **list; 5955 char *argv[2], **list;
@@ -5882,13 +6027,15 @@ static void switch_off_special_sigs(unsigned mask)
5882 sig++; 6027 sig++;
5883 if (!(mask & 1)) 6028 if (!(mask & 1))
5884 continue; 6029 continue;
5885 if (G.traps) { 6030#if ENABLE_HUSH_TRAP
5886 if (G.traps[sig] && !G.traps[sig][0]) 6031 if (G_traps) {
6032 if (G_traps[sig] && !G_traps[sig][0])
5887 /* trap is '', has to remain SIG_IGN */ 6033 /* trap is '', has to remain SIG_IGN */
5888 continue; 6034 continue;
5889 free(G.traps[sig]); 6035 free(G_traps[sig]);
5890 G.traps[sig] = NULL; 6036 G_traps[sig] = NULL;
5891 } 6037 }
6038#endif
5892 /* We are here only if no trap or trap was not '' */ 6039 /* We are here only if no trap or trap was not '' */
5893 install_sighandler(sig, SIG_DFL); 6040 install_sighandler(sig, SIG_DFL);
5894 } 6041 }
@@ -5905,7 +6052,7 @@ static void reset_traps_to_defaults(void)
5905 /* This function is always called in a child shell 6052 /* This function is always called in a child shell
5906 * after fork (not vfork, NOMMU doesn't use this function). 6053 * after fork (not vfork, NOMMU doesn't use this function).
5907 */ 6054 */
5908 unsigned sig; 6055 IF_HUSH_TRAP(unsigned sig;)
5909 unsigned mask; 6056 unsigned mask;
5910 6057
5911 /* Child shells are not interactive. 6058 /* Child shells are not interactive.
@@ -5914,35 +6061,37 @@ static void reset_traps_to_defaults(void)
5914 * Same goes for SIGTERM, SIGHUP, SIGINT. 6061 * Same goes for SIGTERM, SIGHUP, SIGINT.
5915 */ 6062 */
5916 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; 6063 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
5917 if (!G.traps && !mask) 6064 if (!G_traps && !mask)
5918 return; /* already no traps and no special sigs */ 6065 return; /* already no traps and no special sigs */
5919 6066
5920 /* Switch off special sigs */ 6067 /* Switch off special sigs */
5921 switch_off_special_sigs(mask); 6068 switch_off_special_sigs(mask);
5922#if ENABLE_HUSH_JOB 6069# if ENABLE_HUSH_JOB
5923 G_fatal_sig_mask = 0; 6070 G_fatal_sig_mask = 0;
5924#endif 6071# endif
5925 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; 6072 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
5926 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS 6073 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
5927 * remain set in G.special_sig_mask */ 6074 * remain set in G.special_sig_mask */
5928 6075
5929 if (!G.traps) 6076# if ENABLE_HUSH_TRAP
6077 if (!G_traps)
5930 return; 6078 return;
5931 6079
5932 /* Reset all sigs to default except ones with empty traps */ 6080 /* Reset all sigs to default except ones with empty traps */
5933 for (sig = 0; sig < NSIG; sig++) { 6081 for (sig = 0; sig < NSIG; sig++) {
5934 if (!G.traps[sig]) 6082 if (!G_traps[sig])
5935 continue; /* no trap: nothing to do */ 6083 continue; /* no trap: nothing to do */
5936 if (!G.traps[sig][0]) 6084 if (!G_traps[sig][0])
5937 continue; /* empty trap: has to remain SIG_IGN */ 6085 continue; /* empty trap: has to remain SIG_IGN */
5938 /* sig has non-empty trap, reset it: */ 6086 /* sig has non-empty trap, reset it: */
5939 free(G.traps[sig]); 6087 free(G_traps[sig]);
5940 G.traps[sig] = NULL; 6088 G_traps[sig] = NULL;
5941 /* There is no signal for trap 0 (EXIT) */ 6089 /* There is no signal for trap 0 (EXIT) */
5942 if (sig == 0) 6090 if (sig == 0)
5943 continue; 6091 continue;
5944 install_sighandler(sig, pick_sighandler(sig)); 6092 install_sighandler(sig, pick_sighandler(sig));
5945 } 6093 }
6094# endif
5946} 6095}
5947 6096
5948#else /* !BB_MMU */ 6097#else /* !BB_MMU */
@@ -5982,10 +6131,10 @@ static void re_execute_shell(char ***to_free, const char *s,
5982 cnt++; 6131 cnt++;
5983 6132
5984 empty_trap_mask = 0; 6133 empty_trap_mask = 0;
5985 if (G.traps) { 6134 if (G_traps) {
5986 int sig; 6135 int sig;
5987 for (sig = 1; sig < NSIG; sig++) { 6136 for (sig = 1; sig < NSIG; sig++) {
5988 if (G.traps[sig] && !G.traps[sig][0]) 6137 if (G_traps[sig] && !G_traps[sig][0])
5989 empty_trap_mask |= 1LL << sig; 6138 empty_trap_mask |= 1LL << sig;
5990 } 6139 }
5991 } 6140 }
@@ -6178,6 +6327,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
6178 xmove_fd(channel[1], 1); 6327 xmove_fd(channel[1], 1);
6179 /* Prevent it from trying to handle ctrl-z etc */ 6328 /* Prevent it from trying to handle ctrl-z etc */
6180 IF_HUSH_JOB(G.run_list_level = 1;) 6329 IF_HUSH_JOB(G.run_list_level = 1;)
6330# if ENABLE_HUSH_TRAP
6181 /* Awful hack for `trap` or $(trap). 6331 /* Awful hack for `trap` or $(trap).
6182 * 6332 *
6183 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html 6333 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
@@ -6221,6 +6371,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
6221 fflush_all(); /* important */ 6371 fflush_all(); /* important */
6222 _exit(0); 6372 _exit(0);
6223 } 6373 }
6374# endif
6224# if BB_MMU 6375# if BB_MMU
6225 reset_traps_to_defaults(); 6376 reset_traps_to_defaults();
6226 parse_and_run_string(s); 6377 parse_and_run_string(s);
@@ -6629,6 +6780,7 @@ static struct function *new_function(char *name)
6629 return funcp; 6780 return funcp;
6630} 6781}
6631 6782
6783# if ENABLE_HUSH_UNSET
6632static void unset_func(const char *name) 6784static void unset_func(const char *name)
6633{ 6785{
6634 struct function **funcpp = find_function_slot(name); 6786 struct function **funcpp = find_function_slot(name);
@@ -6644,13 +6796,14 @@ static void unset_func(const char *name)
6644 if (funcp->body) { 6796 if (funcp->body) {
6645 free_pipe_list(funcp->body); 6797 free_pipe_list(funcp->body);
6646 free(funcp->name); 6798 free(funcp->name);
6647# if !BB_MMU 6799# if !BB_MMU
6648 free(funcp->body_as_string); 6800 free(funcp->body_as_string);
6649# endif 6801# endif
6650 } 6802 }
6651 free(funcp); 6803 free(funcp);
6652 } 6804 }
6653} 6805}
6806# endif
6654 6807
6655# if BB_MMU 6808# if BB_MMU
6656#define exec_function(to_free, funcp, argv) \ 6809#define exec_function(to_free, funcp, argv) \
@@ -7043,7 +7196,7 @@ static void insert_bg_job(struct pipe *pi)
7043 job->cmdtext = xstrdup(get_cmdtext(pi)); 7196 job->cmdtext = xstrdup(get_cmdtext(pi));
7044 7197
7045 if (G_interactive_fd) 7198 if (G_interactive_fd)
7046 printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); 7199 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
7047 G.last_jobid = job->jobid; 7200 G.last_jobid = job->jobid;
7048} 7201}
7049 7202
@@ -7518,7 +7671,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7518 } 7671 }
7519 7672
7520 /* Expand the rest into (possibly) many strings each */ 7673 /* Expand the rest into (possibly) many strings each */
7521#if ENABLE_HUSH_BASH_COMPAT 7674#if BASH_TEST2
7522 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { 7675 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
7523 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 7676 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
7524 } else 7677 } else
@@ -8118,10 +8271,12 @@ static void install_sighandlers(unsigned mask)
8118 if (old_handler == SIG_IGN) { 8271 if (old_handler == SIG_IGN) {
8119 /* oops... restore back to IGN, and record this fact */ 8272 /* oops... restore back to IGN, and record this fact */
8120 install_sighandler(sig, old_handler); 8273 install_sighandler(sig, old_handler);
8121 if (!G.traps) 8274#if ENABLE_HUSH_TRAP
8122 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 8275 if (!G_traps)
8123 free(G.traps[sig]); 8276 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
8124 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ 8277 free(G_traps[sig]);
8278 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
8279#endif
8125 } 8280 }
8126 } 8281 }
8127} 8282}
@@ -8272,7 +8427,7 @@ int hush_main(int argc, char **argv)
8272 /* Export PWD */ 8427 /* Export PWD */
8273 set_pwd_var(/*exp:*/ 1); 8428 set_pwd_var(/*exp:*/ 1);
8274 8429
8275#if ENABLE_HUSH_BASH_COMPAT 8430#if BASH_HOSTNAME_VAR
8276 /* Set (but not export) HOSTNAME unless already set */ 8431 /* Set (but not export) HOSTNAME unless already set */
8277 if (!get_local_var_value("HOSTNAME")) { 8432 if (!get_local_var_value("HOSTNAME")) {
8278 struct utsname uts; 8433 struct utsname uts;
@@ -8422,10 +8577,10 @@ int hush_main(int argc, char **argv)
8422 if (empty_trap_mask != 0) { 8577 if (empty_trap_mask != 0) {
8423 int sig; 8578 int sig;
8424 install_special_sighandlers(); 8579 install_special_sighandlers();
8425 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 8580 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
8426 for (sig = 1; sig < NSIG; sig++) { 8581 for (sig = 1; sig < NSIG; sig++) {
8427 if (empty_trap_mask & (1LL << sig)) { 8582 if (empty_trap_mask & (1LL << sig)) {
8428 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ 8583 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
8429 install_sighandler(sig, SIG_IGN); 8584 install_sighandler(sig, SIG_IGN);
8430 } 8585 }
8431 } 8586 }
@@ -8669,6 +8824,7 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
8669 return 0; 8824 return 0;
8670} 8825}
8671 8826
8827#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
8672static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) 8828static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
8673{ 8829{
8674 int argc = 0; 8830 int argc = 0;
@@ -8678,24 +8834,50 @@ static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char *
8678 } 8834 }
8679 return applet_main_func(argc, argv - argc); 8835 return applet_main_func(argc, argv - argc);
8680} 8836}
8681 8837#endif
8838#if ENABLE_HUSH_TEST || BASH_TEST2
8682static int FAST_FUNC builtin_test(char **argv) 8839static int FAST_FUNC builtin_test(char **argv)
8683{ 8840{
8684 return run_applet_main(argv, test_main); 8841 return run_applet_main(argv, test_main);
8685} 8842}
8686 8843#endif
8844#if ENABLE_HUSH_ECHO
8687static int FAST_FUNC builtin_echo(char **argv) 8845static int FAST_FUNC builtin_echo(char **argv)
8688{ 8846{
8689 return run_applet_main(argv, echo_main); 8847 return run_applet_main(argv, echo_main);
8690} 8848}
8691 8849#endif
8692#if ENABLE_PRINTF 8850#if ENABLE_HUSH_PRINTF
8693static int FAST_FUNC builtin_printf(char **argv) 8851static int FAST_FUNC builtin_printf(char **argv)
8694{ 8852{
8695 return run_applet_main(argv, printf_main); 8853 return run_applet_main(argv, printf_main);
8696} 8854}
8697#endif 8855#endif
8698 8856
8857#if ENABLE_HUSH_HELP
8858static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
8859{
8860 const struct built_in_command *x;
8861
8862 printf(
8863 "Built-in commands:\n"
8864 "------------------\n");
8865 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
8866 if (x->b_descr)
8867 printf("%-10s%s\n", x->b_cmd, x->b_descr);
8868 }
8869 return EXIT_SUCCESS;
8870}
8871#endif
8872
8873#if MAX_HISTORY && ENABLE_FEATURE_EDITING
8874static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
8875{
8876 show_history(G.line_input_state);
8877 return EXIT_SUCCESS;
8878}
8879#endif
8880
8699static char **skip_dash_dash(char **argv) 8881static char **skip_dash_dash(char **argv)
8700{ 8882{
8701 argv++; 8883 argv++;
@@ -8704,24 +8886,6 @@ static char **skip_dash_dash(char **argv)
8704 return argv; 8886 return argv;
8705} 8887}
8706 8888
8707static int FAST_FUNC builtin_eval(char **argv)
8708{
8709 int rcode = EXIT_SUCCESS;
8710
8711 argv = skip_dash_dash(argv);
8712 if (*argv) {
8713 char *str = expand_strvec_to_string(argv);
8714 /* bash:
8715 * eval "echo Hi; done" ("done" is syntax error):
8716 * "echo Hi" will not execute too.
8717 */
8718 parse_and_run_string(str);
8719 free(str);
8720 rcode = G.last_exitcode;
8721 }
8722 return rcode;
8723}
8724
8725static int FAST_FUNC builtin_cd(char **argv) 8889static int FAST_FUNC builtin_cd(char **argv)
8726{ 8890{
8727 const char *newdir; 8891 const char *newdir;
@@ -8749,6 +8913,30 @@ static int FAST_FUNC builtin_cd(char **argv)
8749 return EXIT_SUCCESS; 8913 return EXIT_SUCCESS;
8750} 8914}
8751 8915
8916static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
8917{
8918 puts(get_cwd(0));
8919 return EXIT_SUCCESS;
8920}
8921
8922static int FAST_FUNC builtin_eval(char **argv)
8923{
8924 int rcode = EXIT_SUCCESS;
8925
8926 argv = skip_dash_dash(argv);
8927 if (*argv) {
8928 char *str = expand_strvec_to_string(argv);
8929 /* bash:
8930 * eval "echo Hi; done" ("done" is syntax error):
8931 * "echo Hi" will not execute too.
8932 */
8933 parse_and_run_string(str);
8934 free(str);
8935 rcode = G.last_exitcode;
8936 }
8937 return rcode;
8938}
8939
8752static int FAST_FUNC builtin_exec(char **argv) 8940static int FAST_FUNC builtin_exec(char **argv)
8753{ 8941{
8754 argv = skip_dash_dash(argv); 8942 argv = skip_dash_dash(argv);
@@ -8794,6 +8982,148 @@ static int FAST_FUNC builtin_exit(char **argv)
8794 hush_exit(xatoi(argv[0]) & 0xff); 8982 hush_exit(xatoi(argv[0]) & 0xff);
8795} 8983}
8796 8984
8985#if ENABLE_HUSH_TYPE
8986/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
8987static int FAST_FUNC builtin_type(char **argv)
8988{
8989 int ret = EXIT_SUCCESS;
8990
8991 while (*++argv) {
8992 const char *type;
8993 char *path = NULL;
8994
8995 if (0) {} /* make conditional compile easier below */
8996 /*else if (find_alias(*argv))
8997 type = "an alias";*/
8998#if ENABLE_HUSH_FUNCTIONS
8999 else if (find_function(*argv))
9000 type = "a function";
9001#endif
9002 else if (find_builtin(*argv))
9003 type = "a shell builtin";
9004 else if ((path = find_in_path(*argv)) != NULL)
9005 type = path;
9006 else {
9007 bb_error_msg("type: %s: not found", *argv);
9008 ret = EXIT_FAILURE;
9009 continue;
9010 }
9011
9012 printf("%s is %s\n", *argv, type);
9013 free(path);
9014 }
9015
9016 return ret;
9017}
9018#endif
9019
9020#if ENABLE_HUSH_READ
9021/* Interruptibility of read builtin in bash
9022 * (tested on bash-4.2.8 by sending signals (not by ^C)):
9023 *
9024 * Empty trap makes read ignore corresponding signal, for any signal.
9025 *
9026 * SIGINT:
9027 * - terminates non-interactive shell;
9028 * - interrupts read in interactive shell;
9029 * if it has non-empty trap:
9030 * - executes trap and returns to command prompt in interactive shell;
9031 * - executes trap and returns to read in non-interactive shell;
9032 * SIGTERM:
9033 * - is ignored (does not interrupt) read in interactive shell;
9034 * - terminates non-interactive shell;
9035 * if it has non-empty trap:
9036 * - executes trap and returns to read;
9037 * SIGHUP:
9038 * - terminates shell (regardless of interactivity);
9039 * if it has non-empty trap:
9040 * - executes trap and returns to read;
9041 */
9042static int FAST_FUNC builtin_read(char **argv)
9043{
9044 const char *r;
9045 char *opt_n = NULL;
9046 char *opt_p = NULL;
9047 char *opt_t = NULL;
9048 char *opt_u = NULL;
9049 const char *ifs;
9050 int read_flags;
9051
9052 /* "!": do not abort on errors.
9053 * Option string must start with "sr" to match BUILTIN_READ_xxx
9054 */
9055 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
9056 if (read_flags == (uint32_t)-1)
9057 return EXIT_FAILURE;
9058 argv += optind;
9059 ifs = get_local_var_value("IFS"); /* can be NULL */
9060
9061 again:
9062 r = shell_builtin_read(set_local_var_from_halves,
9063 argv,
9064 ifs,
9065 read_flags,
9066 opt_n,
9067 opt_p,
9068 opt_t,
9069 opt_u
9070 );
9071
9072 if ((uintptr_t)r == 1 && errno == EINTR) {
9073 unsigned sig = check_and_run_traps();
9074 if (sig && sig != SIGINT)
9075 goto again;
9076 }
9077
9078 if ((uintptr_t)r > 1) {
9079 bb_error_msg("%s", r);
9080 r = (char*)(uintptr_t)1;
9081 }
9082
9083 return (uintptr_t)r;
9084}
9085#endif
9086
9087#if ENABLE_HUSH_UMASK
9088static int FAST_FUNC builtin_umask(char **argv)
9089{
9090 int rc;
9091 mode_t mask;
9092
9093 rc = 1;
9094 mask = umask(0);
9095 argv = skip_dash_dash(argv);
9096 if (argv[0]) {
9097 mode_t old_mask = mask;
9098
9099 /* numeric umasks are taken as-is */
9100 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
9101 if (!isdigit(argv[0][0]))
9102 mask ^= 0777;
9103 mask = bb_parse_mode(argv[0], mask);
9104 if (!isdigit(argv[0][0]))
9105 mask ^= 0777;
9106 if ((unsigned)mask > 0777) {
9107 mask = old_mask;
9108 /* bash messages:
9109 * bash: umask: 'q': invalid symbolic mode operator
9110 * bash: umask: 999: octal number out of range
9111 */
9112 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
9113 rc = 0;
9114 }
9115 } else {
9116 /* Mimic bash */
9117 printf("%04o\n", (unsigned) mask);
9118 /* fall through and restore mask which we set to 0 */
9119 }
9120 umask(mask);
9121
9122 return !rc; /* rc != 0 - success */
9123}
9124#endif
9125
9126#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
8797static void print_escaped(const char *s) 9127static void print_escaped(const char *s)
8798{ 9128{
8799 if (*s == '\'') 9129 if (*s == '\'')
@@ -8812,11 +9142,13 @@ static void print_escaped(const char *s)
8812 putchar('"'); 9142 putchar('"');
8813 } while (*s); 9143 } while (*s);
8814} 9144}
9145#endif
8815 9146
8816#if !ENABLE_HUSH_LOCAL 9147#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL
9148# if !ENABLE_HUSH_LOCAL
8817#define helper_export_local(argv, exp, lvl) \ 9149#define helper_export_local(argv, exp, lvl) \
8818 helper_export_local(argv, exp) 9150 helper_export_local(argv, exp)
8819#endif 9151# endif
8820static void helper_export_local(char **argv, int exp, int lvl) 9152static void helper_export_local(char **argv, int exp, int lvl)
8821{ 9153{
8822 do { 9154 do {
@@ -8849,14 +9181,14 @@ static void helper_export_local(char **argv, int exp, int lvl)
8849 continue; 9181 continue;
8850 } 9182 }
8851 } 9183 }
8852#if ENABLE_HUSH_LOCAL 9184# if ENABLE_HUSH_LOCAL
8853 if (exp == 0 /* local? */ 9185 if (exp == 0 /* local? */
8854 && var && var->func_nest_level == lvl 9186 && var && var->func_nest_level == lvl
8855 ) { 9187 ) {
8856 /* "local x=abc; ...; local x" - ignore second local decl */ 9188 /* "local x=abc; ...; local x" - ignore second local decl */
8857 continue; 9189 continue;
8858 } 9190 }
8859#endif 9191# endif
8860 /* Exporting non-existing variable. 9192 /* Exporting non-existing variable.
8861 * bash does not put it in environment, 9193 * bash does not put it in environment,
8862 * but remembers that it is exported, 9194 * but remembers that it is exported,
@@ -8872,7 +9204,9 @@ static void helper_export_local(char **argv, int exp, int lvl)
8872 set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); 9204 set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0);
8873 } while (*++argv); 9205 } while (*++argv);
8874} 9206}
9207#endif
8875 9208
9209#if ENABLE_HUSH_EXPORT
8876static int FAST_FUNC builtin_export(char **argv) 9210static int FAST_FUNC builtin_export(char **argv)
8877{ 9211{
8878 unsigned opt_unexport; 9212 unsigned opt_unexport;
@@ -8918,6 +9252,7 @@ static int FAST_FUNC builtin_export(char **argv)
8918 9252
8919 return EXIT_SUCCESS; 9253 return EXIT_SUCCESS;
8920} 9254}
9255#endif
8921 9256
8922#if ENABLE_HUSH_LOCAL 9257#if ENABLE_HUSH_LOCAL
8923static int FAST_FUNC builtin_local(char **argv) 9258static int FAST_FUNC builtin_local(char **argv)
@@ -8931,6 +9266,7 @@ static int FAST_FUNC builtin_local(char **argv)
8931} 9266}
8932#endif 9267#endif
8933 9268
9269#if ENABLE_HUSH_UNSET
8934/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ 9270/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
8935static int FAST_FUNC builtin_unset(char **argv) 9271static int FAST_FUNC builtin_unset(char **argv)
8936{ 9272{
@@ -8958,16 +9294,18 @@ static int FAST_FUNC builtin_unset(char **argv)
8958 ret = EXIT_FAILURE; 9294 ret = EXIT_FAILURE;
8959 } 9295 }
8960 } 9296 }
8961#if ENABLE_HUSH_FUNCTIONS 9297# if ENABLE_HUSH_FUNCTIONS
8962 else { 9298 else {
8963 unset_func(*argv); 9299 unset_func(*argv);
8964 } 9300 }
8965#endif 9301# endif
8966 argv++; 9302 argv++;
8967 } 9303 }
8968 return ret; 9304 return ret;
8969} 9305}
9306#endif
8970 9307
9308#if ENABLE_HUSH_SET
8971/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set 9309/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
8972 * built-in 'set' handler 9310 * built-in 'set' handler
8973 * SUSv3 says: 9311 * SUSv3 says:
@@ -9050,6 +9388,7 @@ static int FAST_FUNC builtin_set(char **argv)
9050 bb_error_msg("set: %s: invalid option", arg); 9388 bb_error_msg("set: %s: invalid option", arg);
9051 return EXIT_FAILURE; 9389 return EXIT_FAILURE;
9052} 9390}
9391#endif
9053 9392
9054static int FAST_FUNC builtin_shift(char **argv) 9393static int FAST_FUNC builtin_shift(char **argv)
9055{ 9394{
@@ -9059,7 +9398,7 @@ static int FAST_FUNC builtin_shift(char **argv)
9059 n = atoi(argv[0]); 9398 n = atoi(argv[0]);
9060 } 9399 }
9061 if (n >= 0 && n < G.global_argc) { 9400 if (n >= 0 && n < G.global_argc) {
9062 if (G.global_args_malloced) { 9401 if (G_global_args_malloced) {
9063 int m = 1; 9402 int m = 1;
9064 while (m <= n) 9403 while (m <= n)
9065 free(G.global_argv[m++]); 9404 free(G.global_argv[m++]);
@@ -9072,87 +9411,78 @@ static int FAST_FUNC builtin_shift(char **argv)
9072 return EXIT_FAILURE; 9411 return EXIT_FAILURE;
9073} 9412}
9074 9413
9075/* Interruptibility of read builtin in bash 9414static int FAST_FUNC builtin_source(char **argv)
9076 * (tested on bash-4.2.8 by sending signals (not by ^C)):
9077 *
9078 * Empty trap makes read ignore corresponding signal, for any signal.
9079 *
9080 * SIGINT:
9081 * - terminates non-interactive shell;
9082 * - interrupts read in interactive shell;
9083 * if it has non-empty trap:
9084 * - executes trap and returns to command prompt in interactive shell;
9085 * - executes trap and returns to read in non-interactive shell;
9086 * SIGTERM:
9087 * - is ignored (does not interrupt) read in interactive shell;
9088 * - terminates non-interactive shell;
9089 * if it has non-empty trap:
9090 * - executes trap and returns to read;
9091 * SIGHUP:
9092 * - terminates shell (regardless of interactivity);
9093 * if it has non-empty trap:
9094 * - executes trap and returns to read;
9095 */
9096static int FAST_FUNC builtin_read(char **argv)
9097{ 9415{
9098 const char *r; 9416 char *arg_path, *filename;
9099 char *opt_n = NULL; 9417 FILE *input;
9100 char *opt_p = NULL; 9418 save_arg_t sv;
9101 char *opt_t = NULL; 9419 char *args_need_save;
9102 char *opt_u = NULL; 9420#if ENABLE_HUSH_FUNCTIONS
9103 const char *ifs; 9421 smallint sv_flg;
9104 int read_flags; 9422#endif
9105 9423
9106 /* "!": do not abort on errors. 9424 argv = skip_dash_dash(argv);
9107 * Option string must start with "sr" to match BUILTIN_READ_xxx 9425 filename = argv[0];
9108 */ 9426 if (!filename) {
9109 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); 9427 /* bash says: "bash: .: filename argument required" */
9110 if (read_flags == (uint32_t)-1) 9428 return 2; /* bash compat */
9429 }
9430 arg_path = NULL;
9431 if (!strchr(filename, '/')) {
9432 arg_path = find_in_path(filename);
9433 if (arg_path)
9434 filename = arg_path;
9435 }
9436 input = remember_FILE(fopen_or_warn(filename, "r"));
9437 free(arg_path);
9438 if (!input) {
9439 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
9440 /* POSIX: non-interactive shell should abort here,
9441 * not merely fail. So far no one complained :)
9442 */
9111 return EXIT_FAILURE; 9443 return EXIT_FAILURE;
9112 argv += optind; 9444 }
9113 ifs = get_local_var_value("IFS"); /* can be NULL */
9114 9445
9115 again: 9446#if ENABLE_HUSH_FUNCTIONS
9116 r = shell_builtin_read(set_local_var_from_halves, 9447 sv_flg = G_flag_return_in_progress;
9117 argv, 9448 /* "we are inside sourced file, ok to use return" */
9118 ifs, 9449 G_flag_return_in_progress = -1;
9119 read_flags, 9450#endif
9120 opt_n, 9451 args_need_save = argv[1]; /* used as a boolean variable */
9121 opt_p, 9452 if (args_need_save)
9122 opt_t, 9453 save_and_replace_G_args(&sv, argv);
9123 opt_u
9124 );
9125 9454
9126 if ((uintptr_t)r == 1 && errno == EINTR) { 9455 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
9127 unsigned sig = check_and_run_traps(); 9456 G.last_exitcode = 0;
9128 if (sig && sig != SIGINT) 9457 parse_and_run_file(input);
9129 goto again; 9458 fclose_and_forget(input);
9130 }
9131 9459
9132 if ((uintptr_t)r > 1) { 9460 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
9133 bb_error_msg("%s", r); 9461 restore_G_args(&sv, argv);
9134 r = (char*)(uintptr_t)1; 9462#if ENABLE_HUSH_FUNCTIONS
9135 } 9463 G_flag_return_in_progress = sv_flg;
9464#endif
9136 9465
9137 return (uintptr_t)r; 9466 return G.last_exitcode;
9138} 9467}
9139 9468
9469#if ENABLE_HUSH_TRAP
9140static int FAST_FUNC builtin_trap(char **argv) 9470static int FAST_FUNC builtin_trap(char **argv)
9141{ 9471{
9142 int sig; 9472 int sig;
9143 char *new_cmd; 9473 char *new_cmd;
9144 9474
9145 if (!G.traps) 9475 if (!G_traps)
9146 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 9476 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
9147 9477
9148 argv++; 9478 argv++;
9149 if (!*argv) { 9479 if (!*argv) {
9150 int i; 9480 int i;
9151 /* No args: print all trapped */ 9481 /* No args: print all trapped */
9152 for (i = 0; i < NSIG; ++i) { 9482 for (i = 0; i < NSIG; ++i) {
9153 if (G.traps[i]) { 9483 if (G_traps[i]) {
9154 printf("trap -- "); 9484 printf("trap -- ");
9155 print_escaped(G.traps[i]); 9485 print_escaped(G_traps[i]);
9156 /* note: bash adds "SIG", but only if invoked 9486 /* note: bash adds "SIG", but only if invoked
9157 * as "bash". If called as "sh", or if set -o posix, 9487 * as "bash". If called as "sh", or if set -o posix,
9158 * then it prints short signal names. 9488 * then it prints short signal names.
@@ -9182,11 +9512,11 @@ static int FAST_FUNC builtin_trap(char **argv)
9182 continue; 9512 continue;
9183 } 9513 }
9184 9514
9185 free(G.traps[sig]); 9515 free(G_traps[sig]);
9186 G.traps[sig] = xstrdup(new_cmd); 9516 G_traps[sig] = xstrdup(new_cmd);
9187 9517
9188 debug_printf("trap: setting SIG%s (%i) to '%s'\n", 9518 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
9189 get_signame(sig), sig, G.traps[sig]); 9519 get_signame(sig), sig, G_traps[sig]);
9190 9520
9191 /* There is no signal for 0 (EXIT) */ 9521 /* There is no signal for 0 (EXIT) */
9192 if (sig == 0) 9522 if (sig == 0)
@@ -9226,59 +9556,54 @@ static int FAST_FUNC builtin_trap(char **argv)
9226 argv++; 9556 argv++;
9227 goto process_sig_list; 9557 goto process_sig_list;
9228} 9558}
9229
9230/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
9231static int FAST_FUNC builtin_type(char **argv)
9232{
9233 int ret = EXIT_SUCCESS;
9234
9235 while (*++argv) {
9236 const char *type;
9237 char *path = NULL;
9238
9239 if (0) {} /* make conditional compile easier below */
9240 /*else if (find_alias(*argv))
9241 type = "an alias";*/
9242#if ENABLE_HUSH_FUNCTIONS
9243 else if (find_function(*argv))
9244 type = "a function";
9245#endif 9559#endif
9246 else if (find_builtin(*argv))
9247 type = "a shell builtin";
9248 else if ((path = find_in_path(*argv)) != NULL)
9249 type = path;
9250 else {
9251 bb_error_msg("type: %s: not found", *argv);
9252 ret = EXIT_FAILURE;
9253 continue;
9254 }
9255
9256 printf("%s is %s\n", *argv, type);
9257 free(path);
9258 }
9259
9260 return ret;
9261}
9262 9560
9263#if ENABLE_HUSH_JOB 9561#if ENABLE_HUSH_JOB
9264static struct pipe *parse_jobspec(const char *str) 9562static struct pipe *parse_jobspec(const char *str)
9265{ 9563{
9266 struct pipe *pi; 9564 struct pipe *pi;
9267 int jobnum; 9565 unsigned jobnum;
9268 9566
9269 if (sscanf(str, "%%%d", &jobnum) != 1) { 9567 if (sscanf(str, "%%%u", &jobnum) != 1) {
9270 bb_error_msg("bad argument '%s'", str); 9568 if (str[0] != '%'
9271 return NULL; 9569 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
9570 ) {
9571 bb_error_msg("bad argument '%s'", str);
9572 return NULL;
9573 }
9574 /* It is "%%", "%+" or "%" - current job */
9575 jobnum = G.last_jobid;
9576 if (jobnum == 0) {
9577 bb_error_msg("no current job");
9578 return NULL;
9579 }
9272 } 9580 }
9273 for (pi = G.job_list; pi; pi = pi->next) { 9581 for (pi = G.job_list; pi; pi = pi->next) {
9274 if (pi->jobid == jobnum) { 9582 if (pi->jobid == jobnum) {
9275 return pi; 9583 return pi;
9276 } 9584 }
9277 } 9585 }
9278 bb_error_msg("%d: no such job", jobnum); 9586 bb_error_msg("%u: no such job", jobnum);
9279 return NULL; 9587 return NULL;
9280} 9588}
9281 9589
9590static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
9591{
9592 struct pipe *job;
9593 const char *status_string;
9594
9595 checkjobs(NULL, 0 /*(no pid to wait for)*/);
9596 for (job = G.job_list; job; job = job->next) {
9597 if (job->alive_cmds == job->stopped_cmds)
9598 status_string = "Stopped";
9599 else
9600 status_string = "Running";
9601
9602 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
9603 }
9604 return EXIT_SUCCESS;
9605}
9606
9282/* built-in 'fg' and 'bg' handler */ 9607/* built-in 'fg' and 'bg' handler */
9283static int FAST_FUNC builtin_fg_bg(char **argv) 9608static int FAST_FUNC builtin_fg_bg(char **argv)
9284{ 9609{
@@ -9334,188 +9659,81 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
9334} 9659}
9335#endif 9660#endif
9336 9661
9337#if ENABLE_HUSH_HELP 9662#if ENABLE_HUSH_KILL
9338static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) 9663static int FAST_FUNC builtin_kill(char **argv)
9339{
9340 const struct built_in_command *x;
9341
9342 printf(
9343 "Built-in commands:\n"
9344 "------------------\n");
9345 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
9346 if (x->b_descr)
9347 printf("%-10s%s\n", x->b_cmd, x->b_descr);
9348 }
9349 return EXIT_SUCCESS;
9350}
9351#endif
9352
9353#if MAX_HISTORY && ENABLE_FEATURE_EDITING
9354static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
9355{ 9664{
9356 show_history(G.line_input_state); 9665 int ret = 0;
9357 return EXIT_SUCCESS;
9358}
9359#endif
9360 9666
9361#if ENABLE_HUSH_JOB 9667# if ENABLE_HUSH_JOB
9362static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) 9668 if (argv[1] && strcmp(argv[1], "-l") != 0) {
9363{ 9669 int i = 1;
9364 struct pipe *job;
9365 const char *status_string;
9366 9670
9367 checkjobs(NULL, 0 /*(no pid to wait for)*/); 9671 do {
9368 for (job = G.job_list; job; job = job->next) { 9672 struct pipe *pi;
9369 if (job->alive_cmds == job->stopped_cmds) 9673 char *dst;
9370 status_string = "Stopped"; 9674 int j, n;
9371 else
9372 status_string = "Running";
9373 9675
9374 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); 9676 if (argv[i][0] != '%')
9677 continue;
9678 /*
9679 * "kill %N" - job kill
9680 * Converting to pgrp / pid kill
9681 */
9682 pi = parse_jobspec(argv[i]);
9683 if (!pi) {
9684 /* Eat bad jobspec */
9685 j = i;
9686 do {
9687 j++;
9688 argv[j - 1] = argv[j];
9689 } while (argv[j]);
9690 ret = 1;
9691 i--;
9692 continue;
9693 }
9694 /*
9695 * In jobs started under job control, we signal
9696 * entire process group by kill -PGRP_ID.
9697 * This happens, f.e., in interactive shell.
9698 *
9699 * Otherwise, we signal each child via
9700 * kill PID1 PID2 PID3.
9701 * Testcases:
9702 * sh -c 'sleep 1|sleep 1 & kill %1'
9703 * sh -c 'true|sleep 2 & sleep 1; kill %1'
9704 * sh -c 'true|sleep 1 & sleep 2; kill %1'
9705 */
9706 n = G_interactive_fd ? 1 : pi->num_cmds;
9707 dst = alloca(n * sizeof(int)*4);
9708 argv[i] = dst;
9709 if (G_interactive_fd)
9710 dst += sprintf(dst, " -%u", (int)pi->pgrp);
9711 else for (j = 0; j < n; j++) {
9712 struct command *cmd = &pi->cmds[j];
9713 /* Skip exited members of the job */
9714 if (cmd->pid == 0)
9715 continue;
9716 /*
9717 * kill_main has matching code to expect
9718 * leading space. Needed to not confuse
9719 * negative pids with "kill -SIGNAL_NO" syntax
9720 */
9721 dst += sprintf(dst, " %u", (int)cmd->pid);
9722 }
9723 *dst = '\0';
9724 } while (argv[++i]);
9375 } 9725 }
9376 return EXIT_SUCCESS;
9377}
9378#endif
9379
9380#if HUSH_DEBUG
9381static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
9382{
9383 void *p;
9384 unsigned long l;
9385
9386# ifdef M_TRIM_THRESHOLD
9387 /* Optional. Reduces probability of false positives */
9388 malloc_trim(0);
9389# endif 9726# endif
9390 /* Crude attempt to find where "free memory" starts,
9391 * sans fragmentation. */
9392 p = malloc(240);
9393 l = (unsigned long)p;
9394 free(p);
9395 p = malloc(3400);
9396 if (l < (unsigned long)p) l = (unsigned long)p;
9397 free(p);
9398 9727
9399 9728 if (argv[1] || ret == 0) {
9400# if 0 /* debug */ 9729 ret = run_applet_main(argv, kill_main);
9401 {
9402 struct mallinfo mi = mallinfo();
9403 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
9404 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
9405 } 9730 }
9406# endif 9731 /* else: ret = 1, "kill %bad_jobspec" case */
9407 9732 return ret;
9408 if (!G.memleak_value)
9409 G.memleak_value = l;
9410
9411 l -= G.memleak_value;
9412 if ((long)l < 0)
9413 l = 0;
9414 l /= 1024;
9415 if (l > 127)
9416 l = 127;
9417
9418 /* Exitcode is "how many kilobytes we leaked since 1st call" */
9419 return l;
9420}
9421#endif
9422
9423static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
9424{
9425 puts(get_cwd(0));
9426 return EXIT_SUCCESS;
9427} 9733}
9428
9429static int FAST_FUNC builtin_source(char **argv)
9430{
9431 char *arg_path, *filename;
9432 FILE *input;
9433 save_arg_t sv;
9434#if ENABLE_HUSH_FUNCTIONS
9435 smallint sv_flg;
9436#endif
9437
9438 argv = skip_dash_dash(argv);
9439 filename = argv[0];
9440 if (!filename) {
9441 /* bash says: "bash: .: filename argument required" */
9442 return 2; /* bash compat */
9443 }
9444 arg_path = NULL;
9445 if (!strchr(filename, '/')) {
9446 arg_path = find_in_path(filename);
9447 if (arg_path)
9448 filename = arg_path;
9449 }
9450 input = remember_FILE(fopen_or_warn(filename, "r"));
9451 free(arg_path);
9452 if (!input) {
9453 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
9454 /* POSIX: non-interactive shell should abort here,
9455 * not merely fail. So far no one complained :)
9456 */
9457 return EXIT_FAILURE;
9458 }
9459
9460#if ENABLE_HUSH_FUNCTIONS
9461 sv_flg = G_flag_return_in_progress;
9462 /* "we are inside sourced file, ok to use return" */
9463 G_flag_return_in_progress = -1;
9464#endif 9734#endif
9465 if (argv[1])
9466 save_and_replace_G_args(&sv, argv);
9467
9468 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
9469 G.last_exitcode = 0;
9470 parse_and_run_file(input);
9471 fclose_and_forget(input);
9472
9473 if (argv[1])
9474 restore_G_args(&sv, argv);
9475#if ENABLE_HUSH_FUNCTIONS
9476 G_flag_return_in_progress = sv_flg;
9477#endif
9478
9479 return G.last_exitcode;
9480}
9481
9482static int FAST_FUNC builtin_umask(char **argv)
9483{
9484 int rc;
9485 mode_t mask;
9486
9487 rc = 1;
9488 mask = umask(0);
9489 argv = skip_dash_dash(argv);
9490 if (argv[0]) {
9491 mode_t old_mask = mask;
9492
9493 /* numeric umasks are taken as-is */
9494 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
9495 if (!isdigit(argv[0][0]))
9496 mask ^= 0777;
9497 mask = bb_parse_mode(argv[0], mask);
9498 if (!isdigit(argv[0][0]))
9499 mask ^= 0777;
9500 if ((unsigned)mask > 0777) {
9501 mask = old_mask;
9502 /* bash messages:
9503 * bash: umask: 'q': invalid symbolic mode operator
9504 * bash: umask: 999: octal number out of range
9505 */
9506 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
9507 rc = 0;
9508 }
9509 } else {
9510 /* Mimic bash */
9511 printf("%04o\n", (unsigned) mask);
9512 /* fall through and restore mask which we set to 0 */
9513 }
9514 umask(mask);
9515
9516 return !rc; /* rc != 0 - success */
9517}
9518 9735
9736#if ENABLE_HUSH_WAIT
9519/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 9737/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
9520#if !ENABLE_HUSH_JOB 9738#if !ENABLE_HUSH_JOB
9521# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) 9739# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
@@ -9622,13 +9840,15 @@ static int FAST_FUNC builtin_wait(char **argv)
9622#if ENABLE_HUSH_JOB 9840#if ENABLE_HUSH_JOB
9623 if (argv[0][0] == '%') { 9841 if (argv[0][0] == '%') {
9624 struct pipe *wait_pipe; 9842 struct pipe *wait_pipe;
9843 ret = 127; /* bash compat for bad jobspecs */
9625 wait_pipe = parse_jobspec(*argv); 9844 wait_pipe = parse_jobspec(*argv);
9626 if (wait_pipe) { 9845 if (wait_pipe) {
9627 ret = job_exited_or_stopped(wait_pipe); 9846 ret = job_exited_or_stopped(wait_pipe);
9628 if (ret < 0) 9847 if (ret < 0)
9629 ret = wait_for_child_or_signal(wait_pipe, 0); 9848 ret = wait_for_child_or_signal(wait_pipe, 0);
9630 continue;
9631 } 9849 }
9850 /* else: parse_jobspec() already emitted error msg */
9851 continue;
9632 } 9852 }
9633#endif 9853#endif
9634 /* mimic bash message */ 9854 /* mimic bash message */
@@ -9674,6 +9894,7 @@ static int FAST_FUNC builtin_wait(char **argv)
9674 9894
9675 return ret; 9895 return ret;
9676} 9896}
9897#endif
9677 9898
9678#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS 9899#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
9679static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min) 9900static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
@@ -9740,3 +9961,46 @@ static int FAST_FUNC builtin_return(char **argv)
9740 return rc; 9961 return rc;
9741} 9962}
9742#endif 9963#endif
9964
9965#if ENABLE_HUSH_MEMLEAK
9966static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
9967{
9968 void *p;
9969 unsigned long l;
9970
9971# ifdef M_TRIM_THRESHOLD
9972 /* Optional. Reduces probability of false positives */
9973 malloc_trim(0);
9974# endif
9975 /* Crude attempt to find where "free memory" starts,
9976 * sans fragmentation. */
9977 p = malloc(240);
9978 l = (unsigned long)p;
9979 free(p);
9980 p = malloc(3400);
9981 if (l < (unsigned long)p) l = (unsigned long)p;
9982 free(p);
9983
9984
9985# if 0 /* debug */
9986 {
9987 struct mallinfo mi = mallinfo();
9988 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
9989 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
9990 }
9991# endif
9992
9993 if (!G.memleak_value)
9994 G.memleak_value = l;
9995
9996 l -= G.memleak_value;
9997 if ((long)l < 0)
9998 l = 0;
9999 l /= 1024;
10000 if (l > 127)
10001 l = 127;
10002
10003 /* Exitcode is "how many kilobytes we leaked since 1st call" */
10004 return l;
10005}
10006#endif
diff --git a/shell/hush_test/hush-misc/source_argv_and_shift.right b/shell/hush_test/hush-misc/source_argv_and_shift.right
new file mode 100644
index 000000000..b15cc96e7
--- /dev/null
+++ b/shell/hush_test/hush-misc/source_argv_and_shift.right
@@ -0,0 +1,4 @@
1sourced_arg1:1
2arg1:
3sourced_arg1:a
4arg1:1
diff --git a/shell/hush_test/hush-misc/source_argv_and_shift.tests b/shell/hush_test/hush-misc/source_argv_and_shift.tests
new file mode 100755
index 000000000..66353f3d7
--- /dev/null
+++ b/shell/hush_test/hush-misc/source_argv_and_shift.tests
@@ -0,0 +1,12 @@
1echo 'echo sourced_arg1:$1' >sourced1
2echo 'shift' >>sourced1
3
4set -- 1
5. ./sourced1
6echo arg1:$1
7
8set -- 1
9. ./sourced1 a
10echo arg1:$1
11
12rm sourced1
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 5a5b1780d..653154e34 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -147,7 +147,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
147 // Setting it to more than 1 breaks poll(): 147 // Setting it to more than 1 breaks poll():
148 // it blocks even if there's data. !?? 148 // it blocks even if there's data. !??
149 //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; 149 //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
150 /* reads would block only if < 1 char is available */ 150 /* reads will block only if < 1 char is available */
151 tty.c_cc[VMIN] = 1; 151 tty.c_cc[VMIN] = 1;
152 /* no timeout (reads block forever) */ 152 /* no timeout (reads block forever) */
153 tty.c_cc[VTIME] = 0; 153 tty.c_cc[VTIME] = 0;