aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c465
1 files changed, 294 insertions, 171 deletions
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 }