2025-01-18 14:42:31 +00:00
# ifndef _WIN32
/*
* You can find the latest source code at :
*
* http : //github.com/ericcurtin/linenoise.cpp
*
* Does a number of crazy assumptions that happen to be true in 99.9999 % of
* the 2010 UNIX computers around .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Copyright ( c ) 2010 - 2023 , Salvatore Sanfilippo < antirez at gmail dot com >
* Copyright ( c ) 2010 - 2013 , Pieter Noordhuis < pcnoordhuis at gmail dot com >
* Copyright ( c ) 2025 , Eric Curtin < ericcurtin17 at gmail dot com >
*
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are
* met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* References :
* - http : //invisible-island.net/xterm/ctlseqs/ctlseqs.html
* - http : //www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*
* Todo list :
* - Filter bogus Ctrl + < char > combinations .
* - Win32 support
*
* Bloat :
* - History search like Ctrl + r in readline ?
*
* List of escape sequences used by this program , we do everything just
* with three sequences . In order to be so cheap we may have some
* flickering effect with some slow terminal , but the lesser sequences
* the more compatible .
*
* EL ( Erase Line )
* Sequence : ESC [ n K
* Effect : if n is 0 or missing , clear from cursor to end of line
* Effect : if n is 1 , clear from beginning of line to cursor
* Effect : if n is 2 , clear entire line
*
* CUF ( CUrsor Forward )
* Sequence : ESC [ n C
* Effect : moves cursor forward n chars
*
* CUB ( CUrsor Backward )
* Sequence : ESC [ n D
* Effect : moves cursor backward n chars
*
* The following is used to get the terminal width if getting
* the width with the TIOCGWINSZ ioctl fails
*
* DSR ( Device Status Report )
* Sequence : ESC [ 6 n
2025-03-03 12:44:56 +00:00
* Effect : reports the current cursor position as ESC [ n ; m R
2025-01-18 14:42:31 +00:00
* where n is the row and m is the column
*
* When multi line mode is enabled , we also use an additional escape
* sequence . However multi line editing is disabled by default .
*
* CUU ( Cursor Up )
* Sequence : ESC [ n A
* Effect : moves cursor up of n chars .
*
* CUD ( Cursor Down )
* Sequence : ESC [ n B
* Effect : moves cursor down of n chars .
*
* When linenoiseClearScreen ( ) is called , two additional escape sequences
* are used in order to clear the screen and position the cursor at home
* position .
*
* CUP ( Cursor position )
* Sequence : ESC [ H
* Effect : moves the cursor to upper left corner
*
* ED ( Erase display )
* Sequence : ESC [ 2 J
* Effect : clear the whole screen
*
*/
2025-01-21 09:32:35 +00:00
# include "linenoise.h"
# include <ctype.h>
# include <errno.h>
2025-03-03 12:44:56 +00:00
# include <poll.h>
2025-01-21 09:32:35 +00:00
# include <stdio.h>
# include <string.h>
# include <sys / file.h>
# include <sys / ioctl.h>
# include <sys / stat.h>
# include <sys / types.h>
# include <termios.h>
# include <unistd.h>
# include <memory>
# include <string>
# include <vector>
# define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
# define LINENOISE_MAX_LINE 4096
static std : : vector < const char * > unsupported_term = { " dumb " , " cons25 " , " emacs " } ;
2025-01-18 14:42:31 +00:00
static linenoiseCompletionCallback * completionCallback = NULL ;
static linenoiseHintsCallback * hintsCallback = NULL ;
static linenoiseFreeHintsCallback * freeHintsCallback = NULL ;
static char * linenoiseNoTTY ( void ) ;
static void refreshLineWithCompletion ( struct linenoiseState * ls , linenoiseCompletions * lc , int flags ) ;
static void refreshLineWithFlags ( struct linenoiseState * l , int flags ) ;
static struct termios orig_termios ; /* In order to restore at exit.*/
static int maskmode = 0 ; /* Show "***" instead of input. For passwords. */
static int rawmode = 0 ; /* For atexit() function to check if restore is needed*/
static int mlmode = 0 ; /* Multi line mode. Default is single line. */
static int atexit_registered = 0 ; /* Register atexit just 1 time. */
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN ;
static int history_len = 0 ;
static char * * history = NULL ;
enum KEY_ACTION {
KEY_NULL = 0 , /* NULL */
CTRL_A = 1 , /* Ctrl+a */
CTRL_B = 2 , /* Ctrl-b */
CTRL_C = 3 , /* Ctrl-c */
CTRL_D = 4 , /* Ctrl-d */
CTRL_E = 5 , /* Ctrl-e */
CTRL_F = 6 , /* Ctrl-f */
CTRL_H = 8 , /* Ctrl-h */
TAB = 9 , /* Tab */
CTRL_K = 11 , /* Ctrl+k */
CTRL_L = 12 , /* Ctrl+l */
ENTER = 13 , /* Enter */
CTRL_N = 14 , /* Ctrl-n */
CTRL_P = 16 , /* Ctrl-p */
CTRL_T = 20 , /* Ctrl-t */
CTRL_U = 21 , /* Ctrl+u */
CTRL_W = 23 , /* Ctrl+w */
ESC = 27 , /* Escape */
BACKSPACE = 127 /* Backspace */
} ;
static void linenoiseAtExit ( void ) ;
int linenoiseHistoryAdd ( const char * line ) ;
# define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen
# define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen.
# define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
static void refreshLine ( struct linenoiseState * l ) ;
2025-01-21 09:32:35 +00:00
class File {
public :
FILE * file = nullptr ;
FILE * open ( const std : : string & filename , const char * mode ) {
file = fopen ( filename . c_str ( ) , mode ) ;
return file ;
}
int lock ( ) {
if ( file ) {
fd = fileno ( file ) ;
if ( flock ( fd , LOCK_EX | LOCK_NB ) ! = 0 ) {
fd = - 1 ;
return 1 ;
}
}
return 0 ;
}
~ File ( ) {
if ( fd > = 0 ) {
flock ( fd , LOCK_UN ) ;
}
if ( file ) {
fclose ( file ) ;
}
}
private :
int fd = - 1 ;
} ;
2025-01-18 14:42:31 +00:00
#if 0
2025-03-03 12:44:56 +00:00
/* Debugging function. */
__attribute__ ( ( format ( printf , 1 , 2 ) ) )
2025-01-18 14:42:31 +00:00
static void lndebug ( const char * fmt , . . . ) {
2025-01-21 09:32:35 +00:00
static File file ;
if ( file . file = = nullptr ) {
file . open ( " /tmp/lndebug.txt " , " a " ) ;
2025-01-18 14:42:31 +00:00
}
2025-01-21 09:32:35 +00:00
if ( file . file ! = nullptr ) {
2025-01-18 14:42:31 +00:00
va_list args ;
va_start ( args , fmt ) ;
2025-01-21 09:32:35 +00:00
vfprintf ( file . file , fmt , args ) ;
2025-01-18 14:42:31 +00:00
va_end ( args ) ;
2025-01-21 09:32:35 +00:00
fflush ( file . file ) ;
2025-01-18 14:42:31 +00:00
}
}
# endif
2025-03-03 12:44:56 +00:00
/* ========================== Encoding functions ============================= */
/* Get length of previous UTF8 codepoint */
static size_t prevUtf8CodePointLen ( const char * buf , int pos ) {
int end = pos - - ;
while ( pos > = 0 & & ( ( unsigned char ) buf [ pos ] & 0xC0 ) = = 0x80 ) {
pos - - ;
}
return end - pos ;
}
/* Convert UTF8 to Unicode code point */
static size_t utf8BytesToCodePoint ( const char * buf , size_t len , int * cp ) {
if ( len ) {
unsigned char byte = buf [ 0 ] ;
if ( ( byte & 0x80 ) = = 0 ) {
* cp = byte ;
return 1 ;
} else if ( ( byte & 0xE0 ) = = 0xC0 ) {
if ( len > = 2 ) {
* cp = ( ( ( unsigned long ) ( buf [ 0 ] & 0x1F ) ) < < 6 ) | ( ( unsigned long ) ( buf [ 1 ] & 0x3F ) ) ;
return 2 ;
}
} else if ( ( byte & 0xF0 ) = = 0xE0 ) {
if ( len > = 3 ) {
* cp = ( ( ( unsigned long ) ( buf [ 0 ] & 0x0F ) ) < < 12 ) | ( ( ( unsigned long ) ( buf [ 1 ] & 0x3F ) ) < < 6 ) |
( ( unsigned long ) ( buf [ 2 ] & 0x3F ) ) ;
return 3 ;
}
} else if ( ( byte & 0xF8 ) = = 0xF0 ) {
if ( len > = 4 ) {
* cp = ( ( ( unsigned long ) ( buf [ 0 ] & 0x07 ) ) < < 18 ) | ( ( ( unsigned long ) ( buf [ 1 ] & 0x3F ) ) < < 12 ) |
( ( ( unsigned long ) ( buf [ 2 ] & 0x3F ) ) < < 6 ) | ( ( unsigned long ) ( buf [ 3 ] & 0x3F ) ) ;
return 4 ;
}
}
}
return 0 ;
}
/* Check if the code is a wide character */
static const unsigned long wideCharTable [ ] [ 2 ] = {
/* BEGIN: WIDE CHAR TABLE */
{ 0x1100 , 0x115F } ,
{ 0x231A , 0x231B } ,
{ 0x2329 , 0x232A } ,
{ 0x23E9 , 0x23EC } ,
{ 0x23F0 , 0x23F0 } ,
{ 0x23F3 , 0x23F3 } ,
{ 0x25FD , 0x25FE } ,
{ 0x2614 , 0x2615 } ,
{ 0x2630 , 0x2637 } ,
{ 0x2648 , 0x2653 } ,
{ 0x267F , 0x267F } ,
{ 0x268A , 0x268F } ,
{ 0x2693 , 0x2693 } ,
{ 0x26A1 , 0x26A1 } ,
{ 0x26AA , 0x26AB } ,
{ 0x26BD , 0x26BE } ,
{ 0x26C4 , 0x26C5 } ,
{ 0x26CE , 0x26CE } ,
{ 0x26D4 , 0x26D4 } ,
{ 0x26EA , 0x26EA } ,
{ 0x26F2 , 0x26F3 } ,
{ 0x26F5 , 0x26F5 } ,
{ 0x26FA , 0x26FA } ,
{ 0x26FD , 0x26FD } ,
{ 0x2705 , 0x2705 } ,
{ 0x270A , 0x270B } ,
{ 0x2728 , 0x2728 } ,
{ 0x274C , 0x274C } ,
{ 0x274E , 0x274E } ,
{ 0x2753 , 0x2755 } ,
{ 0x2757 , 0x2757 } ,
{ 0x2795 , 0x2797 } ,
{ 0x27B0 , 0x27B0 } ,
{ 0x27BF , 0x27BF } ,
{ 0x2B1B , 0x2B1C } ,
{ 0x2B50 , 0x2B50 } ,
{ 0x2B55 , 0x2B55 } ,
{ 0x2E80 , 0x2E99 } ,
{ 0x2E9B , 0x2EF3 } ,
{ 0x2F00 , 0x2FD5 } ,
{ 0x2FF0 , 0x303E } ,
{ 0x3041 , 0x3096 } ,
{ 0x3099 , 0x30FF } ,
{ 0x3105 , 0x312F } ,
{ 0x3131 , 0x318E } ,
{ 0x3190 , 0x31E5 } ,
{ 0x31EF , 0x321E } ,
{ 0x3220 , 0x3247 } ,
{ 0x3250 , 0xA48C } ,
{ 0xA490 , 0xA4C6 } ,
{ 0xA960 , 0xA97C } ,
{ 0xAC00 , 0xD7A3 } ,
{ 0xF900 , 0xFAFF } ,
{ 0xFE10 , 0xFE19 } ,
{ 0xFE30 , 0xFE52 } ,
{ 0xFE54 , 0xFE66 } ,
{ 0xFE68 , 0xFE6B } ,
{ 0xFF01 , 0xFF60 } ,
{ 0xFFE0 , 0xFFE6 } ,
{ 0x16FE0 , 0x16FE4 } ,
{ 0x16FF0 , 0x16FF1 } ,
{ 0x17000 , 0x187F7 } ,
{ 0x18800 , 0x18CD5 } ,
{ 0x18CFF , 0x18D08 } ,
{ 0x1AFF0 , 0x1AFF3 } ,
{ 0x1AFF5 , 0x1AFFB } ,
{ 0x1AFFD , 0x1AFFE } ,
{ 0x1B000 , 0x1B122 } ,
{ 0x1B132 , 0x1B132 } ,
{ 0x1B150 , 0x1B152 } ,
{ 0x1B155 , 0x1B155 } ,
{ 0x1B164 , 0x1B167 } ,
{ 0x1B170 , 0x1B2FB } ,
{ 0x1D300 , 0x1D356 } ,
{ 0x1D360 , 0x1D376 } ,
{ 0x1F004 , 0x1F004 } ,
{ 0x1F0CF , 0x1F0CF } ,
{ 0x1F18E , 0x1F18E } ,
{ 0x1F191 , 0x1F19A } ,
{ 0x1F200 , 0x1F202 } ,
{ 0x1F210 , 0x1F23B } ,
{ 0x1F240 , 0x1F248 } ,
{ 0x1F250 , 0x1F251 } ,
{ 0x1F260 , 0x1F265 } ,
{ 0x1F300 , 0x1F320 } ,
{ 0x1F32D , 0x1F335 } ,
{ 0x1F337 , 0x1F37C } ,
{ 0x1F37E , 0x1F393 } ,
{ 0x1F3A0 , 0x1F3CA } ,
{ 0x1F3CF , 0x1F3D3 } ,
{ 0x1F3E0 , 0x1F3F0 } ,
{ 0x1F3F4 , 0x1F3F4 } ,
{ 0x1F3F8 , 0x1F43E } ,
{ 0x1F440 , 0x1F440 } ,
{ 0x1F442 , 0x1F4FC } ,
{ 0x1F4FF , 0x1F53D } ,
{ 0x1F54B , 0x1F54E } ,
{ 0x1F550 , 0x1F567 } ,
{ 0x1F57A , 0x1F57A } ,
{ 0x1F595 , 0x1F596 } ,
{ 0x1F5A4 , 0x1F5A4 } ,
{ 0x1F5FB , 0x1F64F } ,
{ 0x1F680 , 0x1F6C5 } ,
{ 0x1F6CC , 0x1F6CC } ,
{ 0x1F6D0 , 0x1F6D2 } ,
{ 0x1F6D5 , 0x1F6D7 } ,
{ 0x1F6DC , 0x1F6DF } ,
{ 0x1F6EB , 0x1F6EC } ,
{ 0x1F6F4 , 0x1F6FC } ,
{ 0x1F7E0 , 0x1F7EB } ,
{ 0x1F7F0 , 0x1F7F0 } ,
{ 0x1F90C , 0x1F93A } ,
{ 0x1F93C , 0x1F945 } ,
{ 0x1F947 , 0x1F9FF } ,
{ 0x1FA70 , 0x1FA7C } ,
{ 0x1FA80 , 0x1FA89 } ,
{ 0x1FA8F , 0x1FAC6 } ,
{ 0x1FACE , 0x1FADC } ,
{ 0x1FADF , 0x1FAE9 } ,
{ 0x1FAF0 , 0x1FAF8 } ,
{ 0x20000 , 0x2FFFD } ,
{ 0x30000 , 0x3FFFD }
/* END: WIDE CHAR TABLE */
} ;
static const size_t wideCharTableSize = sizeof ( wideCharTable ) / sizeof ( wideCharTable [ 0 ] ) ;
static bool isWideChar ( unsigned long cp ) {
for ( size_t i = 0 ; i < wideCharTableSize ; i + + ) {
auto first_code = wideCharTable [ i ] [ 0 ] ;
auto last_code = wideCharTable [ i ] [ 1 ] ;
if ( first_code > cp ) {
return false ;
}
if ( first_code < = cp & & cp < = last_code ) {
return true ;
}
}
return false ;
}
/* Check if the code is a combining character */
static const unsigned long combiningCharTable [ ] = {
/* BEGIN: COMBINING CHAR TABLE */
0x0300 , 0x0301 , 0x0302 , 0x0303 , 0x0304 , 0x0305 , 0x0306 , 0x0307 , 0x0308 , 0x0309 , 0x030A , 0x030B , 0x030C ,
0x030D , 0x030E , 0x030F , 0x0310 , 0x0311 , 0x0312 , 0x0313 , 0x0314 , 0x0315 , 0x0316 , 0x0317 , 0x0318 , 0x0319 ,
0x031A , 0x031B , 0x031C , 0x031D , 0x031E , 0x031F , 0x0320 , 0x0321 , 0x0322 , 0x0323 , 0x0324 , 0x0325 , 0x0326 ,
0x0327 , 0x0328 , 0x0329 , 0x032A , 0x032B , 0x032C , 0x032D , 0x032E , 0x032F , 0x0330 , 0x0331 , 0x0332 , 0x0333 ,
0x0334 , 0x0335 , 0x0336 , 0x0337 , 0x0338 , 0x0339 , 0x033A , 0x033B , 0x033C , 0x033D , 0x033E , 0x033F , 0x0340 ,
0x0341 , 0x0342 , 0x0343 , 0x0344 , 0x0345 , 0x0346 , 0x0347 , 0x0348 , 0x0349 , 0x034A , 0x034B , 0x034C , 0x034D ,
0x034E , 0x034F , 0x0350 , 0x0351 , 0x0352 , 0x0353 , 0x0354 , 0x0355 , 0x0356 , 0x0357 , 0x0358 , 0x0359 , 0x035A ,
0x035B , 0x035C , 0x035D , 0x035E , 0x035F , 0x0360 , 0x0361 , 0x0362 , 0x0363 , 0x0364 , 0x0365 , 0x0366 , 0x0367 ,
0x0368 , 0x0369 , 0x036A , 0x036B , 0x036C , 0x036D , 0x036E , 0x036F , 0x0483 , 0x0484 , 0x0485 , 0x0486 , 0x0487 ,
0x0591 , 0x0592 , 0x0593 , 0x0594 , 0x0595 , 0x0596 , 0x0597 , 0x0598 , 0x0599 , 0x059A , 0x059B , 0x059C , 0x059D ,
0x059E , 0x059F , 0x05A0 , 0x05A1 , 0x05A2 , 0x05A3 , 0x05A4 , 0x05A5 , 0x05A6 , 0x05A7 , 0x05A8 , 0x05A9 , 0x05AA ,
0x05AB , 0x05AC , 0x05AD , 0x05AE , 0x05AF , 0x05B0 , 0x05B1 , 0x05B2 , 0x05B3 , 0x05B4 , 0x05B5 , 0x05B6 , 0x05B7 ,
0x05B8 , 0x05B9 , 0x05BA , 0x05BB , 0x05BC , 0x05BD , 0x05BF , 0x05C1 , 0x05C2 , 0x05C4 , 0x05C5 , 0x05C7 , 0x0610 ,
0x0611 , 0x0612 , 0x0613 , 0x0614 , 0x0615 , 0x0616 , 0x0617 , 0x0618 , 0x0619 , 0x061A , 0x064B , 0x064C , 0x064D ,
0x064E , 0x064F , 0x0650 , 0x0651 , 0x0652 , 0x0653 , 0x0654 , 0x0655 , 0x0656 , 0x0657 , 0x0658 , 0x0659 , 0x065A ,
0x065B , 0x065C , 0x065D , 0x065E , 0x065F , 0x0670 , 0x06D6 , 0x06D7 , 0x06D8 , 0x06D9 , 0x06DA , 0x06DB , 0x06DC ,
0x06DF , 0x06E0 , 0x06E1 , 0x06E2 , 0x06E3 , 0x06E4 , 0x06E7 , 0x06E8 , 0x06EA , 0x06EB , 0x06EC , 0x06ED , 0x0711 ,
0x0730 , 0x0731 , 0x0732 , 0x0733 , 0x0734 , 0x0735 , 0x0736 , 0x0737 , 0x0738 , 0x0739 , 0x073A , 0x073B , 0x073C ,
0x073D , 0x073E , 0x073F , 0x0740 , 0x0741 , 0x0742 , 0x0743 , 0x0744 , 0x0745 , 0x0746 , 0x0747 , 0x0748 , 0x0749 ,
0x074A , 0x07A6 , 0x07A7 , 0x07A8 , 0x07A9 , 0x07AA , 0x07AB , 0x07AC , 0x07AD , 0x07AE , 0x07AF , 0x07B0 , 0x07EB ,
0x07EC , 0x07ED , 0x07EE , 0x07EF , 0x07F0 , 0x07F1 , 0x07F2 , 0x07F3 , 0x07FD , 0x0816 , 0x0817 , 0x0818 , 0x0819 ,
0x081B , 0x081C , 0x081D , 0x081E , 0x081F , 0x0820 , 0x0821 , 0x0822 , 0x0823 , 0x0825 , 0x0826 , 0x0827 , 0x0829 ,
0x082A , 0x082B , 0x082C , 0x082D , 0x0859 , 0x085A , 0x085B , 0x0897 , 0x0898 , 0x0899 , 0x089A , 0x089B , 0x089C ,
0x089D , 0x089E , 0x089F , 0x08CA , 0x08CB , 0x08CC , 0x08CD , 0x08CE , 0x08CF , 0x08D0 , 0x08D1 , 0x08D2 , 0x08D3 ,
0x08D4 , 0x08D5 , 0x08D6 , 0x08D7 , 0x08D8 , 0x08D9 , 0x08DA , 0x08DB , 0x08DC , 0x08DD , 0x08DE , 0x08DF , 0x08E0 ,
0x08E1 , 0x08E3 , 0x08E4 , 0x08E5 , 0x08E6 , 0x08E7 , 0x08E8 , 0x08E9 , 0x08EA , 0x08EB , 0x08EC , 0x08ED , 0x08EE ,
0x08EF , 0x08F0 , 0x08F1 , 0x08F2 , 0x08F3 , 0x08F4 , 0x08F5 , 0x08F6 , 0x08F7 , 0x08F8 , 0x08F9 , 0x08FA , 0x08FB ,
0x08FC , 0x08FD , 0x08FE , 0x08FF , 0x0900 , 0x0901 , 0x0902 , 0x093A , 0x093C , 0x0941 , 0x0942 , 0x0943 , 0x0944 ,
0x0945 , 0x0946 , 0x0947 , 0x0948 , 0x094D , 0x0951 , 0x0952 , 0x0953 , 0x0954 , 0x0955 , 0x0956 , 0x0957 , 0x0962 ,
0x0963 , 0x0981 , 0x09BC , 0x09C1 , 0x09C2 , 0x09C3 , 0x09C4 , 0x09CD , 0x09E2 , 0x09E3 , 0x09FE , 0x0A01 , 0x0A02 ,
0x0A3C , 0x0A41 , 0x0A42 , 0x0A47 , 0x0A48 , 0x0A4B , 0x0A4C , 0x0A4D , 0x0A51 , 0x0A70 , 0x0A71 , 0x0A75 , 0x0A81 ,
0x0A82 , 0x0ABC , 0x0AC1 , 0x0AC2 , 0x0AC3 , 0x0AC4 , 0x0AC5 , 0x0AC7 , 0x0AC8 , 0x0ACD , 0x0AE2 , 0x0AE3 , 0x0AFA ,
0x0AFB , 0x0AFC , 0x0AFD , 0x0AFE , 0x0AFF , 0x0B01 , 0x0B3C , 0x0B3F , 0x0B41 , 0x0B42 , 0x0B43 , 0x0B44 , 0x0B4D ,
0x0B55 , 0x0B56 , 0x0B62 , 0x0B63 , 0x0B82 , 0x0BC0 , 0x0BCD , 0x0C00 , 0x0C04 , 0x0C3C , 0x0C3E , 0x0C3F , 0x0C40 ,
0x0C46 , 0x0C47 , 0x0C48 , 0x0C4A , 0x0C4B , 0x0C4C , 0x0C4D , 0x0C55 , 0x0C56 , 0x0C62 , 0x0C63 , 0x0C81 , 0x0CBC ,
0x0CBF , 0x0CC6 , 0x0CCC , 0x0CCD , 0x0CE2 , 0x0CE3 , 0x0D00 , 0x0D01 , 0x0D3B , 0x0D3C , 0x0D41 , 0x0D42 , 0x0D43 ,
0x0D44 , 0x0D4D , 0x0D62 , 0x0D63 , 0x0D81 , 0x0DCA , 0x0DD2 , 0x0DD3 , 0x0DD4 , 0x0DD6 , 0x0E31 , 0x0E34 , 0x0E35 ,
0x0E36 , 0x0E37 , 0x0E38 , 0x0E39 , 0x0E3A , 0x0E47 , 0x0E48 , 0x0E49 , 0x0E4A , 0x0E4B , 0x0E4C , 0x0E4D , 0x0E4E ,
0x0EB1 , 0x0EB4 , 0x0EB5 , 0x0EB6 , 0x0EB7 , 0x0EB8 , 0x0EB9 , 0x0EBA , 0x0EBB , 0x0EBC , 0x0EC8 , 0x0EC9 , 0x0ECA ,
0x0ECB , 0x0ECC , 0x0ECD , 0x0ECE , 0x0F18 , 0x0F19 , 0x0F35 , 0x0F37 , 0x0F39 , 0x0F71 , 0x0F72 , 0x0F73 , 0x0F74 ,
0x0F75 , 0x0F76 , 0x0F77 , 0x0F78 , 0x0F79 , 0x0F7A , 0x0F7B , 0x0F7C , 0x0F7D , 0x0F7E , 0x0F80 , 0x0F81 , 0x0F82 ,
0x0F83 , 0x0F84 , 0x0F86 , 0x0F87 , 0x0F8D , 0x0F8E , 0x0F8F , 0x0F90 , 0x0F91 , 0x0F92 , 0x0F93 , 0x0F94 , 0x0F95 ,
0x0F96 , 0x0F97 , 0x0F99 , 0x0F9A , 0x0F9B , 0x0F9C , 0x0F9D , 0x0F9E , 0x0F9F , 0x0FA0 , 0x0FA1 , 0x0FA2 , 0x0FA3 ,
0x0FA4 , 0x0FA5 , 0x0FA6 , 0x0FA7 , 0x0FA8 , 0x0FA9 , 0x0FAA , 0x0FAB , 0x0FAC , 0x0FAD , 0x0FAE , 0x0FAF , 0x0FB0 ,
0x0FB1 , 0x0FB2 , 0x0FB3 , 0x0FB4 , 0x0FB5 , 0x0FB6 , 0x0FB7 , 0x0FB8 , 0x0FB9 , 0x0FBA , 0x0FBB , 0x0FBC , 0x0FC6 ,
0x102D , 0x102E , 0x102F , 0x1030 , 0x1032 , 0x1033 , 0x1034 , 0x1035 , 0x1036 , 0x1037 , 0x1039 , 0x103A , 0x103D ,
0x103E , 0x1058 , 0x1059 , 0x105E , 0x105F , 0x1060 , 0x1071 , 0x1072 , 0x1073 , 0x1074 , 0x1082 , 0x1085 , 0x1086 ,
0x108D , 0x109D , 0x135D , 0x135E , 0x135F , 0x1712 , 0x1713 , 0x1714 , 0x1732 , 0x1733 , 0x1752 , 0x1753 , 0x1772 ,
0x1773 , 0x17B4 , 0x17B5 , 0x17B7 , 0x17B8 , 0x17B9 , 0x17BA , 0x17BB , 0x17BC , 0x17BD , 0x17C6 , 0x17C9 , 0x17CA ,
0x17CB , 0x17CC , 0x17CD , 0x17CE , 0x17CF , 0x17D0 , 0x17D1 , 0x17D2 , 0x17D3 , 0x17DD , 0x180B , 0x180C , 0x180D ,
0x180F , 0x1885 , 0x1886 , 0x18A9 , 0x1920 , 0x1921 , 0x1922 , 0x1927 , 0x1928 , 0x1932 , 0x1939 , 0x193A , 0x193B ,
0x1A17 , 0x1A18 , 0x1A1B , 0x1A56 , 0x1A58 , 0x1A59 , 0x1A5A , 0x1A5B , 0x1A5C , 0x1A5D , 0x1A5E , 0x1A60 , 0x1A62 ,
0x1A65 , 0x1A66 , 0x1A67 , 0x1A68 , 0x1A69 , 0x1A6A , 0x1A6B , 0x1A6C , 0x1A73 , 0x1A74 , 0x1A75 , 0x1A76 , 0x1A77 ,
0x1A78 , 0x1A79 , 0x1A7A , 0x1A7B , 0x1A7C , 0x1A7F , 0x1AB0 , 0x1AB1 , 0x1AB2 , 0x1AB3 , 0x1AB4 , 0x1AB5 , 0x1AB6 ,
0x1AB7 , 0x1AB8 , 0x1AB9 , 0x1ABA , 0x1ABB , 0x1ABC , 0x1ABD , 0x1ABF , 0x1AC0 , 0x1AC1 , 0x1AC2 , 0x1AC3 , 0x1AC4 ,
0x1AC5 , 0x1AC6 , 0x1AC7 , 0x1AC8 , 0x1AC9 , 0x1ACA , 0x1ACB , 0x1ACC , 0x1ACD , 0x1ACE , 0x1B00 , 0x1B01 , 0x1B02 ,
0x1B03 , 0x1B34 , 0x1B36 , 0x1B37 , 0x1B38 , 0x1B39 , 0x1B3A , 0x1B3C , 0x1B42 , 0x1B6B , 0x1B6C , 0x1B6D , 0x1B6E ,
0x1B6F , 0x1B70 , 0x1B71 , 0x1B72 , 0x1B73 , 0x1B80 , 0x1B81 , 0x1BA2 , 0x1BA3 , 0x1BA4 , 0x1BA5 , 0x1BA8 , 0x1BA9 ,
0x1BAB , 0x1BAC , 0x1BAD , 0x1BE6 , 0x1BE8 , 0x1BE9 , 0x1BED , 0x1BEF , 0x1BF0 , 0x1BF1 , 0x1C2C , 0x1C2D , 0x1C2E ,
0x1C2F , 0x1C30 , 0x1C31 , 0x1C32 , 0x1C33 , 0x1C36 , 0x1C37 , 0x1CD0 , 0x1CD1 , 0x1CD2 , 0x1CD4 , 0x1CD5 , 0x1CD6 ,
0x1CD7 , 0x1CD8 , 0x1CD9 , 0x1CDA , 0x1CDB , 0x1CDC , 0x1CDD , 0x1CDE , 0x1CDF , 0x1CE0 , 0x1CE2 , 0x1CE3 , 0x1CE4 ,
0x1CE5 , 0x1CE6 , 0x1CE7 , 0x1CE8 , 0x1CED , 0x1CF4 , 0x1CF8 , 0x1CF9 , 0x1DC0 , 0x1DC1 , 0x1DC2 , 0x1DC3 , 0x1DC4 ,
0x1DC5 , 0x1DC6 , 0x1DC7 , 0x1DC8 , 0x1DC9 , 0x1DCA , 0x1DCB , 0x1DCC , 0x1DCD , 0x1DCE , 0x1DCF , 0x1DD0 , 0x1DD1 ,
0x1DD2 , 0x1DD3 , 0x1DD4 , 0x1DD5 , 0x1DD6 , 0x1DD7 , 0x1DD8 , 0x1DD9 , 0x1DDA , 0x1DDB , 0x1DDC , 0x1DDD , 0x1DDE ,
0x1DDF , 0x1DE0 , 0x1DE1 , 0x1DE2 , 0x1DE3 , 0x1DE4 , 0x1DE5 , 0x1DE6 , 0x1DE7 , 0x1DE8 , 0x1DE9 , 0x1DEA , 0x1DEB ,
0x1DEC , 0x1DED , 0x1DEE , 0x1DEF , 0x1DF0 , 0x1DF1 , 0x1DF2 , 0x1DF3 , 0x1DF4 , 0x1DF5 , 0x1DF6 , 0x1DF7 , 0x1DF8 ,
0x1DF9 , 0x1DFA , 0x1DFB , 0x1DFC , 0x1DFD , 0x1DFE , 0x1DFF , 0x20D0 , 0x20D1 , 0x20D2 , 0x20D3 , 0x20D4 , 0x20D5 ,
0x20D6 , 0x20D7 , 0x20D8 , 0x20D9 , 0x20DA , 0x20DB , 0x20DC , 0x20E1 , 0x20E5 , 0x20E6 , 0x20E7 , 0x20E8 , 0x20E9 ,
0x20EA , 0x20EB , 0x20EC , 0x20ED , 0x20EE , 0x20EF , 0x20F0 , 0x2CEF , 0x2CF0 , 0x2CF1 , 0x2D7F , 0x2DE0 , 0x2DE1 ,
0x2DE2 , 0x2DE3 , 0x2DE4 , 0x2DE5 , 0x2DE6 , 0x2DE7 , 0x2DE8 , 0x2DE9 , 0x2DEA , 0x2DEB , 0x2DEC , 0x2DED , 0x2DEE ,
0x2DEF , 0x2DF0 , 0x2DF1 , 0x2DF2 , 0x2DF3 , 0x2DF4 , 0x2DF5 , 0x2DF6 , 0x2DF7 , 0x2DF8 , 0x2DF9 , 0x2DFA , 0x2DFB ,
0x2DFC , 0x2DFD , 0x2DFE , 0x2DFF , 0x302A , 0x302B , 0x302C , 0x302D , 0x3099 , 0x309A , 0xA66F , 0xA674 , 0xA675 ,
0xA676 , 0xA677 , 0xA678 , 0xA679 , 0xA67A , 0xA67B , 0xA67C , 0xA67D , 0xA69E , 0xA69F , 0xA6F0 , 0xA6F1 , 0xA802 ,
0xA806 , 0xA80B , 0xA825 , 0xA826 , 0xA82C , 0xA8C4 , 0xA8C5 , 0xA8E0 , 0xA8E1 , 0xA8E2 , 0xA8E3 , 0xA8E4 , 0xA8E5 ,
0xA8E6 , 0xA8E7 , 0xA8E8 , 0xA8E9 , 0xA8EA , 0xA8EB , 0xA8EC , 0xA8ED , 0xA8EE , 0xA8EF , 0xA8F0 , 0xA8F1 , 0xA8FF ,
0xA926 , 0xA927 , 0xA928 , 0xA929 , 0xA92A , 0xA92B , 0xA92C , 0xA92D , 0xA947 , 0xA948 , 0xA949 , 0xA94A , 0xA94B ,
0xA94C , 0xA94D , 0xA94E , 0xA94F , 0xA950 , 0xA951 , 0xA980 , 0xA981 , 0xA982 , 0xA9B3 , 0xA9B6 , 0xA9B7 , 0xA9B8 ,
0xA9B9 , 0xA9BC , 0xA9BD , 0xA9E5 , 0xAA29 , 0xAA2A , 0xAA2B , 0xAA2C , 0xAA2D , 0xAA2E , 0xAA31 , 0xAA32 , 0xAA35 ,
0xAA36 , 0xAA43 , 0xAA4C , 0xAA7C , 0xAAB0 , 0xAAB2 , 0xAAB3 , 0xAAB4 , 0xAAB7 , 0xAAB8 , 0xAABE , 0xAABF , 0xAAC1 ,
0xAAEC , 0xAAED , 0xAAF6 , 0xABE5 , 0xABE8 , 0xABED , 0xFB1E , 0xFE00 , 0xFE01 , 0xFE02 , 0xFE03 , 0xFE04 , 0xFE05 ,
0xFE06 , 0xFE07 , 0xFE08 , 0xFE09 , 0xFE0A , 0xFE0B , 0xFE0C , 0xFE0D , 0xFE0E , 0xFE0F , 0xFE20 , 0xFE21 , 0xFE22 ,
0xFE23 , 0xFE24 , 0xFE25 , 0xFE26 , 0xFE27 , 0xFE28 , 0xFE29 , 0xFE2A , 0xFE2B , 0xFE2C , 0xFE2D , 0xFE2E , 0xFE2F ,
0x101FD , 0x102E0 , 0x10376 , 0x10377 , 0x10378 , 0x10379 , 0x1037A , 0x10A01 , 0x10A02 , 0x10A03 , 0x10A05 , 0x10A06 , 0x10A0C ,
0x10A0D , 0x10A0E , 0x10A0F , 0x10A38 , 0x10A39 , 0x10A3A , 0x10A3F , 0x10AE5 , 0x10AE6 , 0x10D24 , 0x10D25 , 0x10D26 , 0x10D27 ,
0x10D69 , 0x10D6A , 0x10D6B , 0x10D6C , 0x10D6D , 0x10EAB , 0x10EAC , 0x10EFC , 0x10EFD , 0x10EFE , 0x10EFF , 0x10F46 , 0x10F47 ,
0x10F48 , 0x10F49 , 0x10F4A , 0x10F4B , 0x10F4C , 0x10F4D , 0x10F4E , 0x10F4F , 0x10F50 , 0x10F82 , 0x10F83 , 0x10F84 , 0x10F85 ,
0x11001 , 0x11038 , 0x11039 , 0x1103A , 0x1103B , 0x1103C , 0x1103D , 0x1103E , 0x1103F , 0x11040 , 0x11041 , 0x11042 , 0x11043 ,
0x11044 , 0x11045 , 0x11046 , 0x11070 , 0x11073 , 0x11074 , 0x1107F , 0x11080 , 0x11081 , 0x110B3 , 0x110B4 , 0x110B5 , 0x110B6 ,
0x110B9 , 0x110BA , 0x110C2 , 0x11100 , 0x11101 , 0x11102 , 0x11127 , 0x11128 , 0x11129 , 0x1112A , 0x1112B , 0x1112D , 0x1112E ,
0x1112F , 0x11130 , 0x11131 , 0x11132 , 0x11133 , 0x11134 , 0x11173 , 0x11180 , 0x11181 , 0x111B6 , 0x111B7 , 0x111B8 , 0x111B9 ,
0x111BA , 0x111BB , 0x111BC , 0x111BD , 0x111BE , 0x111C9 , 0x111CA , 0x111CB , 0x111CC , 0x111CF , 0x1122F , 0x11230 , 0x11231 ,
0x11234 , 0x11236 , 0x11237 , 0x1123E , 0x11241 , 0x112DF , 0x112E3 , 0x112E4 , 0x112E5 , 0x112E6 , 0x112E7 , 0x112E8 , 0x112E9 ,
0x112EA , 0x11300 , 0x11301 , 0x1133B , 0x1133C , 0x11340 , 0x11366 , 0x11367 , 0x11368 , 0x11369 , 0x1136A , 0x1136B , 0x1136C ,
0x11370 , 0x11371 , 0x11372 , 0x11373 , 0x11374 , 0x113BB , 0x113BC , 0x113BD , 0x113BE , 0x113BF , 0x113C0 , 0x113CE , 0x113D0 ,
0x113D2 , 0x113E1 , 0x113E2 , 0x11438 , 0x11439 , 0x1143A , 0x1143B , 0x1143C , 0x1143D , 0x1143E , 0x1143F , 0x11442 , 0x11443 ,
0x11444 , 0x11446 , 0x1145E , 0x114B3 , 0x114B4 , 0x114B5 , 0x114B6 , 0x114B7 , 0x114B8 , 0x114BA , 0x114BF , 0x114C0 , 0x114C2 ,
0x114C3 , 0x115B2 , 0x115B3 , 0x115B4 , 0x115B5 , 0x115BC , 0x115BD , 0x115BF , 0x115C0 , 0x115DC , 0x115DD , 0x11633 , 0x11634 ,
0x11635 , 0x11636 , 0x11637 , 0x11638 , 0x11639 , 0x1163A , 0x1163D , 0x1163F , 0x11640 , 0x116AB , 0x116AD , 0x116B0 , 0x116B1 ,
0x116B2 , 0x116B3 , 0x116B4 , 0x116B5 , 0x116B7 , 0x1171D , 0x1171F , 0x11722 , 0x11723 , 0x11724 , 0x11725 , 0x11727 , 0x11728 ,
0x11729 , 0x1172A , 0x1172B , 0x1182F , 0x11830 , 0x11831 , 0x11832 , 0x11833 , 0x11834 , 0x11835 , 0x11836 , 0x11837 , 0x11839 ,
0x1183A , 0x1193B , 0x1193C , 0x1193E , 0x11943 , 0x119D4 , 0x119D5 , 0x119D6 , 0x119D7 , 0x119DA , 0x119DB , 0x119E0 , 0x11A01 ,
0x11A02 , 0x11A03 , 0x11A04 , 0x11A05 , 0x11A06 , 0x11A07 , 0x11A08 , 0x11A09 , 0x11A0A , 0x11A33 , 0x11A34 , 0x11A35 , 0x11A36 ,
0x11A37 , 0x11A38 , 0x11A3B , 0x11A3C , 0x11A3D , 0x11A3E , 0x11A47 , 0x11A51 , 0x11A52 , 0x11A53 , 0x11A54 , 0x11A55 , 0x11A56 ,
0x11A59 , 0x11A5A , 0x11A5B , 0x11A8A , 0x11A8B , 0x11A8C , 0x11A8D , 0x11A8E , 0x11A8F , 0x11A90 , 0x11A91 , 0x11A92 , 0x11A93 ,
0x11A94 , 0x11A95 , 0x11A96 , 0x11A98 , 0x11A99 , 0x11C30 , 0x11C31 , 0x11C32 , 0x11C33 , 0x11C34 , 0x11C35 , 0x11C36 , 0x11C38 ,
0x11C39 , 0x11C3A , 0x11C3B , 0x11C3C , 0x11C3D , 0x11C3F , 0x11C92 , 0x11C93 , 0x11C94 , 0x11C95 , 0x11C96 , 0x11C97 , 0x11C98 ,
0x11C99 , 0x11C9A , 0x11C9B , 0x11C9C , 0x11C9D , 0x11C9E , 0x11C9F , 0x11CA0 , 0x11CA1 , 0x11CA2 , 0x11CA3 , 0x11CA4 , 0x11CA5 ,
0x11CA6 , 0x11CA7 , 0x11CAA , 0x11CAB , 0x11CAC , 0x11CAD , 0x11CAE , 0x11CAF , 0x11CB0 , 0x11CB2 , 0x11CB3 , 0x11CB5 , 0x11CB6 ,
0x11D31 , 0x11D32 , 0x11D33 , 0x11D34 , 0x11D35 , 0x11D36 , 0x11D3A , 0x11D3C , 0x11D3D , 0x11D3F , 0x11D40 , 0x11D41 , 0x11D42 ,
0x11D43 , 0x11D44 , 0x11D45 , 0x11D47 , 0x11D90 , 0x11D91 , 0x11D95 , 0x11D97 , 0x11EF3 , 0x11EF4 , 0x11F00 , 0x11F01 , 0x11F36 ,
0x11F37 , 0x11F38 , 0x11F39 , 0x11F3A , 0x11F40 , 0x11F42 , 0x11F5A , 0x13440 , 0x13447 , 0x13448 , 0x13449 , 0x1344A , 0x1344B ,
0x1344C , 0x1344D , 0x1344E , 0x1344F , 0x13450 , 0x13451 , 0x13452 , 0x13453 , 0x13454 , 0x13455 , 0x1611E , 0x1611F , 0x16120 ,
0x16121 , 0x16122 , 0x16123 , 0x16124 , 0x16125 , 0x16126 , 0x16127 , 0x16128 , 0x16129 , 0x1612D , 0x1612E , 0x1612F , 0x16AF0 ,
0x16AF1 , 0x16AF2 , 0x16AF3 , 0x16AF4 , 0x16B30 , 0x16B31 , 0x16B32 , 0x16B33 , 0x16B34 , 0x16B35 , 0x16B36 , 0x16F4F , 0x16F8F ,
0x16F90 , 0x16F91 , 0x16F92 , 0x16FE4 , 0x1BC9D , 0x1BC9E , 0x1CF00 , 0x1CF01 , 0x1CF02 , 0x1CF03 , 0x1CF04 , 0x1CF05 , 0x1CF06 ,
0x1CF07 , 0x1CF08 , 0x1CF09 , 0x1CF0A , 0x1CF0B , 0x1CF0C , 0x1CF0D , 0x1CF0E , 0x1CF0F , 0x1CF10 , 0x1CF11 , 0x1CF12 , 0x1CF13 ,
0x1CF14 , 0x1CF15 , 0x1CF16 , 0x1CF17 , 0x1CF18 , 0x1CF19 , 0x1CF1A , 0x1CF1B , 0x1CF1C , 0x1CF1D , 0x1CF1E , 0x1CF1F , 0x1CF20 ,
0x1CF21 , 0x1CF22 , 0x1CF23 , 0x1CF24 , 0x1CF25 , 0x1CF26 , 0x1CF27 , 0x1CF28 , 0x1CF29 , 0x1CF2A , 0x1CF2B , 0x1CF2C , 0x1CF2D ,
0x1CF30 , 0x1CF31 , 0x1CF32 , 0x1CF33 , 0x1CF34 , 0x1CF35 , 0x1CF36 , 0x1CF37 , 0x1CF38 , 0x1CF39 , 0x1CF3A , 0x1CF3B , 0x1CF3C ,
0x1CF3D , 0x1CF3E , 0x1CF3F , 0x1CF40 , 0x1CF41 , 0x1CF42 , 0x1CF43 , 0x1CF44 , 0x1CF45 , 0x1CF46 , 0x1D167 , 0x1D168 , 0x1D169 ,
0x1D17B , 0x1D17C , 0x1D17D , 0x1D17E , 0x1D17F , 0x1D180 , 0x1D181 , 0x1D182 , 0x1D185 , 0x1D186 , 0x1D187 , 0x1D188 , 0x1D189 ,
0x1D18A , 0x1D18B , 0x1D1AA , 0x1D1AB , 0x1D1AC , 0x1D1AD , 0x1D242 , 0x1D243 , 0x1D244 , 0x1DA00 , 0x1DA01 , 0x1DA02 , 0x1DA03 ,
0x1DA04 , 0x1DA05 , 0x1DA06 , 0x1DA07 , 0x1DA08 , 0x1DA09 , 0x1DA0A , 0x1DA0B , 0x1DA0C , 0x1DA0D , 0x1DA0E , 0x1DA0F , 0x1DA10 ,
0x1DA11 , 0x1DA12 , 0x1DA13 , 0x1DA14 , 0x1DA15 , 0x1DA16 , 0x1DA17 , 0x1DA18 , 0x1DA19 , 0x1DA1A , 0x1DA1B , 0x1DA1C , 0x1DA1D ,
0x1DA1E , 0x1DA1F , 0x1DA20 , 0x1DA21 , 0x1DA22 , 0x1DA23 , 0x1DA24 , 0x1DA25 , 0x1DA26 , 0x1DA27 , 0x1DA28 , 0x1DA29 , 0x1DA2A ,
0x1DA2B , 0x1DA2C , 0x1DA2D , 0x1DA2E , 0x1DA2F , 0x1DA30 , 0x1DA31 , 0x1DA32 , 0x1DA33 , 0x1DA34 , 0x1DA35 , 0x1DA36 , 0x1DA3B ,
0x1DA3C , 0x1DA3D , 0x1DA3E , 0x1DA3F , 0x1DA40 , 0x1DA41 , 0x1DA42 , 0x1DA43 , 0x1DA44 , 0x1DA45 , 0x1DA46 , 0x1DA47 , 0x1DA48 ,
0x1DA49 , 0x1DA4A , 0x1DA4B , 0x1DA4C , 0x1DA4D , 0x1DA4E , 0x1DA4F , 0x1DA50 , 0x1DA51 , 0x1DA52 , 0x1DA53 , 0x1DA54 , 0x1DA55 ,
0x1DA56 , 0x1DA57 , 0x1DA58 , 0x1DA59 , 0x1DA5A , 0x1DA5B , 0x1DA5C , 0x1DA5D , 0x1DA5E , 0x1DA5F , 0x1DA60 , 0x1DA61 , 0x1DA62 ,
0x1DA63 , 0x1DA64 , 0x1DA65 , 0x1DA66 , 0x1DA67 , 0x1DA68 , 0x1DA69 , 0x1DA6A , 0x1DA6B , 0x1DA6C , 0x1DA75 , 0x1DA84 , 0x1DA9B ,
0x1DA9C , 0x1DA9D , 0x1DA9E , 0x1DA9F , 0x1DAA1 , 0x1DAA2 , 0x1DAA3 , 0x1DAA4 , 0x1DAA5 , 0x1DAA6 , 0x1DAA7 , 0x1DAA8 , 0x1DAA9 ,
0x1DAAA , 0x1DAAB , 0x1DAAC , 0x1DAAD , 0x1DAAE , 0x1DAAF , 0x1E000 , 0x1E001 , 0x1E002 , 0x1E003 , 0x1E004 , 0x1E005 , 0x1E006 ,
0x1E008 , 0x1E009 , 0x1E00A , 0x1E00B , 0x1E00C , 0x1E00D , 0x1E00E , 0x1E00F , 0x1E010 , 0x1E011 , 0x1E012 , 0x1E013 , 0x1E014 ,
0x1E015 , 0x1E016 , 0x1E017 , 0x1E018 , 0x1E01B , 0x1E01C , 0x1E01D , 0x1E01E , 0x1E01F , 0x1E020 , 0x1E021 , 0x1E023 , 0x1E024 ,
0x1E026 , 0x1E027 , 0x1E028 , 0x1E029 , 0x1E02A , 0x1E08F , 0x1E130 , 0x1E131 , 0x1E132 , 0x1E133 , 0x1E134 , 0x1E135 , 0x1E136 ,
0x1E2AE , 0x1E2EC , 0x1E2ED , 0x1E2EE , 0x1E2EF , 0x1E4EC , 0x1E4ED , 0x1E4EE , 0x1E4EF , 0x1E5EE , 0x1E5EF , 0x1E8D0 , 0x1E8D1 ,
0x1E8D2 , 0x1E8D3 , 0x1E8D4 , 0x1E8D5 , 0x1E8D6 , 0x1E944 , 0x1E945 , 0x1E946 , 0x1E947 , 0x1E948 , 0x1E949 , 0x1E94A , 0xE0100 ,
0xE0101 , 0xE0102 , 0xE0103 , 0xE0104 , 0xE0105 , 0xE0106 , 0xE0107 , 0xE0108 , 0xE0109 , 0xE010A , 0xE010B , 0xE010C , 0xE010D ,
0xE010E , 0xE010F , 0xE0110 , 0xE0111 , 0xE0112 , 0xE0113 , 0xE0114 , 0xE0115 , 0xE0116 , 0xE0117 , 0xE0118 , 0xE0119 , 0xE011A ,
0xE011B , 0xE011C , 0xE011D , 0xE011E , 0xE011F , 0xE0120 , 0xE0121 , 0xE0122 , 0xE0123 , 0xE0124 , 0xE0125 , 0xE0126 , 0xE0127 ,
0xE0128 , 0xE0129 , 0xE012A , 0xE012B , 0xE012C , 0xE012D , 0xE012E , 0xE012F , 0xE0130 , 0xE0131 , 0xE0132 , 0xE0133 , 0xE0134 ,
0xE0135 , 0xE0136 , 0xE0137 , 0xE0138 , 0xE0139 , 0xE013A , 0xE013B , 0xE013C , 0xE013D , 0xE013E , 0xE013F , 0xE0140 , 0xE0141 ,
0xE0142 , 0xE0143 , 0xE0144 , 0xE0145 , 0xE0146 , 0xE0147 , 0xE0148 , 0xE0149 , 0xE014A , 0xE014B , 0xE014C , 0xE014D , 0xE014E ,
0xE014F , 0xE0150 , 0xE0151 , 0xE0152 , 0xE0153 , 0xE0154 , 0xE0155 , 0xE0156 , 0xE0157 , 0xE0158 , 0xE0159 , 0xE015A , 0xE015B ,
0xE015C , 0xE015D , 0xE015E , 0xE015F , 0xE0160 , 0xE0161 , 0xE0162 , 0xE0163 , 0xE0164 , 0xE0165 , 0xE0166 , 0xE0167 , 0xE0168 ,
0xE0169 , 0xE016A , 0xE016B , 0xE016C , 0xE016D , 0xE016E , 0xE016F , 0xE0170 , 0xE0171 , 0xE0172 , 0xE0173 , 0xE0174 , 0xE0175 ,
0xE0176 , 0xE0177 , 0xE0178 , 0xE0179 , 0xE017A , 0xE017B , 0xE017C , 0xE017D , 0xE017E , 0xE017F , 0xE0180 , 0xE0181 , 0xE0182 ,
0xE0183 , 0xE0184 , 0xE0185 , 0xE0186 , 0xE0187 , 0xE0188 , 0xE0189 , 0xE018A , 0xE018B , 0xE018C , 0xE018D , 0xE018E , 0xE018F ,
0xE0190 , 0xE0191 , 0xE0192 , 0xE0193 , 0xE0194 , 0xE0195 , 0xE0196 , 0xE0197 , 0xE0198 , 0xE0199 , 0xE019A , 0xE019B , 0xE019C ,
0xE019D , 0xE019E , 0xE019F , 0xE01A0 , 0xE01A1 , 0xE01A2 , 0xE01A3 , 0xE01A4 , 0xE01A5 , 0xE01A6 , 0xE01A7 , 0xE01A8 , 0xE01A9 ,
0xE01AA , 0xE01AB , 0xE01AC , 0xE01AD , 0xE01AE , 0xE01AF , 0xE01B0 , 0xE01B1 , 0xE01B2 , 0xE01B3 , 0xE01B4 , 0xE01B5 , 0xE01B6 ,
0xE01B7 , 0xE01B8 , 0xE01B9 , 0xE01BA , 0xE01BB , 0xE01BC , 0xE01BD , 0xE01BE , 0xE01BF , 0xE01C0 , 0xE01C1 , 0xE01C2 , 0xE01C3 ,
0xE01C4 , 0xE01C5 , 0xE01C6 , 0xE01C7 , 0xE01C8 , 0xE01C9 , 0xE01CA , 0xE01CB , 0xE01CC , 0xE01CD , 0xE01CE , 0xE01CF , 0xE01D0 ,
0xE01D1 , 0xE01D2 , 0xE01D3 , 0xE01D4 , 0xE01D5 , 0xE01D6 , 0xE01D7 , 0xE01D8 , 0xE01D9 , 0xE01DA , 0xE01DB , 0xE01DC , 0xE01DD ,
0xE01DE , 0xE01DF , 0xE01E0 , 0xE01E1 , 0xE01E2 , 0xE01E3 , 0xE01E4 , 0xE01E5 , 0xE01E6 , 0xE01E7 , 0xE01E8 , 0xE01E9 , 0xE01EA ,
0xE01EB , 0xE01EC , 0xE01ED , 0xE01EE , 0xE01EF
/* END: COMBINING CHAR TABLE */
} ;
static const unsigned long combiningCharTableSize = sizeof ( combiningCharTable ) / sizeof ( combiningCharTable [ 0 ] ) ;
static bool isCombiningChar ( unsigned long cp ) {
for ( size_t i = 0 ; i < combiningCharTableSize ; i + + ) {
auto code = combiningCharTable [ i ] ;
if ( code > cp ) {
return false ;
}
if ( code = = cp ) {
return true ;
}
}
return false ;
}
/* Get length of previous grapheme */
static size_t defaultPrevCharLen ( const char * buf , size_t /*buf_len*/ , size_t pos , size_t * col_len ) {
size_t end = pos ;
while ( pos > 0 ) {
size_t len = prevUtf8CodePointLen ( buf , pos ) ;
pos - = len ;
int cp ;
utf8BytesToCodePoint ( buf + pos , len , & cp ) ;
if ( ! isCombiningChar ( cp ) ) {
if ( col_len ! = NULL ) {
* col_len = isWideChar ( cp ) ? 2 : 1 ;
}
return end - pos ;
}
}
/* NOTREACHED */
return 0 ;
}
/* Get length of next grapheme */
static size_t defaultNextCharLen ( const char * buf , size_t buf_len , size_t pos , size_t * col_len ) {
size_t beg = pos ;
int cp ;
size_t len = utf8BytesToCodePoint ( buf + pos , buf_len - pos , & cp ) ;
if ( isCombiningChar ( cp ) ) {
/* NOTREACHED */
return 0 ;
}
if ( col_len ! = NULL ) {
* col_len = isWideChar ( cp ) ? 2 : 1 ;
}
pos + = len ;
while ( pos < buf_len ) {
int cp ;
len = utf8BytesToCodePoint ( buf + pos , buf_len - pos , & cp ) ;
if ( ! isCombiningChar ( cp ) ) {
return pos - beg ;
}
pos + = len ;
}
return pos - beg ;
}
/* Read a Unicode from file. */
static size_t defaultReadCode ( int fd , char * buf , size_t buf_len , int * cp ) {
if ( buf_len < 1 ) {
return - 1 ;
}
size_t nread = read ( fd , & buf [ 0 ] , 1 ) ;
if ( nread < = 0 ) {
return nread ;
}
unsigned char byte = buf [ 0 ] ;
if ( ( byte & 0x80 ) = = 0 ) {
;
} else if ( ( byte & 0xE0 ) = = 0xC0 ) {
if ( buf_len < 2 ) {
return - 1 ;
}
nread = read ( fd , & buf [ 1 ] , 1 ) ;
if ( nread < = 0 ) {
return nread ;
}
} else if ( ( byte & 0xF0 ) = = 0xE0 ) {
if ( buf_len < 3 ) {
return - 1 ;
}
nread = read ( fd , & buf [ 1 ] , 2 ) ;
if ( nread < = 0 ) {
return nread ;
}
} else if ( ( byte & 0xF8 ) = = 0xF0 ) {
if ( buf_len < 3 ) {
return - 1 ;
}
nread = read ( fd , & buf [ 1 ] , 3 ) ;
if ( nread < = 0 ) {
return nread ;
}
} else {
return - 1 ;
}
return utf8BytesToCodePoint ( buf , buf_len , cp ) ;
}
/* Set default encoding functions */
static linenoisePrevCharLen * prevCharLen = defaultPrevCharLen ;
static linenoiseNextCharLen * nextCharLen = defaultNextCharLen ;
static linenoiseReadCode * readCode = defaultReadCode ;
/* Set used defined encoding functions */
void linenoiseSetEncodingFunctions ( linenoisePrevCharLen * prevCharLenFunc , linenoiseNextCharLen * nextCharLenFunc ,
linenoiseReadCode * readCodeFunc ) {
prevCharLen = prevCharLenFunc ;
nextCharLen = nextCharLenFunc ;
readCode = readCodeFunc ;
}
2025-01-18 14:42:31 +00:00
/* ======================= Low level terminal handling ====================== */
/* Enable "mask mode". When it is enabled, instead of the input that
* the user is typing , the terminal will just display a corresponding
* number of asterisks , like " **** " . This is useful for passwords and other
* secrets that should not be displayed . */
void linenoiseMaskModeEnable ( void ) {
maskmode = 1 ;
}
/* Disable mask mode. */
void linenoiseMaskModeDisable ( void ) {
maskmode = 0 ;
}
/* Set if to use or not the multi line mode. */
void linenoiseSetMultiLine ( int ml ) {
mlmode = ml ;
}
/* Return true if the terminal name is in the list of terminals we know are
* not able to understand basic escape sequences . */
static int isUnsupportedTerm ( void ) {
char * term = getenv ( " TERM " ) ;
if ( term = = NULL ) return 0 ;
2025-01-21 09:32:35 +00:00
for ( size_t j = 0 ; j < unsupported_term . size ( ) ; + + j ) {
if ( ! strcasecmp ( term , unsupported_term [ j ] ) ) {
return 1 ;
}
}
2025-01-18 14:42:31 +00:00
return 0 ;
}
/* Raw mode: 1960 magic shit. */
static int enableRawMode ( int fd ) {
struct termios raw ;
if ( ! isatty ( STDIN_FILENO ) ) goto fatal ;
if ( ! atexit_registered ) {
atexit ( linenoiseAtExit ) ;
atexit_registered = 1 ;
}
if ( tcgetattr ( fd , & orig_termios ) = = - 1 ) goto fatal ;
raw = orig_termios ; /* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start / stop output control . */
raw . c_iflag & = ~ ( BRKINT | ICRNL | INPCK | ISTRIP | IXON ) ;
/* output modes - disable post processing */
raw . c_oflag & = ~ ( OPOST ) ;
/* control modes - set 8 bit chars */
raw . c_cflag | = ( CS8 ) ;
/* local modes - choing off, canonical off, no extended functions,
* no signal chars ( ^ Z , ^ C ) */
raw . c_lflag & = ~ ( ECHO | ICANON | IEXTEN | ISIG ) ;
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte , without timeout . */
raw . c_cc [ VMIN ] = 1 ; raw . c_cc [ VTIME ] = 0 ; /* 1 byte, no timer */
/* put terminal in raw mode after flushing */
if ( tcsetattr ( fd , TCSAFLUSH , & raw ) < 0 ) goto fatal ;
rawmode = 1 ;
return 0 ;
fatal :
errno = ENOTTY ;
return - 1 ;
}
static void disableRawMode ( int fd ) {
/* Don't even check the return value as it's too late. */
if ( rawmode & & tcsetattr ( fd , TCSAFLUSH , & orig_termios ) ! = - 1 )
rawmode = 0 ;
}
/* Use the ESC [6n escape sequence to query the horizontal cursor position
* and return it . On error - 1 is returned , on success the position of the
* cursor . */
static int getCursorPosition ( int ifd , int ofd ) {
char buf [ 32 ] ;
int cols , rows ;
unsigned int i = 0 ;
/* Report cursor location */
if ( write ( ofd , " \x1b [6n " , 4 ) ! = 4 ) return - 1 ;
/* Read the response: ESC [ rows ; cols R */
while ( i < sizeof ( buf ) - 1 ) {
if ( read ( ifd , buf + i , 1 ) ! = 1 ) break ;
if ( buf [ i ] = = ' R ' ) break ;
i + + ;
}
buf [ i ] = ' \0 ' ;
/* Parse it. */
if ( buf [ 0 ] ! = ESC | | buf [ 1 ] ! = ' [ ' ) return - 1 ;
if ( sscanf ( buf + 2 , " %d;%d " , & rows , & cols ) ! = 2 ) return - 1 ;
return cols ;
}
/* Try to get the number of columns in the current terminal, or assume 80
* if it fails . */
static int getColumns ( int ifd , int ofd ) {
struct winsize ws ;
if ( ioctl ( 1 , TIOCGWINSZ , & ws ) = = - 1 | | ws . ws_col = = 0 ) {
/* ioctl() failed. Try to query the terminal itself. */
int start , cols ;
/* Get the initial position so we can restore it later. */
start = getCursorPosition ( ifd , ofd ) ;
if ( start = = - 1 ) goto failed ;
/* Go to right margin and get position. */
if ( write ( ofd , " \x1b [999C " , 6 ) ! = 6 ) goto failed ;
cols = getCursorPosition ( ifd , ofd ) ;
if ( cols = = - 1 ) goto failed ;
/* Restore position. */
if ( cols > start ) {
char seq [ 32 ] ;
snprintf ( seq , 32 , " \x1b [%dD " , cols - start ) ;
if ( write ( ofd , seq , strlen ( seq ) ) = = - 1 ) {
/* Can't recover... */
}
}
return cols ;
} else {
return ws . ws_col ;
}
failed :
return 80 ;
}
/* Clear the screen. Used to handle ctrl+l */
void linenoiseClearScreen ( void ) {
if ( write ( STDOUT_FILENO , " \x1b [H \x1b [2J " , 7 ) < = 0 ) {
/* nothing to do, just to avoid warning. */
}
}
/* Beep, used for completion when there is nothing to complete or when all
* the choices were already shown . */
static void linenoiseBeep ( void ) {
fprintf ( stderr , " \ x7 " ) ;
fflush ( stderr ) ;
}
/* Called by completeLine() and linenoiseShow() to render the current
* edited line with the proposed completion . If the current completion table
* is already available , it is passed as second argument , otherwise the
* function will use the callback to obtain it .
*
* Flags are the same as refreshLine * ( ) , that is REFRESH_ * macros . */
static void refreshLineWithCompletion ( struct linenoiseState * ls , linenoiseCompletions * lc , int flags ) {
/* Obtain the table of completions if the caller didn't provide one. */
2025-01-21 09:32:35 +00:00
linenoiseCompletions ctable ;
2025-01-18 14:42:31 +00:00
if ( lc = = NULL ) {
2025-01-21 09:32:35 +00:00
completionCallback ( ls - > buf , & ctable ) ;
2025-01-18 14:42:31 +00:00
lc = & ctable ;
}
/* Show the edited line with completion if possible, or just refresh. */
if ( ls - > completion_idx < lc - > len ) {
struct linenoiseState saved = * ls ;
ls - > len = ls - > pos = strlen ( lc - > cvec [ ls - > completion_idx ] ) ;
ls - > buf = lc - > cvec [ ls - > completion_idx ] ;
2025-01-21 09:32:35 +00:00
refreshLineWithFlags ( ls , flags ) ;
2025-01-18 14:42:31 +00:00
ls - > len = saved . len ;
ls - > pos = saved . pos ;
ls - > buf = saved . buf ;
} else {
2025-01-21 09:32:35 +00:00
refreshLineWithFlags ( ls , flags ) ;
2025-01-18 14:42:31 +00:00
}
2025-01-21 09:32:35 +00:00
if ( lc = = & ctable ) {
ctable . to_free = false ;
}
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
enum ESC_TYPE { ESC_NULL = 0 , ESC_DELETE , ESC_UP , ESC_DOWN , ESC_RIGHT , ESC_LEFT , ESC_HOME , ESC_END } ;
static ESC_TYPE readEscapeSequence ( struct linenoiseState * l ) {
/* Check if the file input has additional data. */
struct pollfd pfd ;
pfd . fd = l - > ifd ;
pfd . events = POLLIN ;
auto ret = poll ( & pfd , 1 , 1 ) ; // 1 millisecond timeout
if ( ret < = 0 ) { // -1: error, 0: timeout
return ESC_NULL ;
}
/* Read the next two bytes representing the escape sequence.
* Use two calls to handle slow terminals returning the two
* chars at different times . */
char seq [ 3 ] ;
if ( read ( l - > ifd , seq , 1 ) = = - 1 ) {
return ESC_NULL ;
}
if ( read ( l - > ifd , seq + 1 , 1 ) = = - 1 ) {
return ESC_NULL ;
}
/* ESC [ sequences. */
if ( seq [ 0 ] = = ' [ ' ) {
if ( seq [ 1 ] > = ' 0 ' & & seq [ 1 ] < = ' 9 ' ) {
/* Extended escape, read additional byte. */
if ( read ( l - > ifd , seq + 2 , 1 ) = = - 1 ) {
return ESC_NULL ;
}
if ( seq [ 2 ] = = ' ~ ' ) {
switch ( seq [ 1 ] ) {
case ' 3 ' :
return ESC_DELETE ;
}
}
} else {
switch ( seq [ 1 ] ) {
case ' A ' :
return ESC_UP ;
case ' B ' :
return ESC_DOWN ;
case ' C ' :
return ESC_RIGHT ;
case ' D ' :
return ESC_LEFT ;
case ' H ' :
return ESC_HOME ;
case ' F ' :
return ESC_END ;
}
}
}
/* ESC O sequences. */
else if ( seq [ 0 ] = = ' O ' ) {
switch ( seq [ 1 ] ) {
case ' H ' :
return ESC_HOME ;
case ' F ' :
return ESC_END ;
}
}
return ESC_NULL ;
}
2025-01-18 14:42:31 +00:00
/* This is an helper function for linenoiseEdit*() and is called when the
* user types the < tab > key in order to complete the string currently in the
* input .
*
* The state of the editing is encapsulated into the pointed linenoiseState
* structure as described in the structure definition .
*
* If the function returns non - zero , the caller should handle the
* returned value as a byte read from the standard input , and process
* it as usually : this basically means that the function may return a byte
2025-03-03 12:44:56 +00:00
* read from the terminal but not processed . Otherwise , if zero is returned ,
2025-01-18 14:42:31 +00:00
* the input was consumed by the completeLine ( ) function to navigate the
* possible completions , and the caller should read for the next characters
* from stdin . */
2025-03-03 12:44:56 +00:00
static int completeLine ( struct linenoiseState * ls , int keypressed , ESC_TYPE esc_type ) {
2025-01-21 09:32:35 +00:00
linenoiseCompletions lc ;
2025-01-18 14:42:31 +00:00
int nwritten ;
char c = keypressed ;
2025-01-21 09:32:35 +00:00
completionCallback ( ls - > buf , & lc ) ;
2025-01-18 14:42:31 +00:00
if ( lc . len = = 0 ) {
linenoiseBeep ( ) ;
ls - > in_completion = 0 ;
} else {
2025-03-03 12:44:56 +00:00
if ( c = = TAB ) {
if ( ls - > in_completion = = 0 ) {
ls - > in_completion = 1 ;
ls - > completion_idx = 0 ;
} else {
ls - > completion_idx = ( ls - > completion_idx + 1 ) % ( lc . len + 1 ) ;
if ( ls - > completion_idx = = lc . len ) {
linenoiseBeep ( ) ;
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
}
c = 0 ;
} else if ( c = = ESC & & esc_type = = ESC_NULL ) {
/* Re-show original buffer */
if ( ls - > completion_idx < lc . len ) {
refreshLine ( ls ) ;
}
ls - > in_completion = 0 ;
c = 0 ;
} else {
/* Update buffer and return */
if ( ls - > completion_idx < lc . len ) {
nwritten = snprintf ( ls - > buf , ls - > buflen , " %s " , lc . cvec [ ls - > completion_idx ] ) ;
ls - > len = ls - > pos = nwritten ;
}
ls - > in_completion = 0 ;
2025-01-18 14:42:31 +00:00
}
/* Show completion or original buffer */
if ( ls - > in_completion & & ls - > completion_idx < lc . len ) {
2025-01-21 09:32:35 +00:00
refreshLineWithCompletion ( ls , & lc , REFRESH_ALL ) ;
2025-01-18 14:42:31 +00:00
} else {
refreshLine ( ls ) ;
}
}
return c ; /* Return last read character */
}
/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback ( linenoiseCompletionCallback * fn ) {
completionCallback = fn ;
}
/* Register a hits function to be called to show hits to the user at the
* right of the prompt . */
void linenoiseSetHintsCallback ( linenoiseHintsCallback * fn ) {
hintsCallback = fn ;
}
/* Register a function to free the hints returned by the hints callback
* registered with linenoiseSetHintsCallback ( ) . */
void linenoiseSetFreeHintsCallback ( linenoiseFreeHintsCallback * fn ) {
freeHintsCallback = fn ;
}
/* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the
* user typed < tab > . See the example . c source code for a very easy to
* understand example . */
void linenoiseAddCompletion ( linenoiseCompletions * lc , const char * str ) {
2025-01-21 09:32:35 +00:00
const size_t len = strlen ( str ) ;
auto copy = std : : make_unique < char [ ] > ( len + 1 ) ;
if ( ! copy ) {
2025-01-18 14:42:31 +00:00
return ;
}
2025-01-21 09:32:35 +00:00
memcpy ( copy . get ( ) , str , len + 1 ) ;
char * * cvec = static_cast < char * * > ( std : : realloc ( lc - > cvec , sizeof ( char * ) * ( lc - > len + 1 ) ) ) ;
if ( cvec = = nullptr ) {
return ;
}
2025-01-18 14:42:31 +00:00
2025-01-21 09:32:35 +00:00
lc - > cvec = cvec ;
lc - > cvec [ lc - > len + + ] = copy . release ( ) ;
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
/* Get column length from begining of buffer to current byte position */
static size_t columnPos ( const char * buf , size_t buf_len , size_t pos ) {
size_t ret = 0 ;
size_t off = 0 ;
while ( off < pos ) {
size_t col_len ;
size_t len = nextCharLen ( buf , buf_len , off , & col_len ) ;
off + = len ;
ret + = col_len ;
}
return ret ;
}
2025-01-18 14:42:31 +00:00
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
* to the right of the prompt . */
2025-03-03 12:44:56 +00:00
static void refreshShowHints ( std : : string & ab , struct linenoiseState * l , int pcollen ) {
2025-01-18 14:42:31 +00:00
char seq [ 64 ] ;
2025-03-03 12:44:56 +00:00
size_t collen = pcollen + columnPos ( l - > buf , l - > len , l - > len ) ;
if ( hintsCallback & & collen < l - > cols ) {
2025-01-18 14:42:31 +00:00
int color = - 1 , bold = 0 ;
const char * hint = hintsCallback ( l - > buf , & color , & bold ) ;
if ( hint ) {
int hintlen = strlen ( hint ) ;
2025-03-03 12:44:56 +00:00
int hintmaxlen = l - > cols - collen ;
2025-01-18 14:42:31 +00:00
if ( hintlen > hintmaxlen ) hintlen = hintmaxlen ;
if ( bold = = 1 & & color = = - 1 ) color = 37 ;
if ( color ! = - 1 | | bold ! = 0 )
snprintf ( seq , 64 , " \033 [%d;%d;49m " , bold , color ) ;
else
seq [ 0 ] = ' \0 ' ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
ab . append ( hint , hintlen ) ;
2025-01-18 14:42:31 +00:00
if ( color ! = - 1 | | bold ! = 0 )
2025-01-21 09:32:35 +00:00
ab . append ( " \033 [0m " ) ;
2025-01-18 14:42:31 +00:00
/* Call the function to free the hint returned. */
if ( freeHintsCallback ) freeHintsCallback ( hint ) ;
}
}
}
2025-03-03 12:44:56 +00:00
/* Check if text is an ANSI escape sequence */
static int isAnsiEscape ( const char * buf , size_t buf_len , size_t * len ) {
if ( buf_len > 2 & & ! memcmp ( " \033 [ " , buf , 2 ) ) {
size_t off = 2 ;
while ( off < buf_len ) {
switch ( buf [ off + + ] ) {
case ' A ' :
case ' B ' :
case ' C ' :
case ' D ' :
case ' E ' :
case ' F ' :
case ' G ' :
case ' H ' :
case ' J ' :
case ' K ' :
case ' S ' :
case ' T ' :
case ' f ' :
case ' m ' :
* len = off ;
return 1 ;
}
}
}
return 0 ;
}
/* Get column length of prompt text */
static size_t promptTextColumnLen ( const char * prompt , size_t plen ) {
char buf [ LINENOISE_MAX_LINE ] ;
size_t buf_len = 0 ;
size_t off = 0 ;
while ( off < plen ) {
size_t len ;
if ( isAnsiEscape ( prompt + off , plen - off , & len ) ) {
off + = len ;
continue ;
}
buf [ buf_len + + ] = prompt [ off + + ] ;
}
return columnPos ( buf , buf_len , buf_len ) ;
}
2025-01-18 14:42:31 +00:00
/* Single line low level line refresh.
*
* Rewrite the currently edited line accordingly to the buffer content ,
* cursor position , and number of columns of the terminal .
*
* Flags is REFRESH_ * macros . The function can just remove the old
* prompt , just write it , or both . */
static void refreshSingleLine ( struct linenoiseState * l , int flags ) {
char seq [ 64 ] ;
2025-03-03 12:44:56 +00:00
size_t pcollen = promptTextColumnLen ( l - > prompt , strlen ( l - > prompt ) ) ;
2025-01-18 14:42:31 +00:00
int fd = l - > ofd ;
char * buf = l - > buf ;
size_t len = l - > len ;
size_t pos = l - > pos ;
2025-01-21 09:32:35 +00:00
std : : string ab ;
2025-03-03 12:44:56 +00:00
while ( ( pcollen + columnPos ( buf , len , pos ) ) > = l - > cols ) {
int chlen = nextCharLen ( buf , len , 0 , NULL ) ;
buf + = chlen ;
len - = chlen ;
pos - = chlen ;
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
while ( pcollen + columnPos ( buf , len , len ) > l - > cols ) {
len - = prevCharLen ( buf , len , len , NULL ) ;
2025-01-18 14:42:31 +00:00
}
/* Cursor to left edge */
snprintf ( seq , sizeof ( seq ) , " \r " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
if ( flags & REFRESH_WRITE ) {
/* Write the prompt and the current buffer content */
2025-01-21 09:32:35 +00:00
ab . append ( l - > prompt ) ;
2025-01-18 14:42:31 +00:00
if ( maskmode = = 1 ) {
2025-01-21 09:32:35 +00:00
while ( len - - ) {
ab . append ( " * " ) ;
}
2025-01-18 14:42:31 +00:00
} else {
2025-01-21 09:32:35 +00:00
ab . append ( buf , len ) ;
2025-01-18 14:42:31 +00:00
}
/* Show hits if any. */
2025-03-03 12:44:56 +00:00
refreshShowHints ( ab , l , pcollen ) ;
2025-01-18 14:42:31 +00:00
}
/* Erase to right */
snprintf ( seq , sizeof ( seq ) , " \x1b [0K " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
if ( flags & REFRESH_WRITE ) {
/* Move cursor to original position. */
2025-03-03 12:44:56 +00:00
snprintf ( seq , sizeof ( seq ) , " \r \x1b [%dC " , ( int ) ( columnPos ( buf , len , pos ) + pcollen ) ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
2025-01-21 09:32:35 +00:00
( void ) ! write ( fd , ab . c_str ( ) , ab . size ( ) ) ; /* Can't recover from write error. */
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
/* Get column length from begining of buffer to current byte position for multiline mode*/
static size_t columnPosForMultiLine ( const char * buf , size_t buf_len , size_t pos , size_t cols , size_t ini_pos ) {
size_t ret = 0 ;
size_t colwid = ini_pos ;
size_t off = 0 ;
while ( off < buf_len ) {
size_t col_len ;
size_t len = nextCharLen ( buf , buf_len , off , & col_len ) ;
int dif = ( int ) ( colwid + col_len ) - ( int ) cols ;
if ( dif > 0 ) {
ret + = dif ;
colwid = col_len ;
} else if ( dif = = 0 ) {
colwid = 0 ;
} else {
colwid + = col_len ;
}
if ( off > = pos ) {
break ;
}
off + = len ;
ret + = col_len ;
}
return ret ;
}
2025-01-18 14:42:31 +00:00
/* Multi line low level line refresh.
*
* Rewrite the currently edited line accordingly to the buffer content ,
* cursor position , and number of columns of the terminal .
*
* Flags is REFRESH_ * macros . The function can just remove the old
* prompt , just write it , or both . */
static void refreshMultiLine ( struct linenoiseState * l , int flags ) {
char seq [ 64 ] ;
2025-03-03 12:44:56 +00:00
size_t pcollen = promptTextColumnLen ( l - > prompt , strlen ( l - > prompt ) ) ;
int colpos = columnPosForMultiLine ( l - > buf , l - > len , l - > len , l - > cols , pcollen ) ;
int colpos2 ; /* cursor column position. */
int rows = ( pcollen + colpos + l - > cols - 1 ) / l - > cols ; /* rows used by current buf. */
int rpos = ( pcollen + l - > oldcolpos + l - > cols ) / l - > cols ; /* cursor relative row. */
2025-01-18 14:42:31 +00:00
int rpos2 ; /* rpos after refresh. */
2025-03-03 12:44:56 +00:00
int col ; /* column position, zero-based. */
2025-01-18 14:42:31 +00:00
int old_rows = l - > oldrows ;
int fd = l - > ofd , j ;
2025-01-21 09:32:35 +00:00
std : : string ab ;
2025-01-18 14:42:31 +00:00
l - > oldrows = rows ;
/* First step: clear all the lines used before. To do so start by
* going to the last row . */
if ( flags & REFRESH_CLEAN ) {
2025-03-03 12:44:56 +00:00
if ( old_rows - rpos > 0 ) {
2025-01-18 14:42:31 +00:00
snprintf ( seq , 64 , " \x1b [%dB " , old_rows - rpos ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
/* Now for every row clear it, go up. */
2025-03-03 12:44:56 +00:00
for ( j = 0 ; j < old_rows - 1 ; j + + ) {
2025-01-18 14:42:31 +00:00
snprintf ( seq , 64 , " \r \x1b [0K \x1b [1A " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
}
if ( flags & REFRESH_ALL ) {
/* Clean the top line. */
snprintf ( seq , 64 , " \r \x1b [0K " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
/* Get column length to cursor position */
colpos2 = columnPosForMultiLine ( l - > buf , l - > len , l - > pos , l - > cols , pcollen ) ;
2025-01-18 14:42:31 +00:00
if ( flags & REFRESH_WRITE ) {
/* Write the prompt and the current buffer content */
2025-01-21 09:32:35 +00:00
ab . append ( l - > prompt ) ;
2025-01-18 14:42:31 +00:00
if ( maskmode = = 1 ) {
2025-01-21 09:32:35 +00:00
for ( unsigned int i = 0 ; i < l - > len ; + + i ) {
ab . append ( " * " ) ;
}
2025-01-18 14:42:31 +00:00
} else {
2025-01-21 09:32:35 +00:00
ab . append ( l - > buf , l - > len ) ;
2025-01-18 14:42:31 +00:00
}
/* Show hits if any. */
2025-03-03 12:44:56 +00:00
refreshShowHints ( ab , l , pcollen ) ;
2025-01-18 14:42:31 +00:00
/* If we are at the very end of the screen with our prompt, we need to
* emit a newline and move the prompt to the first column . */
2025-03-03 12:44:56 +00:00
if ( l - > pos & & l - > pos = = l - > len & & ( colpos2 + pcollen ) % l - > cols = = 0 ) {
2025-01-21 09:32:35 +00:00
ab . append ( " \n " ) ;
2025-01-18 14:42:31 +00:00
snprintf ( seq , 64 , " \r " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
rows + + ;
if ( rows > ( int ) l - > oldrows ) l - > oldrows = rows ;
}
/* Move cursor to right position. */
2025-03-03 12:44:56 +00:00
rpos2 = ( pcollen + colpos2 + l - > cols ) / l - > cols ; /* Current cursor relative row */
2025-01-18 14:42:31 +00:00
2025-03-03 12:44:56 +00:00
/* Go up till we reach the expected position. */
if ( rows - rpos2 > 0 ) {
2025-01-18 14:42:31 +00:00
snprintf ( seq , 64 , " \x1b [%dA " , rows - rpos2 ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
/* Set column. */
2025-03-03 12:44:56 +00:00
col = ( pcollen + colpos2 ) % l - > cols ;
2025-01-18 14:42:31 +00:00
if ( col )
snprintf ( seq , 64 , " \r \x1b [%dC " , col ) ;
else
snprintf ( seq , 64 , " \r " ) ;
2025-01-21 09:32:35 +00:00
ab . append ( seq ) ;
2025-01-18 14:42:31 +00:00
}
2025-03-03 12:44:56 +00:00
l - > oldcolpos = colpos2 ;
2025-01-21 09:32:35 +00:00
( void ) ! write ( fd , ab . c_str ( ) , ab . size ( ) ) ; /* Can't recover from write error. */
2025-01-18 14:42:31 +00:00
}
/* Calls the two low level functions refreshSingleLine() or
* refreshMultiLine ( ) according to the selected mode . */
static void refreshLineWithFlags ( struct linenoiseState * l , int flags ) {
if ( mlmode )
refreshMultiLine ( l , flags ) ;
else
refreshSingleLine ( l , flags ) ;
}
/* Utility function to avoid specifying REFRESH_ALL all the times. */
static void refreshLine ( struct linenoiseState * l ) {
refreshLineWithFlags ( l , REFRESH_ALL ) ;
}
/* Hide the current line, when using the multiplexing API. */
void linenoiseHide ( struct linenoiseState * l ) {
if ( mlmode )
refreshMultiLine ( l , REFRESH_CLEAN ) ;
else
refreshSingleLine ( l , REFRESH_CLEAN ) ;
}
/* Show the current line, when using the multiplexing API. */
void linenoiseShow ( struct linenoiseState * l ) {
if ( l - > in_completion ) {
refreshLineWithCompletion ( l , NULL , REFRESH_WRITE ) ;
} else {
refreshLineWithFlags ( l , REFRESH_WRITE ) ;
}
}
/* Insert the character 'c' at cursor current position.
*
* On error writing to the terminal - 1 is returned , otherwise 0. */
2025-03-03 12:44:56 +00:00
static int linenoiseEditInsert ( struct linenoiseState * l , const char * cbuf , int clen ) {
if ( l - > len + clen < = l - > buflen ) {
2025-01-18 14:42:31 +00:00
if ( l - > len = = l - > pos ) {
2025-03-03 12:44:56 +00:00
memcpy ( & l - > buf [ l - > pos ] , cbuf , clen ) ;
l - > pos + = clen ;
l - > len + = clen ;
;
2025-01-18 14:42:31 +00:00
l - > buf [ l - > len ] = ' \0 ' ;
2025-03-03 12:44:56 +00:00
if ( ( ! mlmode & & promptTextColumnLen ( l - > prompt , l - > plen ) + columnPos ( l - > buf , l - > len , l - > len ) < l - > cols & &
! hintsCallback ) ) {
2025-01-18 14:42:31 +00:00
/* Avoid a full update of the line in the
* trivial case . */
2025-03-03 12:44:56 +00:00
if ( maskmode = = 1 ) {
static const char d = ' * ' ;
if ( write ( l - > ofd , & d , 1 ) = = - 1 ) {
return - 1 ;
}
} else {
if ( write ( l - > ofd , cbuf , clen ) = = - 1 ) {
return - 1 ;
}
}
2025-01-18 14:42:31 +00:00
} else {
refreshLine ( l ) ;
}
} else {
2025-03-03 12:44:56 +00:00
memmove ( l - > buf + l - > pos + clen , l - > buf + l - > pos , l - > len - l - > pos ) ;
memcpy ( & l - > buf [ l - > pos ] , cbuf , clen ) ;
l - > pos + = clen ;
l - > len + = clen ;
2025-01-18 14:42:31 +00:00
l - > buf [ l - > len ] = ' \0 ' ;
refreshLine ( l ) ;
}
}
return 0 ;
}
/* Move cursor on the left. */
static void linenoiseEditMoveLeft ( struct linenoiseState * l ) {
if ( l - > pos > 0 ) {
2025-03-03 12:44:56 +00:00
l - > pos - = prevCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
2025-01-18 14:42:31 +00:00
refreshLine ( l ) ;
}
}
/* Move cursor on the right. */
static void linenoiseEditMoveRight ( struct linenoiseState * l ) {
if ( l - > pos ! = l - > len ) {
2025-03-03 12:44:56 +00:00
l - > pos + = nextCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
2025-01-18 14:42:31 +00:00
refreshLine ( l ) ;
}
}
/* Move cursor to the start of the line. */
static void linenoiseEditMoveHome ( struct linenoiseState * l ) {
if ( l - > pos ! = 0 ) {
l - > pos = 0 ;
refreshLine ( l ) ;
}
}
/* Move cursor to the end of the line. */
static void linenoiseEditMoveEnd ( struct linenoiseState * l ) {
if ( l - > pos ! = l - > len ) {
l - > pos = l - > len ;
refreshLine ( l ) ;
}
}
/* Substitute the currently edited line with the next or previous history
* entry as specified by ' dir ' . */
# define LINENOISE_HISTORY_NEXT 0
# define LINENOISE_HISTORY_PREV 1
static void linenoiseEditHistoryNext ( struct linenoiseState * l , int dir ) {
if ( history_len > 1 ) {
/* Update the current history entry before to
* overwrite it with the next one . */
free ( history [ history_len - 1 - l - > history_index ] ) ;
history [ history_len - 1 - l - > history_index ] = strdup ( l - > buf ) ;
/* Show the new entry */
l - > history_index + = ( dir = = LINENOISE_HISTORY_PREV ) ? 1 : - 1 ;
if ( l - > history_index < 0 ) {
l - > history_index = 0 ;
return ;
} else if ( l - > history_index > = history_len ) {
l - > history_index = history_len - 1 ;
return ;
}
strncpy ( l - > buf , history [ history_len - 1 - l - > history_index ] , l - > buflen ) ;
l - > buf [ l - > buflen - 1 ] = ' \0 ' ;
l - > len = l - > pos = strlen ( l - > buf ) ;
refreshLine ( l ) ;
}
}
/* Delete the character at the right of the cursor without altering the cursor
* position . Basically this is what happens with the " Delete " keyboard key . */
static void linenoiseEditDelete ( struct linenoiseState * l ) {
if ( l - > len > 0 & & l - > pos < l - > len ) {
2025-03-03 12:44:56 +00:00
int chlen = nextCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
memmove ( l - > buf + l - > pos , l - > buf + l - > pos + chlen , l - > len - l - > pos - chlen ) ;
l - > len - = chlen ;
2025-01-18 14:42:31 +00:00
l - > buf [ l - > len ] = ' \0 ' ;
refreshLine ( l ) ;
}
}
/* Backspace implementation. */
static void linenoiseEditBackspace ( struct linenoiseState * l ) {
if ( l - > pos > 0 & & l - > len > 0 ) {
2025-03-03 12:44:56 +00:00
int chlen = prevCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
memmove ( l - > buf + l - > pos - chlen , l - > buf + l - > pos , l - > len - l - > pos ) ;
l - > pos - = chlen ;
l - > len - = chlen ;
2025-01-18 14:42:31 +00:00
l - > buf [ l - > len ] = ' \0 ' ;
refreshLine ( l ) ;
}
}
2025-03-03 12:44:56 +00:00
/* Delete the previous word, maintaining the cursor at the start of the
2025-01-18 14:42:31 +00:00
* current word . */
static void linenoiseEditDeletePrevWord ( struct linenoiseState * l ) {
size_t old_pos = l - > pos ;
size_t diff ;
while ( l - > pos > 0 & & l - > buf [ l - > pos - 1 ] = = ' ' )
l - > pos - - ;
while ( l - > pos > 0 & & l - > buf [ l - > pos - 1 ] ! = ' ' )
l - > pos - - ;
diff = old_pos - l - > pos ;
memmove ( l - > buf + l - > pos , l - > buf + old_pos , l - > len - old_pos + 1 ) ;
l - > len - = diff ;
refreshLine ( l ) ;
}
/* This function is part of the multiplexed API of Linenoise, that is used
* in order to implement the blocking variant of the API but can also be
* called by the user directly in an event driven program . It will :
*
* 1. Initialize the linenoise state passed by the user .
* 2. Put the terminal in RAW mode .
* 3. Show the prompt .
* 4. Return control to the user , that will have to call linenoiseEditFeed ( )
* each time there is some data arriving in the standard input .
*
* The user can also call linenoiseEditHide ( ) and linenoiseEditShow ( ) if it
2025-03-03 12:44:56 +00:00
* is required to show some input arriving asynchronously , without mixing
2025-01-18 14:42:31 +00:00
* it with the currently edited line .
*
* When linenoiseEditFeed ( ) returns non - NULL , the user finished with the
* line editing session ( pressed enter CTRL - D / C ) : in this case the caller
* needs to call linenoiseEditStop ( ) to put back the terminal in normal
* mode . This will not destroy the buffer , as long as the linenoiseState
* is still valid in the context of the caller .
*
* The function returns 0 on success , or - 1 if writing to standard output
* fails . If stdin_fd or stdout_fd are set to - 1 , the default is to use
* STDIN_FILENO and STDOUT_FILENO .
*/
int linenoiseEditStart ( struct linenoiseState * l , int stdin_fd , int stdout_fd , char * buf , size_t buflen , const char * prompt ) {
/* Populate the linenoise state that we pass to functions implementing
* specific editing functionalities . */
l - > in_completion = 0 ;
l - > ifd = stdin_fd ! = - 1 ? stdin_fd : STDIN_FILENO ;
l - > ofd = stdout_fd ! = - 1 ? stdout_fd : STDOUT_FILENO ;
l - > buf = buf ;
l - > buflen = buflen ;
l - > prompt = prompt ;
l - > plen = strlen ( prompt ) ;
2025-03-03 12:44:56 +00:00
l - > oldcolpos = l - > pos = 0 ;
2025-01-18 14:42:31 +00:00
l - > len = 0 ;
/* Enter raw mode. */
if ( enableRawMode ( l - > ifd ) = = - 1 ) return - 1 ;
l - > cols = getColumns ( stdin_fd , stdout_fd ) ;
l - > oldrows = 0 ;
l - > history_index = 0 ;
/* Buffer starts empty. */
l - > buf [ 0 ] = ' \0 ' ;
2025-03-03 12:44:56 +00:00
l - > buflen - - ; /* Make sure there is always space for the nullterm */
2025-01-18 14:42:31 +00:00
/* If stdin is not a tty, stop here with the initialization. We
* will actually just read a line from standard input in blocking
* mode later , in linenoiseEditFeed ( ) . */
if ( ! isatty ( l - > ifd ) ) return 0 ;
/* The latest history entry is always our current buffer, that
* initially is just an empty string . */
linenoiseHistoryAdd ( " " ) ;
if ( write ( l - > ofd , prompt , l - > plen ) = = - 1 ) return - 1 ;
return 0 ;
}
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. " ;
2025-03-03 12:44:56 +00:00
static const char * handleEnterKey ( struct linenoiseState * l ) {
- - history_len ;
free ( history [ history_len ] ) ;
if ( mlmode ) {
linenoiseEditMoveEnd ( l ) ;
}
if ( hintsCallback ) {
/* Force a refresh without hints to leave the previous
* line as the user typed it after a newline . */
linenoiseHintsCallback * hc = hintsCallback ;
hintsCallback = NULL ;
refreshLine ( l ) ;
hintsCallback = hc ;
}
return strdup ( l - > buf ) ;
}
static const char * handleCtrlCKey ( ) {
errno = EAGAIN ;
return NULL ;
}
static const char * handleCtrlDKey ( struct linenoiseState * l ) {
if ( l - > len > 0 ) {
linenoiseEditDelete ( l ) ;
return linenoiseEditMore ;
}
- - history_len ;
free ( history [ history_len ] ) ;
errno = ENOENT ;
return NULL ;
}
static void handleCtrlTKey ( struct linenoiseState * l ) {
if ( l - > pos > 0 & & l - > pos < l - > len ) {
auto prev_chlen = prevCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
auto curr_chlen = nextCharLen ( l - > buf , l - > len , l - > pos , NULL ) ;
std : : string prev_char ( prev_chlen , 0 ) ;
memcpy ( prev_char . data ( ) , l - > buf + l - > pos - prev_chlen , prev_chlen ) ;
memmove ( l - > buf + l - > pos - prev_chlen , l - > buf + l - > pos , curr_chlen ) ;
memmove ( l - > buf + l - > pos - prev_chlen + curr_chlen , prev_char . data ( ) , prev_chlen ) ;
l - > pos = l - > pos - prev_chlen + curr_chlen ;
if ( l - > pos + prev_chlen ! = l - > len ) {
l - > pos + = prev_chlen ;
}
refreshLine ( l ) ;
}
}
static void handleEscapeSequence ( struct linenoiseState * l , int esc_type ) {
switch ( esc_type ) {
case ESC_NULL :
break ;
case ESC_DELETE :
linenoiseEditDelete ( l ) ;
break ;
case ESC_UP :
linenoiseEditHistoryNext ( l , LINENOISE_HISTORY_PREV ) ;
break ;
case ESC_DOWN :
linenoiseEditHistoryNext ( l , LINENOISE_HISTORY_NEXT ) ;
break ;
case ESC_RIGHT :
linenoiseEditMoveRight ( l ) ;
break ;
case ESC_LEFT :
linenoiseEditMoveLeft ( l ) ;
break ;
case ESC_HOME :
linenoiseEditMoveHome ( l ) ;
break ;
case ESC_END :
linenoiseEditMoveEnd ( l ) ;
break ;
}
}
static void handleCtrlUKey ( struct linenoiseState * l ) {
l - > buf [ 0 ] = ' \0 ' ;
l - > pos = l - > len = 0 ;
refreshLine ( l ) ;
}
static void handleCtrlKKey ( struct linenoiseState * l ) {
l - > buf [ l - > pos ] = ' \0 ' ;
l - > len = l - > pos ;
refreshLine ( l ) ;
}
static const char * processInputCharacter ( struct linenoiseState * l , int c , char * cbuf , int nread , int esc_type ) {
switch ( c ) {
case ENTER :
return handleEnterKey ( l ) ;
case CTRL_C :
return handleCtrlCKey ( ) ;
case BACKSPACE :
case CTRL_H :
linenoiseEditBackspace ( l ) ;
break ;
case CTRL_D : /* ctrl-d, remove char at right of cursor, or if the
line is empty , act as end - of - file . */
return handleCtrlDKey ( l ) ;
case CTRL_T :
handleCtrlTKey ( l ) ;
break ;
case CTRL_B :
linenoiseEditMoveLeft ( l ) ;
break ;
case CTRL_F :
linenoiseEditMoveRight ( l ) ;
break ;
case CTRL_P :
linenoiseEditHistoryNext ( l , LINENOISE_HISTORY_PREV ) ;
break ;
case CTRL_N :
linenoiseEditHistoryNext ( l , LINENOISE_HISTORY_NEXT ) ;
break ;
case ESC :
handleEscapeSequence ( l , esc_type ) ;
break ;
default :
if ( linenoiseEditInsert ( l , cbuf , nread ) ) {
return NULL ;
}
break ;
case CTRL_U : /* Ctrl+u, delete the whole line. */
handleCtrlUKey ( l ) ;
break ;
case CTRL_K : /* Ctrl+k, delete from current to end of line. */
handleCtrlKKey ( l ) ;
break ;
case CTRL_A : /* Ctrl+a, go to the start of the line */
linenoiseEditMoveHome ( l ) ;
break ;
case CTRL_E : /* ctrl+e, go to the end of the line */
linenoiseEditMoveEnd ( l ) ;
break ;
case CTRL_L : /* ctrl+l, clear screen */
linenoiseClearScreen ( ) ;
refreshLine ( l ) ;
break ;
case CTRL_W : /* ctrl+w, delete previous word */
linenoiseEditDeletePrevWord ( l ) ;
break ;
}
return linenoiseEditMore ;
}
2025-01-18 14:42:31 +00:00
/* This function is part of the multiplexed API of linenoise, see the top
* comment on linenoiseEditStart ( ) for more information . Call this function
* each time there is some data to read from the standard input file
* descriptor . In the case of blocking operations , this function can just be
* called in a loop , and block .
*
* The function returns linenoiseEditMore to signal that line editing is still
* in progress , that is , the user didn ' t yet pressed enter / CTRL - D . Otherwise
* the function returns the pointer to the heap - allocated buffer with the
* edited line , that the user should free with linenoiseFree ( ) .
*
* On special conditions , NULL is returned and errno is populated :
*
* EAGAIN if the user pressed Ctrl - C
* ENOENT if the user pressed Ctrl - D
*
* Some other errno : I / O error .
*/
2025-03-03 12:44:56 +00:00
const char * linenoiseEditFeed ( struct linenoiseState * l ) {
/* Not a TTY, pass control to line reading without character count
* limits . */
2025-01-18 14:42:31 +00:00
if ( ! isatty ( l - > ifd ) ) return linenoiseNoTTY ( ) ;
2025-03-03 12:44:56 +00:00
int c ;
2025-01-18 14:42:31 +00:00
int nread ;
2025-03-03 12:44:56 +00:00
char cbuf [ 32 ] ;
2025-01-18 14:42:31 +00:00
2025-03-03 12:44:56 +00:00
nread = readCode ( l - > ifd , cbuf , sizeof ( cbuf ) , & c ) ;
2025-01-18 14:42:31 +00:00
if ( nread < = 0 ) return NULL ;
2025-03-03 12:44:56 +00:00
auto esc_type = ESC_NULL ;
if ( c = = ESC ) {
esc_type = readEscapeSequence ( l ) ;
}
2025-01-18 14:42:31 +00:00
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd . Otherwise it will return the
* character that should be handled next . */
if ( ( l - > in_completion | | c = = 9 ) & & completionCallback ! = NULL ) {
2025-03-03 12:44:56 +00:00
c = completeLine ( l , c , esc_type ) ;
2025-01-18 14:42:31 +00:00
/* Read next character when 0 */
if ( c = = 0 ) return linenoiseEditMore ;
}
2025-03-03 12:44:56 +00:00
return processInputCharacter ( l , c , cbuf , nread , esc_type ) ;
2025-01-18 14:42:31 +00:00
}
/* This is part of the multiplexed linenoise API. See linenoiseEditStart()
* for more information . This function is called when linenoiseEditFeed ( )
* returns something different than NULL . At this point the user input
* is in the buffer , and we can restore the terminal in normal mode . */
void linenoiseEditStop ( struct linenoiseState * l ) {
if ( ! isatty ( l - > ifd ) ) return ;
disableRawMode ( l - > ifd ) ;
printf ( " \n " ) ;
}
/* This just implements a blocking loop for the multiplexed API.
2025-03-03 12:44:56 +00:00
* In many applications that are not event - driven , we can just call
2025-01-18 14:42:31 +00:00
* the blocking linenoise API , wait for the user to complete the editing
* and return the buffer . */
static const char * linenoiseBlockingEdit ( int stdin_fd , int stdout_fd , char * buf , size_t buflen , const char * prompt )
{
struct linenoiseState l ;
/* Editing without a buffer is invalid. */
if ( buflen = = 0 ) {
errno = EINVAL ;
return NULL ;
}
linenoiseEditStart ( & l , stdin_fd , stdout_fd , buf , buflen , prompt ) ;
const char * res ;
while ( ( res = linenoiseEditFeed ( & l ) ) = = linenoiseEditMore ) ;
linenoiseEditStop ( & l ) ;
return res ;
}
/* This special mode is used by linenoise in order to print scan codes
* on screen for debugging / development purposes . It is implemented
* by the linenoise_example program using the - - keycodes option . */
void linenoisePrintKeyCodes ( void ) {
char quit [ 4 ] ;
printf ( " Linenoise key codes debugging mode. \n "
" Press keys to see scan codes. Type 'quit' at any time to exit. \n " ) ;
if ( enableRawMode ( STDIN_FILENO ) = = - 1 ) return ;
memset ( quit , ' ' , 4 ) ;
while ( 1 ) {
char c ;
int nread ;
nread = read ( STDIN_FILENO , & c , 1 ) ;
if ( nread < = 0 ) continue ;
memmove ( quit , quit + 1 , sizeof ( quit ) - 1 ) ; /* shift string to left. */
quit [ sizeof ( quit ) - 1 ] = c ; /* Insert current char on the right. */
if ( memcmp ( quit , " quit " , sizeof ( quit ) ) = = 0 ) break ;
2025-03-03 12:44:56 +00:00
printf ( " '%c' %02x (%d) (type quit to exit) \n " , isprint ( ( int ) c ) ? c : ' ? ' , ( int ) c , ( int ) c ) ;
2025-01-18 14:42:31 +00:00
printf ( " \r " ) ; /* Go left edge manually, we are in raw mode. */
fflush ( stdout ) ;
}
disableRawMode ( STDIN_FILENO ) ;
}
/* This function is called when linenoise() is called with the standard
* input file descriptor not attached to a TTY . So for example when the
* program using linenoise is called in pipe or with a file redirected
* to its standard input . In this case , we want to be able to return the
* line regardless of its length ( by default we are limited to 4 k ) . */
static char * linenoiseNoTTY ( void ) {
char * line = NULL ;
size_t len = 0 , maxlen = 0 ;
while ( 1 ) {
if ( len = = maxlen ) {
if ( maxlen = = 0 ) maxlen = 16 ;
maxlen * = 2 ;
char * oldval = line ;
line = ( char * ) realloc ( line , maxlen ) ;
if ( line = = NULL ) {
if ( oldval ) free ( oldval ) ;
return NULL ;
}
}
int c = fgetc ( stdin ) ;
if ( c = = EOF | | c = = ' \n ' ) {
if ( c = = EOF & & len = = 0 ) {
free ( line ) ;
return NULL ;
} else {
line [ len ] = ' \0 ' ;
return line ;
}
} else {
line [ len ] = c ;
len + + ;
}
}
}
/* The high level function that is the main API of the linenoise library.
* This function checks if the terminal has basic capabilities , just checking
* for a blacklist of stupid terminals , and later either calls the line
* editing function or uses dummy fgets ( ) so that you will be able to type
* something even in the most desperate of the conditions . */
const char * linenoise ( const char * prompt ) {
char buf [ LINENOISE_MAX_LINE ] ;
if ( ! isatty ( STDIN_FILENO ) ) {
/* Not a tty: read from file / pipe. In this mode we don't want any
* limit to the line size , so we call a function to handle that . */
return linenoiseNoTTY ( ) ;
} else if ( isUnsupportedTerm ( ) ) {
size_t len ;
printf ( " %s " , prompt ) ;
fflush ( stdout ) ;
if ( fgets ( buf , LINENOISE_MAX_LINE , stdin ) = = NULL ) return NULL ;
len = strlen ( buf ) ;
while ( len & & ( buf [ len - 1 ] = = ' \n ' | | buf [ len - 1 ] = = ' \r ' ) ) {
len - - ;
buf [ len ] = ' \0 ' ;
}
return strdup ( buf ) ;
} else {
const char * retval = linenoiseBlockingEdit ( STDIN_FILENO , STDOUT_FILENO , buf , LINENOISE_MAX_LINE , prompt ) ;
return retval ;
}
}
/* This is just a wrapper the user may want to call in order to make sure
* the linenoise returned buffer is freed with the same allocator it was
* created with . Useful when the main program is using an alternative
* allocator . */
void linenoiseFree ( void * ptr ) {
if ( ptr = = linenoiseEditMore ) return ; // Protect from API misuse.
free ( ptr ) ;
}
/* ================================ History ================================= */
/* Free the history, but does not reset it. Only used when we have to
* exit ( ) to avoid memory leaks are reported by valgrind & co . */
static void freeHistory ( void ) {
if ( history ) {
int j ;
for ( j = 0 ; j < history_len ; j + + )
free ( history [ j ] ) ;
free ( history ) ;
}
}
/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit ( void ) {
disableRawMode ( STDIN_FILENO ) ;
freeHistory ( ) ;
}
/* This is the API call to add a new entry in the linenoise history.
* It uses a fixed array of char pointers that are shifted ( memmoved )
* when the history max length is reached in order to remove the older
* entry and make room for the new one , so it is not exactly suitable for huge
* histories , but will work well for a few hundred of entries .
*
* Using a circular buffer is smarter , but a bit more complex to handle . */
int linenoiseHistoryAdd ( const char * line ) {
char * linecopy ;
if ( history_max_len = = 0 ) return 0 ;
/* Initialization on first call. */
if ( history = = NULL ) {
history = ( char * * ) malloc ( sizeof ( char * ) * history_max_len ) ;
if ( history = = NULL ) return 0 ;
memset ( history , 0 , ( sizeof ( char * ) * history_max_len ) ) ;
}
/* Don't add duplicated lines. */
if ( history_len & & ! strcmp ( history [ history_len - 1 ] , line ) ) return 0 ;
/* Add an heap allocated copy of the line in the history.
* If we reached the max length , remove the older line . */
linecopy = strdup ( line ) ;
if ( ! linecopy ) return 0 ;
if ( history_len = = history_max_len ) {
free ( history [ 0 ] ) ;
memmove ( history , history + 1 , sizeof ( char * ) * ( history_max_len - 1 ) ) ;
history_len - - ;
}
history [ history_len ] = linecopy ;
history_len + + ;
return 1 ;
}
/* Set the maximum length for the history. This function can be called even
* if there is already some history , the function will make sure to retain
* just the latest ' len ' elements if the new history length value is smaller
* than the amount of items already inside the history . */
int linenoiseHistorySetMaxLen ( int len ) {
char * * new_ptr ;
if ( len < 1 ) return 0 ;
if ( history ) {
int tocopy = history_len ;
new_ptr = ( char * * ) malloc ( sizeof ( char * ) * len ) ;
if ( new_ptr = = NULL ) return 0 ;
/* If we can't copy everything, free the elements we'll not use. */
if ( len < tocopy ) {
int j ;
for ( j = 0 ; j < tocopy - len ; j + + ) free ( history [ j ] ) ;
tocopy = len ;
}
memset ( new_ptr , 0 , sizeof ( char * ) * len ) ;
memcpy ( new_ptr , history + ( history_len - tocopy ) , sizeof ( char * ) * tocopy ) ;
free ( history ) ;
history = new_ptr ;
}
history_max_len = len ;
if ( history_len > history_max_len )
history_len = history_max_len ;
return 1 ;
}
/* Save the history in the specified file. On success 0 is returned
* otherwise - 1 is returned . */
int linenoiseHistorySave ( const char * filename ) {
mode_t old_umask = umask ( S_IXUSR | S_IRWXG | S_IRWXO ) ;
2025-01-21 09:32:35 +00:00
File file ;
file . open ( filename , " w " ) ;
2025-01-18 14:42:31 +00:00
umask ( old_umask ) ;
2025-01-21 09:32:35 +00:00
if ( file . file = = NULL ) {
return - 1 ;
}
2025-01-18 14:42:31 +00:00
chmod ( filename , S_IRUSR | S_IWUSR ) ;
2025-01-21 09:32:35 +00:00
for ( int j = 0 ; j < history_len ; + + j ) {
fprintf ( file . file , " %s \n " , history [ j ] ) ;
}
2025-01-18 14:42:31 +00:00
return 0 ;
}
/* Load the history from the specified file. If the file does not exist
* zero is returned and no operation is performed .
*
* If the file exists and the operation succeeded 0 is returned , otherwise
* on error - 1 is returned . */
int linenoiseHistoryLoad ( const char * filename ) {
2025-01-21 09:32:35 +00:00
File file ;
file . open ( filename , " r " ) ;
2025-01-18 14:42:31 +00:00
char buf [ LINENOISE_MAX_LINE ] ;
2025-01-21 09:32:35 +00:00
if ( file . file = = NULL ) {
return - 1 ;
}
2025-01-18 14:42:31 +00:00
2025-01-21 09:32:35 +00:00
while ( fgets ( buf , LINENOISE_MAX_LINE , file . file ) ! = NULL ) {
2025-01-18 14:42:31 +00:00
char * p ;
p = strchr ( buf , ' \r ' ) ;
if ( ! p ) p = strchr ( buf , ' \n ' ) ;
if ( p ) * p = ' \0 ' ;
linenoiseHistoryAdd ( buf ) ;
}
return 0 ;
}
# endif