X-Git-Url: https://repo.jachan.dev/J-u-boot.git/blobdiff_plain/b5b0237d0216db34605ca54b83588fcfcf5e63a8..67254214930cd2cb52279b01690c1f820a7f83db:/common/cli_readline.c diff --git a/common/cli_readline.c b/common/cli_readline.c index 5c158d03b4a..2507be22952 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -12,8 +12,11 @@ #include #include #include +#include +#include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -61,7 +64,6 @@ static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) #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) @@ -71,8 +73,13 @@ static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) #define getcmd_getch() getchar() #define getcmd_cbeep() getcmd_putch('\a') +#ifdef CONFIG_SPL_BUILD +#define HIST_MAX 3 +#define HIST_SIZE 32 +#else #define HIST_MAX 20 #define HIST_SIZE CONFIG_SYS_CBSIZE +#endif static int hist_max; static int hist_add_idx; @@ -80,12 +87,20 @@ 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) +static void getcmd_putchars(int count, int ch) +{ + int i; + + for (i = 0; i < count; i++) + getcmd_putch(ch); +} + +static int hist_init(void) { + unsigned char *hist; int i; hist_max = 0; @@ -93,10 +108,14 @@ static void hist_init(void) hist_cur = -1; hist_num = 0; - for (i = 0; i < HIST_MAX; i++) { - hist_list[i] = hist_lines[i]; - hist_list[i][0] = '\0'; - } + hist = calloc(HIST_MAX, HIST_SIZE + 1); + if (!hist) + return -ENOMEM; + + for (i = 0; i < HIST_MAX; i++) + hist_list[i] = hist + (i * (HIST_SIZE + 1)); + + return 0; } static void cread_add_to_hist(char *line) @@ -155,11 +174,10 @@ static char *hist_next(void) return ret; } -#ifndef CONFIG_CMDLINE_EDITING -static void cread_print_hist_list(void) +void cread_print_hist_list(void) { int i; - unsigned long n; + uint n; n = hist_num - hist_max; @@ -174,36 +192,35 @@ static void cread_print_hist_list(void) i++; } } -#endif /* CONFIG_CMDLINE_EDITING */ #define BEGINNING_OF_LINE() { \ - while (num) { \ + while (cls->num) { \ getcmd_putch(CTL_BACKSPACE); \ - num--; \ + cls->num--; \ } \ } #define ERASE_TO_EOL() { \ - if (num < eol_num) { \ - printf("%*s", (int)(eol_num - num), ""); \ + if (cls->num < cls->eol_num) { \ + printf("%*s", (int)(cls->eol_num - cls->num), ""); \ do { \ getcmd_putch(CTL_BACKSPACE); \ - } while (--eol_num > num); \ + } while (--cls->eol_num > cls->num); \ } \ } -#define REFRESH_TO_EOL() { \ - if (num < eol_num) { \ - wlen = eol_num - num; \ - putnstr(buf + num, wlen); \ - num = eol_num; \ - } \ +#define REFRESH_TO_EOL() { \ + if (cls->num < cls->eol_num) { \ + uint wlen = cls->eol_num - cls->num; \ + putnstr(buf + cls->num, wlen); \ + cls->num = cls->eol_num; \ + } \ } -static void cread_add_char(char ichar, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) +static void cread_add_char(char ichar, int insert, uint *num, + uint *eol_num, char *buf, uint len) { - unsigned long wlen; + uint wlen; /* room ??? */ if (insert || *num == *eol_num) { @@ -234,8 +251,7 @@ static void cread_add_char(char ichar, int insert, unsigned long *num, } static void cread_add_str(char *str, int strsize, int insert, - unsigned long *num, unsigned long *eol_num, - char *buf, unsigned long len) + uint *num, uint *eol_num, char *buf, uint len) { while (strsize--) { cread_add_char(*str, insert, num, eol_num, buf, len); @@ -243,211 +259,117 @@ static void cread_add_str(char *str, int strsize, int insert, } } -static int cread_line(const char *const prompt, char *buf, unsigned int *len, - int timeout) +int cread_line_process_ch(struct cli_line_state *cls, char ichar) { - 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; + char *buf = cls->buf; - if (init_len) - cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); + /* ichar=0x0 when error occurs in U-Boot getc */ + if (!ichar) + return -EAGAIN; - while (1) { - if (bootretry_tstc_timeout()) - return -2; /* timed out */ - 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(); - - /* ichar=0x0 when error occurs in U-Boot getc */ - if (!ichar) - continue; + if (ichar == '\n') { + putc('\n'); + buf[cls->eol_num] = '\0'; /* terminate the string */ + return 0; + } - if ((ichar == '\n') || (ichar == '\r')) { - putc('\n'); - break; + switch (ichar) { + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -EINTR; + case CTL_CH('f'): + if (cls->num < cls->eol_num) { + getcmd_putch(buf[cls->num]); + cls->num++; } - - /* - * handle standard linux xterm esc sequences for arrow key, etc. - */ - if (esc_len != 0) { - enum { ESC_REJECT, ESC_SAVE, ESC_CONVERTED } act = ESC_REJECT; - - if (esc_len == 1) { - if (ichar == '[' || ichar == 'O') - act = ESC_SAVE; - } else if (esc_len == 2) { - switch (ichar) { - case 'D': /* <- key */ - ichar = CTL_CH('b'); - act = ESC_CONVERTED; - break; /* pass off to ^B handler */ - case 'C': /* -> key */ - ichar = CTL_CH('f'); - act = ESC_CONVERTED; - break; /* pass off to ^F handler */ - case 'H': /* Home key */ - ichar = CTL_CH('a'); - act = ESC_CONVERTED; - break; /* pass off to ^A handler */ - case 'F': /* End key */ - ichar = CTL_CH('e'); - act = ESC_CONVERTED; - break; /* pass off to ^E handler */ - case 'A': /* up arrow */ - ichar = CTL_CH('p'); - act = ESC_CONVERTED; - break; /* pass off to ^P handler */ - case 'B': /* down arrow */ - ichar = CTL_CH('n'); - act = ESC_CONVERTED; - break; /* pass off to ^N handler */ - case '1': - case '3': - case '4': - case '7': - case '8': - if (esc_save[1] == '[') { - /* see if next character is ~ */ - act = ESC_SAVE; - } - break; - } - } else if (esc_len == 3) { - if (ichar == '~') { - switch (esc_save[2]) { - case '3': /* Delete key */ - ichar = CTL_CH('d'); - act = ESC_CONVERTED; - break; /* pass to ^D handler */ - case '1': /* Home key */ - case '7': - ichar = CTL_CH('a'); - act = ESC_CONVERTED; - break; /* pass to ^A handler */ - case '4': /* End key */ - case '8': - ichar = CTL_CH('e'); - act = ESC_CONVERTED; - break; /* pass to ^E handler */ - } - } - } - - switch (act) { - case ESC_SAVE: - esc_save[esc_len++] = ichar; - continue; - case ESC_REJECT: - esc_save[esc_len++] = ichar; - cread_add_str(esc_save, esc_len, insert, - &num, &eol_num, buf, *len); - esc_len = 0; - continue; - case ESC_CONVERTED: - esc_len = 0; - break; - } + break; + case CTL_CH('b'): + if (cls->num) { + getcmd_putch(CTL_BACKSPACE); + cls->num--; } - - 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('d'): + if (cls->num < cls->eol_num) { + uint wlen; + + wlen = cls->eol_num - cls->num - 1; + if (wlen) { + memmove(&buf[cls->num], &buf[cls->num + 1], + wlen); + putnstr(buf + cls->num, wlen); } - 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(' '); + do { 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); + } while (wlen--); + cls->eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + cls->insert = !cls->insert; + break; + case CTL_CH('w'): + if (cls->num) { + uint base, wlen; + + for (base = cls->num - 1; + base >= 0 && buf[base] == ' ';) + base--; + for (; base > 0 && buf[base - 1] != ' ';) + base--; + + /* now delete chars from base to cls->num */ + wlen = cls->num - base; + cls->eol_num -= wlen; + memmove(&buf[base], &buf[cls->num], + cls->eol_num - base + 1); + cls->num = base; + getcmd_putchars(wlen, CTL_BACKSPACE); + puts(buf + base); + getcmd_putchars(wlen, ' '); + getcmd_putchars(wlen + cls->eol_num - cls->num, + CTL_BACKSPACE); + } + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (cls->num) { + uint wlen; + + wlen = cls->eol_num - cls->num; + cls->num--; + memmove(&buf[cls->num], &buf[cls->num + 1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + cls->num, wlen); + getcmd_putch(' '); + do { 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'): - { + } while (wlen--); + cls->eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + if (cls->history) { char *hline; - esc_len = 0; - if (ichar == CTL_CH('p')) hline = hist_prev(); else @@ -455,7 +377,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, if (!hline) { getcmd_cbeep(); - continue; + break; } /* nuke the current line */ @@ -467,41 +389,106 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, /* copy new line into place and display */ strcpy(buf, hline); - eol_num = strlen(buf); + cls->eol_num = strlen(buf); REFRESH_TO_EOL(); - continue; + break; } -#ifdef CONFIG_AUTO_COMPLETE - case '\t': { + break; + case '\t': + if (IS_ENABLED(CONFIG_AUTO_COMPLETE) && cls->cmd_complete) { int num2, col; /* do not autocomplete when in the middle */ - if (num < eol_num) { + if (cls->num < cls->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; + buf[cls->num] = '\0'; + col = strlen(cls->prompt) + cls->eol_num; + num2 = cls->num; + if (cmd_auto_complete(cls->prompt, buf, &num2, &col)) { + col = num2 - cls->num; + cls->num += col; + cls->eol_num += col; } break; } -#endif - default: - if (ichar >= ' ' && ichar <= '~') { - cread_add_char(ichar, insert, &num, &eol_num, - buf, *len); + fallthrough; + default: + cread_add_char(ichar, cls->insert, &cls->num, &cls->eol_num, + buf, cls->len); + break; + } + + /* + * keep the string terminated...if we added a char at the end then we + * want a \0 after it + */ + buf[cls->eol_num] = '\0'; + + return -EAGAIN; +} + +void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size) +{ + int init_len = strlen(buf); + + memset(cls, '\0', sizeof(struct cli_line_state)); + cls->insert = true; + cls->buf = buf; + cls->len = buf_size; + + if (init_len) + cread_add_str(buf, init_len, 0, &cls->num, &cls->eol_num, buf, + buf_size); +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + struct cli_ch_state s_cch, *cch = &s_cch; + struct cli_line_state s_cls, *cls = &s_cls; + char ichar; + int first = 1; + + cli_ch_init(cch); + cli_cread_init(cls, buf, *len); + cls->prompt = prompt; + cls->history = true; + cls->cmd_complete = true; + + while (1) { + int ret; + + /* Check for saved characters */ + ichar = cli_ch_process(cch, 0); + + if (!ichar) { + if (bootretry_tstc_timeout()) + return -2; /* timed out */ + if (first && timeout) { + u64 etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + schedule(); + } + first = 0; } - break; + + ichar = getcmd_getch(); + ichar = cli_ch_process(cch, ichar); } + + ret = cread_line_process_ch(cls, ichar); + if (ret == -EINTR) + return -1; + else if (!ret) + break; } - *len = eol_num; - buf[eol_num] = '\0'; /* lose the newline */ + *len = cls->eol_num; if (buf[0] && buf[0] != CREAD_HIST_CHAR) cread_add_to_hist(buf); @@ -510,6 +497,19 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, return 0; } +#else /* !CONFIG_CMDLINE_EDITING */ + +static inline int hist_init(void) +{ + return 0; +} + +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) +{ + return 0; +} + #endif /* CONFIG_CMDLINE_EDITING */ /****************************************************************************/ @@ -525,41 +525,22 @@ int cli_readline(const char *const prompt) return cli_readline_into_buffer(prompt, console_buffer, 0); } - -int cli_readline_into_buffer(const char *const prompt, char *buffer, - int timeout) +/** + * cread_line_simple() - Simple (small) command-line reader + * + * This supports only basic editing, with no cursor movement + * + * @prompt: Prompt to display + * @p: Text buffer to edit + * Return: length of text buffer, or -1 if input was cannncelled (Ctrl-C) + */ +static int cread_line_simple(const char *const prompt, char *p) { - 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; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + int c; /* print prompt */ if (prompt) { @@ -571,7 +552,7 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, for (;;) { if (bootretry_tstc_timeout()) return -2; /* timed out */ - WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + schedule(); /* Trigger watchdog, if needed */ c = getchar(); @@ -613,14 +594,15 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, continue; default: - /* - * Must be a normal character then - */ - if (n < CONFIG_SYS_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ -#ifdef CONFIG_AUTO_COMPLETE + /* Must be a normal character then */ + if (n >= CONFIG_SYS_CBSIZE - 2) { /* Buffer full */ + putc('\a'); + break; + } + if (c == '\t') { /* expand TABs */ + if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) { /* - * if auto completion triggered just + * if auto-completion triggered just * continue */ *p = '\0'; @@ -630,29 +612,56 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, p = p_buf + n; /* reset */ continue; } -#endif - puts(tab_seq + (col & 07)); - col += 8 - (col & 07); - } else { - char __maybe_unused 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'); + puts(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + char __maybe_unused 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; + break; } } -#ifdef CONFIG_CMDLINE_EDITING +} + +int cli_readline_into_buffer(const char *const prompt, char *buffer, + int timeout) +{ + char *p = buffer; + uint 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 (IS_ENABLED(CONFIG_CMDLINE_EDITING) && (gd->flags & GD_FLG_RELOC)) { + if (!initted) { + rc = hist_init(); + if (rc == 0) + initted = 1; + } + + if (prompt) + puts(prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { + return cread_line_simple(prompt, p); } -#endif }