diff --git a/common/Makefile b/common/Makefile index da184a8..7998325 100644 --- a/common/Makefile +++ b/common/Makefile @@ -11,7 +11,14 @@ obj-y += main.o obj-y += command.o obj-y += exports.o obj-y += hash.o -obj-$(CONFIG_SYS_HUSH_PARSER) += cli_hush.o +ifdef CONFIG_SYS_HUSH_PARSER +obj-y += cli_hush.o +endif + +# We always have this since drivers/ddr/fs/interactive.c needs it +obj-y += cli_simple.o + +obj-y += cli_readline.o obj-y += s_record.o obj-y += xyzModem.o obj-y += cmd_disk.o diff --git a/common/cli_readline.c b/common/cli_readline.c new file mode 100644 index 0000000..cea0b7c --- /dev/null +++ b/common/cli_readline.c @@ -0,0 +1,670 @@ +/* + * (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, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const char erase_seq[] = "\b \b"; /* erase sequence */ +static const char tab_seq[] = " "; /* used to expand TABs */ + +#ifdef CONFIG_BOOT_RETRY_TIME +static uint64_t endtime; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ +#endif + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) + return p; + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + puts(erase_seq); + (*colp)--; + } + for (s = buffer; s < p; ++s) { + if (*s == '\t') { + puts(tab_seq + ((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putc(*s); + } + } + } else { + puts(erase_seq); + (*colp)--; + } + (*np)--; + + return p; +} + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu + */ + +#define putnstr(str, n) printf("%.*s", (int)n, str) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putc(ch) +#define getcmd_getch() getc() +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 20 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ + +#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char *hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = hist_list[hist_cur]; + } + + return ret; +} + +static char *hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = hist_list[hist_cur]; + + return ret; +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned long n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf("%s\n", hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf("%*s", (int)(eol_num - num), ""); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) + memmove(&buf[*num+1], &buf[*num], wlen-1); + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) + getcmd_putch(CTL_BACKSPACE); + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, + unsigned long *num, unsigned long *eol_num, + char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + unsigned long num = 0; + unsigned long eol_num = 0; + unsigned long wlen; + char ichar; + int insert = 1; + int esc_len = 0; + char esc_save[8]; + int init_len = strlen(buf); + int first = 1; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + + while (1) { +#ifdef CONFIG_BOOT_RETRY_TIME + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } +#endif + if (first && timeout) { + uint64_t etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } + first = 0; + } + + ichar = getcmd_getch(); + + if ((ichar == '\n') || (ichar == '\r')) { + putc('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + if (esc_len == 1) { + if (ichar == '[') { + esc_save[esc_len] = ichar; + esc_len = 2; + } else { + cread_add_str(esc_save, esc_len, + insert, &num, &eol_num, + buf, *len); + esc_len = 0; + } + continue; + } + + switch (ichar) { + case 'D': /* <- key */ + ichar = CTL_CH('b'); + esc_len = 0; + break; + case 'C': /* -> key */ + ichar = CTL_CH('f'); + esc_len = 0; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + esc_len = 0; + break; /* pass off to ^A handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + esc_len = 0; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + esc_len = 0; + break; /* pass off to ^N handler */ + default: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + puts("impossible condition #876\n"); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + cread_add_char(ichar, insert, &num, &eol_num, buf, + *len); + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +int readline(const char *const prompt) +{ + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return readline_into_buffer(prompt, console_buffer, 0); +} + + +int readline_into_buffer(const char *const prompt, char *buffer, int timeout) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + /* + * History uses a global array which is not + * writable until after relocation to RAM. + * Revert to non-history version if still + * running from flash. + */ + if (gd->flags & GD_FLG_RELOC) { + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + puts(prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { +#endif /* CONFIG_CMDLINE_EDITING */ + char *p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen(prompt); + puts(prompt); + } + col = plen; + + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } +#endif + WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + +#ifdef CONFIG_SHOW_ACTIVITY + while (!tstc()) { + show_activity(0); + WATCHDOG_RESET(); + } +#endif + c = getc(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + puts("\r\n"); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + puts(erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p = delete_char(p_buf, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* + * if auto completion triggered just + * continue + */ + *p = '\0'; + if (cmd_auto_complete(prompt, + console_buffer, + &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + puts(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putc('\a'); + } + } + } +#ifdef CONFIG_CMDLINE_EDITING + } +#endif +} + +#ifdef CONFIG_BOOT_RETRY_TIME +/*************************************************************************** + * initialize command line timeout + */ +void init_cmd_timeout(void) +{ + char *s = getenv("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} + +void bootretry_dont_retry(void) +{ + retry_time = -1; +} + +#endif diff --git a/common/cli_simple.c b/common/cli_simple.c new file mode 100644 index 0000000..0610615 --- /dev/null +++ b/common/cli_simple.c @@ -0,0 +1,338 @@ +/* + * (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, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + + +int 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 = 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_loop(void) +{ + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; + + int len; + int flag; + int rc = 1; + + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + if (rc >= 0) { + /* Saw enough of a valid command to + * restart the timeout. + */ + reset_cmd_timeout(); + } +#endif + len = 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("\n"); + else + rc = run_command(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; +} diff --git a/common/main.c b/common/main.c index 6d1c3a9..88b0adb 100644 --- a/common/main.c +++ b/common/main.c @@ -2,10 +2,6 @@ * (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, - * * SPDX-License-Identifier: GPL-2.0+ */ @@ -20,8 +16,6 @@ #include #include #include -#include -#include DECLARE_GLOBAL_DATA_PTR; @@ -33,34 +27,12 @@ void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progre #define MAX_DELAY_STOP_STR 32 -#define DEBUG_PARSER 0 /* set to 1 to debug */ - -#define debug_parser(fmt, args...) \ - debug_cond(DEBUG_PARSER, fmt, ##args) - #ifndef DEBUG_BOOTKEYS #define DEBUG_BOOTKEYS 0 #endif #define debug_bootkeys(fmt, args...) \ debug_cond(DEBUG_BOOTKEYS, fmt, ##args) -char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ - -static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); -static const char erase_seq[] = "\b \b"; /* erase sequence */ -static const char tab_seq[] = " "; /* used to expand TABs */ - -#ifdef CONFIG_BOOT_RETRY_TIME -static uint64_t endtime = 0; /* must be set, default is instant timeout */ -static int retry_time = -1; /* -1 so can call readline before main_loop */ -#endif - -#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) - -#ifndef CONFIG_BOOT_RETRY_MIN -#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME -#endif - #ifdef CONFIG_MODEM_SUPPORT int do_mdm_init = 0; extern void mdm_init(void); /* defined in board.c */ @@ -162,7 +134,7 @@ static int abortboot_keyed(int bootdelay) # ifdef CONFIG_BOOT_RETRY_TIME /* don't retry auto boot */ if (! delaykey[i].retry) - retry_time = -1; + bootretry_dont_retry(); # endif abort = 1; } @@ -416,12 +388,6 @@ static void process_boot_delay(void) void main_loop(void) { -#ifndef CONFIG_SYS_HUSH_PARSER - static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; - int len; - int rc = 1; - int flag; -#endif #ifdef CONFIG_PREBOOT char *p; #endif @@ -489,943 +455,13 @@ void main_loop(void) /* This point is never reached */ for (;;); #else - for (;;) { -#ifdef CONFIG_BOOT_RETRY_TIME - if (rc >= 0) { - /* Saw enough of a valid command to - * restart the timeout. - */ - reset_cmd_timeout(); - } -#endif - len = 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 ("\n"); - else - rc = run_command(lastcommand, flag); - - if (rc <= 0) { - /* invalid command or not repeatable, forget it */ - lastcommand[0] = 0; - } - } + cli_loop(); #endif /*CONFIG_SYS_HUSH_PARSER*/ } -#ifdef CONFIG_BOOT_RETRY_TIME -/*************************************************************************** - * initialize command line timeout - */ -void init_cmd_timeout(void) -{ - char *s = getenv ("bootretry"); - - if (s != NULL) - retry_time = (int)simple_strtol(s, NULL, 10); - else - retry_time = CONFIG_BOOT_RETRY_TIME; - - if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) - retry_time = CONFIG_BOOT_RETRY_MIN; -} - -/*************************************************************************** - * reset command line timeout to retry_time seconds - */ -void reset_cmd_timeout(void) -{ - endtime = endtick(retry_time); -} -#endif - -#ifdef CONFIG_CMDLINE_EDITING - -/* - * cmdline-editing related codes from vivi. - * Author: Janghoon Lyu - */ - -#define putnstr(str,n) do { \ - printf ("%.*s", (int)n, str); \ - } while (0) - -#define CTL_CH(c) ((c) - 'a' + 1) -#define CTL_BACKSPACE ('\b') -#define DEL ((char)255) -#define DEL7 ((char)127) -#define CREAD_HIST_CHAR ('!') - -#define getcmd_putch(ch) putc(ch) -#define getcmd_getch() getc() -#define getcmd_cbeep() getcmd_putch('\a') - -#define HIST_MAX 20 -#define HIST_SIZE CONFIG_SYS_CBSIZE - -static int hist_max; -static int hist_add_idx; -static int hist_cur = -1; -static unsigned hist_num; - -static char *hist_list[HIST_MAX]; -static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ - -#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) - -static void hist_init(void) -{ - int i; - - hist_max = 0; - hist_add_idx = 0; - hist_cur = -1; - hist_num = 0; - - for (i = 0; i < HIST_MAX; i++) { - hist_list[i] = hist_lines[i]; - hist_list[i][0] = '\0'; - } -} - -static void cread_add_to_hist(char *line) -{ - strcpy(hist_list[hist_add_idx], line); - - if (++hist_add_idx >= HIST_MAX) - hist_add_idx = 0; - - if (hist_add_idx > hist_max) - hist_max = hist_add_idx; - - hist_num++; -} - -static char* hist_prev(void) -{ - char *ret; - int old_cur; - - if (hist_cur < 0) - return NULL; - - old_cur = hist_cur; - if (--hist_cur < 0) - hist_cur = hist_max; - - if (hist_cur == hist_add_idx) { - hist_cur = old_cur; - ret = NULL; - } else - ret = hist_list[hist_cur]; - - return (ret); -} - -static char* hist_next(void) -{ - char *ret; - - if (hist_cur < 0) - return NULL; - - if (hist_cur == hist_add_idx) - return NULL; - - if (++hist_cur > hist_max) - hist_cur = 0; - - if (hist_cur == hist_add_idx) { - ret = ""; - } else - ret = hist_list[hist_cur]; - - return (ret); -} - -#ifndef CONFIG_CMDLINE_EDITING -static void cread_print_hist_list(void) -{ - int i; - unsigned long n; - - n = hist_num - hist_max; - - i = hist_add_idx + 1; - while (1) { - if (i > hist_max) - i = 0; - if (i == hist_add_idx) - break; - printf("%s\n", hist_list[i]); - n++; - i++; - } -} -#endif /* CONFIG_CMDLINE_EDITING */ - -#define BEGINNING_OF_LINE() { \ - while (num) { \ - getcmd_putch(CTL_BACKSPACE); \ - num--; \ - } \ -} - -#define ERASE_TO_EOL() { \ - if (num < eol_num) { \ - printf("%*s", (int)(eol_num - num), ""); \ - do { \ - getcmd_putch(CTL_BACKSPACE); \ - } while (--eol_num > num); \ - } \ -} - -#define REFRESH_TO_EOL() { \ - if (num < eol_num) { \ - wlen = eol_num - num; \ - putnstr(buf + num, wlen); \ - num = eol_num; \ - } \ -} - -static void cread_add_char(char ichar, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) -{ - unsigned long wlen; - - /* room ??? */ - if (insert || *num == *eol_num) { - if (*eol_num > len - 1) { - getcmd_cbeep(); - return; - } - (*eol_num)++; - } - - if (insert) { - wlen = *eol_num - *num; - if (wlen > 1) { - memmove(&buf[*num+1], &buf[*num], wlen-1); - } - - buf[*num] = ichar; - putnstr(buf + *num, wlen); - (*num)++; - while (--wlen) { - getcmd_putch(CTL_BACKSPACE); - } - } else { - /* echo the character */ - wlen = 1; - buf[*num] = ichar; - putnstr(buf + *num, wlen); - (*num)++; - } -} - -static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) -{ - while (strsize--) { - cread_add_char(*str, insert, num, eol_num, buf, len); - str++; - } -} - -static int cread_line(const char *const prompt, char *buf, unsigned int *len, - int timeout) -{ - unsigned long num = 0; - unsigned long eol_num = 0; - unsigned long wlen; - char ichar; - int insert = 1; - int esc_len = 0; - char esc_save[8]; - int init_len = strlen(buf); - int first = 1; - - if (init_len) - cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); - - while (1) { -#ifdef CONFIG_BOOT_RETRY_TIME - while (!tstc()) { /* while no incoming data */ - if (retry_time >= 0 && get_ticks() > endtime) - return (-2); /* timed out */ - WATCHDOG_RESET(); - } -#endif - if (first && timeout) { - uint64_t etime = endtick(timeout); - - while (!tstc()) { /* while no incoming data */ - if (get_ticks() >= etime) - return -2; /* timed out */ - WATCHDOG_RESET(); - } - first = 0; - } - - ichar = getcmd_getch(); - - if ((ichar == '\n') || (ichar == '\r')) { - putc('\n'); - break; - } - - /* - * handle standard linux xterm esc sequences for arrow key, etc. - */ - if (esc_len != 0) { - if (esc_len == 1) { - if (ichar == '[') { - esc_save[esc_len] = ichar; - esc_len = 2; - } else { - cread_add_str(esc_save, esc_len, insert, - &num, &eol_num, buf, *len); - esc_len = 0; - } - continue; - } - - switch (ichar) { - - case 'D': /* <- key */ - ichar = CTL_CH('b'); - esc_len = 0; - break; - case 'C': /* -> key */ - ichar = CTL_CH('f'); - esc_len = 0; - break; /* pass off to ^F handler */ - case 'H': /* Home key */ - ichar = CTL_CH('a'); - esc_len = 0; - break; /* pass off to ^A handler */ - case 'A': /* up arrow */ - ichar = CTL_CH('p'); - esc_len = 0; - break; /* pass off to ^P handler */ - case 'B': /* down arrow */ - ichar = CTL_CH('n'); - esc_len = 0; - break; /* pass off to ^N handler */ - default: - esc_save[esc_len++] = ichar; - cread_add_str(esc_save, esc_len, insert, - &num, &eol_num, buf, *len); - esc_len = 0; - continue; - } - } - - switch (ichar) { - case 0x1b: - if (esc_len == 0) { - esc_save[esc_len] = ichar; - esc_len = 1; - } else { - puts("impossible condition #876\n"); - esc_len = 0; - } - break; - - case CTL_CH('a'): - BEGINNING_OF_LINE(); - break; - case CTL_CH('c'): /* ^C - break */ - *buf = '\0'; /* discard input */ - return (-1); - case CTL_CH('f'): - if (num < eol_num) { - getcmd_putch(buf[num]); - num++; - } - break; - case CTL_CH('b'): - if (num) { - getcmd_putch(CTL_BACKSPACE); - num--; - } - break; - case CTL_CH('d'): - if (num < eol_num) { - wlen = eol_num - num - 1; - if (wlen) { - memmove(&buf[num], &buf[num+1], wlen); - putnstr(buf + num, wlen); - } - - getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - eol_num--; - } - break; - case CTL_CH('k'): - ERASE_TO_EOL(); - break; - case CTL_CH('e'): - REFRESH_TO_EOL(); - break; - case CTL_CH('o'): - insert = !insert; - break; - case CTL_CH('x'): - case CTL_CH('u'): - BEGINNING_OF_LINE(); - ERASE_TO_EOL(); - break; - case DEL: - case DEL7: - case 8: - if (num) { - wlen = eol_num - num; - num--; - memmove(&buf[num], &buf[num+1], wlen); - getcmd_putch(CTL_BACKSPACE); - putnstr(buf + num, wlen); - getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - eol_num--; - } - break; - case CTL_CH('p'): - case CTL_CH('n'): - { - char * hline; - - esc_len = 0; - - if (ichar == CTL_CH('p')) - hline = hist_prev(); - else - hline = hist_next(); - - if (!hline) { - getcmd_cbeep(); - continue; - } - - /* nuke the current line */ - /* first, go home */ - BEGINNING_OF_LINE(); - - /* erase to end of line */ - ERASE_TO_EOL(); - - /* copy new line into place and display */ - strcpy(buf, hline); - eol_num = strlen(buf); - REFRESH_TO_EOL(); - continue; - } -#ifdef CONFIG_AUTO_COMPLETE - case '\t': { - int num2, col; - - /* do not autocomplete when in the middle */ - if (num < eol_num) { - getcmd_cbeep(); - break; - } - - buf[num] = '\0'; - col = strlen(prompt) + eol_num; - num2 = num; - if (cmd_auto_complete(prompt, buf, &num2, &col)) { - col = num2 - num; - num += col; - eol_num += col; - } - break; - } -#endif - default: - cread_add_char(ichar, insert, &num, &eol_num, buf, *len); - break; - } - } - *len = eol_num; - buf[eol_num] = '\0'; /* lose the newline */ - - if (buf[0] && buf[0] != CREAD_HIST_CHAR) - cread_add_to_hist(buf); - hist_cur = hist_add_idx; - - return 0; -} - -#endif /* CONFIG_CMDLINE_EDITING */ - /****************************************************************************/ /* - * Prompt for input and read a line. - * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, - * time out when time goes past endtime (timebase time in ticks). - * Return: number of read characters - * -1 if break - * -2 if timed out - */ -int readline (const char *const prompt) -{ - /* - * If console_buffer isn't 0-length the user will be prompted to modify - * it instead of entering it from scratch as desired. - */ - console_buffer[0] = '\0'; - - return readline_into_buffer(prompt, console_buffer, 0); -} - - -int readline_into_buffer(const char *const prompt, char *buffer, int timeout) -{ - char *p = buffer; -#ifdef CONFIG_CMDLINE_EDITING - unsigned int len = CONFIG_SYS_CBSIZE; - int rc; - static int initted = 0; - - /* - * History uses a global array which is not - * writable until after relocation to RAM. - * Revert to non-history version if still - * running from flash. - */ - if (gd->flags & GD_FLG_RELOC) { - if (!initted) { - hist_init(); - initted = 1; - } - - if (prompt) - puts (prompt); - - rc = cread_line(prompt, p, &len, timeout); - return rc < 0 ? rc : len; - - } else { -#endif /* CONFIG_CMDLINE_EDITING */ - char * p_buf = p; - int n = 0; /* buffer index */ - int plen = 0; /* prompt length */ - int col; /* output column cnt */ - char c; - - /* print prompt */ - if (prompt) { - plen = strlen (prompt); - puts (prompt); - } - col = plen; - - for (;;) { -#ifdef CONFIG_BOOT_RETRY_TIME - while (!tstc()) { /* while no incoming data */ - if (retry_time >= 0 && get_ticks() > endtime) - return (-2); /* timed out */ - WATCHDOG_RESET(); - } -#endif - WATCHDOG_RESET(); /* Trigger watchdog, if needed */ - -#ifdef CONFIG_SHOW_ACTIVITY - while (!tstc()) { - show_activity(0); - WATCHDOG_RESET(); - } -#endif - c = getc(); - - /* - * Special character handling - */ - switch (c) { - case '\r': /* Enter */ - case '\n': - *p = '\0'; - puts ("\r\n"); - return p - p_buf; - - case '\0': /* nul */ - continue; - - case 0x03: /* ^C - break */ - p_buf[0] = '\0'; /* discard input */ - return -1; - - case 0x15: /* ^U - erase line */ - while (col > plen) { - puts (erase_seq); - --col; - } - p = p_buf; - n = 0; - continue; - - case 0x17: /* ^W - erase word */ - p=delete_char(p_buf, p, &col, &n, plen); - while ((n > 0) && (*p != ' ')) { - p=delete_char(p_buf, p, &col, &n, plen); - } - continue; - - case 0x08: /* ^H - backspace */ - case 0x7F: /* DEL - backspace */ - p=delete_char(p_buf, p, &col, &n, plen); - continue; - - default: - /* - * Must be a normal character then - */ - if (n < CONFIG_SYS_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ -#ifdef CONFIG_AUTO_COMPLETE - /* if auto completion triggered just continue */ - *p = '\0'; - if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { - p = p_buf + n; /* reset */ - continue; - } -#endif - puts (tab_seq+(col&07)); - col += 8 - (col&07); - } else { - char buf[2]; - - /* - * Echo input using puts() to force an - * LCD flush if we are using an LCD - */ - ++col; - buf[0] = c; - buf[1] = '\0'; - puts(buf); - } - *p++ = c; - ++n; - } else { /* Buffer full */ - putc ('\a'); - } - } - } -#ifdef CONFIG_CMDLINE_EDITING - } -#endif -} - -/****************************************************************************/ - -static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) -{ - char *s; - - if (*np == 0) { - return (p); - } - - if (*(--p) == '\t') { /* will retype the whole line */ - while (*colp > plen) { - puts (erase_seq); - (*colp)--; - } - for (s=buffer; s= 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 */ - if ((argc = parse_line (finaltoken, argv)) == 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; -} -#endif - -/* * Run a command using the selected parser. * * @param cmd Command to run @@ -1436,10 +472,10 @@ int run_command(const char *cmd, int flag) { #ifndef CONFIG_SYS_HUSH_PARSER /* - * builtin_run_command can return 0 or 1 for success, so clean up + * cli_run_command can return 0 or 1 for success, so clean up * its result. */ - if (builtin_run_command(cmd, flag) == -1) + if (cli_simple_run_command(cmd, flag) == -1) return 1; return 0; @@ -1449,49 +485,6 @@ int run_command(const char *cmd, int flag) #endif } -#ifndef CONFIG_SYS_HUSH_PARSER -/** - * Execute a list of command separated by ; or \n using the built-in parser. - * - * This function cannot take a const char * for the command, since if it - * finds newlines in the string, it replaces them with \0. - * - * @param cmd String containing list of commands - * @param flag Execution flags (CMD_FLAG_...) - * @return 0 on success, or != 0 on error. - */ -static int builtin_run_command_list(char *cmd, int flag) -{ - char *line, *next; - int rcode = 0; - - /* - * Break into individual lines, and execute each line; terminate on - * error. - */ - line = next = cmd; - while (*next) { - if (*next == '\n') { - *next = '\0'; - /* run only non-empty commands */ - if (*line) { - debug("** exec: \"%s\"\n", line); - if (builtin_run_command(line, 0) < 0) { - rcode = 1; - break; - } - } - line = next + 1; - } - ++next; - } - if (rcode == 0 && *line) - rcode = (builtin_run_command(line, 0) >= 0); - - return rcode; -} -#endif - int run_command_list(const char *cmd, int len, int flag) { int need_buff = 1; @@ -1525,7 +518,7 @@ 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. */ - rcode = builtin_run_command_list(buff, flag); + rcode = cli_simple_run_command_list(buff, flag); if (need_buff) free(buff); #endif diff --git a/include/cli.h b/include/cli.h index 0075bd4..b04539f 100644 --- a/include/cli.h +++ b/include/cli.h @@ -99,4 +99,9 @@ int readline_into_buffer(const char *const prompt, char *buffer, int timeout); */ int parse_line(char *line, char *argv[]); +/** bootretry_dont_retry() - Indicate that we should not retry the boot */ +void bootretry_dont_retry(void); + +#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) + #endif