All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
To: u-boot@lists.denx.de
Subject: [PATCH 2/3] cli_hush.c: add "call" command
Date: Fri, 25 Sep 2020 13:19:41 +0200	[thread overview]
Message-ID: <20200925111942.4629-3-rasmus.villemoes@prevas.dk> (raw)
In-Reply-To: <20200925111942.4629-1-rasmus.villemoes@prevas.dk>

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(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 0c984d735d..306f115c32 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -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
diff --git a/common/cli_hush.c b/common/cli_hush.c
index 072b871f1e..e17fba99ee 100644
--- a/common/cli_hush.c
+++ b/common/cli_hush.c
@@ -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
 /****************************************************************************/
-- 
2.23.0

  parent reply	other threads:[~2020-09-25 11:19 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-25 11:19 [PATCH 0/3] add "call" command Rasmus Villemoes
2020-09-25 11:19 ` [PATCH 1/3] cli_hush.c: refactor handle_dollar() to prepare for cmd_call Rasmus Villemoes
2020-09-25 13:02   ` Wolfgang Denk
2020-09-25 11:19 ` Rasmus Villemoes [this message]
2020-09-25 13:18   ` [PATCH 2/3] cli_hush.c: add "call" command Rasmus Villemoes
2020-09-26  8:37     ` Wolfgang Denk
2020-09-25 11:19 ` [PATCH 3/3] ut: add small hush tests Rasmus Villemoes
2020-09-25 11:52 ` [PATCH 0/3] add "call" command Heinrich Schuchardt
2020-09-25 12:36   ` Rasmus Villemoes
2020-09-25 13:09   ` Wolfgang Denk
2020-09-25 13:38     ` Rasmus Villemoes
2020-09-25 13:38     ` Heinrich Schuchardt
2020-09-25 13:51       ` Rasmus Villemoes
2020-09-26  8:55         ` Wolfgang Denk
2020-09-26  8:51       ` Wolfgang Denk
2020-09-26 10:39         ` Heinrich Schuchardt
2020-09-26 14:13           ` Wolfgang Denk
2020-09-25 12:59 ` Wolfgang Denk
2020-09-25 14:40   ` Simon Glass
2020-09-26 14:02     ` Wolfgang Denk
2020-09-29 17:45       ` Tom Rini
2020-09-30 11:46         ` Wolfgang Denk
2020-10-07  7:20 ` [PATCH v2 0/3] allow positional arguments with "run" Rasmus Villemoes
2020-10-07  7:20   ` [PATCH v2 1/3] cli_hush.c: refactor handle_dollar() to prepare for "run with arguments" Rasmus Villemoes
2020-10-12  3:34     ` Simon Glass
2020-10-07  7:20   ` [PATCH v2 2/3] allow positional arguments with "run" command Rasmus Villemoes
2020-10-12  3:34     ` Simon Glass
2020-10-12  7:06       ` Rasmus Villemoes
2020-10-15 15:05         ` Simon Glass
2020-10-15 22:06           ` Rasmus Villemoes
2020-10-19  7:31             ` Wolfgang Denk
2020-10-19  8:31               ` Rasmus Villemoes
2020-10-19  8:49                 ` Wolfgang Denk
2020-10-07  7:20   ` [PATCH v2 3/3] ut: add small hush tests Rasmus Villemoes
2020-10-12  3:34     ` Simon Glass
2020-11-05  7:25   ` [PATCH v2 0/3] allow positional arguments with "run" Rasmus Villemoes
2020-11-06 20:52     ` Tom Rini
2020-11-08 13:28       ` Wolfgang Denk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200925111942.4629-3-rasmus.villemoes@prevas.dk \
    --to=rasmus.villemoes@prevas.dk \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.