diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-05-23 16:49:49 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-05-23 16:52:00 +0200 |
commit | 426392f71d5f45aad8c57969643fd6c365ce2362 (patch) | |
tree | 3e5b6ddfc395e26f1a931f9a97516ede9b7c68d3 | |
parent | 1ec240e3df3e9c8571c0df174b9f239b451dbf3a (diff) | |
download | sink-426392f71d5f45aad8c57969643fd6c365ce2362.tar.gz sink-426392f71d5f45aad8c57969643fd6c365ce2362.zip |
Replaced readline with cpp-linenoise
... a single header readline replacement that works on all linux, osx
and windows (or so they claim). Besides cleaning up the code
considerably, it should help us build sinksh on windows where readline
is not really (there are some ancient broken readline ports) available.
cpp-readline comes from here: https://github.com/yhirose/cpp-linenoise
-rw-r--r-- | cmake/modules/FindReadline.cmake | 47 | ||||
-rw-r--r-- | sinksh/CMakeLists.txt | 6 | ||||
-rw-r--r-- | sinksh/repl/linenoise.hpp | 2412 | ||||
-rw-r--r-- | sinksh/repl/repl.cpp | 10 | ||||
-rw-r--r-- | sinksh/repl/replStates.cpp | 68 |
5 files changed, 2440 insertions, 103 deletions
diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake deleted file mode 100644 index 5e110da..0000000 --- a/cmake/modules/FindReadline.cmake +++ /dev/null | |||
@@ -1,47 +0,0 @@ | |||
1 | # - Try to find readline include dirs and libraries | ||
2 | # | ||
3 | # Usage of this module as follows: | ||
4 | # | ||
5 | # find_package(Readline) | ||
6 | # | ||
7 | # Variables used by this module, they can change the default behaviour and need | ||
8 | # to be set before calling find_package: | ||
9 | # | ||
10 | # Readline_ROOT_DIR Set this variable to the root installation of | ||
11 | # readline if the module has problems finding the | ||
12 | # proper installation path. | ||
13 | # | ||
14 | # Variables defined by this module: | ||
15 | # | ||
16 | # READLINE_FOUND System has readline, include and lib dirs found | ||
17 | # Readline_INCLUDE_DIR The readline include directories. | ||
18 | # Readline_LIBRARY The readline library. | ||
19 | |||
20 | find_path(Readline_ROOT_DIR | ||
21 | NAMES include/readline/readline.h | ||
22 | ) | ||
23 | |||
24 | find_path(Readline_INCLUDE_DIR | ||
25 | NAMES readline/readline.h | ||
26 | HINTS ${Readline_ROOT_DIR}/include | ||
27 | ) | ||
28 | |||
29 | find_library(Readline_LIBRARY | ||
30 | NAMES readline | ||
31 | HINTS ${Readline_ROOT_DIR}/lib | ||
32 | ) | ||
33 | |||
34 | set(Readline_VERSION Readline_VERSION-NOTFOUND) | ||
35 | if (Readline_INCLUDE_DIR) | ||
36 | if(EXISTS "${Readline_INCLUDE_DIR}/readline/readline.h") | ||
37 | file(STRINGS "${Readline_INCLUDE_DIR}/readline/readline.h" _Readline_HEADER_CONTENTS REGEX "#define RL_VERSION_[A-Z]+") | ||
38 | string(REGEX REPLACE ".*#define RL_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" Readline_VERSION_MAJOR "${_Readline_HEADER_CONTENTS}") | ||
39 | string(REGEX REPLACE ".*#define RL_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" Readline_VERSION_MINOR "${_Readline_HEADER_CONTENTS}") | ||
40 | set(Readline_VERSION ${Readline_VERSION_MAJOR}.${Readline_VERSION_MINOR}) | ||
41 | unset(_Readline_HEADER_CONTENTS) | ||
42 | endif() | ||
43 | endif() | ||
44 | |||
45 | find_package_handle_standard_args(Readline FOUND_VAR Readline_FOUND | ||
46 | REQUIRED_VARS Readline_LIBRARY Readline_INCLUDE_DIR Readline_ROOT_DIR | ||
47 | VERSION_VAR Readline_VERSION) | ||
diff --git a/sinksh/CMakeLists.txt b/sinksh/CMakeLists.txt index f55f424..cfff36c 100644 --- a/sinksh/CMakeLists.txt +++ b/sinksh/CMakeLists.txt | |||
@@ -1,7 +1,5 @@ | |||
1 | project(sinksh) | 1 | project(sinksh) |
2 | 2 | ||
3 | find_package(Readline 5.0 REQUIRED) | ||
4 | |||
5 | set(sink_cli_SRCS | 3 | set(sink_cli_SRCS |
6 | main.cpp | 4 | main.cpp |
7 | syntaxtree.cpp | 5 | syntaxtree.cpp |
@@ -27,9 +25,9 @@ set(sink_cli_SRCS | |||
27 | state.cpp | 25 | state.cpp |
28 | utils.cpp) | 26 | utils.cpp) |
29 | 27 | ||
30 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${Readline_INCLUDE_DIR}) | 28 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
31 | 29 | ||
32 | add_executable(${PROJECT_NAME} ${sink_cli_SRCS}) | 30 | add_executable(${PROJECT_NAME} ${sink_cli_SRCS}) |
33 | target_link_libraries(${PROJECT_NAME} Qt5::Core ${Readline_LIBRARY} sink ${XAPIAN_LIBRARIES}) | 31 | target_link_libraries(${PROJECT_NAME} Qt5::Core sink ${XAPIAN_LIBRARIES}) |
34 | install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) | 32 | install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) |
35 | 33 | ||
diff --git a/sinksh/repl/linenoise.hpp b/sinksh/repl/linenoise.hpp new file mode 100644 index 0000000..fa7aa7f --- /dev/null +++ b/sinksh/repl/linenoise.hpp | |||
@@ -0,0 +1,2412 @@ | |||
1 | /* | ||
2 | * linenoise.hpp -- Multi-platfrom C++ header-only linenoise library. | ||
3 | * https://github.com/yhirose/cpp-linenoise | ||
4 | * | ||
5 | * All credits and commendations have to go to the authors of the | ||
6 | * following excellent libraries. | ||
7 | * | ||
8 | * - linenoise.h and linenose.c (https://github.com/antirez/linenoise) | ||
9 | * - ANSI.c (https://github.com/adoxa/ansicon) | ||
10 | * - Win32_ANSI.h and Win32_ANSI.c (https://github.com/MSOpenTech/redis) | ||
11 | * | ||
12 | * ------------------------------------------------------------------------ | ||
13 | * | ||
14 | * Copyright (c) 2015 yhirose | ||
15 | * All rights reserved. | ||
16 | * | ||
17 | * Redistribution and use in source and binary forms, with or without | ||
18 | * modification, are permitted provided that the following conditions are met: | ||
19 | * | ||
20 | * 1. Redistributions of source code must retain the above copyright notice, this | ||
21 | * list of conditions and the following disclaimer. | ||
22 | * 2. Redistributions in binary form must reproduce the above copyright notice, | ||
23 | * this list of conditions and the following disclaimer in the documentation | ||
24 | * and/or other materials provided with the distribution. | ||
25 | * | ||
26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
28 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
29 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
30 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
31 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
32 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
33 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
36 | */ | ||
37 | |||
38 | /* linenoise.h -- guerrilla line editing library against the idea that a | ||
39 | * line editing lib needs to be 20,000 lines of C code. | ||
40 | * | ||
41 | * See linenoise.c for more information. | ||
42 | * | ||
43 | * ------------------------------------------------------------------------ | ||
44 | * | ||
45 | * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com> | ||
46 | * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> | ||
47 | * | ||
48 | * All rights reserved. | ||
49 | * | ||
50 | * Redistribution and use in source and binary forms, with or without | ||
51 | * modification, are permitted provided that the following conditions are | ||
52 | * met: | ||
53 | * | ||
54 | * * Redistributions of source code must retain the above copyright | ||
55 | * notice, this list of conditions and the following disclaimer. | ||
56 | * | ||
57 | * * Redistributions in binary form must reproduce the above copyright | ||
58 | * notice, this list of conditions and the following disclaimer in the | ||
59 | * documentation and/or other materials provided with the distribution. | ||
60 | * | ||
61 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
62 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
63 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
64 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
65 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
66 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
67 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
68 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
69 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
70 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
71 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
72 | */ | ||
73 | |||
74 | /* | ||
75 | * ANSI.c - ANSI escape sequence console driver. | ||
76 | * | ||
77 | * Copyright (C) 2005-2014 Jason Hood | ||
78 | * This software is provided 'as-is', without any express or implied | ||
79 | * warranty. In no event will the author be held liable for any damages | ||
80 | * arising from the use of this software. | ||
81 | * | ||
82 | * Permission is granted to anyone to use this software for any purpose, | ||
83 | * including commercial applications, and to alter it and redistribute it | ||
84 | * freely, subject to the following restrictions: | ||
85 | * | ||
86 | * 1. The origin of this software must not be misrepresented; you must not | ||
87 | * claim that you wrote the original software. If you use this software | ||
88 | * in a product, an acknowledgment in the product documentation would be | ||
89 | * appreciated but is not required. | ||
90 | * 2. Altered source versions must be plainly marked as such, and must not be | ||
91 | * misrepresented as being the original software. | ||
92 | * 3. This notice may not be removed or altered from any source distribution. | ||
93 | * | ||
94 | * Jason Hood | ||
95 | * jadoxa@yahoo.com.au | ||
96 | */ | ||
97 | |||
98 | /* | ||
99 | * Win32_ANSI.h and Win32_ANSI.c | ||
100 | * | ||
101 | * Derived from ANSI.c by Jason Hood, from his ansicon project (https://github.com/adoxa/ansicon), with modifications. | ||
102 | * | ||
103 | * Copyright (c), Microsoft Open Technologies, Inc. | ||
104 | * All rights reserved. | ||
105 | * Redistribution and use in source and binary forms, with or without | ||
106 | * modification, are permitted provided that the following conditions are met: | ||
107 | * - Redistributions of source code must retain the above copyright notice, | ||
108 | * this list of conditions and the following disclaimer. | ||
109 | * - Redistributions in binary form must reproduce the above copyright notice, | ||
110 | * this list of conditions and the following disclaimer in the documentation | ||
111 | * and/or other materials provided with the distribution. | ||
112 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
113 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
114 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
115 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
116 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
117 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
118 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
119 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
120 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
121 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
122 | */ | ||
123 | |||
124 | #ifndef __LINENOISE_HPP | ||
125 | #define __LINENOISE_HPP | ||
126 | |||
127 | #ifndef _WIN32 | ||
128 | #include <termios.h> | ||
129 | #include <unistd.h> | ||
130 | #include <sys/ioctl.h> | ||
131 | #else | ||
132 | #ifndef NOMINMAX | ||
133 | #define NOMINMAX | ||
134 | #endif | ||
135 | #include <Windows.h> | ||
136 | #include <io.h> | ||
137 | #ifndef STDIN_FILENO | ||
138 | #define STDIN_FILENO (_fileno(stdin)) | ||
139 | #endif | ||
140 | #ifndef STDOUT_FILENO | ||
141 | #define STDOUT_FILENO 1 | ||
142 | #endif | ||
143 | #define isatty _isatty | ||
144 | #define write win32_write | ||
145 | #define read _read | ||
146 | #endif | ||
147 | #include <stdlib.h> | ||
148 | #include <stdio.h> | ||
149 | #include <errno.h> | ||
150 | #include <string.h> | ||
151 | #include <stdlib.h> | ||
152 | #include <ctype.h> | ||
153 | #include <sys/types.h> | ||
154 | #include <string> | ||
155 | #include <fstream> | ||
156 | #include <functional> | ||
157 | #include <vector> | ||
158 | #include <iostream> | ||
159 | |||
160 | namespace linenoise { | ||
161 | |||
162 | typedef std::function<void (const char*, std::vector<std::string>&)> CompletionCallback; | ||
163 | |||
164 | #ifdef _WIN32 | ||
165 | |||
166 | namespace ansi { | ||
167 | |||
168 | #define lenof(array) (sizeof(array)/sizeof(*(array))) | ||
169 | |||
170 | typedef struct | ||
171 | { | ||
172 | BYTE foreground; // ANSI base color (0 to 7; add 30) | ||
173 | BYTE background; // ANSI base color (0 to 7; add 40) | ||
174 | BYTE bold; // console FOREGROUND_INTENSITY bit | ||
175 | BYTE underline; // console BACKGROUND_INTENSITY bit | ||
176 | BYTE rvideo; // swap foreground/bold & background/underline | ||
177 | BYTE concealed; // set foreground/bold to background/underline | ||
178 | BYTE reverse; // swap console foreground & background attributes | ||
179 | } GRM, *PGRM; // Graphic Rendition Mode | ||
180 | |||
181 | |||
182 | inline bool is_digit(char c) { return '0' <= c && c <= '9'; } | ||
183 | |||
184 | // ========== Global variables and constants | ||
185 | |||
186 | HANDLE hConOut; // handle to CONOUT$ | ||
187 | |||
188 | const char ESC = '\x1B'; // ESCape character | ||
189 | const char BEL = '\x07'; | ||
190 | const char SO = '\x0E'; // Shift Out | ||
191 | const char SI = '\x0F'; // Shift In | ||
192 | |||
193 | const int MAX_ARG = 16; // max number of args in an escape sequence | ||
194 | int state; // automata state | ||
195 | WCHAR prefix; // escape sequence prefix ( '[', ']' or '(' ); | ||
196 | WCHAR prefix2; // secondary prefix ( '?' or '>' ); | ||
197 | WCHAR suffix; // escape sequence suffix | ||
198 | int es_argc; // escape sequence args count | ||
199 | int es_argv[MAX_ARG]; // escape sequence args | ||
200 | WCHAR Pt_arg[MAX_PATH * 2]; // text parameter for Operating System Command | ||
201 | int Pt_len; | ||
202 | BOOL shifted; | ||
203 | |||
204 | |||
205 | // DEC Special Graphics Character Set from | ||
206 | // http://vt100.net/docs/vt220-rm/table2-4.html | ||
207 | // Some of these may not look right, depending on the font and code page (in | ||
208 | // particular, the Control Pictures probably won't work at all). | ||
209 | const WCHAR G1[] = | ||
210 | { | ||
211 | ' ', // _ - blank | ||
212 | L'\x2666', // ` - Black Diamond Suit | ||
213 | L'\x2592', // a - Medium Shade | ||
214 | L'\x2409', // b - HT | ||
215 | L'\x240c', // c - FF | ||
216 | L'\x240d', // d - CR | ||
217 | L'\x240a', // e - LF | ||
218 | L'\x00b0', // f - Degree Sign | ||
219 | L'\x00b1', // g - Plus-Minus Sign | ||
220 | L'\x2424', // h - NL | ||
221 | L'\x240b', // i - VT | ||
222 | L'\x2518', // j - Box Drawings Light Up And Left | ||
223 | L'\x2510', // k - Box Drawings Light Down And Left | ||
224 | L'\x250c', // l - Box Drawings Light Down And Right | ||
225 | L'\x2514', // m - Box Drawings Light Up And Right | ||
226 | L'\x253c', // n - Box Drawings Light Vertical And Horizontal | ||
227 | L'\x00af', // o - SCAN 1 - Macron | ||
228 | L'\x25ac', // p - SCAN 3 - Black Rectangle | ||
229 | L'\x2500', // q - SCAN 5 - Box Drawings Light Horizontal | ||
230 | L'_', // r - SCAN 7 - Low Line | ||
231 | L'_', // s - SCAN 9 - Low Line | ||
232 | L'\x251c', // t - Box Drawings Light Vertical And Right | ||
233 | L'\x2524', // u - Box Drawings Light Vertical And Left | ||
234 | L'\x2534', // v - Box Drawings Light Up And Horizontal | ||
235 | L'\x252c', // w - Box Drawings Light Down And Horizontal | ||
236 | L'\x2502', // x - Box Drawings Light Vertical | ||
237 | L'\x2264', // y - Less-Than Or Equal To | ||
238 | L'\x2265', // z - Greater-Than Or Equal To | ||
239 | L'\x03c0', // { - Greek Small Letter Pi | ||
240 | L'\x2260', // | - Not Equal To | ||
241 | L'\x00a3', // } - Pound Sign | ||
242 | L'\x00b7', // ~ - Middle Dot | ||
243 | }; | ||
244 | |||
245 | #define FIRST_G1 '_' | ||
246 | #define LAST_G1 '~' | ||
247 | |||
248 | |||
249 | // color constants | ||
250 | |||
251 | #define FOREGROUND_BLACK 0 | ||
252 | #define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE | ||
253 | |||
254 | #define BACKGROUND_BLACK 0 | ||
255 | #define BACKGROUND_WHITE BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE | ||
256 | |||
257 | const BYTE foregroundcolor[8] = | ||
258 | { | ||
259 | FOREGROUND_BLACK, // black foreground | ||
260 | FOREGROUND_RED, // red foreground | ||
261 | FOREGROUND_GREEN, // green foreground | ||
262 | FOREGROUND_RED | FOREGROUND_GREEN, // yellow foreground | ||
263 | FOREGROUND_BLUE, // blue foreground | ||
264 | FOREGROUND_BLUE | FOREGROUND_RED, // magenta foreground | ||
265 | FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan foreground | ||
266 | FOREGROUND_WHITE // white foreground | ||
267 | }; | ||
268 | |||
269 | const BYTE backgroundcolor[8] = | ||
270 | { | ||
271 | BACKGROUND_BLACK, // black background | ||
272 | BACKGROUND_RED, // red background | ||
273 | BACKGROUND_GREEN, // green background | ||
274 | BACKGROUND_RED | BACKGROUND_GREEN, // yellow background | ||
275 | BACKGROUND_BLUE, // blue background | ||
276 | BACKGROUND_BLUE | BACKGROUND_RED, // magenta background | ||
277 | BACKGROUND_BLUE | BACKGROUND_GREEN, // cyan background | ||
278 | BACKGROUND_WHITE, // white background | ||
279 | }; | ||
280 | |||
281 | const BYTE attr2ansi[8] = // map console attribute to ANSI number | ||
282 | { | ||
283 | 0, // black | ||
284 | 4, // blue | ||
285 | 2, // green | ||
286 | 6, // cyan | ||
287 | 1, // red | ||
288 | 5, // magenta | ||
289 | 3, // yellow | ||
290 | 7 // white | ||
291 | }; | ||
292 | |||
293 | GRM grm; | ||
294 | |||
295 | // saved cursor position | ||
296 | COORD SavePos; | ||
297 | |||
298 | // ========== Print Buffer functions | ||
299 | |||
300 | #define BUFFER_SIZE 2048 | ||
301 | |||
302 | int nCharInBuffer; | ||
303 | WCHAR ChBuffer[BUFFER_SIZE]; | ||
304 | |||
305 | //----------------------------------------------------------------------------- | ||
306 | // FlushBuffer() | ||
307 | // Writes the buffer to the console and empties it. | ||
308 | //----------------------------------------------------------------------------- | ||
309 | |||
310 | inline void FlushBuffer(void) | ||
311 | { | ||
312 | DWORD nWritten; | ||
313 | if (nCharInBuffer <= 0) return; | ||
314 | WriteConsoleW(hConOut, ChBuffer, nCharInBuffer, &nWritten, NULL); | ||
315 | nCharInBuffer = 0; | ||
316 | } | ||
317 | |||
318 | //----------------------------------------------------------------------------- | ||
319 | // PushBuffer( WCHAR c ) | ||
320 | // Adds a character in the buffer. | ||
321 | //----------------------------------------------------------------------------- | ||
322 | |||
323 | inline void PushBuffer(WCHAR c) | ||
324 | { | ||
325 | if (shifted && c >= FIRST_G1 && c <= LAST_G1) | ||
326 | c = G1[c - FIRST_G1]; | ||
327 | ChBuffer[nCharInBuffer] = c; | ||
328 | if (++nCharInBuffer == BUFFER_SIZE) | ||
329 | FlushBuffer(); | ||
330 | } | ||
331 | |||
332 | //----------------------------------------------------------------------------- | ||
333 | // SendSequence( LPWSTR seq ) | ||
334 | // Send the string to the input buffer. | ||
335 | //----------------------------------------------------------------------------- | ||
336 | |||
337 | inline void SendSequence(LPWSTR seq) | ||
338 | { | ||
339 | DWORD out; | ||
340 | INPUT_RECORD in; | ||
341 | HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); | ||
342 | |||
343 | in.EventType = KEY_EVENT; | ||
344 | in.Event.KeyEvent.bKeyDown = TRUE; | ||
345 | in.Event.KeyEvent.wRepeatCount = 1; | ||
346 | in.Event.KeyEvent.wVirtualKeyCode = 0; | ||
347 | in.Event.KeyEvent.wVirtualScanCode = 0; | ||
348 | in.Event.KeyEvent.dwControlKeyState = 0; | ||
349 | for (; *seq; ++seq) | ||
350 | { | ||
351 | in.Event.KeyEvent.uChar.UnicodeChar = *seq; | ||
352 | WriteConsoleInput(hStdIn, &in, 1, &out); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | // ========== Print functions | ||
357 | |||
358 | //----------------------------------------------------------------------------- | ||
359 | // InterpretEscSeq() | ||
360 | // Interprets the last escape sequence scanned by ParseAndPrintANSIString | ||
361 | // prefix escape sequence prefix | ||
362 | // es_argc escape sequence args count | ||
363 | // es_argv[] escape sequence args array | ||
364 | // suffix escape sequence suffix | ||
365 | // | ||
366 | // for instance, with \e[33;45;1m we have | ||
367 | // prefix = '[', | ||
368 | // es_argc = 3, es_argv[0] = 33, es_argv[1] = 45, es_argv[2] = 1 | ||
369 | // suffix = 'm' | ||
370 | //----------------------------------------------------------------------------- | ||
371 | |||
372 | inline void InterpretEscSeq(void) | ||
373 | { | ||
374 | int i; | ||
375 | WORD attribut; | ||
376 | CONSOLE_SCREEN_BUFFER_INFO Info; | ||
377 | CONSOLE_CURSOR_INFO CursInfo; | ||
378 | DWORD len, NumberOfCharsWritten; | ||
379 | COORD Pos; | ||
380 | SMALL_RECT Rect; | ||
381 | CHAR_INFO CharInfo; | ||
382 | |||
383 | if (prefix == '[') | ||
384 | { | ||
385 | if (prefix2 == '?' && (suffix == 'h' || suffix == 'l')) | ||
386 | { | ||
387 | if (es_argc == 1 && es_argv[0] == 25) | ||
388 | { | ||
389 | GetConsoleCursorInfo(hConOut, &CursInfo); | ||
390 | CursInfo.bVisible = (suffix == 'h'); | ||
391 | SetConsoleCursorInfo(hConOut, &CursInfo); | ||
392 | return; | ||
393 | } | ||
394 | } | ||
395 | // Ignore any other \e[? or \e[> sequences. | ||
396 | if (prefix2 != 0) | ||
397 | return; | ||
398 | |||
399 | GetConsoleScreenBufferInfo(hConOut, &Info); | ||
400 | switch (suffix) | ||
401 | { | ||
402 | case 'm': | ||
403 | if (es_argc == 0) es_argv[es_argc++] = 0; | ||
404 | for (i = 0; i < es_argc; i++) | ||
405 | { | ||
406 | if (30 <= es_argv[i] && es_argv[i] <= 37) | ||
407 | grm.foreground = es_argv[i] - 30; | ||
408 | else if (40 <= es_argv[i] && es_argv[i] <= 47) | ||
409 | grm.background = es_argv[i] - 40; | ||
410 | else switch (es_argv[i]) | ||
411 | { | ||
412 | case 0: | ||
413 | case 39: | ||
414 | case 49: | ||
415 | { | ||
416 | WCHAR def[4]; | ||
417 | int a; | ||
418 | *def = '7'; def[1] = '\0'; | ||
419 | GetEnvironmentVariableW(L"ANSICON_DEF", def, lenof(def)); | ||
420 | a = wcstol(def, NULL, 16); | ||
421 | grm.reverse = FALSE; | ||
422 | if (a < 0) | ||
423 | { | ||
424 | grm.reverse = TRUE; | ||
425 | a = -a; | ||
426 | } | ||
427 | if (es_argv[i] != 49) | ||
428 | grm.foreground = attr2ansi[a & 7]; | ||
429 | if (es_argv[i] != 39) | ||
430 | grm.background = attr2ansi[(a >> 4) & 7]; | ||
431 | if (es_argv[i] == 0) | ||
432 | { | ||
433 | if (es_argc == 1) | ||
434 | { | ||
435 | grm.bold = a & FOREGROUND_INTENSITY; | ||
436 | grm.underline = a & BACKGROUND_INTENSITY; | ||
437 | } | ||
438 | else | ||
439 | { | ||
440 | grm.bold = 0; | ||
441 | grm.underline = 0; | ||
442 | } | ||
443 | grm.rvideo = 0; | ||
444 | grm.concealed = 0; | ||
445 | } | ||
446 | } | ||
447 | break; | ||
448 | |||
449 | case 1: grm.bold = FOREGROUND_INTENSITY; break; | ||
450 | case 5: // blink | ||
451 | case 4: grm.underline = BACKGROUND_INTENSITY; break; | ||
452 | case 7: grm.rvideo = 1; break; | ||
453 | case 8: grm.concealed = 1; break; | ||
454 | case 21: // oops, this actually turns on double underline | ||
455 | case 22: grm.bold = 0; break; | ||
456 | case 25: | ||
457 | case 24: grm.underline = 0; break; | ||
458 | case 27: grm.rvideo = 0; break; | ||
459 | case 28: grm.concealed = 0; break; | ||
460 | } | ||
461 | } | ||
462 | if (grm.concealed) | ||
463 | { | ||
464 | if (grm.rvideo) | ||
465 | { | ||
466 | attribut = foregroundcolor[grm.foreground] | ||
467 | | backgroundcolor[grm.foreground]; | ||
468 | if (grm.bold) | ||
469 | attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; | ||
470 | } | ||
471 | else | ||
472 | { | ||
473 | attribut = foregroundcolor[grm.background] | ||
474 | | backgroundcolor[grm.background]; | ||
475 | if (grm.underline) | ||
476 | attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; | ||
477 | } | ||
478 | } | ||
479 | else if (grm.rvideo) | ||
480 | { | ||
481 | attribut = foregroundcolor[grm.background] | ||
482 | | backgroundcolor[grm.foreground]; | ||
483 | if (grm.bold) | ||
484 | attribut |= BACKGROUND_INTENSITY; | ||
485 | if (grm.underline) | ||
486 | attribut |= FOREGROUND_INTENSITY; | ||
487 | } | ||
488 | else | ||
489 | attribut = foregroundcolor[grm.foreground] | grm.bold | ||
490 | | backgroundcolor[grm.background] | grm.underline; | ||
491 | if (grm.reverse) | ||
492 | attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); | ||
493 | SetConsoleTextAttribute(hConOut, attribut); | ||
494 | return; | ||
495 | |||
496 | case 'J': | ||
497 | if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[J == ESC[0J | ||
498 | if (es_argc != 1) return; | ||
499 | switch (es_argv[0]) | ||
500 | { | ||
501 | case 0: // ESC[0J erase from cursor to end of display | ||
502 | len = (Info.dwSize.Y - Info.dwCursorPosition.Y - 1) * Info.dwSize.X | ||
503 | + Info.dwSize.X - Info.dwCursorPosition.X - 1; | ||
504 | FillConsoleOutputCharacter(hConOut, ' ', len, | ||
505 | Info.dwCursorPosition, | ||
506 | &NumberOfCharsWritten); | ||
507 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, len, | ||
508 | Info.dwCursorPosition, | ||
509 | &NumberOfCharsWritten); | ||
510 | return; | ||
511 | |||
512 | case 1: // ESC[1J erase from start to cursor. | ||
513 | Pos.X = 0; | ||
514 | Pos.Y = 0; | ||
515 | len = Info.dwCursorPosition.Y * Info.dwSize.X | ||
516 | + Info.dwCursorPosition.X + 1; | ||
517 | FillConsoleOutputCharacter(hConOut, ' ', len, Pos, | ||
518 | &NumberOfCharsWritten); | ||
519 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, len, Pos, | ||
520 | &NumberOfCharsWritten); | ||
521 | return; | ||
522 | |||
523 | case 2: // ESC[2J Clear screen and home cursor | ||
524 | Pos.X = 0; | ||
525 | Pos.Y = 0; | ||
526 | len = Info.dwSize.X * Info.dwSize.Y; | ||
527 | FillConsoleOutputCharacter(hConOut, ' ', len, Pos, | ||
528 | &NumberOfCharsWritten); | ||
529 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, len, Pos, | ||
530 | &NumberOfCharsWritten); | ||
531 | SetConsoleCursorPosition(hConOut, Pos); | ||
532 | return; | ||
533 | |||
534 | default: | ||
535 | return; | ||
536 | } | ||
537 | |||
538 | case 'K': | ||
539 | if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[K == ESC[0K | ||
540 | if (es_argc != 1) return; | ||
541 | switch (es_argv[0]) | ||
542 | { | ||
543 | case 0: // ESC[0K Clear to end of line | ||
544 | len = Info.dwSize.X - Info.dwCursorPosition.X + 1; | ||
545 | FillConsoleOutputCharacter(hConOut, ' ', len, | ||
546 | Info.dwCursorPosition, | ||
547 | &NumberOfCharsWritten); | ||
548 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, len, | ||
549 | Info.dwCursorPosition, | ||
550 | &NumberOfCharsWritten); | ||
551 | return; | ||
552 | |||
553 | case 1: // ESC[1K Clear from start of line to cursor | ||
554 | Pos.X = 0; | ||
555 | Pos.Y = Info.dwCursorPosition.Y; | ||
556 | FillConsoleOutputCharacter(hConOut, ' ', | ||
557 | Info.dwCursorPosition.X + 1, Pos, | ||
558 | &NumberOfCharsWritten); | ||
559 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, | ||
560 | Info.dwCursorPosition.X + 1, Pos, | ||
561 | &NumberOfCharsWritten); | ||
562 | return; | ||
563 | |||
564 | case 2: // ESC[2K Clear whole line. | ||
565 | Pos.X = 0; | ||
566 | Pos.Y = Info.dwCursorPosition.Y; | ||
567 | FillConsoleOutputCharacter(hConOut, ' ', Info.dwSize.X, Pos, | ||
568 | &NumberOfCharsWritten); | ||
569 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, | ||
570 | Info.dwSize.X, Pos, | ||
571 | &NumberOfCharsWritten); | ||
572 | return; | ||
573 | |||
574 | default: | ||
575 | return; | ||
576 | } | ||
577 | |||
578 | case 'X': // ESC[#X Erase # characters. | ||
579 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[X == ESC[1X | ||
580 | if (es_argc != 1) return; | ||
581 | FillConsoleOutputCharacter(hConOut, ' ', es_argv[0], | ||
582 | Info.dwCursorPosition, | ||
583 | &NumberOfCharsWritten); | ||
584 | FillConsoleOutputAttribute(hConOut, Info.wAttributes, es_argv[0], | ||
585 | Info.dwCursorPosition, | ||
586 | &NumberOfCharsWritten); | ||
587 | return; | ||
588 | |||
589 | case 'L': // ESC[#L Insert # blank lines. | ||
590 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[L == ESC[1L | ||
591 | if (es_argc != 1) return; | ||
592 | Rect.Left = 0; | ||
593 | Rect.Top = Info.dwCursorPosition.Y; | ||
594 | Rect.Right = Info.dwSize.X - 1; | ||
595 | Rect.Bottom = Info.dwSize.Y - 1; | ||
596 | Pos.X = 0; | ||
597 | Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; | ||
598 | CharInfo.Char.UnicodeChar = ' '; | ||
599 | CharInfo.Attributes = Info.wAttributes; | ||
600 | ScrollConsoleScreenBuffer(hConOut, &Rect, NULL, Pos, &CharInfo); | ||
601 | return; | ||
602 | |||
603 | case 'M': // ESC[#M Delete # lines. | ||
604 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[M == ESC[1M | ||
605 | if (es_argc != 1) return; | ||
606 | if (es_argv[0] > Info.dwSize.Y - Info.dwCursorPosition.Y) | ||
607 | es_argv[0] = Info.dwSize.Y - Info.dwCursorPosition.Y; | ||
608 | Rect.Left = 0; | ||
609 | Rect.Top = Info.dwCursorPosition.Y + es_argv[0]; | ||
610 | Rect.Right = Info.dwSize.X - 1; | ||
611 | Rect.Bottom = Info.dwSize.Y - 1; | ||
612 | Pos.X = 0; | ||
613 | Pos.Y = Info.dwCursorPosition.Y; | ||
614 | CharInfo.Char.UnicodeChar = ' '; | ||
615 | CharInfo.Attributes = Info.wAttributes; | ||
616 | ScrollConsoleScreenBuffer(hConOut, &Rect, NULL, Pos, &CharInfo); | ||
617 | return; | ||
618 | |||
619 | case 'P': // ESC[#P Delete # characters. | ||
620 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[P == ESC[1P | ||
621 | if (es_argc != 1) return; | ||
622 | if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) | ||
623 | es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; | ||
624 | Rect.Left = Info.dwCursorPosition.X + es_argv[0]; | ||
625 | Rect.Top = Info.dwCursorPosition.Y; | ||
626 | Rect.Right = Info.dwSize.X - 1; | ||
627 | Rect.Bottom = Info.dwCursorPosition.Y; | ||
628 | CharInfo.Char.UnicodeChar = ' '; | ||
629 | CharInfo.Attributes = Info.wAttributes; | ||
630 | ScrollConsoleScreenBuffer(hConOut, &Rect, NULL, Info.dwCursorPosition, | ||
631 | &CharInfo); | ||
632 | return; | ||
633 | |||
634 | case '@': // ESC[#@ Insert # blank characters. | ||
635 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[@ == ESC[1@ | ||
636 | if (es_argc != 1) return; | ||
637 | if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) | ||
638 | es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; | ||
639 | Rect.Left = Info.dwCursorPosition.X; | ||
640 | Rect.Top = Info.dwCursorPosition.Y; | ||
641 | Rect.Right = Info.dwSize.X - 1 - es_argv[0]; | ||
642 | Rect.Bottom = Info.dwCursorPosition.Y; | ||
643 | Pos.X = Info.dwCursorPosition.X + es_argv[0]; | ||
644 | Pos.Y = Info.dwCursorPosition.Y; | ||
645 | CharInfo.Char.UnicodeChar = ' '; | ||
646 | CharInfo.Attributes = Info.wAttributes; | ||
647 | ScrollConsoleScreenBuffer(hConOut, &Rect, NULL, Pos, &CharInfo); | ||
648 | return; | ||
649 | |||
650 | case 'k': // ESC[#k | ||
651 | case 'A': // ESC[#A Moves cursor up # lines | ||
652 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[A == ESC[1A | ||
653 | if (es_argc != 1) return; | ||
654 | Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; | ||
655 | if (Pos.Y < 0) Pos.Y = 0; | ||
656 | Pos.X = Info.dwCursorPosition.X; | ||
657 | SetConsoleCursorPosition(hConOut, Pos); | ||
658 | return; | ||
659 | |||
660 | case 'e': // ESC[#e | ||
661 | case 'B': // ESC[#B Moves cursor down # lines | ||
662 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[B == ESC[1B | ||
663 | if (es_argc != 1) return; | ||
664 | Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; | ||
665 | if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; | ||
666 | Pos.X = Info.dwCursorPosition.X; | ||
667 | SetConsoleCursorPosition(hConOut, Pos); | ||
668 | return; | ||
669 | |||
670 | case 'a': // ESC[#a | ||
671 | case 'C': // ESC[#C Moves cursor forward # spaces | ||
672 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[C == ESC[1C | ||
673 | if (es_argc != 1) return; | ||
674 | Pos.X = Info.dwCursorPosition.X + es_argv[0]; | ||
675 | if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; | ||
676 | Pos.Y = Info.dwCursorPosition.Y; | ||
677 | SetConsoleCursorPosition(hConOut, Pos); | ||
678 | return; | ||
679 | |||
680 | case 'j': // ESC[#j | ||
681 | case 'D': // ESC[#D Moves cursor back # spaces | ||
682 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[D == ESC[1D | ||
683 | if (es_argc != 1) return; | ||
684 | Pos.X = Info.dwCursorPosition.X - es_argv[0]; | ||
685 | if (Pos.X < 0) Pos.X = 0; | ||
686 | Pos.Y = Info.dwCursorPosition.Y; | ||
687 | SetConsoleCursorPosition(hConOut, Pos); | ||
688 | return; | ||
689 | |||
690 | case 'E': // ESC[#E Moves cursor down # lines, column 1. | ||
691 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[E == ESC[1E | ||
692 | if (es_argc != 1) return; | ||
693 | Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; | ||
694 | if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; | ||
695 | Pos.X = 0; | ||
696 | SetConsoleCursorPosition(hConOut, Pos); | ||
697 | return; | ||
698 | |||
699 | case 'F': // ESC[#F Moves cursor up # lines, column 1. | ||
700 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[F == ESC[1F | ||
701 | if (es_argc != 1) return; | ||
702 | Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; | ||
703 | if (Pos.Y < 0) Pos.Y = 0; | ||
704 | Pos.X = 0; | ||
705 | SetConsoleCursorPosition(hConOut, Pos); | ||
706 | return; | ||
707 | |||
708 | case '`': // ESC[#` | ||
709 | case 'G': // ESC[#G Moves cursor column # in current row. | ||
710 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[G == ESC[1G | ||
711 | if (es_argc != 1) return; | ||
712 | Pos.X = es_argv[0] - 1; | ||
713 | if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; | ||
714 | if (Pos.X < 0) Pos.X = 0; | ||
715 | Pos.Y = Info.dwCursorPosition.Y; | ||
716 | SetConsoleCursorPosition(hConOut, Pos); | ||
717 | return; | ||
718 | |||
719 | case 'd': // ESC[#d Moves cursor row #, current column. | ||
720 | if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[d == ESC[1d | ||
721 | if (es_argc != 1) return; | ||
722 | Pos.Y = es_argv[0] - 1; | ||
723 | if (Pos.Y < 0) Pos.Y = 0; | ||
724 | if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; | ||
725 | SetConsoleCursorPosition(hConOut, Pos); | ||
726 | return; | ||
727 | |||
728 | case 'f': // ESC[#;#f | ||
729 | case 'H': // ESC[#;#H Moves cursor to line #, column # | ||
730 | if (es_argc == 0) | ||
731 | es_argv[es_argc++] = 1; // ESC[H == ESC[1;1H | ||
732 | if (es_argc == 1) | ||
733 | es_argv[es_argc++] = 1; // ESC[#H == ESC[#;1H | ||
734 | if (es_argc > 2) return; | ||
735 | Pos.X = es_argv[1] - 1; | ||
736 | if (Pos.X < 0) Pos.X = 0; | ||
737 | if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; | ||
738 | Pos.Y = es_argv[0] - 1; | ||
739 | if (Pos.Y < 0) Pos.Y = 0; | ||
740 | if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; | ||
741 | SetConsoleCursorPosition(hConOut, Pos); | ||
742 | return; | ||
743 | |||
744 | case 's': // ESC[s Saves cursor position for recall later | ||
745 | if (es_argc != 0) return; | ||
746 | SavePos = Info.dwCursorPosition; | ||
747 | return; | ||
748 | |||
749 | case 'u': // ESC[u Return to saved cursor position | ||
750 | if (es_argc != 0) return; | ||
751 | SetConsoleCursorPosition(hConOut, SavePos); | ||
752 | return; | ||
753 | |||
754 | case 'n': // ESC[#n Device status report | ||
755 | if (es_argc != 1) return; // ESC[n == ESC[0n -> ignored | ||
756 | switch (es_argv[0]) | ||
757 | { | ||
758 | case 5: // ESC[5n Report status | ||
759 | SendSequence(L"\33[0n"); // "OK" | ||
760 | return; | ||
761 | |||
762 | case 6: // ESC[6n Report cursor position | ||
763 | { | ||
764 | WCHAR buf[32]; | ||
765 | swprintf(buf, 32, L"\33[%d;%dR", Info.dwCursorPosition.Y + 1, | ||
766 | Info.dwCursorPosition.X + 1); | ||
767 | SendSequence(buf); | ||
768 | } | ||
769 | return; | ||
770 | |||
771 | default: | ||
772 | return; | ||
773 | } | ||
774 | |||
775 | case 't': // ESC[#t Window manipulation | ||
776 | if (es_argc != 1) return; | ||
777 | if (es_argv[0] == 21) // ESC[21t Report xterm window's title | ||
778 | { | ||
779 | WCHAR buf[MAX_PATH * 2]; | ||
780 | DWORD len = GetConsoleTitleW(buf + 3, lenof(buf) - 3 - 2); | ||
781 | // Too bad if it's too big or fails. | ||
782 | buf[0] = ESC; | ||
783 | buf[1] = ']'; | ||
784 | buf[2] = 'l'; | ||
785 | buf[3 + len] = ESC; | ||
786 | buf[3 + len + 1] = '\\'; | ||
787 | buf[3 + len + 2] = '\0'; | ||
788 | SendSequence(buf); | ||
789 | } | ||
790 | return; | ||
791 | |||
792 | default: | ||
793 | return; | ||
794 | } | ||
795 | } | ||
796 | else // (prefix == ']') | ||
797 | { | ||
798 | // Ignore any \e]? or \e]> sequences. | ||
799 | if (prefix2 != 0) | ||
800 | return; | ||
801 | |||
802 | if (es_argc == 1 && es_argv[0] == 0) // ESC]0;titleST | ||
803 | { | ||
804 | SetConsoleTitleW(Pt_arg); | ||
805 | } | ||
806 | } | ||
807 | } | ||
808 | |||
809 | //----------------------------------------------------------------------------- | ||
810 | // ParseAndPrintANSIString(hDev, lpBuffer, nNumberOfBytesToWrite) | ||
811 | // Parses the string lpBuffer, interprets the escapes sequences and prints the | ||
812 | // characters in the device hDev (console). | ||
813 | // The lexer is a three states automata. | ||
814 | // If the number of arguments es_argc > MAX_ARG, only the MAX_ARG-1 firsts and | ||
815 | // the last arguments are processed (no es_argv[] overflow). | ||
816 | //----------------------------------------------------------------------------- | ||
817 | |||
818 | inline BOOL ParseAndPrintANSIString(HANDLE hDev, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) | ||
819 | { | ||
820 | DWORD i; | ||
821 | LPCSTR s; | ||
822 | |||
823 | if (hDev != hConOut) // reinit if device has changed | ||
824 | { | ||
825 | hConOut = hDev; | ||
826 | state = 1; | ||
827 | shifted = FALSE; | ||
828 | } | ||
829 | for (i = nNumberOfBytesToWrite, s = (LPCSTR)lpBuffer; i > 0; i--, s++) | ||
830 | { | ||
831 | if (state == 1) | ||
832 | { | ||
833 | if (*s == ESC) state = 2; | ||
834 | else if (*s == SO) shifted = TRUE; | ||
835 | else if (*s == SI) shifted = FALSE; | ||
836 | else PushBuffer(*s); | ||
837 | } | ||
838 | else if (state == 2) | ||
839 | { | ||
840 | if (*s == ESC); // \e\e...\e == \e | ||
841 | else if ((*s == '[') || (*s == ']')) | ||
842 | { | ||
843 | FlushBuffer(); | ||
844 | prefix = *s; | ||
845 | prefix2 = 0; | ||
846 | state = 3; | ||
847 | Pt_len = 0; | ||
848 | *Pt_arg = '\0'; | ||
849 | } | ||
850 | else if (*s == ')' || *s == '(') state = 6; | ||
851 | else state = 1; | ||
852 | } | ||
853 | else if (state == 3) | ||
854 | { | ||
855 | if (is_digit(*s)) | ||
856 | { | ||
857 | es_argc = 0; | ||
858 | es_argv[0] = *s - '0'; | ||
859 | state = 4; | ||
860 | } | ||
861 | else if (*s == ';') | ||
862 | { | ||
863 | es_argc = 1; | ||
864 | es_argv[0] = 0; | ||
865 | es_argv[1] = 0; | ||
866 | state = 4; | ||
867 | } | ||
868 | else if (*s == '?' || *s == '>') | ||
869 | { | ||
870 | prefix2 = *s; | ||
871 | } | ||
872 | else | ||
873 | { | ||
874 | es_argc = 0; | ||
875 | suffix = *s; | ||
876 | InterpretEscSeq(); | ||
877 | state = 1; | ||
878 | } | ||
879 | } | ||
880 | else if (state == 4) | ||
881 | { | ||
882 | if (is_digit(*s)) | ||
883 | { | ||
884 | es_argv[es_argc] = 10 * es_argv[es_argc] + (*s - '0'); | ||
885 | } | ||
886 | else if (*s == ';') | ||
887 | { | ||
888 | if (es_argc < MAX_ARG - 1) es_argc++; | ||
889 | es_argv[es_argc] = 0; | ||
890 | if (prefix == ']') | ||
891 | state = 5; | ||
892 | } | ||
893 | else | ||
894 | { | ||
895 | es_argc++; | ||
896 | suffix = *s; | ||
897 | InterpretEscSeq(); | ||
898 | state = 1; | ||
899 | } | ||
900 | } | ||
901 | else if (state == 5) | ||
902 | { | ||
903 | if (*s == BEL) | ||
904 | { | ||
905 | Pt_arg[Pt_len] = '\0'; | ||
906 | InterpretEscSeq(); | ||
907 | state = 1; | ||
908 | } | ||
909 | else if (*s == '\\' && Pt_len > 0 && Pt_arg[Pt_len - 1] == ESC) | ||
910 | { | ||
911 | Pt_arg[--Pt_len] = '\0'; | ||
912 | InterpretEscSeq(); | ||
913 | state = 1; | ||
914 | } | ||
915 | else if (Pt_len < lenof(Pt_arg) - 1) | ||
916 | Pt_arg[Pt_len++] = *s; | ||
917 | } | ||
918 | else if (state == 6) | ||
919 | { | ||
920 | // Ignore it (ESC ) 0 is implicit; nothing else is supported). | ||
921 | state = 1; | ||
922 | } | ||
923 | } | ||
924 | FlushBuffer(); | ||
925 | if (lpNumberOfBytesWritten != NULL) | ||
926 | *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i; | ||
927 | return (i == 0); | ||
928 | } | ||
929 | |||
930 | } // namespace ansi | ||
931 | |||
932 | HANDLE hOut; | ||
933 | HANDLE hIn; | ||
934 | DWORD consolemodeIn = 0; | ||
935 | |||
936 | inline int win32read(int *c) { | ||
937 | DWORD foo; | ||
938 | INPUT_RECORD b; | ||
939 | KEY_EVENT_RECORD e; | ||
940 | BOOL altgr; | ||
941 | |||
942 | while (1) { | ||
943 | if (!ReadConsoleInput(hIn, &b, 1, &foo)) return 0; | ||
944 | if (!foo) return 0; | ||
945 | |||
946 | if (b.EventType == KEY_EVENT && b.Event.KeyEvent.bKeyDown) { | ||
947 | |||
948 | e = b.Event.KeyEvent; | ||
949 | *c = b.Event.KeyEvent.uChar.AsciiChar; | ||
950 | |||
951 | altgr = e.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED); | ||
952 | |||
953 | if (e.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) && !altgr) { | ||
954 | |||
955 | /* Ctrl+Key */ | ||
956 | switch (*c) { | ||
957 | case 'D': | ||
958 | *c = 4; | ||
959 | return 1; | ||
960 | case 'C': | ||
961 | *c = 3; | ||
962 | return 1; | ||
963 | case 'H': | ||
964 | *c = 8; | ||
965 | return 1; | ||
966 | case 'T': | ||
967 | *c = 20; | ||
968 | return 1; | ||
969 | case 'B': /* ctrl-b, left_arrow */ | ||
970 | *c = 2; | ||
971 | return 1; | ||
972 | case 'F': /* ctrl-f right_arrow*/ | ||
973 | *c = 6; | ||
974 | return 1; | ||
975 | case 'P': /* ctrl-p up_arrow*/ | ||
976 | *c = 16; | ||
977 | return 1; | ||
978 | case 'N': /* ctrl-n down_arrow*/ | ||
979 | *c = 14; | ||
980 | return 1; | ||
981 | case 'U': /* Ctrl+u, delete the whole line. */ | ||
982 | *c = 21; | ||
983 | return 1; | ||
984 | case 'K': /* Ctrl+k, delete from current to end of line. */ | ||
985 | *c = 11; | ||
986 | return 1; | ||
987 | case 'A': /* Ctrl+a, go to the start of the line */ | ||
988 | *c = 1; | ||
989 | return 1; | ||
990 | case 'E': /* ctrl+e, go to the end of the line */ | ||
991 | *c = 5; | ||
992 | return 1; | ||
993 | } | ||
994 | |||
995 | /* Other Ctrl+KEYs ignored */ | ||
996 | } else { | ||
997 | |||
998 | switch (e.wVirtualKeyCode) { | ||
999 | |||
1000 | case VK_ESCAPE: /* ignore - send ctrl-c, will return -1 */ | ||
1001 | *c = 3; | ||
1002 | return 1; | ||
1003 | case VK_RETURN: /* enter */ | ||
1004 | *c = 13; | ||
1005 | return 1; | ||
1006 | case VK_LEFT: /* left */ | ||
1007 | *c = 2; | ||
1008 | return 1; | ||
1009 | case VK_RIGHT: /* right */ | ||
1010 | *c = 6; | ||
1011 | return 1; | ||
1012 | case VK_UP: /* up */ | ||
1013 | *c = 16; | ||
1014 | return 1; | ||
1015 | case VK_DOWN: /* down */ | ||
1016 | *c = 14; | ||
1017 | return 1; | ||
1018 | case VK_HOME: | ||
1019 | *c = 1; | ||
1020 | return 1; | ||
1021 | case VK_END: | ||
1022 | *c = 5; | ||
1023 | return 1; | ||
1024 | case VK_BACK: | ||
1025 | *c = 8; | ||
1026 | return 1; | ||
1027 | case VK_DELETE: | ||
1028 | *c = 127; | ||
1029 | return 1; | ||
1030 | default: | ||
1031 | if (*c) return 1; | ||
1032 | } | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | return -1; /* Makes compiler happy */ | ||
1038 | } | ||
1039 | |||
1040 | inline int win32_write(int fd, const void *buffer, unsigned int count) { | ||
1041 | if (fd == _fileno(stdout)) { | ||
1042 | DWORD bytesWritten = 0; | ||
1043 | if (FALSE != ansi::ParseAndPrintANSIString(GetStdHandle(STD_OUTPUT_HANDLE), buffer, (DWORD)count, &bytesWritten)) { | ||
1044 | return (int)bytesWritten; | ||
1045 | } else { | ||
1046 | errno = GetLastError(); | ||
1047 | return 0; | ||
1048 | } | ||
1049 | } else if (fd == _fileno(stderr)) { | ||
1050 | DWORD bytesWritten = 0; | ||
1051 | if (FALSE != ansi::ParseAndPrintANSIString(GetStdHandle(STD_ERROR_HANDLE), buffer, (DWORD)count, &bytesWritten)) { | ||
1052 | return (int)bytesWritten; | ||
1053 | } else { | ||
1054 | errno = GetLastError(); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | } else { | ||
1058 | return _write(fd, buffer, count); | ||
1059 | } | ||
1060 | } | ||
1061 | #endif // _WIN32 | ||
1062 | |||
1063 | #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 | ||
1064 | #define LINENOISE_MAX_LINE 4096 | ||
1065 | static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; | ||
1066 | static CompletionCallback completionCallback; | ||
1067 | |||
1068 | #ifndef _WIN32 | ||
1069 | static struct termios orig_termios; /* In order to restore at exit.*/ | ||
1070 | #endif | ||
1071 | static bool rawmode = false; /* For atexit() function to check if restore is needed*/ | ||
1072 | static bool mlmode = false; /* Multi line mode. Default is single line. */ | ||
1073 | static bool atexit_registered = false; /* Register atexit just 1 time. */ | ||
1074 | static size_t history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; | ||
1075 | static std::vector<std::string> history; | ||
1076 | |||
1077 | /* The linenoiseState structure represents the state during line editing. | ||
1078 | * We pass this state to functions implementing specific editing | ||
1079 | * functionalities. */ | ||
1080 | struct linenoiseState { | ||
1081 | int ifd; /* Terminal stdin file descriptor. */ | ||
1082 | int ofd; /* Terminal stdout file descriptor. */ | ||
1083 | char *buf; /* Edited line buffer. */ | ||
1084 | int buflen; /* Edited line buffer size. */ | ||
1085 | std::string prompt; /* Prompt to display. */ | ||
1086 | int pos; /* Current cursor position. */ | ||
1087 | int oldcolpos; /* Previous refresh cursor column position. */ | ||
1088 | int len; /* Current edited line length. */ | ||
1089 | int cols; /* Number of columns in terminal. */ | ||
1090 | int maxrows; /* Maximum num of rows used so far (multiline mode) */ | ||
1091 | int history_index; /* The history index we are currently editing. */ | ||
1092 | }; | ||
1093 | |||
1094 | enum KEY_ACTION { | ||
1095 | KEY_NULL = 0, /* NULL */ | ||
1096 | CTRL_A = 1, /* Ctrl+a */ | ||
1097 | CTRL_B = 2, /* Ctrl-b */ | ||
1098 | CTRL_C = 3, /* Ctrl-c */ | ||
1099 | CTRL_D = 4, /* Ctrl-d */ | ||
1100 | CTRL_E = 5, /* Ctrl-e */ | ||
1101 | CTRL_F = 6, /* Ctrl-f */ | ||
1102 | CTRL_H = 8, /* Ctrl-h */ | ||
1103 | TAB = 9, /* Tab */ | ||
1104 | CTRL_K = 11, /* Ctrl+k */ | ||
1105 | CTRL_L = 12, /* Ctrl+l */ | ||
1106 | ENTER = 13, /* Enter */ | ||
1107 | CTRL_N = 14, /* Ctrl-n */ | ||
1108 | CTRL_P = 16, /* Ctrl-p */ | ||
1109 | CTRL_T = 20, /* Ctrl-t */ | ||
1110 | CTRL_U = 21, /* Ctrl+u */ | ||
1111 | CTRL_W = 23, /* Ctrl+w */ | ||
1112 | ESC = 27, /* Escape */ | ||
1113 | BACKSPACE = 127 /* Backspace */ | ||
1114 | }; | ||
1115 | |||
1116 | void linenoiseAtExit(void); | ||
1117 | bool AddHistory(const char *line); | ||
1118 | void refreshLine(struct linenoiseState *l); | ||
1119 | |||
1120 | /* ============================ UTF8 utilities ============================== */ | ||
1121 | |||
1122 | static unsigned long unicodeWideCharTable[][2] = { | ||
1123 | { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x2E99, }, { 0x2E9B, 0x2EF3, }, | ||
1124 | { 0x2F00, 0x2FD5, }, { 0x2FF0, 0x2FFB, }, { 0x3000, 0x303E, }, { 0x3041, 0x3096, }, | ||
1125 | { 0x3099, 0x30FF, }, { 0x3105, 0x312D, }, { 0x3131, 0x318E, }, { 0x3190, 0x31BA, }, | ||
1126 | { 0x31C0, 0x31E3, }, { 0x31F0, 0x321E, }, { 0x3220, 0x3247, }, { 0x3250, 0x4DBF, }, | ||
1127 | { 0x4E00, 0xA48C, }, { 0xA490, 0xA4C6, }, { 0xA960, 0xA97C, }, { 0xAC00, 0xD7A3, }, | ||
1128 | { 0xF900, 0xFAFF, }, { 0xFE10, 0xFE19, }, { 0xFE30, 0xFE52, }, { 0xFE54, 0xFE66, }, | ||
1129 | { 0xFE68, 0xFE6B, }, { 0xFF01, 0xFFE6, }, | ||
1130 | { 0x1B000, 0x1B001, }, { 0x1F200, 0x1F202, }, { 0x1F210, 0x1F23A, }, | ||
1131 | { 0x1F240, 0x1F248, }, { 0x1F250, 0x1F251, }, { 0x20000, 0x3FFFD, }, | ||
1132 | }; | ||
1133 | |||
1134 | static int unicodeWideCharTableSize = sizeof(unicodeWideCharTable) / sizeof(unicodeWideCharTable[0]); | ||
1135 | |||
1136 | static int unicodeIsWideChar(unsigned long cp) | ||
1137 | { | ||
1138 | int i; | ||
1139 | for (i = 0; i < unicodeWideCharTableSize; i++) { | ||
1140 | if (unicodeWideCharTable[i][0] <= cp && cp <= unicodeWideCharTable[i][1]) { | ||
1141 | return 1; | ||
1142 | } | ||
1143 | } | ||
1144 | return 0; | ||
1145 | } | ||
1146 | |||
1147 | static unsigned long unicodeCombiningCharTable[] = { | ||
1148 | 0x0300,0x0301,0x0302,0x0303,0x0304,0x0305,0x0306,0x0307, | ||
1149 | 0x0308,0x0309,0x030A,0x030B,0x030C,0x030D,0x030E,0x030F, | ||
1150 | 0x0310,0x0311,0x0312,0x0313,0x0314,0x0315,0x0316,0x0317, | ||
1151 | 0x0318,0x0319,0x031A,0x031B,0x031C,0x031D,0x031E,0x031F, | ||
1152 | 0x0320,0x0321,0x0322,0x0323,0x0324,0x0325,0x0326,0x0327, | ||
1153 | 0x0328,0x0329,0x032A,0x032B,0x032C,0x032D,0x032E,0x032F, | ||
1154 | 0x0330,0x0331,0x0332,0x0333,0x0334,0x0335,0x0336,0x0337, | ||
1155 | 0x0338,0x0339,0x033A,0x033B,0x033C,0x033D,0x033E,0x033F, | ||
1156 | 0x0340,0x0341,0x0342,0x0343,0x0344,0x0345,0x0346,0x0347, | ||
1157 | 0x0348,0x0349,0x034A,0x034B,0x034C,0x034D,0x034E,0x034F, | ||
1158 | 0x0350,0x0351,0x0352,0x0353,0x0354,0x0355,0x0356,0x0357, | ||
1159 | 0x0358,0x0359,0x035A,0x035B,0x035C,0x035D,0x035E,0x035F, | ||
1160 | 0x0360,0x0361,0x0362,0x0363,0x0364,0x0365,0x0366,0x0367, | ||
1161 | 0x0368,0x0369,0x036A,0x036B,0x036C,0x036D,0x036E,0x036F, | ||
1162 | 0x0483,0x0484,0x0485,0x0486,0x0487,0x0591,0x0592,0x0593, | ||
1163 | 0x0594,0x0595,0x0596,0x0597,0x0598,0x0599,0x059A,0x059B, | ||
1164 | 0x059C,0x059D,0x059E,0x059F,0x05A0,0x05A1,0x05A2,0x05A3, | ||
1165 | 0x05A4,0x05A5,0x05A6,0x05A7,0x05A8,0x05A9,0x05AA,0x05AB, | ||
1166 | 0x05AC,0x05AD,0x05AE,0x05AF,0x05B0,0x05B1,0x05B2,0x05B3, | ||
1167 | 0x05B4,0x05B5,0x05B6,0x05B7,0x05B8,0x05B9,0x05BA,0x05BB, | ||
1168 | 0x05BC,0x05BD,0x05BF,0x05C1,0x05C2,0x05C4,0x05C5,0x05C7, | ||
1169 | 0x0610,0x0611,0x0612,0x0613,0x0614,0x0615,0x0616,0x0617, | ||
1170 | 0x0618,0x0619,0x061A,0x064B,0x064C,0x064D,0x064E,0x064F, | ||
1171 | 0x0650,0x0651,0x0652,0x0653,0x0654,0x0655,0x0656,0x0657, | ||
1172 | 0x0658,0x0659,0x065A,0x065B,0x065C,0x065D,0x065E,0x065F, | ||
1173 | 0x0670,0x06D6,0x06D7,0x06D8,0x06D9,0x06DA,0x06DB,0x06DC, | ||
1174 | 0x06DF,0x06E0,0x06E1,0x06E2,0x06E3,0x06E4,0x06E7,0x06E8, | ||
1175 | 0x06EA,0x06EB,0x06EC,0x06ED,0x0711,0x0730,0x0731,0x0732, | ||
1176 | 0x0733,0x0734,0x0735,0x0736,0x0737,0x0738,0x0739,0x073A, | ||
1177 | 0x073B,0x073C,0x073D,0x073E,0x073F,0x0740,0x0741,0x0742, | ||
1178 | 0x0743,0x0744,0x0745,0x0746,0x0747,0x0748,0x0749,0x074A, | ||
1179 | 0x07A6,0x07A7,0x07A8,0x07A9,0x07AA,0x07AB,0x07AC,0x07AD, | ||
1180 | 0x07AE,0x07AF,0x07B0,0x07EB,0x07EC,0x07ED,0x07EE,0x07EF, | ||
1181 | 0x07F0,0x07F1,0x07F2,0x07F3,0x0816,0x0817,0x0818,0x0819, | ||
1182 | 0x081B,0x081C,0x081D,0x081E,0x081F,0x0820,0x0821,0x0822, | ||
1183 | 0x0823,0x0825,0x0826,0x0827,0x0829,0x082A,0x082B,0x082C, | ||
1184 | 0x082D,0x0859,0x085A,0x085B,0x08E3,0x08E4,0x08E5,0x08E6, | ||
1185 | 0x08E7,0x08E8,0x08E9,0x08EA,0x08EB,0x08EC,0x08ED,0x08EE, | ||
1186 | 0x08EF,0x08F0,0x08F1,0x08F2,0x08F3,0x08F4,0x08F5,0x08F6, | ||
1187 | 0x08F7,0x08F8,0x08F9,0x08FA,0x08FB,0x08FC,0x08FD,0x08FE, | ||
1188 | 0x08FF,0x0900,0x0901,0x0902,0x093A,0x093C,0x0941,0x0942, | ||
1189 | 0x0943,0x0944,0x0945,0x0946,0x0947,0x0948,0x094D,0x0951, | ||
1190 | 0x0952,0x0953,0x0954,0x0955,0x0956,0x0957,0x0962,0x0963, | ||
1191 | 0x0981,0x09BC,0x09C1,0x09C2,0x09C3,0x09C4,0x09CD,0x09E2, | ||
1192 | 0x09E3,0x0A01,0x0A02,0x0A3C,0x0A41,0x0A42,0x0A47,0x0A48, | ||
1193 | 0x0A4B,0x0A4C,0x0A4D,0x0A51,0x0A70,0x0A71,0x0A75,0x0A81, | ||
1194 | 0x0A82,0x0ABC,0x0AC1,0x0AC2,0x0AC3,0x0AC4,0x0AC5,0x0AC7, | ||
1195 | 0x0AC8,0x0ACD,0x0AE2,0x0AE3,0x0B01,0x0B3C,0x0B3F,0x0B41, | ||
1196 | 0x0B42,0x0B43,0x0B44,0x0B4D,0x0B56,0x0B62,0x0B63,0x0B82, | ||
1197 | 0x0BC0,0x0BCD,0x0C00,0x0C3E,0x0C3F,0x0C40,0x0C46,0x0C47, | ||
1198 | 0x0C48,0x0C4A,0x0C4B,0x0C4C,0x0C4D,0x0C55,0x0C56,0x0C62, | ||
1199 | 0x0C63,0x0C81,0x0CBC,0x0CBF,0x0CC6,0x0CCC,0x0CCD,0x0CE2, | ||
1200 | 0x0CE3,0x0D01,0x0D41,0x0D42,0x0D43,0x0D44,0x0D4D,0x0D62, | ||
1201 | 0x0D63,0x0DCA,0x0DD2,0x0DD3,0x0DD4,0x0DD6,0x0E31,0x0E34, | ||
1202 | 0x0E35,0x0E36,0x0E37,0x0E38,0x0E39,0x0E3A,0x0E47,0x0E48, | ||
1203 | 0x0E49,0x0E4A,0x0E4B,0x0E4C,0x0E4D,0x0E4E,0x0EB1,0x0EB4, | ||
1204 | 0x0EB5,0x0EB6,0x0EB7,0x0EB8,0x0EB9,0x0EBB,0x0EBC,0x0EC8, | ||
1205 | 0x0EC9,0x0ECA,0x0ECB,0x0ECC,0x0ECD,0x0F18,0x0F19,0x0F35, | ||
1206 | 0x0F37,0x0F39,0x0F71,0x0F72,0x0F73,0x0F74,0x0F75,0x0F76, | ||
1207 | 0x0F77,0x0F78,0x0F79,0x0F7A,0x0F7B,0x0F7C,0x0F7D,0x0F7E, | ||
1208 | 0x0F80,0x0F81,0x0F82,0x0F83,0x0F84,0x0F86,0x0F87,0x0F8D, | ||
1209 | 0x0F8E,0x0F8F,0x0F90,0x0F91,0x0F92,0x0F93,0x0F94,0x0F95, | ||
1210 | 0x0F96,0x0F97,0x0F99,0x0F9A,0x0F9B,0x0F9C,0x0F9D,0x0F9E, | ||
1211 | 0x0F9F,0x0FA0,0x0FA1,0x0FA2,0x0FA3,0x0FA4,0x0FA5,0x0FA6, | ||
1212 | 0x0FA7,0x0FA8,0x0FA9,0x0FAA,0x0FAB,0x0FAC,0x0FAD,0x0FAE, | ||
1213 | 0x0FAF,0x0FB0,0x0FB1,0x0FB2,0x0FB3,0x0FB4,0x0FB5,0x0FB6, | ||
1214 | 0x0FB7,0x0FB8,0x0FB9,0x0FBA,0x0FBB,0x0FBC,0x0FC6,0x102D, | ||
1215 | 0x102E,0x102F,0x1030,0x1032,0x1033,0x1034,0x1035,0x1036, | ||
1216 | 0x1037,0x1039,0x103A,0x103D,0x103E,0x1058,0x1059,0x105E, | ||
1217 | 0x105F,0x1060,0x1071,0x1072,0x1073,0x1074,0x1082,0x1085, | ||
1218 | 0x1086,0x108D,0x109D,0x135D,0x135E,0x135F,0x1712,0x1713, | ||
1219 | 0x1714,0x1732,0x1733,0x1734,0x1752,0x1753,0x1772,0x1773, | ||
1220 | 0x17B4,0x17B5,0x17B7,0x17B8,0x17B9,0x17BA,0x17BB,0x17BC, | ||
1221 | 0x17BD,0x17C6,0x17C9,0x17CA,0x17CB,0x17CC,0x17CD,0x17CE, | ||
1222 | 0x17CF,0x17D0,0x17D1,0x17D2,0x17D3,0x17DD,0x180B,0x180C, | ||
1223 | 0x180D,0x18A9,0x1920,0x1921,0x1922,0x1927,0x1928,0x1932, | ||
1224 | 0x1939,0x193A,0x193B,0x1A17,0x1A18,0x1A1B,0x1A56,0x1A58, | ||
1225 | 0x1A59,0x1A5A,0x1A5B,0x1A5C,0x1A5D,0x1A5E,0x1A60,0x1A62, | ||
1226 | 0x1A65,0x1A66,0x1A67,0x1A68,0x1A69,0x1A6A,0x1A6B,0x1A6C, | ||
1227 | 0x1A73,0x1A74,0x1A75,0x1A76,0x1A77,0x1A78,0x1A79,0x1A7A, | ||
1228 | 0x1A7B,0x1A7C,0x1A7F,0x1AB0,0x1AB1,0x1AB2,0x1AB3,0x1AB4, | ||
1229 | 0x1AB5,0x1AB6,0x1AB7,0x1AB8,0x1AB9,0x1ABA,0x1ABB,0x1ABC, | ||
1230 | 0x1ABD,0x1B00,0x1B01,0x1B02,0x1B03,0x1B34,0x1B36,0x1B37, | ||
1231 | 0x1B38,0x1B39,0x1B3A,0x1B3C,0x1B42,0x1B6B,0x1B6C,0x1B6D, | ||
1232 | 0x1B6E,0x1B6F,0x1B70,0x1B71,0x1B72,0x1B73,0x1B80,0x1B81, | ||
1233 | 0x1BA2,0x1BA3,0x1BA4,0x1BA5,0x1BA8,0x1BA9,0x1BAB,0x1BAC, | ||
1234 | 0x1BAD,0x1BE6,0x1BE8,0x1BE9,0x1BED,0x1BEF,0x1BF0,0x1BF1, | ||
1235 | 0x1C2C,0x1C2D,0x1C2E,0x1C2F,0x1C30,0x1C31,0x1C32,0x1C33, | ||
1236 | 0x1C36,0x1C37,0x1CD0,0x1CD1,0x1CD2,0x1CD4,0x1CD5,0x1CD6, | ||
1237 | 0x1CD7,0x1CD8,0x1CD9,0x1CDA,0x1CDB,0x1CDC,0x1CDD,0x1CDE, | ||
1238 | 0x1CDF,0x1CE0,0x1CE2,0x1CE3,0x1CE4,0x1CE5,0x1CE6,0x1CE7, | ||
1239 | 0x1CE8,0x1CED,0x1CF4,0x1CF8,0x1CF9,0x1DC0,0x1DC1,0x1DC2, | ||
1240 | 0x1DC3,0x1DC4,0x1DC5,0x1DC6,0x1DC7,0x1DC8,0x1DC9,0x1DCA, | ||
1241 | 0x1DCB,0x1DCC,0x1DCD,0x1DCE,0x1DCF,0x1DD0,0x1DD1,0x1DD2, | ||
1242 | 0x1DD3,0x1DD4,0x1DD5,0x1DD6,0x1DD7,0x1DD8,0x1DD9,0x1DDA, | ||
1243 | 0x1DDB,0x1DDC,0x1DDD,0x1DDE,0x1DDF,0x1DE0,0x1DE1,0x1DE2, | ||
1244 | 0x1DE3,0x1DE4,0x1DE5,0x1DE6,0x1DE7,0x1DE8,0x1DE9,0x1DEA, | ||
1245 | 0x1DEB,0x1DEC,0x1DED,0x1DEE,0x1DEF,0x1DF0,0x1DF1,0x1DF2, | ||
1246 | 0x1DF3,0x1DF4,0x1DF5,0x1DFC,0x1DFD,0x1DFE,0x1DFF,0x20D0, | ||
1247 | 0x20D1,0x20D2,0x20D3,0x20D4,0x20D5,0x20D6,0x20D7,0x20D8, | ||
1248 | 0x20D9,0x20DA,0x20DB,0x20DC,0x20E1,0x20E5,0x20E6,0x20E7, | ||
1249 | 0x20E8,0x20E9,0x20EA,0x20EB,0x20EC,0x20ED,0x20EE,0x20EF, | ||
1250 | 0x20F0,0x2CEF,0x2CF0,0x2CF1,0x2D7F,0x2DE0,0x2DE1,0x2DE2, | ||
1251 | 0x2DE3,0x2DE4,0x2DE5,0x2DE6,0x2DE7,0x2DE8,0x2DE9,0x2DEA, | ||
1252 | 0x2DEB,0x2DEC,0x2DED,0x2DEE,0x2DEF,0x2DF0,0x2DF1,0x2DF2, | ||
1253 | 0x2DF3,0x2DF4,0x2DF5,0x2DF6,0x2DF7,0x2DF8,0x2DF9,0x2DFA, | ||
1254 | 0x2DFB,0x2DFC,0x2DFD,0x2DFE,0x2DFF,0x302A,0x302B,0x302C, | ||
1255 | 0x302D,0x3099,0x309A,0xA66F,0xA674,0xA675,0xA676,0xA677, | ||
1256 | 0xA678,0xA679,0xA67A,0xA67B,0xA67C,0xA67D,0xA69E,0xA69F, | ||
1257 | 0xA6F0,0xA6F1,0xA802,0xA806,0xA80B,0xA825,0xA826,0xA8C4, | ||
1258 | 0xA8E0,0xA8E1,0xA8E2,0xA8E3,0xA8E4,0xA8E5,0xA8E6,0xA8E7, | ||
1259 | 0xA8E8,0xA8E9,0xA8EA,0xA8EB,0xA8EC,0xA8ED,0xA8EE,0xA8EF, | ||
1260 | 0xA8F0,0xA8F1,0xA926,0xA927,0xA928,0xA929,0xA92A,0xA92B, | ||
1261 | 0xA92C,0xA92D,0xA947,0xA948,0xA949,0xA94A,0xA94B,0xA94C, | ||
1262 | 0xA94D,0xA94E,0xA94F,0xA950,0xA951,0xA980,0xA981,0xA982, | ||
1263 | 0xA9B3,0xA9B6,0xA9B7,0xA9B8,0xA9B9,0xA9BC,0xA9E5,0xAA29, | ||
1264 | 0xAA2A,0xAA2B,0xAA2C,0xAA2D,0xAA2E,0xAA31,0xAA32,0xAA35, | ||
1265 | 0xAA36,0xAA43,0xAA4C,0xAA7C,0xAAB0,0xAAB2,0xAAB3,0xAAB4, | ||
1266 | 0xAAB7,0xAAB8,0xAABE,0xAABF,0xAAC1,0xAAEC,0xAAED,0xAAF6, | ||
1267 | 0xABE5,0xABE8,0xABED,0xFB1E,0xFE00,0xFE01,0xFE02,0xFE03, | ||
1268 | 0xFE04,0xFE05,0xFE06,0xFE07,0xFE08,0xFE09,0xFE0A,0xFE0B, | ||
1269 | 0xFE0C,0xFE0D,0xFE0E,0xFE0F,0xFE20,0xFE21,0xFE22,0xFE23, | ||
1270 | 0xFE24,0xFE25,0xFE26,0xFE27,0xFE28,0xFE29,0xFE2A,0xFE2B, | ||
1271 | 0xFE2C,0xFE2D,0xFE2E,0xFE2F, | ||
1272 | 0x101FD,0x102E0,0x10376,0x10377,0x10378,0x10379,0x1037A,0x10A01, | ||
1273 | 0x10A02,0x10A03,0x10A05,0x10A06,0x10A0C,0x10A0D,0x10A0E,0x10A0F, | ||
1274 | 0x10A38,0x10A39,0x10A3A,0x10A3F,0x10AE5,0x10AE6,0x11001,0x11038, | ||
1275 | 0x11039,0x1103A,0x1103B,0x1103C,0x1103D,0x1103E,0x1103F,0x11040, | ||
1276 | 0x11041,0x11042,0x11043,0x11044,0x11045,0x11046,0x1107F,0x11080, | ||
1277 | 0x11081,0x110B3,0x110B4,0x110B5,0x110B6,0x110B9,0x110BA,0x11100, | ||
1278 | 0x11101,0x11102,0x11127,0x11128,0x11129,0x1112A,0x1112B,0x1112D, | ||
1279 | 0x1112E,0x1112F,0x11130,0x11131,0x11132,0x11133,0x11134,0x11173, | ||
1280 | 0x11180,0x11181,0x111B6,0x111B7,0x111B8,0x111B9,0x111BA,0x111BB, | ||
1281 | 0x111BC,0x111BD,0x111BE,0x111CA,0x111CB,0x111CC,0x1122F,0x11230, | ||
1282 | 0x11231,0x11234,0x11236,0x11237,0x112DF,0x112E3,0x112E4,0x112E5, | ||
1283 | 0x112E6,0x112E7,0x112E8,0x112E9,0x112EA,0x11300,0x11301,0x1133C, | ||
1284 | 0x11340,0x11366,0x11367,0x11368,0x11369,0x1136A,0x1136B,0x1136C, | ||
1285 | 0x11370,0x11371,0x11372,0x11373,0x11374,0x114B3,0x114B4,0x114B5, | ||
1286 | 0x114B6,0x114B7,0x114B8,0x114BA,0x114BF,0x114C0,0x114C2,0x114C3, | ||
1287 | 0x115B2,0x115B3,0x115B4,0x115B5,0x115BC,0x115BD,0x115BF,0x115C0, | ||
1288 | 0x115DC,0x115DD,0x11633,0x11634,0x11635,0x11636,0x11637,0x11638, | ||
1289 | 0x11639,0x1163A,0x1163D,0x1163F,0x11640,0x116AB,0x116AD,0x116B0, | ||
1290 | 0x116B1,0x116B2,0x116B3,0x116B4,0x116B5,0x116B7,0x1171D,0x1171E, | ||
1291 | 0x1171F,0x11722,0x11723,0x11724,0x11725,0x11727,0x11728,0x11729, | ||
1292 | 0x1172A,0x1172B,0x16AF0,0x16AF1,0x16AF2,0x16AF3,0x16AF4,0x16B30, | ||
1293 | 0x16B31,0x16B32,0x16B33,0x16B34,0x16B35,0x16B36,0x16F8F,0x16F90, | ||
1294 | 0x16F91,0x16F92,0x1BC9D,0x1BC9E,0x1D167,0x1D168,0x1D169,0x1D17B, | ||
1295 | 0x1D17C,0x1D17D,0x1D17E,0x1D17F,0x1D180,0x1D181,0x1D182,0x1D185, | ||
1296 | 0x1D186,0x1D187,0x1D188,0x1D189,0x1D18A,0x1D18B,0x1D1AA,0x1D1AB, | ||
1297 | 0x1D1AC,0x1D1AD,0x1D242,0x1D243,0x1D244,0x1DA00,0x1DA01,0x1DA02, | ||
1298 | 0x1DA03,0x1DA04,0x1DA05,0x1DA06,0x1DA07,0x1DA08,0x1DA09,0x1DA0A, | ||
1299 | 0x1DA0B,0x1DA0C,0x1DA0D,0x1DA0E,0x1DA0F,0x1DA10,0x1DA11,0x1DA12, | ||
1300 | 0x1DA13,0x1DA14,0x1DA15,0x1DA16,0x1DA17,0x1DA18,0x1DA19,0x1DA1A, | ||
1301 | 0x1DA1B,0x1DA1C,0x1DA1D,0x1DA1E,0x1DA1F,0x1DA20,0x1DA21,0x1DA22, | ||
1302 | 0x1DA23,0x1DA24,0x1DA25,0x1DA26,0x1DA27,0x1DA28,0x1DA29,0x1DA2A, | ||
1303 | 0x1DA2B,0x1DA2C,0x1DA2D,0x1DA2E,0x1DA2F,0x1DA30,0x1DA31,0x1DA32, | ||
1304 | 0x1DA33,0x1DA34,0x1DA35,0x1DA36,0x1DA3B,0x1DA3C,0x1DA3D,0x1DA3E, | ||
1305 | 0x1DA3F,0x1DA40,0x1DA41,0x1DA42,0x1DA43,0x1DA44,0x1DA45,0x1DA46, | ||
1306 | 0x1DA47,0x1DA48,0x1DA49,0x1DA4A,0x1DA4B,0x1DA4C,0x1DA4D,0x1DA4E, | ||
1307 | 0x1DA4F,0x1DA50,0x1DA51,0x1DA52,0x1DA53,0x1DA54,0x1DA55,0x1DA56, | ||
1308 | 0x1DA57,0x1DA58,0x1DA59,0x1DA5A,0x1DA5B,0x1DA5C,0x1DA5D,0x1DA5E, | ||
1309 | 0x1DA5F,0x1DA60,0x1DA61,0x1DA62,0x1DA63,0x1DA64,0x1DA65,0x1DA66, | ||
1310 | 0x1DA67,0x1DA68,0x1DA69,0x1DA6A,0x1DA6B,0x1DA6C,0x1DA75,0x1DA84, | ||
1311 | 0x1DA9B,0x1DA9C,0x1DA9D,0x1DA9E,0x1DA9F,0x1DAA1,0x1DAA2,0x1DAA3, | ||
1312 | 0x1DAA4,0x1DAA5,0x1DAA6,0x1DAA7,0x1DAA8,0x1DAA9,0x1DAAA,0x1DAAB, | ||
1313 | 0x1DAAC,0x1DAAD,0x1DAAE,0x1DAAF,0x1E8D0,0x1E8D1,0x1E8D2,0x1E8D3, | ||
1314 | 0x1E8D4,0x1E8D5,0x1E8D6,0xE0100,0xE0101,0xE0102,0xE0103,0xE0104, | ||
1315 | 0xE0105,0xE0106,0xE0107,0xE0108,0xE0109,0xE010A,0xE010B,0xE010C, | ||
1316 | 0xE010D,0xE010E,0xE010F,0xE0110,0xE0111,0xE0112,0xE0113,0xE0114, | ||
1317 | 0xE0115,0xE0116,0xE0117,0xE0118,0xE0119,0xE011A,0xE011B,0xE011C, | ||
1318 | 0xE011D,0xE011E,0xE011F,0xE0120,0xE0121,0xE0122,0xE0123,0xE0124, | ||
1319 | 0xE0125,0xE0126,0xE0127,0xE0128,0xE0129,0xE012A,0xE012B,0xE012C, | ||
1320 | 0xE012D,0xE012E,0xE012F,0xE0130,0xE0131,0xE0132,0xE0133,0xE0134, | ||
1321 | 0xE0135,0xE0136,0xE0137,0xE0138,0xE0139,0xE013A,0xE013B,0xE013C, | ||
1322 | 0xE013D,0xE013E,0xE013F,0xE0140,0xE0141,0xE0142,0xE0143,0xE0144, | ||
1323 | 0xE0145,0xE0146,0xE0147,0xE0148,0xE0149,0xE014A,0xE014B,0xE014C, | ||
1324 | 0xE014D,0xE014E,0xE014F,0xE0150,0xE0151,0xE0152,0xE0153,0xE0154, | ||
1325 | 0xE0155,0xE0156,0xE0157,0xE0158,0xE0159,0xE015A,0xE015B,0xE015C, | ||
1326 | 0xE015D,0xE015E,0xE015F,0xE0160,0xE0161,0xE0162,0xE0163,0xE0164, | ||
1327 | 0xE0165,0xE0166,0xE0167,0xE0168,0xE0169,0xE016A,0xE016B,0xE016C, | ||
1328 | 0xE016D,0xE016E,0xE016F,0xE0170,0xE0171,0xE0172,0xE0173,0xE0174, | ||
1329 | 0xE0175,0xE0176,0xE0177,0xE0178,0xE0179,0xE017A,0xE017B,0xE017C, | ||
1330 | 0xE017D,0xE017E,0xE017F,0xE0180,0xE0181,0xE0182,0xE0183,0xE0184, | ||
1331 | 0xE0185,0xE0186,0xE0187,0xE0188,0xE0189,0xE018A,0xE018B,0xE018C, | ||
1332 | 0xE018D,0xE018E,0xE018F,0xE0190,0xE0191,0xE0192,0xE0193,0xE0194, | ||
1333 | 0xE0195,0xE0196,0xE0197,0xE0198,0xE0199,0xE019A,0xE019B,0xE019C, | ||
1334 | 0xE019D,0xE019E,0xE019F,0xE01A0,0xE01A1,0xE01A2,0xE01A3,0xE01A4, | ||
1335 | 0xE01A5,0xE01A6,0xE01A7,0xE01A8,0xE01A9,0xE01AA,0xE01AB,0xE01AC, | ||
1336 | 0xE01AD,0xE01AE,0xE01AF,0xE01B0,0xE01B1,0xE01B2,0xE01B3,0xE01B4, | ||
1337 | 0xE01B5,0xE01B6,0xE01B7,0xE01B8,0xE01B9,0xE01BA,0xE01BB,0xE01BC, | ||
1338 | 0xE01BD,0xE01BE,0xE01BF,0xE01C0,0xE01C1,0xE01C2,0xE01C3,0xE01C4, | ||
1339 | 0xE01C5,0xE01C6,0xE01C7,0xE01C8,0xE01C9,0xE01CA,0xE01CB,0xE01CC, | ||
1340 | 0xE01CD,0xE01CE,0xE01CF,0xE01D0,0xE01D1,0xE01D2,0xE01D3,0xE01D4, | ||
1341 | 0xE01D5,0xE01D6,0xE01D7,0xE01D8,0xE01D9,0xE01DA,0xE01DB,0xE01DC, | ||
1342 | 0xE01DD,0xE01DE,0xE01DF,0xE01E0,0xE01E1,0xE01E2,0xE01E3,0xE01E4, | ||
1343 | 0xE01E5,0xE01E6,0xE01E7,0xE01E8,0xE01E9,0xE01EA,0xE01EB,0xE01EC, | ||
1344 | 0xE01ED,0xE01EE,0xE01EF, | ||
1345 | }; | ||
1346 | |||
1347 | static int unicodeCombiningCharTableSize = sizeof(unicodeCombiningCharTable) / sizeof(unicodeCombiningCharTable[0]); | ||
1348 | |||
1349 | inline int unicodeIsCombiningChar(unsigned long cp) | ||
1350 | { | ||
1351 | int i; | ||
1352 | for (i = 0; i < unicodeCombiningCharTableSize; i++) { | ||
1353 | if (unicodeCombiningCharTable[i] == cp) { | ||
1354 | return 1; | ||
1355 | } | ||
1356 | } | ||
1357 | return 0; | ||
1358 | } | ||
1359 | |||
1360 | /* Get length of previous UTF8 character | ||
1361 | */ | ||
1362 | inline int unicodePrevUTF8CharLen(char* buf, int pos) | ||
1363 | { | ||
1364 | int end = pos--; | ||
1365 | while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80) { | ||
1366 | pos--; | ||
1367 | } | ||
1368 | return end - pos; | ||
1369 | } | ||
1370 | |||
1371 | /* Get length of previous UTF8 character | ||
1372 | */ | ||
1373 | inline int unicodeUTF8CharLen(char* buf, int buf_len, int pos) | ||
1374 | { | ||
1375 | if (pos == buf_len) { return 0; } | ||
1376 | unsigned char ch = buf[pos]; | ||
1377 | if (ch < 0x80) { return 1; } | ||
1378 | else if (ch < 0xE0) { return 2; } | ||
1379 | else if (ch < 0xF0) { return 3; } | ||
1380 | else { return 4; } | ||
1381 | } | ||
1382 | |||
1383 | /* Convert UTF8 to Unicode code point | ||
1384 | */ | ||
1385 | inline int unicodeUTF8CharToCodePoint( | ||
1386 | const char* buf, | ||
1387 | int len, | ||
1388 | int* cp) | ||
1389 | { | ||
1390 | if (len) { | ||
1391 | unsigned char byte = buf[0]; | ||
1392 | if ((byte & 0x80) == 0) { | ||
1393 | *cp = byte; | ||
1394 | return 1; | ||
1395 | } else if ((byte & 0xE0) == 0xC0) { | ||
1396 | if (len >= 2) { | ||
1397 | *cp = (((unsigned long)(buf[0] & 0x1F)) << 6) | | ||
1398 | ((unsigned long)(buf[1] & 0x3F)); | ||
1399 | return 2; | ||
1400 | } | ||
1401 | } else if ((byte & 0xF0) == 0xE0) { | ||
1402 | if (len >= 3) { | ||
1403 | *cp = (((unsigned long)(buf[0] & 0x0F)) << 12) | | ||
1404 | (((unsigned long)(buf[1] & 0x3F)) << 6) | | ||
1405 | ((unsigned long)(buf[2] & 0x3F)); | ||
1406 | return 3; | ||
1407 | } | ||
1408 | } else if ((byte & 0xF8) == 0xF0) { | ||
1409 | if (len >= 4) { | ||
1410 | *cp = (((unsigned long)(buf[0] & 0x07)) << 18) | | ||
1411 | (((unsigned long)(buf[1] & 0x3F)) << 12) | | ||
1412 | (((unsigned long)(buf[2] & 0x3F)) << 6) | | ||
1413 | ((unsigned long)(buf[3] & 0x3F)); | ||
1414 | return 4; | ||
1415 | } | ||
1416 | } | ||
1417 | } | ||
1418 | return 0; | ||
1419 | } | ||
1420 | |||
1421 | /* Get length of grapheme | ||
1422 | */ | ||
1423 | inline int unicodeGraphemeLen(char* buf, int buf_len, int pos) | ||
1424 | { | ||
1425 | if (pos == buf_len) { | ||
1426 | return 0; | ||
1427 | } | ||
1428 | int beg = pos; | ||
1429 | pos += unicodeUTF8CharLen(buf, buf_len, pos); | ||
1430 | while (pos < buf_len) { | ||
1431 | int len = unicodeUTF8CharLen(buf, buf_len, pos); | ||
1432 | int cp = 0; | ||
1433 | unicodeUTF8CharToCodePoint(buf + pos, len, &cp); | ||
1434 | if (!unicodeIsCombiningChar(cp)) { | ||
1435 | return pos - beg; | ||
1436 | } | ||
1437 | pos += len; | ||
1438 | } | ||
1439 | return pos - beg; | ||
1440 | } | ||
1441 | |||
1442 | /* Get length of previous grapheme | ||
1443 | */ | ||
1444 | inline int unicodePrevGraphemeLen(char* buf, int pos) | ||
1445 | { | ||
1446 | if (pos == 0) { | ||
1447 | return 0; | ||
1448 | } | ||
1449 | int end = pos; | ||
1450 | while (pos > 0) { | ||
1451 | int len = unicodePrevUTF8CharLen(buf, pos); | ||
1452 | pos -= len; | ||
1453 | int cp = 0; | ||
1454 | unicodeUTF8CharToCodePoint(buf + pos, len, &cp); | ||
1455 | if (!unicodeIsCombiningChar(cp)) { | ||
1456 | return end - pos; | ||
1457 | } | ||
1458 | } | ||
1459 | return 0; | ||
1460 | } | ||
1461 | |||
1462 | inline int isAnsiEscape(const char* buf, int buf_len, int* len) | ||
1463 | { | ||
1464 | if (buf_len > 2 && !memcmp("\033[", buf, 2)) { | ||
1465 | int off = 2; | ||
1466 | while (off < buf_len) { | ||
1467 | switch (buf[off++]) { | ||
1468 | case 'A': case 'B': case 'C': case 'D': | ||
1469 | case 'E': case 'F': case 'G': case 'H': | ||
1470 | case 'J': case 'K': case 'S': case 'T': | ||
1471 | case 'f': case 'm': | ||
1472 | *len = off; | ||
1473 | return 1; | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | return 0; | ||
1478 | } | ||
1479 | |||
1480 | /* Get column position for the single line mode. | ||
1481 | */ | ||
1482 | inline int unicodeColumnPos(const char* buf, int buf_len) | ||
1483 | { | ||
1484 | int ret = 0; | ||
1485 | |||
1486 | int off = 0; | ||
1487 | while (off < buf_len) { | ||
1488 | int len; | ||
1489 | if (isAnsiEscape(buf + off, buf_len - off, &len)) { | ||
1490 | off += len; | ||
1491 | continue; | ||
1492 | } | ||
1493 | |||
1494 | int cp = 0; | ||
1495 | len = unicodeUTF8CharToCodePoint(buf + off, buf_len - off, &cp); | ||
1496 | |||
1497 | if (!unicodeIsCombiningChar(cp)) { | ||
1498 | ret += unicodeIsWideChar(cp) ? 2 : 1; | ||
1499 | } | ||
1500 | |||
1501 | off += len; | ||
1502 | } | ||
1503 | |||
1504 | return ret; | ||
1505 | } | ||
1506 | |||
1507 | /* Get column position for the multi line mode. | ||
1508 | */ | ||
1509 | inline int unicodeColumnPosForMultiLine(char* buf, int buf_len, int pos, int cols, int ini_pos) | ||
1510 | { | ||
1511 | int ret = 0; | ||
1512 | int colwid = ini_pos; | ||
1513 | |||
1514 | int off = 0; | ||
1515 | while (off < buf_len) { | ||
1516 | int cp = 0; | ||
1517 | int len = unicodeUTF8CharToCodePoint(buf + off, buf_len - off, &cp); | ||
1518 | |||
1519 | int wid = 0; | ||
1520 | if (!unicodeIsCombiningChar(cp)) { | ||
1521 | wid = unicodeIsWideChar(cp) ? 2 : 1; | ||
1522 | } | ||
1523 | |||
1524 | int dif = (int)(colwid + wid) - (int)cols; | ||
1525 | if (dif > 0) { | ||
1526 | ret += dif; | ||
1527 | colwid = wid; | ||
1528 | } else if (dif == 0) { | ||
1529 | colwid = 0; | ||
1530 | } else { | ||
1531 | colwid += wid; | ||
1532 | } | ||
1533 | |||
1534 | if (off >= pos) { | ||
1535 | break; | ||
1536 | } | ||
1537 | |||
1538 | off += len; | ||
1539 | ret += wid; | ||
1540 | } | ||
1541 | |||
1542 | return ret; | ||
1543 | } | ||
1544 | |||
1545 | /* Read UTF8 character from file. | ||
1546 | */ | ||
1547 | inline int unicodeReadUTF8Char(int fd, char* buf, int* cp) | ||
1548 | { | ||
1549 | int nread = read(fd,&buf[0],1); | ||
1550 | |||
1551 | if (nread <= 0) { return nread; } | ||
1552 | |||
1553 | unsigned char byte = buf[0]; | ||
1554 | |||
1555 | if ((byte & 0x80) == 0) { | ||
1556 | ; | ||
1557 | } else if ((byte & 0xE0) == 0xC0) { | ||
1558 | nread = read(fd,&buf[1],1); | ||
1559 | if (nread <= 0) { return nread; } | ||
1560 | } else if ((byte & 0xF0) == 0xE0) { | ||
1561 | nread = read(fd,&buf[1],2); | ||
1562 | if (nread <= 0) { return nread; } | ||
1563 | } else if ((byte & 0xF8) == 0xF0) { | ||
1564 | nread = read(fd,&buf[1],3); | ||
1565 | if (nread <= 0) { return nread; } | ||
1566 | } else { | ||
1567 | return -1; | ||
1568 | } | ||
1569 | |||
1570 | return unicodeUTF8CharToCodePoint(buf, 4, cp); | ||
1571 | } | ||
1572 | |||
1573 | /* ======================= Low level terminal handling ====================== */ | ||
1574 | |||
1575 | /* Set if to use or not the multi line mode. */ | ||
1576 | inline void SetMultiLine(bool ml) { | ||
1577 | mlmode = ml; | ||
1578 | } | ||
1579 | |||
1580 | /* Return true if the terminal name is in the list of terminals we know are | ||
1581 | * not able to understand basic escape sequences. */ | ||
1582 | inline bool isUnsupportedTerm(void) { | ||
1583 | #ifndef _WIN32 | ||
1584 | char *term = getenv("TERM"); | ||
1585 | int j; | ||
1586 | |||
1587 | if (term == NULL) return false; | ||
1588 | for (j = 0; unsupported_term[j]; j++) | ||
1589 | if (!strcasecmp(term,unsupported_term[j])) return true; | ||
1590 | #endif | ||
1591 | return false; | ||
1592 | } | ||
1593 | |||
1594 | /* Raw mode: 1960 magic shit. */ | ||
1595 | inline bool enableRawMode(int fd) { | ||
1596 | #ifndef _WIN32 | ||
1597 | struct termios raw; | ||
1598 | |||
1599 | if (!isatty(STDIN_FILENO)) goto fatal; | ||
1600 | if (!atexit_registered) { | ||
1601 | atexit(linenoiseAtExit); | ||
1602 | atexit_registered = true; | ||
1603 | } | ||
1604 | if (tcgetattr(fd,&orig_termios) == -1) goto fatal; | ||
1605 | |||
1606 | raw = orig_termios; /* modify the original mode */ | ||
1607 | /* input modes: no break, no CR to NL, no parity check, no strip char, | ||
1608 | * no start/stop output control. */ | ||
1609 | raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); | ||
1610 | /* output modes - disable post processing */ | ||
1611 | raw.c_oflag &= ~(OPOST); | ||
1612 | /* control modes - set 8 bit chars */ | ||
1613 | raw.c_cflag |= (CS8); | ||
1614 | /* local modes - choing off, canonical off, no extended functions, | ||
1615 | * no signal chars (^Z,^C) */ | ||
1616 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); | ||
1617 | /* control chars - set return condition: min number of bytes and timer. | ||
1618 | * We want read to return every single byte, without timeout. */ | ||
1619 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ | ||
1620 | |||
1621 | /* put terminal in raw mode after flushing */ | ||
1622 | if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; | ||
1623 | rawmode = true; | ||
1624 | #else | ||
1625 | if (!atexit_registered) { | ||
1626 | /* Cleanup them at exit */ | ||
1627 | atexit(linenoiseAtExit); | ||
1628 | atexit_registered = true; | ||
1629 | |||
1630 | /* Init windows console handles only once */ | ||
1631 | hOut = GetStdHandle(STD_OUTPUT_HANDLE); | ||
1632 | if (hOut==INVALID_HANDLE_VALUE) goto fatal; | ||
1633 | } | ||
1634 | |||
1635 | DWORD consolemodeOut; | ||
1636 | if (!GetConsoleMode(hOut, &consolemodeOut)) { | ||
1637 | CloseHandle(hOut); | ||
1638 | errno = ENOTTY; | ||
1639 | return false; | ||
1640 | }; | ||
1641 | |||
1642 | hIn = GetStdHandle(STD_INPUT_HANDLE); | ||
1643 | if (hIn == INVALID_HANDLE_VALUE) { | ||
1644 | CloseHandle(hOut); | ||
1645 | errno = ENOTTY; | ||
1646 | return false; | ||
1647 | } | ||
1648 | |||
1649 | GetConsoleMode(hIn, &consolemodeIn); | ||
1650 | SetConsoleMode(hIn, ENABLE_PROCESSED_INPUT); | ||
1651 | |||
1652 | rawmode = true; | ||
1653 | #endif | ||
1654 | return true; | ||
1655 | |||
1656 | fatal: | ||
1657 | errno = ENOTTY; | ||
1658 | return false; | ||
1659 | } | ||
1660 | |||
1661 | inline void disableRawMode(int fd) { | ||
1662 | #ifdef _WIN32 | ||
1663 | if (consolemodeIn) { | ||
1664 | SetConsoleMode(hIn, consolemodeIn); | ||
1665 | consolemodeIn = 0; | ||
1666 | } | ||
1667 | rawmode = false; | ||
1668 | #else | ||
1669 | /* Don't even check the return value as it's too late. */ | ||
1670 | if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) | ||
1671 | rawmode = false; | ||
1672 | #endif | ||
1673 | } | ||
1674 | |||
1675 | /* Use the ESC [6n escape sequence to query the horizontal cursor position | ||
1676 | * and return it. On error -1 is returned, on success the position of the | ||
1677 | * cursor. */ | ||
1678 | inline int getCursorPosition(int ifd, int ofd) { | ||
1679 | char buf[32]; | ||
1680 | int cols, rows; | ||
1681 | unsigned int i = 0; | ||
1682 | |||
1683 | /* Report cursor location */ | ||
1684 | if (write(ofd, "\x1b[6n", 4) != 4) return -1; | ||
1685 | |||
1686 | /* Read the response: ESC [ rows ; cols R */ | ||
1687 | while (i < sizeof(buf)-1) { | ||
1688 | if (read(ifd,buf+i,1) != 1) break; | ||
1689 | if (buf[i] == 'R') break; | ||
1690 | i++; | ||
1691 | } | ||
1692 | buf[i] = '\0'; | ||
1693 | |||
1694 | /* Parse it. */ | ||
1695 | if (buf[0] != ESC || buf[1] != '[') return -1; | ||
1696 | if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; | ||
1697 | return cols; | ||
1698 | } | ||
1699 | |||
1700 | /* Try to get the number of columns in the current terminal, or assume 80 | ||
1701 | * if it fails. */ | ||
1702 | inline int getColumns(int ifd, int ofd) { | ||
1703 | #ifdef _WIN32 | ||
1704 | CONSOLE_SCREEN_BUFFER_INFO b; | ||
1705 | |||
1706 | if (!GetConsoleScreenBufferInfo(hOut, &b)) return 80; | ||
1707 | return b.srWindow.Right - b.srWindow.Left; | ||
1708 | #else | ||
1709 | struct winsize ws; | ||
1710 | |||
1711 | if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { | ||
1712 | /* ioctl() failed. Try to query the terminal itself. */ | ||
1713 | int start, cols; | ||
1714 | |||
1715 | /* Get the initial position so we can restore it later. */ | ||
1716 | start = getCursorPosition(ifd,ofd); | ||
1717 | if (start == -1) goto failed; | ||
1718 | |||
1719 | /* Go to right margin and get position. */ | ||
1720 | if (write(ofd,"\x1b[999C",6) != 6) goto failed; | ||
1721 | cols = getCursorPosition(ifd,ofd); | ||
1722 | if (cols == -1) goto failed; | ||
1723 | |||
1724 | /* Restore position. */ | ||
1725 | if (cols > start) { | ||
1726 | char seq[32]; | ||
1727 | snprintf(seq,32,"\x1b[%dD",cols-start); | ||
1728 | if (write(ofd,seq,strlen(seq)) == -1) { | ||
1729 | /* Can't recover... */ | ||
1730 | } | ||
1731 | } | ||
1732 | return cols; | ||
1733 | } else { | ||
1734 | return ws.ws_col; | ||
1735 | } | ||
1736 | |||
1737 | failed: | ||
1738 | return 80; | ||
1739 | #endif | ||
1740 | } | ||
1741 | |||
1742 | /* Clear the screen. Used to handle ctrl+l */ | ||
1743 | inline void linenoiseClearScreen(void) { | ||
1744 | if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { | ||
1745 | /* nothing to do, just to avoid warning. */ | ||
1746 | } | ||
1747 | } | ||
1748 | |||
1749 | /* Beep, used for completion when there is nothing to complete or when all | ||
1750 | * the choices were already shown. */ | ||
1751 | inline void linenoiseBeep(void) { | ||
1752 | fprintf(stderr, "\x7"); | ||
1753 | fflush(stderr); | ||
1754 | } | ||
1755 | |||
1756 | /* ============================== Completion ================================ */ | ||
1757 | |||
1758 | /* This is an helper function for linenoiseEdit() and is called when the | ||
1759 | * user types the <tab> key in order to complete the string currently in the | ||
1760 | * input. | ||
1761 | * | ||
1762 | * The state of the editing is encapsulated into the pointed linenoiseState | ||
1763 | * structure as described in the structure definition. */ | ||
1764 | inline int completeLine(struct linenoiseState *ls, char *cbuf, int *c) { | ||
1765 | std::vector<std::string> lc; | ||
1766 | int nread = 0, nwritten; | ||
1767 | *c = 0; | ||
1768 | |||
1769 | completionCallback(ls->buf,lc); | ||
1770 | if (lc.empty()) { | ||
1771 | linenoiseBeep(); | ||
1772 | } else { | ||
1773 | int stop = 0, i = 0; | ||
1774 | |||
1775 | while(!stop) { | ||
1776 | /* Show completion or original buffer */ | ||
1777 | if (i < static_cast<int>(lc.size())) { | ||
1778 | struct linenoiseState saved = *ls; | ||
1779 | |||
1780 | ls->len = ls->pos = static_cast<int>(lc[i].size()); | ||
1781 | ls->buf = &lc[i][0]; | ||
1782 | refreshLine(ls); | ||
1783 | ls->len = saved.len; | ||
1784 | ls->pos = saved.pos; | ||
1785 | ls->buf = saved.buf; | ||
1786 | } else { | ||
1787 | refreshLine(ls); | ||
1788 | } | ||
1789 | |||
1790 | //nread = read(ls->ifd,&c,1); | ||
1791 | #ifdef _WIN32 | ||
1792 | nread = win32read(c); | ||
1793 | if (nread == 1) { | ||
1794 | cbuf[0] = *c; | ||
1795 | } | ||
1796 | #else | ||
1797 | nread = unicodeReadUTF8Char(ls->ifd,cbuf,c); | ||
1798 | #endif | ||
1799 | if (nread <= 0) { | ||
1800 | *c = -1; | ||
1801 | return nread; | ||
1802 | } | ||
1803 | |||
1804 | switch(*c) { | ||
1805 | case 9: /* tab */ | ||
1806 | i = (i+1) % (lc.size()+1); | ||
1807 | if (i == lc.size()) linenoiseBeep(); | ||
1808 | break; | ||
1809 | case 27: /* escape */ | ||
1810 | /* Re-show original buffer */ | ||
1811 | if (i < static_cast<int>(lc.size())) refreshLine(ls); | ||
1812 | stop = 1; | ||
1813 | break; | ||
1814 | default: | ||
1815 | /* Update buffer and return */ | ||
1816 | if (i < static_cast<int>(lc.size())) { | ||
1817 | nwritten = snprintf(ls->buf,ls->buflen,"%s",&lc[i][0]); | ||
1818 | ls->len = ls->pos = nwritten; | ||
1819 | } | ||
1820 | stop = 1; | ||
1821 | break; | ||
1822 | } | ||
1823 | } | ||
1824 | } | ||
1825 | |||
1826 | return nread; | ||
1827 | } | ||
1828 | |||
1829 | /* Register a callback function to be called for tab-completion. */ | ||
1830 | static void SetCompletionCallback(CompletionCallback fn) { | ||
1831 | completionCallback = fn; | ||
1832 | } | ||
1833 | |||
1834 | /* =========================== Line editing ================================= */ | ||
1835 | |||
1836 | /* Single line low level line refresh. | ||
1837 | * | ||
1838 | * Rewrite the currently edited line accordingly to the buffer content, | ||
1839 | * cursor position, and number of columns of the terminal. */ | ||
1840 | inline void refreshSingleLine(struct linenoiseState *l) { | ||
1841 | char seq[64]; | ||
1842 | int pcolwid = unicodeColumnPos(l->prompt.c_str(), static_cast<int>(l->prompt.length())); | ||
1843 | int fd = l->ofd; | ||
1844 | char *buf = l->buf; | ||
1845 | int len = l->len; | ||
1846 | int pos = l->pos; | ||
1847 | std::string ab; | ||
1848 | |||
1849 | while((pcolwid+unicodeColumnPos(buf, pos)) >= l->cols) { | ||
1850 | int glen = unicodeGraphemeLen(buf, len, 0); | ||
1851 | buf += glen; | ||
1852 | len -= glen; | ||
1853 | pos -= glen; | ||
1854 | } | ||
1855 | while (pcolwid+unicodeColumnPos(buf, len) > l->cols) { | ||
1856 | len -= unicodePrevGraphemeLen(buf, len); | ||
1857 | } | ||
1858 | |||
1859 | /* Cursor to left edge */ | ||
1860 | snprintf(seq,64,"\r"); | ||
1861 | ab += seq; | ||
1862 | /* Write the prompt and the current buffer content */ | ||
1863 | ab += l->prompt; | ||
1864 | ab.append(buf, len); | ||
1865 | /* Erase to right */ | ||
1866 | snprintf(seq,64,"\x1b[0K"); | ||
1867 | ab += seq; | ||
1868 | /* Move cursor to original position. */ | ||
1869 | snprintf(seq,64,"\r\x1b[%dC", (int)(unicodeColumnPos(buf, pos)+pcolwid)); | ||
1870 | ab += seq; | ||
1871 | if (write(fd,ab.c_str(), static_cast<int>(ab.length())) == -1) {} /* Can't recover from write error. */ | ||
1872 | } | ||
1873 | |||
1874 | /* Multi line low level line refresh. | ||
1875 | * | ||
1876 | * Rewrite the currently edited line accordingly to the buffer content, | ||
1877 | * cursor position, and number of columns of the terminal. */ | ||
1878 | inline void refreshMultiLine(struct linenoiseState *l) { | ||
1879 | char seq[64]; | ||
1880 | int pcolwid = unicodeColumnPos(l->prompt.c_str(), static_cast<int>(l->prompt.length())); | ||
1881 | int colpos = unicodeColumnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcolwid); | ||
1882 | int colpos2; /* cursor column position. */ | ||
1883 | int rows = (pcolwid+colpos+l->cols-1)/l->cols; /* rows used by current buf. */ | ||
1884 | int rpos = (pcolwid+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */ | ||
1885 | int rpos2; /* rpos after refresh. */ | ||
1886 | int col; /* colum position, zero-based. */ | ||
1887 | int old_rows = (int)l->maxrows; | ||
1888 | int fd = l->ofd, j; | ||
1889 | std::string ab; | ||
1890 | |||
1891 | /* Update maxrows if needed. */ | ||
1892 | if (rows > (int)l->maxrows) l->maxrows = rows; | ||
1893 | |||
1894 | /* First step: clear all the lines used before. To do so start by | ||
1895 | * going to the last row. */ | ||
1896 | if (old_rows-rpos > 0) { | ||
1897 | snprintf(seq,64,"\x1b[%dB", old_rows-rpos); | ||
1898 | ab += seq; | ||
1899 | } | ||
1900 | |||
1901 | /* Now for every row clear it, go up. */ | ||
1902 | for (j = 0; j < old_rows-1; j++) { | ||
1903 | snprintf(seq,64,"\r\x1b[0K\x1b[1A"); | ||
1904 | ab += seq; | ||
1905 | } | ||
1906 | |||
1907 | /* Clean the top line. */ | ||
1908 | snprintf(seq,64,"\r\x1b[0K"); | ||
1909 | ab += seq; | ||
1910 | |||
1911 | /* Write the prompt and the current buffer content */ | ||
1912 | ab += l->prompt; | ||
1913 | ab.append(l->buf, l->len); | ||
1914 | |||
1915 | /* Get text width to cursor position */ | ||
1916 | colpos2 = unicodeColumnPosForMultiLine(l->buf, l->len, l->pos, l->cols, pcolwid); | ||
1917 | |||
1918 | /* If we are at the very end of the screen with our prompt, we need to | ||
1919 | * emit a newline and move the prompt to the first column. */ | ||
1920 | if (l->pos && | ||
1921 | l->pos == l->len && | ||
1922 | (colpos2+pcolwid) % l->cols == 0) | ||
1923 | { | ||
1924 | ab += "\n"; | ||
1925 | snprintf(seq,64,"\r"); | ||
1926 | ab += seq; | ||
1927 | rows++; | ||
1928 | if (rows > (int)l->maxrows) l->maxrows = rows; | ||
1929 | } | ||
1930 | |||
1931 | /* Move cursor to right position. */ | ||
1932 | rpos2 = (pcolwid+colpos2+l->cols)/l->cols; /* current cursor relative row. */ | ||
1933 | |||
1934 | /* Go up till we reach the expected positon. */ | ||
1935 | if (rows-rpos2 > 0) { | ||
1936 | snprintf(seq,64,"\x1b[%dA", rows-rpos2); | ||
1937 | ab += seq; | ||
1938 | } | ||
1939 | |||
1940 | /* Set column. */ | ||
1941 | col = (pcolwid + colpos2) % l->cols; | ||
1942 | if (col) | ||
1943 | snprintf(seq,64,"\r\x1b[%dC", col); | ||
1944 | else | ||
1945 | snprintf(seq,64,"\r"); | ||
1946 | ab += seq; | ||
1947 | |||
1948 | l->oldcolpos = colpos2; | ||
1949 | |||
1950 | if (write(fd,ab.c_str(), static_cast<int>(ab.length())) == -1) {} /* Can't recover from write error. */ | ||
1951 | } | ||
1952 | |||
1953 | /* Calls the two low level functions refreshSingleLine() or | ||
1954 | * refreshMultiLine() according to the selected mode. */ | ||
1955 | inline void refreshLine(struct linenoiseState *l) { | ||
1956 | if (mlmode) | ||
1957 | refreshMultiLine(l); | ||
1958 | else | ||
1959 | refreshSingleLine(l); | ||
1960 | } | ||
1961 | |||
1962 | /* Insert the character 'c' at cursor current position. | ||
1963 | * | ||
1964 | * On error writing to the terminal -1 is returned, otherwise 0. */ | ||
1965 | inline int linenoiseEditInsert(struct linenoiseState *l, const char* cbuf, int clen) { | ||
1966 | if (l->len < l->buflen) { | ||
1967 | if (l->len == l->pos) { | ||
1968 | memcpy(&l->buf[l->pos],cbuf,clen); | ||
1969 | l->pos+=clen; | ||
1970 | l->len+=clen;; | ||
1971 | l->buf[l->len] = '\0'; | ||
1972 | if ((!mlmode && unicodeColumnPos(l->prompt.c_str(), static_cast<int>(l->prompt.length()))+unicodeColumnPos(l->buf,l->len) < l->cols) /* || mlmode */) { | ||
1973 | /* Avoid a full update of the line in the | ||
1974 | * trivial case. */ | ||
1975 | if (write(l->ofd,cbuf,clen) == -1) return -1; | ||
1976 | } else { | ||
1977 | refreshLine(l); | ||
1978 | } | ||
1979 | } else { | ||
1980 | memmove(l->buf+l->pos+clen,l->buf+l->pos,l->len-l->pos); | ||
1981 | memcpy(&l->buf[l->pos],cbuf,clen); | ||
1982 | l->pos+=clen; | ||
1983 | l->len+=clen; | ||
1984 | l->buf[l->len] = '\0'; | ||
1985 | refreshLine(l); | ||
1986 | } | ||
1987 | } | ||
1988 | return 0; | ||
1989 | } | ||
1990 | |||
1991 | /* Move cursor on the left. */ | ||
1992 | inline void linenoiseEditMoveLeft(struct linenoiseState *l) { | ||
1993 | if (l->pos > 0) { | ||
1994 | l->pos -= unicodePrevGraphemeLen(l->buf, l->pos); | ||
1995 | refreshLine(l); | ||
1996 | } | ||
1997 | } | ||
1998 | |||
1999 | /* Move cursor on the right. */ | ||
2000 | inline void linenoiseEditMoveRight(struct linenoiseState *l) { | ||
2001 | if (l->pos != l->len) { | ||
2002 | l->pos += unicodeGraphemeLen(l->buf, l->len, l->pos); | ||
2003 | refreshLine(l); | ||
2004 | } | ||
2005 | } | ||
2006 | |||
2007 | /* Move cursor to the start of the line. */ | ||
2008 | inline void linenoiseEditMoveHome(struct linenoiseState *l) { | ||
2009 | if (l->pos != 0) { | ||
2010 | l->pos = 0; | ||
2011 | refreshLine(l); | ||
2012 | } | ||
2013 | } | ||
2014 | |||
2015 | /* Move cursor to the end of the line. */ | ||
2016 | inline void linenoiseEditMoveEnd(struct linenoiseState *l) { | ||
2017 | if (l->pos != l->len) { | ||
2018 | l->pos = l->len; | ||
2019 | refreshLine(l); | ||
2020 | } | ||
2021 | } | ||
2022 | |||
2023 | /* Substitute the currently edited line with the next or previous history | ||
2024 | * entry as specified by 'dir'. */ | ||
2025 | #define LINENOISE_HISTORY_NEXT 0 | ||
2026 | #define LINENOISE_HISTORY_PREV 1 | ||
2027 | inline void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { | ||
2028 | if (history.size() > 1) { | ||
2029 | /* Update the current history entry before to | ||
2030 | * overwrite it with the next one. */ | ||
2031 | history[history.size() - 1 - l->history_index] = l->buf; | ||
2032 | /* Show the new entry */ | ||
2033 | l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; | ||
2034 | if (l->history_index < 0) { | ||
2035 | l->history_index = 0; | ||
2036 | return; | ||
2037 | } else if (l->history_index >= (int)history.size()) { | ||
2038 | l->history_index = static_cast<int>(history.size())-1; | ||
2039 | return; | ||
2040 | } | ||
2041 | memset(l->buf, 0, l->buflen); | ||
2042 | strcpy(l->buf,history[history.size() - 1 - l->history_index].c_str()); | ||
2043 | l->len = l->pos = static_cast<int>(strlen(l->buf)); | ||
2044 | refreshLine(l); | ||
2045 | } | ||
2046 | } | ||
2047 | |||
2048 | /* Delete the character at the right of the cursor without altering the cursor | ||
2049 | * position. Basically this is what happens with the "Delete" keyboard key. */ | ||
2050 | inline void linenoiseEditDelete(struct linenoiseState *l) { | ||
2051 | if (l->len > 0 && l->pos < l->len) { | ||
2052 | int glen = unicodeGraphemeLen(l->buf,l->len,l->pos); | ||
2053 | memmove(l->buf+l->pos,l->buf+l->pos+glen,l->len-l->pos-glen); | ||
2054 | l->len-=glen; | ||
2055 | l->buf[l->len] = '\0'; | ||
2056 | refreshLine(l); | ||
2057 | } | ||
2058 | } | ||
2059 | |||
2060 | /* Backspace implementation. */ | ||
2061 | inline void linenoiseEditBackspace(struct linenoiseState *l) { | ||
2062 | if (l->pos > 0 && l->len > 0) { | ||
2063 | int glen = unicodePrevGraphemeLen(l->buf,l->pos); | ||
2064 | memmove(l->buf+l->pos-glen,l->buf+l->pos,l->len-l->pos); | ||
2065 | l->pos-=glen; | ||
2066 | l->len-=glen; | ||
2067 | l->buf[l->len] = '\0'; | ||
2068 | refreshLine(l); | ||
2069 | } | ||
2070 | } | ||
2071 | |||
2072 | /* Delete the previosu word, maintaining the cursor at the start of the | ||
2073 | * current word. */ | ||
2074 | inline void linenoiseEditDeletePrevWord(struct linenoiseState *l) { | ||
2075 | int old_pos = l->pos; | ||
2076 | int diff; | ||
2077 | |||
2078 | while (l->pos > 0 && l->buf[l->pos-1] == ' ') | ||
2079 | l->pos--; | ||
2080 | while (l->pos > 0 && l->buf[l->pos-1] != ' ') | ||
2081 | l->pos--; | ||
2082 | diff = old_pos - l->pos; | ||
2083 | memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); | ||
2084 | l->len -= diff; | ||
2085 | refreshLine(l); | ||
2086 | } | ||
2087 | |||
2088 | /* This function is the core of the line editing capability of linenoise. | ||
2089 | * It expects 'fd' to be already in "raw mode" so that every key pressed | ||
2090 | * will be returned ASAP to read(). | ||
2091 | * | ||
2092 | * The resulting string is put into 'buf' when the user type enter, or | ||
2093 | * when ctrl+d is typed. | ||
2094 | * | ||
2095 | * The function returns the length of the current buffer. */ | ||
2096 | inline int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, int buflen, const char *prompt) | ||
2097 | { | ||
2098 | struct linenoiseState l; | ||
2099 | |||
2100 | /* Populate the linenoise state that we pass to functions implementing | ||
2101 | * specific editing functionalities. */ | ||
2102 | l.ifd = stdin_fd; | ||
2103 | l.ofd = stdout_fd; | ||
2104 | l.buf = buf; | ||
2105 | l.buflen = buflen; | ||
2106 | l.prompt = prompt; | ||
2107 | l.oldcolpos = l.pos = 0; | ||
2108 | l.len = 0; | ||
2109 | l.cols = getColumns(stdin_fd, stdout_fd); | ||
2110 | l.maxrows = 0; | ||
2111 | l.history_index = 0; | ||
2112 | |||
2113 | /* Buffer starts empty. */ | ||
2114 | l.buf[0] = '\0'; | ||
2115 | l.buflen--; /* Make sure there is always space for the nulterm */ | ||
2116 | |||
2117 | /* The latest history entry is always our current buffer, that | ||
2118 | * initially is just an empty string. */ | ||
2119 | AddHistory(""); | ||
2120 | |||
2121 | if (write(l.ofd,prompt, static_cast<int>(l.prompt.length())) == -1) return -1; | ||
2122 | while(1) { | ||
2123 | int c; | ||
2124 | char cbuf[4]; | ||
2125 | int nread; | ||
2126 | char seq[3]; | ||
2127 | |||
2128 | #ifdef _WIN32 | ||
2129 | nread = win32read(&c); | ||
2130 | if (nread == 1) { | ||
2131 | cbuf[0] = c; | ||
2132 | } | ||
2133 | #else | ||
2134 | nread = unicodeReadUTF8Char(l.ifd,cbuf,&c); | ||
2135 | #endif | ||
2136 | if (nread <= 0) return (int)l.len; | ||
2137 | |||
2138 | /* Only autocomplete when the callback is set. It returns < 0 when | ||
2139 | * there was an error reading from fd. Otherwise it will return the | ||
2140 | * character that should be handled next. */ | ||
2141 | if (c == 9 && completionCallback != NULL) { | ||
2142 | nread = completeLine(&l,cbuf,&c); | ||
2143 | /* Return on errors */ | ||
2144 | if (c < 0) return l.len; | ||
2145 | /* Read next character when 0 */ | ||
2146 | if (c == 0) continue; | ||
2147 | } | ||
2148 | |||
2149 | switch(c) { | ||
2150 | case ENTER: /* enter */ | ||
2151 | if (!history.empty()) history.pop_back(); | ||
2152 | if (mlmode) linenoiseEditMoveEnd(&l); | ||
2153 | return (int)l.len; | ||
2154 | case CTRL_C: /* ctrl-c */ | ||
2155 | errno = EAGAIN; | ||
2156 | return -1; | ||
2157 | case BACKSPACE: /* backspace */ | ||
2158 | case 8: /* ctrl-h */ | ||
2159 | linenoiseEditBackspace(&l); | ||
2160 | break; | ||
2161 | case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the | ||
2162 | line is empty, act as end-of-file. */ | ||
2163 | if (l.len > 0) { | ||
2164 | linenoiseEditDelete(&l); | ||
2165 | } else { | ||
2166 | history.pop_back(); | ||
2167 | return -1; | ||
2168 | } | ||
2169 | break; | ||
2170 | case CTRL_T: /* ctrl-t, swaps current character with previous. */ | ||
2171 | if (l.pos > 0 && l.pos < l.len) { | ||
2172 | int aux = buf[l.pos-1]; | ||
2173 | buf[l.pos-1] = buf[l.pos]; | ||
2174 | buf[l.pos] = aux; | ||
2175 | if (l.pos != l.len-1) l.pos++; | ||
2176 | refreshLine(&l); | ||
2177 | } | ||
2178 | break; | ||
2179 | case CTRL_B: /* ctrl-b */ | ||
2180 | linenoiseEditMoveLeft(&l); | ||
2181 | break; | ||
2182 | case CTRL_F: /* ctrl-f */ | ||
2183 | linenoiseEditMoveRight(&l); | ||
2184 | break; | ||
2185 | case CTRL_P: /* ctrl-p */ | ||
2186 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); | ||
2187 | break; | ||
2188 | case CTRL_N: /* ctrl-n */ | ||
2189 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); | ||
2190 | break; | ||
2191 | case ESC: /* escape sequence */ | ||
2192 | /* Read the next two bytes representing the escape sequence. | ||
2193 | * Use two calls to handle slow terminals returning the two | ||
2194 | * chars at different times. */ | ||
2195 | if (read(l.ifd,seq,1) == -1) break; | ||
2196 | if (read(l.ifd,seq+1,1) == -1) break; | ||
2197 | |||
2198 | /* ESC [ sequences. */ | ||
2199 | if (seq[0] == '[') { | ||
2200 | if (seq[1] >= '0' && seq[1] <= '9') { | ||
2201 | /* Extended escape, read additional byte. */ | ||
2202 | if (read(l.ifd,seq+2,1) == -1) break; | ||
2203 | if (seq[2] == '~') { | ||
2204 | switch(seq[1]) { | ||
2205 | case '3': /* Delete key. */ | ||
2206 | linenoiseEditDelete(&l); | ||
2207 | break; | ||
2208 | } | ||
2209 | } | ||
2210 | } else { | ||
2211 | switch(seq[1]) { | ||
2212 | case 'A': /* Up */ | ||
2213 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); | ||
2214 | break; | ||
2215 | case 'B': /* Down */ | ||
2216 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); | ||
2217 | break; | ||
2218 | case 'C': /* Right */ | ||
2219 | linenoiseEditMoveRight(&l); | ||
2220 | break; | ||
2221 | case 'D': /* Left */ | ||
2222 | linenoiseEditMoveLeft(&l); | ||
2223 | break; | ||
2224 | case 'H': /* Home */ | ||
2225 | linenoiseEditMoveHome(&l); | ||
2226 | break; | ||
2227 | case 'F': /* End*/ | ||
2228 | linenoiseEditMoveEnd(&l); | ||
2229 | break; | ||
2230 | } | ||
2231 | } | ||
2232 | } | ||
2233 | |||
2234 | /* ESC O sequences. */ | ||
2235 | else if (seq[0] == 'O') { | ||
2236 | switch(seq[1]) { | ||
2237 | case 'H': /* Home */ | ||
2238 | linenoiseEditMoveHome(&l); | ||
2239 | break; | ||
2240 | case 'F': /* End*/ | ||
2241 | linenoiseEditMoveEnd(&l); | ||
2242 | break; | ||
2243 | } | ||
2244 | } | ||
2245 | break; | ||
2246 | default: | ||
2247 | if (linenoiseEditInsert(&l,cbuf,nread)) return -1; | ||
2248 | break; | ||
2249 | case CTRL_U: /* Ctrl+u, delete the whole line. */ | ||
2250 | buf[0] = '\0'; | ||
2251 | l.pos = l.len = 0; | ||
2252 | refreshLine(&l); | ||
2253 | break; | ||
2254 | case CTRL_K: /* Ctrl+k, delete from current to end of line. */ | ||
2255 | buf[l.pos] = '\0'; | ||
2256 | l.len = l.pos; | ||
2257 | refreshLine(&l); | ||
2258 | break; | ||
2259 | case CTRL_A: /* Ctrl+a, go to the start of the line */ | ||
2260 | linenoiseEditMoveHome(&l); | ||
2261 | break; | ||
2262 | case CTRL_E: /* ctrl+e, go to the end of the line */ | ||
2263 | linenoiseEditMoveEnd(&l); | ||
2264 | break; | ||
2265 | case CTRL_L: /* ctrl+l, clear screen */ | ||
2266 | linenoiseClearScreen(); | ||
2267 | refreshLine(&l); | ||
2268 | break; | ||
2269 | case CTRL_W: /* ctrl+w, delete previous word */ | ||
2270 | linenoiseEditDeletePrevWord(&l); | ||
2271 | break; | ||
2272 | } | ||
2273 | } | ||
2274 | return l.len; | ||
2275 | } | ||
2276 | |||
2277 | /* This function calls the line editing function linenoiseEdit() using | ||
2278 | * the STDIN file descriptor set in raw mode. */ | ||
2279 | inline bool linenoiseRaw(const char *prompt, std::string& line) { | ||
2280 | bool quit = false; | ||
2281 | |||
2282 | if (!isatty(STDIN_FILENO)) { | ||
2283 | /* Not a tty: read from file / pipe. */ | ||
2284 | std::getline(std::cin, line); | ||
2285 | } else { | ||
2286 | /* Interactive editing. */ | ||
2287 | if (enableRawMode(STDIN_FILENO) == false) { | ||
2288 | return quit; | ||
2289 | } | ||
2290 | |||
2291 | char buf[LINENOISE_MAX_LINE]; | ||
2292 | auto count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, LINENOISE_MAX_LINE, prompt); | ||
2293 | if (count == -1) { | ||
2294 | quit = true; | ||
2295 | } else { | ||
2296 | line.assign(buf, count); | ||
2297 | } | ||
2298 | |||
2299 | disableRawMode(STDIN_FILENO); | ||
2300 | printf("\n"); | ||
2301 | } | ||
2302 | return quit; | ||
2303 | } | ||
2304 | |||
2305 | /* The high level function that is the main API of the linenoise library. | ||
2306 | * This function checks if the terminal has basic capabilities, just checking | ||
2307 | * for a blacklist of stupid terminals, and later either calls the line | ||
2308 | * editing function or uses dummy fgets() so that you will be able to type | ||
2309 | * something even in the most desperate of the conditions. */ | ||
2310 | inline bool Readline(const char *prompt, std::string& line) { | ||
2311 | if (isUnsupportedTerm()) { | ||
2312 | printf("%s",prompt); | ||
2313 | fflush(stdout); | ||
2314 | std::getline(std::cin, line); | ||
2315 | return false; | ||
2316 | } else { | ||
2317 | return linenoiseRaw(prompt, line); | ||
2318 | } | ||
2319 | } | ||
2320 | |||
2321 | inline std::string Readline(const char *prompt, bool& quit) { | ||
2322 | std::string line; | ||
2323 | quit = Readline(prompt, line); | ||
2324 | return line; | ||
2325 | } | ||
2326 | |||
2327 | inline std::string Readline(const char *prompt) { | ||
2328 | bool quit; // dummy | ||
2329 | return Readline(prompt, quit); | ||
2330 | } | ||
2331 | |||
2332 | /* ================================ History ================================= */ | ||
2333 | |||
2334 | /* At exit we'll try to fix the terminal to the initial conditions. */ | ||
2335 | inline void linenoiseAtExit(void) { | ||
2336 | disableRawMode(STDIN_FILENO); | ||
2337 | } | ||
2338 | |||
2339 | /* This is the API call to add a new entry in the linenoise history. | ||
2340 | * It uses a fixed array of char pointers that are shifted (memmoved) | ||
2341 | * when the history max length is reached in order to remove the older | ||
2342 | * entry and make room for the new one, so it is not exactly suitable for huge | ||
2343 | * histories, but will work well for a few hundred of entries. | ||
2344 | * | ||
2345 | * Using a circular buffer is smarter, but a bit more complex to handle. */ | ||
2346 | inline bool AddHistory(const char* line) { | ||
2347 | if (history_max_len == 0) return false; | ||
2348 | |||
2349 | /* Don't add duplicated lines. */ | ||
2350 | if (!history.empty() && history.back() == line) return false; | ||
2351 | |||
2352 | /* If we reached the max length, remove the older line. */ | ||
2353 | if (history.size() == history_max_len) { | ||
2354 | history.erase(history.begin()); | ||
2355 | } | ||
2356 | history.push_back(line); | ||
2357 | |||
2358 | return true; | ||
2359 | } | ||
2360 | |||
2361 | /* Set the maximum length for the history. This function can be called even | ||
2362 | * if there is already some history, the function will make sure to retain | ||
2363 | * just the latest 'len' elements if the new history length value is smaller | ||
2364 | * than the amount of items already inside the history. */ | ||
2365 | inline bool SetHistoryMaxLen(size_t len) { | ||
2366 | if (len < 1) return false; | ||
2367 | history_max_len = len; | ||
2368 | if (len < history.size()) { | ||
2369 | history.resize(len); | ||
2370 | } | ||
2371 | return true; | ||
2372 | } | ||
2373 | |||
2374 | /* Save the history in the specified file. On success *true* is returned | ||
2375 | * otherwise *false* is returned. */ | ||
2376 | inline bool SaveHistory(const char* path) { | ||
2377 | std::ofstream f(path); // TODO: need 'std::ios::binary'? | ||
2378 | if (!f) return false; | ||
2379 | for (const auto& h: history) { | ||
2380 | f << h << std::endl; | ||
2381 | } | ||
2382 | return true; | ||
2383 | } | ||
2384 | |||
2385 | /* Load the history from the specified file. If the file does not exist | ||
2386 | * zero is returned and no operation is performed. | ||
2387 | * | ||
2388 | * If the file exists and the operation succeeded *true* is returned, otherwise | ||
2389 | * on error *false* is returned. */ | ||
2390 | inline bool LoadHistory(const char* path) { | ||
2391 | std::ifstream f(path); | ||
2392 | if (!f) return false; | ||
2393 | std::string line; | ||
2394 | while (std::getline(f, line)) { | ||
2395 | AddHistory(line.c_str()); | ||
2396 | } | ||
2397 | return true; | ||
2398 | } | ||
2399 | |||
2400 | inline const std::vector<std::string>& GetHistory() { | ||
2401 | return history; | ||
2402 | } | ||
2403 | |||
2404 | } // namespace linenoise | ||
2405 | |||
2406 | #ifdef _WIN32 | ||
2407 | #undef isatty | ||
2408 | #undef write | ||
2409 | #undef read | ||
2410 | #endif | ||
2411 | |||
2412 | #endif /* __LINENOISE_HPP */ | ||
diff --git a/sinksh/repl/repl.cpp b/sinksh/repl/repl.cpp index 21209fc..32932cb 100644 --- a/sinksh/repl/repl.cpp +++ b/sinksh/repl/repl.cpp | |||
@@ -19,8 +19,6 @@ | |||
19 | 19 | ||
20 | #include "repl.h" | 20 | #include "repl.h" |
21 | 21 | ||
22 | #include <readline/history.h> | ||
23 | |||
24 | #include <QDir> | 22 | #include <QDir> |
25 | #include <QFile> | 23 | #include <QFile> |
26 | #include <QFinalState> | 24 | #include <QFinalState> |
@@ -29,13 +27,12 @@ | |||
29 | 27 | ||
30 | #include "replStates.h" | 28 | #include "replStates.h" |
31 | #include "syntaxtree.h" | 29 | #include "syntaxtree.h" |
30 | #include "linenoise.hpp" | ||
32 | 31 | ||
33 | Repl::Repl(QObject *parent) | 32 | Repl::Repl(QObject *parent) |
34 | : QStateMachine(parent) | 33 | : QStateMachine(parent) |
35 | { | 34 | { |
36 | // readline history setup | 35 | linenoise::LoadHistory(commandHistoryPath().toLocal8Bit()); |
37 | using_history(); | ||
38 | read_history(commandHistoryPath().toLocal8Bit()); | ||
39 | 36 | ||
40 | // create all states | 37 | // create all states |
41 | ReadState *read = new ReadState(this); | 38 | ReadState *read = new ReadState(this); |
@@ -64,8 +61,7 @@ Repl::Repl(QObject *parent) | |||
64 | 61 | ||
65 | Repl::~Repl() | 62 | Repl::~Repl() |
66 | { | 63 | { |
67 | // readline history writing | 64 | linenoise::SaveHistory(commandHistoryPath().toLocal8Bit()); |
68 | write_history(commandHistoryPath().toLocal8Bit()); | ||
69 | } | 65 | } |
70 | 66 | ||
71 | void Repl::printWelcomeBanner() | 67 | void Repl::printWelcomeBanner() |
diff --git a/sinksh/repl/replStates.cpp b/sinksh/repl/replStates.cpp index 18081df..c4b08b7 100644 --- a/sinksh/repl/replStates.cpp +++ b/sinksh/repl/replStates.cpp | |||
@@ -21,46 +21,54 @@ | |||
21 | 21 | ||
22 | #include <iostream> | 22 | #include <iostream> |
23 | 23 | ||
24 | #include <readline/readline.h> | ||
25 | #include <readline/history.h> | ||
26 | |||
27 | #include <QDebug> | 24 | #include <QDebug> |
28 | #include <QEvent> | 25 | #include <QEvent> |
29 | #include <QStateMachine> | 26 | #include <QStateMachine> |
27 | #include "linenoise.hpp" | ||
30 | 28 | ||
31 | #include "syntaxtree.h" | 29 | #include "syntaxtree.h" |
32 | 30 | ||
33 | static char *sink_cli_next_tab_complete_match(const char *text, int state); | ||
34 | static char ** sink_cli_tab_completion(const char *text, int start, int end); | ||
35 | |||
36 | ReadState::ReadState(QState *parent) | 31 | ReadState::ReadState(QState *parent) |
37 | : QState(parent) | 32 | : QState(parent) |
38 | { | 33 | { |
39 | rl_completion_entry_function = sink_cli_next_tab_complete_match; | 34 | linenoise::SetCompletionCallback([](const char* editBuffer, std::vector<std::string>& completions) { |
40 | rl_attempted_completion_function = sink_cli_tab_completion; | 35 | QStringList words = QString(editBuffer).split(" ", QString::SkipEmptyParts); |
36 | const QString fragment = words.takeLast(); | ||
37 | Syntax::List nearest = SyntaxTree::self()->nearestSyntax(words, fragment); | ||
38 | if (nearest.isEmpty()) { | ||
39 | SyntaxTree::Command command = SyntaxTree::self()->match(words); | ||
40 | if (command.first && command.first->completer) { | ||
41 | QStringList commandCompletions = command.first->completer(words, fragment, SyntaxTree::self()->state()); | ||
42 | for (const auto &c : commandCompletions) { | ||
43 | completions.push_back(c.toStdString()); | ||
44 | } | ||
45 | } | ||
46 | } else { | ||
47 | for (const auto &n : nearest) { | ||
48 | completions.push_back(n.keyword.toStdString()); | ||
49 | } | ||
50 | } | ||
51 | }); | ||
41 | } | 52 | } |
42 | 53 | ||
43 | void ReadState::onEntry(QEvent *event) | 54 | void ReadState::onEntry(QEvent *event) |
44 | { | 55 | { |
45 | Q_UNUSED(event) | 56 | Q_UNUSED(event) |
46 | char *line = readline(prompt()); | ||
47 | 57 | ||
48 | if (!line) { | 58 | std::string line; |
59 | if (linenoise::Readline(prompt(), line)) { | ||
49 | std::cout << std::endl; | 60 | std::cout << std::endl; |
50 | emit exitRequested(); | 61 | emit exitRequested(); |
51 | return; | 62 | return; |
52 | } | 63 | } |
53 | 64 | ||
54 | // we have actual data, so let's wait for a full line of text | 65 | // we have actual data, so let's wait for a full line of text |
55 | QByteArray input(line); | 66 | const QString text = QString::fromStdString(line).simplified(); |
56 | const QString text = QString(line).simplified(); | ||
57 | //qDebug() << "Line is ... " << text; | ||
58 | 67 | ||
59 | if (text.length() > 0) { | 68 | if (text.length() > 0) { |
60 | add_history(line); | 69 | linenoise::AddHistory(line.c_str()); |
61 | } | 70 | } |
62 | 71 | ||
63 | free(line); | ||
64 | emit command(text); | 72 | emit command(text); |
65 | } | 73 | } |
66 | 74 | ||
@@ -136,36 +144,6 @@ void PrintState::onEntry(QEvent *event) | |||
136 | emit completed(); | 144 | emit completed(); |
137 | } | 145 | } |
138 | 146 | ||
139 | static QStringList tab_completion_full_state; | ||
140 | static bool tab_completion_at_root = false; | ||
141 | |||
142 | static char **sink_cli_tab_completion(const char *text, int start, int end) | ||
143 | { | ||
144 | tab_completion_at_root = start == 0; | ||
145 | tab_completion_full_state = QString(rl_line_buffer).remove(start, end - start).split(" ", QString::SkipEmptyParts); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | static char *sink_cli_next_tab_complete_match(const char *text, int state) | ||
150 | { | ||
151 | const QString fragment(text); | ||
152 | Syntax::List nearest = SyntaxTree::self()->nearestSyntax(tab_completion_full_state, fragment); | ||
153 | //for (auto syntax: nearest) { qDebug() << "Nearest: " << syntax.keyword; } | ||
154 | |||
155 | if (nearest.isEmpty()) { | ||
156 | SyntaxTree::Command command = SyntaxTree::self()->match(tab_completion_full_state); | ||
157 | if (command.first && command.first->completer) { | ||
158 | QStringList commandCompletions = command.first->completer(tab_completion_full_state, fragment, SyntaxTree::self()->state()); | ||
159 | if (commandCompletions.size() > state) { | ||
160 | return qstrdup(commandCompletions[state].toUtf8()); | ||
161 | } | ||
162 | } | ||
163 | } else if (nearest.size() > state) { | ||
164 | return qstrdup(nearest[state].keyword.toUtf8()); | ||
165 | } | ||
166 | |||
167 | return rl_filename_completion_function(text, state); | ||
168 | } | ||
169 | 147 | ||
170 | //Ignore warning I don't know how to fix in a moc file | 148 | //Ignore warning I don't know how to fix in a moc file |
171 | #pragma clang diagnostic push | 149 | #pragma clang diagnostic push |