//config: bool "bash-compatible extensions"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable bash-compatible extensions.
//config:
//config:config HUSH_BRACE_EXPANSION
//config: bool "Brace expansion"
//config: bool "Save command history to .hush_history"
//config: default y
//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
-//config: help
-//config: Enable history saving in hush.
//config:
//config:config HUSH_JOB
//config: bool "Job control"
//config: but no separate process group is formed.
//config:
//config:config HUSH_TICK
-//config: bool "Process substitution"
+//config: bool "Support process substitution"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable process substitution `command` and $(command) in hush.
+//config: Enable `command` and $(command).
//config:
//config:config HUSH_IF
//config: bool "Support if/then/elif/else/fi"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable if/then/elif/else/fi in hush.
//config:
//config:config HUSH_LOOPS
//config: bool "Support for, while and until loops"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable for, while and until loops in hush.
//config:
//config:config HUSH_CASE
//config: bool "Support case ... esac statement"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable case ... esac statement in hush. +400 bytes.
+//config: Enable case ... esac statement. +400 bytes.
//config:
//config:config HUSH_FUNCTIONS
//config: bool "Support funcname() { commands; } syntax"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable support for shell functions in hush. +800 bytes.
+//config: Enable support for shell functions. +800 bytes.
//config:
//config:config HUSH_LOCAL
//config: bool "local builtin"
//config: This instructs hush to print commands before execution.
//config: Adds ~300 bytes.
//config:
-//config:config HUSH_EXPORT
-//config: bool "export builtin"
+//config:config HUSH_ECHO
+//config: bool "echo builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable export builtin in hush.
//config:
-//config:config HUSH_EXPORT_N
-//config: bool "Support 'export -n' option"
+//config:config HUSH_PRINTF
+//config: bool "printf builtin"
//config: default y
-//config: depends on HUSH_EXPORT
-//config: help
-//config: export -n unexports variables. It is a bash extension.
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config:
//config:config HUSH_HELP
//config: bool "help builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable help builtin in hush. Code size + ~1 kbyte.
//config:
-//config:config HUSH_PRINTF
-//config: bool "printf builtin"
+//config:config HUSH_EXPORT
+//config: bool "export builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
+//config:
+//config:config HUSH_EXPORT_N
+//config: bool "Support 'export -n' option"
+//config: default y
+//config: depends on HUSH_EXPORT
//config: help
-//config: Enable printf builtin in hush.
+//config: export -n unexports variables. It is a bash extension.
//config:
//config:config HUSH_KILL
-//config: bool "kill builtin (for kill %jobspec)"
+//config: bool "kill builtin (supports kill %jobspec)"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable kill builtin in hush.
//config:
//config:config HUSH_WAIT
//config: bool "wait builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable wait builtin in hush.
//config:
//config:config HUSH_TRAP
//config: bool "trap builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable trap builtin in hush.
-//config:
-//config:config HUSH_ULIMIT
-//config: bool "ulimit builtin"
-//config: default y
-//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable ulimit builtin in hush.
//config:
//config:config HUSH_TYPE
//config: bool "type builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable type builtin in hush.
//config:
//config:config HUSH_READ
//config: bool "read builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable read builtin in hush.
//config:
//config:config HUSH_SET
//config: bool "set builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable set builtin in hush.
//config:
//config:config HUSH_UNSET
//config: bool "unset builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable unset builtin in hush.
+//config:
+//config:config HUSH_ULIMIT
+//config: bool "ulimit builtin"
+//config: default y
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config:
//config:config HUSH_UMASK
//config: bool "umask builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
-//config: help
-//config: Enable umask builtin in hush.
+//config:
+//config:config HUSH_MEMLEAK
+//config: bool "memleak builtin (debugging)"
+//config: default n
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config:
//config:config MSH
//config: bool "msh (deprecated: aliased to hush)"
smallint exiting; /* used to prevent EXIT trap recursion */
/* These four support $?, $#, and $1 */
smalluint last_exitcode;
+#if ENABLE_HUSH_SET
/* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
smalluint global_args_malloced;
+# define G_global_args_malloced (G.global_args_malloced)
+#else
+# define G_global_args_malloced 0
+#endif
/* how many non-NULL argv's we have. NB: $# + 1 */
int global_argc;
char **global_argv;
unsigned special_sig_mask;
#if ENABLE_HUSH_JOB
unsigned fatal_sig_mask;
-# define G_fatal_sig_mask G.fatal_sig_mask
+# define G_fatal_sig_mask (G.fatal_sig_mask)
#else
# define G_fatal_sig_mask 0
#endif
# define G_traps ((char**)NULL)
#endif
sigset_t pending_set;
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
unsigned long memleak_value;
+#endif
+#if HUSH_DEBUG
int debug_indent;
#endif
struct sigaction sa;
/* Function prototypes for builtins */
static int builtin_cd(char **argv) FAST_FUNC;
+#if ENABLE_HUSH_ECHO
static int builtin_echo(char **argv) FAST_FUNC;
+#endif
static int builtin_eval(char **argv) FAST_FUNC;
static int builtin_exec(char **argv) FAST_FUNC;
static int builtin_exit(char **argv) FAST_FUNC;
#if ENABLE_HUSH_LOCAL
static int builtin_local(char **argv) FAST_FUNC;
#endif
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
static int builtin_memleak(char **argv) FAST_FUNC;
#endif
#if ENABLE_HUSH_PRINTF
#if ENABLE_HUSH_LOCAL
BLTIN("local" , builtin_local , "Set local variables"),
#endif
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
BLTIN("memleak" , builtin_memleak , NULL),
#endif
#if ENABLE_HUSH_READ
BLTIN("wait" , builtin_wait , "Wait for process"),
#endif
};
-/* For now, echo and test are unconditionally enabled.
- * Maybe make it configurable? */
static const struct built_in_command bltins2[] = {
BLTIN("[" , builtin_test , NULL),
+#if ENABLE_HUSH_ECHO
BLTIN("echo" , builtin_echo , NULL),
+#endif
#if ENABLE_HUSH_PRINTF
BLTIN("printf" , builtin_printf , NULL),
#endif
char *sv_argv0;
char **sv_g_argv;
int sv_g_argc;
- smallint sv_g_malloced;
+ IF_HUSH_SET(smallint sv_g_malloced;)
} save_arg_t;
static void save_and_replace_G_args(save_arg_t *sv, char **argv)
sv->sv_argv0 = argv[0];
sv->sv_g_argv = G.global_argv;
sv->sv_g_argc = G.global_argc;
- sv->sv_g_malloced = G.global_args_malloced;
+ IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
argv[0] = G.global_argv[0]; /* retain $0 */
G.global_argv = argv;
- G.global_args_malloced = 0;
+ IF_HUSH_SET(G.global_args_malloced = 0;)
n = 1;
while (*++argv)
static void restore_G_args(save_arg_t *sv, char **argv)
{
- char **pp;
-
+#if ENABLE_HUSH_SET
if (G.global_args_malloced) {
/* someone ran "set -- arg1 arg2 ...", undo */
- pp = G.global_argv;
+ char **pp = G.global_argv;
while (*++pp) /* note: does not free $0 */
free(*pp);
free(G.global_argv);
}
+#endif
argv[0] = sv->sv_argv0;
G.global_argv = sv->sv_g_argv;
G.global_argc = sv->sv_g_argc;
- G.global_args_malloced = sv->sv_g_malloced;
+ IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
}
free(strings);
}
+#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_BASH_COMPAT || ENABLE_HUSH_READ
static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
{
char *var = xasprintf("%s=%s", name, val);
set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
}
+#endif
/*
{
return run_applet_main(argv, test_main);
}
-
+#if ENABLE_HUSH_ECHO
static int FAST_FUNC builtin_echo(char **argv)
{
return run_applet_main(argv, echo_main);
}
-
+#endif
#if ENABLE_HUSH_PRINTF
static int FAST_FUNC builtin_printf(char **argv)
{
hush_exit(xatoi(argv[0]) & 0xff);
}
+#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
static void print_escaped(const char *s)
{
if (*s == '\'')
putchar('"');
} while (*s);
}
+#endif
#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL
# if !ENABLE_HUSH_LOCAL
n = atoi(argv[0]);
}
if (n >= 0 && n < G.global_argc) {
- if (G.global_args_malloced) {
+ if (G_global_args_malloced) {
int m = 1;
while (m <= n)
free(G.global_argv[m++]);
return pi;
}
}
- bb_error_msg("%d: no such job", jobnum);
+ bb_error_msg("%u: no such job", jobnum);
return NULL;
}
}
#endif
-#if HUSH_DEBUG
+#if ENABLE_HUSH_MEMLEAK
static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
{
void *p;
char *arg_path, *filename;
FILE *input;
save_arg_t sv;
+ char *args_need_save;
#if ENABLE_HUSH_FUNCTIONS
smallint sv_flg;
#endif
/* "we are inside sourced file, ok to use return" */
G_flag_return_in_progress = -1;
#endif
- if (argv[1])
+ args_need_save = argv[1]; /* used as a boolean variable */
+ if (args_need_save)
save_and_replace_G_args(&sv, argv);
/* "false; . ./empty_line; echo Zero:$?" should print 0 */
parse_and_run_file(input);
fclose_and_forget(input);
- if (argv[1])
+ if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
restore_G_args(&sv, argv);
#if ENABLE_HUSH_FUNCTIONS
G_flag_return_in_progress = sv_flg;
{
int ret = 0;
- argv = skip_dash_dash(argv);
- if (argv[0] && strcmp(argv[0], "-l") != 0) {
- int i = 0;
+# if ENABLE_HUSH_JOB
+ if (argv[1] && strcmp(argv[1], "-l") != 0) {
+ int i = 1;
do {
struct pipe *pi;
* sh -c 'true|sleep 2 & sleep 1; kill %1'
* sh -c 'true|sleep 1 & sleep 2; kill %1'
*/
- n = pi->num_cmds;
- if (ENABLE_HUSH_JOB && G_interactive_fd)
- n = 1;
+ n = G_interactive_fd ? 1 : pi->num_cmds;
dst = alloca(n * sizeof(int)*4);
argv[i] = dst;
-#if ENABLE_HUSH_JOB
if (G_interactive_fd)
dst += sprintf(dst, " -%u", (int)pi->pgrp);
- else
-#endif
- for (j = 0; j < n; j++) {
+ else for (j = 0; j < n; j++) {
struct command *cmd = &pi->cmds[j];
/* Skip exited members of the job */
if (cmd->pid == 0)
*dst = '\0';
} while (argv[++i]);
}
+# endif
- if (argv[0] || ret == 0) {
- argv--;
- argv[0] = (char*)"kill"; /* why? think about "kill -- PID" */
- /* kill_main also handles "killall" etc, so it does look at argv[0]! */
+ if (argv[1] || ret == 0) {
ret = run_applet_main(argv, kill_main);
}
+ /* else: ret = 1, "kill %bad_jobspec" case */
return ret;
}
#endif