#include "cli_readline.h"
#include "common.h"
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <ctype.h>
#include "config.h"
#include "con-utils.h"
#include "print-utils.h"
#include "command.h"
+#include "debug.h"
+#define DEBUG_READLINE 0 /* set to 1 to debug */
+
+#define debug_readline(fmt, args...) \
+ debug_cond(DEBUG_READLINE, fmt, ##args)
+
+
+
+char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
+
+#define CTL_CH(c) ((c) - 'a' + 1)
+#define CTL_BACKSPACE ('\b')
+#define DEL ((char)255)
+#define DEL7 ((char)127)
+
/************************************************************************************************/
/* TODO:
FKEY_TBL_ITEM(5~, KEY_PPAGE), // Previous-page key
FKEY_TBL_ITEM(4~, KEY_END), // End key
FKEY_TBL_ITEM(Z, KEY_BTAB), // Back tab key
+/* */
+FKEY_TBL_ITEM(H, KEY_HOME), // Home key
+FKEY_TBL_ITEM(F, KEY_END), // End key
/* VT400: */
FKEY_TBL_ITEM(11~, KEY_F(1)), // Function key F1
FKEY_TBL_ITEM(12~, KEY_F(2)), // Function key F2
FKEY_TBL_ITEM(13~, KEY_F(3)), // Function key F3
FKEY_TBL_ITEM(14~, KEY_F(4)), // Function key F4
FKEY_TBL_ITEM(15~, KEY_F(5)), // Function key F5
-/* Linux consoe */
+/* Linux console */
FKEY_TBL_ITEM([A, KEY_F(1)), // Function key F1
FKEY_TBL_ITEM([B, KEY_F(2)), // Function key F2
FKEY_TBL_ITEM([C, KEY_F(3)), // Function key F3
static vtparse_state_t state = STATE_GROUND;
char buf[CHB_SIZE+1];
uint8_t param[2];
- uint8_t i_buf;
+ uint8_t i_buf = 0;
uint8_t i_param;
int ch;
state = STATE_GROUND;
break;
case STATE_SS3:
- if (ch == 'F') /* weird */
+ if (ch == 'F')
ch = KEY_END;
+ if (ch == 'H')
+ ch = KEY_HOME;
state = STATE_GROUND;
break;
case STATE_CSI_ENTRY:
/************************************************************************************************/
-
-char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
-
/*
* cmdline-editing related codes from vivi.
* Author: Janghoon Lyu <nandy@mizi.com>
*/
-static void putnstr(char *str, int n)
-{
- /* printf_P(PSTR("%.*s"), (int)n, str) */
- while (n-- && *str)
- putchar(*str++);
-}
-
-
-#define CTL_CH(c) ((c) - 'a' + 1)
-#define CTL_BACKSPACE ('\b')
-#define DEL ((char)255)
-#define DEL7 ((char)127)
-#define getcmd_putch(ch) putchar(ch)
-#define getcmd_getch() vt_parse()
-#define getcmd_cbeep() getcmd_putch('\a')
+char histbuf[CONFIG_SYS_HISTSIZE+1];
+#define HISTBUFE (histbuf+CONFIG_SYS_HISTSIZE)
+static char *hist_head;
+static char *hist_cur;
-struct hist_node_s {
- struct hist_node_s *next;
- char line[];
-};
-typedef struct hist_node_s hist_node;
+static void hist_reset(void)
+{
+ if (hist_head == NULL)
+ hist_head = HISTBUFE;
+ hist_cur = hist_head;
+}
+static char *hist_entry_next(char *p)
+{
+ if (*p)
+ while (*p++);
-static hist_node *hist_head;
-static hist_node *hist_cur;
+ return p;
+}
-static hist_node *hist_search_node(char *line)
+static char *hist_entry_prev(char *p)
{
- hist_node *p = hist_head;
+ if (p == hist_head)
+ return NULL;
+
+ --p;
+ while (p != hist_head && *(p-1) != 0)
+ --p;
- while (p && strcmp(p->line, line))
- p = p->next;
return p;
}
-#if 0
-static hist_node *hist_insert(char *line)
+static char *hist_search_entry(char *line)
{
- hist_node *p = (hist_node *) malloc(sizeof (hist_node) + strlen(line) + 1);
+ char *p = hist_head;
- if (p) {
- strcpy(p->line, line);
- p->next = hist_head;
- hist_head = p;
- }
- return p;
+ while (*p && strcmp(p, line))
+ p = hist_entry_next(p);
+ return *p ? p : NULL;
}
-#endif
-static hist_node *hist_new(char *line)
+static char *hist_delete(size_t amount)
{
- hist_node *p = (hist_node *) malloc(sizeof (hist_node) + strlen(line) + 1);
+ char *p;
- if (p) {
- strcpy(p->line, line);
- p->next = NULL;
+ p = HISTBUFE - amount;
+ if (p < hist_head)
+ p = hist_head;
+
+ while (p > hist_head && *(p-1))
+ --p;
+
+ if (p == hist_head)
+ hist_head = HISTBUFE;
+ else {
+ size_t shift = HISTBUFE - p;
+ size_t len = p - hist_head;
+ hist_head = memmove(hist_head + shift, hist_head, len);
}
- return p;
+ return hist_head;
}
-static hist_node *hist_delete(void)
+static char *hist_delete_entry(char *entry)
{
- hist_node *p = NULL;
- hist_node *q = hist_head;
+ size_t shift = strlen(entry) + 1;
+ size_t len = entry - hist_head;
- if (q) {
- while(q->next) {
- p = q;
- q = q->next;
- }
- free(q);
- if (p)
- p->next = NULL;
- }
- return p;
+ hist_head = memmove(hist_head + shift, hist_head, len);
+
+ return hist_head;
}
-static hist_node *hist_unlink(hist_node *pos)
+
+static char *hist_add_entry(char *line)
{
- hist_node *p = NULL;
- hist_node *q = hist_head;
+ char *p = hist_head;
- while(q && q != pos) {
- p = q;
- q = q->next;
- }
- if (q) {
- if (p)
- p->next = q->next;
- else
- hist_head = q->next;
- q->next = NULL;
- }
- return q;
+ if (p == NULL)
+ p = HISTBUFE;
+
+ char *q = p - strlen(line) - 1;
+ if (q < histbuf)
+ q = histbuf;
+
+ strlcpy(q, line, p - q);
+
+ hist_head = q;
+
+ return hist_head;
}
-static uint_fast8_t hist_count(void)
+static uint_fast8_t hist_get_count(void)
{
- hist_node *p = hist_head;
+ char *p = hist_head;
uint_fast8_t n = 0;
- while (p) {
+ while (*p) {
++n;
- p = p->next;
+ while (*p++);
}
return n;
}
-static hist_node *cread_add_to_hist(char *line)
+static int hist_get_size(void)
{
- hist_node * p;
+ return HISTBUFE - hist_head;
+}
- p = hist_search_node(line);
- if (p)
- hist_unlink(p);
- else
- p = hist_new(line);
+static char *cread_add_to_hist(char *line)
+{
+ char *p;
- if (p) {
- p->next = hist_head;
- hist_head = p;
+ p = hist_search_entry(line);
+ if (p)
+ hist_delete_entry(p);
+ else {
+ size_t free = hist_head - histbuf;
+ if (free < strlen(line) + 1)
+ hist_delete(strlen(line) + 1 - free);
}
+ hist_add_entry(line);
- if (hist_count() > CONFIG_SYS_HIST_MAX)
- hist_delete();
return p;
}
static char *hist_prev(void)
{
- hist_node *p = hist_cur;
+ char *p = hist_cur;
- if (p == NULL)
+ if (*p == '\0')
return NULL;
+ hist_cur = hist_entry_next(p);
- hist_cur = hist_cur->next;
-
- return p->line;
+ return p;
}
static char *hist_next(void)
{
- hist_node *p = NULL;
- hist_node *q = hist_head;
+ char *p = hist_cur;
- if(q == hist_cur)
+ if(p == hist_head)
return NULL;
- while(q->next != hist_cur) {
- p = q;
- q = q->next;
+ p = hist_entry_prev(p);
+ hist_cur = p;
+
+ return p == hist_head ? "" : hist_entry_prev(p);
+}
+
+static char *hist_search_backward(char* buf, uint8_t num)
+{
+ char *p = hist_cur;
+
+ if (*p == '\0')
+ return NULL;
+
+ while (*p && strncmp(p, buf, num))
+ p = hist_entry_next(p);
+
+ if(!strncmp(p, buf, num)) {
+ hist_cur = hist_entry_next(p);
+ return p;
}
- hist_cur = q;
- return p ? p->line : "";
+ return NULL;
}
+static char *hist_search_forward (char* buf, uint8_t num)
+{
+ char *p = hist_cur;
+
+ if(p != hist_head && (p = hist_entry_prev(p)) != NULL) {
+ do {
+ p = hist_entry_prev(p);
+ } while (p && strncmp(p, buf, num) != 0);
-#define BEGINNING_OF_LINE() { \
- while (num) { \
- getcmd_putch(CTL_BACKSPACE); \
- num--; \
- } \
+ //if(!strncmp(p, buf, num)) {
+ if(p) {
+ hist_cur = hist_entry_next(p);
+ return p;
+ }
+ }
+
+ return NULL;
}
-#define ERASE_TO_EOL() { \
- if (num < eol_num) { \
- /* printf_P(PSTR("%*S"), (int)(eol_num - num), PSTR("")); */ \
- print_blanks(eol_num - num); \
- do { \
- getcmd_putch(CTL_BACKSPACE); \
- } while (--eol_num > num); \
- } \
+static void putnstr(char *str, int n)
+{
+ /* printf_P(PSTR("%.*s"), (int)n, str) */
+ while (n-- && *str)
+ putchar(*str++);
+}
+
+static void getcmd_putch(int ch) { putchar(ch);}
+static int getcmd_getch(void) { return vt_parse();}
+static void getcmd_cbeep(void) { getcmd_putch('\a');}
+
+static void beginning_of_line(uint8_t *num)
+{
+ while (*num) {
+ getcmd_putch(CTL_BACKSPACE);
+ (*num)--;
+ }
}
-#define REFRESH_TO_EOL() { \
- if (num < eol_num) { \
- uint_fast8_t wlen = eol_num - num; \
- putnstr(buf + num, wlen); \
- num = eol_num; \
- } \
+static void erase_to_eol(uint_fast8_t *num, uint_fast8_t *eol_num)
+{
+ if (*num < *eol_num) {
+ /* printf_P(PSTR("%*S"), (int)(*eol_num - *num), PSTR("")); */
+ print_blanks(*eol_num - *num);
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (--(*eol_num) > *num);
+ }
+}
+
+static void refresh_to_eol(char *buf, uint_fast8_t *num, uint_fast8_t *eol_num)
+{
+ if (*num < *eol_num) {
+ uint_fast8_t wlen = *eol_num - *num;
+ putnstr(buf + *num, wlen);
+ *num = *eol_num;
+ }
}
static void cread_add_char(char ichar, bool insert, uint_fast8_t *num,
getcmd_putch(CTL_BACKSPACE);
} else {
/* echo the character */
- wlen = 1;
buf[*num] = ichar;
- putnstr(buf + *num, wlen);
+ putnstr(buf + *num, 1);
(*num)++;
}
}
-static void cread_add_str(char *str, uint_fast8_t strsize, bool insert,
- uint_fast8_t *num, uint_fast8_t *eol_num,
- char *buf, uint_fast8_t len)
+static void cread_add_str(char *str, bool insert, uint_fast8_t *num,
+ uint_fast8_t *eol_num, char *buf, uint_fast8_t len)
{
- while (strsize--) {
- cread_add_char(*str, insert, num, eol_num, buf, len);
- str++;
- }
+ char c;
+
+ while ((c = *str++) != '\0')
+ cread_add_char(c, insert, num, eol_num, buf, len);
}
static int cread_line(const FLASH char *const prompt, char *buf,
- uint_fast8_t *len, bool enable_history)
+ uint_fast8_t len, bool enable_history)
{
uint_fast8_t num = 0;
uint_fast8_t eol_num = 0;
(void) prompt;
- uint_fast8_t init_len = strlen(buf);
- if (init_len)
- cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
+ if (buf[0])
+ cread_add_str(buf, 1, &num, &eol_num, buf, len);
+
+ hist_reset();
while (1) {
int ichar = getcmd_getch();
case KEY_HOME:
case CTL_CH('a'):
- BEGINNING_OF_LINE();
+ beginning_of_line(&num);
break;
case CTL_CH('c'): /* ^C - break */
+ putchar('\n');
*buf = '\0'; /* discard input */
return -1;
case KEY_RIGHT:
}
break;
case CTL_CH('k'): /* kill-line */
- ERASE_TO_EOL();
+ erase_to_eol(&num, &eol_num);
break;
case KEY_END:
case CTL_CH('e'):
- REFRESH_TO_EOL();
+ refresh_to_eol(buf, &num, &eol_num);
break;
case KEY_IC:
case CTL_CH('o'):
break;
case CTL_CH('x'):
case CTL_CH('u'): /* kill-whole-line */
- BEGINNING_OF_LINE();
- ERASE_TO_EOL();
+ beginning_of_line(&num);
+ erase_to_eol(&num, &eol_num);
break;
case DEL:
case DEL7:
case 8: /* backward-delete-char */
if (num) {
- uint_fast8_t wlen = eol_num - num;
- num--;
+ uint_fast8_t wlen = eol_num - --num;
+ buf[eol_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--);
+ } while (--wlen);
eol_num--;
}
break;
hline = hist_next();
if (hline) {
- /* nuke the current line */
/* first, go home */
- BEGINNING_OF_LINE();
-
+ beginning_of_line(&num);
+ /* overwrite current line */
+ cread_add_str(hline, 0, &num, &eol_num, buf, len);
/* erase to end of line */
- ERASE_TO_EOL();
+ erase_to_eol(&num, &eol_num);
- /* copy new line into place and display */
- strcpy(buf, hline);
- eol_num = strlen(buf);
- REFRESH_TO_EOL();
} else {
getcmd_cbeep();
}
getcmd_cbeep();
}
break;
-#if 0
case KEY_PPAGE: /* history-search-backward */
case KEY_NPAGE: /* history-search-forward */
if (enable_history) {
char *hline;
-
if (ichar == KEY_PPAGE)
- hline = hist_search_backward();
+ hline = hist_search_backward(buf, num);
else
- hline = hist_search_forward();
+ hline = hist_search_forward(buf, num);
if (hline) {
- /* nuke the current line */
- /* first, go home */
- BEGINNING_OF_LINE();
-
+ uint_fast8_t num2 = num;
+ /* overwrite current line from cursor position */
+ cread_add_str(hline+num, 0, &num2, &eol_num, buf, len);
/* 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();
+ erase_to_eol(&num2, &eol_num);
+ /* cursor back */
+ while (num2-- > num)
+ getcmd_putch(CTL_BACKSPACE);
} else {
getcmd_cbeep();
}
getcmd_cbeep();
}
break;
-#endif
#ifdef CONFIG_AUTO_COMPLETE
case '\t': {
int num2, col;
#endif
default:
if (isprint(ichar))
- cread_add_char(ichar, insert, &num, &eol_num, buf,
- *len);
+ cread_add_char(ichar, insert, &num, &eol_num, buf, len);
break;
}
}
- *len = eol_num;
+ while (eol_num && buf[eol_num-1] == ' ')
+ --eol_num; /* remove trailing blanks */
buf[eol_num] = '\0'; /* lose the newline */
- if (enable_history) {
- if (buf[0])
- hist_cur = cread_add_to_hist(buf);
+ uint_fast8_t i = 0;
+ while (buf[i] == ' ')
+ ++i; /* remove leading blanks */
+ if (i) {
+ eol_num -= i;
+ memmove(buf, buf+i, eol_num+1);
+ }
+
+ debug_readline("### hist_head: %p, hist_cur: %p\n", hist_head, hist_cur);
+ if (enable_history && buf[0]) {
+ cread_add_to_hist(buf);
+ debug_readline("### hist_head: %p, hist_cur: %p, hist_size: %3d, hist_count: %2d\n",
+ hist_head, hist_cur, hist_get_size(), hist_get_count());
}
- return 0;
+ return eol_num;
}
/****************************************************************************/
-static int cli_readline_into_buffer(const FLASH char *const prompt,
- char *buffer, bool enable_history)
-{
- char *p = buffer;
- uint_fast8_t len = CONFIG_SYS_CBSIZE;
- int rc;
-
- if (prompt)
- my_puts_P(prompt);
-
- rc = cread_line(prompt, p, &len, enable_history);
- return rc < 0 ? rc : (int) len;
-}
-
int cli_readline(const FLASH char *const prompt, bool enable_history)
{
/*
*/
console_buffer[0] = '\0';
- return cli_readline_into_buffer(prompt, console_buffer, enable_history);
+ if (prompt)
+ my_puts_P(prompt);
+
+ return cread_line(prompt, console_buffer, CONFIG_SYS_CBSIZE, enable_history);
}