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

Sean Anderson
2021-07-01T06:18:40Z

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

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