From: Michal Sojka Date: Thu, 1 Aug 2013 09:08:17 +0000 (+0200) Subject: cmdproc: Add history handling X-Git-Url: http://rtime.felk.cvut.cz/gitweb/sysless.git/commitdiff_plain/a64937815f12fabe7ab94b61e73281da70ab7dac cmdproc: Add history handling Now, command processor can remember several last entered lines. The user can naviagte the history with Up/Down or Ctrl-P/Ctrl-N keys. When hist and hist_size fields of ed_line are zero, the behavior of the cmdproc should be the same as without this change. --- diff --git a/libs4c/cmdproc/cmd_io_line.c b/libs4c/cmdproc/cmd_io_line.c index 7209d0f..7ad0ec8 100644 --- a/libs4c/cmdproc/cmd_io_line.c +++ b/libs4c/cmdproc/cmd_io_line.c @@ -20,6 +20,7 @@ #include #include "cmd_proc_priv.h" #include +#include /* cmd_io line editor */ @@ -90,6 +91,80 @@ int cmd_io_line_out(cmd_io_t *cmd_io) return 1; } +static void replace_history(ed_line_buf_t *el, cmd_io_t* io_stack) +{ + if(!el->lastch) + el->inbuf=0; /* Make inbuf consistent */ + while (el->inbuf) { /* Delete previous input */ + el->inbuf--; + while (cmd_io_putc(io_stack,'\b') < 0); + } + if (el->hist_idx >= 0) { + strncpy(el->buf, el->hist + el->hist_idx*el->alloc, el->alloc); + while (el->buf[el->inbuf]) { + el->lastch = el->buf[el->inbuf++]; + while (cmd_io_putc(io_stack, el->lastch) < 0); + } + } + cmd_io_puts(io_stack, "\033[K"); /* Erase End of Line */ +} + +/** + * @return One when the character was consumed, zero otherwise. + */ +static int handle_history(ed_line_buf_t *el, char ch, cmd_io_t* io_stack) +{ + enum { NONE, UP, DOWN } dir = NONE; + + if (!(el->hist && el->hist_size && el->hist_size >= 2*el->alloc)) + return 0; + + switch (ch) { + case 14: /* Ctrl-N */ + dir = DOWN; + break; + case 16: /* Ctrl-P */ + dir = UP; + break; + case '\033': /* ESC */ + strcpy(el->esc, "\033"); + return 1; + default: + if (!el->esc[0]) + return 0; + + char *p = el->esc+1; + while (*p && p < el->esc + sizeof(el->esc) - 2) p++; + if (!*p) { + p[0] = ch; + p[1] = 0; + } + if (isalpha(ch)) { + if (strcmp(el->esc, "\033[A") == 0) dir = UP; + else if (strcmp(el->esc, "\033[B") == 0) dir = DOWN; + el->esc[0] = 0; + } + if (dir == NONE) + return 1; + } + + if (dir == DOWN) { + if (el->hist_idx >= 0) + el->hist_idx--; + else + return 1; + } else if (dir == UP) { + if ((el->hist_idx+2) * el->alloc <= el->hist_size && + el->hist[(el->hist_idx+1) * el->alloc]) + el->hist_idx++; + else + return 1; + } + + replace_history(el, io_stack); + return 1; +} + /* process input & perform echo if requested */ int cmd_io_line_in(cmd_io_t *cmd_io) { @@ -102,6 +177,8 @@ int cmd_io_line_in(cmd_io_t *cmd_io) while((ch=cmd_io_getc(io_stack))>=0){ // DPRINT("Added %c (%d)\n", ch, ch); + if (handle_history(el, ch, io_stack) == 1) + continue; int eol = cmd_ed_line_buf(el,ch); if (eol == -1) return -1; @@ -110,6 +187,14 @@ int cmd_io_line_in(cmd_io_t *cmd_io) while(cmd_io_putc(io_stack,'\r')<0); while(cmd_io_putc(io_stack,'\n')<0); } + if (el->hist && el->hist_size) { + el->hist_idx = -1; + if ((el->hist[0] && strcmp(el->buf, el->hist) == 0) || /* The same command as before */ + !el->buf[0]) /* Empty line */ + return 1; /* Don't add to the history */ + memmove(el->hist + el->alloc, el->hist, el->hist_size - el->alloc); + strncpy(el->hist, el->buf, el->alloc); + } return 1; } else diff --git a/libs4c/cmdproc/cmd_proc.h b/libs4c/cmdproc/cmd_proc.h index db6d423..badbe6a 100644 --- a/libs4c/cmdproc/cmd_proc.h +++ b/libs4c/cmdproc/cmd_proc.h @@ -48,6 +48,10 @@ typedef struct{ * If FL_ELB_INSEND is set, lastch is * index of last sent char. */ char *buf; + char *hist; /* Previous input lines (history) */ + int hist_size; /* Size of the history buffer (must be at least 2*alloc) */ + int hist_idx; /* Index of the last selected history line or -1 if no history was yet selected */ + char esc[6]; /* Buffer for ESC sequence processing */ } ed_line_buf_t; /* Structure for character input output. It is used either for direct diff --git a/libs4c/cmdproc/cmdio_std_line.c b/libs4c/cmdproc/cmdio_std_line.c index 63cb615..48f366f 100644 --- a/libs4c/cmdproc/cmdio_std_line.c +++ b/libs4c/cmdproc/cmdio_std_line.c @@ -14,6 +14,7 @@ #define ED_LINE_CHARS 80 char ed_line_in_std[ED_LINE_CHARS+1]; +char ed_line_in_std_history[10*(ED_LINE_CHARS+1)]; char ed_line_out_std[ED_LINE_CHARS+1]; @@ -23,7 +24,9 @@ ed_line_buf_t ed_line_buf_in_std={ alloc:sizeof(ed_line_in_std), maxlen:0, lastch:0, - buf:ed_line_in_std + buf:ed_line_in_std, + hist:ed_line_in_std_history, + hist_size:sizeof(ed_line_in_std_history), }; ed_line_buf_t ed_line_buf_out_std={