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