diff options
author | Mathias Stearn <mathias@10gen.com> | 2011-04-11 18:54:09 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2011-04-11 19:00:15 -0400 |
commit | 00c383f6c0551f5b1748a29d00a8a77b52d72a16 (patch) | |
tree | 97903ef920eda015f5edb5b7b6ce5f5b454d424c /third_party | |
parent | 73126163cc233828fa724d12ff0d197dcf3b41e3 (diff) | |
download | mongo-00c383f6c0551f5b1748a29d00a8a77b52d72a16.tar.gz |
first pass at merging posix and win32 linenoise codebases SERVER-1763
Diffstat (limited to 'third_party')
-rw-r--r-- | third_party/linenoise/linenoise.cpp | 305 |
1 files changed, 218 insertions, 87 deletions
diff --git a/third_party/linenoise/linenoise.cpp b/third_party/linenoise/linenoise.cpp index b752ab95caf..70fdbaf7137 100644 --- a/third_party/linenoise/linenoise.cpp +++ b/third_party/linenoise/linenoise.cpp @@ -82,6 +82,26 @@ * */ +#ifdef _WIN32 + +#include <conio.h> +#include <windows.h> +#include <stdio.h> +#include <io.h> +#include <errno.h> +#define snprintf _snprintf +#define strcasecmp _stricmp +#define strdup _strdup +#define isatty _isatty +#define write _write +#define STDIN_FILENO 0 + +static HANDLE console_in, console_out; +static DWORD oldMode; + + +#else /* _WIN32 */ + #include <termios.h> #include <unistd.h> #include <stdlib.h> @@ -92,6 +112,10 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> + +static struct termios orig_termios; /* in order to restore at exit */ +#endif /* _WIN32 */ + #include "linenoise.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 @@ -99,7 +123,6 @@ static const char *unsupported_term[] = {"dumb","cons25",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; -static struct termios orig_termios; /* in order to restore at exit */ static int rawmode = 0; /* for atexit() function to check if restore is needed*/ static int atexit_registered = 0; /* register atexit just 1 time */ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; @@ -130,6 +153,16 @@ static void freeHistory(void) { } static int enableRawMode(int fd) { +#ifdef _WIN32 + if (!console_in) { + console_in = GetStdHandle(STD_INPUT_HANDLE); + console_out = GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleMode(console_in, &oldMode); + SetConsoleMode(console_in, oldMode & ~(ENABLE_LINE_INPUT | ENABLE_LINE_INPUT)); + } + return 0; +#else struct termios raw; if (!isatty(STDIN_FILENO)) goto fatal; @@ -162,12 +195,19 @@ static int enableRawMode(int fd) { fatal: errno = ENOTTY; return -1; +#endif } static void disableRawMode(int fd) { +#ifdef _WIN32 + SetConsoleMode(console_in, oldMode); + console_in = 0; + console_out = 0; +#else /* Don't even check the return value as it's too late. */ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) rawmode = 0; +#endif } /* At exit we'll try to fix the terminal to the initial conditions. */ @@ -177,14 +217,28 @@ static void linenoiseAtExit(void) { } static int getColumns(void) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(console_out, &inf); + return inf.dwSize.X; +#else struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80; return ws.ws_col; +#endif +} + +#ifdef _WIN32 +static void output(const char* str, size_t len, int x, int y) +{ + COORD pos = { (SHORT)x, (SHORT)y }; + DWORD count = 0; + WriteConsoleOutputCharacterA(console_out, str, len, pos, &count); } +#endif static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) { - char seq[64]; size_t plen = strlen(prompt); while((plen+pos) >= cols) { @@ -196,21 +250,109 @@ static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_ len--; } - /* Cursor to left edge */ - snprintf(seq,64,"\x1b[0G"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Write the prompt and the current buffer content */ - if (write(fd,prompt,strlen(prompt)) == -1) return; - if (write(fd,buf,len) == -1) return; - /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - if (write(fd,seq,strlen(seq)) == -1) return; - /* Move cursor to original position. */ - snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); - if (write(fd,seq,strlen(seq)) == -1) return; +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(console_out, &inf); + output(prompt, plen, 0, inf.dwCursorPosition.Y); + output(buf, len, plen, inf.dwCursorPosition.Y); + if (plen + len < (size_t)inf.dwSize.X) { + /* Blank to EOL */ + char* tmp = (char*)malloc(inf.dwSize.X - (plen + len)); + memset(tmp, ' ', inf.dwSize.X - (plen + len)); + output(tmp, inf.dwSize.X - (plen + len), len + plen, inf.dwCursorPosition.Y); + free(tmp); + } + inf.dwCursorPosition.X = (SHORT)(pos + plen); + SetConsoleCursorPosition(console_out, inf.dwCursorPosition); +#else + { + char seq[64]; + /* Cursor to left edge */ + snprintf(seq,64,"\x1b[0G"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Write the prompt and the current buffer content */ + if (write(fd,prompt,strlen(prompt)) == -1) return; + if (write(fd,buf,len) == -1) return; + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + if (write(fd,seq,strlen(seq)) == -1) return; + /* Move cursor to original position. */ + snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); + if (write(fd,seq,strlen(seq)) == -1) return; + } +#endif +} + +/* Note that this should parse some special keys into their emacs ctrl-key combos + * Return of -1 signifies unrecognized code + */ +static char linenoiseReadChar(int fd){ +#ifdef _WIN32 + INPUT_RECORD rec; + DWORD count; + do { + ReadConsoleInputA(console_in, &rec, 1, &count); + } while (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown); + + if (rec.Event.KeyEvent.uChar.AsciiChar == 0) { + /* handle keys that aren't converted to ASCII */ + switch (rec.Event.KeyEvent.wVirtualKeyCode) { + case VK_LEFT: return 2; /* ctrl-b */ + case VK_RIGHT: return 6; /* ctrl-f */ + case VK_UP: return 16; /* ctrl-p */ + case VK_DOWN: return 14; /* ctrl-n */ + case VK_DELETE: return 127; /* ascii DEL byte */ + case VK_HOME: return 1; /* ctrl-a */ + case VK_END: return 5; /* ctrl-e */ + default: return -1; + } + } + return rec.Event.KeyEvent.uChar.AsciiChar; +#else + char c; + int nread; + char seq[2], seq2[2]; + + nread = read(fd,&c,1); + if (nread <= 0) return 0; + + if (c == 27) { /* escape */ + if (read(fd,seq,2) == -1) return 0; + if (seq[0] == 91){ + if (seq[1] == 68) { /* left arrow */ + return 2; /* ctrl-b */ + } else if (seq[1] == 67) { /* right arrow */ + return 6; /* ctrl-f */ + } else if (seq[1] == 65) { /* up arrow */ + return 16; /* ctrl-p */ + } else if (seq[1] == 66) { /* up arrow */ + return 14; /* ctrl-n */ + } else if (seq[1] > 48 && seq[1] < 55) { + /* extended escape */ + if (read(fd,seq2,2) == -1) return 0; + if (seq[1] == 51 && seq2[0] == 126) { /* delete */ + return 127; /* ascii DEL byte */ + } else { + return -1; + } + } else { + return -1; + } + } else { + return -1; + } + } else if (c == 127) { + /* some consoles use 127 for backspace rather than delete. + * we only use it for delete */ + return 8; + } + + return c; /* normalish character */ +#endif } static void beep() { + /* doesn't do anything on windows but harmless */ fprintf(stderr, "\x7"); fflush(stderr); } @@ -225,7 +367,7 @@ static void freeCompletions(linenoiseCompletions *lc) { static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) { linenoiseCompletions lc = { 0, NULL }; - int nread, nwritten; + int nwritten; char c = 0; completionCallback(buf,&lc); @@ -244,13 +386,12 @@ static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, si refreshLine(fd,prompt,buf,*len,*pos,cols); } - nread = read(fd,&c,1); - if (nread <= 0) { - freeCompletions(&lc); - return -1; - } + c = linenoiseReadChar(fd); switch(c) { + case 0: + freeCompletions(&lc); + return -1; case 9: /* tab */ i = (i+1) % (lc.len+1); if (i == lc.len) beep(); @@ -279,9 +420,21 @@ static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, si } void linenoiseClearScreen(void) { - if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) { +#ifdef _WIN32 + COORD coord = {0, 0}; + CONSOLE_SCREEN_BUFFER_INFO inf; + DWORD count; + DWORD size; + + GetConsoleScreenBufferInfo(console_out, &inf); + size = inf.dwSize.X * inf.dwSize.Y; + FillConsoleOutputCharacterA(console_out, ' ', size, coord, &count ); + SetConsoleCursorPosition(console_out, coord); +#else + if (write(1,"\x1b[H\x1b[2J",7) <= 0) { /* nothing to do, just to avoid warning. */ } +#endif } static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) { @@ -298,14 +451,12 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) * initially is just an empty string. */ linenoiseHistoryAdd(""); - if (write(fd,prompt,plen) == -1) return -1; + if (write(1,prompt,plen) == -1) return -1; while(1) { - char c; - int nread; - char seq[2], seq2[2]; + char c = linenoiseReadChar(fd); - nread = read(fd,&c,1); - if (nread <= 0) return len; + if (c == 0) return len; + if (c == (char)-1) continue; /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the @@ -326,8 +477,13 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) case 3: /* ctrl-c */ errno = EAGAIN; return -1; - case 127: /* backspace */ - case 8: /* ctrl-h */ + case 127: /* delete */ + memmove(buf+pos,buf+pos+1,len-pos-1); + len--; + buf[len] = '\0'; + refreshLine(fd,prompt,buf,len,pos,cols); + break; + case 8: /* backspace or ctrl-h */ if (pos > 0 && len > 0) { memmove(buf+pos-1,buf+pos,len-pos); pos--; @@ -357,69 +513,44 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) refreshLine(fd,prompt,buf,len,pos,cols); } break; - case 2: /* ctrl-b */ - goto left_arrow; + case 2: /* ctrl-b */ /* left arrow */ + if (pos > 0) { + pos--; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; case 6: /* ctrl-f */ - goto right_arrow; + /* right arrow */ + if (pos != len) { + pos++; + refreshLine(fd,prompt,buf,len,pos,cols); + } + break; case 16: /* ctrl-p */ - seq[1] = 65; - goto up_down_arrow; case 14: /* ctrl-n */ - seq[1] = 66; - goto up_down_arrow; - break; - case 27: /* escape sequence */ - if (read(fd,seq,2) == -1) break; - if (seq[0] == 91 && seq[1] == 68) { -left_arrow: - /* left arrow */ - if (pos > 0) { - pos--; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && seq[1] == 67) { -right_arrow: - /* right arrow */ - if (pos != len) { - pos++; - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { -up_down_arrow: - /* up and down arrow: history */ - 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(buf); - /* Show the new entry */ - history_index += (seq[1] == 65) ? 1 : -1; - if (history_index < 0) { - history_index = 0; - break; - } else if (history_index >= history_len) { - history_index = history_len-1; - break; - } - strncpy(buf,history[history_len-1-history_index],buflen); - buf[buflen] = '\0'; - len = pos = strlen(buf); - refreshLine(fd,prompt,buf,len,pos,cols); - } - } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) { - /* extended escape */ - if (read(fd,seq2,2) == -1) break; - if (seq[1] == 51 && seq2[0] == 126) { - /* delete */ - if (len > 0 && pos < len) { - memmove(buf+pos,buf+pos+1,len-pos-1); - len--; - buf[len] = '\0'; - refreshLine(fd,prompt,buf,len,pos,cols); - } + /* up and down arrow: history */ + 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(buf); + /* Show the new entry */ + history_index += (c == 16) ? 1 : -1; + if (history_index < 0) { + history_index = 0; + break; + } else if (history_index >= history_len) { + history_index = history_len-1; + break; } + strncpy(buf,history[history_len-1-history_index],buflen); + buf[buflen] = '\0'; + len = pos = strlen(buf); + refreshLine(fd,prompt,buf,len,pos,cols); } break; + case 27: /* escape sequence */ + break; /* should be handled by linenoiseReadChar */ default: if (len < buflen) { if (len == pos) { @@ -430,7 +561,7 @@ up_down_arrow: if (plen+len < cols) { /* Avoid a full update of the line in the * trivial case. */ - if (write(fd,&c,1) == -1) return -1; + if (write(1,&c,1) == -1) return -1; } else { refreshLine(fd,prompt,buf,len,pos,cols); } |