zahl

Log | Files | Refs | README

crossline.c (55128B)


      1 /* crossline.c -- Version 1.0
      2  *
      3  * Crossline is a small, self-contained, zero-config, MIT licensed,
      4  *   cross-platform, readline and libedit replacement.
      5  *
      6  * Press <F1> to get full shortcuts list.
      7  *
      8  * You can find the latest source code and description at:
      9  *
     10  *   https://github.com/jcwangxp/crossline
     11  *
     12  * ------------------------------------------------------------------------
     13  *
     14  * MIT License
     15  *
     16  * Copyright (c) 2019, JC Wang (wang_junchuan@163.com)
     17  *
     18  * Permission is hereby granted, free of charge, to any person obtaining a copy
     19  * of this software and associated documentation files (the "Software"), to deal
     20  * in the Software without restriction, including without limitation the rights
     21  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     22  * copies of the Software, and to permit persons to whom the Software is
     23  * furnished to do so, subject to the following conditions:
     24  *
     25  * The above copyright notice and this permission notice shall be included in all
     26  * copies or substantial portions of the Software.
     27  *
     28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     31  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     33  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     34  * SOFTWARE.
     35  * ------------------------------------------------------------------------
     36  */
     37 
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <errno.h>
     42 #include <ctype.h>
     43 #include <stdint.h>
     44 
     45 #ifdef _WIN32
     46 	#include <io.h>
     47 	#include <conio.h>
     48 	#include <windows.h>
     49   #ifndef STDIN_FILENO
     50 	#define STDIN_FILENO 			_fileno(stdin)
     51 	#define STDOUT_FILENO 			_fileno(stdout)
     52   #endif
     53 	#define isatty					_isatty
     54 	#define strcasecmp				_stricmp
     55 	#define strncasecmp				_strnicmp
     56 	static int s_crossline_win = 1;
     57 #else
     58 	#include <unistd.h>
     59 	#include <termios.h>
     60 	#include <fcntl.h>
     61 	#include <signal.h>
     62 	#include <sys/ioctl.h>
     63 	#include <sys/stat.h>
     64 	static int s_crossline_win = 0;
     65 #endif
     66 
     67 #include "crossline.h"
     68 
     69 /*****************************************************************************/
     70 
     71 // Default word delimiters for move and cut
     72 #define CROSS_DFT_DELIMITER			" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
     73 
     74 #define CROSS_HISTORY_MAX_LINE		256		// Maximum history line number
     75 #define CROSS_HISTORY_BUF_LEN		4096	// History line length
     76 #define CROSS_HIS_MATCH_PAT_NUM		16		// History search pattern number
     77 
     78 #define CROSS_COMPLET_MAX_LINE		1024	// Maximum completion word number
     79 #define CROSS_COMPLET_WORD_LEN		64		// Completion word length
     80 #define CROSS_COMPLET_HELP_LEN		256		// Completion word's help length
     81 #define CROSS_COMPLET_HINT_LEN		128		// Completion syntax hints length
     82 
     83 // Make control-characters readable
     84 #define CTRL_KEY(key)				(key - 0x40)
     85 // Build special key code for escape sequences
     86 #define ALT_KEY(key)				(key + ((KEY_ESC+1)<<8))
     87 #define ESC_KEY3(ch)				((KEY_ESC<<8) + ch)
     88 #define ESC_KEY4(ch1,ch2)			((KEY_ESC<<8) + ((ch1)<<16) + ch2)
     89 #define ESC_KEY6(ch1,ch2,ch3)		((KEY_ESC<<8) + ((ch1)<<16) + ((ch2)<<24) + ch3)
     90 #define ESC_OKEY(ch)				((KEY_ESC<<8) + ('O'<<16) + ch)
     91 
     92 /*****************************************************************************/
     93 
     94 enum {
     95 	KEY_TAB			= 9,	// Autocomplete.
     96 	KEY_BACKSPACE	= 8,	// Delete character before cursor.
     97 	KEY_ENTER		= 13,	// Accept line. (Linux)
     98 	KEY_ENTER2		= 10,	// Accept line. (Windows)
     99 	KEY_ESC			= 27,	// Escapce
    100 	KEY_DEL2		= 127,  // It's treaded as Backspace is Linux
    101 	KEY_DEBUG		= 30,	// Ctrl-^ Enter keyboard debug mode
    102 
    103 #ifdef _WIN32 // Windows
    104 
    105 	KEY_INSERT		= (KEY_ESC<<8) + 'R', // Paste last cut text.
    106 	KEY_DEL			= (KEY_ESC<<8) + 'S', // Delete character under cursor.
    107 	KEY_HOME		= (KEY_ESC<<8) + 'G', // Move cursor to start of line.
    108 	KEY_END			= (KEY_ESC<<8) + 'O', // Move cursor to end of line.
    109 	KEY_PGUP		= (KEY_ESC<<8) + 'I', // Move to first line in history.
    110 	KEY_PGDN		= (KEY_ESC<<8) + 'Q', // Move to end of input history.
    111 	KEY_UP			= (KEY_ESC<<8) + 'H', // Fetch previous line in history.
    112 	KEY_DOWN		= (KEY_ESC<<8) + 'P', // Fetch next line in history.
    113 	KEY_LEFT		= (KEY_ESC<<8) + 'K', // Move back a character.
    114 	KEY_RIGHT		= (KEY_ESC<<8) + 'M', // Move forward a character.
    115 
    116 	KEY_CTRL_DEL	= (KEY_ESC<<8) + 147, // Cut word following cursor.
    117 	KEY_CTRL_HOME	= (KEY_ESC<<8) + 'w', // Cut from start of line to cursor.
    118 	KEY_CTRL_END	= (KEY_ESC<<8) + 'u', // Cut from cursor to end of line.
    119 	KEY_CTRL_UP		= (KEY_ESC<<8) + 141, // Uppercase current or following word.
    120 	KEY_CTRL_DOWN	= (KEY_ESC<<8) + 145, // Lowercase current or following word.
    121 	KEY_CTRL_LEFT	= (KEY_ESC<<8) + 's', // Move back a word.
    122 	KEY_CTRL_RIGHT	= (KEY_ESC<<8) + 't', // Move forward a word.
    123 	KEY_CTRL_BACKSPACE	= (KEY_ESC<<8) + 127, // Cut from start of line to cursor.
    124 
    125 	KEY_ALT_DEL		= ALT_KEY(163),		// Cut word following cursor.
    126 	KEY_ALT_HOME	= ALT_KEY(151), 	// Cut from start of line to cursor.
    127 	KEY_ALT_END		= ALT_KEY(159), 	// Cut from cursor to end of line.
    128 	KEY_ALT_UP		= ALT_KEY(152),		// Uppercase current or following word.
    129 	KEY_ALT_DOWN	= ALT_KEY(160),		// Lowercase current or following word.
    130 	KEY_ALT_LEFT	= ALT_KEY(155),		// Move back a word.
    131 	KEY_ALT_RIGHT	= ALT_KEY(157),		// Move forward a word.
    132 	KEY_ALT_BACKSPACE	= ALT_KEY(KEY_BACKSPACE), // Cut from start of line to cursor.
    133 
    134 	KEY_F1			= (KEY_ESC<<8) + ';',	// Show help.
    135 	KEY_F2			= (KEY_ESC<<8) + '<',	// Show history.
    136 	KEY_F3			= (KEY_ESC<<8) + '=',	// Clear history (need confirm).
    137 	KEY_F4			= (KEY_ESC<<8) + '>',	// Search history with current input.
    138 
    139 #else // Linux
    140 
    141 	KEY_INSERT		= ESC_KEY4('2','~'),	// vt100 Esc[2~: Paste last cut text.
    142 	KEY_DEL			= ESC_KEY4('3','~'),	// vt100 Esc[3~: Delete character under cursor.
    143 	KEY_HOME		= ESC_KEY4('1','~'),	// vt100 Esc[1~: Move cursor to start of line.
    144 	KEY_END			= ESC_KEY4('4','~'),	// vt100 Esc[4~: Move cursor to end of line.
    145 	KEY_PGUP		= ESC_KEY4('5','~'),	// vt100 Esc[5~: Move to first line in history.
    146 	KEY_PGDN		= ESC_KEY4('6','~'),	// vt100 Esc[6~: Move to end of input history.
    147 	KEY_UP			= ESC_KEY3('A'), 		//       Esc[A: Fetch previous line in history.
    148 	KEY_DOWN		= ESC_KEY3('B'),		//       Esc[B: Fetch next line in history.
    149 	KEY_LEFT		= ESC_KEY3('D'), 		//       Esc[D: Move back a character.
    150 	KEY_RIGHT		= ESC_KEY3('C'), 		//       Esc[C: Move forward a character.
    151 	KEY_HOME2		= ESC_KEY3('H'),		// xterm Esc[H: Move cursor to start of line.
    152 	KEY_END2		= ESC_KEY3('F'),		// xterm Esc[F: Move cursor to end of line.
    153 
    154 	KEY_CTRL_DEL	= ESC_KEY6('3','5','~'), // xterm Esc[3;5~: Cut word following cursor.
    155 	KEY_CTRL_HOME	= ESC_KEY6('1','5','H'), // xterm Esc[1;5H: Cut from start of line to cursor.
    156 	KEY_CTRL_END	= ESC_KEY6('1','5','F'), // xterm Esc[1;5F: Cut from cursor to end of line.
    157 	KEY_CTRL_UP		= ESC_KEY6('1','5','A'), // xterm Esc[1;5A: Uppercase current or following word.
    158 	KEY_CTRL_DOWN	= ESC_KEY6('1','5','B'), // xterm Esc[1;5B: Lowercase current or following word.
    159 	KEY_CTRL_LEFT	= ESC_KEY6('1','5','D'), // xterm Esc[1;5D: Move back a word.
    160 	KEY_CTRL_RIGHT	= ESC_KEY6('1','5','C'), // xterm Esc[1;5C: Move forward a word.
    161 	KEY_CTRL_BACKSPACE	= 31, 				 // xterm Cut from start of line to cursor.
    162 	KEY_CTRL_UP2	= ESC_OKEY('A'),		 // vt100 EscOA: Uppercase current or following word.
    163 	KEY_CTRL_DOWN2	= ESC_OKEY('B'), 		 // vt100 EscOB: Lowercase current or following word.
    164 	KEY_CTRL_LEFT2	= ESC_OKEY('D'),		 // vt100 EscOD: Move back a word.
    165 	KEY_CTRL_RIGHT2	= ESC_OKEY('C'),		 // vt100 EscOC: Move forward a word.
    166 
    167 	KEY_ALT_DEL		= ESC_KEY6('3','3','~'), // xterm Esc[3;3~: Cut word following cursor.
    168 	KEY_ALT_HOME	= ESC_KEY6('1','3','H'), // xterm Esc[1;3H: Cut from start of line to cursor.
    169 	KEY_ALT_END		= ESC_KEY6('1','3','F'), // xterm Esc[1;3F: Cut from cursor to end of line.
    170 	KEY_ALT_UP		= ESC_KEY6('1','3','A'), // xterm Esc[1;3A: Uppercase current or following word.
    171 	KEY_ALT_DOWN	= ESC_KEY6('1','3','B'), // xterm Esc[1;3B: Lowercase current or following word.
    172 	KEY_ALT_LEFT	= ESC_KEY6('1','3','D'), // xterm Esc[1;3D: Move back a word.
    173 	KEY_ALT_RIGHT	= ESC_KEY6('1','3','C'), // xterm Esc[1;3C: Move forward a word.
    174 	KEY_ALT_BACKSPACE	= ALT_KEY(KEY_DEL2), // Cut from start of line to cursor.
    175 
    176 	KEY_F1			= ESC_OKEY('P'),		 // 	  EscOP: Show help.
    177 	KEY_F2			= ESC_OKEY('Q'),		 // 	  EscOQ: Show history.
    178 	KEY_F3			= ESC_OKEY('R'),		 //       EscOP: Clear history (need confirm).
    179 	KEY_F4			= ESC_OKEY('S'),		 //       EscOP: Search history with current input.
    180 
    181 	KEY_F1_2		= ESC_KEY4('[', 'A'),	 // linux Esc[[A: Show help.
    182 	KEY_F2_2		= ESC_KEY4('[', 'B'),	 // linux Esc[[B: Show history.
    183 	KEY_F3_2		= ESC_KEY4('[', 'C'),	 // linux Esc[[C: Clear history (need confirm).
    184 	KEY_F4_2		= ESC_KEY4('[', 'D'),	 // linux Esc[[D: Search history with current input.
    185 
    186 #endif
    187 };
    188 
    189 /*****************************************************************************/
    190 
    191 typedef struct crossline_completions_t {
    192 	int		num;
    193 	char	word[CROSS_COMPLET_MAX_LINE][CROSS_COMPLET_WORD_LEN];
    194 	char	help[CROSS_COMPLET_MAX_LINE][CROSS_COMPLET_HELP_LEN];
    195 	char	hints[CROSS_COMPLET_HINT_LEN];
    196 	crossline_color_e	color_word[CROSS_COMPLET_MAX_LINE];
    197 	crossline_color_e	color_help[CROSS_COMPLET_MAX_LINE];
    198 	crossline_color_e	color_hints;
    199 } crossline_completions_t;
    200 
    201 static char		s_word_delimiter[64] = CROSS_DFT_DELIMITER;
    202 static char 	s_history_buf[CROSS_HISTORY_MAX_LINE][CROSS_HISTORY_BUF_LEN];
    203 static uint32_t s_history_id = 0; // Increase always, wrap until UINT_MAX
    204 static char 	s_clip_buf[CROSS_HISTORY_BUF_LEN]; // Buf to store cut text
    205 static crossline_completion_callback s_completion_callback = NULL;
    206 static int		s_paging_print_line = 0; // For paging control
    207 static int		s_got_resize 		= 0; // Window size changed
    208 static crossline_color_e s_prompt_color = CROSSLINE_COLOR_DEFAULT;
    209 
    210 static char* 	crossline_readline_edit (char *buf, int size, const char *prompt, int has_input, int in_his);
    211 static int		crossline_history_dump (FILE *file, int print_id, char *patterns, int sel_id, int paging);
    212 
    213 #define isdelim(ch)		(NULL != strchr(s_word_delimiter, ch))	// Check ch is word delimiter
    214 
    215 // Debug macro.
    216 #if 0
    217 static FILE *s_crossline_debug_fp = NULL;
    218 #define crossline_debug(...) \
    219 	do { \
    220 		if (NULL == s_crossline_debug_fp) { s_crossline_debug_fp = fopen("crossline_debug.txt", "a"); } \
    221 		fprintf (s_crossline_debug_fp, __VA_ARGS__); \
    222 		fflush (s_crossline_debug_fp); \
    223 	} while (0)
    224 #else
    225 #define crossline_debug(...)
    226 #endif
    227 
    228 /*****************************************************************************/
    229 
    230 static char* s_crossline_help[] = {
    231 " Misc Commands",
    232 " +-------------------------+--------------------------------------------------+",
    233 " | F1                      |  Show edit shortcuts help.                       |",
    234 " | Ctrl-^                  |  Enter keyboard debugging mode.                  |",
    235 " +-------------------------+--------------------------------------------------+",
    236 " Move Commands",
    237 " +-------------------------+--------------------------------------------------+",
    238 " | Ctrl-B, Left            |  Move back a character.                          |",
    239 " | Ctrl-F, Right           |  Move forward a character.                       |",
    240 " | Up, ESC+Up              |  Move cursor to up line. (For multiple lines)    |",
    241 " |   Ctrl-Up, Alt-Up       |  (Ctrl-Up, Alt-Up only supports Windows/Xterm)   |",
    242 " | Down, ESC+Down          |  Move cursor to down line. (For multiple lines)  |",
    243 " |   Ctrl-Down,Alt-Down    |  (Ctrl-Down, Alt-Down only support Windows/Xterm)|",
    244 " | Alt-B, ESC+Left,        |  Move back a word.                               |",
    245 " |   Ctrl-Left, Alt-Left   |  (Ctrl-Left, Alt-Left only support Windows/Xterm)|",
    246 " | Alt-F, ESC+Right,       |  Move forward a word.                            |",
    247 " |   Ctrl-Right, Alt-Right | (Ctrl-Right,Alt-Right only support Windows/Xterm)|",
    248 " | Ctrl-A, Home            |  Move cursor to start of line.                   |",
    249 " | Ctrl-E, End             |  Move cursor to end of line.                     |",
    250 " | Ctrl-L                  |  Clear screen and redisplay line.                |",
    251 " +-------------------------+--------------------------------------------------+",
    252 " Edit Commands",
    253 " +-------------------------+--------------------------------------------------+",
    254 " | Ctrl-H, Backspace       |  Delete character before cursor.                 |",
    255 " | Ctrl-D, DEL             |  Delete character under cursor.                  |",
    256 " | Alt-U                   |  Uppercase current or following word.            |",
    257 " | Alt-L                   |  Lowercase current or following word.            |",
    258 " | Alt-C                   |  Capitalize current or following word.           |",
    259 " | Alt-\\                  |  Delete whitespace around cursor.                |",
    260 " | Ctrl-T                  |  Transpose character.                            |",
    261 " +-------------------------+--------------------------------------------------+",
    262 " Cut&Paste Commands",
    263 " +-------------------------+--------------------------------------------------+",
    264 " | Ctrl-K, ESC+End,        |  Cut from cursor to end of line.                 |",
    265 " |   Ctrl-End, Alt-End     |  (Ctrl-End, Alt-End only support Windows/Xterm)  |",
    266 " | Ctrl-U, ESC+Home,       |  Cut from start of line to cursor.               |",
    267 " |   Ctrl-Home, Alt-Home   |  (Ctrl-Home, Alt-Home only support Windows/Xterm)|",
    268 " | Ctrl-X                  |  Cut whole line.                                 |",
    269 " | Alt-Backspace,          |  Cut word to left of cursor.                     |",
    270 " |    Esc+Backspace,       |                                                  |",
    271 " |    Clt-Backspace        |  (Clt-Backspace only supports Windows/Xterm)     |",
    272 " | Alt-D, ESC+Del,         |  Cut word following cursor.                      |",
    273 " |    Alt-Del, Ctrl-Del    |  (Alt-Del,Ctrl-Del only support Windows/Xterm)   |",
    274 " | Ctrl-W                  |  Cut to left till whitespace (not word).         |",
    275 " | Ctrl-Y, Ctrl-V, Insert  |  Paste last cut text.                            |",
    276 " +-------------------------+--------------------------------------------------+",
    277 " Complete Commands",
    278 " +-------------------------+--------------------------------------------------+",
    279 " | TAB, Ctrl-I             |  Autocomplete.                                   |",
    280 " | Alt-=, Alt-?            |  List possible completions.                      |",
    281 " +-------------------------+--------------------------------------------------+",
    282 " History Commands",
    283 " +-------------------------+--------------------------------------------------+",
    284 " | Ctrl-P, Up              |  Fetch previous line in history.                 |",
    285 " | Ctrl-N, Down            |  Fetch next line in history.                     |",
    286 " | Alt-<,  PgUp            |  Move to first line in history.                  |",
    287 " | Alt->,  PgDn            |  Move to end of input history.                   |",
    288 " | Ctrl-R, Ctrl-S          |  Search history.                                 |",
    289 " | F4                      |  Search history with current input.              |",
    290 " | F1                      |  Show search help when in search mode.           |",
    291 " | F2                      |  Show history.                                   |",
    292 " | F3                      |  Clear history (need confirm).                   |",
    293 " +-------------------------+--------------------------------------------------+",
    294 " Control Commands",
    295 " +-------------------------+--------------------------------------------------+",
    296 " | Enter,  Ctrl-J, Ctrl-M  |  EOL and accept line.                            |",
    297 " | Ctrl-C, Ctrl-G          |  EOF and abort line.                             |",
    298 " | Ctrl-D                  |  EOF if line is empty.                           |",
    299 " | Alt-R                   |  Revert line.                                    |",
    300 " | Ctrl-Z                  |  Suspend Job. (Linux Only, fg will resume edit)  |",
    301 " +-------------------------+--------------------------------------------------+",
    302 " Note: If Alt-key doesn't work, an alternate way is to press ESC first then press key, see above ESC+Key.",
    303 " Note: In multiple lines:",
    304 "       Up/Down and Ctrl/Alt-Up, Ctrl/Alt-Down will move between lines.",
    305 "       Up key will fetch history when cursor in first line or end of last line(for quick history move)",
    306 "       Down key will fetch history when cursor in last line.",
    307 "       Ctrl/Alt-Up, Ctrl/Alt-Down will just move between lines.",
    308 NULL};
    309 
    310 static char* s_search_help[] = {
    311 "Patterns are separated by ' ', patter match is case insensitive:",
    312 "  (Hint: use Ctrl-Y/Ctrl-V/Insert to paste last paterns)",
    313 "    select:   choose line including 'select'",
    314 "    -select:  choose line excluding 'select'",
    315 "    \"select from\":  choose line including \"select from\"",
    316 "    -\"select from\": choose line excluding \"select from\"",
    317 "Example:",
    318 "    \"select from\" where -\"order by\" -limit:  ",
    319 "         choose line including \"select from\" and 'where'",
    320 "         and excluding \"order by\" or 'limit'",
    321 NULL};
    322 
    323 /*****************************************************************************/
    324 
    325 // Main API to read a line, return buf if get line, return NULL if EOF.
    326 static char* crossline_readline_internal (const char *prompt, char *buf, int size, int has_input)
    327 {
    328 	int not_support = 0, len;
    329 
    330 	if ((NULL == buf) || (size <= 1))
    331 		{ return NULL; }
    332 	if (!isatty(STDIN_FILENO)) {  // input is not from a terminal
    333 		not_support = 1;
    334 	} else {
    335 		char *term = getenv("TERM");
    336 		if (NULL != term) {
    337 			if (!strcasecmp(term, "dumb") || !strcasecmp(term, "cons25") ||  !strcasecmp(term, "emacs"))
    338 				{ not_support = 1; }
    339 		}
    340 	}
    341 	if (not_support) {
    342         if (NULL == fgets(buf, size, stdin))
    343 			{ return NULL; }
    344         for (len = (int)strlen(buf); (len > 0) && (('\n'==buf[len-1]) || ('\r'==buf[len-1])); --len)
    345 			{ buf[len-1] = '\0'; }
    346         return buf;
    347 	}
    348 
    349 	return crossline_readline_edit (buf, size, prompt, has_input, 0);
    350 }
    351 char* crossline_readline (const char *prompt, char *buf, int size)
    352 {
    353 	return crossline_readline_internal (prompt, buf, size, 0);
    354 }
    355 char* crossline_readline2 (const char *prompt, char *buf, int size)
    356 {
    357 	return crossline_readline_internal (prompt, buf, size, 1);
    358 }
    359 
    360 // Set move/cut word delimiter, defaut is all not digital and alphabetic characters.
    361 void  crossline_delimiter_set (const char *delim)
    362 {
    363 	if (NULL != delim) {
    364 		strncpy (s_word_delimiter, delim, sizeof(s_word_delimiter) - 1);
    365 		s_word_delimiter[sizeof(s_word_delimiter) - 1] = '\0';
    366 	}
    367 }
    368 
    369 void crossline_history_show (void)
    370 {
    371 	crossline_history_dump (stdout, 1, NULL, 0, isatty(STDIN_FILENO));
    372 }
    373 
    374 void  crossline_history_clear (void)
    375 {
    376 	memset (s_history_buf, 0, sizeof (s_history_buf));
    377 	s_history_id = 0;
    378 }
    379 
    380 int crossline_history_save (const char *filename)
    381 {
    382 	if (NULL == filename) {
    383 		return -1;
    384 	} else {
    385 		FILE *file = fopen(filename, "wt");
    386 		if (file == NULL) {	return -1;	}
    387 		crossline_history_dump (file, 0, NULL, 0, 0);
    388 		fclose(file);
    389 	}
    390 	return 0;
    391 }
    392 
    393 int crossline_history_load (const char* filename)
    394 {
    395 	int		len;
    396 	char	buf[CROSS_HISTORY_BUF_LEN];
    397 	FILE	*file;
    398 
    399 	if (NULL == filename)	{	return -1; }
    400 	file = fopen(filename, "rt");
    401 	if (NULL == file)	{ return -1; }
    402 	while (NULL != fgets(buf, CROSS_HISTORY_BUF_LEN, file)) {
    403         for (len = (int)strlen(buf); (len > 0) && (('\n'==buf[len-1]) || ('\r'==buf[len-1])); --len)
    404 			{ buf[len-1] = '\0'; }
    405 		if (len > 0) {
    406 			buf[CROSS_HISTORY_BUF_LEN-1] = '\0';
    407 			strcpy (s_history_buf[(s_history_id++) % CROSS_HISTORY_MAX_LINE], buf);
    408 		}
    409 	}
    410 	fclose(file);
    411 	return 0;
    412 }
    413 
    414 // Register completion callback.
    415 void crossline_completion_register (crossline_completion_callback pCbFunc)
    416 {
    417 	s_completion_callback = pCbFunc;
    418 }
    419 
    420 // Add completion in callback. Word is must, help for word is optional.
    421 void  crossline_completion_add_color (crossline_completions_t *pCompletions, const char *word, 
    422 											crossline_color_e wcolor, const char *help, crossline_color_e hcolor)
    423 {
    424 	if ((NULL != pCompletions) && (NULL != word) && (pCompletions->num < CROSS_COMPLET_MAX_LINE)) {
    425 		strncpy (pCompletions->word[pCompletions->num], word, CROSS_COMPLET_WORD_LEN);
    426 		pCompletions->word[pCompletions->num][CROSS_COMPLET_WORD_LEN - 1] = '\0';
    427 		pCompletions->color_word[pCompletions->num] = wcolor;
    428 		pCompletions->help[pCompletions->num][0] = '\0';
    429 		if (NULL != help) {
    430 			strncpy (pCompletions->help[pCompletions->num], help, CROSS_COMPLET_HELP_LEN);
    431 			pCompletions->help[pCompletions->num][CROSS_COMPLET_HELP_LEN - 1] = '\0';
    432 			pCompletions->color_help[pCompletions->num] = hcolor;
    433 		}
    434 		pCompletions->num++;
    435 	}
    436 }
    437 void crossline_completion_add (crossline_completions_t *pCompletions, const char *word, const char *help)
    438 {
    439 	crossline_completion_add_color (pCompletions, word, CROSSLINE_COLOR_DEFAULT, help, CROSSLINE_COLOR_DEFAULT);
    440 }
    441 
    442 // Set syntax hints in callback.
    443 void  crossline_hints_set_color (crossline_completions_t *pCompletions, const char *hints, crossline_color_e color)
    444 {
    445 	if ((NULL != pCompletions) && (NULL != hints)) {
    446 		strncpy (pCompletions->hints, hints, CROSS_COMPLET_HINT_LEN - 1);
    447 		pCompletions->hints[CROSS_COMPLET_HINT_LEN - 1] = '\0';
    448 		pCompletions->color_hints = color;
    449 	}
    450 }
    451 void crossline_hints_set (crossline_completions_t *pCompletions, const char *hints)
    452 {
    453 	crossline_hints_set_color (pCompletions, hints, CROSSLINE_COLOR_DEFAULT);
    454 }
    455 
    456 /*****************************************************************************/
    457 
    458 int crossline_paging_set (int enable)
    459 {
    460 	int prev = s_paging_print_line >=0;
    461 	s_paging_print_line = enable ? 0 : -1;
    462 	return prev;
    463 }
    464 
    465 int crossline_paging_check (int line_len)
    466 {
    467 	char *paging_hints = "*** Press <Space> or <Enter> to continue . . .";
    468 	int	i, ch, rows, cols, len = (int)strlen(paging_hints);
    469 
    470 	if ((s_paging_print_line < 0) || !isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))	{ return 0; }
    471 	crossline_screen_get (&rows, &cols);
    472 	s_paging_print_line += (line_len + cols - 1) / cols;
    473 	if (s_paging_print_line >= (rows - 1)) {
    474 		printf ("%s", paging_hints);
    475 		ch = crossline_getch();
    476 		if (0 == ch) { crossline_getch(); }	// some terminal server may send 0 after Enter
    477 		// clear paging hints
    478 		for (i = 0; i < len; ++i) { printf ("\b"); }
    479 		for (i = 0; i < len; ++i) { printf (" ");  }
    480 		for (i = 0; i < len; ++i) { printf ("\b"); }
    481 		s_paging_print_line = 0;
    482 		if ((' ' != ch) && (KEY_ENTER != ch) && (KEY_ENTER2 != ch)) {
    483 			return 1; 
    484 		}
    485 	}
    486 	return 0;
    487 }
    488 
    489 /*****************************************************************************/
    490 
    491 void  crossline_prompt_color_set (crossline_color_e color)
    492 {
    493 	s_prompt_color	= color;
    494 }
    495 
    496 void crossline_screen_clear ()
    497 {
    498 	int ret = system (s_crossline_win ? "cls" : "clear");
    499 	(void) ret;
    500 }
    501 
    502 #ifdef _WIN32	// Windows
    503 
    504 int crossline_getch (void)
    505 {
    506 	fflush (stdout);
    507 	return _getch();
    508 }
    509 void crossline_screen_get (int *pRows, int *pCols)
    510 {
    511 	CONSOLE_SCREEN_BUFFER_INFO inf;
    512 	GetConsoleScreenBufferInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    513 	*pCols = inf.srWindow.Right - inf.srWindow.Left + 1;
    514 	*pRows = inf.srWindow.Bottom - inf.srWindow.Top + 1;
    515 	*pCols = *pCols > 1 ? *pCols : 160;
    516 	*pRows = *pRows > 1 ? *pRows : 24;
    517 }
    518 int crossline_cursor_get (int *pRow, int *pCol)
    519 {
    520 	CONSOLE_SCREEN_BUFFER_INFO inf;
    521 	GetConsoleScreenBufferInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    522 	*pRow = inf.dwCursorPosition.Y - inf.srWindow.Top;
    523 	*pCol = inf.dwCursorPosition.X - inf.srWindow.Left;
    524 	return 0;
    525 }
    526 void crossline_cursor_set (int row, int col)
    527 {
    528 	CONSOLE_SCREEN_BUFFER_INFO inf;
    529 	GetConsoleScreenBufferInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    530 	inf.dwCursorPosition.Y = (SHORT)row + inf.srWindow.Top;	
    531 	inf.dwCursorPosition.X = (SHORT)col + inf.srWindow.Left;
    532 	SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), inf.dwCursorPosition);
    533 }
    534 void crossline_cursor_move (int row_off, int col_off)
    535 {
    536 	CONSOLE_SCREEN_BUFFER_INFO inf;
    537 	GetConsoleScreenBufferInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    538 	inf.dwCursorPosition.Y += (SHORT)row_off;
    539 	inf.dwCursorPosition.X += (SHORT)col_off;
    540 	SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), inf.dwCursorPosition);
    541 }
    542 void crossline_cursor_hide (int bHide)
    543 {
    544 	CONSOLE_CURSOR_INFO inf;
    545 	GetConsoleCursorInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    546 	inf.bVisible = !bHide;
    547 	SetConsoleCursorInfo (GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    548 }
    549 
    550 void crossline_color_set (crossline_color_e color)
    551 {
    552     CONSOLE_SCREEN_BUFFER_INFO info;
    553 	static WORD dft_wAttributes = 0;
    554 	WORD wAttributes = 0;
    555 	if (!dft_wAttributes) {
    556 		GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
    557 		dft_wAttributes = info.wAttributes;
    558 	}
    559 	if (CROSSLINE_FGCOLOR_DEFAULT == (color&CROSSLINE_FGCOLOR_MASK)) {
    560 		wAttributes |= dft_wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
    561 	} else {
    562 		wAttributes |= (color&CROSSLINE_FGCOLOR_BRIGHT) ? FOREGROUND_INTENSITY : 0;
    563 		switch (color&CROSSLINE_FGCOLOR_MASK) {
    564 			case CROSSLINE_FGCOLOR_RED:  	wAttributes |= FOREGROUND_RED;	break;
    565 			case CROSSLINE_FGCOLOR_GREEN:  	wAttributes |= FOREGROUND_GREEN;break;
    566 			case CROSSLINE_FGCOLOR_BLUE:  	wAttributes |= FOREGROUND_BLUE;	break;
    567 			case CROSSLINE_FGCOLOR_YELLOW:  wAttributes |= FOREGROUND_RED | FOREGROUND_GREEN;	break;
    568 			case CROSSLINE_FGCOLOR_MAGENTA: wAttributes |= FOREGROUND_RED | FOREGROUND_BLUE;	break;
    569 			case CROSSLINE_FGCOLOR_CYAN:	wAttributes |= FOREGROUND_GREEN | FOREGROUND_BLUE;	break;
    570 			case CROSSLINE_FGCOLOR_WHITE:   wAttributes |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;break;
    571 		}
    572 	}
    573 	if (CROSSLINE_BGCOLOR_DEFAULT == (color&CROSSLINE_BGCOLOR_MASK)) {
    574 		wAttributes |= dft_wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
    575 	} else {
    576 		wAttributes |= (color&CROSSLINE_BGCOLOR_BRIGHT) ? BACKGROUND_INTENSITY : 0;
    577 		switch (color&CROSSLINE_BGCOLOR_MASK) {
    578 			case CROSSLINE_BGCOLOR_RED:  	wAttributes |= BACKGROUND_RED;	break;
    579 			case CROSSLINE_BGCOLOR_GREEN:  	wAttributes |= BACKGROUND_GREEN;break;
    580 			case CROSSLINE_BGCOLOR_BLUE:  	wAttributes |= BACKGROUND_BLUE;	break;
    581 			case CROSSLINE_BGCOLOR_YELLOW:  wAttributes |= BACKGROUND_RED | BACKGROUND_GREEN;	break;
    582 			case CROSSLINE_BGCOLOR_MAGENTA: wAttributes |= BACKGROUND_RED | BACKGROUND_BLUE;	break;
    583 			case CROSSLINE_BGCOLOR_CYAN:	wAttributes |= BACKGROUND_GREEN | BACKGROUND_BLUE;	break;
    584 			case CROSSLINE_BGCOLOR_WHITE:   wAttributes |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;break;
    585 		}
    586 	}
    587 	if (color & CROSSLINE_UNDERLINE)
    588 		{ wAttributes |= COMMON_LVB_UNDERSCORE; }
    589 	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), wAttributes);
    590 }
    591 
    592 #else // Linux
    593 
    594 int crossline_getch ()
    595 {
    596 	char ch = 0;
    597 	struct termios old_term, cur_term;
    598 	fflush (stdout);
    599 	if (tcgetattr(STDIN_FILENO, &old_term) < 0)	{ perror("tcsetattr"); }
    600 	cur_term = old_term;
    601 	cur_term.c_lflag &= ~(ICANON | ECHO | ISIG); // echoing off, canonical off, no signal chars
    602 	cur_term.c_cc[VMIN] = 1;
    603 	cur_term.c_cc[VTIME] = 0;
    604 	if (tcsetattr(STDIN_FILENO, TCSANOW, &cur_term) < 0)	{ perror("tcsetattr"); }
    605 	if (read(STDIN_FILENO, &ch, 1) < 0)	{ /* perror("read()"); */ } // signal will interrupt
    606 	if (tcsetattr(STDIN_FILENO, TCSADRAIN, &old_term) < 0)	{ perror("tcsetattr"); }
    607 	return ch;
    608 }
    609 void crossline_screen_get (int *pRows, int *pCols)
    610 {
    611 	struct winsize ws = {};
    612 	(void)ioctl (1, TIOCGWINSZ, &ws);
    613 	*pCols = ws.ws_col;
    614 	*pRows = ws.ws_row;
    615 	*pCols = *pCols > 1 ? *pCols : 160;
    616 	*pRows = *pRows > 1 ? *pRows : 24;
    617 }
    618 int crossline_cursor_get (int *pRow, int *pCol)
    619 {
    620 	int i;
    621 	char buf[32];
    622 	printf ("\e[6n");
    623 	for (i = 0; i < (char)sizeof(buf)-1; ++i) {
    624 		buf[i] = (char)crossline_getch ();
    625 		if ('R' == buf[i]) { break; }
    626 	}
    627 	buf[i] = '\0';
    628 	if (2 != sscanf (buf, "\e[%d;%dR", pRow, pCol)) { return -1; }
    629 	(*pRow)--; (*pCol)--;
    630 	return 0;
    631 }
    632 void crossline_cursor_set (int row, int col)
    633 {
    634 	printf("\e[%d;%dH", row+1, col+1);
    635 }
    636 void crossline_cursor_move (int row_off, int col_off)
    637 {
    638 	if (col_off > 0)		{ printf ("\e[%dC", col_off);  }
    639 	else if (col_off < 0)	{ printf ("\e[%dD", -col_off); }
    640 	if (row_off > 0)		{ printf ("\e[%dB", row_off);  }
    641 	else if (row_off < 0)	{ printf ("\e[%dA", -row_off); }
    642 }
    643 void crossline_cursor_hide (int bHide)
    644 {
    645 	printf("\e[?25%c", bHide?'l':'h');
    646 }
    647 
    648 void crossline_color_set (crossline_color_e color)
    649 {
    650 	if (!isatty(STDOUT_FILENO))		{ return; }
    651 	printf ("\033[m");
    652 	if (CROSSLINE_FGCOLOR_DEFAULT != (color&CROSSLINE_FGCOLOR_MASK)) 
    653 		{ printf ("\033[%dm", 29 + (color&CROSSLINE_FGCOLOR_MASK) + ((color&CROSSLINE_FGCOLOR_BRIGHT)?60:0)); }
    654 	if (CROSSLINE_BGCOLOR_DEFAULT != (color&CROSSLINE_BGCOLOR_MASK)) 
    655 		{ printf ("\033[%dm", 39 + ((color&CROSSLINE_BGCOLOR_MASK)>>8) + ((color&CROSSLINE_BGCOLOR_BRIGHT)?60:0)); }
    656 	if (color & CROSSLINE_UNDERLINE)
    657 		{ printf ("\033[4m"); }
    658 }
    659 
    660 #endif // #ifdef _WIN32
    661 
    662 /*****************************************************************************/
    663 
    664 static void crossline_show_help (int show_search)
    665 {
    666 	int	i;
    667 	char **help = show_search ? s_search_help : s_crossline_help;
    668  	printf (" \b\n");
    669 	for (i = 0; NULL != help[i]; ++i) {
    670 		printf ("%s\n", help[i]);
    671 		if (crossline_paging_check ((int)strlen(help[i])+1))
    672 			{ break; }
    673 	}
    674 }
    675 
    676 static void str_to_lower (char *str)
    677 {
    678 	for (; '\0' != *str; ++str)
    679 		{ *str = (char)tolower (*str); }
    680 }
    681 
    682 // Match including(no prefix) and excluding(with prefix: '-') patterns.
    683 static int crossline_match_patterns (const char *str, char *word[], int num)
    684 {
    685 	int i;
    686 	char buf[CROSS_HISTORY_BUF_LEN];
    687 
    688 	strncpy (buf, str, sizeof(buf) - 1);
    689 	buf[sizeof(buf) - 1] = '\0';
    690 	str_to_lower (buf);
    691 	for (i = 0; i < num; ++i) {
    692 		if ('-' == word[i][0]) {
    693 			if (NULL != strstr (buf, &word[i][1]))
    694 				{ return 0; }
    695 		} else if (NULL == strstr (buf, word[i]))
    696 			{ return 0; }
    697 	}
    698 	return 1;
    699 }
    700 
    701 // Split pattern string to individual pattern list, handle composite words embraced with " ".
    702 static int crossline_split_patterns (char *patterns, char *pat_list[], int max)
    703 {
    704 	int i, num = 0;
    705 	char *pch = patterns;
    706 
    707 	if (NULL == patterns) { return 0; }
    708 	while (' ' == *pch)	{ ++pch; }
    709 	while ((num < max) && (NULL != pch)) {
    710 		if (('"' == *pch) || (('-' == *pch) && ('"' == *(pch+1)))) {
    711 			if ('"' != *pch)	{ *(pch+1) = '-'; }
    712 			pat_list[num++] = ++pch;
    713 			if (NULL != (pch = strchr(pch, '"'))) {
    714 				*pch++ = '\0';
    715 				while (' ' == *pch)	{ ++pch; }
    716 			}
    717 		} else {
    718 			pat_list[num++] = pch;
    719 			if (NULL != (pch = strchr (pch, ' '))) {
    720 				*pch = '\0';
    721 				while (' ' == *(++pch))	;
    722 			}
    723 		}
    724 	}
    725 	for (i = 0; i < num; ++i)
    726 		{ str_to_lower (pat_list[i]); }
    727 	return num;
    728 }
    729 
    730 // If patterns is not NULL, will filter history.
    731 // If sel_id > 0, return the real id+1 in history buf, else return history number dumped.
    732 static int crossline_history_dump (FILE *file, int print_id, char *patterns, int sel_id, int paging)
    733 {
    734 	uint32_t i;
    735 	int		id = 0, num=0;
    736 	char	*pat_list[CROSS_HIS_MATCH_PAT_NUM], *history;
    737 
    738 	num = crossline_split_patterns (patterns, pat_list, CROSS_HIS_MATCH_PAT_NUM);
    739 	for (i = s_history_id; i < s_history_id + CROSS_HISTORY_MAX_LINE; ++i) {
    740 		history = s_history_buf[i % CROSS_HISTORY_MAX_LINE];
    741 		if ('\0' != history[0]) {
    742 			if ((NULL != patterns) && !crossline_match_patterns (history, pat_list, num))
    743 				{ continue; }
    744 			if (sel_id > 0) {
    745 				if (++id == sel_id)
    746 					{ return (i % CROSS_HISTORY_MAX_LINE) + 1; }
    747 				continue;
    748 			}
    749 			if (print_id)	{ fprintf (file, "%4d  %s\n", ++id, history); }
    750 			else			{ fprintf (file, "%s\n", history); }
    751 			if (paging) {
    752 				if (crossline_paging_check ((int)strlen(history)+(print_id?7:1)))
    753 					{ break; }
    754 			}
    755 		}
    756 	}
    757 	return id;
    758 }
    759 
    760 // Search history, input will be initial search patterns.
    761 static int crossline_history_search (char *input)
    762 {
    763 	uint32_t his_id = 0, count;
    764 	char pattern[CROSS_HISTORY_BUF_LEN], buf[8] = "1";
    765 
    766 	printf (" \b\n");
    767 	if (NULL != input) {
    768 		strncpy (pattern, input, sizeof(pattern) - 1);
    769 		pattern[sizeof(pattern) - 1] = '\0';
    770 	}
    771 	// Get search patterns
    772 	if (NULL == crossline_readline_edit(pattern, sizeof (pattern), "Input Patterns <F1> help: ", (NULL!=input), 1))
    773 		{ return 0; }
    774 	strncpy (s_clip_buf, pattern, sizeof(s_clip_buf) - 1);
    775 	s_clip_buf[sizeof(s_clip_buf) - 1] = '\0';
    776 	count = crossline_history_dump (stdout, 1, pattern, 0, 1);
    777 	if (0 == count)	{ return 0; } // Nothing found, just return
    778 	// Get choice
    779 	if (NULL == crossline_readline_edit (buf, sizeof (buf), "Input history id: ", (1==count), 1))
    780 		{ return 0; }
    781 	his_id = atoi (buf);
    782 	if (('\0' != buf[0]) && ((his_id > count) || (his_id <= 0))) {
    783 		printf ("Invalid history id: %s\n", buf);
    784 		return 0;
    785 	}
    786 	return crossline_history_dump (stdout, 1, pattern, his_id, 0);
    787 }
    788 
    789 // Show completions returned by callback.
    790 static int crossline_show_completions (crossline_completions_t *pCompletions)
    791 {
    792 	int i, j, ret = 0, word_len = 0, with_help = 0, rows, cols, word_num;
    793 
    794 	if (('\0' != pCompletions->hints[0]) || (pCompletions->num > 0)) {
    795 		printf (" \b\n");
    796 		ret = 1;
    797 	}
    798 	// Print syntax hints.
    799 	if ('\0' != pCompletions->hints[0]) {
    800 		printf ("Please input: "); 
    801 		crossline_color_set (pCompletions->color_hints);
    802 		printf ("%s", pCompletions->hints); 
    803 		crossline_color_set (CROSSLINE_COLOR_DEFAULT);
    804 		printf ("\n");
    805 	}
    806 	if (0 == pCompletions->num)	{ return ret; }
    807 	for (i = 0; i < pCompletions->num; ++i) {
    808 		if ((int)strlen(pCompletions->word[i]) > word_len)
    809 			{ word_len = (int)strlen(pCompletions->word[i]); }
    810 		if ('\0' != pCompletions->help[i][0])	{ with_help = 1; }
    811 	}
    812 	if (with_help) {
    813 		// Print words with help format.
    814 		for (i = 0; i < pCompletions->num; ++i) {
    815 			crossline_color_set (pCompletions->color_word[i]);
    816 			printf ("%s", pCompletions->word[i]);
    817 			for (j = 0; j < 4+word_len-(int)strlen(pCompletions->word[i]); ++j)
    818 				{ printf (" "); }
    819 			crossline_color_set (pCompletions->color_help[i]);
    820 			printf ("%s", pCompletions->help[i]);
    821 			crossline_color_set (CROSSLINE_COLOR_DEFAULT);
    822 			printf ("\n");
    823 			if (crossline_paging_check((int)strlen(pCompletions->help[i])+4+word_len+1))
    824 				{ break; }
    825 		}
    826 		return ret;
    827 	}
    828 
    829 	// Print words list in multiple columns.
    830 	crossline_screen_get (&rows, &cols);
    831 	word_num = (cols - 1 - word_len) / (word_len + 4) + 1;
    832 	for (i = 1; i <= pCompletions->num; ++i) {
    833 		crossline_color_set (pCompletions->color_word[i-1]);
    834 		printf ("%s", pCompletions->word[i-1]);
    835 		crossline_color_set (CROSSLINE_COLOR_DEFAULT);
    836 		for (j = 0; j < ((i%word_num)?4:0)+word_len-(int)strlen(pCompletions->word[i-1]); ++j)
    837 			{ printf (" "); }
    838 		if (0 == (i % word_num)) {
    839 			printf ("\n");
    840 			if (crossline_paging_check (word_len))
    841 				{ return ret; }
    842 		}
    843 	}
    844 
    845 	if (pCompletions->num % word_num) { printf ("\n"); }
    846 	return ret;
    847 }
    848 
    849 static int crossline_updown_move (const char *prompt, int *pCurPos, int *pCurNum, int off, int bForce)
    850 {
    851 	int rows, cols, len = (int)strlen(prompt), cur_pos=*pCurPos;
    852 	crossline_screen_get (&rows, &cols);
    853 	if (!bForce && (*pCurPos == *pCurNum))	{ return 0; } // at end of last line
    854 	if (off < 0) {
    855 		if ((*pCurPos+len)/cols == 0) { return 0; } // at first line
    856 		*pCurPos -= cols;
    857 		if (*pCurPos < 0) { *pCurPos = 0; }
    858 		crossline_cursor_move (-1, (*pCurPos+len)%cols-(cur_pos+len)%cols);
    859 	} else {
    860 		if ((*pCurPos+len)/cols == (*pCurNum+len)/cols) { return 0; } // at last line
    861 		*pCurPos += cols;
    862 		if (*pCurPos > *pCurNum) { *pCurPos = *pCurNum - 1; } // one char left to avoid history shortcut
    863 		crossline_cursor_move (1, (*pCurPos+len)%cols-(cur_pos+len)%cols);
    864 	}
    865 	return 1;
    866 }
    867 
    868 // Refreash current print line and move cursor to new_pos.
    869 static void crossline_refreash (const char *prompt, char *buf, int *pCurPos, int *pCurNum, int new_pos, int new_num, int bChg)
    870 {
    871 	int i, pos_row, pos_col, len = (int)strlen(prompt);
    872 	static int rows = 0, cols = 0;
    873 
    874 	if (bChg || !rows || s_crossline_win) { crossline_screen_get (&rows, &cols); }
    875 	if (!bChg) { // just move cursor
    876 		pos_row = (new_pos+len)/cols - (*pCurPos+len)/cols;
    877 		pos_col = (new_pos+len)%cols - (*pCurPos+len)%cols;
    878 		crossline_cursor_move (pos_row, pos_col);
    879 	} else {
    880 		buf[new_num] = '\0';
    881 		if (bChg > 1) { // refreash as less as possbile
    882 			printf ("%s", &buf[bChg-1]);
    883 		} else {
    884 			pos_row = (*pCurPos + len) / cols;
    885 			crossline_cursor_move (-pos_row, 0);
    886 			crossline_color_set (s_prompt_color);
    887 			printf ("\r%s", prompt);
    888 			crossline_color_set (CROSSLINE_COLOR_DEFAULT);
    889 			printf ("%s", buf);
    890 		}
    891 		if (!s_crossline_win && new_num>0 && !((new_num+len)%cols)) { printf("\n"); }
    892 		for (i=*pCurNum-new_num; i > 0; --i) { printf (" "); }
    893 		if (!s_crossline_win && *pCurNum>new_num && !((*pCurNum+len)%cols)) { printf("\n"); }
    894 		pos_row = (new_num+len)/cols - (*pCurNum+len)/cols;
    895 		if (pos_row < 0) { crossline_cursor_move (pos_row, 0); } 
    896 		printf ("\r");
    897 		pos_row = (new_pos+len)/cols - (new_num+len)/cols;
    898 		crossline_cursor_move (pos_row, (new_pos+len)%cols);
    899 	}
    900 	*pCurPos = new_pos;
    901 	*pCurNum = new_num;
    902 }
    903 
    904 static void crossline_print (const char *prompt, char *buf, int *pCurPos, int *pCurNum, int new_pos, int new_num)
    905 {
    906 	*pCurPos = *pCurNum = 0;
    907 	crossline_refreash (prompt, buf, pCurPos, pCurNum, new_pos, new_num, 1);
    908 }
    909 
    910 // Copy part text[cut_beg, cut_end] from src to dest
    911 static void crossline_text_copy (char *dest, const char *src, int cut_beg, int cut_end)
    912 {
    913 	int len = cut_end - cut_beg;
    914 	len = (len < CROSS_HISTORY_BUF_LEN) ? len : (CROSS_HISTORY_BUF_LEN - 1);
    915 	if (len > 0) {
    916 		memcpy (dest, &src[cut_beg], len);
    917 		dest[len] = '\0';
    918 	}
    919 }
    920 
    921 // Copy from history buffer to dest
    922 static void crossline_history_copy (const char *prompt, char *buf, int size, int *pos, int *num, int history_id)
    923 {
    924 	strncpy (buf, s_history_buf[history_id % CROSS_HISTORY_MAX_LINE], size - 1);
    925 	buf[size - 1] = '\0';
    926 	crossline_refreash (prompt, buf, pos, num, (int)strlen(buf), (int)strlen(buf), 1);
    927 }
    928 
    929 /*****************************************************************************/
    930 
    931 // Convert ESC+Key to Alt-Key
    932 static int crossline_key_esc2alt (int ch)
    933 {
    934 	switch (ch) {
    935 	case KEY_DEL:	ch = KEY_ALT_DEL;	break;
    936 	case KEY_HOME:	ch = KEY_ALT_HOME;	break;
    937 	case KEY_END:	ch = KEY_ALT_END;	break;
    938 	case KEY_UP:	ch = KEY_ALT_UP;	break;
    939 	case KEY_DOWN:	ch = KEY_ALT_DOWN;	break;
    940 	case KEY_LEFT:	ch = KEY_ALT_LEFT;	break;
    941 	case KEY_RIGHT:	ch = KEY_ALT_RIGHT;	break;
    942 	case KEY_BACKSPACE:	ch = KEY_ALT_BACKSPACE;	break;
    943 	}
    944 	return ch;
    945 }
    946 
    947 // Map other function keys to main key
    948 static int crossline_key_mapping (int ch)
    949 {
    950 	switch (ch) {
    951 #ifndef _WIN32
    952 	case KEY_HOME2:			ch = KEY_HOME;			break;
    953 	case KEY_END2:			ch = KEY_END;			break;
    954 	case KEY_CTRL_UP2:		ch = KEY_CTRL_UP;		break;
    955 	case KEY_CTRL_DOWN2:	ch = KEY_CTRL_DOWN;		break;
    956 	case KEY_CTRL_LEFT2:	ch = KEY_CTRL_LEFT;		break;
    957 	case KEY_CTRL_RIGHT2:	ch = KEY_CTRL_RIGHT;	break;
    958 	case KEY_F1_2:			ch = KEY_F1;			break;
    959 	case KEY_F2_2:			ch = KEY_F2;			break;
    960 	case KEY_F3_2:			ch = KEY_F3;			break;
    961 	case KEY_F4_2:			ch = KEY_F4;			break;
    962 #endif
    963 	case KEY_DEL2:			ch = KEY_BACKSPACE;		break;
    964 	}
    965 	return ch;
    966 }
    967 
    968 #ifdef _WIN32	// Windows
    969 // Read a KEY from keyboard, is_esc indicats whether it's a function key.
    970 static int crossline_getkey (int *is_esc)
    971 {
    972 	int ch = crossline_getch (), esc;
    973 	if ((GetKeyState (VK_CONTROL) & 0x8000) && (KEY_DEL2 == ch)) {
    974 		ch = KEY_CTRL_BACKSPACE;
    975 	} else if ((224 == ch) || (0 == ch)) {
    976 		*is_esc = 1;
    977 		ch = crossline_getch ();
    978 		ch = (GetKeyState (VK_MENU) & 0x8000) ? ALT_KEY(ch) : ch + (KEY_ESC<<8);
    979 	} else if (KEY_ESC == ch) { // Handle ESC+Key
    980 		*is_esc = 1;
    981 		ch = crossline_getkey (&esc);
    982 		ch = crossline_key_esc2alt (ch);
    983 	} else if (GetKeyState (VK_MENU) & 0x8000 && !(GetKeyState (VK_CONTROL) & 0x8000) ) {
    984 		*is_esc = 1; ch = ALT_KEY(ch);
    985 	}
    986 	return ch;
    987 }
    988 
    989 void crossline_winchg_reg (void)	{ }
    990 
    991 #else // Linux
    992 
    993 // Convert escape sequences to internal special function key
    994 static int crossline_get_esckey (int ch)
    995 {
    996 	int ch2;
    997 	if (0 == ch)	{ ch = crossline_getch (); }
    998 	if ('[' == ch) {
    999 		ch = crossline_getch ();
   1000 		if ((ch>='0') && (ch<='6')) {
   1001 			ch2 = crossline_getch ();
   1002 			if ('~' == ch2)	{ ch = ESC_KEY4 (ch, ch2); } // ex. Esc[4~
   1003 			else if (';' == ch2) {
   1004 				ch2 = crossline_getch();
   1005 				if (('5' != ch2) && ('3' != ch2))
   1006 					{ return 0; }
   1007 				ch = ESC_KEY6 (ch, ch2, crossline_getch()); // ex. Esc[1;5B
   1008 			}
   1009 		} else if ('[' == ch) {
   1010 			ch = ESC_KEY4 ('[', crossline_getch());	// ex. Esc[[A
   1011 		} else { ch = ESC_KEY3 (ch); }	// ex. Esc[A
   1012 	} else if ('O' == ch) {
   1013 		ch = ESC_OKEY (crossline_getch());	// ex. EscOP
   1014 	} else { ch = ALT_KEY (ch); } // ex. Alt+Backspace
   1015 	return ch;
   1016 }
   1017 
   1018 // Read a KEY from keyboard, is_esc indicats whether it's a function key.
   1019 static int crossline_getkey (int *is_esc)
   1020 {
   1021 	int ch = crossline_getch();
   1022 	if (KEY_ESC == ch) {
   1023 		*is_esc = 1;
   1024 		ch = crossline_getch ();
   1025 		if (KEY_ESC == ch) { // Handle ESC+Key
   1026 			ch = crossline_get_esckey (0);
   1027 			ch = crossline_key_mapping (ch);
   1028 			ch = crossline_key_esc2alt (ch);
   1029 		} else { ch = crossline_get_esckey (ch); }
   1030 	}
   1031 	return ch;
   1032 }
   1033 
   1034 static void crossline_winchg_event (int arg)
   1035 { s_got_resize = 1; }
   1036 static void crossline_winchg_reg (void)
   1037 {
   1038 	struct sigaction sa;
   1039 	sigemptyset(&sa.sa_mask);
   1040 	sa.sa_flags = 0;
   1041 	sa.sa_handler = &crossline_winchg_event;
   1042 	sigaction (SIGWINCH, &sa, NULL);
   1043 	s_got_resize = 0;
   1044 }
   1045 
   1046 #endif // #ifdef _WIN32
   1047 
   1048 /*****************************************************************************/
   1049 
   1050 /* Internal readline from terminal. has_input indicates buf has inital input.
   1051  * in_his will disable history and complete shortcuts
   1052  */
   1053 static char* crossline_readline_edit (char *buf, int size, const char *prompt, int has_input, int in_his)
   1054 {
   1055 	int		pos = 0, num = 0, read_end = 0, is_esc;
   1056 	int		ch, len, new_pos, copy_buf = 0, i, len2;
   1057 	uint32_t history_id = s_history_id, search_his;
   1058 	char	input[CROSS_HISTORY_BUF_LEN];
   1059 	crossline_completions_t		completions;
   1060 
   1061 	prompt = (NULL != prompt) ? prompt : "";
   1062 	if (has_input) {
   1063 		num = pos = (int)strlen (buf);
   1064 		crossline_text_copy (input, buf, pos, num);
   1065 	} else
   1066 		{ buf[0] = input[0] = '\0'; }
   1067 	crossline_print (prompt, buf, &pos, &num, pos, num);
   1068 	crossline_winchg_reg ();
   1069 
   1070 	do {
   1071 		is_esc = 0;
   1072 		ch = crossline_getkey (&is_esc);
   1073 		ch = crossline_key_mapping (ch);
   1074 
   1075 		if (s_got_resize) { // Handle window resizing for Linux, Windows can handle it automatically
   1076 			new_pos = pos;
   1077 			crossline_refreash (prompt, buf, &pos, &num, 0, num, 0); // goto beginning of line
   1078 			printf ("\x1b[J"); // clear to end of screen
   1079 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num, 1);
   1080 			s_got_resize = 0;
   1081 		}
   1082 
   1083 		switch (ch) {
   1084 /* Misc Commands */
   1085 		case KEY_F1:	// Show help
   1086 			crossline_show_help (in_his);
   1087 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1088 			break;
   1089 
   1090 		case KEY_DEBUG:	// Enter keyboard debug mode
   1091 			printf(" \b\nEnter keyboard debug mode, <Ctrl-C> to exit debug\n");
   1092 			while (CTRL_KEY('C') != (ch=crossline_getch()))
   1093 				{ printf ("%3d 0x%02x (%c)\n", ch, ch, isprint(ch) ? ch : ' '); }
   1094 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1095 			break;
   1096 
   1097 /* Move Commands */
   1098 		case KEY_LEFT:	// Move back a character.
   1099 		case CTRL_KEY('B'):
   1100 			if (pos > 0)
   1101 				{ crossline_refreash (prompt, buf, &pos, &num, pos-1, num, 0); }
   1102 			break;
   1103 
   1104 		case KEY_RIGHT:	// Move forward a character.
   1105 		case CTRL_KEY('F'):
   1106 			if (pos < num)
   1107 				{ crossline_refreash (prompt, buf, &pos, &num, pos+1, num, 0); }
   1108 			break;
   1109 
   1110 		case ALT_KEY('b'):	// Move back a word.
   1111 		case ALT_KEY('B'):
   1112 		case KEY_CTRL_LEFT:
   1113 		case KEY_ALT_LEFT:
   1114 			for (new_pos=pos-1; (new_pos > 0) && isdelim(buf[new_pos]); --new_pos)	;
   1115 			for (; (new_pos > 0) && !isdelim(buf[new_pos]); --new_pos)	;
   1116 			crossline_refreash (prompt, buf, &pos, &num, new_pos?new_pos+1:new_pos, num, 0);
   1117 			break;
   1118 
   1119 		case ALT_KEY('f'):	 // Move forward a word.
   1120 		case ALT_KEY('F'):
   1121 		case KEY_CTRL_RIGHT:
   1122 		case KEY_ALT_RIGHT:
   1123 			for (new_pos=pos; (new_pos < num) && isdelim(buf[new_pos]); ++new_pos)	;
   1124 			for (; (new_pos < num) && !isdelim(buf[new_pos]); ++new_pos)	;
   1125 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num, 0);
   1126 			break;
   1127 
   1128 		case CTRL_KEY('A'):	// Move cursor to start of line.
   1129 		case KEY_HOME:
   1130 			crossline_refreash (prompt, buf, &pos, &num, 0, num, 0);
   1131 			break;
   1132 
   1133 		case CTRL_KEY('E'):	// Move cursor to end of line
   1134 		case KEY_END:
   1135 			crossline_refreash (prompt, buf, &pos, &num, num, num, 0);
   1136 			break;
   1137 
   1138 		case CTRL_KEY('L'):	// Clear screen and redisplay line
   1139 			crossline_screen_clear ();
   1140 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1141 			break;
   1142 
   1143 		case KEY_CTRL_UP: // Move to up line
   1144 		case KEY_ALT_UP:
   1145 			crossline_updown_move (prompt, &pos, &num, -1, 1);
   1146 			break;
   1147 
   1148 		case KEY_ALT_DOWN: // Move to down line
   1149 		case KEY_CTRL_DOWN:
   1150 			crossline_updown_move (prompt, &pos, &num, 1, 1);
   1151 			break;
   1152 
   1153 /* Edit Commands */
   1154 		case KEY_BACKSPACE: // Delete char to left of cursor (same with CTRL_KEY('H'))
   1155 			if (pos > 0) {
   1156 				memmove (&buf[pos-1], &buf[pos], num - pos);
   1157 				crossline_refreash (prompt, buf, &pos, &num, pos-1, num-1, 1);
   1158 			}
   1159 			break;
   1160 
   1161 		case KEY_DEL:	// Delete character under cursor
   1162 		case CTRL_KEY('D'):
   1163 			if (pos < num) {
   1164 				memmove (&buf[pos], &buf[pos+1], num - pos - 1);
   1165 				crossline_refreash (prompt, buf, &pos, &num, pos, num - 1, 1);
   1166 			} else if ((0 == num) && (ch == CTRL_KEY('D'))) // On an empty line, EOF
   1167 				 { printf (" \b\n"); read_end = -1; }
   1168 			break;
   1169 
   1170 		case ALT_KEY('u'):	// Uppercase current or following word.
   1171 		case ALT_KEY('U'):
   1172 			for (new_pos = pos; (new_pos < num) && isdelim(buf[new_pos]); ++new_pos)	;
   1173 			for (; (new_pos < num) && !isdelim(buf[new_pos]); ++new_pos)
   1174 				{ buf[new_pos] = (char)toupper (buf[new_pos]); }
   1175 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num, 1);
   1176 			break;
   1177 
   1178 		case ALT_KEY('l'):	// Lowercase current or following word.
   1179 		case ALT_KEY('L'):
   1180 			for (new_pos = pos; (new_pos < num) && isdelim(buf[new_pos]); ++new_pos)	;
   1181 			for (; (new_pos < num) && !isdelim(buf[new_pos]); ++new_pos)
   1182 				{ buf[new_pos] = (char)tolower (buf[new_pos]); }
   1183 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num, 1);
   1184 			break;
   1185 
   1186 		case ALT_KEY('c'):	// Capitalize current or following word.
   1187 		case ALT_KEY('C'):
   1188 			for (new_pos = pos; (new_pos < num) && isdelim(buf[new_pos]); ++new_pos)	;
   1189 			if (new_pos<num)
   1190 				{ buf[new_pos] = (char)toupper (buf[new_pos]); }
   1191 			for (; new_pos<num && !isdelim(buf[new_pos]); ++new_pos)	;
   1192 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num, 1);
   1193 			break;
   1194 
   1195 		case ALT_KEY('\\'): // Delete whitespace around cursor.
   1196 			for (new_pos = pos; (new_pos > 0) && (' ' == buf[new_pos]); --new_pos)	;
   1197 			memmove (&buf[new_pos], &buf[pos], num - pos);
   1198 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num - (pos-new_pos), 1);
   1199 			for (new_pos = pos; (new_pos < num) && (' ' == buf[new_pos]); ++new_pos)	;
   1200 			memmove (&buf[pos], &buf[new_pos], num - new_pos);
   1201 			crossline_refreash (prompt, buf, &pos, &num, pos, num - (new_pos-pos), 1);
   1202 			break;
   1203 
   1204 		case CTRL_KEY('T'): // Transpose previous character with current character.
   1205 			if ((pos > 0) && !isdelim(buf[pos]) && !isdelim(buf[pos-1])) {
   1206 				ch = buf[pos];
   1207 				buf[pos] = buf[pos-1];
   1208 				buf[pos-1] = (char)ch;
   1209 				crossline_refreash (prompt, buf, &pos, &num, pos<num?pos+1:pos, num, 1);
   1210 			} else if ((pos > 1) && !isdelim(buf[pos-1]) && !isdelim(buf[pos-2])) {
   1211 				ch = buf[pos-1];
   1212 				buf[pos-1] = buf[pos-2];
   1213 				buf[pos-2] = (char)ch;
   1214 				crossline_refreash (prompt, buf, &pos, &num, pos, num, 1);
   1215 			}
   1216 			break;
   1217 
   1218 /* Cut&Paste Commands */
   1219 		case CTRL_KEY('K'): // Cut from cursor to end of line.
   1220 		case KEY_CTRL_END:
   1221 		case KEY_ALT_END:
   1222 			crossline_text_copy (s_clip_buf, buf, pos, num);
   1223 			crossline_refreash (prompt, buf, &pos, &num, pos, pos, 1);
   1224 			break;
   1225 
   1226 		case CTRL_KEY('U'): // Cut from start of line to cursor.
   1227 		case KEY_CTRL_HOME:
   1228 		case KEY_ALT_HOME:
   1229 			crossline_text_copy (s_clip_buf, buf, 0, pos);
   1230 			memmove (&buf[0], &buf[pos], num-pos);
   1231 			crossline_refreash (prompt, buf, &pos, &num, 0, num - pos, 1);
   1232 			break;
   1233 
   1234 		case CTRL_KEY('X'):	// Cut whole line.
   1235 			crossline_text_copy (s_clip_buf, buf, 0, num);
   1236 			// fall through
   1237 		case ALT_KEY('r'):	// Revert line
   1238 		case ALT_KEY('R'):
   1239 			crossline_refreash (prompt, buf, &pos, &num, 0, 0, 1);
   1240 			break;
   1241 
   1242 		case CTRL_KEY('W'): // Cut whitespace (not word) to left of cursor.
   1243 		case KEY_ALT_BACKSPACE: // Cut word to left of cursor.
   1244 		case KEY_CTRL_BACKSPACE:
   1245 			new_pos = pos;
   1246 			if ((new_pos > 1) && isdelim(buf[new_pos-1]))	{ --new_pos; }
   1247 			for (; (new_pos > 0) && isdelim(buf[new_pos]); --new_pos)	;
   1248 			if (CTRL_KEY('W') == ch) {
   1249 				for (; (new_pos > 0) && (' ' != buf[new_pos]); --new_pos)	;
   1250 			} else {
   1251 				for (; (new_pos > 0) && !isdelim(buf[new_pos]); --new_pos)	;
   1252 			}
   1253 			if ((new_pos>0) && (new_pos<pos) && isdelim(buf[new_pos]))	{ new_pos++; }
   1254 			crossline_text_copy (s_clip_buf, buf, new_pos, pos);
   1255 			memmove (&buf[new_pos], &buf[pos], num - pos);
   1256 			crossline_refreash (prompt, buf, &pos, &num, new_pos, num - (pos-new_pos), 1);
   1257 			break;
   1258 
   1259 		case ALT_KEY('d'): // Cut word following cursor.
   1260 		case ALT_KEY('D'):
   1261 		case KEY_ALT_DEL:
   1262 		case KEY_CTRL_DEL:
   1263 			for (new_pos = pos; (new_pos < num) && isdelim(buf[new_pos]); ++new_pos)	;
   1264 			for (; (new_pos < num) && !isdelim(buf[new_pos]); ++new_pos)	;
   1265 			crossline_text_copy (s_clip_buf, buf, pos, new_pos);
   1266 			memmove (&buf[pos], &buf[new_pos], num - new_pos);
   1267 			crossline_refreash (prompt, buf, &pos, &num, pos, num - (new_pos-pos), 1);
   1268 			break;
   1269 
   1270 		case CTRL_KEY('Y'):	// Paste last cut text.
   1271 		case CTRL_KEY('V'):
   1272 		case KEY_INSERT:
   1273 			if ((len=(int)strlen(s_clip_buf)) + num < size) {
   1274 				memmove (&buf[pos+len], &buf[pos], num - pos);
   1275 				memcpy (&buf[pos], s_clip_buf, len);
   1276 				crossline_refreash (prompt, buf, &pos, &num, pos+len, num+len, 1);
   1277 			}
   1278 			break;
   1279 
   1280 /* Complete Commands */
   1281 		case KEY_TAB:		// Autocomplete (same with CTRL_KEY('I'))
   1282 		case ALT_KEY('='):	// List possible completions.
   1283 		case ALT_KEY('?'):
   1284 			if (in_his || (NULL == s_completion_callback) || (pos != num))
   1285 				{ break; }
   1286 			buf[pos] = '\0';
   1287 			completions.num = 0;
   1288 			completions.hints[0] = '\0';
   1289 			s_completion_callback (buf, &completions);
   1290 			if (completions.num >= 1) {
   1291 				if (KEY_TAB == ch) {
   1292 					len2 = len = (int)strlen(completions.word[0]);
   1293 					// Find common string for autocompletion
   1294 					for (i = 1; (i < completions.num) && (len > 0); ++i) {
   1295 						while ((len > 0) && strncasecmp(completions.word[0], completions.word[i], len)) { len--; }
   1296 					}
   1297 					if (len > 0) {
   1298 						if (len2 > num) len2 = num;
   1299 						while ((len2 > 0) && strncasecmp(completions.word[0], &buf[num-len2], len2)) { len2--; }
   1300 						new_pos = num - len2;
   1301 						if (new_pos+i+1 < size) {
   1302 							for (i = 0; i < len; ++i) { buf[new_pos+i] = completions.word[0][i]; }
   1303 							if (1 == completions.num) { buf[new_pos + (i++)] = ' '; }
   1304 							crossline_refreash (prompt, buf, &pos, &num, new_pos+i, new_pos+i, 1);
   1305 						}
   1306 					}
   1307 				}
   1308 			}
   1309 			if (((completions.num != 1) || (KEY_TAB != ch)) && crossline_show_completions(&completions))
   1310 				{ crossline_print (prompt, buf, &pos, &num, pos, num); }
   1311 			break;
   1312 
   1313 /* History Commands */
   1314 		case KEY_UP:		// Fetch previous line in history.
   1315 			if (crossline_updown_move (prompt, &pos, &num, -1, 0)) { break; } // check multi line move up
   1316 		case CTRL_KEY('P'):
   1317 			if (in_his) { break; }
   1318 			if (!copy_buf)
   1319 				{ crossline_text_copy (input, buf, 0, num); copy_buf = 1; }
   1320 			if ((history_id > 0) && (history_id+CROSS_HISTORY_MAX_LINE > s_history_id))
   1321 				{ crossline_history_copy (prompt, buf, size, &pos, &num, --history_id); }
   1322 			break;
   1323 
   1324 		case KEY_DOWN:		// Fetch next line in history.
   1325 			if (crossline_updown_move (prompt, &pos, &num, 1, 0)) { break; } // check multi line move down
   1326 		case CTRL_KEY('N'):
   1327 			if (in_his) { break; }
   1328 			if (!copy_buf)
   1329 				{ crossline_text_copy (input, buf, 0, num); copy_buf = 1; }
   1330 			if (history_id+1 < s_history_id)
   1331 				{ crossline_history_copy (prompt, buf, size, &pos, &num, ++history_id); }
   1332 			else {
   1333 				history_id = s_history_id;
   1334 				strncpy (buf, input, size - 1);
   1335 				buf[size - 1] = '\0';
   1336 				crossline_refreash (prompt, buf, &pos, &num, (int)strlen(buf), (int)strlen(buf), 1);
   1337 			}
   1338 			break; //case UP/DOWN
   1339 
   1340 		case ALT_KEY('<'):	// Move to first line in history.
   1341 		case KEY_PGUP:
   1342 			if (in_his) { break; }
   1343 			if (!copy_buf)
   1344 				{ crossline_text_copy (input, buf, 0, num); copy_buf = 1; }
   1345 			if (s_history_id > 0) {
   1346 				history_id = s_history_id < CROSS_HISTORY_MAX_LINE ? 0 : s_history_id-CROSS_HISTORY_MAX_LINE;
   1347 				crossline_history_copy (prompt, buf, size, &pos, &num, history_id);
   1348 			}
   1349 			break;
   1350 
   1351 		case ALT_KEY('>'):	// Move to end of input history.
   1352 		case KEY_PGDN:
   1353 			if (in_his) { break; }
   1354 			if (!copy_buf)
   1355 				{ crossline_text_copy (input, buf, 0, num); copy_buf = 1; }
   1356 			history_id = s_history_id;
   1357 			strncpy (buf, input, size-1);
   1358 			buf[size-1] = '\0';
   1359 			crossline_refreash (prompt, buf, &pos, &num, (int)strlen(buf), (int)strlen(buf), 1);
   1360 			break;
   1361 
   1362 		case CTRL_KEY('R'):	// Search history
   1363 		case CTRL_KEY('S'):
   1364 		case KEY_F4:		// Search history with current input.
   1365 			if (in_his) { break; }
   1366 			crossline_text_copy (input, buf, 0, num);
   1367 			search_his = crossline_history_search ((KEY_F4 == ch) ? buf : NULL);
   1368 			if (search_his > 0)
   1369 				{ strncpy (buf, s_history_buf[search_his-1], size-1); }
   1370 			else { strncpy (buf, input, size-1); }
   1371 			buf[size-1] = '\0';
   1372 			crossline_print (prompt, buf, &pos, &num, (int)strlen(buf), (int)strlen(buf));
   1373 			break;
   1374 
   1375 		case KEY_F2:	// Show history
   1376 			if (in_his || (0 == s_history_id)) { break; }
   1377 			printf (" \b\n");
   1378 			crossline_history_show ();
   1379 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1380 			break;
   1381 
   1382 		case KEY_F3:	// Clear history
   1383 			if (in_his) { break; }
   1384 			printf(" \b\n!!! Confirm to clear history [y]: ");
   1385 			if ('y' == crossline_getch()) {
   1386 				printf(" \b\nHistory are cleared!");
   1387 				crossline_history_clear ();
   1388 				history_id = 0;
   1389 			}
   1390 			printf (" \b\n");
   1391 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1392 			break;
   1393 
   1394 /* Control Commands */
   1395 		case KEY_ENTER:		// Accept line (same with CTRL_KEY('M'))
   1396 		case KEY_ENTER2:	// same with CTRL_KEY('J')
   1397 			crossline_refreash (prompt, buf, &pos, &num, num, num, 0);
   1398 			printf (" \b\n");
   1399 			read_end = 1;
   1400 			break;
   1401 
   1402 		case CTRL_KEY('C'):	// Abort line.
   1403 		case CTRL_KEY('G'):
   1404 			crossline_refreash (prompt, buf, &pos, &num, num, num, 0);
   1405 			if (CTRL_KEY('C') == ch)	{ printf (" \b^C\n"); }
   1406 			else	{ printf (" \b\n"); }
   1407 			num = pos = 0;
   1408 			errno = EAGAIN;
   1409 			read_end = -1;
   1410 			break;;
   1411 
   1412 		case CTRL_KEY('Z'):
   1413 #ifndef _WIN32
   1414 			raise(SIGSTOP);    // Suspend current process
   1415 			crossline_print (prompt, buf, &pos, &num, pos, num);
   1416 #endif
   1417 			break;
   1418 
   1419 		default:
   1420 			if (!is_esc && isprint(ch) && (num < size-1)) {
   1421 				memmove (&buf[pos+1], &buf[pos], num - pos);
   1422 				buf[pos] = (char)ch;
   1423 				crossline_refreash (prompt, buf, &pos, &num, pos+1, num+1, pos+1);
   1424 				copy_buf = 0;
   1425 			}
   1426 			break;
   1427         } // switch( ch )
   1428 	 	fflush(stdout);
   1429 	} while ( !read_end );
   1430 
   1431 	if (read_end < 0) { return NULL; }
   1432 	if ((num > 0) && (' ' == buf[num-1]))	{ num--; }
   1433 	buf[num] = '\0';
   1434 	if (!in_his && (num > 0) && strcmp(buf,"history")) { // Save history
   1435 		if ((0 == s_history_id) || strncmp (buf, s_history_buf[(s_history_id-1)%CROSS_HISTORY_MAX_LINE], CROSS_HISTORY_BUF_LEN)) {
   1436 			strncpy (s_history_buf[s_history_id % CROSS_HISTORY_MAX_LINE], buf, CROSS_HISTORY_BUF_LEN);
   1437 			s_history_buf[s_history_id % CROSS_HISTORY_MAX_LINE][CROSS_HISTORY_BUF_LEN - 1] = '\0';
   1438 			history_id = ++s_history_id;
   1439 			copy_buf = 0;
   1440 		}
   1441 	}
   1442 
   1443 	return buf;
   1444 }