]> Git Repo - J-u-boot.git/blobdiff - common/cli_readline.c
bloblist: check bloblist with specified buffer size
[J-u-boot.git] / common / cli_readline.c
index 5c158d03b4adf58827e3d876f752cc4ba3c4d11f..2507be2295269a64b9eefe50ba4572c5e4111337 100644 (file)
 #include <bootretry.h>
 #include <cli.h>
 #include <command.h>
+#include <hang.h>
+#include <malloc.h>
 #include <time.h>
 #include <watchdog.h>
+#include <asm/global_data.h>
 
 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
 }
This page took 0.053285 seconds and 4 git commands to generate.