@@ -443,6 +443,14 @@ config CMD_RUN
help
Run the command in the given environment variable.
+config CMD_CALL
+ bool "call"
+ depends on HUSH_PARSER
+ depends on CMD_RUN
+ help
+ Call function defined in environment variable, setting
+ positional arguments $1..$9.
+
config CMD_IMI
bool "iminfo"
default y
@@ -135,6 +135,17 @@ DECLARE_GLOBAL_DATA_PTR;
#define syntax() syntax_err()
#define xstrdup strdup
#define error_msg printf
+
+#ifdef CONFIG_CMD_CALL
+#define MAX_CALL_ARGS 9
+struct call_args {
+ struct call_args *prev;
+ int count;
+ char *args[MAX_CALL_ARGS]; /* [0] holds $1 etc. */
+};
+static struct call_args *current_call_args;
+#endif
+
#else
typedef enum {
REDIRECT_INPUT = 1,
@@ -2144,6 +2155,10 @@ char *get_local_var(const char *s)
#ifdef __U_BOOT__
if (*s == '$')
return get_dollar_var(s[1]);
+ /* To make ${1:-default} work: */
+ if (IS_ENABLED(CONFIG_CMD_CALL) &&
+ '1' <= s[0] && s[0] <= '9' && !s[1])
+ return get_dollar_var(s[0]);
#endif
for (cur = top_vars; cur; cur=cur->next)
@@ -2826,6 +2841,23 @@ static char *get_dollar_var(char ch)
case '?':
sprintf(buf, "%u", (unsigned int)last_return_code);
break;
+#ifdef CONFIG_CMD_CALL
+ case '#':
+ if (!current_call_args)
+ return NULL;
+ sprintf(buf, "%u", current_call_args->count);
+ break;
+ case '1' ... '9': {
+ const struct call_args *ca = current_call_args;
+ int i = ch - '1';
+
+ if (!ca)
+ return NULL;
+ if (i >= ca->count)
+ return NULL;
+ return ca->args[i];
+ }
+#endif
default:
return NULL;
}
@@ -2865,10 +2897,14 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
} else switch (ch) {
#ifdef __U_BOOT__
case '?':
+#ifdef CONFIG_CMD_CALL
+ case '1' ... '9':
+ case '#':
+#endif
ctx->child->sp++;
b_addchr(dest, SPECIAL_VAR_SYMBOL);
b_addchr(dest, '$');
- b_addchr(dest, '?');
+ b_addchr(dest, ch);
b_addchr(dest, SPECIAL_VAR_SYMBOL);
advance = 1;
break;
@@ -3711,5 +3747,42 @@ U_BOOT_CMD(
" - print value of hushshell variable 'name'"
);
+#ifdef CONFIG_CMD_CALL
+static int do_cmd_call(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct call_args ca;
+ char *run_args[2];
+ int i, ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ ca.count = argc - 2;
+ for (i = 2; i < argc; ++i)
+ ca.args[i - 2] = argv[i];
+ ca.prev = current_call_args;
+ current_call_args = &ca;
+
+ run_args[0] = "run";
+ run_args[1] = argv[1];
+ ret = do_run(cmdtp, flag, 2, run_args);
+
+ current_call_args = ca.prev;
+
+ return ret;
+}
+
+U_BOOT_CMD_COMPLETE(
+ call, 1 + 1 + MAX_CALL_ARGS, 0, do_cmd_call,
+ "call command in environment variable, setting positional arguments $1..$9",
+ "var [args...]\n"
+ " - run the command(s) in the environment variable 'var',\n"
+ " with $1..$9 set to the positional arguments",
+ var_complete
+);
+
+#endif
+
#endif
/****************************************************************************/
Currently, the only way to emulate functions with arguments in the busybox shell is by doing "foo=arg1; bar=arg2; run func" and having "func" refer to $foo and $bar. That works, but is a bit clunky, and also suffers from foo and bar being set globally - if func itself wants to run other "functions" defined in the environment, those other functions better not use the same parameter names: setenv g 'do_g_stuff $foo' setenv f 'do_f_stuff $foo $bar; foo=123; run g; do_more_f_stuff $foo $bar' Sure, f could do a "saved_foo=$foo; .... foo=$saved_foo" dance, but that makes everything even more clunky. In order to increase readability, add a little helper "call" that is like "run", but which sets local shell variables $1 through $9 (and $#). As in a "real" shell, they are local to the current function, so if f is called with two arguments, and f calls g with one argument, g sees $2 as unset. Then the above can be written setenv g 'do_g_stuff $1' setenv f 'do_f_stuff $1 $2; call g 123; do_more_f_stuff $1 $2' Everything except - b_addchr(dest, '?'); + b_addchr(dest, ch); is under CONFIG_CMD_CALL, and when CONFIG_CMD_CALL=n, the ch there can only be '?'. So no functional change when CONFIG_CMD_CALL is not selected. "Real shells" have special syntax for defining a function, but calling a function is the same as calling builtins or external commands. So the "call" may admittedly be seen as a bit of a kludge. It should be rather easy to make custom (i.e., defined in the environment) functions "transparently callable" on top of this infrastructure, i.e. so one could just say f a b c instead of call f a b c However, that behaviour should be controlled by a separate config knob, and can be added later if anyone actually wants it. Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk> --- cmd/Kconfig | 8 +++++ common/cli_hush.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-)