| 1 | #ifndef _WIN32 |
| 2 | /* |
| 3 | * You can find the latest source code at: |
| 4 | * |
| 5 | * http://github.com/ericcurtin/linenoise.cpp |
| 6 | * |
| 7 | * Does a number of crazy assumptions that happen to be true in 99.9999% of |
| 8 | * the 2010 UNIX computers around. |
| 9 | * |
| 10 | * ------------------------------------------------------------------------ |
| 11 | * |
| 12 | * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com> |
| 13 | * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
| 14 | * Copyright (c) 2025, Eric Curtin <ericcurtin17 at gmail dot com> |
| 15 | * |
| 16 | * All rights reserved. |
| 17 | * |
| 18 | * Redistribution and use in source and binary forms, with or without |
| 19 | * modification, are permitted provided that the following conditions are |
| 20 | * met: |
| 21 | * |
| 22 | * * Redistributions of source code must retain the above copyright |
| 23 | * notice, this list of conditions and the following disclaimer. |
| 24 | * |
| 25 | * * Redistributions in binary form must reproduce the above copyright |
| 26 | * notice, this list of conditions and the following disclaimer in the |
| 27 | * documentation and/or other materials provided with the distribution. |
| 28 | * |
| 29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 30 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 31 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 32 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 33 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 34 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 35 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 36 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 37 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 38 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 39 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 40 | * |
| 41 | * ------------------------------------------------------------------------ |
| 42 | * |
| 43 | * References: |
| 44 | * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html |
| 45 | * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html |
| 46 | * |
| 47 | * Todo list: |
| 48 | * - Filter bogus Ctrl+<char> combinations. |
| 49 | * - Win32 support |
| 50 | * |
| 51 | * Bloat: |
| 52 | * - History search like Ctrl+r in readline? |
| 53 | * |
| 54 | * List of escape sequences used by this program, we do everything just |
| 55 | * with three sequences. In order to be so cheap we may have some |
| 56 | * flickering effect with some slow terminal, but the lesser sequences |
| 57 | * the more compatible. |
| 58 | * |
| 59 | * EL (Erase Line) |
| 60 | * Sequence: ESC [ n K |
| 61 | * Effect: if n is 0 or missing, clear from cursor to end of line |
| 62 | * Effect: if n is 1, clear from beginning of line to cursor |
| 63 | * Effect: if n is 2, clear entire line |
| 64 | * |
| 65 | * CUF (CUrsor Forward) |
| 66 | * Sequence: ESC [ n C |
| 67 | * Effect: moves cursor forward n chars |
| 68 | * |
| 69 | * CUB (CUrsor Backward) |
| 70 | * Sequence: ESC [ n D |
| 71 | * Effect: moves cursor backward n chars |
| 72 | * |
| 73 | * The following is used to get the terminal width if getting |
| 74 | * the width with the TIOCGWINSZ ioctl fails |
| 75 | * |
| 76 | * DSR (Device Status Report) |
| 77 | * Sequence: ESC [ 6 n |
| 78 | * Effect: reports the current cursor position as ESC [ n ; m R |
| 79 | * where n is the row and m is the column |
| 80 | * |
| 81 | * When multi line mode is enabled, we also use an additional escape |
| 82 | * sequence. However multi line editing is disabled by default. |
| 83 | * |
| 84 | * CUU (Cursor Up) |
| 85 | * Sequence: ESC [ n A |
| 86 | * Effect: moves cursor up of n chars. |
| 87 | * |
| 88 | * CUD (Cursor Down) |
| 89 | * Sequence: ESC [ n B |
| 90 | * Effect: moves cursor down of n chars. |
| 91 | * |
| 92 | * When linenoiseClearScreen() is called, two additional escape sequences |
| 93 | * are used in order to clear the screen and position the cursor at home |
| 94 | * position. |
| 95 | * |
| 96 | * CUP (Cursor position) |
| 97 | * Sequence: ESC [ H |
| 98 | * Effect: moves the cursor to upper left corner |
| 99 | * |
| 100 | * ED (Erase display) |
| 101 | * Sequence: ESC [ 2 J |
| 102 | * Effect: clear the whole screen |
| 103 | * |
| 104 | */ |
| 105 | |
| 106 | # include "linenoise.h" |
| 107 | |
| 108 | # include <ctype.h> |
| 109 | # include <errno.h> |
| 110 | # include <poll.h> |
| 111 | # include <stdio.h> |
| 112 | # include <string.h> |
| 113 | # include <sys/file.h> |
| 114 | # include <sys/ioctl.h> |
| 115 | # include <sys/stat.h> |
| 116 | # include <sys/types.h> |
| 117 | # include <termios.h> |
| 118 | # include <unistd.h> |
| 119 | |
| 120 | # include <memory> |
| 121 | # include <string> |
| 122 | # include <vector> |
| 123 | |
| 124 | # define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 |
| 125 | # define LINENOISE_MAX_LINE 4096 |
| 126 | static std::vector<const char *> unsupported_term = { "dumb" , "cons25" , "emacs" }; |
| 127 | static linenoiseCompletionCallback *completionCallback = NULL; |
| 128 | static linenoiseHintsCallback *hintsCallback = NULL; |
| 129 | static linenoiseFreeHintsCallback *freeHintsCallback = NULL; |
| 130 | static char *linenoiseNoTTY(void); |
| 131 | static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags); |
| 132 | static void refreshLineWithFlags(struct linenoiseState *l, int flags); |
| 133 | |
| 134 | static struct termios orig_termios; /* In order to restore at exit.*/ |
| 135 | static int maskmode = 0; /* Show "***" instead of input. For passwords. */ |
| 136 | static int rawmode = 0; /* For atexit() function to check if restore is needed*/ |
| 137 | static int mlmode = 0; /* Multi line mode. Default is single line. */ |
| 138 | static int atexit_registered = 0; /* Register atexit just 1 time. */ |
| 139 | static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; |
| 140 | static int history_len = 0; |
| 141 | static char **history = NULL; |
| 142 | |
| 143 | enum KEY_ACTION{ |
| 144 | KEY_NULL = 0, /* NULL */ |
| 145 | CTRL_A = 1, /* Ctrl+a */ |
| 146 | CTRL_B = 2, /* Ctrl-b */ |
| 147 | CTRL_C = 3, /* Ctrl-c */ |
| 148 | CTRL_D = 4, /* Ctrl-d */ |
| 149 | CTRL_E = 5, /* Ctrl-e */ |
| 150 | CTRL_F = 6, /* Ctrl-f */ |
| 151 | CTRL_H = 8, /* Ctrl-h */ |
| 152 | TAB = 9, /* Tab */ |
| 153 | CTRL_K = 11, /* Ctrl+k */ |
| 154 | CTRL_L = 12, /* Ctrl+l */ |
| 155 | ENTER = 13, /* Enter */ |
| 156 | CTRL_N = 14, /* Ctrl-n */ |
| 157 | CTRL_P = 16, /* Ctrl-p */ |
| 158 | CTRL_T = 20, /* Ctrl-t */ |
| 159 | CTRL_U = 21, /* Ctrl+u */ |
| 160 | CTRL_W = 23, /* Ctrl+w */ |
| 161 | ESC = 27, /* Escape */ |
| 162 | BACKSPACE = 127 /* Backspace */ |
| 163 | }; |
| 164 | |
| 165 | static void linenoiseAtExit(void); |
| 166 | int linenoiseHistoryAdd(const char *line); |
| 167 | #define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen |
| 168 | #define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen. |
| 169 | #define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both. |
| 170 | static void refreshLine(struct linenoiseState *l); |
| 171 | |
| 172 | class File { |
| 173 | public: |
| 174 | FILE * file = nullptr; |
| 175 | |
| 176 | FILE * open(const std::string & filename, const char * mode) { |
| 177 | file = fopen(filename: filename.c_str(), modes: mode); |
| 178 | |
| 179 | return file; |
| 180 | } |
| 181 | |
| 182 | int lock() { |
| 183 | if (file) { |
| 184 | fd = fileno(stream: file); |
| 185 | if (flock(fd: fd, LOCK_EX | LOCK_NB) != 0) { |
| 186 | fd = -1; |
| 187 | |
| 188 | return 1; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | return 0; |
| 193 | } |
| 194 | |
| 195 | ~File() { |
| 196 | if (fd >= 0) { |
| 197 | flock(fd: fd, LOCK_UN); |
| 198 | } |
| 199 | |
| 200 | if (file) { |
| 201 | fclose(stream: file); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | private: |
| 206 | int fd = -1; |
| 207 | }; |
| 208 | |
| 209 | #if 0 |
| 210 | /* Debugging function. */ |
| 211 | __attribute__((format(printf, 1, 2))) |
| 212 | static void lndebug(const char *fmt, ...) { |
| 213 | static File file; |
| 214 | if (file.file == nullptr) { |
| 215 | file.open("/tmp/lndebug.txt" , "a" ); |
| 216 | } |
| 217 | |
| 218 | if (file.file != nullptr) { |
| 219 | va_list args; |
| 220 | va_start(args, fmt); |
| 221 | vfprintf(file.file, fmt, args); |
| 222 | va_end(args); |
| 223 | fflush(file.file); |
| 224 | } |
| 225 | } |
| 226 | #endif |
| 227 | |
| 228 | /* ========================== Encoding functions ============================= */ |
| 229 | |
| 230 | /* Get length of previous UTF8 codepoint */ |
| 231 | static size_t prevUtf8CodePointLen(const char * buf, int pos) { |
| 232 | int end = pos--; |
| 233 | while (pos >= 0 && ((unsigned char) buf[pos] & 0xC0) == 0x80) { |
| 234 | pos--; |
| 235 | } |
| 236 | return end - pos; |
| 237 | } |
| 238 | |
| 239 | /* Convert UTF8 to Unicode code point */ |
| 240 | static size_t utf8BytesToCodePoint(const char * buf, size_t len, int * cp) { |
| 241 | if (len) { |
| 242 | unsigned char byte = buf[0]; |
| 243 | if ((byte & 0x80) == 0) { |
| 244 | *cp = byte; |
| 245 | return 1; |
| 246 | } else if ((byte & 0xE0) == 0xC0) { |
| 247 | if (len >= 2) { |
| 248 | *cp = (((unsigned long) (buf[0] & 0x1F)) << 6) | ((unsigned long) (buf[1] & 0x3F)); |
| 249 | return 2; |
| 250 | } |
| 251 | } else if ((byte & 0xF0) == 0xE0) { |
| 252 | if (len >= 3) { |
| 253 | *cp = (((unsigned long) (buf[0] & 0x0F)) << 12) | (((unsigned long) (buf[1] & 0x3F)) << 6) | |
| 254 | ((unsigned long) (buf[2] & 0x3F)); |
| 255 | return 3; |
| 256 | } |
| 257 | } else if ((byte & 0xF8) == 0xF0) { |
| 258 | if (len >= 4) { |
| 259 | *cp = (((unsigned long) (buf[0] & 0x07)) << 18) | (((unsigned long) (buf[1] & 0x3F)) << 12) | |
| 260 | (((unsigned long) (buf[2] & 0x3F)) << 6) | ((unsigned long) (buf[3] & 0x3F)); |
| 261 | return 4; |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | return 0; |
| 266 | } |
| 267 | |
| 268 | /* Check if the code is a wide character */ |
| 269 | static const unsigned long wideCharTable[][2] = { |
| 270 | /* BEGIN: WIDE CHAR TABLE */ |
| 271 | { 0x1100, 0x115F }, |
| 272 | { 0x231A, 0x231B }, |
| 273 | { 0x2329, 0x232A }, |
| 274 | { 0x23E9, 0x23EC }, |
| 275 | { 0x23F0, 0x23F0 }, |
| 276 | { 0x23F3, 0x23F3 }, |
| 277 | { 0x25FD, 0x25FE }, |
| 278 | { 0x2614, 0x2615 }, |
| 279 | { 0x2630, 0x2637 }, |
| 280 | { 0x2648, 0x2653 }, |
| 281 | { 0x267F, 0x267F }, |
| 282 | { 0x268A, 0x268F }, |
| 283 | { 0x2693, 0x2693 }, |
| 284 | { 0x26A1, 0x26A1 }, |
| 285 | { 0x26AA, 0x26AB }, |
| 286 | { 0x26BD, 0x26BE }, |
| 287 | { 0x26C4, 0x26C5 }, |
| 288 | { 0x26CE, 0x26CE }, |
| 289 | { 0x26D4, 0x26D4 }, |
| 290 | { 0x26EA, 0x26EA }, |
| 291 | { 0x26F2, 0x26F3 }, |
| 292 | { 0x26F5, 0x26F5 }, |
| 293 | { 0x26FA, 0x26FA }, |
| 294 | { 0x26FD, 0x26FD }, |
| 295 | { 0x2705, 0x2705 }, |
| 296 | { 0x270A, 0x270B }, |
| 297 | { 0x2728, 0x2728 }, |
| 298 | { 0x274C, 0x274C }, |
| 299 | { 0x274E, 0x274E }, |
| 300 | { 0x2753, 0x2755 }, |
| 301 | { 0x2757, 0x2757 }, |
| 302 | { 0x2795, 0x2797 }, |
| 303 | { 0x27B0, 0x27B0 }, |
| 304 | { 0x27BF, 0x27BF }, |
| 305 | { 0x2B1B, 0x2B1C }, |
| 306 | { 0x2B50, 0x2B50 }, |
| 307 | { 0x2B55, 0x2B55 }, |
| 308 | { 0x2E80, 0x2E99 }, |
| 309 | { 0x2E9B, 0x2EF3 }, |
| 310 | { 0x2F00, 0x2FD5 }, |
| 311 | { 0x2FF0, 0x303E }, |
| 312 | { 0x3041, 0x3096 }, |
| 313 | { 0x3099, 0x30FF }, |
| 314 | { 0x3105, 0x312F }, |
| 315 | { 0x3131, 0x318E }, |
| 316 | { 0x3190, 0x31E5 }, |
| 317 | { 0x31EF, 0x321E }, |
| 318 | { 0x3220, 0x3247 }, |
| 319 | { 0x3250, 0xA48C }, |
| 320 | { 0xA490, 0xA4C6 }, |
| 321 | { 0xA960, 0xA97C }, |
| 322 | { 0xAC00, 0xD7A3 }, |
| 323 | { 0xF900, 0xFAFF }, |
| 324 | { 0xFE10, 0xFE19 }, |
| 325 | { 0xFE30, 0xFE52 }, |
| 326 | { 0xFE54, 0xFE66 }, |
| 327 | { 0xFE68, 0xFE6B }, |
| 328 | { 0xFF01, 0xFF60 }, |
| 329 | { 0xFFE0, 0xFFE6 }, |
| 330 | { 0x16FE0, 0x16FE4 }, |
| 331 | { 0x16FF0, 0x16FF1 }, |
| 332 | { 0x17000, 0x187F7 }, |
| 333 | { 0x18800, 0x18CD5 }, |
| 334 | { 0x18CFF, 0x18D08 }, |
| 335 | { 0x1AFF0, 0x1AFF3 }, |
| 336 | { 0x1AFF5, 0x1AFFB }, |
| 337 | { 0x1AFFD, 0x1AFFE }, |
| 338 | { 0x1B000, 0x1B122 }, |
| 339 | { 0x1B132, 0x1B132 }, |
| 340 | { 0x1B150, 0x1B152 }, |
| 341 | { 0x1B155, 0x1B155 }, |
| 342 | { 0x1B164, 0x1B167 }, |
| 343 | { 0x1B170, 0x1B2FB }, |
| 344 | { 0x1D300, 0x1D356 }, |
| 345 | { 0x1D360, 0x1D376 }, |
| 346 | { 0x1F004, 0x1F004 }, |
| 347 | { 0x1F0CF, 0x1F0CF }, |
| 348 | { 0x1F18E, 0x1F18E }, |
| 349 | { 0x1F191, 0x1F19A }, |
| 350 | { 0x1F200, 0x1F202 }, |
| 351 | { 0x1F210, 0x1F23B }, |
| 352 | { 0x1F240, 0x1F248 }, |
| 353 | { 0x1F250, 0x1F251 }, |
| 354 | { 0x1F260, 0x1F265 }, |
| 355 | { 0x1F300, 0x1F320 }, |
| 356 | { 0x1F32D, 0x1F335 }, |
| 357 | { 0x1F337, 0x1F37C }, |
| 358 | { 0x1F37E, 0x1F393 }, |
| 359 | { 0x1F3A0, 0x1F3CA }, |
| 360 | { 0x1F3CF, 0x1F3D3 }, |
| 361 | { 0x1F3E0, 0x1F3F0 }, |
| 362 | { 0x1F3F4, 0x1F3F4 }, |
| 363 | { 0x1F3F8, 0x1F43E }, |
| 364 | { 0x1F440, 0x1F440 }, |
| 365 | { 0x1F442, 0x1F4FC }, |
| 366 | { 0x1F4FF, 0x1F53D }, |
| 367 | { 0x1F54B, 0x1F54E }, |
| 368 | { 0x1F550, 0x1F567 }, |
| 369 | { 0x1F57A, 0x1F57A }, |
| 370 | { 0x1F595, 0x1F596 }, |
| 371 | { 0x1F5A4, 0x1F5A4 }, |
| 372 | { 0x1F5FB, 0x1F64F }, |
| 373 | { 0x1F680, 0x1F6C5 }, |
| 374 | { 0x1F6CC, 0x1F6CC }, |
| 375 | { 0x1F6D0, 0x1F6D2 }, |
| 376 | { 0x1F6D5, 0x1F6D7 }, |
| 377 | { 0x1F6DC, 0x1F6DF }, |
| 378 | { 0x1F6EB, 0x1F6EC }, |
| 379 | { 0x1F6F4, 0x1F6FC }, |
| 380 | { 0x1F7E0, 0x1F7EB }, |
| 381 | { 0x1F7F0, 0x1F7F0 }, |
| 382 | { 0x1F90C, 0x1F93A }, |
| 383 | { 0x1F93C, 0x1F945 }, |
| 384 | { 0x1F947, 0x1F9FF }, |
| 385 | { 0x1FA70, 0x1FA7C }, |
| 386 | { 0x1FA80, 0x1FA89 }, |
| 387 | { 0x1FA8F, 0x1FAC6 }, |
| 388 | { 0x1FACE, 0x1FADC }, |
| 389 | { 0x1FADF, 0x1FAE9 }, |
| 390 | { 0x1FAF0, 0x1FAF8 }, |
| 391 | { 0x20000, 0x2FFFD }, |
| 392 | { 0x30000, 0x3FFFD } |
| 393 | /* END: WIDE CHAR TABLE */ |
| 394 | }; |
| 395 | |
| 396 | static const size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]); |
| 397 | |
| 398 | static bool isWideChar(unsigned long cp) { |
| 399 | for (size_t i = 0; i < wideCharTableSize; i++) { |
| 400 | auto first_code = wideCharTable[i][0]; |
| 401 | auto last_code = wideCharTable[i][1]; |
| 402 | if (first_code > cp) { |
| 403 | return false; |
| 404 | } |
| 405 | if (first_code <= cp && cp <= last_code) { |
| 406 | return true; |
| 407 | } |
| 408 | } |
| 409 | return false; |
| 410 | } |
| 411 | |
| 412 | /* Check if the code is a combining character */ |
| 413 | static const unsigned long combiningCharTable[] = { |
| 414 | /* BEGIN: COMBINING CHAR TABLE */ |
| 415 | 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, |
| 416 | 0x030D, 0x030E, 0x030F, 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, |
| 417 | 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, |
| 418 | 0x0327, 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, 0x0330, 0x0331, 0x0332, 0x0333, |
| 419 | 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, 0x0340, |
| 420 | 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, |
| 421 | 0x034E, 0x034F, 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035A, |
| 422 | 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, |
| 423 | 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, |
| 424 | 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, |
| 425 | 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, |
| 426 | 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, |
| 427 | 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7, 0x0610, |
| 428 | 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, 0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, |
| 429 | 0x064E, 0x064F, 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, 0x0658, 0x0659, 0x065A, |
| 430 | 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, 0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, |
| 431 | 0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8, 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, |
| 432 | 0x0730, 0x0731, 0x0732, 0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A, 0x073B, 0x073C, |
| 433 | 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742, 0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, |
| 434 | 0x074A, 0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD, 0x07AE, 0x07AF, 0x07B0, 0x07EB, |
| 435 | 0x07EC, 0x07ED, 0x07EE, 0x07EF, 0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818, 0x0819, |
| 436 | 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821, 0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, |
| 437 | 0x082A, 0x082B, 0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x0897, 0x0898, 0x0899, 0x089A, 0x089B, 0x089C, |
| 438 | 0x089D, 0x089E, 0x089F, 0x08CA, 0x08CB, 0x08CC, 0x08CD, 0x08CE, 0x08CF, 0x08D0, 0x08D1, 0x08D2, 0x08D3, |
| 439 | 0x08D4, 0x08D5, 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC, 0x08DD, 0x08DE, 0x08DF, 0x08E0, |
| 440 | 0x08E1, 0x08E3, 0x08E4, 0x08E5, 0x08E6, 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED, 0x08EE, |
| 441 | 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5, 0x08F6, 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, |
| 442 | 0x08FC, 0x08FD, 0x08FE, 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C, 0x0941, 0x0942, 0x0943, 0x0944, |
| 443 | 0x0945, 0x0946, 0x0947, 0x0948, 0x094D, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, 0x0962, |
| 444 | 0x0963, 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09CD, 0x09E2, 0x09E3, 0x09FE, 0x0A01, 0x0A02, |
| 445 | 0x0A3C, 0x0A41, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70, 0x0A71, 0x0A75, 0x0A81, |
| 446 | 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA, |
| 447 | 0x0AFB, 0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F, 0x0B41, 0x0B42, 0x0B43, 0x0B44, 0x0B4D, |
| 448 | 0x0B55, 0x0B56, 0x0B62, 0x0B63, 0x0B82, 0x0BC0, 0x0BCD, 0x0C00, 0x0C04, 0x0C3C, 0x0C3E, 0x0C3F, 0x0C40, |
| 449 | 0x0C46, 0x0C47, 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C55, 0x0C56, 0x0C62, 0x0C63, 0x0C81, 0x0CBC, |
| 450 | 0x0CBF, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D00, 0x0D01, 0x0D3B, 0x0D3C, 0x0D41, 0x0D42, 0x0D43, |
| 451 | 0x0D44, 0x0D4D, 0x0D62, 0x0D63, 0x0D81, 0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD6, 0x0E31, 0x0E34, 0x0E35, |
| 452 | 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, |
| 453 | 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC, 0x0EC8, 0x0EC9, 0x0ECA, |
| 454 | 0x0ECB, 0x0ECC, 0x0ECD, 0x0ECE, 0x0F18, 0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F71, 0x0F72, 0x0F73, 0x0F74, |
| 455 | 0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E, 0x0F80, 0x0F81, 0x0F82, |
| 456 | 0x0F83, 0x0F84, 0x0F86, 0x0F87, 0x0F8D, 0x0F8E, 0x0F8F, 0x0F90, 0x0F91, 0x0F92, 0x0F93, 0x0F94, 0x0F95, |
| 457 | 0x0F96, 0x0F97, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, |
| 458 | 0x0FA4, 0x0FA5, 0x0FA6, 0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE, 0x0FAF, 0x0FB0, |
| 459 | 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC, 0x0FC6, |
| 460 | 0x102D, 0x102E, 0x102F, 0x1030, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1039, 0x103A, 0x103D, |
| 461 | 0x103E, 0x1058, 0x1059, 0x105E, 0x105F, 0x1060, 0x1071, 0x1072, 0x1073, 0x1074, 0x1082, 0x1085, 0x1086, |
| 462 | 0x108D, 0x109D, 0x135D, 0x135E, 0x135F, 0x1712, 0x1713, 0x1714, 0x1732, 0x1733, 0x1752, 0x1753, 0x1772, |
| 463 | 0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17B8, 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD, 0x17C6, 0x17C9, 0x17CA, |
| 464 | 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17DD, 0x180B, 0x180C, 0x180D, |
| 465 | 0x180F, 0x1885, 0x1886, 0x18A9, 0x1920, 0x1921, 0x1922, 0x1927, 0x1928, 0x1932, 0x1939, 0x193A, 0x193B, |
| 466 | 0x1A17, 0x1A18, 0x1A1B, 0x1A56, 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E, 0x1A60, 0x1A62, |
| 467 | 0x1A65, 0x1A66, 0x1A67, 0x1A68, 0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A73, 0x1A74, 0x1A75, 0x1A76, 0x1A77, |
| 468 | 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7F, 0x1AB0, 0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, |
| 469 | 0x1AB7, 0x1AB8, 0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1ABF, 0x1AC0, 0x1AC1, 0x1AC2, 0x1AC3, 0x1AC4, |
| 470 | 0x1AC5, 0x1AC6, 0x1AC7, 0x1AC8, 0x1AC9, 0x1ACA, 0x1ACB, 0x1ACC, 0x1ACD, 0x1ACE, 0x1B00, 0x1B01, 0x1B02, |
| 471 | 0x1B03, 0x1B34, 0x1B36, 0x1B37, 0x1B38, 0x1B39, 0x1B3A, 0x1B3C, 0x1B42, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, |
| 472 | 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1B80, 0x1B81, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5, 0x1BA8, 0x1BA9, |
| 473 | 0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, 0x1BE8, 0x1BE9, 0x1BED, 0x1BEF, 0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, 0x1C2E, |
| 474 | 0x1C2F, 0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C36, 0x1C37, 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD4, 0x1CD5, 0x1CD6, |
| 475 | 0x1CD7, 0x1CD8, 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, 0x1CE2, 0x1CE3, 0x1CE4, |
| 476 | 0x1CE5, 0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, 0x1CF4, 0x1CF8, 0x1CF9, 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, |
| 477 | 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE, 0x1DCF, 0x1DD0, 0x1DD1, |
| 478 | 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6, 0x1DD7, 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE, |
| 479 | 0x1DDF, 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6, 0x1DE7, 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, |
| 480 | 0x1DEC, 0x1DED, 0x1DEE, 0x1DEF, 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6, 0x1DF7, 0x1DF8, |
| 481 | 0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE, 0x1DFF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, |
| 482 | 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, |
| 483 | 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, 0x2DE0, 0x2DE1, |
| 484 | 0x2DE2, 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, 0x2DE8, 0x2DE9, 0x2DEA, 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, |
| 485 | 0x2DEF, 0x2DF0, 0x2DF1, 0x2DF2, 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, 0x2DF8, 0x2DF9, 0x2DFA, 0x2DFB, |
| 486 | 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, 0x302A, 0x302B, 0x302C, 0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, 0xA675, |
| 487 | 0xA676, 0xA677, 0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, 0xA67D, 0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, |
| 488 | 0xA806, 0xA80B, 0xA825, 0xA826, 0xA82C, 0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3, 0xA8E4, 0xA8E5, |
| 489 | 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB, 0xA8EC, 0xA8ED, 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, |
| 490 | 0xA926, 0xA927, 0xA928, 0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947, 0xA948, 0xA949, 0xA94A, 0xA94B, |
| 491 | 0xA94C, 0xA94D, 0xA94E, 0xA94F, 0xA950, 0xA951, 0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7, 0xA9B8, |
| 492 | 0xA9B9, 0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B, 0xAA2C, 0xAA2D, 0xAA2E, 0xAA31, 0xAA32, 0xAA35, |
| 493 | 0xAA36, 0xAA43, 0xAA4C, 0xAA7C, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8, 0xAABE, 0xAABF, 0xAAC1, |
| 494 | 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8, 0xABED, 0xFB1E, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, |
| 495 | 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, 0xFE20, 0xFE21, 0xFE22, |
| 496 | 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, |
| 497 | 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379, 0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, |
| 498 | 0x10A0D, 0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6, 0x10D24, 0x10D25, 0x10D26, 0x10D27, |
| 499 | 0x10D69, 0x10D6A, 0x10D6B, 0x10D6C, 0x10D6D, 0x10EAB, 0x10EAC, 0x10EFC, 0x10EFD, 0x10EFE, 0x10EFF, 0x10F46, 0x10F47, |
| 500 | 0x10F48, 0x10F49, 0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E, 0x10F4F, 0x10F50, 0x10F82, 0x10F83, 0x10F84, 0x10F85, |
| 501 | 0x11001, 0x11038, 0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, 0x11040, 0x11041, 0x11042, 0x11043, |
| 502 | 0x11044, 0x11045, 0x11046, 0x11070, 0x11073, 0x11074, 0x1107F, 0x11080, 0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6, |
| 503 | 0x110B9, 0x110BA, 0x110C2, 0x11100, 0x11101, 0x11102, 0x11127, 0x11128, 0x11129, 0x1112A, 0x1112B, 0x1112D, 0x1112E, |
| 504 | 0x1112F, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, 0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7, 0x111B8, 0x111B9, |
| 505 | 0x111BA, 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9, 0x111CA, 0x111CB, 0x111CC, 0x111CF, 0x1122F, 0x11230, 0x11231, |
| 506 | 0x11234, 0x11236, 0x11237, 0x1123E, 0x11241, 0x112DF, 0x112E3, 0x112E4, 0x112E5, 0x112E6, 0x112E7, 0x112E8, 0x112E9, |
| 507 | 0x112EA, 0x11300, 0x11301, 0x1133B, 0x1133C, 0x11340, 0x11366, 0x11367, 0x11368, 0x11369, 0x1136A, 0x1136B, 0x1136C, |
| 508 | 0x11370, 0x11371, 0x11372, 0x11373, 0x11374, 0x113BB, 0x113BC, 0x113BD, 0x113BE, 0x113BF, 0x113C0, 0x113CE, 0x113D0, |
| 509 | 0x113D2, 0x113E1, 0x113E2, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E, 0x1143F, 0x11442, 0x11443, |
| 510 | 0x11444, 0x11446, 0x1145E, 0x114B3, 0x114B4, 0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0, 0x114C2, |
| 511 | 0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD, 0x115BF, 0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, |
| 512 | 0x11635, 0x11636, 0x11637, 0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB, 0x116AD, 0x116B0, 0x116B1, |
| 513 | 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7, 0x1171D, 0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727, 0x11728, |
| 514 | 0x11729, 0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832, 0x11833, 0x11834, 0x11835, 0x11836, 0x11837, 0x11839, |
| 515 | 0x1183A, 0x1193B, 0x1193C, 0x1193E, 0x11943, 0x119D4, 0x119D5, 0x119D6, 0x119D7, 0x119DA, 0x119DB, 0x119E0, 0x11A01, |
| 516 | 0x11A02, 0x11A03, 0x11A04, 0x11A05, 0x11A06, 0x11A07, 0x11A08, 0x11A09, 0x11A0A, 0x11A33, 0x11A34, 0x11A35, 0x11A36, |
| 517 | 0x11A37, 0x11A38, 0x11A3B, 0x11A3C, 0x11A3D, 0x11A3E, 0x11A47, 0x11A51, 0x11A52, 0x11A53, 0x11A54, 0x11A55, 0x11A56, |
| 518 | 0x11A59, 0x11A5A, 0x11A5B, 0x11A8A, 0x11A8B, 0x11A8C, 0x11A8D, 0x11A8E, 0x11A8F, 0x11A90, 0x11A91, 0x11A92, 0x11A93, |
| 519 | 0x11A94, 0x11A95, 0x11A96, 0x11A98, 0x11A99, 0x11C30, 0x11C31, 0x11C32, 0x11C33, 0x11C34, 0x11C35, 0x11C36, 0x11C38, |
| 520 | 0x11C39, 0x11C3A, 0x11C3B, 0x11C3C, 0x11C3D, 0x11C3F, 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96, 0x11C97, 0x11C98, |
| 521 | 0x11C99, 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E, 0x11C9F, 0x11CA0, 0x11CA1, 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, |
| 522 | 0x11CA6, 0x11CA7, 0x11CAA, 0x11CAB, 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0, 0x11CB2, 0x11CB3, 0x11CB5, 0x11CB6, |
| 523 | 0x11D31, 0x11D32, 0x11D33, 0x11D34, 0x11D35, 0x11D36, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D40, 0x11D41, 0x11D42, |
| 524 | 0x11D43, 0x11D44, 0x11D45, 0x11D47, 0x11D90, 0x11D91, 0x11D95, 0x11D97, 0x11EF3, 0x11EF4, 0x11F00, 0x11F01, 0x11F36, |
| 525 | 0x11F37, 0x11F38, 0x11F39, 0x11F3A, 0x11F40, 0x11F42, 0x11F5A, 0x13440, 0x13447, 0x13448, 0x13449, 0x1344A, 0x1344B, |
| 526 | 0x1344C, 0x1344D, 0x1344E, 0x1344F, 0x13450, 0x13451, 0x13452, 0x13453, 0x13454, 0x13455, 0x1611E, 0x1611F, 0x16120, |
| 527 | 0x16121, 0x16122, 0x16123, 0x16124, 0x16125, 0x16126, 0x16127, 0x16128, 0x16129, 0x1612D, 0x1612E, 0x1612F, 0x16AF0, |
| 528 | 0x16AF1, 0x16AF2, 0x16AF3, 0x16AF4, 0x16B30, 0x16B31, 0x16B32, 0x16B33, 0x16B34, 0x16B35, 0x16B36, 0x16F4F, 0x16F8F, |
| 529 | 0x16F90, 0x16F91, 0x16F92, 0x16FE4, 0x1BC9D, 0x1BC9E, 0x1CF00, 0x1CF01, 0x1CF02, 0x1CF03, 0x1CF04, 0x1CF05, 0x1CF06, |
| 530 | 0x1CF07, 0x1CF08, 0x1CF09, 0x1CF0A, 0x1CF0B, 0x1CF0C, 0x1CF0D, 0x1CF0E, 0x1CF0F, 0x1CF10, 0x1CF11, 0x1CF12, 0x1CF13, |
| 531 | 0x1CF14, 0x1CF15, 0x1CF16, 0x1CF17, 0x1CF18, 0x1CF19, 0x1CF1A, 0x1CF1B, 0x1CF1C, 0x1CF1D, 0x1CF1E, 0x1CF1F, 0x1CF20, |
| 532 | 0x1CF21, 0x1CF22, 0x1CF23, 0x1CF24, 0x1CF25, 0x1CF26, 0x1CF27, 0x1CF28, 0x1CF29, 0x1CF2A, 0x1CF2B, 0x1CF2C, 0x1CF2D, |
| 533 | 0x1CF30, 0x1CF31, 0x1CF32, 0x1CF33, 0x1CF34, 0x1CF35, 0x1CF36, 0x1CF37, 0x1CF38, 0x1CF39, 0x1CF3A, 0x1CF3B, 0x1CF3C, |
| 534 | 0x1CF3D, 0x1CF3E, 0x1CF3F, 0x1CF40, 0x1CF41, 0x1CF42, 0x1CF43, 0x1CF44, 0x1CF45, 0x1CF46, 0x1D167, 0x1D168, 0x1D169, |
| 535 | 0x1D17B, 0x1D17C, 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182, 0x1D185, 0x1D186, 0x1D187, 0x1D188, 0x1D189, |
| 536 | 0x1D18A, 0x1D18B, 0x1D1AA, 0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01, 0x1DA02, 0x1DA03, |
| 537 | 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09, 0x1DA0A, 0x1DA0B, 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, |
| 538 | 0x1DA11, 0x1DA12, 0x1DA13, 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19, 0x1DA1A, 0x1DA1B, 0x1DA1C, 0x1DA1D, |
| 539 | 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21, 0x1DA22, 0x1DA23, 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29, 0x1DA2A, |
| 540 | 0x1DA2B, 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31, 0x1DA32, 0x1DA33, 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, |
| 541 | 0x1DA3C, 0x1DA3D, 0x1DA3E, 0x1DA3F, 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45, 0x1DA46, 0x1DA47, 0x1DA48, |
| 542 | 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D, 0x1DA4E, 0x1DA4F, 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55, |
| 543 | 0x1DA56, 0x1DA57, 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D, 0x1DA5E, 0x1DA5F, 0x1DA60, 0x1DA61, 0x1DA62, |
| 544 | 0x1DA63, 0x1DA64, 0x1DA65, 0x1DA66, 0x1DA67, 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75, 0x1DA84, 0x1DA9B, |
| 545 | 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2, 0x1DAA3, 0x1DAA4, 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, |
| 546 | 0x1DAAA, 0x1DAAB, 0x1DAAC, 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002, 0x1E003, 0x1E004, 0x1E005, 0x1E006, |
| 547 | 0x1E008, 0x1E009, 0x1E00A, 0x1E00B, 0x1E00C, 0x1E00D, 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013, 0x1E014, |
| 548 | 0x1E015, 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D, 0x1E01E, 0x1E01F, 0x1E020, 0x1E021, 0x1E023, 0x1E024, |
| 549 | 0x1E026, 0x1E027, 0x1E028, 0x1E029, 0x1E02A, 0x1E08F, 0x1E130, 0x1E131, 0x1E132, 0x1E133, 0x1E134, 0x1E135, 0x1E136, |
| 550 | 0x1E2AE, 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF, 0x1E4EC, 0x1E4ED, 0x1E4EE, 0x1E4EF, 0x1E5EE, 0x1E5EF, 0x1E8D0, 0x1E8D1, |
| 551 | 0x1E8D2, 0x1E8D3, 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946, 0x1E947, 0x1E948, 0x1E949, 0x1E94A, 0xE0100, |
| 552 | 0xE0101, 0xE0102, 0xE0103, 0xE0104, 0xE0105, 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B, 0xE010C, 0xE010D, |
| 553 | 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113, 0xE0114, 0xE0115, 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, |
| 554 | 0xE011B, 0xE011C, 0xE011D, 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123, 0xE0124, 0xE0125, 0xE0126, 0xE0127, |
| 555 | 0xE0128, 0xE0129, 0xE012A, 0xE012B, 0xE012C, 0xE012D, 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133, 0xE0134, |
| 556 | 0xE0135, 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B, 0xE013C, 0xE013D, 0xE013E, 0xE013F, 0xE0140, 0xE0141, |
| 557 | 0xE0142, 0xE0143, 0xE0144, 0xE0145, 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B, 0xE014C, 0xE014D, 0xE014E, |
| 558 | 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153, 0xE0154, 0xE0155, 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B, |
| 559 | 0xE015C, 0xE015D, 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163, 0xE0164, 0xE0165, 0xE0166, 0xE0167, 0xE0168, |
| 560 | 0xE0169, 0xE016A, 0xE016B, 0xE016C, 0xE016D, 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173, 0xE0174, 0xE0175, |
| 561 | 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B, 0xE017C, 0xE017D, 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, |
| 562 | 0xE0183, 0xE0184, 0xE0185, 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B, 0xE018C, 0xE018D, 0xE018E, 0xE018F, |
| 563 | 0xE0190, 0xE0191, 0xE0192, 0xE0193, 0xE0194, 0xE0195, 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B, 0xE019C, |
| 564 | 0xE019D, 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3, 0xE01A4, 0xE01A5, 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, |
| 565 | 0xE01AA, 0xE01AB, 0xE01AC, 0xE01AD, 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3, 0xE01B4, 0xE01B5, 0xE01B6, |
| 566 | 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB, 0xE01BC, 0xE01BD, 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3, |
| 567 | 0xE01C4, 0xE01C5, 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB, 0xE01CC, 0xE01CD, 0xE01CE, 0xE01CF, 0xE01D0, |
| 568 | 0xE01D1, 0xE01D2, 0xE01D3, 0xE01D4, 0xE01D5, 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB, 0xE01DC, 0xE01DD, |
| 569 | 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3, 0xE01E4, 0xE01E5, 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, |
| 570 | 0xE01EB, 0xE01EC, 0xE01ED, 0xE01EE, 0xE01EF |
| 571 | /* END: COMBINING CHAR TABLE */ |
| 572 | }; |
| 573 | |
| 574 | static const unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]); |
| 575 | |
| 576 | static bool isCombiningChar(unsigned long cp) { |
| 577 | for (size_t i = 0; i < combiningCharTableSize; i++) { |
| 578 | auto code = combiningCharTable[i]; |
| 579 | if (code > cp) { |
| 580 | return false; |
| 581 | } |
| 582 | if (code == cp) { |
| 583 | return true; |
| 584 | } |
| 585 | } |
| 586 | return false; |
| 587 | } |
| 588 | |
| 589 | /* Get length of previous grapheme */ |
| 590 | static size_t defaultPrevCharLen(const char * buf, size_t /*buf_len*/, size_t pos, size_t * col_len) { |
| 591 | size_t end = pos; |
| 592 | while (pos > 0) { |
| 593 | size_t len = prevUtf8CodePointLen(buf, pos); |
| 594 | pos -= len; |
| 595 | int cp; |
| 596 | utf8BytesToCodePoint(buf: buf + pos, len, cp: &cp); |
| 597 | if (!isCombiningChar(cp)) { |
| 598 | if (col_len != NULL) { |
| 599 | *col_len = isWideChar(cp) ? 2 : 1; |
| 600 | } |
| 601 | return end - pos; |
| 602 | } |
| 603 | } |
| 604 | /* NOTREACHED */ |
| 605 | return 0; |
| 606 | } |
| 607 | |
| 608 | /* Get length of next grapheme */ |
| 609 | static size_t defaultNextCharLen(const char * buf, size_t buf_len, size_t pos, size_t * col_len) { |
| 610 | size_t beg = pos; |
| 611 | int cp; |
| 612 | size_t len = utf8BytesToCodePoint(buf: buf + pos, len: buf_len - pos, cp: &cp); |
| 613 | if (isCombiningChar(cp)) { |
| 614 | /* NOTREACHED */ |
| 615 | return 0; |
| 616 | } |
| 617 | if (col_len != NULL) { |
| 618 | *col_len = isWideChar(cp) ? 2 : 1; |
| 619 | } |
| 620 | pos += len; |
| 621 | while (pos < buf_len) { |
| 622 | int cp; |
| 623 | len = utf8BytesToCodePoint(buf: buf + pos, len: buf_len - pos, cp: &cp); |
| 624 | if (!isCombiningChar(cp)) { |
| 625 | return pos - beg; |
| 626 | } |
| 627 | pos += len; |
| 628 | } |
| 629 | return pos - beg; |
| 630 | } |
| 631 | |
| 632 | /* Read a Unicode from file. */ |
| 633 | static size_t defaultReadCode(int fd, char * buf, size_t buf_len, int * cp) { |
| 634 | if (buf_len < 1) { |
| 635 | return -1; |
| 636 | } |
| 637 | size_t nread = read(fd: fd, buf: &buf[0], nbytes: 1); |
| 638 | if (nread <= 0) { |
| 639 | return nread; |
| 640 | } |
| 641 | |
| 642 | unsigned char byte = buf[0]; |
| 643 | if ((byte & 0x80) == 0) { |
| 644 | ; |
| 645 | } else if ((byte & 0xE0) == 0xC0) { |
| 646 | if (buf_len < 2) { |
| 647 | return -1; |
| 648 | } |
| 649 | nread = read(fd: fd, buf: &buf[1], nbytes: 1); |
| 650 | if (nread <= 0) { |
| 651 | return nread; |
| 652 | } |
| 653 | } else if ((byte & 0xF0) == 0xE0) { |
| 654 | if (buf_len < 3) { |
| 655 | return -1; |
| 656 | } |
| 657 | nread = read(fd: fd, buf: &buf[1], nbytes: 2); |
| 658 | if (nread <= 0) { |
| 659 | return nread; |
| 660 | } |
| 661 | } else if ((byte & 0xF8) == 0xF0) { |
| 662 | if (buf_len < 3) { |
| 663 | return -1; |
| 664 | } |
| 665 | nread = read(fd: fd, buf: &buf[1], nbytes: 3); |
| 666 | if (nread <= 0) { |
| 667 | return nread; |
| 668 | } |
| 669 | } else { |
| 670 | return -1; |
| 671 | } |
| 672 | |
| 673 | return utf8BytesToCodePoint(buf, len: buf_len, cp); |
| 674 | } |
| 675 | |
| 676 | /* Set default encoding functions */ |
| 677 | static linenoisePrevCharLen * prevCharLen = defaultPrevCharLen; |
| 678 | static linenoiseNextCharLen * nextCharLen = defaultNextCharLen; |
| 679 | static linenoiseReadCode * readCode = defaultReadCode; |
| 680 | |
| 681 | /* Set used defined encoding functions */ |
| 682 | void linenoiseSetEncodingFunctions(linenoisePrevCharLen * prevCharLenFunc, linenoiseNextCharLen * nextCharLenFunc, |
| 683 | linenoiseReadCode * readCodeFunc) { |
| 684 | prevCharLen = prevCharLenFunc; |
| 685 | nextCharLen = nextCharLenFunc; |
| 686 | readCode = readCodeFunc; |
| 687 | } |
| 688 | |
| 689 | /* ======================= Low level terminal handling ====================== */ |
| 690 | |
| 691 | /* Enable "mask mode". When it is enabled, instead of the input that |
| 692 | * the user is typing, the terminal will just display a corresponding |
| 693 | * number of asterisks, like "****". This is useful for passwords and other |
| 694 | * secrets that should not be displayed. */ |
| 695 | void linenoiseMaskModeEnable(void) { |
| 696 | maskmode = 1; |
| 697 | } |
| 698 | |
| 699 | /* Disable mask mode. */ |
| 700 | void linenoiseMaskModeDisable(void) { |
| 701 | maskmode = 0; |
| 702 | } |
| 703 | |
| 704 | /* Set if to use or not the multi line mode. */ |
| 705 | void linenoiseSetMultiLine(int ml) { |
| 706 | mlmode = ml; |
| 707 | } |
| 708 | |
| 709 | /* Return true if the terminal name is in the list of terminals we know are |
| 710 | * not able to understand basic escape sequences. */ |
| 711 | static int isUnsupportedTerm(void) { |
| 712 | char *term = getenv(name: "TERM" ); |
| 713 | if (term == NULL) return 0; |
| 714 | for (size_t j = 0; j < unsupported_term.size(); ++j) { |
| 715 | if (!strcasecmp(s1: term, s2: unsupported_term[j])) { |
| 716 | return 1; |
| 717 | } |
| 718 | } |
| 719 | return 0; |
| 720 | } |
| 721 | |
| 722 | /* Raw mode: 1960 magic shit. */ |
| 723 | static int enableRawMode(int fd) { |
| 724 | struct termios raw; |
| 725 | |
| 726 | if (!isatty(STDIN_FILENO)) goto fatal; |
| 727 | if (!atexit_registered) { |
| 728 | atexit(func: linenoiseAtExit); |
| 729 | atexit_registered = 1; |
| 730 | } |
| 731 | if (tcgetattr(fd: fd,termios_p: &orig_termios) == -1) goto fatal; |
| 732 | |
| 733 | raw = orig_termios; /* modify the original mode */ |
| 734 | /* input modes: no break, no CR to NL, no parity check, no strip char, |
| 735 | * no start/stop output control. */ |
| 736 | raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); |
| 737 | /* output modes - disable post processing */ |
| 738 | raw.c_oflag &= ~(OPOST); |
| 739 | /* control modes - set 8 bit chars */ |
| 740 | raw.c_cflag |= (CS8); |
| 741 | /* local modes - choing off, canonical off, no extended functions, |
| 742 | * no signal chars (^Z,^C) */ |
| 743 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); |
| 744 | /* control chars - set return condition: min number of bytes and timer. |
| 745 | * We want read to return every single byte, without timeout. */ |
| 746 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ |
| 747 | |
| 748 | /* put terminal in raw mode after flushing */ |
| 749 | if (tcsetattr(fd: fd,TCSAFLUSH,termios_p: &raw) < 0) goto fatal; |
| 750 | rawmode = 1; |
| 751 | return 0; |
| 752 | |
| 753 | fatal: |
| 754 | errno = ENOTTY; |
| 755 | return -1; |
| 756 | } |
| 757 | |
| 758 | static void disableRawMode(int fd) { |
| 759 | /* Don't even check the return value as it's too late. */ |
| 760 | if (rawmode && tcsetattr(fd: fd,TCSAFLUSH,termios_p: &orig_termios) != -1) |
| 761 | rawmode = 0; |
| 762 | } |
| 763 | |
| 764 | /* Use the ESC [6n escape sequence to query the horizontal cursor position |
| 765 | * and return it. On error -1 is returned, on success the position of the |
| 766 | * cursor. */ |
| 767 | static int getCursorPosition(int ifd, int ofd) { |
| 768 | char buf[32]; |
| 769 | int cols, rows; |
| 770 | unsigned int i = 0; |
| 771 | |
| 772 | /* Report cursor location */ |
| 773 | if (write(fd: ofd, buf: "\x1b[6n" , n: 4) != 4) return -1; |
| 774 | |
| 775 | /* Read the response: ESC [ rows ; cols R */ |
| 776 | while (i < sizeof(buf)-1) { |
| 777 | if (read(fd: ifd,buf: buf+i,nbytes: 1) != 1) break; |
| 778 | if (buf[i] == 'R') break; |
| 779 | i++; |
| 780 | } |
| 781 | buf[i] = '\0'; |
| 782 | |
| 783 | /* Parse it. */ |
| 784 | if (buf[0] != ESC || buf[1] != '[') return -1; |
| 785 | if (sscanf(s: buf+2,format: "%d;%d" ,&rows,&cols) != 2) return -1; |
| 786 | return cols; |
| 787 | } |
| 788 | |
| 789 | /* Try to get the number of columns in the current terminal, or assume 80 |
| 790 | * if it fails. */ |
| 791 | static int getColumns(int ifd, int ofd) { |
| 792 | struct winsize ws; |
| 793 | |
| 794 | if (ioctl(fd: 1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { |
| 795 | /* ioctl() failed. Try to query the terminal itself. */ |
| 796 | int start, cols; |
| 797 | |
| 798 | /* Get the initial position so we can restore it later. */ |
| 799 | start = getCursorPosition(ifd,ofd); |
| 800 | if (start == -1) goto failed; |
| 801 | |
| 802 | /* Go to right margin and get position. */ |
| 803 | if (write(fd: ofd,buf: "\x1b[999C" ,n: 6) != 6) goto failed; |
| 804 | cols = getCursorPosition(ifd,ofd); |
| 805 | if (cols == -1) goto failed; |
| 806 | |
| 807 | /* Restore position. */ |
| 808 | if (cols > start) { |
| 809 | char seq[32]; |
| 810 | snprintf(s: seq,maxlen: 32,format: "\x1b[%dD" ,cols-start); |
| 811 | if (write(fd: ofd,buf: seq,n: strlen(s: seq)) == -1) { |
| 812 | /* Can't recover... */ |
| 813 | } |
| 814 | } |
| 815 | return cols; |
| 816 | } else { |
| 817 | return ws.ws_col; |
| 818 | } |
| 819 | |
| 820 | failed: |
| 821 | return 80; |
| 822 | } |
| 823 | |
| 824 | /* Clear the screen. Used to handle ctrl+l */ |
| 825 | void linenoiseClearScreen(void) { |
| 826 | if (write(STDOUT_FILENO,buf: "\x1b[H\x1b[2J" ,n: 7) <= 0) { |
| 827 | /* nothing to do, just to avoid warning. */ |
| 828 | } |
| 829 | } |
| 830 | |
| 831 | /* Beep, used for completion when there is nothing to complete or when all |
| 832 | * the choices were already shown. */ |
| 833 | static void linenoiseBeep(void) { |
| 834 | fprintf(stderr, format: "\x7" ); |
| 835 | fflush(stderr); |
| 836 | } |
| 837 | |
| 838 | /* Called by completeLine() and linenoiseShow() to render the current |
| 839 | * edited line with the proposed completion. If the current completion table |
| 840 | * is already available, it is passed as second argument, otherwise the |
| 841 | * function will use the callback to obtain it. |
| 842 | * |
| 843 | * Flags are the same as refreshLine*(), that is REFRESH_* macros. */ |
| 844 | static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) { |
| 845 | /* Obtain the table of completions if the caller didn't provide one. */ |
| 846 | linenoiseCompletions ctable; |
| 847 | if (lc == NULL) { |
| 848 | completionCallback(ls->buf, &ctable); |
| 849 | lc = &ctable; |
| 850 | } |
| 851 | |
| 852 | /* Show the edited line with completion if possible, or just refresh. */ |
| 853 | if (ls->completion_idx < lc->len) { |
| 854 | struct linenoiseState saved = *ls; |
| 855 | ls->len = ls->pos = strlen(s: lc->cvec[ls->completion_idx]); |
| 856 | ls->buf = lc->cvec[ls->completion_idx]; |
| 857 | refreshLineWithFlags(l: ls, flags); |
| 858 | ls->len = saved.len; |
| 859 | ls->pos = saved.pos; |
| 860 | ls->buf = saved.buf; |
| 861 | } else { |
| 862 | refreshLineWithFlags(l: ls, flags); |
| 863 | } |
| 864 | |
| 865 | if (lc == &ctable) { |
| 866 | ctable.to_free = false; |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | enum ESC_TYPE { ESC_NULL = 0, ESC_DELETE, ESC_UP, ESC_DOWN, ESC_RIGHT, ESC_LEFT, ESC_HOME, ESC_END }; |
| 871 | |
| 872 | static ESC_TYPE readEscapeSequence(struct linenoiseState * l) { |
| 873 | /* Check if the file input has additional data. */ |
| 874 | struct pollfd pfd; |
| 875 | pfd.fd = l->ifd; |
| 876 | pfd.events = POLLIN; |
| 877 | |
| 878 | auto ret = poll(fds: &pfd, nfds: 1, timeout: 1); // 1 millisecond timeout |
| 879 | if (ret <= 0) { // -1: error, 0: timeout |
| 880 | return ESC_NULL; |
| 881 | } |
| 882 | |
| 883 | /* Read the next two bytes representing the escape sequence. |
| 884 | * Use two calls to handle slow terminals returning the two |
| 885 | * chars at different times. */ |
| 886 | char seq[3]; |
| 887 | if (read(fd: l->ifd, buf: seq, nbytes: 1) == -1) { |
| 888 | return ESC_NULL; |
| 889 | } |
| 890 | if (read(fd: l->ifd, buf: seq + 1, nbytes: 1) == -1) { |
| 891 | return ESC_NULL; |
| 892 | } |
| 893 | |
| 894 | /* ESC [ sequences. */ |
| 895 | if (seq[0] == '[') { |
| 896 | if (seq[1] >= '0' && seq[1] <= '9') { |
| 897 | /* Extended escape, read additional byte. */ |
| 898 | if (read(fd: l->ifd, buf: seq + 2, nbytes: 1) == -1) { |
| 899 | return ESC_NULL; |
| 900 | } |
| 901 | if (seq[2] == '~') { |
| 902 | switch (seq[1]) { |
| 903 | case '3': |
| 904 | return ESC_DELETE; |
| 905 | } |
| 906 | } |
| 907 | } else { |
| 908 | switch (seq[1]) { |
| 909 | case 'A': |
| 910 | return ESC_UP; |
| 911 | case 'B': |
| 912 | return ESC_DOWN; |
| 913 | case 'C': |
| 914 | return ESC_RIGHT; |
| 915 | case 'D': |
| 916 | return ESC_LEFT; |
| 917 | case 'H': |
| 918 | return ESC_HOME; |
| 919 | case 'F': |
| 920 | return ESC_END; |
| 921 | } |
| 922 | } |
| 923 | } |
| 924 | |
| 925 | /* ESC O sequences. */ |
| 926 | else if (seq[0] == 'O') { |
| 927 | switch (seq[1]) { |
| 928 | case 'H': |
| 929 | return ESC_HOME; |
| 930 | case 'F': |
| 931 | return ESC_END; |
| 932 | } |
| 933 | } |
| 934 | return ESC_NULL; |
| 935 | } |
| 936 | |
| 937 | /* This is an helper function for linenoiseEdit*() and is called when the |
| 938 | * user types the <tab> key in order to complete the string currently in the |
| 939 | * input. |
| 940 | * |
| 941 | * The state of the editing is encapsulated into the pointed linenoiseState |
| 942 | * structure as described in the structure definition. |
| 943 | * |
| 944 | * If the function returns non-zero, the caller should handle the |
| 945 | * returned value as a byte read from the standard input, and process |
| 946 | * it as usually: this basically means that the function may return a byte |
| 947 | * read from the terminal but not processed. Otherwise, if zero is returned, |
| 948 | * the input was consumed by the completeLine() function to navigate the |
| 949 | * possible completions, and the caller should read for the next characters |
| 950 | * from stdin. */ |
| 951 | static int completeLine(struct linenoiseState * ls, int keypressed, ESC_TYPE esc_type) { |
| 952 | linenoiseCompletions lc; |
| 953 | int nwritten; |
| 954 | char c = keypressed; |
| 955 | |
| 956 | completionCallback(ls->buf, &lc); |
| 957 | if (lc.len == 0) { |
| 958 | linenoiseBeep(); |
| 959 | ls->in_completion = 0; |
| 960 | } else { |
| 961 | if (c == TAB) { |
| 962 | if (ls->in_completion == 0) { |
| 963 | ls->in_completion = 1; |
| 964 | ls->completion_idx = 0; |
| 965 | } else { |
| 966 | ls->completion_idx = (ls->completion_idx + 1) % (lc.len + 1); |
| 967 | if (ls->completion_idx == lc.len) { |
| 968 | linenoiseBeep(); |
| 969 | } |
| 970 | } |
| 971 | c = 0; |
| 972 | } else if (c == ESC && esc_type == ESC_NULL) { |
| 973 | /* Re-show original buffer */ |
| 974 | if (ls->completion_idx < lc.len) { |
| 975 | refreshLine(l: ls); |
| 976 | } |
| 977 | ls->in_completion = 0; |
| 978 | c = 0; |
| 979 | } else { |
| 980 | /* Update buffer and return */ |
| 981 | if (ls->completion_idx < lc.len) { |
| 982 | nwritten = snprintf(s: ls->buf, maxlen: ls->buflen, format: "%s" , lc.cvec[ls->completion_idx]); |
| 983 | ls->len = ls->pos = nwritten; |
| 984 | } |
| 985 | ls->in_completion = 0; |
| 986 | } |
| 987 | |
| 988 | /* Show completion or original buffer */ |
| 989 | if (ls->in_completion && ls->completion_idx < lc.len) { |
| 990 | refreshLineWithCompletion(ls, lc: &lc, REFRESH_ALL); |
| 991 | } else { |
| 992 | refreshLine(l: ls); |
| 993 | } |
| 994 | } |
| 995 | |
| 996 | return c; /* Return last read character */ |
| 997 | } |
| 998 | |
| 999 | /* Register a callback function to be called for tab-completion. */ |
| 1000 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { |
| 1001 | completionCallback = fn; |
| 1002 | } |
| 1003 | |
| 1004 | /* Register a hits function to be called to show hits to the user at the |
| 1005 | * right of the prompt. */ |
| 1006 | void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { |
| 1007 | hintsCallback = fn; |
| 1008 | } |
| 1009 | |
| 1010 | /* Register a function to free the hints returned by the hints callback |
| 1011 | * registered with linenoiseSetHintsCallback(). */ |
| 1012 | void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { |
| 1013 | freeHintsCallback = fn; |
| 1014 | } |
| 1015 | |
| 1016 | /* This function is used by the callback function registered by the user |
| 1017 | * in order to add completion options given the input string when the |
| 1018 | * user typed <tab>. See the example.c source code for a very easy to |
| 1019 | * understand example. */ |
| 1020 | void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { |
| 1021 | const size_t len = strlen(s: str); |
| 1022 | auto copy = std::make_unique<char[]>(num: len + 1); |
| 1023 | if (!copy) { |
| 1024 | return; |
| 1025 | } |
| 1026 | |
| 1027 | memcpy(dest: copy.get(), src: str, n: len + 1); |
| 1028 | char ** cvec = static_cast<char **>(std::realloc(ptr: lc->cvec, size: sizeof(char *) * (lc->len + 1))); |
| 1029 | if (cvec == nullptr) { |
| 1030 | return; |
| 1031 | } |
| 1032 | |
| 1033 | lc->cvec = cvec; |
| 1034 | lc->cvec[lc->len++] = copy.release(); |
| 1035 | } |
| 1036 | |
| 1037 | /* Get column length from begining of buffer to current byte position */ |
| 1038 | static size_t columnPos(const char * buf, size_t buf_len, size_t pos) { |
| 1039 | size_t ret = 0; |
| 1040 | size_t off = 0; |
| 1041 | while (off < pos) { |
| 1042 | size_t col_len; |
| 1043 | size_t len = nextCharLen(buf, buf_len, off, &col_len); |
| 1044 | off += len; |
| 1045 | ret += col_len; |
| 1046 | } |
| 1047 | return ret; |
| 1048 | } |
| 1049 | |
| 1050 | /* Helper of refreshSingleLine() and refreshMultiLine() to show hints |
| 1051 | * to the right of the prompt. */ |
| 1052 | static void refreshShowHints(std::string & ab, struct linenoiseState * l, int pcollen) { |
| 1053 | char seq[64]; |
| 1054 | size_t collen = pcollen + columnPos(buf: l->buf, buf_len: l->len, pos: l->len); |
| 1055 | if (hintsCallback && collen < l->cols) { |
| 1056 | int color = -1, bold = 0; |
| 1057 | const char *hint = hintsCallback(l->buf,&color,&bold); |
| 1058 | if (hint) { |
| 1059 | int hintlen = strlen(s: hint); |
| 1060 | int hintmaxlen = l->cols - collen; |
| 1061 | if (hintlen > hintmaxlen) hintlen = hintmaxlen; |
| 1062 | if (bold == 1 && color == -1) color = 37; |
| 1063 | if (color != -1 || bold != 0) |
| 1064 | snprintf(s: seq,maxlen: 64,format: "\033[%d;%d;49m" ,bold,color); |
| 1065 | else |
| 1066 | seq[0] = '\0'; |
| 1067 | ab.append(s: seq); |
| 1068 | ab.append(s: hint, n: hintlen); |
| 1069 | if (color != -1 || bold != 0) |
| 1070 | ab.append(s: "\033[0m" ); |
| 1071 | |
| 1072 | /* Call the function to free the hint returned. */ |
| 1073 | if (freeHintsCallback) freeHintsCallback(hint); |
| 1074 | } |
| 1075 | } |
| 1076 | } |
| 1077 | |
| 1078 | /* Check if text is an ANSI escape sequence */ |
| 1079 | static int isAnsiEscape(const char * buf, size_t buf_len, size_t * len) { |
| 1080 | if (buf_len > 2 && !memcmp(s1: "\033[" , s2: buf, n: 2)) { |
| 1081 | size_t off = 2; |
| 1082 | while (off < buf_len) { |
| 1083 | switch (buf[off++]) { |
| 1084 | case 'A': |
| 1085 | case 'B': |
| 1086 | case 'C': |
| 1087 | case 'D': |
| 1088 | case 'E': |
| 1089 | case 'F': |
| 1090 | case 'G': |
| 1091 | case 'H': |
| 1092 | case 'J': |
| 1093 | case 'K': |
| 1094 | case 'S': |
| 1095 | case 'T': |
| 1096 | case 'f': |
| 1097 | case 'm': |
| 1098 | *len = off; |
| 1099 | return 1; |
| 1100 | } |
| 1101 | } |
| 1102 | } |
| 1103 | return 0; |
| 1104 | } |
| 1105 | |
| 1106 | /* Get column length of prompt text */ |
| 1107 | static size_t promptTextColumnLen(const char * prompt, size_t plen) { |
| 1108 | char buf[LINENOISE_MAX_LINE]; |
| 1109 | size_t buf_len = 0; |
| 1110 | size_t off = 0; |
| 1111 | while (off < plen) { |
| 1112 | size_t len; |
| 1113 | if (isAnsiEscape(buf: prompt + off, buf_len: plen - off, len: &len)) { |
| 1114 | off += len; |
| 1115 | continue; |
| 1116 | } |
| 1117 | buf[buf_len++] = prompt[off++]; |
| 1118 | } |
| 1119 | return columnPos(buf, buf_len, pos: buf_len); |
| 1120 | } |
| 1121 | |
| 1122 | /* Single line low level line refresh. |
| 1123 | * |
| 1124 | * Rewrite the currently edited line accordingly to the buffer content, |
| 1125 | * cursor position, and number of columns of the terminal. |
| 1126 | * |
| 1127 | * Flags is REFRESH_* macros. The function can just remove the old |
| 1128 | * prompt, just write it, or both. */ |
| 1129 | static void refreshSingleLine(struct linenoiseState *l, int flags) { |
| 1130 | char seq[64]; |
| 1131 | size_t pcollen = promptTextColumnLen(prompt: l->prompt, plen: strlen(s: l->prompt)); |
| 1132 | int fd = l->ofd; |
| 1133 | char *buf = l->buf; |
| 1134 | size_t len = l->len; |
| 1135 | size_t pos = l->pos; |
| 1136 | std::string ab; |
| 1137 | |
| 1138 | while ((pcollen + columnPos(buf, buf_len: len, pos)) >= l->cols) { |
| 1139 | int chlen = nextCharLen(buf, len, 0, NULL); |
| 1140 | buf += chlen; |
| 1141 | len -= chlen; |
| 1142 | pos -= chlen; |
| 1143 | } |
| 1144 | while (pcollen + columnPos(buf, buf_len: len, pos: len) > l->cols) { |
| 1145 | len -= prevCharLen(buf, len, len, NULL); |
| 1146 | } |
| 1147 | |
| 1148 | /* Cursor to left edge */ |
| 1149 | snprintf(s: seq,maxlen: sizeof(seq),format: "\r" ); |
| 1150 | ab.append(s: seq); |
| 1151 | |
| 1152 | if (flags & REFRESH_WRITE) { |
| 1153 | /* Write the prompt and the current buffer content */ |
| 1154 | ab.append(s: l->prompt); |
| 1155 | if (maskmode == 1) { |
| 1156 | while (len--) { |
| 1157 | ab.append(s: "*" ); |
| 1158 | } |
| 1159 | } else { |
| 1160 | ab.append(s: buf, n: len); |
| 1161 | } |
| 1162 | /* Show hits if any. */ |
| 1163 | refreshShowHints(ab, l, pcollen); |
| 1164 | } |
| 1165 | |
| 1166 | /* Erase to right */ |
| 1167 | snprintf(s: seq,maxlen: sizeof(seq),format: "\x1b[0K" ); |
| 1168 | ab.append(s: seq); |
| 1169 | if (flags & REFRESH_WRITE) { |
| 1170 | /* Move cursor to original position. */ |
| 1171 | snprintf(s: seq, maxlen: sizeof(seq), format: "\r\x1b[%dC" , (int) (columnPos(buf, buf_len: len, pos) + pcollen)); |
| 1172 | ab.append(s: seq); |
| 1173 | } |
| 1174 | |
| 1175 | (void) !write(fd: fd, buf: ab.c_str(), n: ab.size()); /* Can't recover from write error. */ |
| 1176 | } |
| 1177 | |
| 1178 | /* Get column length from begining of buffer to current byte position for multiline mode*/ |
| 1179 | static size_t columnPosForMultiLine(const char * buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) { |
| 1180 | size_t ret = 0; |
| 1181 | size_t colwid = ini_pos; |
| 1182 | |
| 1183 | size_t off = 0; |
| 1184 | while (off < buf_len) { |
| 1185 | size_t col_len; |
| 1186 | size_t len = nextCharLen(buf, buf_len, off, &col_len); |
| 1187 | |
| 1188 | int dif = (int) (colwid + col_len) - (int) cols; |
| 1189 | if (dif > 0) { |
| 1190 | ret += dif; |
| 1191 | colwid = col_len; |
| 1192 | } else if (dif == 0) { |
| 1193 | colwid = 0; |
| 1194 | } else { |
| 1195 | colwid += col_len; |
| 1196 | } |
| 1197 | |
| 1198 | if (off >= pos) { |
| 1199 | break; |
| 1200 | } |
| 1201 | off += len; |
| 1202 | ret += col_len; |
| 1203 | } |
| 1204 | |
| 1205 | return ret; |
| 1206 | } |
| 1207 | |
| 1208 | /* Multi line low level line refresh. |
| 1209 | * |
| 1210 | * Rewrite the currently edited line accordingly to the buffer content, |
| 1211 | * cursor position, and number of columns of the terminal. |
| 1212 | * |
| 1213 | * Flags is REFRESH_* macros. The function can just remove the old |
| 1214 | * prompt, just write it, or both. */ |
| 1215 | static void refreshMultiLine(struct linenoiseState *l, int flags) { |
| 1216 | char seq[64]; |
| 1217 | size_t pcollen = promptTextColumnLen(prompt: l->prompt, plen: strlen(s: l->prompt)); |
| 1218 | int colpos = columnPosForMultiLine(buf: l->buf, buf_len: l->len, pos: l->len, cols: l->cols, ini_pos: pcollen); |
| 1219 | int colpos2; /* cursor column position. */ |
| 1220 | int rows = (pcollen + colpos + l->cols - 1) / l->cols; /* rows used by current buf. */ |
| 1221 | int rpos = (pcollen + l->oldcolpos + l->cols) / l->cols; /* cursor relative row. */ |
| 1222 | int rpos2; /* rpos after refresh. */ |
| 1223 | int col; /* column position, zero-based. */ |
| 1224 | int old_rows = l->oldrows; |
| 1225 | int fd = l->ofd, j; |
| 1226 | std::string ab; |
| 1227 | l->oldrows = rows; |
| 1228 | |
| 1229 | /* First step: clear all the lines used before. To do so start by |
| 1230 | * going to the last row. */ |
| 1231 | if (flags & REFRESH_CLEAN) { |
| 1232 | if (old_rows - rpos > 0) { |
| 1233 | snprintf(s: seq,maxlen: 64,format: "\x1b[%dB" , old_rows-rpos); |
| 1234 | ab.append(s: seq); |
| 1235 | } |
| 1236 | |
| 1237 | /* Now for every row clear it, go up. */ |
| 1238 | for (j = 0; j < old_rows - 1; j++) { |
| 1239 | snprintf(s: seq,maxlen: 64,format: "\r\x1b[0K\x1b[1A" ); |
| 1240 | ab.append(s: seq); |
| 1241 | } |
| 1242 | } |
| 1243 | |
| 1244 | if (flags & REFRESH_ALL) { |
| 1245 | /* Clean the top line. */ |
| 1246 | snprintf(s: seq,maxlen: 64,format: "\r\x1b[0K" ); |
| 1247 | ab.append(s: seq); |
| 1248 | } |
| 1249 | |
| 1250 | /* Get column length to cursor position */ |
| 1251 | colpos2 = columnPosForMultiLine(buf: l->buf, buf_len: l->len, pos: l->pos, cols: l->cols, ini_pos: pcollen); |
| 1252 | |
| 1253 | if (flags & REFRESH_WRITE) { |
| 1254 | /* Write the prompt and the current buffer content */ |
| 1255 | ab.append(s: l->prompt); |
| 1256 | if (maskmode == 1) { |
| 1257 | for (unsigned int i = 0; i < l->len; ++i) { |
| 1258 | ab.append(s: "*" ); |
| 1259 | } |
| 1260 | } else { |
| 1261 | ab.append(s: l->buf, n: l->len); |
| 1262 | } |
| 1263 | |
| 1264 | /* Show hits if any. */ |
| 1265 | refreshShowHints(ab, l, pcollen); |
| 1266 | |
| 1267 | /* If we are at the very end of the screen with our prompt, we need to |
| 1268 | * emit a newline and move the prompt to the first column. */ |
| 1269 | if (l->pos && l->pos == l->len && (colpos2 + pcollen) % l->cols == 0) { |
| 1270 | ab.append(s: "\n" ); |
| 1271 | snprintf(s: seq,maxlen: 64,format: "\r" ); |
| 1272 | ab.append(s: seq); |
| 1273 | rows++; |
| 1274 | if (rows > (int)l->oldrows) l->oldrows = rows; |
| 1275 | } |
| 1276 | |
| 1277 | /* Move cursor to right position. */ |
| 1278 | rpos2 = (pcollen + colpos2 + l->cols) / l->cols; /* Current cursor relative row */ |
| 1279 | |
| 1280 | /* Go up till we reach the expected position. */ |
| 1281 | if (rows - rpos2 > 0) { |
| 1282 | snprintf(s: seq,maxlen: 64,format: "\x1b[%dA" , rows-rpos2); |
| 1283 | ab.append(s: seq); |
| 1284 | } |
| 1285 | |
| 1286 | /* Set column. */ |
| 1287 | col = (pcollen + colpos2) % l->cols; |
| 1288 | if (col) |
| 1289 | snprintf(s: seq,maxlen: 64,format: "\r\x1b[%dC" , col); |
| 1290 | else |
| 1291 | snprintf(s: seq,maxlen: 64,format: "\r" ); |
| 1292 | ab.append(s: seq); |
| 1293 | } |
| 1294 | |
| 1295 | l->oldcolpos = colpos2; |
| 1296 | |
| 1297 | (void) !write(fd: fd, buf: ab.c_str(), n: ab.size()); /* Can't recover from write error. */ |
| 1298 | } |
| 1299 | |
| 1300 | /* Calls the two low level functions refreshSingleLine() or |
| 1301 | * refreshMultiLine() according to the selected mode. */ |
| 1302 | static void refreshLineWithFlags(struct linenoiseState *l, int flags) { |
| 1303 | if (mlmode) |
| 1304 | refreshMultiLine(l,flags); |
| 1305 | else |
| 1306 | refreshSingleLine(l,flags); |
| 1307 | } |
| 1308 | |
| 1309 | /* Utility function to avoid specifying REFRESH_ALL all the times. */ |
| 1310 | static void refreshLine(struct linenoiseState *l) { |
| 1311 | refreshLineWithFlags(l,REFRESH_ALL); |
| 1312 | } |
| 1313 | |
| 1314 | /* Hide the current line, when using the multiplexing API. */ |
| 1315 | void linenoiseHide(struct linenoiseState *l) { |
| 1316 | if (mlmode) |
| 1317 | refreshMultiLine(l,REFRESH_CLEAN); |
| 1318 | else |
| 1319 | refreshSingleLine(l,REFRESH_CLEAN); |
| 1320 | } |
| 1321 | |
| 1322 | /* Show the current line, when using the multiplexing API. */ |
| 1323 | void linenoiseShow(struct linenoiseState *l) { |
| 1324 | if (l->in_completion) { |
| 1325 | refreshLineWithCompletion(ls: l,NULL,REFRESH_WRITE); |
| 1326 | } else { |
| 1327 | refreshLineWithFlags(l,REFRESH_WRITE); |
| 1328 | } |
| 1329 | } |
| 1330 | |
| 1331 | /* Insert the character 'c' at cursor current position. |
| 1332 | * |
| 1333 | * On error writing to the terminal -1 is returned, otherwise 0. */ |
| 1334 | static int linenoiseEditInsert(struct linenoiseState * l, const char * cbuf, int clen) { |
| 1335 | if (l->len + clen <= l->buflen) { |
| 1336 | if (l->len == l->pos) { |
| 1337 | memcpy(dest: &l->buf[l->pos], src: cbuf, n: clen); |
| 1338 | l->pos += clen; |
| 1339 | l->len += clen; |
| 1340 | ; |
| 1341 | l->buf[l->len] = '\0'; |
| 1342 | if ((!mlmode && promptTextColumnLen(prompt: l->prompt, plen: l->plen) + columnPos(buf: l->buf, buf_len: l->len, pos: l->len) < l->cols && |
| 1343 | !hintsCallback)) { |
| 1344 | /* Avoid a full update of the line in the |
| 1345 | * trivial case. */ |
| 1346 | if (maskmode == 1) { |
| 1347 | static const char d = '*'; |
| 1348 | if (write(fd: l->ofd, buf: &d, n: 1) == -1) { |
| 1349 | return -1; |
| 1350 | } |
| 1351 | } else { |
| 1352 | if (write(fd: l->ofd, buf: cbuf, n: clen) == -1) { |
| 1353 | return -1; |
| 1354 | } |
| 1355 | } |
| 1356 | } else { |
| 1357 | refreshLine(l); |
| 1358 | } |
| 1359 | } else { |
| 1360 | memmove(dest: l->buf + l->pos + clen, src: l->buf + l->pos, n: l->len - l->pos); |
| 1361 | memcpy(dest: &l->buf[l->pos], src: cbuf, n: clen); |
| 1362 | l->pos += clen; |
| 1363 | l->len += clen; |
| 1364 | l->buf[l->len] = '\0'; |
| 1365 | refreshLine(l); |
| 1366 | } |
| 1367 | } |
| 1368 | return 0; |
| 1369 | } |
| 1370 | |
| 1371 | /* Move cursor on the left. */ |
| 1372 | static void linenoiseEditMoveLeft(struct linenoiseState * l) { |
| 1373 | if (l->pos > 0) { |
| 1374 | l->pos -= prevCharLen(l->buf, l->len, l->pos, NULL); |
| 1375 | refreshLine(l); |
| 1376 | } |
| 1377 | } |
| 1378 | |
| 1379 | /* Move cursor on the right. */ |
| 1380 | static void linenoiseEditMoveRight(struct linenoiseState * l) { |
| 1381 | if (l->pos != l->len) { |
| 1382 | l->pos += nextCharLen(l->buf, l->len, l->pos, NULL); |
| 1383 | refreshLine(l); |
| 1384 | } |
| 1385 | } |
| 1386 | |
| 1387 | /* Move cursor to the start of the line. */ |
| 1388 | static void linenoiseEditMoveHome(struct linenoiseState * l) { |
| 1389 | if (l->pos != 0) { |
| 1390 | l->pos = 0; |
| 1391 | refreshLine(l); |
| 1392 | } |
| 1393 | } |
| 1394 | |
| 1395 | /* Move cursor to the end of the line. */ |
| 1396 | static void linenoiseEditMoveEnd(struct linenoiseState * l) { |
| 1397 | if (l->pos != l->len) { |
| 1398 | l->pos = l->len; |
| 1399 | refreshLine(l); |
| 1400 | } |
| 1401 | } |
| 1402 | |
| 1403 | /* Substitute the currently edited line with the next or previous history |
| 1404 | * entry as specified by 'dir'. */ |
| 1405 | #define LINENOISE_HISTORY_NEXT 0 |
| 1406 | #define LINENOISE_HISTORY_PREV 1 |
| 1407 | |
| 1408 | static void linenoiseEditHistoryNext(struct linenoiseState * l, int dir) { |
| 1409 | if (history_len > 1) { |
| 1410 | /* Update the current history entry before to |
| 1411 | * overwrite it with the next one. */ |
| 1412 | free(ptr: history[history_len - 1 - l->history_index]); |
| 1413 | history[history_len - 1 - l->history_index] = strdup(s: l->buf); |
| 1414 | /* Show the new entry */ |
| 1415 | l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; |
| 1416 | if (l->history_index < 0) { |
| 1417 | l->history_index = 0; |
| 1418 | return; |
| 1419 | } else if (l->history_index >= history_len) { |
| 1420 | l->history_index = history_len-1; |
| 1421 | return; |
| 1422 | } |
| 1423 | strncpy(dest: l->buf,src: history[history_len - 1 - l->history_index],n: l->buflen); |
| 1424 | l->buf[l->buflen-1] = '\0'; |
| 1425 | l->len = l->pos = strlen(s: l->buf); |
| 1426 | refreshLine(l); |
| 1427 | } |
| 1428 | } |
| 1429 | |
| 1430 | /* Delete the character at the right of the cursor without altering the cursor |
| 1431 | * position. Basically this is what happens with the "Delete" keyboard key. */ |
| 1432 | static void linenoiseEditDelete(struct linenoiseState * l) { |
| 1433 | if (l->len > 0 && l->pos < l->len) { |
| 1434 | int chlen = nextCharLen(l->buf, l->len, l->pos, NULL); |
| 1435 | memmove(dest: l->buf + l->pos, src: l->buf + l->pos + chlen, n: l->len - l->pos - chlen); |
| 1436 | l->len -= chlen; |
| 1437 | l->buf[l->len] = '\0'; |
| 1438 | refreshLine(l); |
| 1439 | } |
| 1440 | } |
| 1441 | |
| 1442 | /* Backspace implementation. */ |
| 1443 | static void linenoiseEditBackspace(struct linenoiseState * l) { |
| 1444 | if (l->pos > 0 && l->len > 0) { |
| 1445 | int chlen = prevCharLen(l->buf, l->len, l->pos, NULL); |
| 1446 | memmove(dest: l->buf + l->pos - chlen, src: l->buf + l->pos, n: l->len - l->pos); |
| 1447 | l->pos -= chlen; |
| 1448 | l->len -= chlen; |
| 1449 | l->buf[l->len] = '\0'; |
| 1450 | refreshLine(l); |
| 1451 | } |
| 1452 | } |
| 1453 | |
| 1454 | /* Delete the previous word, maintaining the cursor at the start of the |
| 1455 | * current word. */ |
| 1456 | static void linenoiseEditDeletePrevWord(struct linenoiseState * l) { |
| 1457 | size_t old_pos = l->pos; |
| 1458 | size_t diff; |
| 1459 | |
| 1460 | while (l->pos > 0 && l->buf[l->pos-1] == ' ') |
| 1461 | l->pos--; |
| 1462 | while (l->pos > 0 && l->buf[l->pos-1] != ' ') |
| 1463 | l->pos--; |
| 1464 | diff = old_pos - l->pos; |
| 1465 | memmove(dest: l->buf+l->pos,src: l->buf+old_pos,n: l->len-old_pos+1); |
| 1466 | l->len -= diff; |
| 1467 | refreshLine(l); |
| 1468 | } |
| 1469 | |
| 1470 | /* This function is part of the multiplexed API of Linenoise, that is used |
| 1471 | * in order to implement the blocking variant of the API but can also be |
| 1472 | * called by the user directly in an event driven program. It will: |
| 1473 | * |
| 1474 | * 1. Initialize the linenoise state passed by the user. |
| 1475 | * 2. Put the terminal in RAW mode. |
| 1476 | * 3. Show the prompt. |
| 1477 | * 4. Return control to the user, that will have to call linenoiseEditFeed() |
| 1478 | * each time there is some data arriving in the standard input. |
| 1479 | * |
| 1480 | * The user can also call linenoiseEditHide() and linenoiseEditShow() if it |
| 1481 | * is required to show some input arriving asynchronously, without mixing |
| 1482 | * it with the currently edited line. |
| 1483 | * |
| 1484 | * When linenoiseEditFeed() returns non-NULL, the user finished with the |
| 1485 | * line editing session (pressed enter CTRL-D/C): in this case the caller |
| 1486 | * needs to call linenoiseEditStop() to put back the terminal in normal |
| 1487 | * mode. This will not destroy the buffer, as long as the linenoiseState |
| 1488 | * is still valid in the context of the caller. |
| 1489 | * |
| 1490 | * The function returns 0 on success, or -1 if writing to standard output |
| 1491 | * fails. If stdin_fd or stdout_fd are set to -1, the default is to use |
| 1492 | * STDIN_FILENO and STDOUT_FILENO. |
| 1493 | */ |
| 1494 | int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { |
| 1495 | /* Populate the linenoise state that we pass to functions implementing |
| 1496 | * specific editing functionalities. */ |
| 1497 | l->in_completion = 0; |
| 1498 | l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO; |
| 1499 | l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO; |
| 1500 | l->buf = buf; |
| 1501 | l->buflen = buflen; |
| 1502 | l->prompt = prompt; |
| 1503 | l->plen = strlen(s: prompt); |
| 1504 | l->oldcolpos = l->pos = 0; |
| 1505 | l->len = 0; |
| 1506 | |
| 1507 | /* Enter raw mode. */ |
| 1508 | if (enableRawMode(fd: l->ifd) == -1) return -1; |
| 1509 | |
| 1510 | l->cols = getColumns(ifd: stdin_fd, ofd: stdout_fd); |
| 1511 | l->oldrows = 0; |
| 1512 | l->history_index = 0; |
| 1513 | |
| 1514 | /* Buffer starts empty. */ |
| 1515 | l->buf[0] = '\0'; |
| 1516 | l->buflen--; /* Make sure there is always space for the nullterm */ |
| 1517 | |
| 1518 | /* If stdin is not a tty, stop here with the initialization. We |
| 1519 | * will actually just read a line from standard input in blocking |
| 1520 | * mode later, in linenoiseEditFeed(). */ |
| 1521 | if (!isatty(fd: l->ifd)) return 0; |
| 1522 | |
| 1523 | /* The latest history entry is always our current buffer, that |
| 1524 | * initially is just an empty string. */ |
| 1525 | linenoiseHistoryAdd(line: "" ); |
| 1526 | |
| 1527 | if (write(fd: l->ofd,buf: prompt,n: l->plen) == -1) return -1; |
| 1528 | return 0; |
| 1529 | } |
| 1530 | |
| 1531 | const char* linenoiseEditMore = "If you see this, you are misusing the API: when linenoiseEditFeed() is called, if it returns linenoiseEditMore the user is yet editing the line. See the README file for more information." ; |
| 1532 | |
| 1533 | static const char * handleEnterKey(struct linenoiseState * l) { |
| 1534 | --history_len; |
| 1535 | free(ptr: history[history_len]); |
| 1536 | if (mlmode) { |
| 1537 | linenoiseEditMoveEnd(l); |
| 1538 | } |
| 1539 | if (hintsCallback) { |
| 1540 | /* Force a refresh without hints to leave the previous |
| 1541 | * line as the user typed it after a newline. */ |
| 1542 | linenoiseHintsCallback * hc = hintsCallback; |
| 1543 | hintsCallback = NULL; |
| 1544 | refreshLine(l); |
| 1545 | hintsCallback = hc; |
| 1546 | } |
| 1547 | |
| 1548 | return strdup(s: l->buf); |
| 1549 | } |
| 1550 | |
| 1551 | static const char * handleCtrlCKey() { |
| 1552 | errno = EAGAIN; |
| 1553 | return NULL; |
| 1554 | } |
| 1555 | |
| 1556 | static const char * handleCtrlDKey(struct linenoiseState * l) { |
| 1557 | if (l->len > 0) { |
| 1558 | linenoiseEditDelete(l); |
| 1559 | return linenoiseEditMore; |
| 1560 | } |
| 1561 | |
| 1562 | --history_len; |
| 1563 | free(ptr: history[history_len]); |
| 1564 | errno = ENOENT; |
| 1565 | return NULL; |
| 1566 | } |
| 1567 | |
| 1568 | static void handleCtrlTKey(struct linenoiseState * l) { |
| 1569 | if (l->pos > 0 && l->pos < l->len) { |
| 1570 | auto prev_chlen = prevCharLen(l->buf, l->len, l->pos, NULL); |
| 1571 | auto curr_chlen = nextCharLen(l->buf, l->len, l->pos, NULL); |
| 1572 | |
| 1573 | std::string prev_char(prev_chlen, 0); |
| 1574 | memcpy(dest: prev_char.data(), src: l->buf + l->pos - prev_chlen, n: prev_chlen); |
| 1575 | memmove(dest: l->buf + l->pos - prev_chlen, src: l->buf + l->pos, n: curr_chlen); |
| 1576 | memmove(dest: l->buf + l->pos - prev_chlen + curr_chlen, src: prev_char.data(), n: prev_chlen); |
| 1577 | |
| 1578 | l->pos = l->pos - prev_chlen + curr_chlen; |
| 1579 | if (l->pos + prev_chlen != l->len) { |
| 1580 | l->pos += prev_chlen; |
| 1581 | } |
| 1582 | |
| 1583 | refreshLine(l); |
| 1584 | } |
| 1585 | } |
| 1586 | |
| 1587 | static void handleEscapeSequence(struct linenoiseState * l, int esc_type) { |
| 1588 | switch (esc_type) { |
| 1589 | case ESC_NULL: |
| 1590 | break; |
| 1591 | case ESC_DELETE: |
| 1592 | linenoiseEditDelete(l); |
| 1593 | break; |
| 1594 | case ESC_UP: |
| 1595 | linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV); |
| 1596 | break; |
| 1597 | case ESC_DOWN: |
| 1598 | linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT); |
| 1599 | break; |
| 1600 | case ESC_RIGHT: |
| 1601 | linenoiseEditMoveRight(l); |
| 1602 | break; |
| 1603 | case ESC_LEFT: |
| 1604 | linenoiseEditMoveLeft(l); |
| 1605 | break; |
| 1606 | case ESC_HOME: |
| 1607 | linenoiseEditMoveHome(l); |
| 1608 | break; |
| 1609 | case ESC_END: |
| 1610 | linenoiseEditMoveEnd(l); |
| 1611 | break; |
| 1612 | } |
| 1613 | } |
| 1614 | |
| 1615 | static void handleCtrlUKey(struct linenoiseState * l) { |
| 1616 | l->buf[0] = '\0'; |
| 1617 | l->pos = l->len = 0; |
| 1618 | refreshLine(l); |
| 1619 | } |
| 1620 | |
| 1621 | static void handleCtrlKKey(struct linenoiseState * l) { |
| 1622 | l->buf[l->pos] = '\0'; |
| 1623 | l->len = l->pos; |
| 1624 | refreshLine(l); |
| 1625 | } |
| 1626 | |
| 1627 | static const char * processInputCharacter(struct linenoiseState * l, int c, char * cbuf, int nread, int esc_type) { |
| 1628 | switch (c) { |
| 1629 | case ENTER: |
| 1630 | return handleEnterKey(l); |
| 1631 | case CTRL_C: |
| 1632 | return handleCtrlCKey(); |
| 1633 | case BACKSPACE: |
| 1634 | case CTRL_H: |
| 1635 | linenoiseEditBackspace(l); |
| 1636 | break; |
| 1637 | case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the |
| 1638 | line is empty, act as end-of-file. */ |
| 1639 | return handleCtrlDKey(l); |
| 1640 | case CTRL_T: |
| 1641 | handleCtrlTKey(l); |
| 1642 | break; |
| 1643 | case CTRL_B: |
| 1644 | linenoiseEditMoveLeft(l); |
| 1645 | break; |
| 1646 | case CTRL_F: |
| 1647 | linenoiseEditMoveRight(l); |
| 1648 | break; |
| 1649 | case CTRL_P: |
| 1650 | linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV); |
| 1651 | break; |
| 1652 | case CTRL_N: |
| 1653 | linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT); |
| 1654 | break; |
| 1655 | case ESC: |
| 1656 | handleEscapeSequence(l, esc_type); |
| 1657 | break; |
| 1658 | default: |
| 1659 | if (linenoiseEditInsert(l, cbuf, clen: nread)) { |
| 1660 | return NULL; |
| 1661 | } |
| 1662 | break; |
| 1663 | case CTRL_U: /* Ctrl+u, delete the whole line. */ |
| 1664 | handleCtrlUKey(l); |
| 1665 | break; |
| 1666 | case CTRL_K: /* Ctrl+k, delete from current to end of line. */ |
| 1667 | handleCtrlKKey(l); |
| 1668 | break; |
| 1669 | case CTRL_A: /* Ctrl+a, go to the start of the line */ |
| 1670 | linenoiseEditMoveHome(l); |
| 1671 | break; |
| 1672 | case CTRL_E: /* ctrl+e, go to the end of the line */ |
| 1673 | linenoiseEditMoveEnd(l); |
| 1674 | break; |
| 1675 | case CTRL_L: /* ctrl+l, clear screen */ |
| 1676 | linenoiseClearScreen(); |
| 1677 | refreshLine(l); |
| 1678 | break; |
| 1679 | case CTRL_W: /* ctrl+w, delete previous word */ |
| 1680 | linenoiseEditDeletePrevWord(l); |
| 1681 | break; |
| 1682 | } |
| 1683 | return linenoiseEditMore; |
| 1684 | } |
| 1685 | |
| 1686 | /* This function is part of the multiplexed API of linenoise, see the top |
| 1687 | * comment on linenoiseEditStart() for more information. Call this function |
| 1688 | * each time there is some data to read from the standard input file |
| 1689 | * descriptor. In the case of blocking operations, this function can just be |
| 1690 | * called in a loop, and block. |
| 1691 | * |
| 1692 | * The function returns linenoiseEditMore to signal that line editing is still |
| 1693 | * in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise |
| 1694 | * the function returns the pointer to the heap-allocated buffer with the |
| 1695 | * edited line, that the user should free with linenoiseFree(). |
| 1696 | * |
| 1697 | * On special conditions, NULL is returned and errno is populated: |
| 1698 | * |
| 1699 | * EAGAIN if the user pressed Ctrl-C |
| 1700 | * ENOENT if the user pressed Ctrl-D |
| 1701 | * |
| 1702 | * Some other errno: I/O error. |
| 1703 | */ |
| 1704 | const char * linenoiseEditFeed(struct linenoiseState * l) { |
| 1705 | /* Not a TTY, pass control to line reading without character count |
| 1706 | * limits. */ |
| 1707 | if (!isatty(fd: l->ifd)) return linenoiseNoTTY(); |
| 1708 | |
| 1709 | int c; |
| 1710 | int nread; |
| 1711 | char cbuf[32]; |
| 1712 | |
| 1713 | nread = readCode(l->ifd, cbuf, sizeof(cbuf), &c); |
| 1714 | if (nread <= 0) return NULL; |
| 1715 | |
| 1716 | auto esc_type = ESC_NULL; |
| 1717 | if (c == ESC) { |
| 1718 | esc_type = readEscapeSequence(l); |
| 1719 | } |
| 1720 | |
| 1721 | /* Only autocomplete when the callback is set. It returns < 0 when |
| 1722 | * there was an error reading from fd. Otherwise it will return the |
| 1723 | * character that should be handled next. */ |
| 1724 | if ((l->in_completion || c == 9) && completionCallback != NULL) { |
| 1725 | c = completeLine(ls: l, keypressed: c, esc_type); |
| 1726 | /* Read next character when 0 */ |
| 1727 | if (c == 0) return linenoiseEditMore; |
| 1728 | } |
| 1729 | |
| 1730 | return processInputCharacter(l, c, cbuf, nread, esc_type); |
| 1731 | } |
| 1732 | |
| 1733 | /* This is part of the multiplexed linenoise API. See linenoiseEditStart() |
| 1734 | * for more information. This function is called when linenoiseEditFeed() |
| 1735 | * returns something different than NULL. At this point the user input |
| 1736 | * is in the buffer, and we can restore the terminal in normal mode. */ |
| 1737 | void linenoiseEditStop(struct linenoiseState *l) { |
| 1738 | if (!isatty(fd: l->ifd)) return; |
| 1739 | disableRawMode(fd: l->ifd); |
| 1740 | printf(format: "\n" ); |
| 1741 | } |
| 1742 | |
| 1743 | /* This just implements a blocking loop for the multiplexed API. |
| 1744 | * In many applications that are not event-driven, we can just call |
| 1745 | * the blocking linenoise API, wait for the user to complete the editing |
| 1746 | * and return the buffer. */ |
| 1747 | static const char *linenoiseBlockingEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) |
| 1748 | { |
| 1749 | struct linenoiseState l; |
| 1750 | |
| 1751 | /* Editing without a buffer is invalid. */ |
| 1752 | if (buflen == 0) { |
| 1753 | errno = EINVAL; |
| 1754 | return NULL; |
| 1755 | } |
| 1756 | |
| 1757 | linenoiseEditStart(l: &l,stdin_fd,stdout_fd,buf,buflen,prompt); |
| 1758 | const char *res; |
| 1759 | while((res = linenoiseEditFeed(l: &l)) == linenoiseEditMore); |
| 1760 | linenoiseEditStop(l: &l); |
| 1761 | return res; |
| 1762 | } |
| 1763 | |
| 1764 | /* This special mode is used by linenoise in order to print scan codes |
| 1765 | * on screen for debugging / development purposes. It is implemented |
| 1766 | * by the linenoise_example program using the --keycodes option. */ |
| 1767 | void linenoisePrintKeyCodes(void) { |
| 1768 | char quit[4]; |
| 1769 | |
| 1770 | printf(format: "Linenoise key codes debugging mode.\n" |
| 1771 | "Press keys to see scan codes. Type 'quit' at any time to exit.\n" ); |
| 1772 | if (enableRawMode(STDIN_FILENO) == -1) return; |
| 1773 | memset(s: quit,c: ' ',n: 4); |
| 1774 | while(1) { |
| 1775 | char c; |
| 1776 | int nread; |
| 1777 | |
| 1778 | nread = read(STDIN_FILENO,buf: &c,nbytes: 1); |
| 1779 | if (nread <= 0) continue; |
| 1780 | memmove(dest: quit,src: quit+1,n: sizeof(quit)-1); /* shift string to left. */ |
| 1781 | quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ |
| 1782 | if (memcmp(s1: quit,s2: "quit" ,n: sizeof(quit)) == 0) break; |
| 1783 | |
| 1784 | printf(format: "'%c' %02x (%d) (type quit to exit)\n" , isprint((int) c) ? c : '?', (int) c, (int) c); |
| 1785 | printf(format: "\r" ); /* Go left edge manually, we are in raw mode. */ |
| 1786 | fflush(stdout); |
| 1787 | } |
| 1788 | disableRawMode(STDIN_FILENO); |
| 1789 | } |
| 1790 | |
| 1791 | /* This function is called when linenoise() is called with the standard |
| 1792 | * input file descriptor not attached to a TTY. So for example when the |
| 1793 | * program using linenoise is called in pipe or with a file redirected |
| 1794 | * to its standard input. In this case, we want to be able to return the |
| 1795 | * line regardless of its length (by default we are limited to 4k). */ |
| 1796 | static char *linenoiseNoTTY(void) { |
| 1797 | char *line = NULL; |
| 1798 | size_t len = 0, maxlen = 0; |
| 1799 | |
| 1800 | while(1) { |
| 1801 | if (len == maxlen) { |
| 1802 | if (maxlen == 0) maxlen = 16; |
| 1803 | maxlen *= 2; |
| 1804 | char *oldval = line; |
| 1805 | line = (char*) realloc(ptr: line,size: maxlen); |
| 1806 | if (line == NULL) { |
| 1807 | if (oldval) free(ptr: oldval); |
| 1808 | return NULL; |
| 1809 | } |
| 1810 | } |
| 1811 | int c = fgetc(stdin); |
| 1812 | if (c == EOF || c == '\n') { |
| 1813 | if (c == EOF && len == 0) { |
| 1814 | free(ptr: line); |
| 1815 | return NULL; |
| 1816 | } else { |
| 1817 | line[len] = '\0'; |
| 1818 | return line; |
| 1819 | } |
| 1820 | } else { |
| 1821 | line[len] = c; |
| 1822 | len++; |
| 1823 | } |
| 1824 | } |
| 1825 | } |
| 1826 | |
| 1827 | /* The high level function that is the main API of the linenoise library. |
| 1828 | * This function checks if the terminal has basic capabilities, just checking |
| 1829 | * for a blacklist of stupid terminals, and later either calls the line |
| 1830 | * editing function or uses dummy fgets() so that you will be able to type |
| 1831 | * something even in the most desperate of the conditions. */ |
| 1832 | const char *linenoise(const char *prompt) { |
| 1833 | char buf[LINENOISE_MAX_LINE]; |
| 1834 | |
| 1835 | if (!isatty(STDIN_FILENO)) { |
| 1836 | /* Not a tty: read from file / pipe. In this mode we don't want any |
| 1837 | * limit to the line size, so we call a function to handle that. */ |
| 1838 | return linenoiseNoTTY(); |
| 1839 | } else if (isUnsupportedTerm()) { |
| 1840 | size_t len; |
| 1841 | |
| 1842 | printf(format: "%s" ,prompt); |
| 1843 | fflush(stdout); |
| 1844 | if (fgets(s: buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; |
| 1845 | len = strlen(s: buf); |
| 1846 | while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { |
| 1847 | len--; |
| 1848 | buf[len] = '\0'; |
| 1849 | } |
| 1850 | return strdup(s: buf); |
| 1851 | } else { |
| 1852 | const char *retval = linenoiseBlockingEdit(STDIN_FILENO,STDOUT_FILENO,buf,LINENOISE_MAX_LINE,prompt); |
| 1853 | return retval; |
| 1854 | } |
| 1855 | } |
| 1856 | |
| 1857 | /* This is just a wrapper the user may want to call in order to make sure |
| 1858 | * the linenoise returned buffer is freed with the same allocator it was |
| 1859 | * created with. Useful when the main program is using an alternative |
| 1860 | * allocator. */ |
| 1861 | void linenoiseFree(void *ptr) { |
| 1862 | if (ptr == linenoiseEditMore) return; // Protect from API misuse. |
| 1863 | free(ptr: ptr); |
| 1864 | } |
| 1865 | |
| 1866 | /* ================================ History ================================= */ |
| 1867 | |
| 1868 | /* Free the history, but does not reset it. Only used when we have to |
| 1869 | * exit() to avoid memory leaks are reported by valgrind & co. */ |
| 1870 | static void freeHistory(void) { |
| 1871 | if (history) { |
| 1872 | int j; |
| 1873 | |
| 1874 | for (j = 0; j < history_len; j++) |
| 1875 | free(ptr: history[j]); |
| 1876 | free(ptr: history); |
| 1877 | } |
| 1878 | } |
| 1879 | |
| 1880 | /* At exit we'll try to fix the terminal to the initial conditions. */ |
| 1881 | static void linenoiseAtExit(void) { |
| 1882 | disableRawMode(STDIN_FILENO); |
| 1883 | freeHistory(); |
| 1884 | } |
| 1885 | |
| 1886 | /* This is the API call to add a new entry in the linenoise history. |
| 1887 | * It uses a fixed array of char pointers that are shifted (memmoved) |
| 1888 | * when the history max length is reached in order to remove the older |
| 1889 | * entry and make room for the new one, so it is not exactly suitable for huge |
| 1890 | * histories, but will work well for a few hundred of entries. |
| 1891 | * |
| 1892 | * Using a circular buffer is smarter, but a bit more complex to handle. */ |
| 1893 | int linenoiseHistoryAdd(const char *line) { |
| 1894 | char *linecopy; |
| 1895 | |
| 1896 | if (history_max_len == 0) return 0; |
| 1897 | |
| 1898 | /* Initialization on first call. */ |
| 1899 | if (history == NULL) { |
| 1900 | history = (char**) malloc(size: sizeof(char*)*history_max_len); |
| 1901 | if (history == NULL) return 0; |
| 1902 | memset(s: history,c: 0,n: (sizeof(char*)*history_max_len)); |
| 1903 | } |
| 1904 | |
| 1905 | /* Don't add duplicated lines. */ |
| 1906 | if (history_len && !strcmp(s1: history[history_len-1], s2: line)) return 0; |
| 1907 | |
| 1908 | /* Add an heap allocated copy of the line in the history. |
| 1909 | * If we reached the max length, remove the older line. */ |
| 1910 | linecopy = strdup(s: line); |
| 1911 | if (!linecopy) return 0; |
| 1912 | if (history_len == history_max_len) { |
| 1913 | free(ptr: history[0]); |
| 1914 | memmove(dest: history,src: history+1,n: sizeof(char*)*(history_max_len-1)); |
| 1915 | history_len--; |
| 1916 | } |
| 1917 | history[history_len] = linecopy; |
| 1918 | history_len++; |
| 1919 | return 1; |
| 1920 | } |
| 1921 | |
| 1922 | /* Set the maximum length for the history. This function can be called even |
| 1923 | * if there is already some history, the function will make sure to retain |
| 1924 | * just the latest 'len' elements if the new history length value is smaller |
| 1925 | * than the amount of items already inside the history. */ |
| 1926 | int linenoiseHistorySetMaxLen(int len) { |
| 1927 | char **new_ptr; |
| 1928 | |
| 1929 | if (len < 1) return 0; |
| 1930 | if (history) { |
| 1931 | int tocopy = history_len; |
| 1932 | |
| 1933 | new_ptr = (char**) malloc(size: sizeof(char*)*len); |
| 1934 | if (new_ptr == NULL) return 0; |
| 1935 | |
| 1936 | /* If we can't copy everything, free the elements we'll not use. */ |
| 1937 | if (len < tocopy) { |
| 1938 | int j; |
| 1939 | |
| 1940 | for (j = 0; j < tocopy-len; j++) free(ptr: history[j]); |
| 1941 | tocopy = len; |
| 1942 | } |
| 1943 | memset(s: new_ptr,c: 0,n: sizeof(char*)*len); |
| 1944 | memcpy(dest: new_ptr,src: history+(history_len-tocopy), n: sizeof(char*)*tocopy); |
| 1945 | free(ptr: history); |
| 1946 | history = new_ptr; |
| 1947 | } |
| 1948 | history_max_len = len; |
| 1949 | if (history_len > history_max_len) |
| 1950 | history_len = history_max_len; |
| 1951 | return 1; |
| 1952 | } |
| 1953 | |
| 1954 | /* Save the history in the specified file. On success 0 is returned |
| 1955 | * otherwise -1 is returned. */ |
| 1956 | int linenoiseHistorySave(const char *filename) { |
| 1957 | mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); |
| 1958 | File file; |
| 1959 | file.open(filename, mode: "w" ); |
| 1960 | umask(mask: old_umask); |
| 1961 | if (file.file == NULL) { |
| 1962 | return -1; |
| 1963 | } |
| 1964 | chmod(file: filename,S_IRUSR|S_IWUSR); |
| 1965 | for (int j = 0; j < history_len; ++j) { |
| 1966 | fprintf(stream: file.file, format: "%s\n" , history[j]); |
| 1967 | } |
| 1968 | |
| 1969 | return 0; |
| 1970 | } |
| 1971 | |
| 1972 | /* Load the history from the specified file. If the file does not exist |
| 1973 | * zero is returned and no operation is performed. |
| 1974 | * |
| 1975 | * If the file exists and the operation succeeded 0 is returned, otherwise |
| 1976 | * on error -1 is returned. */ |
| 1977 | int linenoiseHistoryLoad(const char *filename) { |
| 1978 | File file; |
| 1979 | file.open(filename, mode: "r" ); |
| 1980 | char buf[LINENOISE_MAX_LINE]; |
| 1981 | if (file.file == NULL) { |
| 1982 | return -1; |
| 1983 | } |
| 1984 | |
| 1985 | while (fgets(s: buf, LINENOISE_MAX_LINE, stream: file.file) != NULL) { |
| 1986 | char *p; |
| 1987 | |
| 1988 | p = strchr(s: buf,c: '\r'); |
| 1989 | if (!p) p = strchr(s: buf,c: '\n'); |
| 1990 | if (p) *p = '\0'; |
| 1991 | linenoiseHistoryAdd(line: buf); |
| 1992 | } |
| 1993 | return 0; |
| 1994 | } |
| 1995 | #endif |
| 1996 | |