diff options
-rw-r--r-- | linenoise/linenoise.c | 198 |
1 files changed, 116 insertions, 82 deletions
diff --git a/linenoise/linenoise.c b/linenoise/linenoise.c index 02be8a53..1329f546 100644 --- a/linenoise/linenoise.c +++ b/linenoise/linenoise.c @@ -118,6 +118,10 @@ #include <unistd.h> #include "linenoise.h" +#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 +#define LINENOISE_MAX_LINE 4096 +#define LINENOISE_MAX_DISP (LINENOISE_MAX_LINE * 2) + /* The lino_state structure represents the state during line editing. * We pass this state to functions implementing specific editing * functionalities. */ @@ -137,13 +141,15 @@ struct lino_state { int ofd; /* Terminal stdout file descriptor. */ /* Volatile state pertaining to just one linenoise call */ - char *buf; /* Edited line buffer. */ - size_t buflen; /* Edited line buffer size. */ + char buf[LINENOISE_MAX_DISP]; /* Displayed line bufer. */ + char data[LINENOISE_MAX_LINE]; /* True data corresponding to display */ const char *prompt; /* Prompt to display. */ size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldpos; /* Previous refresh cursor position. */ - size_t len; /* Current edited line length. */ + size_t len; /* Current edited line display length. */ + size_t dlen; /* True underlying length. */ + size_t dpos; /* True underlying position. */ size_t cols; /* Number of columns in terminal. */ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ int history_index; /* The history index we are currently editing. */ @@ -176,8 +182,6 @@ typedef unsigned char mem_t; mem_t *chk_malloc(size_t n); mem_t *chk_realloc(mem_t *old, size_t size); -#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 -#define LINENOISE_MAX_LINE 4096 static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static lino_t lino_list = { &lino_list, &lino_list }; static int atexit_registered = 0; /* Register atexit just 1 time. */ @@ -348,6 +352,7 @@ static void free_completions(lino_completions_t *lc) { free(lc->cvec); } +static void sync_data_to_buf(struct lino_state *l); static void refresh_line(struct lino_state *l); /* This is an helper function for edit() and is called when the @@ -361,7 +366,7 @@ static int complete_line(struct lino_state *ls) { int nread, nwritten; char c = 0; - ls->completion_callback(ls->buf, &lc, ls->cb_ctx); + ls->completion_callback(ls->data, &lc, ls->cb_ctx); if (lc.len == 0) { generate_beep(ls); } else { @@ -372,12 +377,12 @@ static int complete_line(struct lino_state *ls) { if (i < lc.len) { struct lino_state saved = *ls; - ls->len = ls->pos = strlen(lc.cvec[i]); - ls->buf = lc.cvec[i]; + ls->dpos = ls->dlen = strlen(lc.cvec[i]); + strncpy(ls->data, lc.cvec[i], sizeof ls->data); + ls->data[sizeof ls->data - 1] = 0; + sync_data_to_buf(ls); refresh_line(ls); - ls->len = saved.len; - ls->pos = saved.pos; - ls->buf = saved.buf; + *ls = saved; } else { refresh_line(ls); } @@ -401,8 +406,10 @@ static int complete_line(struct lino_state *ls) { default: /* Update buffer and return */ if (i < lc.len) { - nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); - ls->len = ls->pos = nwritten; + nwritten = snprintf(ls->data, sizeof ls->data, "%s", lc.cvec[i]); + ls->dpos = ls->dlen = nwritten; + sync_data_to_buf(ls); + refresh_line(ls); } stop = 1; break; @@ -470,6 +477,37 @@ static void ab_free(struct abuf *ab) { free(ab->b); } +/* Convert raw data to display data, and recalculate + display length and position. */ +static void sync_data_to_buf(struct lino_state *l) +{ + char *dptr = l->data, *bptr = l->buf; + + for (;;) { + if (dptr - l->data == l->dpos) + l->pos = bptr - l->buf; + + if (*dptr) { + char ch = *dptr++; + + if (ch < 32) { + *bptr++ = '^'; + *bptr++ = 64 + ch; + } else if (ch == 127) { + *bptr++ = '^'; + *bptr++ = '?'; + } else { + *bptr++ = ch; + } + + continue; + } + break; + } + + l->len = bptr - l->buf; +} + /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, @@ -596,6 +634,8 @@ static void refresh_multiline(struct lino_state *l) { /* Calls the two low level functions refresh_singleline() or * refresh_multiline() according to the selected mode. */ static void refresh_line(struct lino_state *ls) { + sync_data_to_buf(ls); + if (ls->mlmode) refresh_multiline(ls); else @@ -606,13 +646,14 @@ static void refresh_line(struct lino_state *ls) { * * On error writing to the terminal -1 is returned, otherwise 0. */ static int edit_insert(struct lino_state *l, char c) { - if (l->len < l->buflen) { - if (l->len == l->pos) { - l->buf[l->pos] = c; - l->pos++; - l->len++; - l->buf[l->len] = '\0'; - if ((!l->mlmode && l->plen+l->len < l->cols) /* || mlmode */) { + if (l->dlen < sizeof l->data - 1) { + if (l->len == l->dpos) { + l->data[l->dpos] = c; + l->dpos++; + l->dlen++; + l->data[l->dlen] = '\0'; + sync_data_to_buf(l); + if ((!l->mlmode && l->len == l->dlen && l->plen+l->len < l->cols) /* || mlmode */) { /* Avoid a full update of the line in the * trivial case. */ if (write(l->ofd,&c,1) == -1) return -1; @@ -620,11 +661,11 @@ static int edit_insert(struct lino_state *l, char c) { refresh_line(l); } } else { - memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); - l->buf[l->pos] = c; - l->len++; - l->pos++; - l->buf[l->len] = '\0'; + memmove(l->data + l->dpos+1, l->data + l->dpos, l->dlen-l->dpos); + l->data[l->dpos] = c; + l->dlen++; + l->dpos++; + l->data[l->dlen] = '\0'; refresh_line(l); } } @@ -633,32 +674,32 @@ static int edit_insert(struct lino_state *l, char c) { /* Move cursor on the left. */ static void edit_move_left(struct lino_state *l) { - if (l->pos > 0) { - l->pos--; + if (l->dpos > 0) { + l->dpos--; refresh_line(l); } } /* Move cursor on the right. */ static void edit_move_right(struct lino_state *l) { - if (l->pos != l->len) { - l->pos++; + if (l->dpos != l->dlen) { + l->dpos++; refresh_line(l); } } /* Move cursor to the start of the line. */ static void edit_move_home(struct lino_state *l) { - if (l->pos != 0) { - l->pos = 0; + if (l->dpos != 0) { + l->dpos = 0; refresh_line(l); } } /* Move cursor to the end of the line. */ static void edit_move_end(struct lino_state *l) { - if (l->pos != l->len) { - l->pos = l->len; + if (l->dpos != l->dlen) { + l->dpos = l->dlen; refresh_line(l); } } @@ -672,7 +713,7 @@ static void edit_history_next(struct lino_state *l, int dir) { /* Update the current history entry before to * overwrite it with the next one. */ free(l->history[l->history_len - 1 - l->history_index]); - l->history[l->history_len - 1 - l->history_index] = strdup(l->buf); + l->history[l->history_len - 1 - l->history_index] = strdup(l->data); /* Show the new entry */ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; if (l->history_index < 0) { @@ -682,9 +723,9 @@ static void edit_history_next(struct lino_state *l, int dir) { l->history_index = l->history_len-1; return; } - strncpy(l->buf,l->history[l->history_len - 1 - l->history_index],l->buflen); - l->buf[l->buflen-1] = '\0'; - l->len = l->pos = strlen(l->buf); + strncpy(l->data,l->history[l->history_len - 1 - l->history_index], sizeof l->data); + l->data[sizeof l->data - 1] = 0; + l->dpos = l->dlen = strlen(l->data); refresh_line(l); } } @@ -692,21 +733,21 @@ static void edit_history_next(struct lino_state *l, int dir) { /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ static void edit_delete(struct lino_state *l) { - if (l->len > 0 && l->pos < l->len) { - memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); - l->len--; - l->buf[l->len] = '\0'; + if (l->dlen > 0 && l->dpos < l->dlen) { + memmove(l->data + l->dpos, l->data + l->dpos + 1, l->dlen - l->dpos - 1); + l->dlen--; + l->data[l->dlen] = '\0'; refresh_line(l); } } /* Backspace implementation. */ static void edit_backspace(struct lino_state *l) { - if (l->pos > 0 && l->len > 0) { - memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); - l->pos--; - l->len--; - l->buf[l->len] = '\0'; + if (l->dpos > 0 && l->dlen > 0) { + memmove(l->data + l->dpos - 1, l->data + l->dpos, l->dlen - l->dpos); + l->dpos--; + l->dlen--; + l->data[l->dlen] = '\0'; refresh_line(l); } } @@ -714,16 +755,16 @@ static void edit_backspace(struct lino_state *l) { /* Delete the previosu word, maintaining the cursor at the start of the * current word. */ static void edit_delete_prev_word(struct lino_state *l) { - size_t old_pos = l->pos; + size_t odpos = l->dpos; size_t diff; - while (l->pos > 0 && l->buf[l->pos-1] == ' ') - l->pos--; - while (l->pos > 0 && l->buf[l->pos-1] != ' ') - l->pos--; - diff = old_pos - l->pos; - memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); - l->len -= diff; + while (l->dpos > 0 && l->data[l->dpos - 1] == ' ') + l->dpos--; + while (l->dpos > 0 && l->data[l->dpos - 1] != ' ') + l->dpos--; + diff = odpos - l->dpos; + memmove(l->data + l->dpos, l->data + odpos, l->dlen - odpos + 1); + l->dlen -= diff; refresh_line(l); } @@ -735,23 +776,20 @@ static void edit_delete_prev_word(struct lino_state *l) { * when ctrl+d is typed. * * The function returns the length of the current buffer. */ -static int edit(lino_t *l, char *buf, size_t buflen, const char *prompt) +static int edit(lino_t *l, const char *prompt) { /* Populate the linenoise state that we pass to functions implementing * specific editing functionalities. */ - l->buf = buf; - l->buflen = buflen; l->prompt = prompt; l->plen = strlen(prompt); - l->oldpos = l->pos = 0; - l->len = 0; + l->oldpos = l->pos = l->len = 0; + l->dpos = l->dlen = 0; l->cols = get_columns(l->ifd, l->ofd); l->maxrows = 0; l->history_index = 0; /* Buffer starts empty. */ - l->buf[0] = '\0'; - l->buflen--; /* Make sure there is always space for the nulterm */ + l->data[0] = '\0'; /* The latest history entry is always our current buffer, that * initially is just an empty string. */ @@ -808,11 +846,11 @@ static int edit(lino_t *l, char *buf, size_t buflen, const char *prompt) } break; case CTRL_T: /* ctrl-t, swaps current character with previous. */ - if (l->pos > 0 && l->pos < l->len) { - int aux = buf[l->pos-1]; - buf[l->pos-1] = buf[l->pos]; - buf[l->pos] = aux; - if (l->pos != l->len-1) l->pos++; + if (l->dpos > 0 && l->dpos < l->dlen) { + int aux = l->data[l->dpos - 1]; + l->data[l->dpos-1] = l->data[l->dpos]; + l->data[l->dpos] = aux; + if (l->dpos != l->dlen - 1) l->dpos++; refresh_line(l); } break; @@ -887,13 +925,13 @@ static int edit(lino_t *l, char *buf, size_t buflen, const char *prompt) if (edit_insert(l,c)) return -1; break; case CTRL_U: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - l->pos = l->len = 0; + l->data[0] = '\0'; + l->dpos = l->dlen = 0; refresh_line(l); break; case CTRL_K: /* Ctrl+k, delete from current to end of line. */ - buf[l->pos] = '\0'; - l->len = l->pos; + l->data[l->dpos] = '\0'; + l->dlen = l->dpos; refresh_line(l); break; case CTRL_A: /* Ctrl+a, go to the start of the line */ @@ -944,16 +982,14 @@ void lino_print_keycodes(lino_t *l) { /* This function calls the line editing function edit() using * the object's file descriptor set in raw mode. */ -static int go_raw(lino_t *ls, char *buf, size_t buflen, const char *prompt) { +static int go_raw(lino_t *ls, const char *prompt) +{ int count; - if (buflen == 0) { - errno = EINVAL; - return -1; - } if (!isatty(STDIN_FILENO)) { + char buf[LINENOISE_MAX_LINE]; /* Not a tty: read from file / pipe. */ - if (fgets(buf, buflen, stdin) == NULL) return -1; + if (fgets(buf, sizeof buf, stdin) == NULL) return -1; count = strlen(buf); if (count && buf[count-1] == '\n') { count--; @@ -962,7 +998,7 @@ static int go_raw(lino_t *ls, char *buf, size_t buflen, const char *prompt) { } else { /* Interactive editing. */ if (enable_raw_mode(ls) == -1) return -1; - count = edit(ls, buf, buflen, prompt); + count = edit(ls, prompt); disable_raw_mode(ls); printf("\n"); } @@ -1012,11 +1048,9 @@ void lino_free(lino_t *ls) * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ char *linenoise(lino_t *ls, const char *prompt) { - char buf[LINENOISE_MAX_LINE]; - int count; - if (is_unsupported_term()) { size_t len; + char buf[LINENOISE_MAX_LINE]; printf("%s",prompt); fflush(stdout); @@ -1028,9 +1062,9 @@ char *linenoise(lino_t *ls, const char *prompt) { } return strdup(buf); } else { - count = go_raw(ls, buf,LINENOISE_MAX_LINE,prompt); + int count = go_raw(ls, prompt); if (count == -1) return NULL; - return strdup(buf); + return strdup(ls->data); } } |