X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=linenoise.c;h=f65d3834170fd0e12ca8e4e9db9a144127ddfcd5;hb=e795d44159bbbd636c599b7827c64e7533fc9334;hp=ba8ab2a3e3af4b2921e2e90a86bb0d0c225772f4;hpb=27ea0ebd4a4d02f7da22d09aa83fe9e971fd864a;p=linenoise.git diff --git a/linenoise.c b/linenoise.c index ba8ab2a..f65d383 100644 --- a/linenoise.c +++ b/linenoise.c @@ -56,10 +56,6 @@ * flickering effect with some slow terminal, but the lesser sequences * the more compatible. * - * CHA (Cursor Horizontal Absolute) - * Sequence: ESC [ n G - * Effect: moves cursor to column n (1 based) - * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line @@ -70,6 +66,10 @@ * Sequence: ESC [ n C * Effect: moves cursor forward of n chars * + * CR (Carriage Return) + * Sequence: \r + * Effect: moves cursor to column 1 + * * The following are used to clear the screen: ESC [ H ESC [ 2 J * This is actually composed of two sequences: * @@ -116,7 +116,7 @@ #else #include #include -#include +#include #define USE_TERMIOS #define HAVE_UNISTD_H #endif @@ -150,6 +150,9 @@ enum { SPECIAL_DELETE = -24, SPECIAL_HOME = -25, SPECIAL_END = -26, + SPECIAL_INSERT = -27, + SPECIAL_PAGE_UP = -28, + SPECIAL_PAGE_DOWN = -29 }; static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; @@ -165,6 +168,7 @@ struct current { int pos; /* Cursor position, measured in chars */ int cols; /* Size of the window, in chars */ const char *prompt; + char *capture; /* Allocated capture buffer, or NULL for none. Always null terminated */ #if defined(USE_TERMIOS) int fd; /* Terminal fd */ #elif defined(USE_WINCONSOLE) @@ -187,6 +191,7 @@ void linenoiseHistoryFree(void) { free(history[j]); free(history); history = NULL; + history_len = 0; } } @@ -204,7 +209,7 @@ static int isUnsupportedTerm(void) { if (term) { int j; for (j = 0; unsupported_term[j]; j++) { - if (strcasecmp(term, unsupported_term[j]) == 0) { + if (strcmp(term, unsupported_term[j]) == 0) { return 1; } } @@ -216,6 +221,7 @@ static int enableRawMode(struct current *current) { struct termios raw; current->fd = STDIN_FILENO; + current->cols = 0; if (!isatty(current->fd) || isUnsupportedTerm() || tcgetattr(current->fd, &orig_termios) == -1) { @@ -233,8 +239,8 @@ fatal: /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); + /* output modes - actually, no need to disable post processing */ + /*raw.c_oflag &= ~(OPOST);*/ /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, @@ -249,8 +255,6 @@ fatal: goto fatal; } rawmode = 1; - - current->cols = 0; return 0; } @@ -268,7 +272,10 @@ static void linenoiseAtExit(void) { linenoiseHistoryFree(); } -/* gcc/glibc insists that we care about the return code of write! */ +/* gcc/glibc insists that we care about the return code of write! + * Clarification: This means that a void-cast like "(void) (EXPR)" + * does not work. + */ #define IGNORE_RC(EXPR) if (EXPR) {} /* This is fdprintf() on some systems, but use a different @@ -286,14 +293,14 @@ static void fd_printf(int fd, const char *format, ...) IGNORE_RC(write(fd, buf, n)); } -static void clearScreen(struct current *current) +void linenoiseClearScreen(void) { - fd_printf(current->fd, "\x1b[H\x1b[2J"); + fd_printf(STDOUT_FILENO, "\x1b[H\x1b[2J"); } static void cursorToLeft(struct current *current) { - fd_printf(current->fd, "\x1b[1G"); + fd_printf(current->fd, "\r"); } static int outputChars(struct current *current, const char *buf, int len) @@ -303,7 +310,7 @@ static int outputChars(struct current *current, const char *buf, int len) static void outputControlChar(struct current *current, char ch) { - fd_printf(current->fd, "\033[7m^%c\033[0m", ch); + fd_printf(current->fd, "\x1b[7m^%c\x1b[0m", ch); } static void eraseEol(struct current *current) @@ -313,7 +320,12 @@ static void eraseEol(struct current *current) static void setCursorPos(struct current *current, int x) { - fd_printf(current->fd, "\x1b[1G\x1b[%dC", x); + if (x == 0) { + cursorToLeft(current); + } + else { + fd_printf(current->fd, "\r\x1b[%dC", x); + } } /** @@ -348,7 +360,7 @@ static int fd_read_char(int fd, int timeout) static int fd_read(struct current *current) { #ifdef USE_UTF8 - char buf[4]; + char buf[MAX_UTF8_LEN]; int n; int i; int c; @@ -357,7 +369,7 @@ static int fd_read(struct current *current) return -1; } n = utf8_charlen(buf[0]); - if (n < 1 || n > 3) { + if (n < 1) { return -1; } for (i = 1; i < n; i++) { @@ -365,7 +377,6 @@ static int fd_read(struct current *current) return -1; } } - buf[n] = 0; /* decode and return the character */ utf8_tounicode(buf, &c); return c; @@ -374,6 +385,101 @@ static int fd_read(struct current *current) #endif } +static int countColorControlChars(const char* prompt) +{ + /* ANSI color control sequences have the form: + * "\x1b" "[" [0-9;]* "m" + * We parse them with a simple state machine. + */ + + enum { + search_esc, + expect_bracket, + expect_trail + } state = search_esc; + int len = 0, found = 0; + char ch; + + /* XXX: Strictly we should be checking utf8 chars rather than + * bytes in case of the extremely unlikely scenario where + * an ANSI sequence is part of a utf8 sequence. + */ + while ((ch = *prompt++) != 0) { + switch (state) { + case search_esc: + if (ch == '\x1b') { + state = expect_bracket; + } + break; + case expect_bracket: + if (ch == '[') { + state = expect_trail; + /* 3 for "\e[ ... m" */ + len = 3; + break; + } + state = search_esc; + break; + case expect_trail: + if ((ch == ';') || ((ch >= '0' && ch <= '9'))) { + /* 0-9, or semicolon */ + len++; + break; + } + if (ch == 'm') { + found += len; + } + state = search_esc; + break; + } + } + + return found; +} + +/** + * Stores the current cursor column in '*cols'. + * Returns 1 if OK, or 0 if failed to determine cursor pos. + */ +static int queryCursor(int fd, int* cols) +{ + /* control sequence - report cursor location */ + fd_printf(fd, "\x1b[6n"); + + /* Parse the response: ESC [ rows ; cols R */ + if (fd_read_char(fd, 100) == 0x1b && + fd_read_char(fd, 100) == '[') { + + int n = 0; + while (1) { + int ch = fd_read_char(fd, 100); + if (ch == ';') { + /* Ignore rows */ + n = 0; + } + else if (ch == 'R') { + /* Got cols */ + if (n != 0 && n < 1000) { + *cols = n; + } + break; + } + else if (ch >= 0 && ch <= '9') { + n = n * 10 + ch - '0'; + } + else { + break; + } + } + return 1; + } + + return 0; +} + +/** + * Updates current->cols with the current window size (width) + */ static int getWindowSize(struct current *current) { struct winsize ws; @@ -388,38 +494,46 @@ static int getWindowSize(struct current *current) * and reading back the cursor position. * Note that this is only done once per call to linenoise rather than * every time the line is refreshed for efficiency reasons. + * + * In more detail, we: + * (a) request current cursor position, + * (b) move cursor far right, + * (c) request cursor position again, + * (d) at last move back to the old position. + * This gives us the width without messing with the externally + * visible cursor position. */ + if (current->cols == 0) { + int here; + current->cols = 80; - /* Move cursor far right and report cursor position */ - fd_printf(current->fd, "\x1b[999G" "\x1b[6n"); - - /* Parse the response: ESC [ rows ; cols R */ - if (fd_read_char(current->fd, 100) == 0x1b && fd_read_char(current->fd, 100) == '[') { - int n = 0; - while (1) { - int ch = fd_read_char(current->fd, 100); - if (ch == ';') { - /* Ignore rows */ - n = 0; - } - else if (ch == 'R') { - /* Got cols */ - if (n != 0 && n < 1000) { - current->cols = n; - } - break; - } - else if (ch >= 0 && ch <= '9') { - n = n * 10 + ch - '0'; - } - else { - break; + /* (a) */ + if (queryCursor (current->fd, &here)) { + /* (b) */ + fd_printf(current->fd, "\x1b[999C"); + + /* (c). Note: If (a) succeeded, then (c) should as well. + * For paranoia we still check and have a fallback action + * for (d) in case of failure.. + */ + if (!queryCursor (current->fd, ¤t->cols)) { + /* (d') Unable to get accurate position data, reset + * the cursor to the far left. While this may not + * restore the exact original position it should not + * be too bad. + */ + fd_printf(current->fd, "\r"); + } else { + /* (d) Reset the cursor back to the original location. */ + if (current->cols > here) { + fd_printf(current->fd, "\x1b[%dD", current->cols - here); } } - } + } /* 1st query failed, doing nothing => default 80 */ } + return 0; } @@ -467,8 +581,14 @@ static int check_special(int fd) c = fd_read_char(fd, 50); if (c == '~') { switch (c2) { + case '2': + return SPECIAL_INSERT; case '3': return SPECIAL_DELETE; + case '5': + return SPECIAL_PAGE_UP; + case '6': + return SPECIAL_PAGE_DOWN; case '7': return SPECIAL_HOME; case '8': @@ -511,24 +631,34 @@ static void disableRawMode(struct current *current) SetConsoleMode(current->inh, orig_consolemode); } -static void clearScreen(struct current *current) +void linenoiseClearScreen(void) { - COORD topleft = { 0, 0 }; - DWORD n; + /* XXX: This is ugly. Should just have the caller pass a handle */ + struct current current; - FillConsoleOutputCharacter(current->outh, ' ', - current->cols * current->rows, topleft, &n); - FillConsoleOutputAttribute(current->outh, - FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, - current->cols * current->rows, topleft, &n); - SetConsoleCursorPosition(current->outh, topleft); + current.outh = GetStdHandle(STD_OUTPUT_HANDLE); + + if (getWindowSize(¤t) == 0) { + COORD topleft = { 0, 0 }; + DWORD n; + + FillConsoleOutputCharacter(current.outh, ' ', + current.cols * current.rows, topleft, &n); + FillConsoleOutputAttribute(current.outh, + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, + current.cols * current.rows, topleft, &n); + SetConsoleCursorPosition(current.outh, topleft); + } } static void cursorToLeft(struct current *current) { - COORD pos = { 0, (SHORT)current->y }; + COORD pos; DWORD n; + pos.X = 0; + pos.Y = (SHORT)current->y; + FillConsoleOutputAttribute(current->outh, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); current->x = 0; @@ -536,19 +666,48 @@ static void cursorToLeft(struct current *current) static int outputChars(struct current *current, const char *buf, int len) { - COORD pos = { (SHORT)current->x, (SHORT)current->y }; + COORD pos; DWORD n; - - WriteConsoleOutputCharacter(current->outh, buf, len, pos, &n); + + pos.Y = (SHORT)current->y; + +#ifdef USE_UTF8 + while ( len > 0 ) { + int c, s; + wchar_t wc; + + s = utf8_tounicode(buf, &c); + + len -= s; + buf += s; + + wc = (wchar_t)c; + + pos.X = (SHORT)current->x; + + /* fixed display utf8 character */ + WriteConsoleOutputCharacterW(current->outh, &wc, 1, pos, &n); + + current->x += utf8_width(c); + } +#else + pos.X = (SHORT)current->x; + + WriteConsoleOutputCharacterA(current->outh, buf, len, pos, &n); current->x += len; +#endif + return 0; } static void outputControlChar(struct current *current, char ch) { - COORD pos = { (SHORT)current->x, (SHORT)current->y }; + COORD pos; DWORD n; + pos.X = (SHORT) current->x; + pos.Y = (SHORT) current->y; + FillConsoleOutputAttribute(current->outh, BACKGROUND_INTENSITY, 2, pos, &n); outputChars(current, "^", 1); outputChars(current, &ch, 1); @@ -556,15 +715,21 @@ static void outputControlChar(struct current *current, char ch) static void eraseEol(struct current *current) { - COORD pos = { (SHORT)current->x, (SHORT)current->y }; + COORD pos; DWORD n; + pos.X = (SHORT) current->x; + pos.Y = (SHORT) current->y; + FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); } static void setCursorPos(struct current *current, int x) { - COORD pos = { (SHORT)x, (SHORT)current->y }; + COORD pos; + + pos.X = (SHORT)x; + pos.Y = (SHORT) current->y; SetConsoleCursorPosition(current->outh, pos); current->x = x; @@ -593,15 +758,23 @@ static int fd_read(struct current *current) return SPECIAL_UP; case VK_DOWN: return SPECIAL_DOWN; + case VK_INSERT: + return SPECIAL_INSERT; case VK_DELETE: return SPECIAL_DELETE; case VK_HOME: return SPECIAL_HOME; case VK_END: return SPECIAL_END; + case VK_PRIOR: + return SPECIAL_PAGE_UP; + case VK_NEXT: + return SPECIAL_PAGE_DOWN; } } /* Note that control characters are already translated in AsciiChar */ + else if (k->wVirtualKeyCode == VK_CONTROL) + continue; else { #ifdef USE_UTF8 return k->uChar.UnicodeChar; @@ -614,6 +787,14 @@ static int fd_read(struct current *current) return -1; } +static int countColorControlChars(const char* prompt) +{ + /* For windows we assume that there are no embedded ansi color + * control sequences. + */ + return 0; +} + static int getWindowSize(struct current *current) { CONSOLE_SCREEN_BUFFER_INFO info; @@ -632,6 +813,7 @@ static int getWindowSize(struct current *current) } #endif +#ifndef utf8_getchars static int utf8_getchars(char *buf, int c) { #ifdef USE_UTF8 @@ -641,6 +823,7 @@ static int utf8_getchars(char *buf, int c) return 1; #endif } +#endif /** * Returns the unicode character at the given offset, @@ -669,12 +852,19 @@ static void refreshLine(const char *prompt, struct current *current) int b; int ch; int n; + int width; + int bufwidth; /* Should intercept SIGWINCH. For now, just get the size every time */ getWindowSize(current); plen = strlen(prompt); - pchars = utf8_strlen(prompt, plen); + pchars = utf8_strwidth(prompt, utf8_strlen(prompt, plen)); + + /* Scan the prompt for embedded ansi color control sequences and + * discount them as characters/columns. + */ + pchars -= countColorControlChars(prompt); /* Account for a line which is too long to fit in the window. * Note that control chars require an extra column @@ -683,7 +873,7 @@ static void refreshLine(const char *prompt, struct current *current) /* How many cols are required to the left of 'pos'? * The prompt, plus one extra for each control char */ - n = pchars + utf8_strlen(buf, current->len); + n = pchars + utf8_strwidth(buf, utf8_strlen(buf, current->len)); b = 0; for (i = 0; i < pos; i++) { b += utf8_tounicode(buf + b, &ch); @@ -692,20 +882,21 @@ static void refreshLine(const char *prompt, struct current *current) } } - /* If too many are need, strip chars off the front of 'buf' - * until it fits. Note that if the current char is a control character, - * we need one extra col. - */ + /* Pluse one if the current char is a control character */ if (current->pos < current->chars && get_char(current, current->pos) < ' ') { n++; } - while (n >= current->cols) { + /* If too many are needed, strip chars off the front of 'buf' + * until it fits. Note that if the current char is a control character, + * we need one extra col. + */ + while (n >= current->cols && pos > 0) { b = utf8_tounicode(buf, &ch); if (ch < ' ') { n--; } - n--; + n -= utf8_width(ch); buf += b; pos--; chars--; @@ -722,13 +913,19 @@ static void refreshLine(const char *prompt, struct current *current) */ b = 0; /* unwritted bytes */ n = 0; /* How many control chars were written */ + width = 0; /* current display width */ + bufwidth = utf8_strwidth(buf, pos); + for (i = 0; i < chars; i++) { int ch; int w = utf8_tounicode(buf + b, &ch); if (ch < ' ') { n++; } - if (pchars + i + n >= current->cols) { + + width += utf8_width(ch); + + if (pchars + width + n >= current->cols) { break; } if (ch < ' ') { @@ -749,7 +946,7 @@ static void refreshLine(const char *prompt, struct current *current) /* Erase to right, move cursor to original position */ eraseEol(current); - setCursorPos(current, pos + pchars + backup); + setCursorPos(current, bufwidth + pchars + backup); } static void set_current(struct current *current, const char *str) @@ -810,7 +1007,7 @@ static int remove_char(struct current *current, int pos) */ static int insert_char(struct current *current, int pos, int ch) { - char buf[3]; + char buf[MAX_UTF8_LEN]; int n = utf8_getchars(buf, ch); if (has_room(current, n) && pos >= 0 && pos <= current->chars) { @@ -843,19 +1040,68 @@ static int insert_char(struct current *current, int pos, int ch) } /** + * Captures up to 'n' characters starting at 'pos' for the cut buffer. + * + * This replaces any existing characters in the cut buffer. + */ +static void capture_chars(struct current *current, int pos, int n) +{ + if (pos >= 0 && (pos + n - 1) < current->chars) { + int p1 = utf8_index(current->buf, pos); + int nbytes = utf8_index(current->buf + p1, n); + + if (nbytes) { + free(current->capture); + /* Include space for the null terminator */ + current->capture = (char *)malloc(nbytes + 1); + memcpy(current->capture, current->buf + p1, nbytes); + current->capture[nbytes] = '\0'; + } + } +} + +/** + * Removes up to 'n' characters at cursor position 'pos'. + * * Returns 0 if no chars were removed or non-zero otherwise. */ static int remove_chars(struct current *current, int pos, int n) { int removed = 0; + + /* First save any chars which will be removed */ + capture_chars(current, pos, n); + while (n-- && remove_char(current, pos)) { removed++; } return removed; } +/** + * Inserts the characters (string) 'chars' at the cursor position 'pos'. + * + * Returns 0 if no chars were inserted or non-zero otherwise. + */ +static int insert_chars(struct current *current, int pos, const char *chars) +{ + int inserted = 0; + + while (*chars) { + int ch; + int n = utf8_tounicode(chars, &ch); + if (insert_char(current, pos, ch) == 0) { + break; + } + inserted++; + pos++; + chars += n; + } + return inserted; +} #ifndef NO_COMPLETION static linenoiseCompletionCallback *completionCallback = NULL; +static void *completionUserdata = NULL; static void beep() { #ifdef USE_TERMIOS @@ -875,7 +1121,7 @@ static int completeLine(struct current *current) { linenoiseCompletions lc = { 0, NULL }; int c = 0; - completionCallback(current->buf,&lc); + completionCallback(current->buf,&lc,completionUserdata); if (lc.len == 0) { beep(); } else { @@ -925,9 +1171,14 @@ static int completeLine(struct current *current) { return c; /* Return last read character */ } -/* Register a callback function to be called for tab-completion. */ -void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { +/* Register a callback function to be called for tab-completion. + Returns the prior callback so that the caller may (if needed) + restore it when done. */ +linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn, void *userdata) { + linenoiseCompletionCallback * old = completionCallback; completionCallback = fn; + completionUserdata = userdata; + return old; } void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { @@ -937,7 +1188,7 @@ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { #endif -static int linenoisePrompt(struct current *current) { +static int linenoiseEdit(struct current *current) { int history_index = 0; /* The latest history entry is always our current buffer, that @@ -955,7 +1206,7 @@ static int linenoisePrompt(struct current *current) { /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ - if (c == 9 && completionCallback != NULL) { + if (c == '\t' && current->pos == current->chars && completionCallback != NULL) { c = completeLine(current); /* Return on errors */ if (c < 0) return current->len; @@ -998,7 +1249,12 @@ process_char: refreshLine(current->prompt, current); } break; - case ctrl('W'): /* ctrl-w */ + case SPECIAL_INSERT: + /* Ignore. Expansion Hook. + * Future possibility: Toggle Insert/Overwrite Modes + */ + break; + case ctrl('W'): /* ctrl-w, delete word at left. save deleted chars */ /* eat any spaces on the left */ { int pos = current->pos; @@ -1064,7 +1320,8 @@ process_char: skipsame = 1; } else if (c >= ' ') { - if (rlen >= (int)sizeof(rbuf) + 3) { + /* >= here to allow for null terminator */ + if (rlen >= (int)sizeof(rbuf) - MAX_UTF8_LEN) { continue; } @@ -1118,15 +1375,17 @@ process_char: } break; case ctrl('T'): /* ctrl-t */ - if (current->pos > 0 && current->pos < current->chars) { - c = get_char(current, current->pos); - remove_char(current, current->pos); + if (current->pos > 0 && current->pos <= current->chars) { + /* If cursor is at end, transpose the previous two chars */ + int fixer = (current->pos == current->chars); + c = get_char(current, current->pos - fixer); + remove_char(current, current->pos - fixer); insert_char(current, current->pos - 1, c); refreshLine(current->prompt, current); } break; case ctrl('V'): /* ctrl-v */ - if (has_room(current, 3)) { + if (has_room(current, MAX_UTF8_LEN)) { /* Insert the ^V first */ if (insert_char(current, current->pos, c)) { refreshLine(current->prompt, current); @@ -1157,26 +1416,34 @@ process_char: refreshLine(current->prompt, current); } break; + case SPECIAL_PAGE_UP: + dir = history_len - history_index - 1; /* move to start of history */ + goto history_navigation; + case SPECIAL_PAGE_DOWN: + dir = -history_index; /* move to 0 == end of history, i.e. current */ + goto history_navigation; case ctrl('P'): case SPECIAL_UP: dir = 1; + goto history_navigation; case ctrl('N'): case SPECIAL_DOWN: +history_navigation: if (history_len > 1) { /* Update the current history entry before to * overwrite it with tne next one. */ - free(history[history_len-1-history_index]); - history[history_len-1-history_index] = strdup(current->buf); + free(history[history_len - 1 - history_index]); + history[history_len - 1 - history_index] = strdup(current->buf); /* Show the new entry */ history_index += dir; if (history_index < 0) { history_index = 0; break; } else if (history_index >= history_len) { - history_index = history_len-1; + history_index = history_len - 1; break; } - set_current(current, history[history_len-1-history_index]); + set_current(current, history[history_len - 1 - history_index]); refreshLine(current->prompt, current); } break; @@ -1190,18 +1457,23 @@ process_char: current->pos = current->chars; refreshLine(current->prompt, current); break; - case ctrl('U'): /* Ctrl+u, delete to beginning of line. */ + case ctrl('U'): /* Ctrl+u, delete to beginning of line, save deleted chars. */ if (remove_chars(current, 0, current->pos)) { refreshLine(current->prompt, current); } break; - case ctrl('K'): /* Ctrl+k, delete from current to end of line. */ + case ctrl('K'): /* Ctrl+k, delete from current to end of line, save deleted chars. */ if (remove_chars(current, current->pos, current->chars - current->pos)) { refreshLine(current->prompt, current); } break; + case ctrl('Y'): /* Ctrl+y, insert saved chars at current position */ + if (current->capture && insert_chars(current, current->pos, current->capture)) { + refreshLine(current->prompt, current); + } + break; case ctrl('L'): /* Ctrl+L, clear screen */ - clearScreen(current); + linenoiseClearScreen(); /* Force recalc of window size for serial terminals */ current->cols = 0; refreshLine(current->prompt, current); @@ -1219,6 +1491,15 @@ process_char: return current->len; } +int linenoiseColumns(void) +{ + struct current current; + enableRawMode (¤t); + getWindowSize (¤t); + disableRawMode (¤t); + return current.cols; +} + char *linenoise(const char *prompt) { int count; @@ -1226,10 +1507,10 @@ char *linenoise(const char *prompt) char buf[LINENOISE_MAX_LINE]; if (enableRawMode(¤t) == -1) { - printf("%s", prompt); + printf("%s", prompt); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) { - return NULL; + return NULL; } count = strlen(buf); if (count && buf[count-1] == '\n') { @@ -1245,10 +1526,14 @@ char *linenoise(const char *prompt) current.chars = 0; current.pos = 0; current.prompt = prompt; + current.capture = NULL; + + count = linenoiseEdit(¤t); - count = linenoisePrompt(¤t); disableRawMode(¤t); printf("\n"); + + free(current.capture); if (count == -1) { return NULL; } @@ -1284,6 +1569,10 @@ int linenoiseHistoryAdd(const char *line) { return 1; } +int linenoiseHistoryGetMaxLen(void) { + return history_max_len; +} + int linenoiseHistorySetMaxLen(int len) { char **newHistory; @@ -1293,8 +1582,16 @@ int linenoiseHistorySetMaxLen(int len) { newHistory = (char **)malloc(sizeof(char*)*len); if (newHistory == NULL) return 0; - if (len < tocopy) tocopy = len; - memcpy(newHistory,history+(history_max_len-tocopy), sizeof(char*)*tocopy); + + /* If we can't copy everything, free the elements we'll not use. */ + if (len < tocopy) { + int j; + + for (j = 0; j < tocopy-len; j++) free(history[j]); + tocopy = len; + } + memset(newHistory,0,sizeof(char*)*len); + memcpy(newHistory,history+(history_len-tocopy), sizeof(char*)*tocopy); free(history); history = newHistory; }