* aaa
*/
//config:config HUSH
-//config: bool "hush"
+//config: bool "hush (64 kb)"
//config: default y
//config: help
-//config: hush is a small shell (25k). It handles the normal flow control
-//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
-//config: case/esac. Redirections, here documents, $((arithmetic))
-//config: and functions are supported.
+//config: hush is a small shell. It handles the normal flow control
+//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
+//config: case/esac. Redirections, here documents, $((arithmetic))
+//config: and functions are supported.
//config:
-//config: It will compile and work on no-mmu systems.
+//config: It will compile and work on no-mmu systems.
//config:
-//config: It does not handle select, aliases, tilde expansion,
-//config: &>file and >&file redirection of stdout+stderr.
+//config: It does not handle select, aliases, tilde expansion,
+//config: &>file and >&file redirection of stdout+stderr.
//config:
//config:config HUSH_BASH_COMPAT
//config: bool "bash-compatible extensions"
//config: default y
//config: depends on HUSH_BASH_COMPAT
//config: help
-//config: Enable {abc,def} extension.
+//config: Enable {abc,def} extension.
//config:
//config:config HUSH_INTERACTIVE
//config: bool "Interactive mode"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable interactive mode (prompt and command editing).
-//config: Without this, hush simply reads and executes commands
-//config: from stdin just like a shell script from a file.
-//config: No prompt, no PS1/PS2 magic shell variables.
+//config: Enable interactive mode (prompt and command editing).
+//config: Without this, hush simply reads and executes commands
+//config: from stdin just like a shell script from a file.
+//config: No prompt, no PS1/PS2 magic shell variables.
//config:
//config:config HUSH_SAVEHISTORY
//config: bool "Save command history to .hush_history"
//config: default y
//config: depends on HUSH_INTERACTIVE
//config: help
-//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
-//config: command (not entire shell), fg/bg builtins work. Without this option,
-//config: "cmd &" still works by simply spawning a process and immediately
-//config: prompting for next command (or executing next command in a script),
-//config: but no separate process group is formed.
+//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
+//config: command (not entire shell), fg/bg builtins work. Without this option,
+//config: "cmd &" still works by simply spawning a process and immediately
+//config: prompting for next command (or executing next command in a script),
+//config: but no separate process group is formed.
//config:
//config:config HUSH_TICK
//config: bool "Support process substitution"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable `command` and $(command).
+//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 case ... esac statement. +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. +800 bytes.
+//config: Enable support for shell functions. +800 bytes.
//config:
//config:config HUSH_LOCAL
//config: bool "local builtin"
//config: default y
//config: depends on HUSH_FUNCTIONS
//config: help
-//config: Enable support for local variables in functions.
+//config: Enable support for local variables in functions.
//config:
//config:config HUSH_RANDOM_SUPPORT
//config: bool "Pseudorandom generator and $RANDOM variable"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
-//config: Each read of "$RANDOM" will generate a new pseudorandom value.
+//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
+//config: Each read of "$RANDOM" will generate a new pseudorandom value.
//config:
//config:config HUSH_MODE_X
//config: bool "Support 'hush -x' option and 'set -x' command"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: This instructs hush to print commands before execution.
-//config: Adds ~300 bytes.
+//config: This instructs hush to print commands before execution.
+//config: Adds ~300 bytes.
//config:
//config:config HUSH_ECHO
//config: bool "echo builtin"
//config: default y
//config: depends on HUSH_EXPORT
//config: help
-//config: export -n unexports variables. It is a bash extension.
+//config: export -n unexports variables. It is a bash extension.
//config:
//config:config HUSH_READONLY
//config: bool "readonly builtin"
//config: default y
//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
-//config: Enable support for read-only variables.
+//config: Enable support for read-only variables.
//config:
//config:config HUSH_KILL
//config: bool "kill builtin (supports kill %jobspec)"
BLTIN("export" , builtin_export , "Set environment variables"),
#endif
#if ENABLE_HUSH_JOB
- BLTIN("fg" , builtin_fg_bg , "Bring job into foreground"),
+ BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
#endif
#if ENABLE_HUSH_HELP
BLTIN("help" , builtin_help , NULL),
BLTIN("unset" , builtin_unset , "Unset variables"),
#endif
#if ENABLE_HUSH_WAIT
- BLTIN("wait" , builtin_wait , "Wait for process"),
+ BLTIN("wait" , builtin_wait , "Wait for process to finish"),
#endif
};
/* These builtins won't be used if we are on NOMMU and need to re-exec
static void o_addblock(o_string *o, const char *str, int len)
{
o_grow_by(o, len);
- memcpy(&o->data[o->length], str, len);
+ ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0';
o->length += len;
- o->data[o->length] = '\0';
}
static void o_addstr(o_string *o, const char *str)
break;
result = xrealloc(result, res_len + (s - val) + repl_len + 1);
- memcpy(result + res_len, val, s - val);
- res_len += s - val;
- strcpy(result + res_len, repl);
- res_len += repl_len;
+ strcpy(mempcpy(result + res_len, val, s - val), repl);
+ res_len += (s - val) + repl_len;
debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
val = s + size;
if (exp_op == '/')
break;
}
- if (val[0] && result) {
+ if (*val && result) {
result = xrealloc(result, res_len + strlen(val) + 1);
strcpy(result + res_len, val);
debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
first_char = arg[0] = arg0 & 0x7f;
exp_op = 0;
- if (first_char == '#' /* ${#... */
- && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
+ if (first_char == '#' && arg[1] /* ${#... but not ${#} */
+ && (!exp_saveptr /* and (not ${#<op_char>...} */
+ || (arg[1] == '?' && arg[2] == '\0') /* or ${#?} - "len of $?") */
+ )
) {
/* It must be length operator: ${#var} */
var++;
/* mimic bash message */
die_if_script("%s: %s",
var,
- exp_word[0] ? exp_word : "parameter null or not set"
+ exp_word[0]
+ ? exp_word
+ : "parameter null or not set"
+ /* ash has more specific messages, a-la: */
+ /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
);
//TODO: how interactive bash aborts expansion mid-command?
} else {
/* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
};
+static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
+{
+ sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
+ sq[i].orig_fd = orig;
+ sq[i].moved_to = moved;
+ sq[i+1].orig_fd = -1; /* end marker */
+ return sq;
+}
+
static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
{
+ int moved_to;
int i = 0;
if (sq) while (sq[i].orig_fd >= 0) {
i++;
}
- sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
- sq[i].orig_fd = fd;
/* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
- sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd);
- debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to);
- if (sq[i].moved_to < 0 && errno != EBADF)
+ moved_to = fcntl_F_DUPFD(fd, avoid_fd);
+ debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
+ if (moved_to < 0 && errno != EBADF)
xfunc_die();
- sq[i+1].orig_fd = -1; /* end marker */
- return sq;
+ return append_squirrel(sq, i, fd, moved_to);
}
/* fd: redirect wants this fd to be used (e.g. 3>file).
*/
return 1;
}
+ if (openfd == redir->rd_fd && sqp) {
+ /* open() gave us precisely the fd we wanted.
+ * This means that this fd was not busy
+ * (not opened to anywhere).
+ * Remember to close it on restore:
+ */
+ struct squirrel *sq = *sqp;
+ int i = 0;
+ if (sq) while (sq[i].orig_fd >= 0)
+ i++;
+ *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */
+ debug_printf_redir("redir to previously closed fd %d\n", openfd);
+ }
} else {
/* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */
openfd = redir->rd_dup;
sighandler_t handler;
sig = get_signum(*argv++);
- if (sig < 0 || sig >= NSIG) {
+ if (sig < 0) {
ret = EXIT_FAILURE;
/* Mimic bash message exactly */
bb_error_msg("trap: %s: invalid signal specification", argv[-1]);