288b29e44d
Move these functions into the command.h header file which is a better fit. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Tom Rini <trini@konsulko.com>
341 lines
7.3 KiB
C
341 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (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>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <bootretry.h>
|
|
#include <cli.h>
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <env.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 = env_get(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 env_get(), 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;
|
|
}
|