diff mbox series

[RFC,13/28] cli: lil: Wire up LIL to the rest of U-Boot

Message ID 20210701061611.957918-14-seanga2@gmail.com
State RFC
Delegated to: Tom Rini
Headers show
Series cli: Add a new shell | expand

Commit Message

Sean Anderson July 1, 2021, 6:15 a.m. UTC
This sets the shell to LIL when CONFIG_LIL is enabled. Repeated commands
are not supporteed. Neither are partial commands a la Hush's secondary
prompt. Setting and getting environmental variables is done through
callbacks to assist with testing.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 cmd/Kconfig      | 12 +++++--
 common/cli.c     | 84 +++++++++++++++++++++++++++++++++++++++---------
 common/cli_lil.c | 32 ++++++++++++++++++
 3 files changed, 111 insertions(+), 17 deletions(-)

Comments

Rasmus Villemoes July 2, 2021, 8:18 a.m. UTC | #1
On 01/07/2021 08.15, Sean Anderson wrote:
> This sets the shell to LIL when CONFIG_LIL is enabled. Repeated commands
> are not supporteed. Neither are partial commands a la Hush's secondary
> prompt. Setting and getting environmental variables is done through
> callbacks to assist with testing.

This all looks very interesting, thanks for doing this!

Re this patch, is there some way LIL and HUSH could coexist, with HUSH
then being the initial shell, and one could then enter a "lil shell"
with the command "lil", just as I can start a busybox shell from bash by
saying "busybox sh". That could make it a bit easier for playing around
with initially.

I have no idea how hard that is to do with Kconfig. Perhaps make LIL
selectable by itself, then have a hidden symbol LIL_SHELL which is
"default y if LIL && !HUSH_PARSER" or something like that.

Rasmus
Sean Anderson July 2, 2021, 1:40 p.m. UTC | #2
On 7/2/21 4:18 AM, Rasmus Villemoes wrote:
> On 01/07/2021 08.15, Sean Anderson wrote:
>> This sets the shell to LIL when CONFIG_LIL is enabled. Repeated commands
>> are not supporteed. Neither are partial commands a la Hush's secondary
>> prompt. Setting and getting environmental variables is done through
>> callbacks to assist with testing.
> 
> This all looks very interesting, thanks for doing this!
> 
> Re this patch, is there some way LIL and HUSH could coexist, with HUSH
> then being the initial shell, and one could then enter a "lil shell"
> with the command "lil", just as I can start a busybox shell from bash by
> saying "busybox sh". That could make it a bit easier for playing around
> with initially.
> 
> I have no idea how hard that is to do with Kconfig. Perhaps make LIL
> selectable by itself, then have a hidden symbol LIL_SHELL which is
> "default y if LIL && !HUSH_PARSER" or something like that.

Yeah, I wanted to do this anyway for unit testing at least.

--Sean
Simon Glass July 5, 2021, 3:29 p.m. UTC | #3
Hi Sean,

On Thu, 1 Jul 2021 at 00:16, Sean Anderson <seanga2@gmail.com> wrote:
>
> This sets the shell to LIL when CONFIG_LIL is enabled. Repeated commands
> are not supporteed. Neither are partial commands a la Hush's secondary
> prompt. Setting and getting environmental variables is done through
> callbacks to assist with testing.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
>  cmd/Kconfig      | 12 +++++--
>  common/cli.c     | 84 +++++++++++++++++++++++++++++++++++++++---------
>  common/cli_lil.c | 32 ++++++++++++++++++
>  3 files changed, 111 insertions(+), 17 deletions(-)

Can you try using if() instead of #ifdef in here?

Regards,
Simon
diff mbox series

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 0a7b73cb6d..b61a7557a9 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -11,9 +11,14 @@  config CMDLINE
 	  Depending on the number of commands enabled, this can add
 	  substantially to the size of U-Boot.
 
+if CMDLINE
+
+choice
+	prompt "Shell"
+	default HUSH_PARSER
+
 config HUSH_PARSER
 	bool "Use hush shell"
-	depends on CMDLINE
 	help
 	  This option enables the "hush" shell (from Busybox) as command line
 	  interpreter, thus enabling powerful command line syntax like
@@ -25,13 +30,14 @@  config HUSH_PARSER
 
 config LIL
 	bool "Use LIL shell"
