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