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