-	depends on CMDLINE
 	help
 	  This options enables the "Little Interpreted Language" (LIL) shell as
 	  command line interpreter, thus enabling powerful command line syntax
 	  like `proc name {args} {body}' functions or `echo [some command]`
 	  command substitution ("tcl scripts").
 
+endchoice
+
 if LIL
 
 config LIL_FULL
@@ -42,6 +48,8 @@  config LIL_FULL
 
 endif
 
+endif
+
 config CMDLINE_EDITING
 	bool "Enable command line editing"
 	depends on CMDLINE
diff --git a/common/cli.c b/common/cli.c
index 048eacb9ef..ad5d76d563 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -12,6 +12,7 @@ 
 #include <bootstage.h>
 #include <cli.h>
 #include <cli_hush.h>
+#include <cli_lil.h>
 #include <command.h>
 #include <console.h>
 #include <env.h>
@@ -22,6 +23,53 @@ 
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#ifdef CONFIG_LIL
+static struct lil *lil;
+
+static int env_setvar(struct lil *lil, const char *name,
+		      struct lil_value **value)
+{
+	if (env_set(name, lil_to_string(*value)))
+		return -1;
+	return 0;
+}
+
+static int env_getvar(struct lil *lil, const char *name,
+		      struct lil_value **value)
+{
+	*value = lil_alloc_string(env_get(name));
+	return 1;
+}
+
+static const struct lil_callbacks env_callbacks = {
+	.setvar = env_setvar,
+	.getvar = env_getvar,
+};
+
+static int lil_run(const char *cmd)
+{
+	int err;
+	struct lil_value *result = lil_parse(lil, cmd, 0, 0);
+	const char *err_msg, *strres = lil_to_string(result);
+
+	/* The result may be very big, so use puts */
+	if (strres && strres[0]) {
+		puts(strres);
+		putc('\n');
+	}
+	lil_free_value(result);
+
+	err = lil_error(lil, &err_msg);
+	if (err) {
+		if (err_msg)
+			printf("error: %s\n", err_msg);
+		else
+			printf("error: %d\n", err);
+	}
+	return !!err;
+}
+#endif
+
 #ifdef CONFIG_CMDLINE
 /*
  * Run a command using the selected parser.
@@ -32,7 +80,15 @@  DECLARE_GLOBAL_DATA_PTR;
  */
 int run_command(const char *cmd, int flag)
 {
-#if !CONFIG_IS_ENABLED(HUSH_PARSER)
+#ifdef CONFIG_HUSH_PARSER
+	int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
+
+	if (flag & CMD_FLAG_ENV)
+		hush_flags |= FLAG_CONT_ON_NEWLINE;
+	return parse_string_outer(cmd, hush_flags);
+#elif defined(CONFIG_LIL)
+	return lil_run(cmd);
+#else
 	/*
 	 * cli_run_command can return 0 or 1 for success, so clean up
 	 * its result.
@@ -41,12 +97,6 @@  int run_command(const char *cmd, int flag)
 		return 1;
 
 	return 0;
-#else
-	int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
-
-	if (flag & CMD_FLAG_ENV)
-		hush_flags |= FLAG_CONT_ON_NEWLINE;
-	return parse_string_outer(cmd, hush_flags);
 #endif
 }
 
@@ -59,9 +109,7 @@  int run_command(const char *cmd, int flag)
  */
 int run_command_repeatable(const char *cmd, int flag)
 {
-#ifndef CONFIG_HUSH_PARSER
-	return cli_simple_run_command(cmd, flag);
-#else
+#ifdef CONFIG_HUSH_PARSER
 	/*
 	 * parse_string_outer() returns 1 for failure, so clean up
 	 * its result.
@@ -71,6 +119,10 @@  int run_command_repeatable(const char *cmd, int flag)
 		return -1;
 
 	return 0;
+#elif defined(CONFIG_LIL)
+	return run_command(cmd, flag);
+#else
+	return cli_simple_run_command(cmd, flag);
 #endif
 }
 #else
@@ -90,7 +142,7 @@  int run_command_list(const char *cmd, int len, int flag)
 
 	if (len == -1) {
 		len = strlen(cmd);
-#ifdef CONFIG_HUSH_PARSER
+#if defined(CONFIG_HUSH_PARSER) || defined(CONFIG_LIL)
 		/* hush will never change our string */
 		need_buff = 0;
 #else
@@ -107,7 +159,9 @@  int run_command_list(const char *cmd, int len, int flag)
 	}
 #ifdef CONFIG_HUSH_PARSER
 	rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
-#else
+#elif defined(CONFIG_LIL)
+	rcode = lil_run(buff);
+#elif defined(CONFIG_CMDLINE)
 	/*
 	 * This function will overwrite any \n it sees with a \0, which
 	 * is why it can't work with a const char *. Here we are making
@@ -115,11 +169,9 @@  int run_command_list(const char *cmd, int len, int flag)
 	 * doing a malloc() which is actually required only in a case that
 	 * is pretty rare.
 	 */
-#ifdef CONFIG_CMDLINE
 	rcode = cli_simple_run_command_list(buff, flag);
 #else
 	rcode = board_run_command(buff);
-#endif
 #endif
 	if (need_buff)
 		free(buff);
@@ -241,9 +293,11 @@  void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
 	u_boot_hush_start();
+#elif defined(CONFIG_LIL)
+	lil = lil_new(&env_callbacks);
 #endif
 
-#if defined(CONFIG_HUSH_INIT_VAR)
+#ifdef CONFIG_HUSH_INIT_VAR
 	hush_init_var();
 #endif
 }
diff --git a/common/cli_lil.c b/common/cli_lil.c
index 50e314a643..66ee62bf33 100644
--- a/common/cli_lil.c
+++ b/common/cli_lil.c
@@ -11,6 +11,7 @@ 
 #include <common.h>
 #include <cli_lil.h>
 #include <console.h>
+#include <command.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -59,6 +60,7 @@  struct lil_var {
 struct lil_env {
 	struct lil_env *parent;
 	struct lil_func *func;
+	const char *proc;
 	struct lil_var **var;
 	size_t vars;
 	struct hashmap varmap;
@@ -1045,7 +1047,9 @@  static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd,
 	struct lil_value *r;
 
 	if (cmd->proc) {
+		lil->env->proc = words->v[0]->d;
 		r = cmd->proc(lil, words->c - 1, words->v + 1);
+		lil->env->proc = NULL;
 	} else {
 		lil_push_env(lil);
 		lil->env->func = cmd;
@@ -2967,8 +2971,33 @@  static struct lil_value *fnc_lmap(struct lil *lil, size_t argc,
 	return NULL;
 }
 
+static struct lil_value *fnc_builtin(struct lil *lil, size_t argc,
+				     struct lil_value **lil_argv)
+{
+	int err, repeatable;
+	size_t i;
+	/*
+	 * We need space for the function name, and the last argv must be NULL
+	 */
+	char **argv = calloc(sizeof(char *), argc + 2);
+
+	argv[0] = (char *)lil->env->proc;
+	for (i = 0; i < argc; i++)
+		argv[i + 1] = (char *)lil_to_string(lil_argv[i]);
+
+	err = cmd_process(0, argc + 1, argv, &repeatable, NULL);
+	if (err)
+		lil_set_errorf(lil, LIL_ERR_USER, "%s failed", argv[0]);
+	free(argv);
+
+	return 0;
+}
+
 static void register_stdcmds(struct lil *lil)
 {
+	struct cmd_tbl *cmdtp, *start = ll_entry_start(struct cmd_tbl, cmd);
+	const int len = ll_entry_count(struct cmd_tbl, cmd);
+
 	lil_register(lil, "decr", fnc_decr);
 	lil_register(lil, "eval", fnc_eval);
 	lil_register(lil, "expr", fnc_expr);
@@ -2984,6 +3013,9 @@  static void register_stdcmds(struct lil *lil)
 	lil_register(lil, "try", fnc_try);
 	lil_register(lil, "while", fnc_while);
 
+	for (cmdtp = start; cmdtp != start + len; cmdtp++)
+		lil_register(lil, cmdtp->name, fnc_builtin);
+
 	if (IS_ENABLED(CONFIG_LIL_FULL)) {
 		lil_register(lil, "append", fnc_append);
 		lil_register(lil, "char", fnc_char);