diff options
author | Ron Yorston <rmy@pobox.com> | 2017-02-08 20:09:29 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-02-08 20:09:29 +0000 |
commit | 373275a708bafb88fa4f0519de2166154f44fed9 (patch) | |
tree | 4587b4fd3f695e0f3705b2a217e199f3144df931 /shell | |
parent | b74b2619779b1deb903b7766261807df1e9b1f7f (diff) | |
parent | c2b18583a3df06aeecf535c3cea6856aa1f2e205 (diff) | |
download | busybox-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.src | 22 | ||||
-rw-r--r-- | shell/ash.c | 465 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/source_argv_and_shift.right | 4 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/source_argv_and_shift.tests | 12 | ||||
-rw-r--r-- | shell/hush.c | 1092 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/source_argv_and_shift.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/source_argv_and_shift.tests | 12 | ||||
-rw-r--r-- | shell/shell_common.c | 2 |
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 | ||
142 | config FEATURE_SH_NOFORK | 130 | config 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 */ |
3792 | static int jobless; //4 | 3799 | static 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 | */ | ||
3825 | static struct termios shell_tty_info; | ||
3826 | static void | ||
3827 | get_tty_state(void) | ||
3828 | { | ||
3829 | if (rootshell && ttyfd >= 0) | ||
3830 | tcgetattr(ttyfd, &shell_tty_info); | ||
3831 | } | ||
3832 | static void | ||
3833 | set_tty_state(void) | ||
3834 | { | ||
3835 | /* if (rootshell) - caller ensures this */ | ||
3836 | if (ttyfd >= 0) | ||
3837 | tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info); | ||
3838 | } | ||
3839 | static int | ||
3840 | job_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 | } | ||
3852 | static void | ||
3853 | restore_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 | |||
3794 | static void | 3867 | static void |
3795 | set_curjob(struct job *jp, unsigned mode) | 3868 | set_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 * | |||
6168 | rmescapes(char *str, int flag) | 6246 | rmescapes(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" */ | ||
7544 | static int | ||
7545 | hasmeta(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) | |||
7734 | static void | 7856 | static void |
7735 | expandmeta(struct strlist *str /*, int flag*/) | 7857 | expandmeta(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 |
9768 | static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } | 9891 | static 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 |
9771 | static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } | 9894 | static 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 |
9774 | static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } | 9897 | static 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 | |||
9778 | static const struct builtincmd builtintab[] = { | 9901 | static 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 |
11826 | static int | 11952 | static int |
11827 | decode_dollar_squote(void) | 11953 | decode_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 @@ | |||
1 | sourced_arg1:1 | ||
2 | arg1: | ||
3 | sourced_arg1:a | ||
4 | arg1: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 @@ | |||
1 | echo 'echo sourced_arg1:$1' >sourced1 | ||
2 | echo 'shift' >>sourced1 | ||
3 | |||
4 | set -- 1 | ||
5 | . ./sourced1 | ||
6 | echo arg1:$1 | ||
7 | |||
8 | set -- 1 | ||
9 | . ./sourced1 a | ||
10 | echo arg1:$1 | ||
11 | |||
12 | rm 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 */ |
841 | static int builtin_cd(char **argv) FAST_FUNC; | 922 | static int builtin_cd(char **argv) FAST_FUNC; |
923 | #if ENABLE_HUSH_ECHO | ||
842 | static int builtin_echo(char **argv) FAST_FUNC; | 924 | static int builtin_echo(char **argv) FAST_FUNC; |
925 | #endif | ||
843 | static int builtin_eval(char **argv) FAST_FUNC; | 926 | static int builtin_eval(char **argv) FAST_FUNC; |
844 | static int builtin_exec(char **argv) FAST_FUNC; | 927 | static int builtin_exec(char **argv) FAST_FUNC; |
845 | static int builtin_exit(char **argv) FAST_FUNC; | 928 | static int builtin_exit(char **argv) FAST_FUNC; |
929 | #if ENABLE_HUSH_EXPORT | ||
846 | static int builtin_export(char **argv) FAST_FUNC; | 930 | static int builtin_export(char **argv) FAST_FUNC; |
931 | #endif | ||
847 | #if ENABLE_HUSH_JOB | 932 | #if ENABLE_HUSH_JOB |
848 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 933 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
849 | static int builtin_jobs(char **argv) FAST_FUNC; | 934 | static 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 |
858 | static int builtin_local(char **argv) FAST_FUNC; | 943 | static int builtin_local(char **argv) FAST_FUNC; |
859 | #endif | 944 | #endif |
860 | #if HUSH_DEBUG | 945 | #if ENABLE_HUSH_MEMLEAK |
861 | static int builtin_memleak(char **argv) FAST_FUNC; | 946 | static int builtin_memleak(char **argv) FAST_FUNC; |
862 | #endif | 947 | #endif |
863 | #if ENABLE_PRINTF | 948 | #if ENABLE_HUSH_PRINTF |
864 | static int builtin_printf(char **argv) FAST_FUNC; | 949 | static int builtin_printf(char **argv) FAST_FUNC; |
865 | #endif | 950 | #endif |
866 | static int builtin_pwd(char **argv) FAST_FUNC; | 951 | static int builtin_pwd(char **argv) FAST_FUNC; |
952 | #if ENABLE_HUSH_READ | ||
867 | static int builtin_read(char **argv) FAST_FUNC; | 953 | static int builtin_read(char **argv) FAST_FUNC; |
954 | #endif | ||
955 | #if ENABLE_HUSH_SET | ||
868 | static int builtin_set(char **argv) FAST_FUNC; | 956 | static int builtin_set(char **argv) FAST_FUNC; |
957 | #endif | ||
869 | static int builtin_shift(char **argv) FAST_FUNC; | 958 | static int builtin_shift(char **argv) FAST_FUNC; |
870 | static int builtin_source(char **argv) FAST_FUNC; | 959 | static int builtin_source(char **argv) FAST_FUNC; |
960 | #if ENABLE_HUSH_TEST || BASH_TEST2 | ||
871 | static int builtin_test(char **argv) FAST_FUNC; | 961 | static int builtin_test(char **argv) FAST_FUNC; |
962 | #endif | ||
963 | #if ENABLE_HUSH_TRAP | ||
872 | static int builtin_trap(char **argv) FAST_FUNC; | 964 | static int builtin_trap(char **argv) FAST_FUNC; |
965 | #endif | ||
966 | #if ENABLE_HUSH_TYPE | ||
873 | static int builtin_type(char **argv) FAST_FUNC; | 967 | static int builtin_type(char **argv) FAST_FUNC; |
968 | #endif | ||
874 | static int builtin_true(char **argv) FAST_FUNC; | 969 | static int builtin_true(char **argv) FAST_FUNC; |
970 | #if ENABLE_HUSH_UMASK | ||
875 | static int builtin_umask(char **argv) FAST_FUNC; | 971 | static int builtin_umask(char **argv) FAST_FUNC; |
972 | #endif | ||
973 | #if ENABLE_HUSH_UNSET | ||
876 | static int builtin_unset(char **argv) FAST_FUNC; | 974 | static int builtin_unset(char **argv) FAST_FUNC; |
975 | #endif | ||
976 | #if ENABLE_HUSH_KILL | ||
977 | static int builtin_kill(char **argv) FAST_FUNC; | ||
978 | #endif | ||
979 | #if ENABLE_HUSH_WAIT | ||
877 | static int builtin_wait(char **argv) FAST_FUNC; | 980 | static int builtin_wait(char **argv) FAST_FUNC; |
981 | #endif | ||
878 | #if ENABLE_HUSH_LOOPS | 982 | #if ENABLE_HUSH_LOOPS |
879 | static int builtin_break(char **argv) FAST_FUNC; | 983 | static int builtin_break(char **argv) FAST_FUNC; |
880 | static int builtin_continue(char **argv) FAST_FUNC; | 984 | static int builtin_continue(char **argv) FAST_FUNC; |
@@ -901,13 +1005,13 @@ struct built_in_command { | |||
901 | }; | 1005 | }; |
902 | 1006 | ||
903 | static const struct built_in_command bltins1[] = { | 1007 | static 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 | */ | ||
957 | static const struct built_in_command bltins2[] = { | 1083 | static 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 | ||
1352 | static void save_and_replace_G_args(save_arg_t *sv, char **argv) | 1487 | static 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 | ||
1371 | static void restore_G_args(save_arg_t *sv, char **argv) | 1506 | static 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 | ||
2033 | static int unset_local_var(const char *name) | 2169 | static 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 | ||
2038 | static void unset_vars(char **strings) | 2175 | static 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 | ||
2053 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2191 | static 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 */ |
5237 | static char *strstr_pattern(char *val, const char *pattern, int *size) | 5382 | static 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 |
5793 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | 5938 | static 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 | */ |
5805 | static char *expand_string_to_string(const char *str, int do_unbackslash) | 5950 | static 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 | ||
6632 | static void unset_func(const char *name) | 6784 | static 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 | ||
8672 | static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) | 8828 | static 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 | ||
8682 | static int FAST_FUNC builtin_test(char **argv) | 8839 | static 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 | ||
8687 | static int FAST_FUNC builtin_echo(char **argv) | 8845 | static 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 |
8693 | static int FAST_FUNC builtin_printf(char **argv) | 8851 | static 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 | ||
8858 | static 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 | ||
8874 | static 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 | |||
8699 | static char **skip_dash_dash(char **argv) | 8881 | static 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 | ||
8707 | static 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 | |||
8725 | static int FAST_FUNC builtin_cd(char **argv) | 8889 | static 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 | ||
8916 | static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | ||
8917 | { | ||
8918 | puts(get_cwd(0)); | ||
8919 | return EXIT_SUCCESS; | ||
8920 | } | ||
8921 | |||
8922 | static 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 | |||
8752 | static int FAST_FUNC builtin_exec(char **argv) | 8940 | static 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 */ | ||
8987 | static 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 | */ | ||
9042 | static 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 | ||
9088 | static 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 | ||
8797 | static void print_escaped(const char *s) | 9127 | static 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 |
8820 | static void helper_export_local(char **argv, int exp, int lvl) | 9152 | static 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 | ||
8876 | static int FAST_FUNC builtin_export(char **argv) | 9210 | static 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 |
8923 | static int FAST_FUNC builtin_local(char **argv) | 9258 | static 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 */ |
8935 | static int FAST_FUNC builtin_unset(char **argv) | 9271 | static 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 | ||
9054 | static int FAST_FUNC builtin_shift(char **argv) | 9393 | static 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 | 9414 | static 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 | */ | ||
9096 | static 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 | ||
9140 | static int FAST_FUNC builtin_trap(char **argv) | 9470 | static 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 */ | ||
9231 | static 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 |
9264 | static struct pipe *parse_jobspec(const char *str) | 9562 | static 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 | ||
9590 | static 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 */ |
9283 | static int FAST_FUNC builtin_fg_bg(char **argv) | 9608 | static 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 |
9338 | static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) | 9663 | static 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 | ||
9354 | static 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 |
9362 | static 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 | ||
9381 | static 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 | |||
9423 | static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | ||
9424 | { | ||
9425 | puts(get_cwd(0)); | ||
9426 | return EXIT_SUCCESS; | ||
9427 | } | 9733 | } |
9428 | |||
9429 | static 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 | |||
9482 | static 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 |
9679 | static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min) | 9900 | static 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 | ||
9966 | static 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 @@ | |||
1 | sourced_arg1:1 | ||
2 | arg1: | ||
3 | sourced_arg1:a | ||
4 | arg1: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 @@ | |||
1 | echo 'echo sourced_arg1:$1' >sourced1 | ||
2 | echo 'shift' >>sourced1 | ||
3 | |||
4 | set -- 1 | ||
5 | . ./sourced1 | ||
6 | echo arg1:$1 | ||
7 | |||
8 | set -- 1 | ||
9 | . ./sourced1 a | ||
10 | echo arg1:$1 | ||
11 | |||
12 | rm 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; |