aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c1092
1 files changed, 678 insertions, 414 deletions
diff --git a/shell/hush.c b/shell/hush.c
index a56d3b280..4123cc19e 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,7 +44,6 @@
44 * special variables (done: PWD, PPID, RANDOM) 44 * special variables (done: PWD, PPID, RANDOM)
45 * tilde expansion 45 * tilde expansion
46 * aliases 46 * aliases
47 * kill %jobspec
48 * follow IFS rules more precisely, including update semantics 47 * follow IFS rules more precisely, including update semantics
49 * builtins mandated by standards we don't support: 48 * builtins mandated by standards we don't support:
50 * [un]alias, command, fc, getopts, newgrp, readonly, times 49 * [un]alias, command, fc, getopts, newgrp, readonly, times
@@ -100,8 +99,6 @@
100//config: bool "bash-compatible extensions" 99//config: bool "bash-compatible extensions"
101//config: default y 100//config: default y
102//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 101//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
103//config: help
104//config: Enable bash-compatible extensions.
105//config: 102//config:
106//config:config HUSH_BRACE_EXPANSION 103//config:config HUSH_BRACE_EXPANSION
107//config: bool "Brace expansion" 104//config: bool "Brace expansion"
@@ -110,13 +107,6 @@
110//config: help 107//config: help
111//config: Enable {abc,def} extension. 108//config: Enable {abc,def} extension.
112//config: 109//config:
113//config:config HUSH_HELP
114//config: bool "help builtin"
115//config: default y
116//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config: help
118//config: Enable help builtin in hush. Code size + ~1 kbyte.
119//config:
120//config:config HUSH_INTERACTIVE 110//config:config HUSH_INTERACTIVE
121//config: bool "Interactive mode" 111//config: bool "Interactive mode"
122//config: default y 112//config: default y
@@ -131,8 +121,6 @@
131//config: bool "Save command history to .hush_history" 121//config: bool "Save command history to .hush_history"
132//config: default y 122//config: default y
133//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY 123//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
134//config: help
135//config: Enable history saving in hush.
136//config: 124//config:
137//config:config HUSH_JOB 125//config:config HUSH_JOB
138//config: bool "Job control" 126//config: bool "Job control"
@@ -146,42 +134,38 @@
146//config: but no separate process group is formed. 134//config: but no separate process group is formed.
147//config: 135//config:
148//config:config HUSH_TICK 136//config:config HUSH_TICK
149//config: bool "Process substitution" 137//config: bool "Support process substitution"
150//config: default y 138//config: default y
151//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 139//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
152//config: help 140//config: help
153//config: Enable process substitution `command` and $(command) in hush. 141//config: Enable `command` and $(command).
154//config: 142//config:
155//config:config HUSH_IF 143//config:config HUSH_IF
156//config: bool "Support if/then/elif/else/fi" 144//config: bool "Support if/then/elif/else/fi"
157//config: default y 145//config: default y
158//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 146//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
159//config: help
160//config: Enable if/then/elif/else/fi in hush.
161//config: 147//config:
162//config:config HUSH_LOOPS 148//config:config HUSH_LOOPS
163//config: bool "Support for, while and until loops" 149//config: bool "Support for, while and until loops"
164//config: default y 150//config: default y
165//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 151//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
166//config: help
167//config: Enable for, while and until loops in hush.
168//config: 152//config:
169//config:config HUSH_CASE 153//config:config HUSH_CASE
170//config: bool "Support case ... esac statement" 154//config: bool "Support case ... esac statement"
171//config: default y 155//config: default y
172//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 156//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
173//config: help 157//config: help
174//config: Enable case ... esac statement in hush. +400 bytes. 158//config: Enable case ... esac statement. +400 bytes.
175//config: 159//config:
176//config:config HUSH_FUNCTIONS 160//config:config HUSH_FUNCTIONS
177//config: bool "Support funcname() { commands; } syntax" 161//config: bool "Support funcname() { commands; } syntax"
178//config: default y 162//config: default y
179//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 163//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
180//config: help 164//config: help
181//config: Enable support for shell functions in hush. +800 bytes. 165//config: Enable support for shell functions. +800 bytes.
182//config: 166//config:
183//config:config HUSH_LOCAL 167//config:config HUSH_LOCAL
184//config: bool "Support local builtin" 168//config: bool "local builtin"
185//config: default y 169//config: default y
186//config: depends on HUSH_FUNCTIONS 170//config: depends on HUSH_FUNCTIONS
187//config: help 171//config: help
@@ -195,20 +179,95 @@
195//config: Enable pseudorandom generator and dynamic variable "$RANDOM". 179//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
196//config: Each read of "$RANDOM" will generate a new pseudorandom value. 180//config: Each read of "$RANDOM" will generate a new pseudorandom value.
197//config: 181//config:
182//config:config HUSH_MODE_X
183//config: bool "Support 'hush -x' option and 'set -x' command"
184//config: default y
185//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
186//config: help
187//config: This instructs hush to print commands before execution.
188//config: Adds ~300 bytes.
189//config:
190//config:config HUSH_ECHO
191//config: bool "echo builtin"
192//config: default y
193//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
194//config:
195//config:config HUSH_PRINTF
196//config: bool "printf builtin"
197//config: default y
198//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
199//config:
200//config:config HUSH_TEST
201//config: bool "test builtin"
202//config: default y
203//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
204//config:
205//config:config HUSH_HELP
206//config: bool "help builtin"
207//config: default y
208//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
209//config:
210//config:config HUSH_EXPORT
211//config: bool "export builtin"
212//config: default y
213//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
214//config:
198//config:config HUSH_EXPORT_N 215//config:config HUSH_EXPORT_N
199//config: bool "Support 'export -n' option" 216//config: bool "Support 'export -n' option"
200//config: default y 217//config: default y
201//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 218//config: depends on HUSH_EXPORT
202//config: help 219//config: help
203//config: export -n unexports variables. It is a bash extension. 220//config: export -n unexports variables. It is a bash extension.
204//config: 221//config:
205//config:config HUSH_MODE_X 222//config:config HUSH_KILL
206//config: bool "Support 'hush -x' option and 'set -x' command" 223//config: bool "kill builtin (supports kill %jobspec)"
224//config: default y
225//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
226//config:
227//config:config HUSH_WAIT
228//config: bool "wait builtin"
229//config: default y
230//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
231//config:
232//config:config HUSH_TRAP
233//config: bool "trap builtin"
234//config: default y
235//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
236//config:
237//config:config HUSH_TYPE
238//config: bool "type builtin"
239//config: default y
240//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
241//config:
242//config:config HUSH_READ
243//config: bool "read builtin"
244//config: default y
245//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
246//config:
247//config:config HUSH_SET
248//config: bool "set builtin"
207//config: default y 249//config: default y
208//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 250//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
209//config: help 251//config:
210//config: This instructs hush to print commands before execution. 252//config:config HUSH_UNSET
211//config: Adds ~300 bytes. 253//config: bool "unset builtin"
254//config: default y
255//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
256//config:
257//config:config HUSH_ULIMIT
258//config: bool "ulimit builtin"
259//config: default y
260//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
261//config:
262//config:config HUSH_UMASK
263//config: bool "umask builtin"
264//config: default y
265//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
266//config:
267//config:config HUSH_MEMLEAK
268//config: bool "memleak builtin (debugging)"
269//config: default n
270//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
212//config: 271//config:
213//config:config MSH 272//config:config MSH
214//config: bool "msh (deprecated: aliased to hush)" 273//config: bool "msh (deprecated: aliased to hush)"
@@ -218,8 +277,9 @@
218//config: msh is deprecated and will be removed, please migrate to hush. 277//config: msh is deprecated and will be removed, please migrate to hush.
219 278
220//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) 279//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
221//applet:IF_MSH(APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 280// APPLET_ODDNAME:name main location suid_type help
222//applet:IF_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 281//applet:IF_MSH( APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
282//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
223//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 283//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
224 284
225//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o 285//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
@@ -267,6 +327,15 @@
267#endif 327#endif
268 328
269 329
330/* So far, all bash compat is controlled by one config option */
331/* Separate defines document which part of code implements what */
332#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
333#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
334#define BASH_TEST2 ENABLE_HUSH_BASH_COMPAT
335#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
336#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
337
338
270/* Build knobs */ 339/* Build knobs */
271#define LEAK_HUNTING 0 340#define LEAK_HUNTING 0
272#define BUILD_AS_NOMMU 0 341#define BUILD_AS_NOMMU 0
@@ -347,12 +416,12 @@
347 416
348#define ERR_PTR ((void*)(long)1) 417#define ERR_PTR ((void*)(long)1)
349 418
350#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 419#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
351 420
352#define _SPECIAL_VARS_STR "_*@$!?#" 421#define _SPECIAL_VARS_STR "_*@$!?#"
353#define SPECIAL_VARS_STR ("_*@$!?#" + 1) 422#define SPECIAL_VARS_STR ("_*@$!?#" + 1)
354#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) 423#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
355#if ENABLE_HUSH_BASH_COMPAT 424#if BASH_PATTERN_SUBST
356/* Support / and // replace ops */ 425/* Support / and // replace ops */
357/* Note that // is stored as \ in "encoded" string representation */ 426/* Note that // is stored as \ in "encoded" string representation */
358# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?" 427# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
@@ -513,7 +582,7 @@ struct command {
513 smallint cmd_type; /* CMD_xxx */ 582 smallint cmd_type; /* CMD_xxx */
514#define CMD_NORMAL 0 583#define CMD_NORMAL 0
515#define CMD_SUBSHELL 1 584#define CMD_SUBSHELL 1
516#if ENABLE_HUSH_BASH_COMPAT 585#if BASH_TEST2
517/* used for "[[ EXPR ]]" */ 586/* used for "[[ EXPR ]]" */
518# define CMD_SINGLEWORD_NOGLOB 2 587# define CMD_SINGLEWORD_NOGLOB 2
519#endif 588#endif
@@ -563,7 +632,7 @@ struct pipe {
563 int alive_cmds; /* number of commands running (not exited) */ 632 int alive_cmds; /* number of commands running (not exited) */
564 int stopped_cmds; /* number of commands alive, but stopped */ 633 int stopped_cmds; /* number of commands alive, but stopped */
565#if ENABLE_HUSH_JOB 634#if ENABLE_HUSH_JOB
566 int jobid; /* job number */ 635 unsigned jobid; /* job number */
567 pid_t pgrp; /* process group ID for the job */ 636 pid_t pgrp; /* process group ID for the job */
568 char *cmdtext; /* name of job */ 637 char *cmdtext; /* name of job */
569#endif 638#endif
@@ -740,7 +809,7 @@ struct globals {
740#endif 809#endif
741#if ENABLE_HUSH_JOB 810#if ENABLE_HUSH_JOB
742 int run_list_level; 811 int run_list_level;
743 int last_jobid; 812 unsigned last_jobid;
744 pid_t saved_tty_pgrp; 813 pid_t saved_tty_pgrp;
745 struct pipe *job_list; 814 struct pipe *job_list;
746# define G_saved_tty_pgrp (G.saved_tty_pgrp) 815# define G_saved_tty_pgrp (G.saved_tty_pgrp)
@@ -770,8 +839,13 @@ struct globals {
770 smallint exiting; /* used to prevent EXIT trap recursion */ 839 smallint exiting; /* used to prevent EXIT trap recursion */
771 /* These four support $?, $#, and $1 */ 840 /* These four support $?, $#, and $1 */
772 smalluint last_exitcode; 841 smalluint last_exitcode;
842#if ENABLE_HUSH_SET
773 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 843 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
774 smalluint global_args_malloced; 844 smalluint global_args_malloced;
845# define G_global_args_malloced (G.global_args_malloced)
846#else
847# define G_global_args_malloced 0
848#endif
775 /* how many non-NULL argv's we have. NB: $# + 1 */ 849 /* how many non-NULL argv's we have. NB: $# + 1 */
776 int global_argc; 850 int global_argc;
777 char **global_argv; 851 char **global_argv;
@@ -810,14 +884,21 @@ struct globals {
810 unsigned special_sig_mask; 884 unsigned special_sig_mask;
811#if ENABLE_HUSH_JOB 885#if ENABLE_HUSH_JOB
812 unsigned fatal_sig_mask; 886 unsigned fatal_sig_mask;
813# define G_fatal_sig_mask G.fatal_sig_mask 887# define G_fatal_sig_mask (G.fatal_sig_mask)
814#else 888#else
815# define G_fatal_sig_mask 0 889# define G_fatal_sig_mask 0
816#endif 890#endif
891#if ENABLE_HUSH_TRAP
817 char **traps; /* char *traps[NSIG] */ 892 char **traps; /* char *traps[NSIG] */
893# define G_traps G.traps
894#else
895# define G_traps ((char**)NULL)
896#endif
818 sigset_t pending_set; 897 sigset_t pending_set;
819#if HUSH_DEBUG 898#if ENABLE_HUSH_MEMLEAK
820 unsigned long memleak_value; 899 unsigned long memleak_value;
900#endif
901#if HUSH_DEBUG
821 int debug_indent; 902 int debug_indent;
822#endif 903#endif
823 struct sigaction sa; 904 struct sigaction sa;
@@ -839,11 +920,15 @@ struct globals {
839 920
840/* Function prototypes for builtins */ 921/* Function prototypes for builtins */
841static int builtin_cd(char **argv) FAST_FUNC; 922static int builtin_cd(char **argv) FAST_FUNC;
923#if ENABLE_HUSH_ECHO
842static int builtin_echo(char **argv) FAST_FUNC; 924static int builtin_echo(char **argv) FAST_FUNC;
925#endif
843static int builtin_eval(char **argv) FAST_FUNC; 926static int builtin_eval(char **argv) FAST_FUNC;
844static int builtin_exec(char **argv) FAST_FUNC; 927static int builtin_exec(char **argv) FAST_FUNC;
845static int builtin_exit(char **argv) FAST_FUNC; 928static int builtin_exit(char **argv) FAST_FUNC;
929#if ENABLE_HUSH_EXPORT
846static int builtin_export(char **argv) FAST_FUNC; 930static int builtin_export(char **argv) FAST_FUNC;
931#endif
847#if ENABLE_HUSH_JOB 932#if ENABLE_HUSH_JOB
848static int builtin_fg_bg(char **argv) FAST_FUNC; 933static int builtin_fg_bg(char **argv) FAST_FUNC;
849static int builtin_jobs(char **argv) FAST_FUNC; 934static int builtin_jobs(char **argv) FAST_FUNC;
@@ -857,24 +942,43 @@ static int builtin_history(char **argv) FAST_FUNC;
857#if ENABLE_HUSH_LOCAL 942#if ENABLE_HUSH_LOCAL
858static int builtin_local(char **argv) FAST_FUNC; 943static int builtin_local(char **argv) FAST_FUNC;
859#endif 944#endif
860#if HUSH_DEBUG 945#if ENABLE_HUSH_MEMLEAK
861static int builtin_memleak(char **argv) FAST_FUNC; 946static int builtin_memleak(char **argv) FAST_FUNC;
862#endif 947#endif
863#if ENABLE_PRINTF 948#if ENABLE_HUSH_PRINTF
864static int builtin_printf(char **argv) FAST_FUNC; 949static int builtin_printf(char **argv) FAST_FUNC;
865#endif 950#endif
866static int builtin_pwd(char **argv) FAST_FUNC; 951static int builtin_pwd(char **argv) FAST_FUNC;
952#if ENABLE_HUSH_READ
867static int builtin_read(char **argv) FAST_FUNC; 953static int builtin_read(char **argv) FAST_FUNC;
954#endif
955#if ENABLE_HUSH_SET
868static int builtin_set(char **argv) FAST_FUNC; 956static int builtin_set(char **argv) FAST_FUNC;
957#endif
869static int builtin_shift(char **argv) FAST_FUNC; 958static int builtin_shift(char **argv) FAST_FUNC;
870static int builtin_source(char **argv) FAST_FUNC; 959static int builtin_source(char **argv) FAST_FUNC;
960#if ENABLE_HUSH_TEST || BASH_TEST2
871static int builtin_test(char **argv) FAST_FUNC; 961static int builtin_test(char **argv) FAST_FUNC;
962#endif
963#if ENABLE_HUSH_TRAP
872static int builtin_trap(char **argv) FAST_FUNC; 964static int builtin_trap(char **argv) FAST_FUNC;
965#endif
966#if ENABLE_HUSH_TYPE
873static int builtin_type(char **argv) FAST_FUNC; 967static int builtin_type(char **argv) FAST_FUNC;
968#endif
874static int builtin_true(char **argv) FAST_FUNC; 969static int builtin_true(char **argv) FAST_FUNC;
970#if ENABLE_HUSH_UMASK
875static int builtin_umask(char **argv) FAST_FUNC; 971static int builtin_umask(char **argv) FAST_FUNC;
972#endif
973#if ENABLE_HUSH_UNSET
876static int builtin_unset(char **argv) FAST_FUNC; 974static int builtin_unset(char **argv) FAST_FUNC;
975#endif
976#if ENABLE_HUSH_KILL
977static int builtin_kill(char **argv) FAST_FUNC;
978#endif
979#if ENABLE_HUSH_WAIT
877static int builtin_wait(char **argv) FAST_FUNC; 980static int builtin_wait(char **argv) FAST_FUNC;
981#endif
878#if ENABLE_HUSH_LOOPS 982#if ENABLE_HUSH_LOOPS
879static int builtin_break(char **argv) FAST_FUNC; 983static int builtin_break(char **argv) FAST_FUNC;
880static int builtin_continue(char **argv) FAST_FUNC; 984static int builtin_continue(char **argv) FAST_FUNC;
@@ -901,13 +1005,13 @@ struct built_in_command {
901}; 1005};
902 1006
903static const struct built_in_command bltins1[] = { 1007static const struct built_in_command bltins1[] = {
904 BLTIN("." , builtin_source , "Run commands in a file"), 1008 BLTIN("." , builtin_source , "Run commands in file"),
905 BLTIN(":" , builtin_true , NULL), 1009 BLTIN(":" , builtin_true , NULL),
906#if ENABLE_HUSH_JOB 1010#if ENABLE_HUSH_JOB
907 BLTIN("bg" , builtin_fg_bg , "Resume a job in the background"), 1011 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
908#endif 1012#endif
909#if ENABLE_HUSH_LOOPS 1013#if ENABLE_HUSH_LOOPS
910 BLTIN("break" , builtin_break , "Exit from a loop"), 1014 BLTIN("break" , builtin_break , "Exit loop"),
911#endif 1015#endif
912 BLTIN("cd" , builtin_cd , "Change directory"), 1016 BLTIN("cd" , builtin_cd , "Change directory"),
913#if ENABLE_HUSH_LOOPS 1017#if ENABLE_HUSH_LOOPS
@@ -915,53 +1019,84 @@ static const struct built_in_command bltins1[] = {
915#endif 1019#endif
916 BLTIN("eval" , builtin_eval , "Construct and run shell command"), 1020 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
917 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"), 1021 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
918 BLTIN("exit" , builtin_exit , "Exit"), 1022 BLTIN("exit" , builtin_exit , NULL),
1023#if ENABLE_HUSH_EXPORT
919 BLTIN("export" , builtin_export , "Set environment variables"), 1024 BLTIN("export" , builtin_export , "Set environment variables"),
1025#endif
920#if ENABLE_HUSH_JOB 1026#if ENABLE_HUSH_JOB
921 BLTIN("fg" , builtin_fg_bg , "Bring job into the foreground"), 1027 BLTIN("fg" , builtin_fg_bg , "Bring job into foreground"),
922#endif 1028#endif
923#if ENABLE_HUSH_HELP 1029#if ENABLE_HUSH_HELP
924 BLTIN("help" , builtin_help , NULL), 1030 BLTIN("help" , builtin_help , NULL),
925#endif 1031#endif
926#if MAX_HISTORY && ENABLE_FEATURE_EDITING 1032#if MAX_HISTORY && ENABLE_FEATURE_EDITING
927 BLTIN("history" , builtin_history , "Show command history"), 1033 BLTIN("history" , builtin_history , "Show history"),
928#endif 1034#endif
929#if ENABLE_HUSH_JOB 1035#if ENABLE_HUSH_JOB
930 BLTIN("jobs" , builtin_jobs , "List jobs"), 1036 BLTIN("jobs" , builtin_jobs , "List jobs"),
931#endif 1037#endif
1038#if ENABLE_HUSH_KILL
1039 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1040#endif
932#if ENABLE_HUSH_LOCAL 1041#if ENABLE_HUSH_LOCAL
933 BLTIN("local" , builtin_local , "Set local variables"), 1042 BLTIN("local" , builtin_local , "Set local variables"),
934#endif 1043#endif
935#if HUSH_DEBUG 1044#if ENABLE_HUSH_MEMLEAK
936 BLTIN("memleak" , builtin_memleak , NULL), 1045 BLTIN("memleak" , builtin_memleak , NULL),
937#endif 1046#endif
1047#if ENABLE_HUSH_READ
938 BLTIN("read" , builtin_read , "Input into variable"), 1048 BLTIN("read" , builtin_read , "Input into variable"),
1049#endif
939#if ENABLE_HUSH_FUNCTIONS 1050#if ENABLE_HUSH_FUNCTIONS
940 BLTIN("return" , builtin_return , "Return from a function"), 1051 BLTIN("return" , builtin_return , "Return from function"),
1052#endif
1053#if ENABLE_HUSH_SET
1054 BLTIN("set" , builtin_set , "Set positional parameters"),
941#endif 1055#endif
942 BLTIN("set" , builtin_set , "Set/unset positional parameters"),
943 BLTIN("shift" , builtin_shift , "Shift positional parameters"), 1056 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
944#if ENABLE_HUSH_BASH_COMPAT 1057#if BASH_SOURCE
945 BLTIN("source" , builtin_source , "Run commands in a file"), 1058 BLTIN("source" , builtin_source , NULL),
946#endif 1059#endif
1060#if ENABLE_HUSH_TRAP
947 BLTIN("trap" , builtin_trap , "Trap signals"), 1061 BLTIN("trap" , builtin_trap , "Trap signals"),
1062#endif
948 BLTIN("true" , builtin_true , NULL), 1063 BLTIN("true" , builtin_true , NULL),
1064#if ENABLE_HUSH_TYPE
949 BLTIN("type" , builtin_type , "Show command type"), 1065 BLTIN("type" , builtin_type , "Show command type"),
950 BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), 1066#endif
1067#if ENABLE_HUSH_ULIMIT
1068 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1069#endif
1070#if ENABLE_HUSH_UMASK
951 BLTIN("umask" , builtin_umask , "Set file creation mask"), 1071 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1072#endif
1073#if ENABLE_HUSH_UNSET
952 BLTIN("unset" , builtin_unset , "Unset variables"), 1074 BLTIN("unset" , builtin_unset , "Unset variables"),
1075#endif
1076#if ENABLE_HUSH_WAIT
953 BLTIN("wait" , builtin_wait , "Wait for process"), 1077 BLTIN("wait" , builtin_wait , "Wait for process"),
1078#endif
954}; 1079};
955/* For now, echo and test are unconditionally enabled. 1080/* These builtins won't be used if we are on NOMMU and need to re-exec
956 * Maybe make it configurable? */ 1081 * (it's cheaper to run an external program in this case):
1082 */
957static const struct built_in_command bltins2[] = { 1083static const struct built_in_command bltins2[] = {
1084#if ENABLE_HUSH_TEST
958 BLTIN("[" , builtin_test , NULL), 1085 BLTIN("[" , builtin_test , NULL),
1086#endif
1087#if BASH_TEST2
1088 BLTIN("[[" , builtin_test , NULL),
1089#endif
1090#if ENABLE_HUSH_ECHO
959 BLTIN("echo" , builtin_echo , NULL), 1091 BLTIN("echo" , builtin_echo , NULL),
960#if ENABLE_PRINTF 1092#endif
1093#if ENABLE_HUSH_PRINTF
961 BLTIN("printf" , builtin_printf , NULL), 1094 BLTIN("printf" , builtin_printf , NULL),
962#endif 1095#endif
963 BLTIN("pwd" , builtin_pwd , NULL), 1096 BLTIN("pwd" , builtin_pwd , NULL),
1097#if ENABLE_HUSH_TEST
964 BLTIN("test" , builtin_test , NULL), 1098 BLTIN("test" , builtin_test , NULL),
1099#endif
965}; 1100};
966 1101
967 1102
@@ -1346,7 +1481,7 @@ typedef struct save_arg_t {
1346 char *sv_argv0; 1481 char *sv_argv0;
1347 char **sv_g_argv; 1482 char **sv_g_argv;
1348 int sv_g_argc; 1483 int sv_g_argc;
1349 smallint sv_g_malloced; 1484 IF_HUSH_SET(smallint sv_g_malloced;)
1350} save_arg_t; 1485} save_arg_t;
1351 1486
1352static void save_and_replace_G_args(save_arg_t *sv, char **argv) 1487static void save_and_replace_G_args(save_arg_t *sv, char **argv)
@@ -1356,11 +1491,11 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1356 sv->sv_argv0 = argv[0]; 1491 sv->sv_argv0 = argv[0];
1357 sv->sv_g_argv = G.global_argv; 1492 sv->sv_g_argv = G.global_argv;
1358 sv->sv_g_argc = G.global_argc; 1493 sv->sv_g_argc = G.global_argc;
1359 sv->sv_g_malloced = G.global_args_malloced; 1494 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1360 1495
1361 argv[0] = G.global_argv[0]; /* retain $0 */ 1496 argv[0] = G.global_argv[0]; /* retain $0 */
1362 G.global_argv = argv; 1497 G.global_argv = argv;
1363 G.global_args_malloced = 0; 1498 IF_HUSH_SET(G.global_args_malloced = 0;)
1364 1499
1365 n = 1; 1500 n = 1;
1366 while (*++argv) 1501 while (*++argv)
@@ -1370,19 +1505,19 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1370 1505
1371static void restore_G_args(save_arg_t *sv, char **argv) 1506static void restore_G_args(save_arg_t *sv, char **argv)
1372{ 1507{
1373 char **pp; 1508#if ENABLE_HUSH_SET
1374
1375 if (G.global_args_malloced) { 1509 if (G.global_args_malloced) {
1376 /* someone ran "set -- arg1 arg2 ...", undo */ 1510 /* someone ran "set -- arg1 arg2 ...", undo */
1377 pp = G.global_argv; 1511 char **pp = G.global_argv;
1378 while (*++pp) /* note: does not free $0 */ 1512 while (*++pp) /* note: does not free $0 */
1379 free(*pp); 1513 free(*pp);
1380 free(G.global_argv); 1514 free(G.global_argv);
1381 } 1515 }
1516#endif
1382 argv[0] = sv->sv_argv0; 1517 argv[0] = sv->sv_argv0;
1383 G.global_argv = sv->sv_g_argv; 1518 G.global_argv = sv->sv_g_argv;
1384 G.global_argc = sv->sv_g_argc; 1519 G.global_argc = sv->sv_g_argc;
1385 G.global_args_malloced = sv->sv_g_malloced; 1520 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1386} 1521}
1387 1522
1388 1523
@@ -1670,13 +1805,13 @@ static void hush_exit(int exitcode)
1670#endif 1805#endif
1671 1806
1672 fflush_all(); 1807 fflush_all();
1673 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { 1808 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
1674 char *argv[3]; 1809 char *argv[3];
1675 /* argv[0] is unused */ 1810 /* argv[0] is unused */
1676 argv[1] = G.traps[0]; 1811 argv[1] = G_traps[0];
1677 argv[2] = NULL; 1812 argv[2] = NULL;
1678 G.exiting = 1; /* prevent EXIT trap recursion */ 1813 G.exiting = 1; /* prevent EXIT trap recursion */
1679 /* Note: G.traps[0] is not cleared! 1814 /* Note: G_traps[0] is not cleared!
1680 * "trap" will still show it, if executed 1815 * "trap" will still show it, if executed
1681 * in the handler */ 1816 * in the handler */
1682 builtin_eval(argv); 1817 builtin_eval(argv);
@@ -1727,14 +1862,14 @@ static int check_and_run_traps(void)
1727 } while (sig < NSIG); 1862 } while (sig < NSIG);
1728 break; 1863 break;
1729 got_sig: 1864 got_sig:
1730 if (G.traps && G.traps[sig]) { 1865 if (G_traps && G_traps[sig]) {
1731 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); 1866 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
1732 if (G.traps[sig][0]) { 1867 if (G_traps[sig][0]) {
1733 /* We have user-defined handler */ 1868 /* We have user-defined handler */
1734 smalluint save_rcode; 1869 smalluint save_rcode;
1735 char *argv[3]; 1870 char *argv[3];
1736 /* argv[0] is unused */ 1871 /* argv[0] is unused */
1737 argv[1] = G.traps[sig]; 1872 argv[1] = G_traps[sig];
1738 argv[2] = NULL; 1873 argv[2] = NULL;
1739 save_rcode = G.last_exitcode; 1874 save_rcode = G.last_exitcode;
1740 builtin_eval(argv); 1875 builtin_eval(argv);
@@ -2030,10 +2165,12 @@ static int unset_local_var_len(const char *name, int name_len)
2030 return EXIT_SUCCESS; 2165 return EXIT_SUCCESS;
2031} 2166}
2032 2167
2168#if ENABLE_HUSH_UNSET
2033static int unset_local_var(const char *name) 2169static int unset_local_var(const char *name)
2034{ 2170{
2035 return unset_local_var_len(name, strlen(name)); 2171 return unset_local_var_len(name, strlen(name));
2036} 2172}
2173#endif
2037 2174
2038static void unset_vars(char **strings) 2175static void unset_vars(char **strings)
2039{ 2176{
@@ -2050,11 +2187,13 @@ static void unset_vars(char **strings)
2050 free(strings); 2187 free(strings);
2051} 2188}
2052 2189
2190#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ
2053static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2191static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2054{ 2192{
2055 char *var = xasprintf("%s=%s", name, val); 2193 char *var = xasprintf("%s=%s", name, val);
2056 set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 2194 set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
2057} 2195}
2196#endif
2058 2197
2059 2198
2060/* 2199/*
@@ -3507,7 +3646,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
3507 (ctx->ctx_res_w == RES_SNTX)); 3646 (ctx->ctx_res_w == RES_SNTX));
3508 return (ctx->ctx_res_w == RES_SNTX); 3647 return (ctx->ctx_res_w == RES_SNTX);
3509 } 3648 }
3510# if ENABLE_HUSH_BASH_COMPAT 3649# if BASH_TEST2
3511 if (strcmp(word->data, "[[") == 0) { 3650 if (strcmp(word->data, "[[") == 0) {
3512 command->cmd_type = CMD_SINGLEWORD_NOGLOB; 3651 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
3513 } 3652 }
@@ -4105,7 +4244,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4105{ 4244{
4106 int ch; 4245 int ch;
4107 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; 4246 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
4108# if ENABLE_HUSH_BASH_COMPAT 4247# if BASH_SUBSTR || BASH_PATTERN_SUBST
4109 char end_char2 = end_ch >> 8; 4248 char end_char2 = end_ch >> 8;
4110# endif 4249# endif
4111 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 4250 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
@@ -4116,7 +4255,11 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4116 syntax_error_unterm_ch(end_ch); 4255 syntax_error_unterm_ch(end_ch);
4117 return 0; 4256 return 0;
4118 } 4257 }
4119 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { 4258 if (ch == end_ch
4259# if BASH_SUBSTR || BASH_PATTERN_SUBST
4260 || ch == end_char2
4261# endif
4262 ) {
4120 if (!dbl) 4263 if (!dbl)
4121 break; 4264 break;
4122 /* we look for closing )) of $((EXPR)) */ 4265 /* we look for closing )) of $((EXPR)) */
@@ -4269,14 +4412,14 @@ static int parse_dollar(o_string *as_string,
4269 4412
4270 /* Eat everything until closing '}' (or ':') */ 4413 /* Eat everything until closing '}' (or ':') */
4271 end_ch = '}'; 4414 end_ch = '}';
4272 if (ENABLE_HUSH_BASH_COMPAT 4415 if (BASH_SUBSTR
4273 && ch == ':' 4416 && ch == ':'
4274 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) 4417 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
4275 ) { 4418 ) {
4276 /* It's ${var:N[:M]} thing */ 4419 /* It's ${var:N[:M]} thing */
4277 end_ch = '}' * 0x100 + ':'; 4420 end_ch = '}' * 0x100 + ':';
4278 } 4421 }
4279 if (ENABLE_HUSH_BASH_COMPAT 4422 if (BASH_PATTERN_SUBST
4280 && ch == '/' 4423 && ch == '/'
4281 ) { 4424 ) {
4282 /* It's ${var/[/]pattern[/repl]} thing */ 4425 /* It's ${var/[/]pattern[/repl]} thing */
@@ -4303,7 +4446,9 @@ static int parse_dollar(o_string *as_string,
4303 o_addchr(as_string, last_ch); 4446 o_addchr(as_string, last_ch);
4304 } 4447 }
4305 4448
4306 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { 4449 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
4450 && (end_ch & 0xff00)
4451 ) {
4307 /* close the first block: */ 4452 /* close the first block: */
4308 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4453 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4309 /* while parsing N from ${var:N[:M]} 4454 /* while parsing N from ${var:N[:M]}
@@ -4314,7 +4459,7 @@ static int parse_dollar(o_string *as_string,
4314 goto again; 4459 goto again;
4315 } 4460 }
4316 /* got '}' */ 4461 /* got '}' */
4317 if (end_ch == '}' * 0x100 + ':') { 4462 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
4318 /* it's ${var:N} - emulate :999999999 */ 4463 /* it's ${var:N} - emulate :999999999 */
4319 o_addstr(dest, "999999999"); 4464 o_addstr(dest, "999999999");
4320 } /* else: it's ${var/[/]pattern} */ 4465 } /* else: it's ${var/[/]pattern} */
@@ -4389,7 +4534,7 @@ static int parse_dollar(o_string *as_string,
4389} 4534}
4390 4535
4391#if BB_MMU 4536#if BB_MMU
4392# if ENABLE_HUSH_BASH_COMPAT 4537# if BASH_PATTERN_SUBST
4393#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ 4538#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
4394 encode_string(dest, input, dquote_end, process_bkslash) 4539 encode_string(dest, input, dquote_end, process_bkslash)
4395# else 4540# else
@@ -4401,7 +4546,7 @@ static int parse_dollar(o_string *as_string,
4401 4546
4402#else /* !MMU */ 4547#else /* !MMU */
4403 4548
4404# if ENABLE_HUSH_BASH_COMPAT 4549# if BASH_PATTERN_SUBST
4405/* all parameters are needed, no macro tricks */ 4550/* all parameters are needed, no macro tricks */
4406# else 4551# else
4407#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ 4552#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
@@ -4414,7 +4559,7 @@ static int encode_string(o_string *as_string,
4414 int dquote_end, 4559 int dquote_end,
4415 int process_bkslash) 4560 int process_bkslash)
4416{ 4561{
4417#if !ENABLE_HUSH_BASH_COMPAT 4562#if !BASH_PATTERN_SUBST
4418 const int process_bkslash = 1; 4563 const int process_bkslash = 1;
4419#endif 4564#endif
4420 int ch; 4565 int ch;
@@ -5057,7 +5202,7 @@ static struct pipe *parse_stream(char **pstring,
5057/*** Execution routines ***/ 5202/*** Execution routines ***/
5058 5203
5059/* Expansion can recurse, need forward decls: */ 5204/* Expansion can recurse, need forward decls: */
5060#if !ENABLE_HUSH_BASH_COMPAT 5205#if !BASH_PATTERN_SUBST
5061/* only ${var/pattern/repl} (its pattern part) needs additional mode */ 5206/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5062#define expand_string_to_string(str, do_unbackslash) \ 5207#define expand_string_to_string(str, do_unbackslash) \
5063 expand_string_to_string(str) 5208 expand_string_to_string(str)
@@ -5178,7 +5323,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha
5178 * Returns malloced string. 5323 * Returns malloced string.
5179 * As an optimization, we return NULL if expansion is not needed. 5324 * As an optimization, we return NULL if expansion is not needed.
5180 */ 5325 */
5181#if !ENABLE_HUSH_BASH_COMPAT 5326#if !BASH_PATTERN_SUBST
5182/* only ${var/pattern/repl} (its pattern part) needs additional mode */ 5327/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5183#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \ 5328#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
5184 encode_then_expand_string(str) 5329 encode_then_expand_string(str)
@@ -5232,7 +5377,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5232} 5377}
5233#endif 5378#endif
5234 5379
5235#if ENABLE_HUSH_BASH_COMPAT 5380#if BASH_PATTERN_SUBST
5236/* ${var/[/]pattern[/repl]} helpers */ 5381/* ${var/[/]pattern[/repl]} helpers */
5237static char *strstr_pattern(char *val, const char *pattern, int *size) 5382static char *strstr_pattern(char *val, const char *pattern, int *size)
5238{ 5383{
@@ -5284,7 +5429,7 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c
5284 debug_printf_varexp("result:'%s'\n", result); 5429 debug_printf_varexp("result:'%s'\n", result);
5285 return result; 5430 return result;
5286} 5431}
5287#endif 5432#endif /* BASH_PATTERN_SUBST */
5288 5433
5289/* Helper: 5434/* Helper:
5290 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. 5435 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
@@ -5332,7 +5477,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5332 if (exp_op == ':') { 5477 if (exp_op == ':') {
5333 exp_op = *exp_word++; 5478 exp_op = *exp_word++;
5334//TODO: try ${var:} and ${var:bogus} in non-bash config 5479//TODO: try ${var:} and ${var:bogus} in non-bash config
5335 if (ENABLE_HUSH_BASH_COMPAT 5480 if (BASH_SUBSTR
5336 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) 5481 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
5337 ) { 5482 ) {
5338 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ 5483 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
@@ -5414,7 +5559,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5414 } 5559 }
5415 } 5560 }
5416 } 5561 }
5417#if ENABLE_HUSH_BASH_COMPAT 5562#if BASH_PATTERN_SUBST
5418 else if (exp_op == '/' || exp_op == '\\') { 5563 else if (exp_op == '/' || exp_op == '\\') {
5419 /* It's ${var/[/]pattern[/repl]} thing. 5564 /* It's ${var/[/]pattern[/repl]} thing.
5420 * Note that in encoded form it has TWO parts: 5565 * Note that in encoded form it has TWO parts:
@@ -5461,9 +5606,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5461 free(repl); 5606 free(repl);
5462 } 5607 }
5463 } 5608 }
5464#endif 5609#endif /* BASH_PATTERN_SUBST */
5465 else if (exp_op == ':') { 5610 else if (exp_op == ':') {
5466#if ENABLE_HUSH_BASH_COMPAT && ENABLE_FEATURE_SH_MATH 5611#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
5467 /* It's ${var:N[:M]} bashism. 5612 /* It's ${var:N[:M]} bashism.
5468 * Note that in encoded form it has TWO parts: 5613 * Note that in encoded form it has TWO parts:
5469 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 5614 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -5499,7 +5644,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5499 } 5644 }
5500 debug_printf_varexp("val:'%s'\n", val); 5645 debug_printf_varexp("val:'%s'\n", val);
5501 } else 5646 } else
5502#endif 5647#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */
5503 { 5648 {
5504 die_if_script("malformed ${%s:...}", var); 5649 die_if_script("malformed ${%s:...}", var);
5505 val = NULL; 5650 val = NULL;
@@ -5789,7 +5934,7 @@ static char **expand_strvec_to_strvec(char **argv)
5789 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); 5934 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
5790} 5935}
5791 5936
5792#if ENABLE_HUSH_BASH_COMPAT 5937#if BASH_TEST2
5793static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 5938static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
5794{ 5939{
5795 return expand_variables(argv, EXP_FLAG_SINGLEWORD); 5940 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
@@ -5804,7 +5949,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
5804 */ 5949 */
5805static char *expand_string_to_string(const char *str, int do_unbackslash) 5950static char *expand_string_to_string(const char *str, int do_unbackslash)
5806{ 5951{
5807#if !ENABLE_HUSH_BASH_COMPAT 5952#if !BASH_PATTERN_SUBST
5808 const int do_unbackslash = 1; 5953 const int do_unbackslash = 1;
5809#endif 5954#endif
5810 char *argv[2], **list; 5955 char *argv[2], **list;
@@ -5882,13 +6027,15 @@ static void switch_off_special_sigs(unsigned mask)
5882 sig++; 6027 sig++;
5883 if (!(mask & 1)) 6028 if (!(mask & 1))
5884 continue; 6029 continue;
5885 if (G.traps) { 6030#if ENABLE_HUSH_TRAP
5886 if (G.traps[sig] && !G.traps[sig][0]) 6031 if (G_traps) {
6032 if (G_traps[sig] && !G_traps[sig][0])
5887 /* trap is '', has to remain SIG_IGN */ 6033 /* trap is '', has to remain SIG_IGN */
5888 continue; 6034 continue;
5889 free(G.traps[sig]); 6035 free(G_traps[sig]);
5890 G.traps[sig] = NULL; 6036 G_traps[sig] = NULL;
5891 } 6037 }
6038#endif
5892 /* We are here only if no trap or trap was not '' */ 6039 /* We are here only if no trap or trap was not '' */
5893 install_sighandler(sig, SIG_DFL); 6040 install_sighandler(sig, SIG_DFL);
5894 } 6041 }
@@ -5905,7 +6052,7 @@ static void reset_traps_to_defaults(void)
5905 /* This function is always called in a child shell 6052 /* This function is always called in a child shell
5906 * after fork (not vfork, NOMMU doesn't use this function). 6053 * after fork (not vfork, NOMMU doesn't use this function).
5907 */ 6054 */
5908 unsigned sig; 6055 IF_HUSH_TRAP(unsigned sig;)
5909 unsigned mask; 6056 unsigned mask;
5910 6057
5911 /* Child shells are not interactive. 6058 /* Child shells are not interactive.
@@ -5914,35 +6061,37 @@ static void reset_traps_to_defaults(void)
5914 * Same goes for SIGTERM, SIGHUP, SIGINT. 6061 * Same goes for SIGTERM, SIGHUP, SIGINT.
5915 */ 6062 */
5916 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; 6063 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
5917 if (!G.traps && !mask) 6064 if (!G_traps && !mask)
5918 return; /* already no traps and no special sigs */ 6065 return; /* already no traps and no special sigs */
5919 6066
5920 /* Switch off special sigs */ 6067 /* Switch off special sigs */
5921 switch_off_special_sigs(mask); 6068 switch_off_special_sigs(mask);
5922#if ENABLE_HUSH_JOB 6069# if ENABLE_HUSH_JOB
5923 G_fatal_sig_mask = 0; 6070 G_fatal_sig_mask = 0;
5924#endif 6071# endif
5925 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; 6072 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
5926 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS 6073 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
5927 * remain set in G.special_sig_mask */ 6074 * remain set in G.special_sig_mask */
5928 6075
5929 if (!G.traps) 6076# if ENABLE_HUSH_TRAP
6077 if (!G_traps)
5930 return; 6078 return;
5931 6079
5932 /* Reset all sigs to default except ones with empty traps */ 6080 /* Reset all sigs to default except ones with empty traps */
5933 for (sig = 0; sig < NSIG; sig++) { 6081 for (sig = 0; sig < NSIG; sig++) {
5934 if (!G.traps[sig]) 6082 if (!G_traps[sig])
5935 continue; /* no trap: nothing to do */ 6083 continue; /* no trap: nothing to do */
5936 if (!G.traps[sig][0]) 6084 if (!G_traps[sig][0])
5937 continue; /* empty trap: has to remain SIG_IGN */ 6085 continue; /* empty trap: has to remain SIG_IGN */
5938 /* sig has non-empty trap, reset it: */ 6086 /* sig has non-empty trap, reset it: */
5939 free(G.traps[sig]); 6087 free(G_traps[sig]);
5940 G.traps[sig] = NULL; 6088 G_traps[sig] = NULL;
5941 /* There is no signal for trap 0 (EXIT) */ 6089 /* There is no signal for trap 0 (EXIT) */
5942 if (sig == 0) 6090 if (sig == 0)
5943 continue; 6091 continue;
5944 install_sighandler(sig, pick_sighandler(sig)); 6092 install_sighandler(sig, pick_sighandler(sig));
5945 } 6093 }
6094# endif
5946} 6095}
5947 6096
5948#else /* !BB_MMU */ 6097#else /* !BB_MMU */
@@ -5982,10 +6131,10 @@ static void re_execute_shell(char ***to_free, const char *s,
5982 cnt++; 6131 cnt++;
5983 6132
5984 empty_trap_mask = 0; 6133 empty_trap_mask = 0;
5985 if (G.traps) { 6134 if (G_traps) {
5986 int sig; 6135 int sig;
5987 for (sig = 1; sig < NSIG; sig++) { 6136 for (sig = 1; sig < NSIG; sig++) {
5988 if (G.traps[sig] && !G.traps[sig][0]) 6137 if (G_traps[sig] && !G_traps[sig][0])
5989 empty_trap_mask |= 1LL << sig; 6138 empty_trap_mask |= 1LL << sig;
5990 } 6139 }
5991 } 6140 }
@@ -6178,6 +6327,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
6178 xmove_fd(channel[1], 1); 6327 xmove_fd(channel[1], 1);
6179 /* Prevent it from trying to handle ctrl-z etc */ 6328 /* Prevent it from trying to handle ctrl-z etc */
6180 IF_HUSH_JOB(G.run_list_level = 1;) 6329 IF_HUSH_JOB(G.run_list_level = 1;)
6330# if ENABLE_HUSH_TRAP
6181 /* Awful hack for `trap` or $(trap). 6331 /* Awful hack for `trap` or $(trap).
6182 * 6332 *
6183 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html 6333 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
@@ -6221,6 +6371,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
6221 fflush_all(); /* important */ 6371 fflush_all(); /* important */
6222 _exit(0); 6372 _exit(0);
6223 } 6373 }
6374# endif
6224# if BB_MMU 6375# if BB_MMU
6225 reset_traps_to_defaults(); 6376 reset_traps_to_defaults();
6226 parse_and_run_string(s); 6377 parse_and_run_string(s);
@@ -6629,6 +6780,7 @@ static struct function *new_function(char *name)
6629 return funcp; 6780 return funcp;
6630} 6781}
6631 6782
6783# if ENABLE_HUSH_UNSET
6632static void unset_func(const char *name) 6784static void unset_func(const char *name)
6633{ 6785{
6634 struct function **funcpp = find_function_slot(name); 6786 struct function **funcpp = find_function_slot(name);
@@ -6644,13 +6796,14 @@ static void unset_func(const char *name)
6644 if (funcp->body) { 6796 if (funcp->body) {
6645 free_pipe_list(funcp->body); 6797 free_pipe_list(funcp->body);
6646 free(funcp->name); 6798 free(funcp->name);
6647# if !BB_MMU 6799# if !BB_MMU
6648 free(funcp->body_as_string); 6800 free(funcp->body_as_string);
6649# endif 6801# endif
6650 } 6802 }
6651 free(funcp); 6803 free(funcp);
6652 } 6804 }
6653} 6805}
6806# endif
6654 6807
6655# if BB_MMU 6808# if BB_MMU
6656#define exec_function(to_free, funcp, argv) \ 6809#define exec_function(to_free, funcp, argv) \
@@ -7043,7 +7196,7 @@ static void insert_bg_job(struct pipe *pi)
7043 job->cmdtext = xstrdup(get_cmdtext(pi)); 7196 job->cmdtext = xstrdup(get_cmdtext(pi));
7044 7197
7045 if (G_interactive_fd) 7198 if (G_interactive_fd)
7046 printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); 7199 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
7047 G.last_jobid = job->jobid; 7200 G.last_jobid = job->jobid;
7048} 7201}
7049 7202
@@ -7518,7 +7671,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7518 } 7671 }
7519 7672
7520 /* Expand the rest into (possibly) many strings each */ 7673 /* Expand the rest into (possibly) many strings each */
7521#if ENABLE_HUSH_BASH_COMPAT 7674#if BASH_TEST2
7522 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { 7675 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
7523 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 7676 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
7524 } else 7677 } else
@@ -8118,10 +8271,12 @@ static void install_sighandlers(unsigned mask)
8118 if (old_handler == SIG_IGN) { 8271 if (old_handler == SIG_IGN) {
8119 /* oops... restore back to IGN, and record this fact */ 8272 /* oops... restore back to IGN, and record this fact */
8120 install_sighandler(sig, old_handler); 8273 install_sighandler(sig, old_handler);
8121 if (!G.traps) 8274#if ENABLE_HUSH_TRAP
8122 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 8275 if (!G_traps)
8123 free(G.traps[sig]); 8276 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
8124 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ 8277 free(G_traps[sig]);
8278 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
8279#endif
8125 } 8280 }
8126 } 8281 }
8127} 8282}
@@ -8272,7 +8427,7 @@ int hush_main(int argc, char **argv)
8272 /* Export PWD */ 8427 /* Export PWD */
8273 set_pwd_var(/*exp:*/ 1); 8428 set_pwd_var(/*exp:*/ 1);
8274 8429
8275#if ENABLE_HUSH_BASH_COMPAT 8430#if BASH_HOSTNAME_VAR
8276 /* Set (but not export) HOSTNAME unless already set */ 8431 /* Set (but not export) HOSTNAME unless already set */
8277 if (!get_local_var_value("HOSTNAME")) { 8432 if (!get_local_var_value("HOSTNAME")) {
8278 struct utsname uts; 8433 struct utsname uts;
@@ -8422,10 +8577,10 @@ int hush_main(int argc, char **argv)
8422 if (empty_trap_mask != 0) { 8577 if (empty_trap_mask != 0) {
8423 int sig; 8578 int sig;
8424 install_special_sighandlers(); 8579 install_special_sighandlers();
8425 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 8580 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
8426 for (sig = 1; sig < NSIG; sig++) { 8581 for (sig = 1; sig < NSIG; sig++) {
8427 if (empty_trap_mask & (1LL << sig)) { 8582 if (empty_trap_mask & (1LL << sig)) {
8428 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ 8583 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
8429 install_sighandler(sig, SIG_IGN); 8584 install_sighandler(sig, SIG_IGN);
8430 } 8585 }
8431 } 8586 }
@@ -8669,6 +8824,7 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
8669 return 0; 8824 return 0;
8670} 8825}
8671 8826
8827#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
8672static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) 8828static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
8673{ 8829{
8674 int argc = 0; 8830 int argc = 0;
@@ -8678,24 +8834,50 @@ static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char *
8678 } 8834 }
8679 return applet_main_func(argc, argv - argc); 8835 return applet_main_func(argc, argv - argc);
8680} 8836}
8681 8837#endif
8838#if ENABLE_HUSH_TEST || BASH_TEST2
8682static int FAST_FUNC builtin_test(char **argv) 8839static int FAST_FUNC builtin_test(char **argv)
8683{ 8840{
8684 return run_applet_main(argv, test_main); 8841 return run_applet_main(argv, test_main);
8685} 8842}
8686 8843#endif
8844#if ENABLE_HUSH_ECHO
8687static int FAST_FUNC builtin_echo(char **argv) 8845static int FAST_FUNC builtin_echo(char **argv)
8688{ 8846{
8689 return run_applet_main(argv, echo_main); 8847 return run_applet_main(argv, echo_main);
8690} 8848}
8691 8849#endif
8692#if ENABLE_PRINTF 8850#if ENABLE_HUSH_PRINTF
8693static int FAST_FUNC builtin_printf(char **argv) 8851static int FAST_FUNC builtin_printf(char **argv)
8694{ 8852{
8695 return run_applet_main(argv, printf_main); 8853 return run_applet_main(argv, printf_main);
8696} 8854}
8697#endif 8855#endif
8698 8856
8857#if ENABLE_HUSH_HELP
8858static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
8859{
8860 const struct built_in_command *x;
8861
8862 printf(
8863 "Built-in commands:\n"
8864 "------------------\n");
8865 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
8866 if (x->b_descr)
8867 printf("%-10s%s\n", x->b_cmd, x->b_descr);
8868 }
8869 return EXIT_SUCCESS;
8870}
8871#endif
8872
8873#if MAX_HISTORY && ENABLE_FEATURE_EDITING
8874static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
8875{
8876 show_history(G.line_input_state);
8877 return EXIT_SUCCESS;
8878}
8879#endif
8880
8699static char **skip_dash_dash(char **argv) 8881static char **skip_dash_dash(char **argv)
8700{ 8882{
8701 argv++; 8883 argv++;
@@ -8704,24 +8886,6 @@ static char **skip_dash_dash(char **argv)
8704 return argv; 8886 return argv;
8705} 8887}
8706 8888
8707static int FAST_FUNC builtin_eval(char **argv)
8708{
8709 int rcode = EXIT_SUCCESS;
8710
8711 argv = skip_dash_dash(argv);
8712 if (*argv) {
8713 char *str = expand_strvec_to_string(argv);
8714 /* bash:
8715 * eval "echo Hi; done" ("done" is syntax error):
8716 * "echo Hi" will not execute too.
8717 */
8718 parse_and_run_string(str);
8719 free(str);
8720 rcode = G.last_exitcode;
8721 }
8722 return rcode;
8723}
8724
8725static int FAST_FUNC builtin_cd(char **argv) 8889static int FAST_FUNC builtin_cd(char **argv)
8726{ 8890{
8727 const char *newdir; 8891 const char *newdir;
@@ -8749,6 +8913,30 @@ static int FAST_FUNC builtin_cd(char **argv)
8749 return EXIT_SUCCESS; 8913 return EXIT_SUCCESS;
8750} 8914}
8751 8915
8916static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
8917{
8918 puts(get_cwd(0));
8919 return EXIT_SUCCESS;
8920}
8921
8922static int FAST_FUNC builtin_eval(char **argv)
8923{
8924 int rcode = EXIT_SUCCESS;
8925
8926 argv = skip_dash_dash(argv);
8927 if (*argv) {
8928 char *str = expand_strvec_to_string(argv);
8929 /* bash:
8930 * eval "echo Hi; done" ("done" is syntax error):
8931 * "echo Hi" will not execute too.
8932 */
8933 parse_and_run_string(str);
8934 free(str);
8935 rcode = G.last_exitcode;
8936 }
8937 return rcode;
8938}
8939
8752static int FAST_FUNC builtin_exec(char **argv) 8940static int FAST_FUNC builtin_exec(char **argv)
8753{ 8941{
8754 argv = skip_dash_dash(argv); 8942 argv = skip_dash_dash(argv);
@@ -8794,6 +8982,148 @@ static int FAST_FUNC builtin_exit(char **argv)
8794 hush_exit(xatoi(argv[0]) & 0xff); 8982 hush_exit(xatoi(argv[0]) & 0xff);
8795} 8983}
8796 8984
8985#if ENABLE_HUSH_TYPE
8986/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
8987static int FAST_FUNC builtin_type(char **argv)
8988{
8989 int ret = EXIT_SUCCESS;
8990
8991 while (*++argv) {
8992 const char *type;
8993 char *path = NULL;
8994
8995 if (0) {} /* make conditional compile easier below */
8996 /*else if (find_alias(*argv))
8997 type = "an alias";*/
8998#if ENABLE_HUSH_FUNCTIONS
8999 else if (find_function(*argv))
9000 type = "a function";
9001#endif
9002 else if (find_builtin(*argv))
9003 type = "a shell builtin";
9004 else if ((path = find_in_path(*argv)) != NULL)
9005 type = path;
9006 else {
9007 bb_error_msg("type: %s: not found", *argv);
9008 ret = EXIT_FAILURE;
9009 continue;
9010 }
9011
9012 printf("%s is %s\n", *argv, type);
9013 free(path);
9014 }
9015
9016 return ret;
9017}
9018#endif
9019
9020#if ENABLE_HUSH_READ
9021/* Interruptibility of read builtin in bash
9022 * (tested on bash-4.2.8 by sending signals (not by ^C)):
9023 *
9024 * Empty trap makes read ignore corresponding signal, for any signal.
9025 *
9026 * SIGINT:
9027 * - terminates non-interactive shell;
9028 * - interrupts read in interactive shell;
9029 * if it has non-empty trap:
9030 * - executes trap and returns to command prompt in interactive shell;
9031 * - executes trap and returns to read in non-interactive shell;
9032 * SIGTERM:
9033 * - is ignored (does not interrupt) read in interactive shell;
9034 * - terminates non-interactive shell;
9035 * if it has non-empty trap:
9036 * - executes trap and returns to read;
9037 * SIGHUP:
9038 * - terminates shell (regardless of interactivity);
9039 * if it has non-empty trap:
9040 * - executes trap and returns to read;
9041 */
9042static int FAST_FUNC builtin_read(char **argv)
9043{
9044 const char *r;
9045 char *opt_n = NULL;
9046 char *opt_p = NULL;
9047 char *opt_t = NULL;
9048 char *opt_u = NULL;
9049 const char *ifs;
9050 int read_flags;
9051
9052 /* "!": do not abort on errors.
9053 * Option string must start with "sr" to match BUILTIN_READ_xxx
9054 */
9055 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
9056 if (read_flags == (uint32_t)-1)
9057 return EXIT_FAILURE;
9058 argv += optind;
9059 ifs = get_local_var_value("IFS"); /* can be NULL */
9060
9061 again:
9062 r = shell_builtin_read(set_local_var_from_halves,
9063 argv,
9064 ifs,
9065 read_flags,
9066 opt_n,
9067 opt_p,
9068 opt_t,
9069 opt_u
9070 );
9071
9072 if ((uintptr_t)r == 1 && errno == EINTR) {
9073 unsigned sig = check_and_run_traps();
9074 if (sig && sig != SIGINT)
9075 goto again;
9076 }
9077
9078 if ((uintptr_t)r > 1) {
9079 bb_error_msg("%s", r);
9080 r = (char*)(uintptr_t)1;
9081 }
9082
9083 return (uintptr_t)r;
9084}
9085#endif
9086
9087#if ENABLE_HUSH_UMASK
9088static int FAST_FUNC builtin_umask(char **argv)
9089{
9090 int rc;
9091 mode_t mask;
9092
9093 rc = 1;
9094 mask = umask(0);
9095 argv = skip_dash_dash(argv);
9096 if (argv[0]) {
9097 mode_t old_mask = mask;
9098
9099 /* numeric umasks are taken as-is */
9100 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
9101 if (!isdigit(argv[0][0]))
9102 mask ^= 0777;
9103 mask = bb_parse_mode(argv[0], mask);
9104 if (!isdigit(argv[0][0]))
9105 mask ^= 0777;
9106 if ((unsigned)mask > 0777) {
9107 mask = old_mask;
9108 /* bash messages:
9109 * bash: umask: 'q': invalid symbolic mode operator
9110 * bash: umask: 999: octal number out of range
9111 */
9112 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
9113 rc = 0;
9114 }
9115 } else {
9116 /* Mimic bash */
9117 printf("%04o\n", (unsigned) mask);
9118 /* fall through and restore mask which we set to 0 */
9119 }
9120 umask(mask);
9121
9122 return !rc; /* rc != 0 - success */
9123}
9124#endif
9125
9126#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
8797static void print_escaped(const char *s) 9127static void print_escaped(const char *s)
8798{ 9128{
8799 if (*s == '\'') 9129 if (*s == '\'')
@@ -8812,11 +9142,13 @@ static void print_escaped(const char *s)
8812 putchar('"'); 9142 putchar('"');
8813 } while (*s); 9143 } while (*s);
8814} 9144}
9145#endif
8815 9146
8816#if !ENABLE_HUSH_LOCAL 9147#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL
9148# if !ENABLE_HUSH_LOCAL
8817#define helper_export_local(argv, exp, lvl) \ 9149#define helper_export_local(argv, exp, lvl) \
8818 helper_export_local(argv, exp) 9150 helper_export_local(argv, exp)
8819#endif 9151# endif
8820static void helper_export_local(char **argv, int exp, int lvl) 9152static void helper_export_local(char **argv, int exp, int lvl)
8821{ 9153{
8822 do { 9154 do {
@@ -8849,14 +9181,14 @@ static void helper_export_local(char **argv, int exp, int lvl)
8849 continue; 9181 continue;
8850 } 9182 }
8851 } 9183 }
8852#if ENABLE_HUSH_LOCAL 9184# if ENABLE_HUSH_LOCAL
8853 if (exp == 0 /* local? */ 9185 if (exp == 0 /* local? */
8854 && var && var->func_nest_level == lvl 9186 && var && var->func_nest_level == lvl
8855 ) { 9187 ) {
8856 /* "local x=abc; ...; local x" - ignore second local decl */ 9188 /* "local x=abc; ...; local x" - ignore second local decl */
8857 continue; 9189 continue;
8858 } 9190 }
8859#endif 9191# endif
8860 /* Exporting non-existing variable. 9192 /* Exporting non-existing variable.
8861 * bash does not put it in environment, 9193 * bash does not put it in environment,
8862 * but remembers that it is exported, 9194 * but remembers that it is exported,
@@ -8872,7 +9204,9 @@ static void helper_export_local(char **argv, int exp, int lvl)
8872 set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); 9204 set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0);
8873 } while (*++argv); 9205 } while (*++argv);
8874} 9206}
9207#endif
8875 9208
9209#if ENABLE_HUSH_EXPORT
8876static int FAST_FUNC builtin_export(char **argv) 9210static int FAST_FUNC builtin_export(char **argv)
8877{ 9211{
8878 unsigned opt_unexport; 9212 unsigned opt_unexport;
@@ -8918,6 +9252,7 @@ static int FAST_FUNC builtin_export(char **argv)
8918 9252
8919 return EXIT_SUCCESS; 9253 return EXIT_SUCCESS;
8920} 9254}
9255#endif
8921 9256
8922#if ENABLE_HUSH_LOCAL 9257#if ENABLE_HUSH_LOCAL
8923static int FAST_FUNC builtin_local(char **argv) 9258static int FAST_FUNC builtin_local(char **argv)
@@ -8931,6 +9266,7 @@ static int FAST_FUNC builtin_local(char **argv)
8931} 9266}
8932#endif 9267#endif
8933 9268
9269#if ENABLE_HUSH_UNSET
8934/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ 9270/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
8935static int FAST_FUNC builtin_unset(char **argv) 9271static int FAST_FUNC builtin_unset(char **argv)
8936{ 9272{
@@ -8958,16 +9294,18 @@ static int FAST_FUNC builtin_unset(char **argv)
8958 ret = EXIT_FAILURE; 9294 ret = EXIT_FAILURE;
8959 } 9295 }
8960 } 9296 }
8961#if ENABLE_HUSH_FUNCTIONS 9297# if ENABLE_HUSH_FUNCTIONS
8962 else { 9298 else {
8963 unset_func(*argv); 9299 unset_func(*argv);
8964 } 9300 }
8965#endif 9301# endif
8966 argv++; 9302 argv++;
8967 } 9303 }
8968 return ret; 9304 return ret;
8969} 9305}
9306#endif
8970 9307
9308#if ENABLE_HUSH_SET
8971/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set 9309/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
8972 * built-in 'set' handler 9310 * built-in 'set' handler
8973 * SUSv3 says: 9311 * SUSv3 says:
@@ -9050,6 +9388,7 @@ static int FAST_FUNC builtin_set(char **argv)
9050 bb_error_msg("set: %s: invalid option", arg); 9388 bb_error_msg("set: %s: invalid option", arg);
9051 return EXIT_FAILURE; 9389 return EXIT_FAILURE;
9052} 9390}
9391#endif
9053 9392
9054static int FAST_FUNC builtin_shift(char **argv) 9393static int FAST_FUNC builtin_shift(char **argv)
9055{ 9394{
@@ -9059,7 +9398,7 @@ static int FAST_FUNC builtin_shift(char **argv)
9059 n = atoi(argv[0]); 9398 n = atoi(argv[0]);
9060 } 9399 }
9061 if (n >= 0 && n < G.global_argc) { 9400 if (n >= 0 && n < G.global_argc) {
9062 if (G.global_args_malloced) { 9401 if (G_global_args_malloced) {
9063 int m = 1; 9402 int m = 1;
9064 while (m <= n) 9403 while (m <= n)
9065 free(G.global_argv[m++]); 9404 free(G.global_argv[m++]);
@@ -9072,87 +9411,78 @@ static int FAST_FUNC builtin_shift(char **argv)
9072 return EXIT_FAILURE; 9411 return EXIT_FAILURE;
9073} 9412}
9074 9413
9075/* Interruptibility of read builtin in bash 9414static int FAST_FUNC builtin_source(char **argv)
9076 * (tested on bash-4.2.8 by sending signals (not by ^C)):
9077 *
9078 * Empty trap makes read ignore corresponding signal, for any signal.
9079 *
9080 * SIGINT:
9081 * - terminates non-interactive shell;
9082 * - interrupts read in interactive shell;
9083 * if it has non-empty trap:
9084 * - executes trap and returns to command prompt in interactive shell;
9085 * - executes trap and returns to read in non-interactive shell;
9086 * SIGTERM:
9087 * - is ignored (does not interrupt) read in interactive shell;
9088 * - terminates non-interactive shell;
9089 * if it has non-empty trap:
9090 * - executes trap and returns to read;
9091 * SIGHUP:
9092 * - terminates shell (regardless of interactivity);
9093 * if it has non-empty trap:
9094 * - executes trap and returns to read;
9095 */
9096static int FAST_FUNC builtin_read(char **argv)
9097{ 9415{
9098 const char *r; 9416 char *arg_path, *filename;
9099 char *opt_n = NULL; 9417 FILE *input;
9100 char *opt_p = NULL; 9418 save_arg_t sv;
9101 char *opt_t = NULL; 9419 char *args_need_save;
9102 char *opt_u = NULL; 9420#if ENABLE_HUSH_FUNCTIONS
9103 const char *ifs; 9421 smallint sv_flg;
9104 int read_flags; 9422#endif
9105 9423
9106 /* "!": do not abort on errors. 9424 argv = skip_dash_dash(argv);
9107 * Option string must start with "sr" to match BUILTIN_READ_xxx 9425 filename = argv[0];
9108 */ 9426 if (!filename) {
9109 read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); 9427 /* bash says: "bash: .: filename argument required" */
9110 if (read_flags == (uint32_t)-1) 9428 return 2; /* bash compat */
9429 }
9430 arg_path = NULL;
9431 if (!strchr(filename, '/')) {
9432 arg_path = find_in_path(filename);
9433 if (arg_path)
9434 filename = arg_path;
9435 }
9436 input = remember_FILE(fopen_or_warn(filename, "r"));
9437 free(arg_path);
9438 if (!input) {
9439 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
9440 /* POSIX: non-interactive shell should abort here,
9441 * not merely fail. So far no one complained :)
9442 */
9111 return EXIT_FAILURE; 9443 return EXIT_FAILURE;
9112 argv += optind; 9444 }
9113 ifs = get_local_var_value("IFS"); /* can be NULL */
9114 9445
9115 again: 9446#if ENABLE_HUSH_FUNCTIONS
9116 r = shell_builtin_read(set_local_var_from_halves, 9447 sv_flg = G_flag_return_in_progress;
9117 argv, 9448 /* "we are inside sourced file, ok to use return" */
9118 ifs, 9449 G_flag_return_in_progress = -1;
9119 read_flags, 9450#endif
9120 opt_n, 9451 args_need_save = argv[1]; /* used as a boolean variable */
9121 opt_p, 9452 if (args_need_save)
9122 opt_t, 9453 save_and_replace_G_args(&sv, argv);
9123 opt_u
9124 );
9125 9454
9126 if ((uintptr_t)r == 1 && errno == EINTR) { 9455 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
9127 unsigned sig = check_and_run_traps(); 9456 G.last_exitcode = 0;
9128 if (sig && sig != SIGINT) 9457 parse_and_run_file(input);
9129 goto again; 9458 fclose_and_forget(input);
9130 }
9131 9459
9132 if ((uintptr_t)r > 1) { 9460 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
9133 bb_error_msg("%s", r); 9461 restore_G_args(&sv, argv);
9134 r = (char*)(uintptr_t)1; 9462#if ENABLE_HUSH_FUNCTIONS
9135 } 9463 G_flag_return_in_progress = sv_flg;
9464#endif
9136 9465
9137 return (uintptr_t)r; 9466 return G.last_exitcode;
9138} 9467}
9139 9468
9469#if ENABLE_HUSH_TRAP
9140static int FAST_FUNC builtin_trap(char **argv) 9470static int FAST_FUNC builtin_trap(char **argv)
9141{ 9471{
9142 int sig; 9472 int sig;
9143 char *new_cmd; 9473 char *new_cmd;
9144 9474
9145 if (!G.traps) 9475 if (!G_traps)
9146 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 9476 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
9147 9477
9148 argv++; 9478 argv++;
9149 if (!*argv) { 9479 if (!*argv) {
9150 int i; 9480 int i;
9151 /* No args: print all trapped */ 9481 /* No args: print all trapped */
9152 for (i = 0; i < NSIG; ++i) { 9482 for (i = 0; i < NSIG; ++i) {
9153 if (G.traps[i]) { 9483 if (G_traps[i]) {
9154 printf("trap -- "); 9484 printf("trap -- ");
9155 print_escaped(G.traps[i]); 9485 print_escaped(G_traps[i]);
9156 /* note: bash adds "SIG", but only if invoked 9486 /* note: bash adds "SIG", but only if invoked
9157 * as "bash". If called as "sh", or if set -o posix, 9487 * as "bash". If called as "sh", or if set -o posix,
9158 * then it prints short signal names. 9488 * then it prints short signal names.
@@ -9182,11 +9512,11 @@ static int FAST_FUNC builtin_trap(char **argv)
9182 continue; 9512 continue;
9183 } 9513 }
9184 9514
9185 free(G.traps[sig]); 9515 free(G_traps[sig]);
9186 G.traps[sig] = xstrdup(new_cmd); 9516 G_traps[sig] = xstrdup(new_cmd);
9187 9517
9188 debug_printf("trap: setting SIG%s (%i) to '%s'\n", 9518 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
9189 get_signame(sig), sig, G.traps[sig]); 9519 get_signame(sig), sig, G_traps[sig]);
9190 9520
9191 /* There is no signal for 0 (EXIT) */ 9521 /* There is no signal for 0 (EXIT) */
9192 if (sig == 0) 9522 if (sig == 0)
@@ -9226,59 +9556,54 @@ static int FAST_FUNC builtin_trap(char **argv)
9226 argv++; 9556 argv++;
9227 goto process_sig_list; 9557 goto process_sig_list;
9228} 9558}
9229
9230/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
9231static int FAST_FUNC builtin_type(char **argv)
9232{
9233 int ret = EXIT_SUCCESS;
9234
9235 while (*++argv) {
9236 const char *type;
9237 char *path = NULL;
9238
9239 if (0) {} /* make conditional compile easier below */
9240 /*else if (find_alias(*argv))
9241 type = "an alias";*/
9242#if ENABLE_HUSH_FUNCTIONS
9243 else if (find_function(*argv))
9244 type = "a function";
9245#endif 9559#endif
9246 else if (find_builtin(*argv))
9247 type = "a shell builtin";
9248 else if ((path = find_in_path(*argv)) != NULL)
9249 type = path;
9250 else {
9251 bb_error_msg("type: %s: not found", *argv);
9252 ret = EXIT_FAILURE;
9253 continue;
9254 }
9255
9256 printf("%s is %s\n", *argv, type);
9257 free(path);
9258 }
9259
9260 return ret;
9261}
9262 9560
9263#if ENABLE_HUSH_JOB 9561#if ENABLE_HUSH_JOB
9264static struct pipe *parse_jobspec(const char *str) 9562static struct pipe *parse_jobspec(const char *str)
9265{ 9563{
9266 struct pipe *pi; 9564 struct pipe *pi;
9267 int jobnum; 9565 unsigned jobnum;
9268 9566
9269 if (sscanf(str, "%%%d", &jobnum) != 1) { 9567 if (sscanf(str, "%%%u", &jobnum) != 1) {
9270 bb_error_msg("bad argument '%s'", str); 9568 if (str[0] != '%'
9271 return NULL; 9569 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
9570 ) {
9571 bb_error_msg("bad argument '%s'", str);
9572 return NULL;
9573 }
9574 /* It is "%%", "%+" or "%" - current job */
9575 jobnum = G.last_jobid;
9576 if (jobnum == 0) {
9577 bb_error_msg("no current job");
9578 return NULL;
9579 }
9272 } 9580 }
9273 for (pi = G.job_list; pi; pi = pi->next) { 9581 for (pi = G.job_list; pi; pi = pi->next) {
9274 if (pi->jobid == jobnum) { 9582 if (pi->jobid == jobnum) {
9275 return pi; 9583 return pi;
9276 } 9584 }
9277 } 9585 }
9278 bb_error_msg("%d: no such job", jobnum); 9586 bb_error_msg("%u: no such job", jobnum);
9279 return NULL; 9587 return NULL;
9280} 9588}
9281 9589
9590static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
9591{
9592 struct pipe *job;
9593 const char *status_string;
9594
9595 checkjobs(NULL, 0 /*(no pid to wait for)*/);
9596 for (job = G.job_list; job; job = job->next) {
9597 if (job->alive_cmds == job->stopped_cmds)
9598 status_string = "Stopped";
9599 else
9600 status_string = "Running";
9601
9602 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
9603 }
9604 return EXIT_SUCCESS;
9605}
9606
9282/* built-in 'fg' and 'bg' handler */ 9607/* built-in 'fg' and 'bg' handler */
9283static int FAST_FUNC builtin_fg_bg(char **argv) 9608static int FAST_FUNC builtin_fg_bg(char **argv)
9284{ 9609{
@@ -9334,188 +9659,81 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
9334} 9659}
9335#endif 9660#endif
9336 9661
9337#if ENABLE_HUSH_HELP 9662#if ENABLE_HUSH_KILL
9338static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) 9663static int FAST_FUNC builtin_kill(char **argv)
9339{
9340 const struct built_in_command *x;
9341
9342 printf(
9343 "Built-in commands:\n"
9344 "------------------\n");
9345 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
9346 if (x->b_descr)
9347 printf("%-10s%s\n", x->b_cmd, x->b_descr);
9348 }
9349 return EXIT_SUCCESS;
9350}
9351#endif
9352
9353#if MAX_HISTORY && ENABLE_FEATURE_EDITING
9354static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
9355{ 9664{
9356 show_history(G.line_input_state); 9665 int ret = 0;
9357 return EXIT_SUCCESS;
9358}
9359#endif
9360 9666
9361#if ENABLE_HUSH_JOB 9667# if ENABLE_HUSH_JOB
9362static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) 9668 if (argv[1] && strcmp(argv[1], "-l") != 0) {
9363{ 9669 int i = 1;
9364 struct pipe *job;
9365 const char *status_string;
9366 9670
9367 checkjobs(NULL, 0 /*(no pid to wait for)*/); 9671 do {
9368 for (job = G.job_list; job; job = job->next) { 9672 struct pipe *pi;
9369 if (job->alive_cmds == job->stopped_cmds) 9673 char *dst;
9370 status_string = "Stopped"; 9674 int j, n;
9371 else
9372 status_string = "Running";
9373 9675
9374 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); 9676 if (argv[i][0] != '%')
9677 continue;
9678 /*
9679 * "kill %N" - job kill
9680 * Converting to pgrp / pid kill
9681 */
9682 pi = parse_jobspec(argv[i]);
9683 if (!pi) {
9684 /* Eat bad jobspec */
9685 j = i;
9686 do {
9687 j++;
9688 argv[j - 1] = argv[j];
9689 } while (argv[j]);
9690 ret = 1;
9691 i--;
9692 continue;
9693 }
9694 /*
9695 * In jobs started under job control, we signal
9696 * entire process group by kill -PGRP_ID.
9697 * This happens, f.e., in interactive shell.
9698 *
9699 * Otherwise, we signal each child via
9700 * kill PID1 PID2 PID3.
9701 * Testcases:
9702 * sh -c 'sleep 1|sleep 1 & kill %1'
9703 * sh -c 'true|sleep 2 & sleep 1; kill %1'
9704 * sh -c 'true|sleep 1 & sleep 2; kill %1'
9705 */
9706 n = G_interactive_fd ? 1 : pi->num_cmds;
9707 dst = alloca(n * sizeof(int)*4);
9708 argv[i] = dst;
9709 if (G_interactive_fd)
9710 dst += sprintf(dst, " -%u", (int)pi->pgrp);
9711 else for (j = 0; j < n; j++) {
9712 struct command *cmd = &pi->cmds[j];
9713 /* Skip exited members of the job */
9714 if (cmd->pid == 0)
9715 continue;
9716 /*
9717 * kill_main has matching code to expect
9718 * leading space. Needed to not confuse
9719 * negative pids with "kill -SIGNAL_NO" syntax
9720 */
9721 dst += sprintf(dst, " %u", (int)cmd->pid);
9722 }
9723 *dst = '\0';
9724 } while (argv[++i]);
9375 } 9725 }
9376 return EXIT_SUCCESS;
9377}
9378#endif
9379
9380#if HUSH_DEBUG
9381static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
9382{
9383 void *p;
9384 unsigned long l;
9385
9386# ifdef M_TRIM_THRESHOLD
9387 /* Optional. Reduces probability of false positives */
9388 malloc_trim(0);
9389# endif 9726# endif
9390 /* Crude attempt to find where "free memory" starts,
9391 * sans fragmentation. */
9392 p = malloc(240);
9393 l = (unsigned long)p;
9394 free(p);
9395 p = malloc(3400);
9396 if (l < (unsigned long)p) l = (unsigned long)p;
9397 free(p);
9398 9727
9399 9728 if (argv[1] || ret == 0) {
9400# if 0 /* debug */ 9729 ret = run_applet_main(argv, kill_main);
9401 {
9402 struct mallinfo mi = mallinfo();
9403 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
9404 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
9405 } 9730 }
9406# endif 9731 /* else: ret = 1, "kill %bad_jobspec" case */
9407 9732 return ret;
9408 if (!G.memleak_value)
9409 G.memleak_value = l;
9410
9411 l -= G.memleak_value;
9412 if ((long)l < 0)
9413 l = 0;
9414 l /= 1024;
9415 if (l > 127)
9416 l = 127;
9417
9418 /* Exitcode is "how many kilobytes we leaked since 1st call" */
9419 return l;
9420}
9421#endif
9422
9423static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
9424{
9425 puts(get_cwd(0));
9426 return EXIT_SUCCESS;
9427} 9733}
9428
9429static int FAST_FUNC builtin_source(char **argv)
9430{
9431 char *arg_path, *filename;
9432 FILE *input;
9433 save_arg_t sv;
9434#if ENABLE_HUSH_FUNCTIONS
9435 smallint sv_flg;
9436#endif
9437
9438 argv = skip_dash_dash(argv);
9439 filename = argv[0];
9440 if (!filename) {
9441 /* bash says: "bash: .: filename argument required" */
9442 return 2; /* bash compat */
9443 }
9444 arg_path = NULL;
9445 if (!strchr(filename, '/')) {
9446 arg_path = find_in_path(filename);
9447 if (arg_path)
9448 filename = arg_path;
9449 }
9450 input = remember_FILE(fopen_or_warn(filename, "r"));
9451 free(arg_path);
9452 if (!input) {
9453 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
9454 /* POSIX: non-interactive shell should abort here,
9455 * not merely fail. So far no one complained :)
9456 */
9457 return EXIT_FAILURE;
9458 }
9459
9460#if ENABLE_HUSH_FUNCTIONS
9461 sv_flg = G_flag_return_in_progress;
9462 /* "we are inside sourced file, ok to use return" */
9463 G_flag_return_in_progress = -1;
9464#endif 9734#endif
9465 if (argv[1])
9466 save_and_replace_G_args(&sv, argv);
9467
9468 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
9469 G.last_exitcode = 0;
9470 parse_and_run_file(input);
9471 fclose_and_forget(input);
9472
9473 if (argv[1])
9474 restore_G_args(&sv, argv);
9475#if ENABLE_HUSH_FUNCTIONS
9476 G_flag_return_in_progress = sv_flg;
9477#endif
9478
9479 return G.last_exitcode;
9480}
9481
9482static int FAST_FUNC builtin_umask(char **argv)
9483{
9484 int rc;
9485 mode_t mask;
9486
9487 rc = 1;
9488 mask = umask(0);
9489 argv = skip_dash_dash(argv);
9490 if (argv[0]) {
9491 mode_t old_mask = mask;
9492
9493 /* numeric umasks are taken as-is */
9494 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
9495 if (!isdigit(argv[0][0]))
9496 mask ^= 0777;
9497 mask = bb_parse_mode(argv[0], mask);
9498 if (!isdigit(argv[0][0]))
9499 mask ^= 0777;
9500 if ((unsigned)mask > 0777) {
9501 mask = old_mask;
9502 /* bash messages:
9503 * bash: umask: 'q': invalid symbolic mode operator
9504 * bash: umask: 999: octal number out of range
9505 */
9506 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
9507 rc = 0;
9508 }
9509 } else {
9510 /* Mimic bash */
9511 printf("%04o\n", (unsigned) mask);
9512 /* fall through and restore mask which we set to 0 */
9513 }
9514 umask(mask);
9515
9516 return !rc; /* rc != 0 - success */
9517}
9518 9735
9736#if ENABLE_HUSH_WAIT
9519/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 9737/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
9520#if !ENABLE_HUSH_JOB 9738#if !ENABLE_HUSH_JOB
9521# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) 9739# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
@@ -9622,13 +9840,15 @@ static int FAST_FUNC builtin_wait(char **argv)
9622#if ENABLE_HUSH_JOB 9840#if ENABLE_HUSH_JOB
9623 if (argv[0][0] == '%') { 9841 if (argv[0][0] == '%') {
9624 struct pipe *wait_pipe; 9842 struct pipe *wait_pipe;
9843 ret = 127; /* bash compat for bad jobspecs */
9625 wait_pipe = parse_jobspec(*argv); 9844 wait_pipe = parse_jobspec(*argv);
9626 if (wait_pipe) { 9845 if (wait_pipe) {
9627 ret = job_exited_or_stopped(wait_pipe); 9846 ret = job_exited_or_stopped(wait_pipe);
9628 if (ret < 0) 9847 if (ret < 0)
9629 ret = wait_for_child_or_signal(wait_pipe, 0); 9848 ret = wait_for_child_or_signal(wait_pipe, 0);
9630 continue;
9631 } 9849 }
9850 /* else: parse_jobspec() already emitted error msg */
9851 continue;
9632 } 9852 }
9633#endif 9853#endif
9634 /* mimic bash message */ 9854 /* mimic bash message */
@@ -9674,6 +9894,7 @@ static int FAST_FUNC builtin_wait(char **argv)
9674 9894
9675 return ret; 9895 return ret;
9676} 9896}
9897#endif
9677 9898
9678#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS 9899#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
9679static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min) 9900static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
@@ -9740,3 +9961,46 @@ static int FAST_FUNC builtin_return(char **argv)
9740 return rc; 9961 return rc;
9741} 9962}
9742#endif 9963#endif
9964
9965#if ENABLE_HUSH_MEMLEAK
9966static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
9967{
9968 void *p;
9969 unsigned long l;
9970
9971# ifdef M_TRIM_THRESHOLD
9972 /* Optional. Reduces probability of false positives */
9973 malloc_trim(0);
9974# endif
9975 /* Crude attempt to find where "free memory" starts,
9976 * sans fragmentation. */
9977 p = malloc(240);
9978 l = (unsigned long)p;
9979 free(p);
9980 p = malloc(3400);
9981 if (l < (unsigned long)p) l = (unsigned long)p;
9982 free(p);
9983
9984
9985# if 0 /* debug */
9986 {
9987 struct mallinfo mi = mallinfo();
9988 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
9989 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
9990 }
9991# endif
9992
9993 if (!G.memleak_value)
9994 G.memleak_value = l;
9995
9996 l -= G.memleak_value;
9997 if ((long)l < 0)
9998 l = 0;
9999 l /= 1024;
10000 if (l > 127)
10001 l = 127;
10002
10003 /* Exitcode is "how many kilobytes we leaked since 1st call" */
10004 return l;
10005}
10006#endif