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

Sean Anderson seanga2 at gmail.com
Thu Jul 1 08:15:56 CEST 2021


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 at gmail.com>
---

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

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);
-- 
2.32.0



More information about the U-Boot mailing list