1 /* linenoise.c -- guerrilla line editing library against the idea that a
2  *
3  * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
4  * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *   * Redistributions of source code must retain the above copyright notice,
12  *     this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  *   * Neither the name of Redis nor the names of its contributors may be used
17  *     to endorse or promote products derived from this software without
18  *     specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  * line editing lib needs to be 20,000 lines of C code.
33  *
34  * You can find the latest source code at:
35  *
36  *   http://github.com/antirez/linenoise
37  *
38  * Does a number of crazy assumptions that happen to be true in 99.9999% of
39  * the 2010 UNIX computers around.
40  *
41  * References:
42  * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
43  * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
44  *
45  * Todo list:
46  * - Switch to gets() if $TERM is something we can't support.
47  * - Filter bogus Ctrl+<char> combinations.
48  * - Win32 support
49  *
50  * Bloat:
51  * - Completion?
52  * - History search like Ctrl+r in readline?
53  *
54  * List of escape sequences used by this program, we do everything just
55  * with three sequences. In order to be so cheap we may have some
56  * flickering effect with some slow terminal, but the lesser sequences
57  * the more compatible.
58  *
59  * CHA (Cursor Horizontal Absolute)
60  *    Sequence: ESC [ n G
61  *    Effect: moves cursor to column n (1 based)
62  *
63  * EL (Erase Line)
64  *    Sequence: ESC [ n K
65  *    Effect: if n is 0 or missing, clear from cursor to end of line
66  *    Effect: if n is 1, clear from beginning of line to cursor
67  *    Effect: if n is 2, clear entire line
68  *
69  * CUF (Cursor Forward)
70  *    Sequence: ESC [ n C
71  *    Effect: moves cursor forward of n chars
72  *
73  * The following are used to clear the screen: ESC [ H ESC [ 2 J
74  * This is actually composed of two sequences:
75  *
76  * cursorhome
77  *    Sequence: ESC [ H
78  *    Effect: moves the cursor to upper left corner
79  *
80  * ED2 (Clear entire screen)
81  *    Sequence: ESC [ 2 J
82  *    Effect: clear the whole screen
83  *
84  */
85 
86 #include "mongo/platform/basic.h"
87 
88 #ifdef _WIN32
89 
90 #include <conio.h>
91 #include <io.h>
92 #define strcasecmp _stricmp
93 #define strdup _strdup
94 #define isatty _isatty
95 #define write _write
96 #define STDIN_FILENO 0
97 
98 #else /* _WIN32 */
99 
100 #include <cctype>
101 #include <signal.h>
102 #include <stdlib.h>
103 #include <string.h>
104 #include <sys/ioctl.h>
105 #include <sys/types.h>
106 #include <termios.h>
107 #include <unistd.h>
108 #include <wctype.h>
109 
110 #endif /* _WIN32 */
111 
112 #include "linenoise.h"
113 #include "linenoise_utf8.h"
114 #include "mk_wcwidth.h"
115 #include <errno.h>
116 #include <fcntl.h>
117 #include <sstream>
118 #include <stdio.h>
119 #include <string>
120 #include <vector>
121 
122 #include "mongo/util/errno_util.h"
123 
124 using std::string;
125 using std::vector;
126 
127 using std::unique_ptr;
128 
129 using linenoise_utf8::UChar8;
130 using linenoise_utf8::UChar32;
131 using linenoise_utf8::copyString8to32;
132 using linenoise_utf8::copyString32;
133 using linenoise_utf8::copyString32to8;
134 using linenoise_utf8::strlen32;
135 using linenoise_utf8::strncmp32;
136 using linenoise_utf8::write32;
137 using linenoise_utf8::Utf8String;
138 using linenoise_utf8::Utf32String;
139 
140 struct linenoiseCompletions {
141     vector<Utf32String> completionStrings;
142 };
143 
144 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 1000
145 #define LINENOISE_MAX_LINE 4096
146 
147 // make control-characters more readable
148 #define ctrlChar(upperCaseASCII) (upperCaseASCII - 0x40)
149 
150 /**
151  * Recompute widths of all characters in a UChar32 buffer
152  * @param text          input buffer of Unicode characters
153  * @param widths        output buffer of character widths
154  * @param charCount     number of characters in buffer
155  */
recomputeCharacterWidths(const UChar32 * text,char * widths,int charCount)156 static void recomputeCharacterWidths(const UChar32* text, char* widths, int charCount) {
157     for (int i = 0; i < charCount; ++i) {
158         widths[i] = mk_wcwidth(text[i]);
159     }
160 }
161 
162 /**
163  * Calculate a new screen position given a starting position, screen width and character count
164  * @param x             initial x position (zero-based)
165  * @param y             initial y position (zero-based)
166  * @param screenColumns screen column count
167  * @param charCount     character positions to advance
168  * @param xOut          returned x position (zero-based)
169  * @param yOut          returned y position (zero-based)
170  */
calculateScreenPosition(int x,int y,int screenColumns,int charCount,int & xOut,int & yOut)171 static void calculateScreenPosition(
172     int x, int y, int screenColumns, int charCount, int& xOut, int& yOut) {
173     xOut = x;
174     yOut = y;
175     int charsRemaining = charCount;
176     while (charsRemaining > 0) {
177         int charsThisRow =
178             (x + charsRemaining < screenColumns) ? charsRemaining : screenColumns - x;
179         xOut = x + charsThisRow;
180         yOut = y;
181         charsRemaining -= charsThisRow;
182         x = 0;
183         ++y;
184     }
185     if (xOut == screenColumns) {  // we have to special-case line wrap
186         xOut = 0;
187         ++yOut;
188     }
189 }
190 
191 /**
192  * Calculate a column width using mk_wcswidth()
193  * @param buf32  text to calculate
194  * @param len    length of text to calculate
195  */
calculateColumnPosition(UChar32 * buf32,int len)196 static int calculateColumnPosition(UChar32* buf32, int len) {
197     int width = mk_wcswidth(reinterpret_cast<const int*>(buf32), len);
198     if (width == -1)
199         return len;
200     else
201         return width;
202 }
203 
isControlChar(UChar32 testChar)204 static bool isControlChar(UChar32 testChar) {
205     return (testChar < ' ') ||                   // C0 controls
206         (testChar >= 0x7F && testChar <= 0x9F);  // DEL and C1 controls
207 }
208 
209 struct PromptBase {              // a convenience struct for grouping prompt info
210     Utf32String promptText;      // our copy of the prompt text, edited
211     char* promptCharWidths;      // character widths from mk_wcwidth()
212     int promptChars;             // chars in promptText
213     int promptExtraLines;        // extra lines (beyond 1) occupied by prompt
214     int promptIndentation;       // column offset to end of prompt
215     int promptLastLinePosition;  // index into promptText where last line begins
216     int promptPreviousInputLen;  // promptChars of previous input line, for clearing
217     int promptCursorRowOffset;   // where the cursor is relative to the start of the prompt
218     int promptScreenColumns;     // width of screen in columns
219     int promptPreviousLen;       // help erasing
220     int promptErrorCode;         // error code (invalid UTF-8) or zero
221 
PromptBasePromptBase222     PromptBase() : promptPreviousInputLen(0) {}
223 };
224 
225 struct PromptInfo : public PromptBase {
PromptInfoPromptInfo226     PromptInfo(const UChar8* textPtr, int columns) {
227         promptExtraLines = 0;
228         promptLastLinePosition = 0;
229         promptPreviousLen = 0;
230         promptScreenColumns = columns;
231         Utf32String tempUnicode(textPtr);
232 
233         // strip control characters from the prompt -- we do allow newline
234         UChar32* pIn = tempUnicode.get();
235         UChar32* pOut = pIn;
236         while (*pIn) {
237             UChar32 c = *pIn;
238             if ('\n' == c || !isControlChar(c)) {
239                 *pOut = c;
240                 ++pOut;
241             }
242             ++pIn;
243         }
244         *pOut = 0;
245         promptChars = pOut - tempUnicode.get();
246         promptText = tempUnicode;
247 
248         int x = 0;
249         for (int i = 0; i < promptChars; ++i) {
250             UChar32 c = promptText[i];
251             if ('\n' == c) {
252                 x = 0;
253                 ++promptExtraLines;
254                 promptLastLinePosition = i + 1;
255             } else {
256                 ++x;
257                 if (x >= promptScreenColumns) {
258                     x = 0;
259                     ++promptExtraLines;
260                     promptLastLinePosition = i + 1;
261                 }
262             }
263         }
264         promptIndentation = promptChars - promptLastLinePosition;
265         promptCursorRowOffset = promptExtraLines;
266     }
267 };
268 
269 // Used with DynamicPrompt (history search)
270 //
271 static const Utf32String forwardSearchBasePrompt(reinterpret_cast<const UChar8*>("(i-search)`"));
272 static const Utf32String reverseSearchBasePrompt(
273     reinterpret_cast<const UChar8*>("(reverse-i-search)`"));
274 static const Utf32String endSearchBasePrompt(reinterpret_cast<const UChar8*>("': "));
275 static Utf32String previousSearchText;  // remembered across invocations of linenoise()
276 
277 // changing prompt for "(reverse-i-search)`text':" etc.
278 //
279 struct DynamicPrompt : public PromptBase {
280     Utf32String searchText;  // text we are searching for
281     char* searchCharWidths;  // character widths from mk_wcwidth()
282     int searchTextLen;       // chars in searchText
283     int direction;           // current search direction, 1=forward, -1=reverse
284 
DynamicPromptDynamicPrompt285     DynamicPrompt(PromptBase& pi, int initialDirection)
286         : searchTextLen(0), direction(initialDirection) {
287         promptScreenColumns = pi.promptScreenColumns;
288         promptCursorRowOffset = 0;
289         Utf32String emptyString(1);
290         searchText = emptyString;
291         const Utf32String* basePrompt =
292             (direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
293         size_t promptStartLength = basePrompt->length();
294         promptChars = promptStartLength + endSearchBasePrompt.length();
295         promptLastLinePosition =
296             promptChars;  // TODO fix this, we are asssuming that the history prompt won't wrap (!)
297         promptPreviousLen = promptChars;
298         Utf32String tempUnicode(promptChars + 1);
299         memcpy(tempUnicode.get(), basePrompt->get(), sizeof(UChar32) * promptStartLength);
300         memcpy(&tempUnicode[promptStartLength],
301                endSearchBasePrompt.get(),
302                sizeof(UChar32) * (endSearchBasePrompt.length() + 1));
303         tempUnicode.initFromBuffer();
304         promptText = tempUnicode;
305         calculateScreenPosition(
306             0, 0, pi.promptScreenColumns, promptChars, promptIndentation, promptExtraLines);
307     }
308 
updateSearchPromptDynamicPrompt309     void updateSearchPrompt(void) {
310         const Utf32String* basePrompt =
311             (direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
312         size_t promptStartLength = basePrompt->length();
313         promptChars = promptStartLength + searchTextLen + endSearchBasePrompt.length();
314         Utf32String tempUnicode(promptChars + 1);
315         memcpy(tempUnicode.get(), basePrompt->get(), sizeof(UChar32) * promptStartLength);
316         memcpy(&tempUnicode[promptStartLength], searchText.get(), sizeof(UChar32) * searchTextLen);
317         size_t endIndex = promptStartLength + searchTextLen;
318         memcpy(&tempUnicode[endIndex],
319                endSearchBasePrompt.get(),
320                sizeof(UChar32) * (endSearchBasePrompt.length() + 1));
321         tempUnicode.initFromBuffer();
322         promptText = tempUnicode;
323     }
324 
updateSearchTextDynamicPrompt325     void updateSearchText(const UChar32* textPtr) {
326         Utf32String tempUnicode(textPtr);
327         searchTextLen = tempUnicode.chars();
328         searchText = tempUnicode;
329         updateSearchPrompt();
330     }
331 };
332 
333 class KillRing {
334     static const int capacity = 10;
335     int size;
336     int index;
337     char indexToSlot[10];
338     vector<Utf32String> theRing;
339 
340 public:
341     enum action { actionOther, actionKill, actionYank };
342     action lastAction;
343     size_t lastYankSize;
344 
KillRing()345     KillRing() : size(0), index(0), lastAction(actionOther) {
346         theRing.reserve(capacity);
347     }
348 
kill(const UChar32 * text,int textLen,bool forward)349     void kill(const UChar32* text, int textLen, bool forward) {
350         if (textLen == 0) {
351             return;
352         }
353         Utf32String killedText(text, textLen);
354         if (lastAction == actionKill && size > 0) {
355             int slot = indexToSlot[0];
356             int currentLen = theRing[slot].length();
357             int resultLen = currentLen + textLen;
358             Utf32String temp(resultLen + 1);
359             if (forward) {
360                 memcpy(temp.get(), theRing[slot].get(), currentLen * sizeof(UChar32));
361                 memcpy(&temp[currentLen], killedText.get(), textLen * sizeof(UChar32));
362             } else {
363                 memcpy(temp.get(), killedText.get(), textLen * sizeof(UChar32));
364                 memcpy(&temp[textLen], theRing[slot].get(), currentLen * sizeof(UChar32));
365             }
366             temp[resultLen] = 0;
367             temp.initFromBuffer();
368             theRing[slot] = temp;
369         } else {
370             if (size < capacity) {
371                 if (size > 0) {
372                     memmove(&indexToSlot[1], &indexToSlot[0], size);
373                 }
374                 indexToSlot[0] = size;
375                 size++;
376                 theRing.push_back(killedText);
377             } else {
378                 int slot = indexToSlot[capacity - 1];
379                 theRing[slot] = killedText;
380                 memmove(&indexToSlot[1], &indexToSlot[0], capacity - 1);
381                 indexToSlot[0] = slot;
382             }
383             index = 0;
384         }
385     }
386 
yank()387     Utf32String* yank() {
388         return (size > 0) ? &theRing[indexToSlot[index]] : 0;
389     }
390 
yankPop()391     Utf32String* yankPop() {
392         if (size == 0) {
393             return 0;
394         }
395         ++index;
396         if (index == size) {
397             index = 0;
398         }
399         return &theRing[indexToSlot[index]];
400     }
401 };
402 
403 class InputBuffer {
404     UChar32* buf32;    // input buffer
405     char* charWidths;  // character widths from mk_wcwidth()
406     int buflen;        // buffer size in characters
407     int len;           // length of text in input buffer
408     int pos;           // character position in buffer ( 0 <= pos <= len )
409 
410     void clearScreen(PromptBase& pi);
411     int incrementalHistorySearch(PromptBase& pi, int startChar);
412     int completeLine(PromptBase& pi);
413     void refreshLine(PromptBase& pi);
414 
415 public:
InputBuffer(UChar32 * buffer,char * widthArray,int bufferLen)416     InputBuffer(UChar32* buffer, char* widthArray, int bufferLen)
417         : buf32(buffer), charWidths(widthArray), buflen(bufferLen - 1), len(0), pos(0) {
418         buf32[0] = 0;
419     }
preloadBuffer(const UChar8 * preloadText)420     void preloadBuffer(const UChar8* preloadText) {
421         size_t ucharCount;
422         int errorCode;
423         copyString8to32(buf32, preloadText, buflen + 1, ucharCount, errorCode);
424         recomputeCharacterWidths(buf32, charWidths, ucharCount);
425         len = ucharCount;
426         pos = ucharCount;
427     }
428     int getInputLine(PromptBase& pi);
length(void) const429     int length(void) const {
430         return len;
431     }
432 };
433 
434 // Special codes for keyboard input:
435 //
436 // Between Windows and the various Linux "terminal" programs, there is some
437 // pretty diverse behavior in the "scan codes" and escape sequences we are
438 // presented with.  So ... we'll translate them all into our own pidgin
439 // pseudocode, trying to stay out of the way of UTF-8 and international
440 // characters.  Here's the general plan.
441 //
442 // "User input keystrokes" (key chords, whatever) will be encoded as a single value.
443 // The low 21 bits are reserved for Unicode characters.  Popular function-type keys
444 // get their own codes in the range 0x10200000 to (if needed) 0x1FE00000, currently
445 // just arrow keys, Home, End and Delete.  Keypresses with Ctrl get ORed with
446 // 0x20000000, with Alt get ORed with 0x40000000.  So, Ctrl+Alt+Home is encoded
447 // as 0x20000000 + 0x40000000 + 0x10A00000 == 0x70A00000.  To keep things complicated,
448 // the Alt key is equivalent to prefixing the keystroke with ESC, so ESC followed by
449 // D is treated the same as Alt + D ... we'll just use Emacs terminology and call
450 // this "Meta".  So, we will encode both ESC followed by D and Alt held down while D
451 // is pressed the same, as Meta-D, encoded as 0x40000064.
452 //
453 // Here are the definitions of our component constants:
454 //
455 // Maximum unsigned 32-bit value    = 0xFFFFFFFF;   // For reference, max 32-bit value
456 // Highest allocated Unicode char   = 0x001FFFFF;   // For reference, max Unicode value
457 static const int META = 0x40000000;          // Meta key combination
458 static const int CTRL = 0x20000000;          // Ctrl key combination
459 static const int SPECIAL_KEY = 0x10000000;   // Common bit for all special keys
460 static const int UP_ARROW_KEY = 0x10200000;  // Special keys
461 static const int DOWN_ARROW_KEY = 0x10400000;
462 static const int RIGHT_ARROW_KEY = 0x10600000;
463 static const int LEFT_ARROW_KEY = 0x10800000;
464 static const int HOME_KEY = 0x10A00000;
465 static const int END_KEY = 0x10C00000;
466 static const int DELETE_KEY = 0x10E00000;
467 static const int PAGE_UP_KEY = 0x11000000;
468 static const int PAGE_DOWN_KEY = 0x11200000;
469 
470 static const char* unsupported_term[] = {"dumb", "cons25", "emacs", NULL};
471 static linenoiseCompletionCallback* completionCallback = NULL;
472 
473 #ifdef _WIN32
474 static HANDLE console_in, console_out;
475 static DWORD oldMode;
476 static WORD oldDisplayAttribute;
477 #else
478 static struct termios orig_termios; /* in order to restore at exit */
479 #endif
480 
481 static KillRing killRing;
482 
483 static int rawmode = 0;           /* for atexit() function to check if restore is needed*/
484 static int atexit_registered = 0; /* register atexit just 1 time */
485 static int historyMaxLen = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
486 static int historyLen = 0;
487 static int historyIndex = 0;
488 static UChar8** history = NULL;
489 
490 // used to emulate Windows command prompt on down-arrow after a recall
491 // we use -2 as our "not set" value because we add 1 to the previous index on down-arrow,
492 // and zero is a valid index (so -1 is a valid "previous index")
493 static int historyPreviousIndex = -2;
494 static bool historyRecallMostRecent = false;
495 
496 static void linenoiseAtExit(void);
497 
isUnsupportedTerm(void)498 static bool isUnsupportedTerm(void) {
499     char* term = getenv("TERM");
500     if (term == NULL)
501         return false;
502     for (int j = 0; unsupported_term[j]; ++j)
503         if (!strcasecmp(term, unsupported_term[j])) {
504             return true;
505         }
506     return false;
507 }
508 
beep()509 static void beep() {
510     fprintf(stderr, "\x7");  // ctrl-G == bell/beep
511     fflush(stderr);
512 }
513 
linenoiseHistoryFree(void)514 void linenoiseHistoryFree(void) {
515     if (history) {
516         for (int j = 0; j < historyLen; ++j)
517             free(history[j]);
518         historyLen = 0;
519         free(history);
520         history = 0;
521     }
522 }
523 
enableRawMode(void)524 static int enableRawMode(void) {
525 #ifdef _WIN32
526     if (!console_in) {
527         console_in = GetStdHandle(STD_INPUT_HANDLE);
528         console_out = GetStdHandle(STD_OUTPUT_HANDLE);
529 
530         GetConsoleMode(console_in, &oldMode);
531         SetConsoleMode(console_in,
532                        oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT));
533     }
534     return 0;
535 #else
536     struct termios raw;
537 
538     if (!isatty(0))
539         goto fatal;
540     if (!atexit_registered) {
541         atexit(linenoiseAtExit);
542         atexit_registered = 1;
543     }
544     if (tcgetattr(0, &orig_termios) == -1)
545         goto fatal;
546 
547     raw = orig_termios; /* modify the original mode */
548                         /* input modes: no break, no CR to NL, no parity check, no strip char,
549                          * no start/stop output control. */
550     raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
551     /* output modes - disable post processing */
552     // this is wrong, we don't want raw output, it turns newlines into straight linefeeds
553     // raw.c_oflag &= ~(OPOST);
554     /* control modes - set 8 bit chars */
555     raw.c_cflag |= (CS8);
556     /* local modes - echoing off, canonical off, no extended functions,
557      * no signal chars (^Z,^C) */
558     raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
559     /* control chars - set return condition: min number of bytes and timer.
560      * We want read to return every single byte, without timeout. */
561     raw.c_cc[VMIN] = 1;
562     raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
563 
564     /* put terminal in raw mode after flushing */
565     if (tcsetattr(0, TCSADRAIN, &raw) < 0)
566         goto fatal;
567     rawmode = 1;
568     return 0;
569 
570 fatal:
571     errno = ENOTTY;
572     return -1;
573 #endif
574 }
575 
disableRawMode(void)576 static void disableRawMode(void) {
577 #ifdef _WIN32
578     SetConsoleMode(console_in, oldMode);
579     console_in = 0;
580     console_out = 0;
581 #else
582     if (rawmode && tcsetattr(0, TCSADRAIN, &orig_termios) != -1)
583         rawmode = 0;
584 #endif
585 }
586 
587 // At exit we'll try to fix the terminal to the initial conditions
linenoiseAtExit(void)588 static void linenoiseAtExit(void) {
589     disableRawMode();
590 }
591 
getScreenColumns(void)592 static int getScreenColumns(void) {
593     int cols;
594 #ifdef _WIN32
595     CONSOLE_SCREEN_BUFFER_INFO inf;
596     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &inf);
597     cols = inf.dwSize.X;
598 #else
599     struct winsize ws;
600     cols = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 80 : ws.ws_col;
601 #endif
602     // cols is 0 in certain circumstances like inside debugger, which creates further issues
603     return (cols > 0) ? cols : 80;
604 }
605 
getScreenRows(void)606 static int getScreenRows(void) {
607     int rows;
608 #ifdef _WIN32
609     CONSOLE_SCREEN_BUFFER_INFO inf;
610     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &inf);
611     rows = 1 + inf.srWindow.Bottom - inf.srWindow.Top;
612 #else
613     struct winsize ws;
614     rows = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 24 : ws.ws_row;
615 #endif
616     return (rows > 0) ? rows : 24;
617 }
618 
setDisplayAttribute(bool enhancedDisplay)619 static void setDisplayAttribute(bool enhancedDisplay) {
620 #ifdef _WIN32
621     if (enhancedDisplay) {
622         CONSOLE_SCREEN_BUFFER_INFO inf;
623         GetConsoleScreenBufferInfo(console_out, &inf);
624         oldDisplayAttribute = inf.wAttributes;
625         BYTE oldLowByte = oldDisplayAttribute & 0xFF;
626         BYTE newLowByte;
627         switch (oldLowByte) {
628             case 0x07:
629                 // newLowByte = FOREGROUND_BLUE | FOREGROUND_INTENSITY;  // too dim
630                 // newLowByte = FOREGROUND_BLUE;                         // even dimmer
631                 newLowByte =
632                     FOREGROUND_BLUE | FOREGROUND_GREEN;  // most similar to xterm appearance
633                 break;
634             case 0x70:
635                 newLowByte = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
636                 break;
637             default:
638                 newLowByte = oldLowByte ^ 0xFF;  // default to inverse video
639                 break;
640         }
641         inf.wAttributes = (inf.wAttributes & 0xFF00) | newLowByte;
642         SetConsoleTextAttribute(console_out, inf.wAttributes);
643     } else {
644         SetConsoleTextAttribute(console_out, oldDisplayAttribute);
645     }
646 #else
647     if (enhancedDisplay) {
648         if (write(1, "\x1b[1;34m", 7) == -1)
649             return; /* bright blue (visible with both B&W bg) */
650     } else {
651         if (write(1, "\x1b[0m", 4) == -1)
652             return; /* reset */
653     }
654 #endif
655 }
656 
657 /**
658  * Display the dynamic incremental search prompt and the current user input line.
659  * @param pi   PromptBase struct holding information about the prompt and our screen position
660  * @param buf32  input buffer to be displayed
661  * @param len  count of characters in the buffer
662  * @param pos  current cursor position within the buffer (0 <= pos <= len)
663  */
dynamicRefresh(PromptBase & pi,UChar32 * buf32,int len,int pos)664 static void dynamicRefresh(PromptBase& pi, UChar32* buf32, int len, int pos) {
665     // calculate the position of the end of the prompt
666     int xEndOfPrompt, yEndOfPrompt;
667     calculateScreenPosition(
668         0, 0, pi.promptScreenColumns, pi.promptChars, xEndOfPrompt, yEndOfPrompt);
669     pi.promptIndentation = xEndOfPrompt;
670 
671     // calculate the position of the end of the input line
672     int xEndOfInput, yEndOfInput;
673     calculateScreenPosition(xEndOfPrompt,
674                             yEndOfPrompt,
675                             pi.promptScreenColumns,
676                             calculateColumnPosition(buf32, len),
677                             xEndOfInput,
678                             yEndOfInput);
679 
680     // calculate the desired position of the cursor
681     int xCursorPos, yCursorPos;
682     calculateScreenPosition(xEndOfPrompt,
683                             yEndOfPrompt,
684                             pi.promptScreenColumns,
685                             calculateColumnPosition(buf32, pos),
686                             xCursorPos,
687                             yCursorPos);
688 
689 #ifdef _WIN32
690     // position at the start of the prompt, clear to end of previous input
691     CONSOLE_SCREEN_BUFFER_INFO inf;
692     GetConsoleScreenBufferInfo(console_out, &inf);
693     inf.dwCursorPosition.X = 0;
694     inf.dwCursorPosition.Y -= pi.promptCursorRowOffset /*- pi.promptExtraLines*/;
695     SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
696     DWORD count;
697     FillConsoleOutputCharacterA(console_out,
698                                 ' ',
699                                 pi.promptPreviousLen + pi.promptPreviousInputLen,
700                                 inf.dwCursorPosition,
701                                 &count);
702     pi.promptPreviousLen = pi.promptIndentation;
703     pi.promptPreviousInputLen = len;
704 
705     // display the prompt
706     if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
707         return;
708 
709     // display the input line
710     if (write32(1, buf32, len) == -1)
711         return;
712 
713     // position the cursor
714     GetConsoleScreenBufferInfo(console_out, &inf);
715     inf.dwCursorPosition.X = xCursorPos;  // 0-based on Win32
716     inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos;
717     SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
718 #else  // _WIN32
719     char seq[64];
720     int cursorRowMovement = pi.promptCursorRowOffset - pi.promptExtraLines;
721     if (cursorRowMovement > 0) {  // move the cursor up as required
722         snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
723         if (write(1, seq, strlen(seq)) == -1)
724             return;
725     }
726     // position at the start of the prompt, clear to end of screen
727     snprintf(seq, sizeof seq, "\x1b[1G\x1b[J");  // 1-based on VT100
728     if (write(1, seq, strlen(seq)) == -1)
729         return;
730 
731     // display the prompt
732     if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
733         return;
734 
735     // display the input line
736     if (write32(1, buf32, len) == -1)
737         return;
738 
739     // we have to generate our own newline on line wrap
740     if (xEndOfInput == 0 && yEndOfInput > 0)
741         if (write(1, "\n", 1) == -1)
742             return;
743 
744     // position the cursor
745     cursorRowMovement = yEndOfInput - yCursorPos;
746     if (cursorRowMovement > 0) {  // move the cursor up as required
747         snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
748         if (write(1, seq, strlen(seq)) == -1)
749             return;
750     }
751     // position the cursor within the line
752     snprintf(seq, sizeof seq, "\x1b[%dG", xCursorPos + 1);  // 1-based on VT100
753     if (write(1, seq, strlen(seq)) == -1)
754         return;
755 #endif
756 
757     pi.promptCursorRowOffset = pi.promptExtraLines + yCursorPos;  // remember row for next pass
758 }
759 
760 /**
761  * Refresh the user's input line: the prompt is already onscreen and is not redrawn here
762  * @param pi   PromptBase struct holding information about the prompt and our screen position
763  */
refreshLine(PromptBase & pi)764 void InputBuffer::refreshLine(PromptBase& pi) {
765     // check for a matching brace/bracket/paren, remember its position if found
766     int highlight = -1;
767     if (pos < len) {
768         /* this scans for a brace matching buf32[pos] to highlight */
769         int scanDirection = 0;
770         if (strchr("}])", buf32[pos]))
771             scanDirection = -1; /* backwards */
772         else if (strchr("{[(", buf32[pos]))
773             scanDirection = 1; /* forwards */
774 
775         if (scanDirection) {
776             int unmatched = scanDirection;
777             for (int i = pos + scanDirection; i >= 0 && i < len; i += scanDirection) {
778                 /* TODO: the right thing when inside a string */
779                 if (strchr("}])", buf32[i]))
780                     --unmatched;
781                 else if (strchr("{[(", buf32[i]))
782                     ++unmatched;
783 
784                 if (unmatched == 0) {
785                     highlight = i;
786                     break;
787                 }
788             }
789         }
790     }
791 
792     // calculate the position of the end of the input line
793     int xEndOfInput, yEndOfInput;
794     calculateScreenPosition(pi.promptIndentation,
795                             0,
796                             pi.promptScreenColumns,
797                             calculateColumnPosition(buf32, len),
798                             xEndOfInput,
799                             yEndOfInput);
800 
801     // calculate the desired position of the cursor
802     int xCursorPos, yCursorPos;
803     calculateScreenPosition(pi.promptIndentation,
804                             0,
805                             pi.promptScreenColumns,
806                             calculateColumnPosition(buf32, pos),
807                             xCursorPos,
808                             yCursorPos);
809 
810 #ifdef _WIN32
811     // position at the end of the prompt, clear to end of previous input
812     CONSOLE_SCREEN_BUFFER_INFO inf;
813     GetConsoleScreenBufferInfo(console_out, &inf);
814     inf.dwCursorPosition.X = pi.promptIndentation;  // 0-based on Win32
815     inf.dwCursorPosition.Y -= pi.promptCursorRowOffset - pi.promptExtraLines;
816     SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
817     DWORD count;
818     if (len < pi.promptPreviousInputLen)
819         FillConsoleOutputCharacterA(
820             console_out, ' ', pi.promptPreviousInputLen, inf.dwCursorPosition, &count);
821     pi.promptPreviousInputLen = len;
822 
823     // display the input line
824     if (highlight == -1) {
825         if (write32(1, buf32, len) == -1)
826             return;
827     } else {
828         if (write32(1, buf32, highlight) == -1)
829             return;
830         setDisplayAttribute(true); /* bright blue (visible with both B&W bg) */
831         if (write32(1, &buf32[highlight], 1) == -1)
832             return;
833         setDisplayAttribute(false);
834         if (write32(1, buf32 + highlight + 1, len - highlight - 1) == -1)
835             return;
836     }
837 
838     // position the cursor
839     GetConsoleScreenBufferInfo(console_out, &inf);
840     inf.dwCursorPosition.X = xCursorPos;  // 0-based on Win32
841     inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos;
842     SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
843 #else  // _WIN32
844     char seq[64];
845     int cursorRowMovement = pi.promptCursorRowOffset - pi.promptExtraLines;
846     if (cursorRowMovement > 0) {  // move the cursor up as required
847         snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
848         if (write(1, seq, strlen(seq)) == -1)
849             return;
850     }
851     // position at the end of the prompt, clear to end of screen
852     snprintf(seq, sizeof seq, "\x1b[%dG\x1b[J", pi.promptIndentation + 1);  // 1-based on VT100
853     if (write(1, seq, strlen(seq)) == -1)
854         return;
855 
856     if (highlight == -1) {  // write unhighlighted text
857         if (write32(1, buf32, len) == -1)
858             return;
859     } else {  // highlight the matching brace/bracket/parenthesis
860         if (write32(1, buf32, highlight) == -1)
861             return;
862         setDisplayAttribute(true);
863         if (write32(1, &buf32[highlight], 1) == -1)
864             return;
865         setDisplayAttribute(false);
866         if (write32(1, buf32 + highlight + 1, len - highlight - 1) == -1)
867             return;
868     }
869 
870     // we have to generate our own newline on line wrap
871     if (xEndOfInput == 0 && yEndOfInput > 0)
872         if (write(1, "\n", 1) == -1)
873             return;
874 
875     // position the cursor
876     cursorRowMovement = yEndOfInput - yCursorPos;
877     if (cursorRowMovement > 0) {  // move the cursor up as required
878         snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
879         if (write(1, seq, strlen(seq)) == -1)
880             return;
881     }
882     // position the cursor within the line
883     snprintf(seq, sizeof seq, "\x1b[%dG", xCursorPos + 1);  // 1-based on VT100
884     if (write(1, seq, strlen(seq)) == -1)
885         return;
886 #endif
887 
888     pi.promptCursorRowOffset = pi.promptExtraLines + yCursorPos;  // remember row for next pass
889 }
890 
891 #ifndef _WIN32
892 
893 /**
894  * Read a UTF-8 sequence from the non-Windows keyboard and return the Unicode (UChar32) character it
895  * encodes
896  *
897  * @return  UChar32 Unicode character
898  */
readUnicodeCharacter(void)899 static UChar32 readUnicodeCharacter(void) {
900     static UChar8 utf8String[5];
901     static size_t utf8Count = 0;
902     while (true) {
903         UChar8 c;
904         if (read(0, &c, 1) <= 0)
905             return 0;
906         if (c <= 0x7F) {  // short circuit ASCII
907             utf8Count = 0;
908             return c;
909         } else if (utf8Count < sizeof(utf8String) - 1) {
910             utf8String[utf8Count++] = c;
911             utf8String[utf8Count] = 0;
912             UChar32 unicodeChar[2];
913             size_t ucharCount;
914             int errorCode;
915             copyString8to32(unicodeChar, utf8String, 2, ucharCount, errorCode);
916             if (ucharCount && errorCode == 0) {
917                 utf8Count = 0;
918                 return unicodeChar[0];
919             }
920         } else {
921             utf8Count = 0;  // this shouldn't happen: got four bytes but no UTF-8 character
922         }
923     }
924 }
925 
926 namespace EscapeSequenceProcessing {  // move these out of global namespace
927 
928 // This chunk of code does parsing of the escape sequences sent by various Linux terminals.
929 //
930 // It handles arrow keys, Home, End and Delete keys by interpreting the sequences sent by
931 // gnome terminal, xterm, rxvt, konsole, aterm and yakuake including the Alt and Ctrl key
932 // combinations that are understood by linenoise.
933 //
934 // The parsing uses tables, a bunch of intermediate dispatch routines and a doDispatch
935 // loop that reads the tables and sends control to "deeper" routines to continue the
936 // parsing.  The starting call to doDispatch( c, initialDispatch ) will eventually return
937 // either a character (with optional CTRL and META bits set), or -1 if parsing fails, or
938 // zero if an attempt to read from the keyboard fails.
939 //
940 // This is rather sloppy escape sequence processing, since we're not paying attention to what the
941 // actual TERM is set to and are processing all key sequences for all terminals, but it works with
942 // the most common keystrokes on the most common terminals.  It's intricate, but the nested 'if'
943 // statements required to do it directly would be worse.  This way has the advantage of allowing
944 // changes and extensions without having to touch a lot of code.
945 
946 // This is a typedef for the routine called by doDispatch().  It takes the current character
947 // as input, does any required processing including reading more characters and calling other
948 // dispatch routines, then eventually returns the final (possibly extended or special) character.
949 //
950 typedef UChar32 (*CharacterDispatchRoutine)(UChar32);
951 
952 // This structure is used by doDispatch() to hold a list of characters to test for and
953 // a list of routines to call if the character matches.  The dispatch routine list is one
954 // longer than the character list; the final entry is used if no character matches.
955 //
956 struct CharacterDispatch {
957     unsigned int len;                    // length of the chars list
958     const char* chars;                   // chars to test
959     CharacterDispatchRoutine* dispatch;  // array of routines to call
960 };
961 
962 // This dispatch routine is given a dispatch table and then farms work out to routines
963 // listed in the table based on the character it is called with.  The dispatch routines can
964 // read more input characters to decide what should eventually be returned.  Eventually,
965 // a called routine returns either a character or -1 to indicate parsing failure.
966 //
doDispatch(UChar32 c,CharacterDispatch & dispatchTable)967 static UChar32 doDispatch(UChar32 c, CharacterDispatch& dispatchTable) {
968     for (unsigned int i = 0; i < dispatchTable.len; ++i) {
969         if (static_cast<unsigned char>(dispatchTable.chars[i]) == c) {
970             return dispatchTable.dispatch[i](c);
971         }
972     }
973     return dispatchTable.dispatch[dispatchTable.len](c);
974 }
975 
976 static UChar32 thisKeyMetaCtrl = 0;  // holds pre-set Meta and/or Ctrl modifiers
977 
978 // Final dispatch routines -- return something
979 //
normalKeyRoutine(UChar32 c)980 static UChar32 normalKeyRoutine(UChar32 c) {
981     return thisKeyMetaCtrl | c;
982 }
upArrowKeyRoutine(UChar32 c)983 static UChar32 upArrowKeyRoutine(UChar32 c) {
984     return thisKeyMetaCtrl | UP_ARROW_KEY;
985 }
downArrowKeyRoutine(UChar32 c)986 static UChar32 downArrowKeyRoutine(UChar32 c) {
987     return thisKeyMetaCtrl | DOWN_ARROW_KEY;
988 }
rightArrowKeyRoutine(UChar32 c)989 static UChar32 rightArrowKeyRoutine(UChar32 c) {
990     return thisKeyMetaCtrl | RIGHT_ARROW_KEY;
991 }
leftArrowKeyRoutine(UChar32 c)992 static UChar32 leftArrowKeyRoutine(UChar32 c) {
993     return thisKeyMetaCtrl | LEFT_ARROW_KEY;
994 }
homeKeyRoutine(UChar32 c)995 static UChar32 homeKeyRoutine(UChar32 c) {
996     return thisKeyMetaCtrl | HOME_KEY;
997 }
endKeyRoutine(UChar32 c)998 static UChar32 endKeyRoutine(UChar32 c) {
999     return thisKeyMetaCtrl | END_KEY;
1000 }
pageUpKeyRoutine(UChar32 c)1001 static UChar32 pageUpKeyRoutine(UChar32 c) {
1002     return thisKeyMetaCtrl | PAGE_UP_KEY;
1003 }
pageDownKeyRoutine(UChar32 c)1004 static UChar32 pageDownKeyRoutine(UChar32 c) {
1005     return thisKeyMetaCtrl | PAGE_DOWN_KEY;
1006 }
deleteCharRoutine(UChar32 c)1007 static UChar32 deleteCharRoutine(UChar32 c) {
1008     return thisKeyMetaCtrl | ctrlChar('H');
1009 }  // key labeled Backspace
deleteKeyRoutine(UChar32 c)1010 static UChar32 deleteKeyRoutine(UChar32 c) {
1011     return thisKeyMetaCtrl | DELETE_KEY;
1012 }  // key labeled Delete
ctrlUpArrowKeyRoutine(UChar32 c)1013 static UChar32 ctrlUpArrowKeyRoutine(UChar32 c) {
1014     return thisKeyMetaCtrl | CTRL | UP_ARROW_KEY;
1015 }
ctrlDownArrowKeyRoutine(UChar32 c)1016 static UChar32 ctrlDownArrowKeyRoutine(UChar32 c) {
1017     return thisKeyMetaCtrl | CTRL | DOWN_ARROW_KEY;
1018 }
ctrlRightArrowKeyRoutine(UChar32 c)1019 static UChar32 ctrlRightArrowKeyRoutine(UChar32 c) {
1020     return thisKeyMetaCtrl | CTRL | RIGHT_ARROW_KEY;
1021 }
ctrlLeftArrowKeyRoutine(UChar32 c)1022 static UChar32 ctrlLeftArrowKeyRoutine(UChar32 c) {
1023     return thisKeyMetaCtrl | CTRL | LEFT_ARROW_KEY;
1024 }
escFailureRoutine(UChar32 c)1025 static UChar32 escFailureRoutine(UChar32 c) {
1026     beep();
1027     return -1;
1028 }
1029 
1030 // Handle ESC [ 1 ; 3 (or 5) <more stuff> escape sequences
1031 //
1032 static CharacterDispatchRoutine escLeftBracket1Semicolon3or5Routines[] = {upArrowKeyRoutine,
1033                                                                           downArrowKeyRoutine,
1034                                                                           rightArrowKeyRoutine,
1035                                                                           leftArrowKeyRoutine,
1036                                                                           escFailureRoutine};
1037 static CharacterDispatch escLeftBracket1Semicolon3or5Dispatch = {
1038     4, "ABCD", escLeftBracket1Semicolon3or5Routines};
1039 
1040 // Handle ESC [ 1 ; <more stuff> escape sequences
1041 //
escLeftBracket1Semicolon3Routine(UChar32 c)1042 static UChar32 escLeftBracket1Semicolon3Routine(UChar32 c) {
1043     c = readUnicodeCharacter();
1044     if (c == 0)
1045         return 0;
1046     thisKeyMetaCtrl |= META;
1047     return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch);
1048 }
escLeftBracket1Semicolon5Routine(UChar32 c)1049 static UChar32 escLeftBracket1Semicolon5Routine(UChar32 c) {
1050     c = readUnicodeCharacter();
1051     if (c == 0)
1052         return 0;
1053     thisKeyMetaCtrl |= CTRL;
1054     return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch);
1055 }
1056 static CharacterDispatchRoutine escLeftBracket1SemicolonRoutines[] = {
1057     escLeftBracket1Semicolon3Routine, escLeftBracket1Semicolon5Routine, escFailureRoutine};
1058 static CharacterDispatch escLeftBracket1SemicolonDispatch = {
1059     2, "35", escLeftBracket1SemicolonRoutines};
1060 
1061 // Handle ESC [ 1 <more stuff> escape sequences
1062 //
escLeftBracket1SemicolonRoutine(UChar32 c)1063 static UChar32 escLeftBracket1SemicolonRoutine(UChar32 c) {
1064     c = readUnicodeCharacter();
1065     if (c == 0)
1066         return 0;
1067     return doDispatch(c, escLeftBracket1SemicolonDispatch);
1068 }
1069 static CharacterDispatchRoutine escLeftBracket1Routines[] = {
1070     homeKeyRoutine, escLeftBracket1SemicolonRoutine, escFailureRoutine};
1071 static CharacterDispatch escLeftBracket1Dispatch = {2, "~;", escLeftBracket1Routines};
1072 
1073 // Handle ESC [ 3 <more stuff> escape sequences
1074 //
1075 static CharacterDispatchRoutine escLeftBracket3Routines[] = {deleteKeyRoutine, escFailureRoutine};
1076 static CharacterDispatch escLeftBracket3Dispatch = {1, "~", escLeftBracket3Routines};
1077 
1078 // Handle ESC [ 4 <more stuff> escape sequences
1079 //
1080 static CharacterDispatchRoutine escLeftBracket4Routines[] = {endKeyRoutine, escFailureRoutine};
1081 static CharacterDispatch escLeftBracket4Dispatch = {1, "~", escLeftBracket4Routines};
1082 
1083 // Handle ESC [ 5 <more stuff> escape sequences
1084 //
1085 static CharacterDispatchRoutine escLeftBracket5Routines[] = {pageUpKeyRoutine, escFailureRoutine};
1086 static CharacterDispatch escLeftBracket5Dispatch = {1, "~", escLeftBracket5Routines};
1087 
1088 // Handle ESC [ 6 <more stuff> escape sequences
1089 //
1090 static CharacterDispatchRoutine escLeftBracket6Routines[] = {pageDownKeyRoutine, escFailureRoutine};
1091 static CharacterDispatch escLeftBracket6Dispatch = {1, "~", escLeftBracket6Routines};
1092 
1093 // Handle ESC [ 7 <more stuff> escape sequences
1094 //
1095 static CharacterDispatchRoutine escLeftBracket7Routines[] = {homeKeyRoutine, escFailureRoutine};
1096 static CharacterDispatch escLeftBracket7Dispatch = {1, "~", escLeftBracket7Routines};
1097 
1098 // Handle ESC [ 8 <more stuff> escape sequences
1099 //
1100 static CharacterDispatchRoutine escLeftBracket8Routines[] = {endKeyRoutine, escFailureRoutine};
1101 static CharacterDispatch escLeftBracket8Dispatch = {1, "~", escLeftBracket8Routines};
1102 
1103 // Handle ESC [ <digit> escape sequences
1104 //
escLeftBracket0Routine(UChar32 c)1105 static UChar32 escLeftBracket0Routine(UChar32 c) {
1106     return escFailureRoutine(c);
1107 }
escLeftBracket1Routine(UChar32 c)1108 static UChar32 escLeftBracket1Routine(UChar32 c) {
1109     c = readUnicodeCharacter();
1110     if (c == 0)
1111         return 0;
1112     return doDispatch(c, escLeftBracket1Dispatch);
1113 }
escLeftBracket2Routine(UChar32 c)1114 static UChar32 escLeftBracket2Routine(UChar32 c) {
1115     return escFailureRoutine(c);  // Insert key, unused
1116 }
escLeftBracket3Routine(UChar32 c)1117 static UChar32 escLeftBracket3Routine(UChar32 c) {
1118     c = readUnicodeCharacter();
1119     if (c == 0)
1120         return 0;
1121     return doDispatch(c, escLeftBracket3Dispatch);
1122 }
escLeftBracket4Routine(UChar32 c)1123 static UChar32 escLeftBracket4Routine(UChar32 c) {
1124     c = readUnicodeCharacter();
1125     if (c == 0)
1126         return 0;
1127     return doDispatch(c, escLeftBracket4Dispatch);
1128 }
escLeftBracket5Routine(UChar32 c)1129 static UChar32 escLeftBracket5Routine(UChar32 c) {
1130     c = readUnicodeCharacter();
1131     if (c == 0)
1132         return 0;
1133     return doDispatch(c, escLeftBracket5Dispatch);
1134 }
escLeftBracket6Routine(UChar32 c)1135 static UChar32 escLeftBracket6Routine(UChar32 c) {
1136     c = readUnicodeCharacter();
1137     if (c == 0)
1138         return 0;
1139     return doDispatch(c, escLeftBracket6Dispatch);
1140 }
escLeftBracket7Routine(UChar32 c)1141 static UChar32 escLeftBracket7Routine(UChar32 c) {
1142     c = readUnicodeCharacter();
1143     if (c == 0)
1144         return 0;
1145     return doDispatch(c, escLeftBracket7Dispatch);
1146 }
escLeftBracket8Routine(UChar32 c)1147 static UChar32 escLeftBracket8Routine(UChar32 c) {
1148     c = readUnicodeCharacter();
1149     if (c == 0)
1150         return 0;
1151     return doDispatch(c, escLeftBracket8Dispatch);
1152 }
escLeftBracket9Routine(UChar32 c)1153 static UChar32 escLeftBracket9Routine(UChar32 c) {
1154     return escFailureRoutine(c);
1155 }
1156 
1157 // Handle ESC [ <more stuff> escape sequences
1158 //
1159 static CharacterDispatchRoutine escLeftBracketRoutines[] = {upArrowKeyRoutine,
1160                                                             downArrowKeyRoutine,
1161                                                             rightArrowKeyRoutine,
1162                                                             leftArrowKeyRoutine,
1163                                                             homeKeyRoutine,
1164                                                             endKeyRoutine,
1165                                                             escLeftBracket0Routine,
1166                                                             escLeftBracket1Routine,
1167                                                             escLeftBracket2Routine,
1168                                                             escLeftBracket3Routine,
1169                                                             escLeftBracket4Routine,
1170                                                             escLeftBracket5Routine,
1171                                                             escLeftBracket6Routine,
1172                                                             escLeftBracket7Routine,
1173                                                             escLeftBracket8Routine,
1174                                                             escLeftBracket9Routine,
1175                                                             escFailureRoutine};
1176 static CharacterDispatch escLeftBracketDispatch = {16, "ABCDHF0123456789", escLeftBracketRoutines};
1177 
1178 // Handle ESC O <char> escape sequences
1179 //
1180 static CharacterDispatchRoutine escORoutines[] = {upArrowKeyRoutine,
1181                                                   downArrowKeyRoutine,
1182                                                   rightArrowKeyRoutine,
1183                                                   leftArrowKeyRoutine,
1184                                                   homeKeyRoutine,
1185                                                   endKeyRoutine,
1186                                                   ctrlUpArrowKeyRoutine,
1187                                                   ctrlDownArrowKeyRoutine,
1188                                                   ctrlRightArrowKeyRoutine,
1189                                                   ctrlLeftArrowKeyRoutine,
1190                                                   escFailureRoutine};
1191 static CharacterDispatch escODispatch = {10, "ABCDHFabcd", escORoutines};
1192 
1193 // Initial ESC dispatch -- could be a Meta prefix or the start of an escape sequence
1194 //
escLeftBracketRoutine(UChar32 c)1195 static UChar32 escLeftBracketRoutine(UChar32 c) {
1196     c = readUnicodeCharacter();
1197     if (c == 0)
1198         return 0;
1199     return doDispatch(c, escLeftBracketDispatch);
1200 }
escORoutine(UChar32 c)1201 static UChar32 escORoutine(UChar32 c) {
1202     c = readUnicodeCharacter();
1203     if (c == 0)
1204         return 0;
1205     return doDispatch(c, escODispatch);
1206 }
1207 static UChar32 setMetaRoutine(UChar32 c);  // need forward reference
1208 static CharacterDispatchRoutine escRoutines[] = {
1209     escLeftBracketRoutine, escORoutine, setMetaRoutine};
1210 static CharacterDispatch escDispatch = {2, "[O", escRoutines};
1211 
1212 // Initial dispatch -- we are not in the middle of anything yet
1213 //
escRoutine(UChar32 c)1214 static UChar32 escRoutine(UChar32 c) {
1215     c = readUnicodeCharacter();
1216     if (c == 0)
1217         return 0;
1218     return doDispatch(c, escDispatch);
1219 }
1220 static CharacterDispatchRoutine initialRoutines[] = {
1221     escRoutine, deleteCharRoutine, normalKeyRoutine};
1222 static CharacterDispatch initialDispatch = {2, "\x1B\x7F", initialRoutines};
1223 
1224 // Special handling for the ESC key because it does double duty
1225 //
setMetaRoutine(UChar32 c)1226 static UChar32 setMetaRoutine(UChar32 c) {
1227     thisKeyMetaCtrl = META;
1228     if (c == 0x1B) {  // another ESC, stay in ESC processing mode
1229         c = readUnicodeCharacter();
1230         if (c == 0)
1231             return 0;
1232         return doDispatch(c, escDispatch);
1233     }
1234     return doDispatch(c, initialDispatch);
1235 }
1236 
1237 }  // namespace EscapeSequenceProcessing // move these out of global namespace
1238 
1239 #endif  // #ifndef _WIN32
1240 
1241 // linenoiseReadChar -- read a keystroke or keychord from the keyboard, and translate it
1242 // into an encoded "keystroke".  When convenient, extended keys are translated into their
1243 // simpler Emacs keystrokes, so an unmodified "left arrow" becomes Ctrl-B.
1244 //
1245 // A return value of zero means "no input available", and a return value of -1 means "invalid key".
1246 //
linenoiseReadChar(void)1247 static UChar32 linenoiseReadChar(void) {
1248 #ifdef _WIN32
1249 
1250     INPUT_RECORD rec;
1251     DWORD count;
1252     int modifierKeys = 0;
1253     bool escSeen = false;
1254     while (true) {
1255         ReadConsoleInputW(console_in, &rec, 1, &count);
1256 #if 0  // helper for debugging keystrokes, display info in the debug "Output" window in the debugger
1257         {
1258             if ( rec.EventType == KEY_EVENT ) {
1259                 //if ( rec.Event.KeyEvent.uChar.UnicodeChar ) {
1260                     char buf[1024];
1261                     sprintf(
1262                             buf,
1263                             "Unicode character 0x%04X, repeat count %d, virtual keycode 0x%04X, "
1264                             "virtual scancode 0x%04X, key %s%s%s%s%s\n",
1265                             rec.Event.KeyEvent.uChar.UnicodeChar,
1266                             rec.Event.KeyEvent.wRepeatCount,
1267                             rec.Event.KeyEvent.wVirtualKeyCode,
1268                             rec.Event.KeyEvent.wVirtualScanCode,
1269                             rec.Event.KeyEvent.bKeyDown ? "down" : "up",
1270                                 (rec.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED)  ?
1271                                     " L-Ctrl" : "",
1272                                 (rec.Event.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED) ?
1273                                     " R-Ctrl" : "",
1274                                 (rec.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED)   ?
1275                                     " L-Alt"  : "",
1276                                 (rec.Event.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED)  ?
1277                                     " R-Alt"  : ""
1278                            );
1279                     OutputDebugStringA( buf );
1280                 //}
1281             }
1282         }
1283 #endif
1284         if (rec.EventType != KEY_EVENT) {
1285             continue;
1286         }
1287         // Windows provides for entry of characters that are not on your keyboard by sending the
1288         // Unicode characters as a "key up" with virtual keycode 0x12 (VK_MENU == Alt key) ...
1289         // accept these characters, otherwise only process characters on "key down"
1290         if (!rec.Event.KeyEvent.bKeyDown && rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) {
1291             continue;
1292         }
1293         modifierKeys = 0;
1294         // AltGr is encoded as ( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED ), so don't treat this
1295         // combination as either CTRL or META we just turn off those two bits, so it is still
1296         // possible to combine CTRL and/or META with an AltGr key by using right-Ctrl and/or
1297         // left-Alt
1298         if ((rec.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) ==
1299             (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) {
1300             rec.Event.KeyEvent.dwControlKeyState &= ~(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
1301         }
1302         if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
1303             modifierKeys |= CTRL;
1304         }
1305         if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
1306             modifierKeys |= META;
1307         }
1308         if (escSeen) {
1309             modifierKeys |= META;
1310         }
1311         if (rec.Event.KeyEvent.uChar.UnicodeChar == 0) {
1312             switch (rec.Event.KeyEvent.wVirtualKeyCode) {
1313                 case VK_LEFT:
1314                     return modifierKeys | LEFT_ARROW_KEY;
1315                 case VK_RIGHT:
1316                     return modifierKeys | RIGHT_ARROW_KEY;
1317                 case VK_UP:
1318                     return modifierKeys | UP_ARROW_KEY;
1319                 case VK_DOWN:
1320                     return modifierKeys | DOWN_ARROW_KEY;
1321                 case VK_DELETE:
1322                     return modifierKeys | DELETE_KEY;
1323                 case VK_HOME:
1324                     return modifierKeys | HOME_KEY;
1325                 case VK_END:
1326                     return modifierKeys | END_KEY;
1327                 case VK_PRIOR:
1328                     return modifierKeys | PAGE_UP_KEY;
1329                 case VK_NEXT:
1330                     return modifierKeys | PAGE_DOWN_KEY;
1331                 default:
1332                     continue;  // in raw mode, ReadConsoleInput shows shift, ctrl ...
1333             }                  //  ... ignore them
1334         } else if (rec.Event.KeyEvent.uChar.UnicodeChar ==
1335                    ctrlChar('[')) {  // ESC, set flag for later
1336             escSeen = true;
1337             continue;
1338         } else {
1339             // we got a real character, return it
1340             return modifierKeys | rec.Event.KeyEvent.uChar.UnicodeChar;
1341         }
1342     }
1343 
1344 #else
1345     UChar32 c;
1346     c = readUnicodeCharacter();
1347     if (c == 0)
1348         return 0;
1349 
1350 // If _DEBUG_LINUX_KEYBOARD is set, then ctrl-^ puts us into a keyboard debugging mode
1351 // where we print out decimal and decoded values for whatever the "terminal" program
1352 // gives us on different keystrokes.  Hit ctrl-C to exit this mode.
1353 //
1354 #define _DEBUG_LINUX_KEYBOARD
1355 #if defined(_DEBUG_LINUX_KEYBOARD)
1356     if (c == ctrlChar('^')) {  // ctrl-^, special debug mode, prints all keys hit, ctrl-C to get out
1357         printf("\nEntering keyboard debugging mode (on ctrl-^), press ctrl-C to exit this mode\n");
1358         while (true) {
1359             unsigned char keys[10];
1360             int ret = read(0, keys, 10);
1361 
1362             if (ret <= 0) {
1363                 printf("\nret: %d\n", ret);
1364             }
1365             for (int i = 0; i < ret; ++i) {
1366                 UChar32 key = static_cast<UChar32>(keys[i]);
1367                 char* friendlyTextPtr;
1368                 char friendlyTextBuf[10];
1369                 const char* prefixText = (key < 0x80) ? "" : "0x80+";
1370                 UChar32 keyCopy = (key < 0x80) ? key : key - 0x80;
1371                 if (keyCopy >= '!' && keyCopy <= '~') {  // printable
1372                     friendlyTextBuf[0] = '\'';
1373                     friendlyTextBuf[1] = keyCopy;
1374                     friendlyTextBuf[2] = '\'';
1375                     friendlyTextBuf[3] = 0;
1376                     friendlyTextPtr = friendlyTextBuf;
1377                 } else if (keyCopy == ' ') {
1378                     friendlyTextPtr = const_cast<char*>("space");
1379                 } else if (keyCopy == 27) {
1380                     friendlyTextPtr = const_cast<char*>("ESC");
1381                 } else if (keyCopy == 0) {
1382                     friendlyTextPtr = const_cast<char*>("NUL");
1383                 } else if (keyCopy == 127) {
1384                     friendlyTextPtr = const_cast<char*>("DEL");
1385                 } else {
1386                     friendlyTextBuf[0] = '^';
1387                     friendlyTextBuf[1] = keyCopy + 0x40;
1388                     friendlyTextBuf[2] = 0;
1389                     friendlyTextPtr = friendlyTextBuf;
1390                 }
1391                 printf("%d x%02X (%s%s)  ", key, key, prefixText, friendlyTextPtr);
1392             }
1393             printf("\x1b[1G\n");  // go to first column of new line
1394 
1395             // drop out of this loop on ctrl-C
1396             if (keys[0] == ctrlChar('C')) {
1397                 printf("Leaving keyboard debugging mode (on ctrl-C)\n");
1398                 fflush(stdout);
1399                 return -2;
1400             }
1401         }
1402     }
1403 #endif  // _DEBUG_LINUX_KEYBOARD
1404 
1405     EscapeSequenceProcessing::thisKeyMetaCtrl = 0;  // no modifiers yet at initialDispatch
1406     return EscapeSequenceProcessing::doDispatch(c, EscapeSequenceProcessing::initialDispatch);
1407 #endif  // #_WIN32
1408 }
1409 
1410 /**
1411  * Free memory used in a recent command completion session
1412  *
1413  * @param lc pointer to a linenoiseCompletions struct
1414  */
freeCompletions(linenoiseCompletions * lc)1415 static void freeCompletions(linenoiseCompletions* lc) {
1416     lc->completionStrings.clear();
1417 }
1418 
1419 /**
1420  * convert {CTRL + 'A'}, {CTRL + 'a'} and {CTRL + ctrlChar( 'A' )} into ctrlChar( 'A' )
1421  * leave META alone
1422  *
1423  * @param c character to clean up
1424  * @return cleaned-up character
1425  */
cleanupCtrl(int c)1426 static int cleanupCtrl(int c) {
1427     if (c & CTRL) {
1428         int d = c & 0x1FF;
1429         if (d >= 'a' && d <= 'z') {
1430             c = (c + ('a' - ctrlChar('A'))) & ~CTRL;
1431         }
1432         if (d >= 'A' && d <= 'Z') {
1433             c = (c + ('A' - ctrlChar('A'))) & ~CTRL;
1434         }
1435         if (d >= ctrlChar('A') && d <= ctrlChar('Z')) {
1436             c = c & ~CTRL;
1437         }
1438     }
1439     return c;
1440 }
1441 
1442 // break characters that may precede items to be completed
1443 static const char breakChars[] = " =+-/\\*?\"'`&<>;|@{([])}";
1444 
1445 // maximum number of completions to display without asking
1446 static const size_t completionCountCutoff = 100;
1447 
1448 /**
1449  * Handle command completion, using a completionCallback() routine to provide possible substitutions
1450  * This routine handles the mechanics of updating the user's input buffer with possible replacement
1451  * of text as the user selects a proposed completion string, or cancels the completion attempt.
1452  * @param pi     PromptBase struct holding information about the prompt and our screen position
1453  */
completeLine(PromptBase & pi)1454 int InputBuffer::completeLine(PromptBase& pi) {
1455     linenoiseCompletions lc;
1456     char c = 0;
1457 
1458     // completionCallback() expects a parsable entity, so find the previous break character and
1459     // extract a copy to parse.  we also handle the case where tab is hit while not at end-of-line.
1460     int startIndex = pos;
1461     while (--startIndex >= 0) {
1462         if (strchr(breakChars, buf32[startIndex])) {
1463             break;
1464         }
1465     }
1466     ++startIndex;
1467     int itemLength = pos - startIndex;
1468     Utf32String unicodeCopy(&buf32[startIndex], itemLength);
1469     Utf8String parseItem(unicodeCopy);
1470 
1471     // get a list of completions
1472     completionCallback(reinterpret_cast<char*>(parseItem.get()), &lc);
1473 
1474     // if no completions, we are done
1475     if (lc.completionStrings.size() == 0) {
1476         beep();
1477         freeCompletions(&lc);
1478         return 0;
1479     }
1480 
1481     // at least one completion
1482     int longestCommonPrefix = 0;
1483     int displayLength = 0;
1484     if (lc.completionStrings.size() == 1) {
1485         longestCommonPrefix = lc.completionStrings[0].length();
1486     } else {
1487         bool keepGoing = true;
1488         while (keepGoing) {
1489             for (size_t j = 0; j < lc.completionStrings.size() - 1; ++j) {
1490                 char c1 = lc.completionStrings[j][longestCommonPrefix];
1491                 char c2 = lc.completionStrings[j + 1][longestCommonPrefix];
1492                 if ((0 == c1) || (0 == c2) || (c1 != c2)) {
1493                     keepGoing = false;
1494                     break;
1495                 }
1496             }
1497             if (keepGoing) {
1498                 ++longestCommonPrefix;
1499             }
1500         }
1501     }
1502     if (lc.completionStrings.size() != 1) {  // beep if ambiguous
1503         beep();
1504     }
1505 
1506     // if we can extend the item, extend it and return to main loop
1507     if (longestCommonPrefix > itemLength) {
1508         displayLength = len + longestCommonPrefix - itemLength;
1509         if (displayLength > buflen) {
1510             longestCommonPrefix -= displayLength - buflen;  // don't overflow buffer
1511             displayLength = buflen;                         // truncate the insertion
1512             beep();                                         // and make a noise
1513         }
1514         Utf32String displayText(displayLength + 1);
1515         memcpy(displayText.get(), buf32, sizeof(UChar32) * startIndex);
1516         memcpy(&displayText[startIndex],
1517                &lc.completionStrings[0][0],
1518                sizeof(UChar32) * longestCommonPrefix);
1519         int tailIndex = startIndex + longestCommonPrefix;
1520         memcpy(&displayText[tailIndex],
1521                &buf32[pos],
1522                sizeof(UChar32) * (displayLength - tailIndex + 1));
1523         copyString32(buf32, displayText.get(), buflen + 1);
1524         pos = startIndex + longestCommonPrefix;
1525         len = displayLength;
1526         refreshLine(pi);
1527         return 0;
1528     }
1529 
1530     // we can't complete any further, wait for second tab
1531     do {
1532         c = linenoiseReadChar();
1533         c = cleanupCtrl(c);
1534     } while (c == static_cast<char>(-1));
1535 
1536     // if any character other than tab, pass it to the main loop
1537     if (c != ctrlChar('I')) {
1538         freeCompletions(&lc);
1539         return c;
1540     }
1541 
1542     // we got a second tab, maybe show list of possible completions
1543     bool showCompletions = true;
1544     bool onNewLine = false;
1545     if (lc.completionStrings.size() > completionCountCutoff) {
1546         int savePos = pos;  // move cursor to EOL to avoid overwriting the command line
1547         pos = len;
1548         refreshLine(pi);
1549         pos = savePos;
1550         printf("\nDisplay all %u possibilities? (y or n)",
1551                static_cast<unsigned int>(lc.completionStrings.size()));
1552         fflush(stdout);
1553         onNewLine = true;
1554         while (c != 'y' && c != 'Y' && c != 'n' && c != 'N' && c != ctrlChar('C')) {
1555             do {
1556                 c = linenoiseReadChar();
1557                 c = cleanupCtrl(c);
1558             } while (c == static_cast<char>(-1));
1559         }
1560         switch (c) {
1561             case 'n':
1562             case 'N':
1563                 showCompletions = false;
1564                 freeCompletions(&lc);
1565                 break;
1566             case ctrlChar('C'):
1567                 showCompletions = false;
1568                 freeCompletions(&lc);
1569                 if (write(1, "^C", 2) == -1)
1570                     return -1;  // Display the ^C we got
1571                 c = 0;
1572                 break;
1573         }
1574     }
1575 
1576     // if showing the list, do it the way readline does it
1577     bool stopList = false;
1578     if (showCompletions) {
1579         int longestCompletion = 0;
1580         for (size_t j = 0; j < lc.completionStrings.size(); ++j) {
1581             itemLength = lc.completionStrings[j].length();
1582             if (itemLength > longestCompletion) {
1583                 longestCompletion = itemLength;
1584             }
1585         }
1586         longestCompletion += 2;
1587         int columnCount = pi.promptScreenColumns / longestCompletion;
1588         if (columnCount < 1) {
1589             columnCount = 1;
1590         }
1591         if (!onNewLine) {       // skip this if we showed "Display all %d possibilities?"
1592             int savePos = pos;  // move cursor to EOL to avoid overwriting the command line
1593             pos = len;
1594             refreshLine(pi);
1595             pos = savePos;
1596         }
1597         size_t pauseRow = getScreenRows() - 1;
1598         size_t rowCount = (lc.completionStrings.size() + columnCount - 1) / columnCount;
1599         for (size_t row = 0; row < rowCount; ++row) {
1600             if (row == pauseRow) {
1601                 printf("\n--More--");
1602                 fflush(stdout);
1603                 c = 0;
1604                 bool doBeep = false;
1605                 while (c != ' ' && c != '\r' && c != '\n' && c != 'y' && c != 'Y' && c != 'n' &&
1606                        c != 'N' && c != 'q' && c != 'Q' && c != ctrlChar('C')) {
1607                     if (doBeep) {
1608                         beep();
1609                     }
1610                     doBeep = true;
1611                     do {
1612                         c = linenoiseReadChar();
1613                         c = cleanupCtrl(c);
1614                     } while (c == static_cast<char>(-1));
1615                 }
1616                 switch (c) {
1617                     case ' ':
1618                     case 'y':
1619                     case 'Y':
1620                         printf("\r        \r");
1621                         pauseRow += getScreenRows() - 1;
1622                         break;
1623                     case '\r':
1624                     case '\n':
1625                         printf("\r        \r");
1626                         ++pauseRow;
1627                         break;
1628                     case 'n':
1629                     case 'N':
1630                     case 'q':
1631                     case 'Q':
1632                         printf("\r        \r");
1633                         stopList = true;
1634                         break;
1635                     case ctrlChar('C'):
1636                         if (write(1, "^C", 2) == -1)
1637                             return -1;  // Display the ^C we got
1638                         stopList = true;
1639                         break;
1640                 }
1641             } else {
1642                 printf("\n");
1643             }
1644             if (stopList) {
1645                 break;
1646             }
1647             for (int column = 0; column < columnCount; ++column) {
1648                 size_t index = (column * rowCount) + row;
1649                 if (index < lc.completionStrings.size()) {
1650                     itemLength = lc.completionStrings[index].length();
1651                     fflush(stdout);
1652                     if (write32(1, lc.completionStrings[index].get(), itemLength) == -1)
1653                         return -1;
1654                     if (((column + 1) * rowCount) + row < lc.completionStrings.size()) {
1655                         for (int k = itemLength; k < longestCompletion; ++k) {
1656                             printf(" ");
1657                         }
1658                     }
1659                 }
1660             }
1661         }
1662         fflush(stdout);
1663         freeCompletions(&lc);
1664     }
1665 
1666     // display the prompt on a new line, then redisplay the input buffer
1667     if (!stopList || c == ctrlChar('C')) {
1668         if (write(1, "\n", 1) == -1)
1669             return 0;
1670     }
1671     if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
1672         return 0;
1673 #ifndef _WIN32
1674     // we have to generate our own newline on line wrap on Linux
1675     if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
1676         if (write(1, "\n", 1) == -1)
1677             return 0;
1678 #endif
1679     pi.promptCursorRowOffset = pi.promptExtraLines;
1680     refreshLine(pi);
1681     return 0;
1682 }
1683 
1684 /**
1685  * Clear the screen ONLY (no redisplay of anything)
1686  */
linenoiseClearScreen(void)1687 void linenoiseClearScreen(void) {
1688 #ifdef _WIN32
1689     COORD coord = {0, 0};
1690     CONSOLE_SCREEN_BUFFER_INFO inf;
1691     HANDLE screenHandle = GetStdHandle(STD_OUTPUT_HANDLE);
1692     GetConsoleScreenBufferInfo(screenHandle, &inf);
1693     SetConsoleCursorPosition(screenHandle, coord);
1694     DWORD count;
1695     FillConsoleOutputCharacterA(screenHandle, ' ', inf.dwSize.X * inf.dwSize.Y, coord, &count);
1696 #else
1697     if (write(1, "\x1b[H\x1b[2J", 7) <= 0)
1698         return;
1699 #endif
1700 }
1701 
clearScreen(PromptBase & pi)1702 void InputBuffer::clearScreen(PromptBase& pi) {
1703     linenoiseClearScreen();
1704     if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
1705         return;
1706 #ifndef _WIN32
1707     // we have to generate our own newline on line wrap on Linux
1708     if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
1709         if (write(1, "\n", 1) == -1)
1710             return;
1711 #endif
1712     pi.promptCursorRowOffset = pi.promptExtraLines;
1713     refreshLine(pi);
1714 }
1715 
1716 /**
1717  * Incremental history search -- take over the prompt and keyboard as the user types a search
1718  * string, deletes characters from it, changes direction, and either accepts the found line (for
1719  * execution orediting) or cancels.
1720  * @param pi        PromptBase struct holding information about the (old, static) prompt and our
1721  *                  screen position
1722  * @param startChar the character that began the search, used to set the initial direction
1723  */
incrementalHistorySearch(PromptBase & pi,int startChar)1724 int InputBuffer::incrementalHistorySearch(PromptBase& pi, int startChar) {
1725     size_t bufferSize;
1726     size_t ucharCount;
1727     int errorCode;
1728 
1729     // if not already recalling, add the current line to the history list so we don't have to
1730     // special case it
1731     if (historyIndex == historyLen - 1) {
1732         free(history[historyLen - 1]);
1733         bufferSize = sizeof(UChar32) * len + 1;
1734         unique_ptr<UChar8[]> tempBuffer(new UChar8[bufferSize]);
1735         copyString32to8(tempBuffer.get(), buf32, bufferSize);
1736         history[historyLen - 1] =
1737             reinterpret_cast<UChar8*>(strdup(reinterpret_cast<const char*>(tempBuffer.get())));
1738     }
1739     int historyLineLength = len;
1740     int historyLinePosition = pos;
1741     UChar32 emptyBuffer[1];
1742     char emptyWidths[1];
1743     InputBuffer empty(emptyBuffer, emptyWidths, 1);
1744     empty.refreshLine(pi);  // erase the old input first
1745     DynamicPrompt dp(pi, (startChar == ctrlChar('R')) ? -1 : 1);
1746 
1747     dp.promptPreviousLen = pi.promptPreviousLen;
1748     dp.promptPreviousInputLen = pi.promptPreviousInputLen;
1749     dynamicRefresh(
1750         dp, buf32, historyLineLength, historyLinePosition);  // draw user's text with our prompt
1751 
1752     // loop until we get an exit character
1753     int c;
1754     bool keepLooping = true;
1755     bool useSearchedLine = true;
1756     bool searchAgain = false;
1757     UChar32* activeHistoryLine = 0;
1758     while (keepLooping) {
1759         c = linenoiseReadChar();
1760         c = cleanupCtrl(c);  // convert CTRL + <char> into normal ctrl
1761 
1762         switch (c) {
1763             // these characters keep the selected text but do not execute it
1764             case ctrlChar('A'):  // ctrl-A, move cursor to start of line
1765             case HOME_KEY:
1766             case ctrlChar('B'):  // ctrl-B, move cursor left by one character
1767             case LEFT_ARROW_KEY:
1768             case META + 'b':  // meta-B, move cursor left by one word
1769             case META + 'B':
1770             case CTRL + LEFT_ARROW_KEY:
1771             case META + LEFT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
1772             case ctrlChar('D'):
1773             case META + 'd':  // meta-D, kill word to right of cursor
1774             case META + 'D':
1775             case ctrlChar('E'):  // ctrl-E, move cursor to end of line
1776             case END_KEY:
1777             case ctrlChar('F'):  // ctrl-F, move cursor right by one character
1778             case RIGHT_ARROW_KEY:
1779             case META + 'f':  // meta-F, move cursor right by one word
1780             case META + 'F':
1781             case CTRL + RIGHT_ARROW_KEY:
1782             case META + RIGHT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
1783             case META + ctrlChar('H'):
1784             case ctrlChar('J'):
1785             case ctrlChar('K'):  // ctrl-K, kill from cursor to end of line
1786             case ctrlChar('M'):
1787             case ctrlChar('N'):  // ctrl-N, recall next line in history
1788             case ctrlChar('P'):  // ctrl-P, recall previous line in history
1789             case DOWN_ARROW_KEY:
1790             case UP_ARROW_KEY:
1791             case ctrlChar('T'):  // ctrl-T, transpose characters
1792             case ctrlChar('U'):  // ctrl-U, kill all characters to the left of the cursor
1793             case ctrlChar('W'):
1794             case META + 'y':  // meta-Y, "yank-pop", rotate popped text
1795             case META + 'Y':
1796             case 127:
1797             case DELETE_KEY:
1798             case META + '<':  // start of history
1799             case PAGE_UP_KEY:
1800             case META + '>':  // end of history
1801             case PAGE_DOWN_KEY:
1802                 keepLooping = false;
1803                 break;
1804 
1805             // these characters revert the input line to its previous state
1806             case ctrlChar('C'):  // ctrl-C, abort this line
1807             case ctrlChar('G'):
1808             case ctrlChar('L'):  // ctrl-L, clear screen and redisplay line
1809                 keepLooping = false;
1810                 useSearchedLine = false;
1811                 if (c != ctrlChar('L')) {
1812                     c = -1;  // ctrl-C and ctrl-G just abort the search and do nothing else
1813                 }
1814                 break;
1815 
1816             // these characters stay in search mode and update the display
1817             case ctrlChar('S'):
1818             case ctrlChar('R'):
1819                 if (dp.searchTextLen == 0) {  // if no current search text, recall previous text
1820                     if (previousSearchText.length()) {
1821                         dp.updateSearchText(previousSearchText.get());
1822                     }
1823                 }
1824                 if ((dp.direction == 1 && c == ctrlChar('R')) ||
1825                     (dp.direction == -1 && c == ctrlChar('S'))) {
1826                     dp.direction = 0 - dp.direction;  // reverse direction
1827                     dp.updateSearchPrompt();          // change the prompt
1828                 } else {
1829                     searchAgain = true;  // same direction, search again
1830                 }
1831                 break;
1832 
1833 // job control is its own thing
1834 #ifndef _WIN32
1835             case ctrlChar('Z'):    // ctrl-Z, job control
1836                 disableRawMode();  // Returning to Linux (whatever) shell, leave raw mode
1837                 raise(SIGSTOP);    // Break out in mid-line
1838                 enableRawMode();   // Back from Linux shell, re-enter raw mode
1839                 {
1840                     bufferSize = historyLineLength + 1;
1841                     unique_ptr<UChar32[]> tempUnicode(new UChar32[bufferSize]);
1842                     copyString8to32(tempUnicode.get(),
1843                                     history[historyIndex],
1844                                     bufferSize,
1845                                     ucharCount,
1846                                     errorCode);
1847                     dynamicRefresh(dp, tempUnicode.get(), historyLineLength, historyLinePosition);
1848                 }
1849                 continue;
1850                 break;
1851 #endif
1852 
1853             // these characters update the search string, and hence the selected input line
1854             case ctrlChar('H'):  // backspace/ctrl-H, delete char to left of cursor
1855                 if (dp.searchTextLen > 0) {
1856                     unique_ptr<UChar32[]> tempUnicode(new UChar32[dp.searchTextLen]);
1857                     --dp.searchTextLen;
1858                     dp.searchText[dp.searchTextLen] = 0;
1859                     copyString32(tempUnicode.get(), dp.searchText.get(), dp.searchTextLen + 1);
1860                     dp.updateSearchText(tempUnicode.get());
1861                 } else {
1862                     beep();
1863                 }
1864                 break;
1865 
1866             case ctrlChar('Y'):  // ctrl-Y, yank killed text
1867                 break;
1868 
1869             default:
1870                 if (!isControlChar(c) && c <= 0x0010FFFF) {  // not an action character
1871                     unique_ptr<UChar32[]> tempUnicode(new UChar32[dp.searchTextLen + 2]);
1872                     copyString32(tempUnicode.get(), dp.searchText.get(), dp.searchTextLen + 2);
1873                     tempUnicode[dp.searchTextLen] = c;
1874                     tempUnicode[dp.searchTextLen + 1] = 0;
1875                     dp.updateSearchText(tempUnicode.get());
1876                 } else {
1877                     beep();
1878                 }
1879         }  // switch
1880 
1881         // if we are staying in search mode, search now
1882         if (keepLooping) {
1883             bufferSize = historyLineLength + 1;
1884             activeHistoryLine = new UChar32[bufferSize];
1885             copyString8to32(
1886                 activeHistoryLine, history[historyIndex], bufferSize, ucharCount, errorCode);
1887             if (dp.searchTextLen > 0) {
1888                 bool found = false;
1889                 int historySearchIndex = historyIndex;
1890                 int lineLength = ucharCount;
1891                 int lineSearchPos = historyLinePosition;
1892                 if (searchAgain) {
1893                     lineSearchPos += dp.direction;
1894                 }
1895                 searchAgain = false;
1896                 while (true) {
1897                     while ((dp.direction > 0) ? (lineSearchPos < lineLength)
1898                                               : (lineSearchPos >= 0)) {
1899                         if (strncmp32(dp.searchText.get(),
1900                                       &activeHistoryLine[lineSearchPos],
1901                                       dp.searchTextLen) == 0) {
1902                             found = true;
1903                             break;
1904                         }
1905                         lineSearchPos += dp.direction;
1906                     }
1907                     if (found) {
1908                         historyIndex = historySearchIndex;
1909                         historyLineLength = lineLength;
1910                         historyLinePosition = lineSearchPos;
1911                         break;
1912                     } else if ((dp.direction > 0) ? (historySearchIndex < historyLen - 1)
1913                                                   : (historySearchIndex > 0)) {
1914                         historySearchIndex += dp.direction;
1915                         bufferSize =
1916                             strlen(reinterpret_cast<char*>(history[historySearchIndex])) + 1;
1917                         delete[] activeHistoryLine;
1918                         activeHistoryLine = new UChar32[bufferSize];
1919                         copyString8to32(activeHistoryLine,
1920                                         history[historySearchIndex],
1921                                         bufferSize,
1922                                         ucharCount,
1923                                         errorCode);
1924                         lineLength = ucharCount;
1925                         lineSearchPos = (dp.direction > 0) ? 0 : (lineLength - dp.searchTextLen);
1926                     } else {
1927                         beep();
1928                         break;
1929                     }
1930                 };  // while
1931             }
1932             if (activeHistoryLine) {
1933                 delete[] activeHistoryLine;
1934             }
1935             bufferSize = historyLineLength + 1;
1936             activeHistoryLine = new UChar32[bufferSize];
1937             copyString8to32(
1938                 activeHistoryLine, history[historyIndex], bufferSize, ucharCount, errorCode);
1939             dynamicRefresh(dp,
1940                            activeHistoryLine,
1941                            historyLineLength,
1942                            historyLinePosition);  // draw user's text with our prompt
1943         }
1944     }  // while
1945 
1946     // leaving history search, restore previous prompt, maybe make searched line current
1947     PromptBase pb;
1948     pb.promptChars = pi.promptIndentation;
1949     Utf32String tempUnicode(pb.promptChars + 1);
1950     copyString32(tempUnicode.get(), &pi.promptText[pi.promptLastLinePosition], pb.promptChars + 1);
1951     tempUnicode.initFromBuffer();
1952     pb.promptText = tempUnicode;
1953     pb.promptExtraLines = 0;
1954     pb.promptIndentation = pi.promptIndentation;
1955     pb.promptLastLinePosition = 0;
1956     pb.promptPreviousInputLen = historyLineLength;
1957     pb.promptCursorRowOffset = dp.promptCursorRowOffset;
1958     pb.promptScreenColumns = pi.promptScreenColumns;
1959     pb.promptPreviousLen = dp.promptChars;
1960     if (useSearchedLine && activeHistoryLine) {
1961         historyRecallMostRecent = true;
1962         copyString32(buf32, activeHistoryLine, buflen + 1);
1963         len = historyLineLength;
1964         pos = historyLinePosition;
1965     }
1966     if (activeHistoryLine) {
1967         delete[] activeHistoryLine;
1968     }
1969     dynamicRefresh(pb, buf32, len, pos);  // redraw the original prompt with current input
1970     pi.promptPreviousInputLen = len;
1971     pi.promptCursorRowOffset = pi.promptExtraLines + pb.promptCursorRowOffset;
1972     previousSearchText = dp.searchText;  // save search text for possible reuse on ctrl-R ctrl-R
1973     return c;                            // pass a character or -1 back to main loop
1974 }
1975 
isCharacterAlphanumeric(UChar32 testChar)1976 static bool isCharacterAlphanumeric(UChar32 testChar) {
1977     return iswalnum(testChar);
1978 }
1979 
getInputLine(PromptBase & pi)1980 int InputBuffer::getInputLine(PromptBase& pi) {
1981     // The latest history entry is always our current buffer
1982     if (len > 0) {
1983         size_t bufferSize = sizeof(UChar32) * len + 1;
1984         unique_ptr<char[]> tempBuffer(new char[bufferSize]);
1985         copyString32to8(reinterpret_cast<UChar8*>(tempBuffer.get()), buf32, bufferSize);
1986         linenoiseHistoryAdd(tempBuffer.get());
1987     } else {
1988         linenoiseHistoryAdd("");
1989     }
1990     historyIndex = historyLen - 1;
1991     historyRecallMostRecent = false;
1992 
1993     // display the prompt
1994     if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
1995         return -1;
1996 
1997 #ifndef _WIN32
1998     // we have to generate our own newline on line wrap on Linux
1999     if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
2000         if (write(1, "\n", 1) == -1)
2001             return -1;
2002 #endif
2003 
2004     // the cursor starts out at the end of the prompt
2005     pi.promptCursorRowOffset = pi.promptExtraLines;
2006 
2007     // kill and yank start in "other" mode
2008     killRing.lastAction = KillRing::actionOther;
2009 
2010     // when history search returns control to us, we execute its terminating keystroke
2011     int terminatingKeystroke = -1;
2012 
2013     // if there is already text in the buffer, display it first
2014     if (len > 0) {
2015         refreshLine(pi);
2016     }
2017 
2018     // loop collecting characters, respond to line editing characters
2019     while (true) {
2020         int c;
2021         if (terminatingKeystroke == -1) {
2022             c = linenoiseReadChar();  // get a new keystroke
2023         } else {
2024             c = terminatingKeystroke;   // use the terminating keystroke from search
2025             terminatingKeystroke = -1;  // clear it once we've used it
2026         }
2027         c = cleanupCtrl(c);  // convert CTRL + <char> into normal ctrl
2028 
2029         if (c == 0) {
2030             return len;
2031         }
2032 
2033         if (c == -1) {
2034             refreshLine(pi);
2035             continue;
2036         }
2037 
2038         if (c == -2) {
2039             if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
2040                 return -1;
2041             refreshLine(pi);
2042             continue;
2043         }
2044 
2045         // ctrl-I/tab, command completion, needs to be before switch statement
2046         if (c == ctrlChar('I') && completionCallback) {
2047             if (pos == 0)  // SERVER-4967 -- in earlier versions, you could paste previous output
2048                 continue;  //  back into the shell ... this output may have leading tabs.
2049                            // This hack (i.e. what the old code did) prevents command completion
2050                            //  on an empty line but lets users paste text with leading tabs.
2051 
2052             killRing.lastAction = KillRing::actionOther;
2053             historyRecallMostRecent = false;
2054 
2055             // completeLine does the actual completion and replacement
2056             c = completeLine(pi);
2057 
2058             if (c < 0)  // return on error
2059                 return len;
2060 
2061             if (c == 0)  // read next character when 0
2062                 continue;
2063 
2064             // deliberate fall-through here, so we use the terminating character
2065         }
2066 
2067         switch (c) {
2068             case ctrlChar('A'):  // ctrl-A, move cursor to start of line
2069             case HOME_KEY:
2070                 killRing.lastAction = KillRing::actionOther;
2071                 pos = 0;
2072                 refreshLine(pi);
2073                 break;
2074 
2075             case ctrlChar('B'):  // ctrl-B, move cursor left by one character
2076             case LEFT_ARROW_KEY:
2077                 killRing.lastAction = KillRing::actionOther;
2078                 if (pos > 0) {
2079                     --pos;
2080                     refreshLine(pi);
2081                 }
2082                 break;
2083 
2084             case META + 'b':  // meta-B, move cursor left by one word
2085             case META + 'B':
2086             case CTRL + LEFT_ARROW_KEY:
2087             case META + LEFT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
2088                 killRing.lastAction = KillRing::actionOther;
2089                 if (pos > 0) {
2090                     while (pos > 0 && !isCharacterAlphanumeric(buf32[pos - 1])) {
2091                         --pos;
2092                     }
2093                     while (pos > 0 && isCharacterAlphanumeric(buf32[pos - 1])) {
2094                         --pos;
2095                     }
2096                     refreshLine(pi);
2097                 }
2098                 break;
2099 
2100             case ctrlChar('C'):  // ctrl-C, abort this line
2101                 killRing.lastAction = KillRing::actionOther;
2102                 historyRecallMostRecent = false;
2103                 errno = EAGAIN;
2104                 --historyLen;
2105                 free(history[historyLen]);
2106                 // we need one last refresh with the cursor at the end of the line
2107                 // so we don't display the next prompt over the previous input line
2108                 pos = len;  // pass len as pos for EOL
2109                 refreshLine(pi);
2110                 if (write(1, "^C", 2) == -1)
2111                     return -1;  // Display the ^C we got
2112                 return -1;
2113 
2114             case META + 'c':  // meta-C, give word initial Cap
2115             case META + 'C':
2116                 killRing.lastAction = KillRing::actionOther;
2117                 historyRecallMostRecent = false;
2118                 if (pos < len) {
2119                     while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
2120                         ++pos;
2121                     }
2122                     if (pos < len && isCharacterAlphanumeric(buf32[pos])) {
2123                         if (buf32[pos] >= 'a' && buf32[pos] <= 'z') {
2124                             buf32[pos] += 'A' - 'a';
2125                         }
2126                         ++pos;
2127                     }
2128                     while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
2129                         if (buf32[pos] >= 'A' && buf32[pos] <= 'Z') {
2130                             buf32[pos] += 'a' - 'A';
2131                         }
2132                         ++pos;
2133                     }
2134                     refreshLine(pi);
2135                 }
2136                 break;
2137 
2138             // ctrl-D, delete the character under the cursor
2139             // on an empty line, exit the shell
2140             case ctrlChar('D'):
2141                 killRing.lastAction = KillRing::actionOther;
2142                 if (len > 0 && pos < len) {
2143                     historyRecallMostRecent = false;
2144                     memmove(buf32 + pos, buf32 + pos + 1, sizeof(UChar32) * (len - pos));
2145                     --len;
2146                     refreshLine(pi);
2147                 } else if (len == 0) {
2148                     --historyLen;
2149                     free(history[historyLen]);
2150                     return -1;
2151                 }
2152                 break;
2153 
2154             case META + 'd':  // meta-D, kill word to right of cursor
2155             case META + 'D':
2156                 if (pos < len) {
2157                     historyRecallMostRecent = false;
2158                     int endingPos = pos;
2159                     while (endingPos < len && !isCharacterAlphanumeric(buf32[endingPos])) {
2160                         ++endingPos;
2161                     }
2162                     while (endingPos < len && isCharacterAlphanumeric(buf32[endingPos])) {
2163                         ++endingPos;
2164                     }
2165                     killRing.kill(&buf32[pos], endingPos - pos, true);
2166                     memmove(
2167                         buf32 + pos, buf32 + endingPos, sizeof(UChar32) * (len - endingPos + 1));
2168                     len -= endingPos - pos;
2169                     refreshLine(pi);
2170                 }
2171                 killRing.lastAction = KillRing::actionKill;
2172                 break;
2173 
2174             case ctrlChar('E'):  // ctrl-E, move cursor to end of line
2175             case END_KEY:
2176                 killRing.lastAction = KillRing::actionOther;
2177                 pos = len;
2178                 refreshLine(pi);
2179                 break;
2180 
2181             case ctrlChar('F'):  // ctrl-F, move cursor right by one character
2182             case RIGHT_ARROW_KEY:
2183                 killRing.lastAction = KillRing::actionOther;
2184                 if (pos < len) {
2185                     ++pos;
2186                     refreshLine(pi);
2187                 }
2188                 break;
2189 
2190             case META + 'f':  // meta-F, move cursor right by one word
2191             case META + 'F':
2192             case CTRL + RIGHT_ARROW_KEY:
2193             case META + RIGHT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
2194                 killRing.lastAction = KillRing::actionOther;
2195                 if (pos < len) {
2196                     while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
2197                         ++pos;
2198                     }
2199                     while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
2200                         ++pos;
2201                     }
2202                     refreshLine(pi);
2203                 }
2204                 break;
2205 
2206             case ctrlChar('H'):  // backspace/ctrl-H, delete char to left of cursor
2207                 killRing.lastAction = KillRing::actionOther;
2208                 if (pos > 0) {
2209                     historyRecallMostRecent = false;
2210                     memmove(buf32 + pos - 1, buf32 + pos, sizeof(UChar32) * (1 + len - pos));
2211                     --pos;
2212                     --len;
2213                     refreshLine(pi);
2214                 }
2215                 break;
2216 
2217             // meta-Backspace, kill word to left of cursor
2218             case META + ctrlChar('H'):
2219                 if (pos > 0) {
2220                     historyRecallMostRecent = false;
2221                     int startingPos = pos;
2222                     while (pos > 0 && !isCharacterAlphanumeric(buf32[pos - 1])) {
2223                         --pos;
2224                     }
2225                     while (pos > 0 && isCharacterAlphanumeric(buf32[pos - 1])) {
2226                         --pos;
2227                     }
2228                     killRing.kill(&buf32[pos], startingPos - pos, false);
2229                     memmove(buf32 + pos,
2230                             buf32 + startingPos,
2231                             sizeof(UChar32) * (len - startingPos + 1));
2232                     len -= startingPos - pos;
2233                     refreshLine(pi);
2234                 }
2235                 killRing.lastAction = KillRing::actionKill;
2236                 break;
2237 
2238             case ctrlChar('J'):  // ctrl-J/linefeed/newline, accept line
2239             case ctrlChar('M'):  // ctrl-M/return/enter
2240                 killRing.lastAction = KillRing::actionOther;
2241                 // we need one last refresh with the cursor at the end of the line
2242                 // so we don't display the next prompt over the previous input line
2243                 pos = len;  // pass len as pos for EOL
2244                 refreshLine(pi);
2245                 historyPreviousIndex = historyRecallMostRecent ? historyIndex : -2;
2246                 --historyLen;
2247                 free(history[historyLen]);
2248                 return len;
2249 
2250             case ctrlChar('K'):  // ctrl-K, kill from cursor to end of line
2251                 killRing.kill(&buf32[pos], len - pos, true);
2252                 buf32[pos] = '\0';
2253                 len = pos;
2254                 refreshLine(pi);
2255                 killRing.lastAction = KillRing::actionKill;
2256                 historyRecallMostRecent = false;
2257                 break;
2258 
2259             case ctrlChar('L'):  // ctrl-L, clear screen and redisplay line
2260                 clearScreen(pi);
2261                 break;
2262 
2263             case META + 'l':  // meta-L, lowercase word
2264             case META + 'L':
2265                 killRing.lastAction = KillRing::actionOther;
2266                 if (pos < len) {
2267                     historyRecallMostRecent = false;
2268                     while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
2269                         ++pos;
2270                     }
2271                     while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
2272                         if (buf32[pos] >= 'A' && buf32[pos] <= 'Z') {
2273                             buf32[pos] += 'a' - 'A';
2274                         }
2275                         ++pos;
2276                     }
2277                     refreshLine(pi);
2278                 }
2279                 break;
2280 
2281             case ctrlChar('N'):  // ctrl-N, recall next line in history
2282             case ctrlChar('P'):  // ctrl-P, recall previous line in history
2283             case DOWN_ARROW_KEY:
2284             case UP_ARROW_KEY:
2285                 killRing.lastAction = KillRing::actionOther;
2286                 // if not already recalling, add the current line to the history list so we don't
2287                 // have to special case it
2288                 if (historyIndex == historyLen - 1) {
2289                     free(history[historyLen - 1]);
2290                     size_t tempBufferSize = sizeof(UChar32) * len + 1;
2291                     unique_ptr<UChar8[]> tempBuffer(new UChar8[tempBufferSize]);
2292                     copyString32to8(tempBuffer.get(), buf32, tempBufferSize);
2293                     history[historyLen - 1] = reinterpret_cast<UChar8*>(
2294                         strdup(reinterpret_cast<const char*>(tempBuffer.get())));
2295                 }
2296                 if (historyLen > 1) {
2297                     if (c == UP_ARROW_KEY) {
2298                         c = ctrlChar('P');
2299                     }
2300                     if (historyPreviousIndex != -2 && c != ctrlChar('P')) {
2301                         historyIndex = 1 + historyPreviousIndex;  // emulate Windows down-arrow
2302                     } else {
2303                         historyIndex += (c == ctrlChar('P')) ? -1 : 1;
2304                     }
2305                     historyPreviousIndex = -2;
2306                     if (historyIndex < 0) {
2307                         historyIndex = 0;
2308                         break;
2309                     } else if (historyIndex >= historyLen) {
2310                         historyIndex = historyLen - 1;
2311                         break;
2312                     }
2313                     historyRecallMostRecent = true;
2314                     size_t ucharCount;
2315                     int errorCode;
2316                     copyString8to32(buf32, history[historyIndex], buflen, ucharCount, errorCode);
2317                     len = pos = ucharCount;
2318                     refreshLine(pi);
2319                 }
2320                 break;
2321 
2322             case ctrlChar('R'):  // ctrl-R, reverse history search
2323             case ctrlChar('S'):  // ctrl-S, forward history search
2324                 terminatingKeystroke = incrementalHistorySearch(pi, c);
2325                 break;
2326 
2327             case ctrlChar('T'):  // ctrl-T, transpose characters
2328                 killRing.lastAction = KillRing::actionOther;
2329                 if (pos > 0 && len > 1) {
2330                     historyRecallMostRecent = false;
2331                     size_t leftCharPos = (pos == len) ? pos - 2 : pos - 1;
2332                     char aux = buf32[leftCharPos];
2333                     buf32[leftCharPos] = buf32[leftCharPos + 1];
2334                     buf32[leftCharPos + 1] = aux;
2335                     if (pos != len)
2336                         ++pos;
2337                     refreshLine(pi);
2338                 }
2339                 break;
2340 
2341             case ctrlChar('U'):  // ctrl-U, kill all characters to the left of the cursor
2342                 if (pos > 0) {
2343                     historyRecallMostRecent = false;
2344                     killRing.kill(&buf32[0], pos, false);
2345                     len -= pos;
2346                     memmove(buf32, buf32 + pos, sizeof(UChar32) * (len + 1));
2347                     pos = 0;
2348                     refreshLine(pi);
2349                 }
2350                 killRing.lastAction = KillRing::actionKill;
2351                 break;
2352 
2353             case META + 'u':  // meta-U, uppercase word
2354             case META + 'U':
2355                 killRing.lastAction = KillRing::actionOther;
2356                 if (pos < len) {
2357                     historyRecallMostRecent = false;
2358                     while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
2359                         ++pos;
2360                     }
2361                     while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
2362                         if (buf32[pos] >= 'a' && buf32[pos] <= 'z') {
2363                             buf32[pos] += 'A' - 'a';
2364                         }
2365                         ++pos;
2366                     }
2367                     refreshLine(pi);
2368                 }
2369                 break;
2370 
2371             // ctrl-W, kill to whitespace (not word) to left of cursor
2372             case ctrlChar('W'):
2373                 if (pos > 0) {
2374                     historyRecallMostRecent = false;
2375                     int startingPos = pos;
2376                     while (pos > 0 && buf32[pos - 1] == ' ') {
2377                         --pos;
2378                     }
2379                     while (pos > 0 && buf32[pos - 1] != ' ') {
2380                         --pos;
2381                     }
2382                     killRing.kill(&buf32[pos], startingPos - pos, false);
2383                     memmove(buf32 + pos,
2384                             buf32 + startingPos,
2385                             sizeof(UChar32) * (len - startingPos + 1));
2386                     len -= startingPos - pos;
2387                     refreshLine(pi);
2388                 }
2389                 killRing.lastAction = KillRing::actionKill;
2390                 break;
2391 
2392             case ctrlChar('Y'):  // ctrl-Y, yank killed text
2393                 historyRecallMostRecent = false;
2394                 {
2395                     Utf32String* restoredText = killRing.yank();
2396                     if (restoredText) {
2397                         bool truncated = false;
2398                         size_t ucharCount = restoredText->length();
2399                         if (ucharCount > static_cast<size_t>(buflen - len)) {
2400                             ucharCount = buflen - len;
2401                             truncated = true;
2402                         }
2403                         memmove(buf32 + pos + ucharCount,
2404                                 buf32 + pos,
2405                                 sizeof(UChar32) * (len - pos + 1));
2406                         memmove(buf32 + pos, restoredText->get(), sizeof(UChar32) * ucharCount);
2407                         pos += ucharCount;
2408                         len += ucharCount;
2409                         refreshLine(pi);
2410                         killRing.lastAction = KillRing::actionYank;
2411                         killRing.lastYankSize = ucharCount;
2412                         if (truncated) {
2413                             beep();
2414                         }
2415                     } else {
2416                         beep();
2417                     }
2418                 }
2419                 break;
2420 
2421             case META + 'y':  // meta-Y, "yank-pop", rotate popped text
2422             case META + 'Y':
2423                 if (killRing.lastAction == KillRing::actionYank) {
2424                     historyRecallMostRecent = false;
2425                     Utf32String* restoredText = killRing.yankPop();
2426                     if (restoredText) {
2427                         bool truncated = false;
2428                         size_t ucharCount = restoredText->length();
2429                         if (ucharCount >
2430                             static_cast<size_t>(killRing.lastYankSize + buflen - len)) {
2431                             ucharCount = killRing.lastYankSize + buflen - len;
2432                             truncated = true;
2433                         }
2434                         if (ucharCount > killRing.lastYankSize) {
2435                             memmove(buf32 + pos + ucharCount - killRing.lastYankSize,
2436                                     buf32 + pos,
2437                                     sizeof(UChar32) * (len - pos + 1));
2438                             memmove(buf32 + pos - killRing.lastYankSize,
2439                                     restoredText->get(),
2440                                     sizeof(UChar32) * ucharCount);
2441                         } else {
2442                             memmove(buf32 + pos - killRing.lastYankSize,
2443                                     restoredText->get(),
2444                                     sizeof(UChar32) * ucharCount);
2445                             memmove(buf32 + pos + ucharCount - killRing.lastYankSize,
2446                                     buf32 + pos,
2447                                     sizeof(UChar32) * (len - pos + 1));
2448                         }
2449                         pos += ucharCount - killRing.lastYankSize;
2450                         len += ucharCount - killRing.lastYankSize;
2451                         killRing.lastYankSize = ucharCount;
2452                         refreshLine(pi);
2453                         if (truncated) {
2454                             beep();
2455                         }
2456                         break;
2457                     }
2458                 }
2459                 beep();
2460                 break;
2461 
2462 #ifndef _WIN32
2463             case ctrlChar('Z'):    // ctrl-Z, job control
2464                 disableRawMode();  // Returning to Linux (whatever) shell, leave raw mode
2465                 raise(SIGSTOP);    // Break out in mid-line
2466                 enableRawMode();   // Back from Linux shell, re-enter raw mode
2467                 if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
2468                     break;        // Redraw prompt
2469                 refreshLine(pi);  // Refresh the line
2470                 break;
2471 #endif
2472 
2473             // DEL, delete the character under the cursor
2474             case 127:
2475             case DELETE_KEY:
2476                 killRing.lastAction = KillRing::actionOther;
2477                 if (len > 0 && pos < len) {
2478                     historyRecallMostRecent = false;
2479                     memmove(buf32 + pos, buf32 + pos + 1, sizeof(UChar32) * (len - pos));
2480                     --len;
2481                     refreshLine(pi);
2482                 }
2483                 break;
2484 
2485             case META + '<':     // meta-<, beginning of history
2486             case PAGE_UP_KEY:    // Page Up, beginning of history
2487             case META + '>':     // meta->, end of history
2488             case PAGE_DOWN_KEY:  // Page Down, end of history
2489                 killRing.lastAction = KillRing::actionOther;
2490                 // if not already recalling, add the current line to the history list so we don't
2491                 // have to special case it
2492                 if (historyIndex == historyLen - 1) {
2493                     free(history[historyLen - 1]);
2494                     size_t tempBufferSize = sizeof(UChar32) * len + 1;
2495                     unique_ptr<UChar8[]> tempBuffer(new UChar8[tempBufferSize]);
2496                     copyString32to8(tempBuffer.get(), buf32, tempBufferSize);
2497                     history[historyLen - 1] = reinterpret_cast<UChar8*>(
2498                         strdup(reinterpret_cast<const char*>(tempBuffer.get())));
2499                 }
2500                 if (historyLen > 1) {
2501                     historyIndex = (c == META + '<' || c == PAGE_UP_KEY) ? 0 : historyLen - 1;
2502                     historyPreviousIndex = -2;
2503                     historyRecallMostRecent = true;
2504                     size_t ucharCount;
2505                     int errorCode;
2506                     copyString8to32(buf32, history[historyIndex], buflen, ucharCount, errorCode);
2507                     len = pos = ucharCount;
2508                     refreshLine(pi);
2509                 }
2510                 break;
2511 
2512             // not one of our special characters, maybe insert it in the buffer
2513             default:
2514                 killRing.lastAction = KillRing::actionOther;
2515                 historyRecallMostRecent = false;
2516                 if (c & (META | CTRL)) {  // beep on unknown Ctrl and/or Meta keys
2517                     beep();
2518                     break;
2519                 }
2520                 if (len < buflen) {
2521                     if (isControlChar(c)) {  // don't insert control characters
2522                         beep();
2523                         break;
2524                     }
2525                     if (len == pos) {  // at end of buffer
2526                         buf32[pos] = c;
2527                         ++pos;
2528                         ++len;
2529                         buf32[len] = '\0';
2530                         int inputLen = calculateColumnPosition(buf32, len);
2531                         if (pi.promptIndentation + inputLen < pi.promptScreenColumns) {
2532                             if (inputLen > pi.promptPreviousInputLen)
2533                                 pi.promptPreviousInputLen = inputLen;
2534                             /* Avoid a full update of the line in the
2535                              * trivial case. */
2536                             if (write32(1, reinterpret_cast<UChar32*>(&c), 1) == -1)
2537                                 return -1;
2538                         } else {
2539                             refreshLine(pi);
2540                         }
2541                     } else {  // not at end of buffer, have to move characters to our right
2542                         memmove(buf32 + pos + 1, buf32 + pos, sizeof(UChar32) * (len - pos));
2543                         buf32[pos] = c;
2544                         ++len;
2545                         ++pos;
2546                         buf32[len] = '\0';
2547                         refreshLine(pi);
2548                     }
2549                 } else {
2550                     beep();  // buffer is full, beep on new characters
2551                 }
2552                 break;
2553         }
2554     }
2555     return len;
2556 }
2557 
2558 string preloadedBufferContents;  // used with linenoisePreloadBuffer
2559 string preloadErrorMessage;
2560 
2561 /**
2562  * linenoisePreloadBuffer provides text to be inserted into the command buffer
2563  *
2564  * the provided text will be processed to be usable and will be used to preload
2565  * the input buffer on the next call to linenoise()
2566  *
2567  * @param preloadText text to begin with on the next call to linenoise()
2568  */
linenoisePreloadBuffer(const char * preloadText)2569 void linenoisePreloadBuffer(const char* preloadText) {
2570     if (!preloadText) {
2571         return;
2572     }
2573     int bufferSize = strlen(preloadText) + 1;
2574     unique_ptr<char[]> tempBuffer(new char[bufferSize]);
2575     strncpy(&tempBuffer[0], preloadText, bufferSize);
2576 
2577     // remove characters that won't display correctly
2578     char* pIn = &tempBuffer[0];
2579     char* pOut = pIn;
2580     bool controlsStripped = false;
2581     bool whitespaceSeen = false;
2582     while (*pIn) {
2583         unsigned char c = *pIn++;  // we need unsigned so chars 0x80 and above are allowed
2584         if ('\r' == c) {           // silently skip CR
2585             continue;
2586         }
2587         if ('\n' == c || '\t' == c) {  // note newline or tab
2588             whitespaceSeen = true;
2589             continue;
2590         }
2591         if (isControlChar(c)) {  // remove other control characters, flag for message
2592             controlsStripped = true;
2593             *pOut++ = ' ';
2594             continue;
2595         }
2596         if (whitespaceSeen) {  // convert whitespace to a single space
2597             *pOut++ = ' ';
2598             whitespaceSeen = false;
2599         }
2600         *pOut++ = c;
2601     }
2602     *pOut = 0;
2603     int processedLength = pOut - tempBuffer.get();
2604     bool lineTruncated = false;
2605     if (processedLength > (LINENOISE_MAX_LINE - 1)) {
2606         lineTruncated = true;
2607         tempBuffer[LINENOISE_MAX_LINE - 1] = 0;
2608     }
2609     preloadedBufferContents = tempBuffer.get();
2610     if (controlsStripped) {
2611         preloadErrorMessage += " [Edited line: control characters were converted to spaces]\n";
2612     }
2613     if (lineTruncated) {
2614         preloadErrorMessage += " [Edited line: the line length was reduced from ";
2615         char buf[128];
2616         snprintf(buf, sizeof(buf), "%d to %d]\n", processedLength, (LINENOISE_MAX_LINE - 1));
2617         preloadErrorMessage += buf;
2618     }
2619 }
2620 
2621 /**
2622  * linenoise is a readline replacement.
2623  *
2624  * call it with a prompt to display and it will return a line of input from the user
2625  *
2626  * @param prompt text of prompt to display to the user
2627  * @return       the returned string belongs to the caller on return and must be freed to prevent
2628  *               memory leaks
2629  */
linenoise(const char * prompt)2630 char* linenoise(const char* prompt) {
2631     if (isatty(STDIN_FILENO)) {  // input is from a terminal
2632         UChar32 buf32[LINENOISE_MAX_LINE];
2633         char charWidths[LINENOISE_MAX_LINE];
2634         if (!preloadErrorMessage.empty()) {
2635             printf("%s", preloadErrorMessage.c_str());
2636             fflush(stdout);
2637             preloadErrorMessage.clear();
2638         }
2639         PromptInfo pi(reinterpret_cast<const UChar8*>(prompt), getScreenColumns());
2640         if (isUnsupportedTerm()) {
2641             if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
2642                 return 0;
2643             fflush(stdout);
2644             if (preloadedBufferContents.empty()) {
2645                 unique_ptr<char[]> buf8(new char[LINENOISE_MAX_LINE]);
2646                 if (fgets(buf8.get(), LINENOISE_MAX_LINE, stdin) == NULL) {
2647                     return NULL;
2648                 }
2649                 size_t len = strlen(buf8.get());
2650                 while (len && (buf8[len - 1] == '\n' || buf8[len - 1] == '\r')) {
2651                     --len;
2652                     buf8[len] = '\0';
2653                 }
2654                 return strdup(buf8.get());  // caller must free buffer
2655             } else {
2656                 char* buf8 = strdup(preloadedBufferContents.c_str());
2657                 preloadedBufferContents.clear();
2658                 return buf8;  // caller must free buffer
2659             }
2660         } else {
2661             if (enableRawMode() == -1) {
2662                 return NULL;
2663             }
2664             InputBuffer ib(buf32, charWidths, LINENOISE_MAX_LINE);
2665             if (!preloadedBufferContents.empty()) {
2666                 ib.preloadBuffer(reinterpret_cast<const UChar8*>(preloadedBufferContents.c_str()));
2667                 preloadedBufferContents.clear();
2668             }
2669             int count = ib.getInputLine(pi);
2670             disableRawMode();
2671             printf("\n");
2672             if (count == -1) {
2673                 return NULL;
2674             }
2675             size_t bufferSize = sizeof(UChar32) * ib.length() + 1;
2676             unique_ptr<UChar8[]> buf8(new UChar8[bufferSize]);
2677             copyString32to8(buf8.get(), buf32, bufferSize);
2678             return strdup(reinterpret_cast<char*>(buf8.get()));  // caller must free buffer
2679         }
2680     } else {  // input not from a terminal, we should work with piped input, i.e. redirected stdin
2681         unique_ptr<char[]> buf8(new char[LINENOISE_MAX_LINE]);
2682         if (fgets(buf8.get(), LINENOISE_MAX_LINE, stdin) == NULL) {
2683             return NULL;
2684         }
2685 
2686         // if fgets() gave us the newline, remove it
2687         int count = strlen(buf8.get());
2688         if (count > 0 && buf8[count - 1] == '\n') {
2689             --count;
2690             buf8[count] = '\0';
2691         }
2692         return strdup(buf8.get());  // caller must free buffer
2693     }
2694 }
2695 
2696 /* Register a callback function to be called for tab-completion. */
linenoiseSetCompletionCallback(linenoiseCompletionCallback * fn)2697 void linenoiseSetCompletionCallback(linenoiseCompletionCallback* fn) {
2698     completionCallback = fn;
2699 }
2700 
linenoiseAddCompletion(linenoiseCompletions * lc,const char * str)2701 void linenoiseAddCompletion(linenoiseCompletions* lc, const char* str) {
2702     lc->completionStrings.push_back(Utf32String(reinterpret_cast<const UChar8*>(str)));
2703 }
2704 
linenoiseHistoryAdd(const char * line)2705 int linenoiseHistoryAdd(const char* line) {
2706     if (historyMaxLen == 0) {
2707         return 0;
2708     }
2709     if (history == NULL) {
2710         history = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * historyMaxLen));
2711         if (history == NULL) {
2712             return 0;
2713         }
2714         memset(history, 0, (sizeof(char*) * historyMaxLen));
2715     }
2716     UChar8* linecopy = reinterpret_cast<UChar8*>(strdup(line));
2717     if (!linecopy) {
2718         return 0;
2719     }
2720     if (historyLen == historyMaxLen) {
2721         free(history[0]);
2722         memmove(history, history + 1, sizeof(char*) * (historyMaxLen - 1));
2723         --historyLen;
2724         if (--historyPreviousIndex < -1) {
2725             historyPreviousIndex = -2;
2726         }
2727     }
2728 
2729     // convert newlines in multi-line code to spaces before storing
2730     UChar8* p = linecopy;
2731     while (*p) {
2732         if (*p == '\n') {
2733             *p = ' ';
2734         }
2735         ++p;
2736     }
2737     history[historyLen] = linecopy;
2738     ++historyLen;
2739     return 1;
2740 }
2741 
linenoiseHistorySetMaxLen(int len)2742 int linenoiseHistorySetMaxLen(int len) {
2743     if (len < 1) {
2744         return 0;
2745     }
2746     if (history) {
2747         int tocopy = historyLen;
2748         UChar8** newHistory = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * len));
2749         if (newHistory == NULL) {
2750             return 0;
2751         }
2752         if (len < tocopy) {
2753             tocopy = len;
2754         }
2755         memcpy(newHistory, history + historyMaxLen - tocopy, sizeof(UChar8*) * tocopy);
2756         free(history);
2757         history = newHistory;
2758     }
2759     historyMaxLen = len;
2760     if (historyLen > historyMaxLen) {
2761         historyLen = historyMaxLen;
2762     }
2763     return 1;
2764 }
2765 
2766 namespace {
linenoiseFileError(mongo::ErrorCodes::Error code,const char * what,const char * filename)2767 mongo::Status linenoiseFileError(mongo::ErrorCodes::Error code,
2768                                  const char* what,
2769                                  const char* filename) {
2770     std::stringstream ss;
2771     ss << "Unable to " << what << " file " << filename << ": " << mongo::errnoWithDescription();
2772     return {code, ss.str()};
2773 }
2774 }  // namespace
2775 
2776 /* Save the history in the specified file. */
linenoiseHistorySave(const char * filename)2777 mongo::Status linenoiseHistorySave(const char* filename) {
2778     FILE* fp;
2779 #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE || defined(__APPLE__)
2780     int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
2781     if (fd == -1) {
2782         return linenoiseFileError(mongo::ErrorCodes::FileOpenFailed, "open()", filename);
2783     }
2784     fp = fdopen(fd, "wt");
2785     if (fp == NULL) {
2786         // We've already failed, so no need to report any close() failure.
2787         (void)close(fd);
2788         return linenoiseFileError(mongo::ErrorCodes::FileOpenFailed, "fdopen()", filename);
2789     }
2790 #else
2791     fp = fopen(filename, "wt");
2792     if (fp == NULL) {
2793         return linenoiseFileError(mongo::ErrorCodes::FileOpenFailed, "fopen()", filename);
2794     }
2795 #endif  // _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE || defined(__APPLE__)
2796 
2797     for (int j = 0; j < historyLen; ++j) {
2798         if (history[j][0] != '\0') {
2799             if (fprintf(fp, "%s\n", history[j]) < 0) {
2800                 // We've already failed, so no need to report any fclose() failure.
2801                 (void)fclose(fp);
2802                 return linenoiseFileError(
2803                     mongo::ErrorCodes::FileStreamFailed, "fprintf() to", filename);
2804             }
2805         }
2806     }
2807     // Closing fp also causes fd to be closed.
2808     if (fclose(fp) != 0) {
2809         return linenoiseFileError(mongo::ErrorCodes::FileStreamFailed, "fclose()", filename);
2810     }
2811     return mongo::Status::OK();
2812 }
2813 
2814 /* Load the history from the specified file. */
linenoiseHistoryLoad(const char * filename)2815 mongo::Status linenoiseHistoryLoad(const char* filename) {
2816     FILE* fp = fopen(filename, "rt");
2817     if (fp == NULL) {
2818         if (errno == ENOENT) {
2819             // Not having a history file isn't an error condition.
2820             // For example, it's always the case when the shell is run for the first time.
2821             return mongo::Status::OK();
2822         }
2823         return linenoiseFileError(mongo::ErrorCodes::FileOpenFailed, "fopen()", filename);
2824     }
2825 
2826     char buf[LINENOISE_MAX_LINE];
2827     while (fgets(buf, LINENOISE_MAX_LINE, fp) != NULL) {
2828         char* p = strchr(buf, '\r');
2829         if (!p) {
2830             p = strchr(buf, '\n');
2831         }
2832         if (p) {
2833             *p = '\0';
2834         }
2835         if (p != buf) {
2836             linenoiseHistoryAdd(buf);
2837         }
2838     }
2839     // fgets() returns NULL on error or EOF (with nothing read).
2840     // So if we aren't EOF, it must have been an error.
2841     if (!feof(fp)) {
2842         // We've already failed, so no need to report any fclose() failure.
2843         (void)fclose(fp);
2844         return linenoiseFileError(mongo::ErrorCodes::FileStreamFailed, "fgets() from", filename);
2845     }
2846     if (fclose(fp) != 0) {
2847         return linenoiseFileError(mongo::ErrorCodes::FileStreamFailed, "fclose()", filename);
2848     }
2849     return mongo::Status::OK();
2850 }
2851