123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- * (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 <console.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;
- }
- void cli_simple_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 __maybe_unused *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 */
- cli_simple_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 + 1] = { 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)
- strlcpy(lastcommand, console_buffer,
- CONFIG_SYS_CBSIZE + 1);
- 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;
- }
|