summaryrefslogtreecommitdiff
path: root/common/cli_simple.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/cli_simple.c')
-rw-r--r--common/cli_simple.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/common/cli_simple.c b/common/cli_simple.c
new file mode 100644
index 0000000..353ceeb
--- /dev/null
+++ b/common/cli_simple.c
@@ -0,0 +1,337 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Add to readline cmdline-editing by
+ * (C) Copyright 2005
+ * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <bootretry.h>
+#include <cli.h>
+#include <linux/ctype.h>
+
+#define DEBUG_PARSER 0 /* set to 1 to debug */
+
+#define debug_parser(fmt, args...) \
+ debug_cond(DEBUG_PARSER, fmt, ##args)
+
+
+int cli_simple_parse_line(char *line, char *argv[])
+{
+ int nargs = 0;
+
+ debug_parser("%s: \"%s\"\n", __func__, line);
+ while (nargs < CONFIG_SYS_MAXARGS) {
+ /* skip any white space */
+ while (isblank(*line))
+ ++line;
+
+ if (*line == '\0') { /* end of line, no more args */
+ argv[nargs] = NULL;
+ debug_parser("%s: nargs=%d\n", __func__, nargs);
+ return nargs;
+ }
+
+ argv[nargs++] = line; /* begin of argument string */
+
+ /* find end of string */
+ while (*line && !isblank(*line))
+ ++line;
+
+ if (*line == '\0') { /* end of line, no more args */
+ argv[nargs] = NULL;
+ debug_parser("parse_line: nargs=%d\n", nargs);
+ return nargs;
+ }
+
+ *line++ = '\0'; /* terminate current arg */
+ }
+
+ printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
+
+ debug_parser("%s: nargs=%d\n", __func__, nargs);
+ return nargs;
+}
+
+static void process_macros(const char *input, char *output)
+{
+ char c, prev;
+ const char *varname_start = NULL;
+ int inputcnt = strlen(input);
+ int outputcnt = CONFIG_SYS_CBSIZE;
+ int state = 0; /* 0 = waiting for '$' */
+
+ /* 1 = waiting for '(' or '{' */
+ /* 2 = waiting for ')' or '}' */
+ /* 3 = waiting for ''' */
+ char *output_start = output;
+
+ debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
+ input);
+
+ prev = '\0'; /* previous character */
+
+ while (inputcnt && outputcnt) {
+ c = *input++;
+ inputcnt--;
+
+ if (state != 3) {
+ /* remove one level of escape characters */
+ if ((c == '\\') && (prev != '\\')) {
+ if (inputcnt-- == 0)
+ break;
+ prev = c;
+ c = *input++;
+ }
+ }
+
+ switch (state) {
+ case 0: /* Waiting for (unescaped) $ */
+ if ((c == '\'') && (prev != '\\')) {
+ state = 3;
+ break;
+ }
+ if ((c == '$') && (prev != '\\')) {
+ state++;
+ } else {
+ *(output++) = c;
+ outputcnt--;
+ }
+ break;
+ case 1: /* Waiting for ( */
+ if (c == '(' || c == '{') {
+ state++;
+ varname_start = input;
+ } else {
+ state = 0;
+ *(output++) = '$';
+ outputcnt--;
+
+ if (outputcnt) {
+ *(output++) = c;
+ outputcnt--;
+ }
+ }
+ break;
+ case 2: /* Waiting for ) */
+ if (c == ')' || c == '}') {
+ int i;
+ char envname[CONFIG_SYS_CBSIZE], *envval;
+ /* Varname # of chars */
+ int envcnt = input - varname_start - 1;
+
+ /* Get the varname */
+ for (i = 0; i < envcnt; i++)
+ envname[i] = varname_start[i];
+ envname[i] = 0;
+
+ /* Get its value */
+ envval = getenv(envname);
+
+ /* Copy into the line if it exists */
+ if (envval != NULL)
+ while ((*envval) && outputcnt) {
+ *(output++) = *(envval++);
+ outputcnt--;
+ }
+ /* Look for another '$' */
+ state = 0;
+ }
+ break;
+ case 3: /* Waiting for ' */
+ if ((c == '\'') && (prev != '\\')) {
+ state = 0;
+ } else {
+ *(output++) = c;
+ outputcnt--;
+ }
+ break;
+ }
+ prev = c;
+ }
+
+ if (outputcnt)
+ *output = 0;
+ else
+ *(output - 1) = 0;
+
+ debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
+ strlen(output_start), output_start);
+}
+
+ /*
+ * WARNING:
+ *
+ * We must create a temporary copy of the command since the command we get
+ * may be the result from getenv(), which returns a pointer directly to
+ * the environment data, which may change magicly when the command we run
+ * creates or modifies environment variables (like "bootp" does).
+ */
+int cli_simple_run_command(const char *cmd, int flag)
+{
+ char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
+ char *token; /* start of token in cmdbuf */
+ char *sep; /* end of token (separator) in cmdbuf */
+ char finaltoken[CONFIG_SYS_CBSIZE];
+ char *str = cmdbuf;
+ char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
+ int argc, inquotes;
+ int repeatable = 1;
+ int rc = 0;
+
+ debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
+ if (DEBUG_PARSER) {
+ /* use puts - string may be loooong */
+ puts(cmd ? cmd : "NULL");
+ puts("\"\n");
+ }
+ clear_ctrlc(); /* forget any previous Control C */
+
+ if (!cmd || !*cmd)
+ return -1; /* empty command */
+
+ if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
+ puts("## Command too long!\n");
+ return -1;
+ }
+
+ strcpy(cmdbuf, cmd);
+
+ /* Process separators and check for invalid
+ * repeatable commands
+ */
+
+ debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
+ while (*str) {
+ /*
+ * Find separator, or string end
+ * Allow simple escape of ';' by writing "\;"
+ */
+ for (inquotes = 0, sep = str; *sep; sep++) {
+ if ((*sep == '\'') &&
+ (*(sep - 1) != '\\'))
+ inquotes = !inquotes;
+
+ if (!inquotes &&
+ (*sep == ';') && /* separator */
+ (sep != str) && /* past string start */
+ (*(sep - 1) != '\\')) /* and NOT escaped */
+ break;
+ }
+
+ /*
+ * Limit the token to data between separators
+ */
+ token = str;
+ if (*sep) {
+ str = sep + 1; /* start of command for next pass */
+ *sep = '\0';
+ } else {
+ str = sep; /* no more commands for next pass */
+ }
+ debug_parser("token: \"%s\"\n", token);
+
+ /* find macros in this token and replace them */
+ process_macros(token, finaltoken);
+
+ /* Extract arguments */
+ argc = cli_simple_parse_line(finaltoken, argv);
+ if (argc == 0) {
+ rc = -1; /* no command at all */
+ continue;
+ }
+
+ if (cmd_process(flag, argc, argv, &repeatable, NULL))
+ rc = -1;
+
+ /* Did the user stop this? */
+ if (had_ctrlc())
+ return -1; /* if stopped then not repeatable */
+ }
+
+ return rc ? rc : repeatable;
+}
+
+void cli_simple_loop(void)
+{
+ static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
+
+ int len;
+ int flag;
+ int rc = 1;
+
+ for (;;) {
+ if (rc >= 0) {
+ /* Saw enough of a valid command to
+ * restart the timeout.
+ */
+ bootretry_reset_cmd_timeout();
+ }
+ len = cli_readline(CONFIG_SYS_PROMPT);
+
+ flag = 0; /* assume no special flags for now */
+ if (len > 0)
+ strcpy(lastcommand, console_buffer);
+ else if (len == 0)
+ flag |= CMD_FLAG_REPEAT;
+#ifdef CONFIG_BOOT_RETRY_TIME
+ else if (len == -2) {
+ /* -2 means timed out, retry autoboot
+ */
+ puts("\nTimed out waiting for command\n");
+# ifdef CONFIG_RESET_TO_RETRY
+ /* Reinit board to run initialization code again */
+ do_reset(NULL, 0, 0, NULL);
+# else
+ return; /* retry autoboot */
+# endif
+ }
+#endif
+
+ if (len == -1)
+ puts("<INTERRUPT>\n");
+ else
+ rc = run_command_repeatable(lastcommand, flag);
+
+ if (rc <= 0) {
+ /* invalid command or not repeatable, forget it */
+ lastcommand[0] = 0;
+ }
+ }
+}
+
+int cli_simple_run_command_list(char *cmd, int flag)
+{
+ char *line, *next;
+ int rcode = 0;
+
+ /*
+ * Break into individual lines, and execute each line; terminate on
+ * error.
+ */
+ next = cmd;
+ line = cmd;
+ while (*next) {
+ if (*next == '\n') {
+ *next = '\0';
+ /* run only non-empty commands */
+ if (*line) {
+ debug("** exec: \"%s\"\n", line);
+ if (cli_simple_run_command(line, 0) < 0) {
+ rcode = 1;
+ break;
+ }
+ }
+ line = next + 1;
+ }
+ ++next;
+ }
+ if (rcode == 0 && *line)
+ rcode = (cli_simple_run_command(line, 0) < 0);
+
+ return rcode;
+}