OCaml port of Linenoise
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add line completion

+106 -1786
+10 -2
example/main.ml
··· 1 + let complete s = 2 + match String.get s 0 with 3 + | 'h' -> [ "hello"; "hello there" ] 4 + | _ | (exception Invalid_argument _) -> [] 5 + 1 6 let () = 2 7 Fmt_tty.setup_std_outputs (); 3 8 let rec loop sys_break = 4 - let prompt = if sys_break then "[\x1b[31m130\x1b[0m] >> " else ">> " in 5 - match Bruit.bruit prompt with 9 + let prompt = 10 + if sys_break then "[\x1b[31m130\x1b[0m] \x1b[33m>>\x1b[0m " 11 + else "\x1b[33m>>\x1b[0m " 12 + in 13 + match Bruit.bruit ~complete prompt with 6 14 | String (Some s) -> 7 15 Fmt.pr "%s\n%!" s; 8 16 loop false
+91 -19
src/bruit.ml
··· 28 28 | _ -> Unknown (Uchar.of_char c) 29 29 30 30 module State = struct 31 + type completion = string -> string list 32 + 31 33 type t = { 32 - in_completion : int; 33 34 ifd : Unix.file_descr; 34 35 ofd : Unix.file_descr; 35 36 buf : bytes; ··· 45 46 old_row_pos : int; 46 47 history_index : int; 47 48 read_buf : Bytes.t; 49 + in_completion : bool; 50 + completion_idx : int; 51 + complete : completion option; 48 52 } 49 53 50 - let make ?(in_completion = 0) ?(old_pos = 0) ?(pos = 0) ?(len = 0) 51 - ?(ifd = Unix.stdin) ?(ofd = Unix.stdout) ~prompt buf = 54 + let make ?(in_completion = false) ?(completion_idx = 0) ?complete 55 + ?(old_pos = 0) ?(pos = 0) ?(len = 0) ?(ifd = Unix.stdin) 56 + ?(ofd = Unix.stdout) ~prompt buf = 52 57 { 53 58 in_completion; 54 59 ifd; ··· 64 69 old_row_pos = 1; 65 70 old_rows = 0; 66 71 history_index = 0; 72 + complete; 73 + completion_idx; 67 74 read_buf = Bytes.create 1 (* For reading a character *); 68 75 } 69 76 70 - let override ?in_completion ?ifd ?ofd ?buf ?buf_len ?prompt ?plen ?old_pos 71 - ?pos ?len ?cols ?old_rows ?old_row_pos ?history_index (t : t) = 77 + let override ?in_completion ?completion_idx ?complete ?ifd ?ofd ?buf ?buf_len 78 + ?prompt ?plen ?old_pos ?pos ?len ?cols ?old_rows ?old_row_pos 79 + ?history_index (t : t) = 72 80 { 73 81 in_completion = Option.value ~default:t.in_completion in_completion; 74 82 ifd = Option.value ~default:t.ifd ifd; ··· 84 92 old_rows = Option.value ~default:t.old_rows old_rows; 85 93 old_row_pos = Option.value ~default:t.old_row_pos old_row_pos; 86 94 history_index = Option.value ~default:t.history_index history_index; 95 + complete = (match complete with Some f -> Some f | None -> t.complete); 87 96 read_buf = t.read_buf; 97 + completion_idx = Option.value ~default:t.completion_idx completion_idx; 88 98 } 89 99 end 90 100 ··· 208 218 209 219 let refresh_line state = refresh_single_line ~flags:[ Rewrite ] state 210 220 221 + let refresh_line_with_completions (state : State.t) lcs = 222 + if state.completion_idx < List.length lcs then 223 + let saved_state = state in 224 + let saved_buf = Bytes.copy state.buf in 225 + let state = 226 + State.override 227 + ~len:(Bytes.length (List.nth lcs state.completion_idx)) 228 + ~pos:(Bytes.length (List.nth lcs state.completion_idx)) 229 + ~buf:(List.nth lcs state.completion_idx) 230 + state 231 + in 232 + let state : State.t = refresh_line state in 233 + State.override state ~len:saved_state.len ~pos:saved_state.pos 234 + ~buf:saved_buf 235 + else state 236 + 211 237 let edit_insert (state : State.t) c = 212 238 let clen = Uchar.utf_8_byte_length c in 213 239 (* At the end of the line *) ··· 244 270 in 245 271 refresh_line state 246 272 273 + let complete_line (state : State.t) c cn = 274 + match cn (String.of_bytes state.buf) with 275 + | [] -> (State.override ~in_completion:false state, `Char c) 276 + | xs -> 277 + let state, c = 278 + match key_of_char c with 279 + | Tab -> 280 + if not state.in_completion then 281 + ( State.override ~in_completion:true ~completion_idx:0 state, 282 + `Edit_more ) 283 + else 284 + ( State.override 285 + ~completion_idx: 286 + ((state.completion_idx + 1) mod (List.length xs + 1)) 287 + state, 288 + `Edit_more ) 289 + | _ -> 290 + let state = 291 + if state.completion_idx < List.length xs then begin 292 + let to_write = List.nth xs state.completion_idx in 293 + let to_write_len = String.length to_write in 294 + Bytes.blit_string to_write 0 state.buf 0 to_write_len; 295 + State.override ~len:to_write_len ~pos:to_write_len state 296 + end 297 + else state 298 + in 299 + (State.override ~in_completion:false state, `Char c) 300 + in 301 + if state.in_completion && state.completion_idx < List.length xs then begin 302 + (refresh_line_with_completions state (List.map Bytes.of_string xs), c) 303 + end 304 + else begin 305 + (refresh_line state, c) 306 + end 307 + 247 308 let edit_feed state = 248 309 match read_char state with 249 310 | `Editing -> Editing state 250 311 | `None -> Finished None 251 312 | `Some c -> ( 252 - (* TODO: line completion *) 253 - match key_of_char c with 254 - | Enter -> Finished (Some state.buf) 255 - | Unknown i -> 256 - let state = edit_insert state i in 257 - Editing state 258 - | Ctrl_d -> if Int.equal state.len 0 then Finished None else assert false 259 - | Ctrl_c -> Ctrl_c 260 - | Backspace -> Editing (edit_backspace state) 261 - | _ -> assert false) 313 + let uc = Uchar.of_char c in 314 + let state, c = 315 + if state.in_completion || key_of_char c = Tab then 316 + match state.complete with 317 + | None -> (state, `Char c) 318 + | Some cn -> complete_line state c cn 319 + else (state, `Char c) 320 + in 321 + match c with 322 + | `Edit_more -> Editing state 323 + | `Char c -> ( 324 + match key_of_char c with 325 + | Enter -> Finished (Some state.buf) 326 + | Ctrl_d -> 327 + if Int.equal state.len 0 then Finished None else assert false 328 + | Ctrl_c -> Ctrl_c 329 + | Backspace -> Editing (edit_backspace state) 330 + | Tab -> Editing state 331 + | Unknown _ | _ -> 332 + let state = edit_insert state uc in 333 + Editing state)) 262 334 263 335 type result = String of string option | Ctrl_c 264 336 265 - let blocking_edit ~stdin ~stdout buf ~prompt = 266 - let state = State.make ~prompt buf in 337 + let blocking_edit ?complete ~stdin ~stdout buf ~prompt = 338 + let state = State.make ?complete ~prompt buf in 267 339 let res = 268 340 edit_start ~stdin ~stdout state @@ fun state -> 269 341 let rec loop = function ··· 276 348 Format.printf "\n%!"; 277 349 res 278 350 279 - let bruit prompt = 351 + let bruit ?complete prompt = 280 352 let prompt = Bytes.of_string prompt in 281 353 let buf = Bytes.create max_line in 282 354 if not (Unix.isatty Unix.stdin) then failwith "Stdin is not a tty" 283 - else blocking_edit ~stdin:Unix.stdin ~stdout:Unix.stdout buf ~prompt 355 + else blocking_edit ?complete ~stdin:Unix.stdin ~stdout:Unix.stdout buf ~prompt 284 356 285 357 (* 286 358 * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
+5 -3
src/bruit.mli
··· 6 6 7 7 type result = String of string option | Ctrl_c 8 8 9 - val bruit : string -> result 10 - (** [bruit prompt] reads from [stdin] and returns the read string if any, and on 11 - [ctrl+c] returns {! Ctrl_c}. *) 9 + val bruit : ?complete:(string -> string list) -> string -> result 10 + (** [bruit ?complete prompt] reads from [stdin] and returns the read string if 11 + any, and on [ctrl+c] returns {! Ctrl_c}. 12 + 13 + @param complete Used for completions, defaults to [None]. *)
-1762
src/linenoise.c
··· 1 - /* linenoise.c -- guerrilla line editing library against the idea that a 2 - * line editing lib needs to be 20,000 lines of C code. 3 - * 4 - * You can find the latest source code at: 5 - * 6 - * http://github.com/antirez/linenoise 7 - * 8 - * Does a number of crazy assumptions that happen to be true in 99.9999% of 9 - * the 2010 UNIX computers around. 10 - * 11 - * ------------------------------------------------------------------------ 12 - * 13 - * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com> 14 - * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> 15 - * 16 - * All rights reserved. 17 - * 18 - * Redistribution and use in source and binary forms, with or without 19 - * modification, are permitted provided that the following conditions are 20 - * met: 21 - * 22 - * * Redistributions of source code must retain the above copyright 23 - * notice, this list of conditions and the following disclaimer. 24 - * 25 - * * Redistributions in binary form must reproduce the above copyright 26 - * notice, this list of conditions and the following disclaimer in the 27 - * documentation and/or other materials provided with the distribution. 28 - * 29 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 - * 41 - * ------------------------------------------------------------------------ 42 - * 43 - * References: 44 - * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html 45 - * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html 46 - * 47 - * Todo list: 48 - * - Filter bogus Ctrl+<char> combinations. 49 - * - Win32 support 50 - * 51 - * Bloat: 52 - * - History search like Ctrl+r in readline? 53 - * 54 - * List of escape sequences used by this program, we do everything just 55 - * with three sequences. In order to be so cheap we may have some 56 - * flickering effect with some slow terminal, but the lesser sequences 57 - * the more compatible. 58 - * 59 - * EL (Erase Line) 60 - * Sequence: ESC [ n K 61 - * Effect: if n is 0 or missing, clear from cursor to end of line 62 - * Effect: if n is 1, clear from beginning of line to cursor 63 - * Effect: if n is 2, clear entire line 64 - * 65 - * CUF (CUrsor Forward) 66 - * Sequence: ESC [ n C 67 - * Effect: moves cursor forward n chars 68 - * 69 - * CUB (CUrsor Backward) 70 - * Sequence: ESC [ n D 71 - * Effect: moves cursor backward n chars 72 - * 73 - * The following is used to get the terminal width if getting 74 - * the width with the TIOCGWINSZ ioctl fails 75 - * 76 - * DSR (Device Status Report) 77 - * Sequence: ESC [ 6 n 78 - * Effect: reports the current cusor position as ESC [ n ; m R 79 - * where n is the row and m is the column 80 - * 81 - * When multi line mode is enabled, we also use an additional escape 82 - * sequence. However multi line editing is disabled by default. 83 - * 84 - * CUU (Cursor Up) 85 - * Sequence: ESC [ n A 86 - * Effect: moves cursor up of n chars. 87 - * 88 - * CUD (Cursor Down) 89 - * Sequence: ESC [ n B 90 - * Effect: moves cursor down of n chars. 91 - * 92 - * When linenoiseClearScreen() is called, two additional escape sequences 93 - * are used in order to clear the screen and position the cursor at home 94 - * position. 95 - * 96 - * CUP (Cursor position) 97 - * Sequence: ESC [ H 98 - * Effect: moves the cursor to upper left corner 99 - * 100 - * ED (Erase display) 101 - * Sequence: ESC [ 2 J 102 - * Effect: clear the whole screen 103 - * 104 - */ 105 - 106 - #include <termios.h> 107 - #include <unistd.h> 108 - #include <stdlib.h> 109 - #include <stdio.h> 110 - #include <errno.h> 111 - #include <string.h> 112 - #include <stdlib.h> 113 - #include <ctype.h> 114 - #include <sys/stat.h> 115 - #include <sys/types.h> 116 - #include <sys/ioctl.h> 117 - #include <unistd.h> 118 - #include <stdint.h> 119 - #include "linenoise.h" 120 - 121 - #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 122 - #define LINENOISE_MAX_LINE 4096 123 - static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; 124 - static linenoiseCompletionCallback *completionCallback = NULL; 125 - static linenoiseHintsCallback *hintsCallback = NULL; 126 - static linenoiseFreeHintsCallback *freeHintsCallback = NULL; 127 - static char *linenoiseNoTTY(void); 128 - static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags); 129 - static void refreshLineWithFlags(struct linenoiseState *l, int flags); 130 - 131 - static struct termios orig_termios; /* In order to restore at exit.*/ 132 - static int maskmode = 0; /* Show "***" instead of input. For passwords. */ 133 - static int rawmode = 0; /* For atexit() function to check if restore is needed*/ 134 - static int mlmode = 0; /* Multi line mode. Default is single line. */ 135 - static int atexit_registered = 0; /* Register atexit just 1 time. */ 136 - static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; 137 - static int history_len = 0; 138 - static char **history = NULL; 139 - 140 - /* =========================== UTF-8 support ================================ */ 141 - 142 - /* Return the number of bytes that compose the UTF-8 character starting at 143 - * 'c'. This function assumes a valid UTF-8 encoding and handles the four 144 - * standard byte patterns: 145 - * 0xxxxxxx -> 1 byte (ASCII) 146 - * 110xxxxx -> 2 bytes 147 - * 1110xxxx -> 3 bytes 148 - * 11110xxx -> 4 bytes */ 149 - static int utf8ByteLen(char c) { 150 - unsigned char uc = (unsigned char)c; 151 - if ((uc & 0x80) == 0) return 1; /* 0xxxxxxx: ASCII */ 152 - if ((uc & 0xE0) == 0xC0) return 2; /* 110xxxxx: 2-byte seq */ 153 - if ((uc & 0xF0) == 0xE0) return 3; /* 1110xxxx: 3-byte seq */ 154 - if ((uc & 0xF8) == 0xF0) return 4; /* 11110xxx: 4-byte seq */ 155 - return 1; /* Fallback for invalid encoding, treat as single byte. */ 156 - } 157 - 158 - /* Decode a UTF-8 sequence starting at 's' into a Unicode codepoint. 159 - * Returns the codepoint value. Assumes valid UTF-8 encoding. */ 160 - static uint32_t utf8DecodeChar(const char *s, size_t *len) { 161 - unsigned char *p = (unsigned char *)s; 162 - uint32_t cp; 163 - 164 - if ((*p & 0x80) == 0) { 165 - *len = 1; 166 - return *p; 167 - } else if ((*p & 0xE0) == 0xC0) { 168 - *len = 2; 169 - cp = (*p & 0x1F) << 6; 170 - cp |= (p[1] & 0x3F); 171 - return cp; 172 - } else if ((*p & 0xF0) == 0xE0) { 173 - *len = 3; 174 - cp = (*p & 0x0F) << 12; 175 - cp |= (p[1] & 0x3F) << 6; 176 - cp |= (p[2] & 0x3F); 177 - return cp; 178 - } else if ((*p & 0xF8) == 0xF0) { 179 - *len = 4; 180 - cp = (*p & 0x07) << 18; 181 - cp |= (p[1] & 0x3F) << 12; 182 - cp |= (p[2] & 0x3F) << 6; 183 - cp |= (p[3] & 0x3F); 184 - return cp; 185 - } 186 - *len = 1; 187 - return *p; /* Fallback for invalid sequences. */ 188 - } 189 - 190 - /* Check if codepoint is a variation selector (emoji style modifiers). */ 191 - static int isVariationSelector(uint32_t cp) { 192 - return cp == 0xFE0E || cp == 0xFE0F; /* Text/emoji style */ 193 - } 194 - 195 - /* Check if codepoint is a skin tone modifier. */ 196 - static int isSkinToneModifier(uint32_t cp) { 197 - return cp >= 0x1F3FB && cp <= 0x1F3FF; 198 - } 199 - 200 - /* Check if codepoint is Zero Width Joiner. */ 201 - static int isZWJ(uint32_t cp) { 202 - return cp == 0x200D; 203 - } 204 - 205 - /* Check if codepoint is a Regional Indicator (for flag emoji). */ 206 - static int isRegionalIndicator(uint32_t cp) { 207 - return cp >= 0x1F1E6 && cp <= 0x1F1FF; 208 - } 209 - 210 - /* Check if codepoint is a combining mark or other zero-width character. */ 211 - static int isCombiningMark(uint32_t cp) { 212 - return (cp >= 0x0300 && cp <= 0x036F) || /* Combining Diacriticals */ 213 - (cp >= 0x1AB0 && cp <= 0x1AFF) || /* Combining Diacriticals Extended */ 214 - (cp >= 0x1DC0 && cp <= 0x1DFF) || /* Combining Diacriticals Supplement */ 215 - (cp >= 0x20D0 && cp <= 0x20FF) || /* Combining Diacriticals for Symbols */ 216 - (cp >= 0xFE20 && cp <= 0xFE2F); /* Combining Half Marks */ 217 - } 218 - 219 - /* Check if codepoint extends the previous character (doesn't start a new grapheme). */ 220 - static int isGraphemeExtend(uint32_t cp) { 221 - return isVariationSelector(cp) || isSkinToneModifier(cp) || 222 - isZWJ(cp) || isCombiningMark(cp); 223 - } 224 - 225 - /* Decode the UTF-8 codepoint ending at position 'pos' (exclusive) and 226 - * return its value. Also sets *cplen to the byte length of the codepoint. */ 227 - static uint32_t utf8DecodePrev(const char *buf, size_t pos, size_t *cplen) { 228 - if (pos == 0) { 229 - *cplen = 0; 230 - return 0; 231 - } 232 - /* Scan backwards to find the start byte. */ 233 - size_t i = pos; 234 - do { 235 - i--; 236 - } while (i > 0 && (pos - i) < 4 && ((unsigned char)buf[i] & 0xC0) == 0x80); 237 - *cplen = pos - i; 238 - size_t dummy; 239 - return utf8DecodeChar(buf + i, &dummy); 240 - } 241 - 242 - /* Given a buffer and a position, return the byte length of the grapheme 243 - * cluster before that position. A grapheme cluster includes: 244 - * - The base character 245 - * - Any following variation selectors, skin tone modifiers 246 - * - ZWJ sequences (emoji joined by Zero Width Joiner) 247 - * - Regional indicator pairs (flag emoji) */ 248 - static size_t utf8PrevCharLen(const char *buf, size_t pos) { 249 - if (pos == 0) return 0; 250 - 251 - size_t total = 0; 252 - size_t curpos = pos; 253 - 254 - /* First, get the last codepoint. */ 255 - size_t cplen; 256 - uint32_t cp = utf8DecodePrev(buf, curpos, &cplen); 257 - if (cplen == 0) return 0; 258 - total += cplen; 259 - curpos -= cplen; 260 - 261 - /* If we're at an extending character, we need to find what it extends. 262 - * Keep going back through the grapheme cluster. */ 263 - while (curpos > 0) { 264 - size_t prevlen; 265 - uint32_t prevcp = utf8DecodePrev(buf, curpos, &prevlen); 266 - if (prevlen == 0) break; 267 - 268 - if (isZWJ(prevcp)) { 269 - /* ZWJ joins two emoji. Include the ZWJ and continue to get 270 - * the preceding character. */ 271 - total += prevlen; 272 - curpos -= prevlen; 273 - /* Now get the character before ZWJ. */ 274 - prevcp = utf8DecodePrev(buf, curpos, &prevlen); 275 - if (prevlen == 0) break; 276 - total += prevlen; 277 - curpos -= prevlen; 278 - cp = prevcp; 279 - continue; /* Check if there's more extending before this. */ 280 - } else if (isGraphemeExtend(cp)) { 281 - /* Current cp is an extending character; include previous. */ 282 - total += prevlen; 283 - curpos -= prevlen; 284 - cp = prevcp; 285 - continue; 286 - } else if (isRegionalIndicator(cp) && isRegionalIndicator(prevcp)) { 287 - /* Two regional indicators form a flag. But we need to be careful: 288 - * flags are always pairs, so only join if we're at an even boundary. 289 - * For simplicity, just join one pair. */ 290 - total += prevlen; 291 - curpos -= prevlen; 292 - break; 293 - } else { 294 - /* No more extending; we've found the start of the cluster. */ 295 - break; 296 - } 297 - } 298 - 299 - return total; 300 - } 301 - 302 - /* Given a buffer, position and total length, return the byte length of the 303 - * grapheme cluster at the current position. */ 304 - static size_t utf8NextCharLen(const char *buf, size_t pos, size_t len) { 305 - if (pos >= len) return 0; 306 - 307 - size_t total = 0; 308 - size_t curpos = pos; 309 - 310 - /* Get the first codepoint. */ 311 - size_t cplen; 312 - uint32_t cp = utf8DecodeChar(buf + curpos, &cplen); 313 - total += cplen; 314 - curpos += cplen; 315 - 316 - int isRI = isRegionalIndicator(cp); 317 - 318 - /* Consume any extending characters that follow. */ 319 - while (curpos < len) { 320 - size_t nextlen; 321 - uint32_t nextcp = utf8DecodeChar(buf + curpos, &nextlen); 322 - 323 - if (isZWJ(nextcp) && curpos + nextlen < len) { 324 - /* ZWJ: include it and the following character. */ 325 - total += nextlen; 326 - curpos += nextlen; 327 - /* Get the character after ZWJ. */ 328 - nextcp = utf8DecodeChar(buf + curpos, &nextlen); 329 - total += nextlen; 330 - curpos += nextlen; 331 - continue; /* Check for more extending after the joined char. */ 332 - } else if (isGraphemeExtend(nextcp)) { 333 - /* Variation selector, skin tone, combining mark, etc. */ 334 - total += nextlen; 335 - curpos += nextlen; 336 - continue; 337 - } else if (isRI && isRegionalIndicator(nextcp)) { 338 - /* Second regional indicator for a flag pair. */ 339 - total += nextlen; 340 - curpos += nextlen; 341 - isRI = 0; /* Only pair once. */ 342 - continue; 343 - } else { 344 - break; 345 - } 346 - } 347 - 348 - return total; 349 - } 350 - 351 - /* Return the display width of a Unicode codepoint. This is a heuristic 352 - * that works for most common cases: 353 - * - Control chars and zero-width: 0 columns 354 - * - Grapheme-extending chars (VS, skin tone, ZWJ): 0 columns 355 - * - ASCII printable: 1 column 356 - * - Wide chars (CJK, emoji, fullwidth): 2 columns 357 - * - Everything else: 1 column 358 - * 359 - * This is not a full wcwidth() implementation, but a minimal heuristic 360 - * that handles emoji and CJK characters reasonably well. */ 361 - static int utf8CharWidth(uint32_t cp) { 362 - /* Control characters and combining marks: zero width. */ 363 - if (cp < 32 || (cp >= 0x7F && cp < 0xA0)) return 0; 364 - if (isCombiningMark(cp)) return 0; 365 - 366 - /* Grapheme-extending characters: zero width. 367 - * These modify the preceding character rather than taking space. */ 368 - if (isVariationSelector(cp)) return 0; 369 - if (isSkinToneModifier(cp)) return 0; 370 - if (isZWJ(cp)) return 0; 371 - 372 - /* Wide character ranges - these display as 2 columns: 373 - * - CJK Unified Ideographs and Extensions 374 - * - Fullwidth forms 375 - * - Various emoji ranges */ 376 - if (cp >= 0x1100 && 377 - (cp <= 0x115F || /* Hangul Jamo */ 378 - cp == 0x2329 || cp == 0x232A || /* Angle brackets */ 379 - (cp >= 0x231A && cp <= 0x231B) || /* Watch, Hourglass */ 380 - (cp >= 0x23E9 && cp <= 0x23F3) || /* Various symbols */ 381 - (cp >= 0x23F8 && cp <= 0x23FA) || /* Various symbols */ 382 - (cp >= 0x25AA && cp <= 0x25AB) || /* Small squares */ 383 - (cp >= 0x25B6 && cp <= 0x25C0) || /* Play/reverse buttons */ 384 - (cp >= 0x25FB && cp <= 0x25FE) || /* Squares */ 385 - (cp >= 0x2600 && cp <= 0x26FF) || /* Misc Symbols (sun, cloud, etc) */ 386 - (cp >= 0x2700 && cp <= 0x27BF) || /* Dingbats (❤, ✂, etc) */ 387 - (cp >= 0x2934 && cp <= 0x2935) || /* Arrows */ 388 - (cp >= 0x2B05 && cp <= 0x2B07) || /* Arrows */ 389 - (cp >= 0x2B1B && cp <= 0x2B1C) || /* Squares */ 390 - cp == 0x2B50 || cp == 0x2B55 || /* Star, circle */ 391 - (cp >= 0x2E80 && cp <= 0xA4CF && 392 - cp != 0x303F) || /* CJK ... Yi */ 393 - (cp >= 0xAC00 && cp <= 0xD7A3) || /* Hangul Syllables */ 394 - (cp >= 0xF900 && cp <= 0xFAFF) || /* CJK Compatibility Ideographs */ 395 - (cp >= 0xFE10 && cp <= 0xFE1F) || /* Vertical forms */ 396 - (cp >= 0xFE30 && cp <= 0xFE6F) || /* CJK Compatibility Forms */ 397 - (cp >= 0xFF00 && cp <= 0xFF60) || /* Fullwidth Forms */ 398 - (cp >= 0xFFE0 && cp <= 0xFFE6) || /* Fullwidth Signs */ 399 - (cp >= 0x1F1E6 && cp <= 0x1F1FF) || /* Regional Indicators (flags) */ 400 - (cp >= 0x1F300 && cp <= 0x1F64F) || /* Misc Symbols and Emoticons */ 401 - (cp >= 0x1F680 && cp <= 0x1F6FF) || /* Transport and Map Symbols */ 402 - (cp >= 0x1F900 && cp <= 0x1F9FF) || /* Supplemental Symbols */ 403 - (cp >= 0x1FA00 && cp <= 0x1FAFF) || /* Chess, Extended-A */ 404 - (cp >= 0x20000 && cp <= 0x2FFFF))) /* CJK Extension B and beyond */ 405 - return 2; 406 - 407 - return 1; /* Default: single width */ 408 - } 409 - 410 - /* Calculate the display width of a UTF-8 string of 'len' bytes. 411 - * This is used for cursor positioning in the terminal. 412 - * Handles grapheme clusters: characters joined by ZWJ contribute 0 width 413 - * after the first character in the sequence. */ 414 - static size_t utf8StrWidth(const char *s, size_t len) { 415 - size_t width = 0; 416 - size_t i = 0; 417 - int after_zwj = 0; /* Track if previous char was ZWJ */ 418 - 419 - while (i < len) { 420 - size_t clen; 421 - uint32_t cp = utf8DecodeChar(s + i, &clen); 422 - 423 - if (after_zwj) { 424 - /* Character after ZWJ: don't add width, it's joined. 425 - * But do check for extending chars after it. */ 426 - after_zwj = 0; 427 - } else { 428 - width += utf8CharWidth(cp); 429 - } 430 - 431 - /* Check if this is a ZWJ - next char will be joined. */ 432 - if (isZWJ(cp)) { 433 - after_zwj = 1; 434 - } 435 - 436 - i += clen; 437 - } 438 - return width; 439 - } 440 - 441 - /* Return the display width of a single UTF-8 character at position 's'. */ 442 - static int utf8SingleCharWidth(const char *s, size_t len) { 443 - if (len == 0) return 0; 444 - size_t clen; 445 - uint32_t cp = utf8DecodeChar(s, &clen); 446 - return utf8CharWidth(cp); 447 - } 448 - 449 - enum KEY_ACTION{ 450 - KEY_NULL = 0, /* NULL */ 451 - CTRL_A = 1, /* Ctrl+a */ 452 - CTRL_B = 2, /* Ctrl-b */ 453 - CTRL_C = 3, /* Ctrl-c */ 454 - CTRL_D = 4, /* Ctrl-d */ 455 - CTRL_E = 5, /* Ctrl-e */ 456 - CTRL_F = 6, /* Ctrl-f */ 457 - CTRL_H = 8, /* Ctrl-h */ 458 - TAB = 9, /* Tab */ 459 - CTRL_K = 11, /* Ctrl+k */ 460 - CTRL_L = 12, /* Ctrl+l */ 461 - ENTER = 13, /* Enter */ 462 - CTRL_N = 14, /* Ctrl-n */ 463 - CTRL_P = 16, /* Ctrl-p */ 464 - CTRL_T = 20, /* Ctrl-t */ 465 - CTRL_U = 21, /* Ctrl+u */ 466 - CTRL_W = 23, /* Ctrl+w */ 467 - ESC = 27, /* Escape */ 468 - BACKSPACE = 127 /* Backspace */ 469 - }; 470 - 471 - static void linenoiseAtExit(void); 472 - int linenoiseHistoryAdd(const char *line); 473 - #define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen 474 - #define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen. 475 - #define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both. 476 - static void refreshLine(struct linenoiseState *l); 477 - 478 - /* Debugging macro. */ 479 - #if 0 480 - FILE *lndebug_fp = NULL; 481 - #define lndebug(...) \ 482 - do { \ 483 - if (lndebug_fp == NULL) { \ 484 - lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ 485 - fprintf(lndebug_fp, \ 486 - "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ 487 - (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ 488 - (int)l->oldrows,old_rows); \ 489 - } \ 490 - fprintf(lndebug_fp, ", " __VA_ARGS__); \ 491 - fflush(lndebug_fp); \ 492 - } while (0) 493 - #else 494 - #define lndebug(fmt, ...) 495 - #endif 496 - 497 - /* ======================= Low level terminal handling ====================== */ 498 - 499 - /* Enable "mask mode". When it is enabled, instead of the input that 500 - * the user is typing, the terminal will just display a corresponding 501 - * number of asterisks, like "****". This is useful for passwords and other 502 - * secrets that should not be displayed. */ 503 - void linenoiseMaskModeEnable(void) { 504 - maskmode = 1; 505 - } 506 - 507 - /* Disable mask mode. */ 508 - void linenoiseMaskModeDisable(void) { 509 - maskmode = 0; 510 - } 511 - 512 - /* Set if to use or not the multi line mode. */ 513 - void linenoiseSetMultiLine(int ml) { 514 - mlmode = ml; 515 - } 516 - 517 - /* Return true if the terminal name is in the list of terminals we know are 518 - * not able to understand basic escape sequences. */ 519 - static int isUnsupportedTerm(void) { 520 - char *term = getenv("TERM"); 521 - int j; 522 - 523 - if (term == NULL) return 0; 524 - for (j = 0; unsupported_term[j]; j++) 525 - if (!strcasecmp(term,unsupported_term[j])) return 1; 526 - return 0; 527 - } 528 - 529 - /* Raw mode: 1960 magic shit. */ 530 - static int enableRawMode(int fd) { 531 - struct termios raw; 532 - 533 - /* Test mode: when LINENOISE_ASSUME_TTY is set, skip terminal setup. 534 - * This allows testing via pipes without a real terminal. */ 535 - if (getenv("LINENOISE_ASSUME_TTY")) { 536 - rawmode = 1; 537 - return 0; 538 - } 539 - 540 - if (!isatty(STDIN_FILENO)) goto fatal; 541 - if (!atexit_registered) { 542 - atexit(linenoiseAtExit); 543 - atexit_registered = 1; 544 - } 545 - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; 546 - 547 - raw = orig_termios; /* modify the original mode */ 548 - /* input modes: no break, no CR to NL, no parity check, no strip char, 549 - * no start/stop output control. */ 550 - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); 551 - /* output modes - disable post processing */ 552 - raw.c_oflag &= ~(OPOST); 553 - /* control modes - set 8 bit chars */ 554 - raw.c_cflag |= (CS8); 555 - /* local modes - choing off, canonical off, no extended functions, 556 - * no signal chars (^Z,^C) */ 557 - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); 558 - /* control chars - set return condition: min number of bytes and timer. 559 - * We want read to return every single byte, without timeout. */ 560 - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ 561 - 562 - /* put terminal in raw mode after flushing */ 563 - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; 564 - rawmode = 1; 565 - return 0; 566 - 567 - fatal: 568 - errno = ENOTTY; 569 - return -1; 570 - } 571 - 572 - static void disableRawMode(int fd) { 573 - /* Test mode: nothing to restore. */ 574 - if (getenv("LINENOISE_ASSUME_TTY")) { 575 - rawmode = 0; 576 - return; 577 - } 578 - /* Don't even check the return value as it's too late. */ 579 - if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) 580 - rawmode = 0; 581 - } 582 - 583 - /* Use the ESC [6n escape sequence to query the horizontal cursor position 584 - * and return it. On error -1 is returned, on success the position of the 585 - * cursor. */ 586 - static int getCursorPosition(int ifd, int ofd) { 587 - char buf[32]; 588 - int cols, rows; 589 - unsigned int i = 0; 590 - 591 - /* Report cursor location */ 592 - if (write(ofd, "\x1b[6n", 4) != 4) return -1; 593 - 594 - /* Read the response: ESC [ rows ; cols R */ 595 - while (i < sizeof(buf)-1) { 596 - if (read(ifd,buf+i,1) != 1) break; 597 - if (buf[i] == 'R') break; 598 - i++; 599 - } 600 - buf[i] = '\0'; 601 - 602 - /* Parse it. */ 603 - if (buf[0] != ESC || buf[1] != '[') return -1; 604 - if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; 605 - return cols; 606 - } 607 - 608 - /* Try to get the number of columns in the current terminal, or assume 80 609 - * if it fails. */ 610 - static int getColumns(int ifd, int ofd) { 611 - struct winsize ws; 612 - 613 - /* Test mode: use LINENOISE_COLS env var for fixed width. */ 614 - char *cols_env = getenv("LINENOISE_COLS"); 615 - if (cols_env) return atoi(cols_env); 616 - 617 - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { 618 - /* ioctl() failed. Try to query the terminal itself. */ 619 - int start, cols; 620 - 621 - /* Get the initial position so we can restore it later. */ 622 - start = getCursorPosition(ifd,ofd); 623 - if (start == -1) goto failed; 624 - 625 - /* Go to right margin and get position. */ 626 - if (write(ofd,"\x1b[999C",6) != 6) goto failed; 627 - cols = getCursorPosition(ifd,ofd); 628 - if (cols == -1) goto failed; 629 - 630 - /* Restore position. */ 631 - if (cols > start) { 632 - char seq[32]; 633 - snprintf(seq,32,"\x1b[%dD",cols-start); 634 - if (write(ofd,seq,strlen(seq)) == -1) { 635 - /* Can't recover... */ 636 - } 637 - } 638 - return cols; 639 - } else { 640 - return ws.ws_col; 641 - } 642 - 643 - failed: 644 - return 80; 645 - } 646 - 647 - /* Clear the screen. Used to handle ctrl+l */ 648 - void linenoiseClearScreen(void) { 649 - if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { 650 - /* nothing to do, just to avoid warning. */ 651 - } 652 - } 653 - 654 - /* Beep, used for completion when there is nothing to complete or when all 655 - * the choices were already shown. */ 656 - static void linenoiseBeep(void) { 657 - fprintf(stderr, "\x7"); 658 - fflush(stderr); 659 - } 660 - 661 - /* ============================== Completion ================================ */ 662 - 663 - /* Free a list of completion option populated by linenoiseAddCompletion(). */ 664 - static void freeCompletions(linenoiseCompletions *lc) { 665 - size_t i; 666 - for (i = 0; i < lc->len; i++) 667 - free(lc->cvec[i]); 668 - if (lc->cvec != NULL) 669 - free(lc->cvec); 670 - } 671 - 672 - /* Called by completeLine() and linenoiseShow() to render the current 673 - * edited line with the proposed completion. If the current completion table 674 - * is already available, it is passed as second argument, otherwise the 675 - * function will use the callback to obtain it. 676 - * 677 - * Flags are the same as refreshLine*(), that is REFRESH_* macros. */ 678 - static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) { 679 - /* Obtain the table of completions if the caller didn't provide one. */ 680 - linenoiseCompletions ctable = { 0, NULL }; 681 - if (lc == NULL) { 682 - completionCallback(ls->buf,&ctable); 683 - lc = &ctable; 684 - } 685 - 686 - /* Show the edited line with completion if possible, or just refresh. */ 687 - if (ls->completion_idx < lc->len) { 688 - struct linenoiseState saved = *ls; 689 - ls->len = ls->pos = strlen(lc->cvec[ls->completion_idx]); 690 - ls->buf = lc->cvec[ls->completion_idx]; 691 - refreshLineWithFlags(ls,flags); 692 - ls->len = saved.len; 693 - ls->pos = saved.pos; 694 - ls->buf = saved.buf; 695 - } else { 696 - refreshLineWithFlags(ls,flags); 697 - } 698 - 699 - /* Free the completions table if needed. */ 700 - if (lc != &ctable) freeCompletions(&ctable); 701 - } 702 - 703 - /* This is an helper function for linenoiseEdit*() and is called when the 704 - * user types the <tab> key in order to complete the string currently in the 705 - * input. 706 - * 707 - * The state of the editing is encapsulated into the pointed linenoiseState 708 - * structure as described in the structure definition. 709 - * 710 - * If the function returns non-zero, the caller should handle the 711 - * returned value as a byte read from the standard input, and process 712 - * it as usually: this basically means that the function may return a byte 713 - * read from the termianl but not processed. Otherwise, if zero is returned, 714 - * the input was consumed by the completeLine() function to navigate the 715 - * possible completions, and the caller should read for the next characters 716 - * from stdin. */ 717 - static int completeLine(struct linenoiseState *ls, int keypressed) { 718 - linenoiseCompletions lc = { 0, NULL }; 719 - int nwritten; 720 - char c = keypressed; 721 - 722 - completionCallback(ls->buf,&lc); 723 - if (lc.len == 0) { 724 - linenoiseBeep(); 725 - ls->in_completion = 0; 726 - } else { 727 - switch(c) { 728 - case 9: /* tab */ 729 - if (ls->in_completion == 0) { 730 - ls->in_completion = 1; 731 - ls->completion_idx = 0; 732 - } else { 733 - ls->completion_idx = (ls->completion_idx+1) % (lc.len+1); 734 - if (ls->completion_idx == lc.len) linenoiseBeep(); 735 - } 736 - c = 0; 737 - break; 738 - case 27: /* escape */ 739 - /* Re-show original buffer */ 740 - if (ls->completion_idx < lc.len) refreshLine(ls); 741 - ls->in_completion = 0; 742 - c = 0; 743 - break; 744 - default: 745 - /* Update buffer and return */ 746 - if (ls->completion_idx < lc.len) { 747 - nwritten = snprintf(ls->buf,ls->buflen,"%s", 748 - lc.cvec[ls->completion_idx]); 749 - ls->len = ls->pos = nwritten; 750 - } 751 - ls->in_completion = 0; 752 - break; 753 - } 754 - 755 - /* Show completion or original buffer */ 756 - if (ls->in_completion && ls->completion_idx < lc.len) { 757 - refreshLineWithCompletion(ls,&lc,REFRESH_ALL); 758 - } else { 759 - refreshLine(ls); 760 - } 761 - } 762 - 763 - freeCompletions(&lc); 764 - return c; /* Return last read character */ 765 - } 766 - 767 - /* Register a callback function to be called for tab-completion. */ 768 - void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { 769 - completionCallback = fn; 770 - } 771 - 772 - /* Register a hits function to be called to show hits to the user at the 773 - * right of the prompt. */ 774 - void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { 775 - hintsCallback = fn; 776 - } 777 - 778 - /* Register a function to free the hints returned by the hints callback 779 - * registered with linenoiseSetHintsCallback(). */ 780 - void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { 781 - freeHintsCallback = fn; 782 - } 783 - 784 - /* This function is used by the callback function registered by the user 785 - * in order to add completion options given the input string when the 786 - * user typed <tab>. See the example.c source code for a very easy to 787 - * understand example. */ 788 - void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { 789 - size_t len = strlen(str); 790 - char *copy, **cvec; 791 - 792 - copy = malloc(len+1); 793 - if (copy == NULL) return; 794 - memcpy(copy,str,len+1); 795 - cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); 796 - if (cvec == NULL) { 797 - free(copy); 798 - return; 799 - } 800 - lc->cvec = cvec; 801 - lc->cvec[lc->len++] = copy; 802 - } 803 - 804 - /* =========================== Line editing ================================= */ 805 - 806 - /* We define a very simple "append buffer" structure, that is an heap 807 - * allocated string where we can append to. This is useful in order to 808 - * write all the escape sequences in a buffer and flush them to the standard 809 - * output in a single call, to avoid flickering effects. */ 810 - struct abuf { 811 - char *b; 812 - int len; 813 - }; 814 - 815 - static void abInit(struct abuf *ab) { 816 - ab->b = NULL; 817 - ab->len = 0; 818 - } 819 - 820 - static void abAppend(struct abuf *ab, const char *s, int len) { 821 - char *new = realloc(ab->b,ab->len+len); 822 - 823 - if (new == NULL) return; 824 - memcpy(new+ab->len,s,len); 825 - ab->b = new; 826 - ab->len += len; 827 - } 828 - 829 - static void abFree(struct abuf *ab) { 830 - free(ab->b); 831 - } 832 - 833 - /* Helper of refreshSingleLine() and refreshMultiLine() to show hints 834 - * to the right of the prompt. Now uses display widths for proper UTF-8. */ 835 - void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pwidth) { 836 - char seq[64]; 837 - size_t bufwidth = utf8StrWidth(l->buf, l->len); 838 - if (hintsCallback && pwidth + bufwidth < l->cols) { 839 - int color = -1, bold = 0; 840 - char *hint = hintsCallback(l->buf,&color,&bold); 841 - if (hint) { 842 - size_t hintlen = strlen(hint); 843 - size_t hintwidth = utf8StrWidth(hint, hintlen); 844 - size_t hintmaxwidth = l->cols - (pwidth + bufwidth); 845 - /* Truncate hint to fit, respecting UTF-8 boundaries. */ 846 - if (hintwidth > hintmaxwidth) { 847 - size_t i = 0, w = 0; 848 - while (i < hintlen) { 849 - size_t clen = utf8NextCharLen(hint, i, hintlen); 850 - int cwidth = utf8SingleCharWidth(hint + i, clen); 851 - if (w + cwidth > hintmaxwidth) break; 852 - w += cwidth; 853 - i += clen; 854 - } 855 - hintlen = i; 856 - } 857 - if (bold == 1 && color == -1) color = 37; 858 - if (color != -1 || bold != 0) 859 - snprintf(seq,64,"\033[%d;%d;49m",bold,color); 860 - else 861 - seq[0] = '\0'; 862 - abAppend(ab,seq,strlen(seq)); 863 - abAppend(ab,hint,hintlen); 864 - if (color != -1 || bold != 0) 865 - abAppend(ab,"\033[0m",4); 866 - /* Call the function to free the hint returned. */ 867 - if (freeHintsCallback) freeHintsCallback(hint); 868 - } 869 - } 870 - } 871 - 872 - /* Single line low level line refresh. 873 - * 874 - * Rewrite the currently edited line accordingly to the buffer content, 875 - * cursor position, and number of columns of the terminal. 876 - * 877 - * Flags is REFRESH_* macros. The function can just remove the old 878 - * prompt, just write it, or both. 879 - * 880 - * This function is UTF-8 aware and uses display widths (not byte counts) 881 - * for cursor positioning and horizontal scrolling. */ 882 - static void refreshSingleLine(struct linenoiseState *l, int flags) { 883 - char seq[64]; 884 - size_t pwidth = utf8StrWidth(l->prompt, l->plen); /* Prompt display width */ 885 - int fd = l->ofd; 886 - char *buf = l->buf; 887 - size_t len = l->len; /* Byte length of buffer to display */ 888 - size_t pos = l->pos; /* Byte position of cursor */ 889 - size_t poscol; /* Display column of cursor */ 890 - size_t lencol; /* Display width of buffer */ 891 - struct abuf ab; 892 - 893 - /* Calculate the display width up to cursor and total display width. */ 894 - poscol = utf8StrWidth(buf, pos); 895 - lencol = utf8StrWidth(buf, len); 896 - 897 - /* Scroll the buffer horizontally if cursor is past the right edge. 898 - * We need to trim full UTF-8 characters from the left until the 899 - * cursor position fits within the terminal width. */ 900 - while (pwidth + poscol >= l->cols) { 901 - size_t clen = utf8NextCharLen(buf, 0, len); 902 - int cwidth = utf8SingleCharWidth(buf, clen); 903 - buf += clen; 904 - len -= clen; 905 - pos -= clen; 906 - poscol -= cwidth; 907 - lencol -= cwidth; 908 - } 909 - 910 - /* Trim from the right if the line still doesn't fit. */ 911 - while (pwidth + lencol > l->cols) { 912 - size_t clen = utf8PrevCharLen(buf, len); 913 - int cwidth = utf8SingleCharWidth(buf + len - clen, clen); 914 - len -= clen; 915 - lencol -= cwidth; 916 - } 917 - 918 - abInit(&ab); 919 - /* Cursor to left edge */ 920 - snprintf(seq,sizeof(seq),"\r"); 921 - abAppend(&ab,seq,strlen(seq)); 922 - 923 - if (flags & REFRESH_WRITE) { 924 - /* Write the prompt and the current buffer content */ 925 - abAppend(&ab,l->prompt,l->plen); 926 - if (maskmode == 1) { 927 - /* In mask mode, we output one '*' per UTF-8 character, not byte */ 928 - size_t i = 0; 929 - while (i < len) { 930 - abAppend(&ab,"*",1); 931 - i += utf8NextCharLen(buf, i, len); 932 - } 933 - } else { 934 - abAppend(&ab,buf,len); 935 - } 936 - /* Show hints if any. */ 937 - refreshShowHints(&ab,l,pwidth); 938 - } 939 - 940 - /* Erase to right */ 941 - snprintf(seq,sizeof(seq),"\x1b[0K"); 942 - abAppend(&ab,seq,strlen(seq)); 943 - 944 - if (flags & REFRESH_WRITE) { 945 - /* Move cursor to original position (using display column, not byte). */ 946 - snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(poscol+pwidth)); 947 - abAppend(&ab,seq,strlen(seq)); 948 - } 949 - 950 - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ 951 - abFree(&ab); 952 - } 953 - 954 - /* Multi line low level line refresh. 955 - * 956 - * Rewrite the currently edited line accordingly to the buffer content, 957 - * cursor position, and number of columns of the terminal. 958 - * 959 - * Flags is REFRESH_* macros. The function can just remove the old 960 - * prompt, just write it, or both. 961 - * 962 - * This function is UTF-8 aware and uses display widths for positioning. */ 963 - static void refreshMultiLine(struct linenoiseState *l, int flags) { 964 - char seq[64]; 965 - size_t pwidth = utf8StrWidth(l->prompt, l->plen); /* Prompt display width */ 966 - size_t bufwidth = utf8StrWidth(l->buf, l->len); /* Buffer display width */ 967 - size_t poswidth = utf8StrWidth(l->buf, l->pos); /* Cursor display width */ 968 - int rows = (pwidth+bufwidth+l->cols-1)/l->cols; /* rows used by current buf. */ 969 - int rpos = l->oldrpos; /* cursor relative row from previous refresh. */ 970 - int rpos2; /* rpos after refresh. */ 971 - int col; /* column position, zero-based. */ 972 - int old_rows = l->oldrows; 973 - int fd = l->ofd, j; 974 - struct abuf ab; 975 - 976 - l->oldrows = rows; 977 - 978 - /* First step: clear all the lines used before. To do so start by 979 - * going to the last row. */ 980 - abInit(&ab); 981 - 982 - if (flags & REFRESH_CLEAN) { 983 - if (old_rows-rpos > 0) { 984 - lndebug("go down %d", old_rows-rpos); 985 - snprintf(seq,64,"\x1b[%dB", old_rows-rpos); 986 - abAppend(&ab,seq,strlen(seq)); 987 - } 988 - 989 - /* Now for every row clear it, go up. */ 990 - for (j = 0; j < old_rows-1; j++) { 991 - lndebug("clear+up"); 992 - snprintf(seq,64,"\r\x1b[0K\x1b[1A"); 993 - abAppend(&ab,seq,strlen(seq)); 994 - } 995 - } 996 - 997 - if (flags & REFRESH_ALL) { 998 - /* Clean the top line. */ 999 - lndebug("clear"); 1000 - snprintf(seq,64,"\r\x1b[0K"); 1001 - abAppend(&ab,seq,strlen(seq)); 1002 - } 1003 - 1004 - if (flags & REFRESH_WRITE) { 1005 - /* Write the prompt and the current buffer content */ 1006 - abAppend(&ab,l->prompt,l->plen); 1007 - if (maskmode == 1) { 1008 - /* In mask mode, output one '*' per UTF-8 character, not byte */ 1009 - size_t i = 0; 1010 - while (i < l->len) { 1011 - abAppend(&ab,"*",1); 1012 - i += utf8NextCharLen(l->buf, i, l->len); 1013 - } 1014 - } else { 1015 - abAppend(&ab,l->buf,l->len); 1016 - } 1017 - 1018 - /* Show hints if any. */ 1019 - refreshShowHints(&ab,l,pwidth); 1020 - 1021 - /* If we are at the very end of the screen with our prompt, we need to 1022 - * emit a newline and move the prompt to the first column. */ 1023 - if (l->pos && 1024 - l->pos == l->len && 1025 - (poswidth+pwidth) % l->cols == 0) 1026 - { 1027 - lndebug("<newline>"); 1028 - abAppend(&ab,"\n",1); 1029 - snprintf(seq,64,"\r"); 1030 - abAppend(&ab,seq,strlen(seq)); 1031 - rows++; 1032 - if (rows > (int)l->oldrows) l->oldrows = rows; 1033 - } 1034 - 1035 - /* Move cursor to right position. */ 1036 - rpos2 = (pwidth+poswidth+l->cols)/l->cols; /* Current cursor relative row */ 1037 - lndebug("rpos2 %d", rpos2); 1038 - 1039 - /* Go up till we reach the expected position. */ 1040 - if (rows-rpos2 > 0) { 1041 - lndebug("go-up %d", rows-rpos2); 1042 - snprintf(seq,64,"\x1b[%dA", rows-rpos2); 1043 - abAppend(&ab,seq,strlen(seq)); 1044 - } 1045 - 1046 - /* Set column. */ 1047 - col = (pwidth+poswidth) % l->cols; 1048 - lndebug("set col %d", 1+col); 1049 - if (col) 1050 - snprintf(seq,64,"\r\x1b[%dC", col); 1051 - else 1052 - snprintf(seq,64,"\r"); 1053 - abAppend(&ab,seq,strlen(seq)); 1054 - } 1055 - 1056 - lndebug("\n"); 1057 - l->oldpos = l->pos; 1058 - if (flags & REFRESH_WRITE) l->oldrpos = rpos2; 1059 - 1060 - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ 1061 - abFree(&ab); 1062 - } 1063 - 1064 - /* Calls the two low level functions refreshSingleLine() or 1065 - * refreshMultiLine() according to the selected mode. */ 1066 - static void refreshLineWithFlags(struct linenoiseState *l, int flags) { 1067 - if (mlmode) 1068 - refreshMultiLine(l,flags); 1069 - else 1070 - refreshSingleLine(l,flags); 1071 - } 1072 - 1073 - /* Utility function to avoid specifying REFRESH_ALL all the times. */ 1074 - static void refreshLine(struct linenoiseState *l) { 1075 - refreshLineWithFlags(l,REFRESH_ALL); 1076 - } 1077 - 1078 - /* Hide the current line, when using the multiplexing API. */ 1079 - void linenoiseHide(struct linenoiseState *l) { 1080 - if (mlmode) 1081 - refreshMultiLine(l,REFRESH_CLEAN); 1082 - else 1083 - refreshSingleLine(l,REFRESH_CLEAN); 1084 - } 1085 - 1086 - /* Show the current line, when using the multiplexing API. */ 1087 - void linenoiseShow(struct linenoiseState *l) { 1088 - if (l->in_completion) { 1089 - refreshLineWithCompletion(l,NULL,REFRESH_WRITE); 1090 - } else { 1091 - refreshLineWithFlags(l,REFRESH_WRITE); 1092 - } 1093 - } 1094 - 1095 - /* Insert the character(s) 'c' of length 'clen' at cursor current position. 1096 - * This handles both single-byte ASCII and multi-byte UTF-8 sequences. 1097 - * 1098 - * On error writing to the terminal -1 is returned, otherwise 0. */ 1099 - int linenoiseEditInsert(struct linenoiseState *l, const char *c, size_t clen) { 1100 - if (l->len + clen <= l->buflen) { 1101 - if (l->len == l->pos) { 1102 - /* Append at end of line. */ 1103 - memcpy(l->buf+l->pos, c, clen); 1104 - l->pos += clen; 1105 - l->len += clen; 1106 - l->buf[l->len] = '\0'; 1107 - if ((!mlmode && 1108 - utf8StrWidth(l->prompt,l->plen)+utf8StrWidth(l->buf,l->len) < l->cols && 1109 - !hintsCallback)) { 1110 - /* Avoid a full update of the line in the trivial case: 1111 - * single-width char, no hints, fits in one line. */ 1112 - if (maskmode == 1) { 1113 - if (write(l->ofd,"*",1) == -1) return -1; 1114 - } else { 1115 - if (write(l->ofd,c,clen) == -1) return -1; 1116 - } 1117 - } else { 1118 - refreshLine(l); 1119 - } 1120 - } else { 1121 - /* Insert in the middle of the line. */ 1122 - memmove(l->buf+l->pos+clen, l->buf+l->pos, l->len-l->pos); 1123 - memcpy(l->buf+l->pos, c, clen); 1124 - l->len += clen; 1125 - l->pos += clen; 1126 - l->buf[l->len] = '\0'; 1127 - refreshLine(l); 1128 - } 1129 - } 1130 - return 0; 1131 - } 1132 - 1133 - /* Move cursor on the left. Moves by one UTF-8 character, not byte. */ 1134 - void linenoiseEditMoveLeft(struct linenoiseState *l) { 1135 - if (l->pos > 0) { 1136 - l->pos -= utf8PrevCharLen(l->buf, l->pos); 1137 - refreshLine(l); 1138 - } 1139 - } 1140 - 1141 - /* Move cursor on the right. Moves by one UTF-8 character, not byte. */ 1142 - void linenoiseEditMoveRight(struct linenoiseState *l) { 1143 - if (l->pos != l->len) { 1144 - l->pos += utf8NextCharLen(l->buf, l->pos, l->len); 1145 - refreshLine(l); 1146 - } 1147 - } 1148 - 1149 - /* Move cursor to the start of the line. */ 1150 - void linenoiseEditMoveHome(struct linenoiseState *l) { 1151 - if (l->pos != 0) { 1152 - l->pos = 0; 1153 - refreshLine(l); 1154 - } 1155 - } 1156 - 1157 - /* Move cursor to the end of the line. */ 1158 - void linenoiseEditMoveEnd(struct linenoiseState *l) { 1159 - if (l->pos != l->len) { 1160 - l->pos = l->len; 1161 - refreshLine(l); 1162 - } 1163 - } 1164 - 1165 - /* Substitute the currently edited line with the next or previous history 1166 - * entry as specified by 'dir'. */ 1167 - #define LINENOISE_HISTORY_NEXT 0 1168 - #define LINENOISE_HISTORY_PREV 1 1169 - void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { 1170 - if (history_len > 1) { 1171 - /* Update the current history entry before to 1172 - * overwrite it with the next one. */ 1173 - free(history[history_len - 1 - l->history_index]); 1174 - history[history_len - 1 - l->history_index] = strdup(l->buf); 1175 - /* Show the new entry */ 1176 - l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; 1177 - if (l->history_index < 0) { 1178 - l->history_index = 0; 1179 - return; 1180 - } else if (l->history_index >= history_len) { 1181 - l->history_index = history_len-1; 1182 - return; 1183 - } 1184 - strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); 1185 - l->buf[l->buflen-1] = '\0'; 1186 - l->len = l->pos = strlen(l->buf); 1187 - refreshLine(l); 1188 - } 1189 - } 1190 - 1191 - /* Delete the character at the right of the cursor without altering the cursor 1192 - * position. Basically this is what happens with the "Delete" keyboard key. 1193 - * Now handles multi-byte UTF-8 characters. */ 1194 - void linenoiseEditDelete(struct linenoiseState *l) { 1195 - if (l->len > 0 && l->pos < l->len) { 1196 - size_t clen = utf8NextCharLen(l->buf, l->pos, l->len); 1197 - memmove(l->buf+l->pos, l->buf+l->pos+clen, l->len-l->pos-clen); 1198 - l->len -= clen; 1199 - l->buf[l->len] = '\0'; 1200 - refreshLine(l); 1201 - } 1202 - } 1203 - 1204 - /* Backspace implementation. Deletes the UTF-8 character before the cursor. */ 1205 - void linenoiseEditBackspace(struct linenoiseState *l) { 1206 - if (l->pos > 0 && l->len > 0) { 1207 - size_t clen = utf8PrevCharLen(l->buf, l->pos); 1208 - memmove(l->buf+l->pos-clen, l->buf+l->pos, l->len-l->pos); 1209 - l->pos -= clen; 1210 - l->len -= clen; 1211 - l->buf[l->len] = '\0'; 1212 - refreshLine(l); 1213 - } 1214 - } 1215 - 1216 - /* Delete the previous word, maintaining the cursor at the start of the 1217 - * current word. Handles UTF-8 by moving character-by-character. */ 1218 - void linenoiseEditDeletePrevWord(struct linenoiseState *l) { 1219 - size_t old_pos = l->pos; 1220 - size_t diff; 1221 - 1222 - /* Skip spaces before the word (move backwards by UTF-8 chars). */ 1223 - while (l->pos > 0 && l->buf[l->pos-1] == ' ') 1224 - l->pos -= utf8PrevCharLen(l->buf, l->pos); 1225 - /* Skip non-space characters (move backwards by UTF-8 chars). */ 1226 - while (l->pos > 0 && l->buf[l->pos-1] != ' ') 1227 - l->pos -= utf8PrevCharLen(l->buf, l->pos); 1228 - diff = old_pos - l->pos; 1229 - memmove(l->buf+l->pos, l->buf+old_pos, l->len-old_pos+1); 1230 - l->len -= diff; 1231 - refreshLine(l); 1232 - } 1233 - 1234 - /* This function is part of the multiplexed API of Linenoise, that is used 1235 - * in order to implement the blocking variant of the API but can also be 1236 - * called by the user directly in an event driven program. It will: 1237 - * 1238 - * 1. Initialize the linenoise state passed by the user. 1239 - * 2. Put the terminal in RAW mode. 1240 - * 3. Show the prompt. 1241 - * 4. Return control to the user, that will have to call linenoiseEditFeed() 1242 - * each time there is some data arriving in the standard input. 1243 - * 1244 - * The user can also call linenoiseEditHide() and linenoiseEditShow() if it 1245 - * is required to show some input arriving asyncronously, without mixing 1246 - * it with the currently edited line. 1247 - * 1248 - * When linenoiseEditFeed() returns non-NULL, the user finished with the 1249 - * line editing session (pressed enter CTRL-D/C): in this case the caller 1250 - * needs to call linenoiseEditStop() to put back the terminal in normal 1251 - * mode. This will not destroy the buffer, as long as the linenoiseState 1252 - * is still valid in the context of the caller. 1253 - * 1254 - * The function returns 0 on success, or -1 if writing to standard output 1255 - * fails. If stdin_fd or stdout_fd are set to -1, the default is to use 1256 - * STDIN_FILENO and STDOUT_FILENO. 1257 - */ 1258 - int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { 1259 - /* Populate the linenoise state that we pass to functions implementing 1260 - * specific editing functionalities. */ 1261 - l->in_completion = 0; 1262 - l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO; 1263 - l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO; 1264 - l->buf = buf; 1265 - l->buflen = buflen; 1266 - l->prompt = prompt; 1267 - l->plen = strlen(prompt); 1268 - l->oldpos = l->pos = 0; 1269 - l->len = 0; 1270 - 1271 - /* Enter raw mode. */ 1272 - if (enableRawMode(l->ifd) == -1) return -1; 1273 - 1274 - l->cols = getColumns(stdin_fd, stdout_fd); 1275 - l->oldrows = 0; 1276 - l->oldrpos = 1; /* Cursor starts on row 1. */ 1277 - l->history_index = 0; 1278 - 1279 - /* Buffer starts empty. */ 1280 - l->buf[0] = '\0'; 1281 - l->buflen--; /* Make sure there is always space for the nulterm */ 1282 - 1283 - /* If stdin is not a tty, stop here with the initialization. We 1284 - * will actually just read a line from standard input in blocking 1285 - * mode later, in linenoiseEditFeed(). */ 1286 - if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return 0; 1287 - 1288 - /* The latest history entry is always our current buffer, that 1289 - * initially is just an empty string. */ 1290 - linenoiseHistoryAdd(""); 1291 - 1292 - if (write(l->ofd,prompt,l->plen) == -1) return -1; 1293 - return 0; 1294 - } 1295 - 1296 - 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."; 1297 - 1298 - /* This function is part of the multiplexed API of linenoise, see the top 1299 - * comment on linenoiseEditStart() for more information. Call this function 1300 - * each time there is some data to read from the standard input file 1301 - * descriptor. In the case of blocking operations, this function can just be 1302 - * called in a loop, and block. 1303 - * 1304 - * The function returns linenoiseEditMore to signal that line editing is still 1305 - * in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise 1306 - * the function returns the pointer to the heap-allocated buffer with the 1307 - * edited line, that the user should free with linenoiseFree(). 1308 - * 1309 - * On special conditions, NULL is returned and errno is populated: 1310 - * 1311 - * EAGAIN if the user pressed Ctrl-C 1312 - * ENOENT if the user pressed Ctrl-D 1313 - * 1314 - * Some other errno: I/O error. 1315 - */ 1316 - char *linenoiseEditFeed(struct linenoiseState *l) { 1317 - /* Not a TTY, pass control to line reading without character 1318 - * count limits. */ 1319 - if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return linenoiseNoTTY(); 1320 - 1321 - char c; 1322 - int nread; 1323 - char seq[3]; 1324 - 1325 - nread = read(l->ifd,&c,1); 1326 - if (nread < 0) { 1327 - return (errno == EAGAIN || errno == EWOULDBLOCK) ? linenoiseEditMore : NULL; 1328 - } else if (nread == 0) { 1329 - return NULL; 1330 - } 1331 - 1332 - /* Only autocomplete when the callback is set. It returns < 0 when 1333 - * there was an error reading from fd. Otherwise it will return the 1334 - * character that should be handled next. */ 1335 - if ((l->in_completion || c == 9) && completionCallback != NULL) { 1336 - c = completeLine(l,c); 1337 - /* Return on errors */ 1338 - if (c < 0) return NULL; 1339 - /* Read next character when 0 */ 1340 - if (c == 0) return linenoiseEditMore; 1341 - } 1342 - 1343 - switch(c) { 1344 - case ENTER: /* enter */ 1345 - history_len--; 1346 - free(history[history_len]); 1347 - if (mlmode) linenoiseEditMoveEnd(l); 1348 - if (hintsCallback) { 1349 - /* Force a refresh without hints to leave the previous 1350 - * line as the user typed it after a newline. */ 1351 - linenoiseHintsCallback *hc = hintsCallback; 1352 - hintsCallback = NULL; 1353 - refreshLine(l); 1354 - hintsCallback = hc; 1355 - } 1356 - return strdup(l->buf); 1357 - case CTRL_C: /* ctrl-c */ 1358 - errno = EAGAIN; 1359 - return NULL; 1360 - case BACKSPACE: /* backspace */ 1361 - case 8: /* ctrl-h */ 1362 - linenoiseEditBackspace(l); 1363 - break; 1364 - case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the 1365 - line is empty, act as end-of-file. */ 1366 - if (l->len > 0) { 1367 - linenoiseEditDelete(l); 1368 - } else { 1369 - history_len--; 1370 - free(history[history_len]); 1371 - errno = ENOENT; 1372 - return NULL; 1373 - } 1374 - break; 1375 - case CTRL_T: /* ctrl-t, swaps current character with previous. */ 1376 - /* Handle UTF-8: swap the two UTF-8 characters around cursor. */ 1377 - if (l->pos > 0 && l->pos < l->len) { 1378 - char tmp[32]; 1379 - size_t prevlen = utf8PrevCharLen(l->buf, l->pos); 1380 - size_t currlen = utf8NextCharLen(l->buf, l->pos, l->len); 1381 - size_t prevstart = l->pos - prevlen; 1382 - /* Copy current char to tmp, move previous char right, paste tmp. */ 1383 - memcpy(tmp, l->buf + l->pos, currlen); 1384 - memmove(l->buf + prevstart + currlen, l->buf + prevstart, prevlen); 1385 - memcpy(l->buf + prevstart, tmp, currlen); 1386 - if (l->pos + currlen <= l->len) l->pos += currlen; 1387 - refreshLine(l); 1388 - } 1389 - break; 1390 - case CTRL_B: /* ctrl-b */ 1391 - linenoiseEditMoveLeft(l); 1392 - break; 1393 - case CTRL_F: /* ctrl-f */ 1394 - linenoiseEditMoveRight(l); 1395 - break; 1396 - case CTRL_P: /* ctrl-p */ 1397 - linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV); 1398 - break; 1399 - case CTRL_N: /* ctrl-n */ 1400 - linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT); 1401 - break; 1402 - case ESC: /* escape sequence */ 1403 - /* Read the next two bytes representing the escape sequence. 1404 - * Use two calls to handle slow terminals returning the two 1405 - * chars at different times. */ 1406 - if (read(l->ifd,seq,1) == -1) break; 1407 - if (read(l->ifd,seq+1,1) == -1) break; 1408 - 1409 - /* ESC [ sequences. */ 1410 - if (seq[0] == '[') { 1411 - if (seq[1] >= '0' && seq[1] <= '9') { 1412 - /* Extended escape, read additional byte. */ 1413 - if (read(l->ifd,seq+2,1) == -1) break; 1414 - if (seq[2] == '~') { 1415 - switch(seq[1]) { 1416 - case '3': /* Delete key. */ 1417 - linenoiseEditDelete(l); 1418 - break; 1419 - } 1420 - } 1421 - } else { 1422 - switch(seq[1]) { 1423 - case 'A': /* Up */ 1424 - linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV); 1425 - break; 1426 - case 'B': /* Down */ 1427 - linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT); 1428 - break; 1429 - case 'C': /* Right */ 1430 - linenoiseEditMoveRight(l); 1431 - break; 1432 - case 'D': /* Left */ 1433 - linenoiseEditMoveLeft(l); 1434 - break; 1435 - case 'H': /* Home */ 1436 - linenoiseEditMoveHome(l); 1437 - break; 1438 - case 'F': /* End*/ 1439 - linenoiseEditMoveEnd(l); 1440 - break; 1441 - } 1442 - } 1443 - } 1444 - 1445 - /* ESC O sequences. */ 1446 - else if (seq[0] == 'O') { 1447 - switch(seq[1]) { 1448 - case 'H': /* Home */ 1449 - linenoiseEditMoveHome(l); 1450 - break; 1451 - case 'F': /* End*/ 1452 - linenoiseEditMoveEnd(l); 1453 - break; 1454 - } 1455 - } 1456 - break; 1457 - default: 1458 - /* Handle UTF-8 multi-byte sequences. When we receive the first byte 1459 - * of a multi-byte UTF-8 character, read the remaining bytes to 1460 - * complete the sequence before inserting. */ 1461 - { 1462 - char utf8[4]; 1463 - int utf8len = utf8ByteLen(c); 1464 - utf8[0] = c; 1465 - if (utf8len > 1) { 1466 - /* Read remaining bytes of the UTF-8 sequence. */ 1467 - int i; 1468 - for (i = 1; i < utf8len; i++) { 1469 - if (read(l->ifd, utf8+i, 1) != 1) break; 1470 - } 1471 - } 1472 - if (linenoiseEditInsert(l, utf8, utf8len)) return NULL; 1473 - } 1474 - break; 1475 - case CTRL_U: /* Ctrl+u, delete the whole line. */ 1476 - l->buf[0] = '\0'; 1477 - l->pos = l->len = 0; 1478 - refreshLine(l); 1479 - break; 1480 - case CTRL_K: /* Ctrl+k, delete from current to end of line. */ 1481 - l->buf[l->pos] = '\0'; 1482 - l->len = l->pos; 1483 - refreshLine(l); 1484 - break; 1485 - case CTRL_A: /* Ctrl+a, go to the start of the line */ 1486 - linenoiseEditMoveHome(l); 1487 - break; 1488 - case CTRL_E: /* ctrl+e, go to the end of the line */ 1489 - linenoiseEditMoveEnd(l); 1490 - break; 1491 - case CTRL_L: /* ctrl+l, clear screen */ 1492 - linenoiseClearScreen(); 1493 - refreshLine(l); 1494 - break; 1495 - case CTRL_W: /* ctrl+w, delete previous word */ 1496 - linenoiseEditDeletePrevWord(l); 1497 - break; 1498 - } 1499 - return linenoiseEditMore; 1500 - } 1501 - 1502 - /* This is part of the multiplexed linenoise API. See linenoiseEditStart() 1503 - * for more information. This function is called when linenoiseEditFeed() 1504 - * returns something different than NULL. At this point the user input 1505 - * is in the buffer, and we can restore the terminal in normal mode. */ 1506 - void linenoiseEditStop(struct linenoiseState *l) { 1507 - if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return; 1508 - disableRawMode(l->ifd); 1509 - printf("\n"); 1510 - } 1511 - 1512 - /* This just implements a blocking loop for the multiplexed API. 1513 - * In many applications that are not event-drivern, we can just call 1514 - * the blocking linenoise API, wait for the user to complete the editing 1515 - * and return the buffer. */ 1516 - static char *linenoiseBlockingEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) 1517 - { 1518 - struct linenoiseState l; 1519 - 1520 - /* Editing without a buffer is invalid. */ 1521 - if (buflen == 0) { 1522 - errno = EINVAL; 1523 - return NULL; 1524 - } 1525 - 1526 - linenoiseEditStart(&l,stdin_fd,stdout_fd,buf,buflen,prompt); 1527 - char *res; 1528 - while((res = linenoiseEditFeed(&l)) == linenoiseEditMore); 1529 - linenoiseEditStop(&l); 1530 - return res; 1531 - } 1532 - 1533 - /* This special mode is used by linenoise in order to print scan codes 1534 - * on screen for debugging / development purposes. It is implemented 1535 - * by the linenoise_example program using the --keycodes option. */ 1536 - void linenoisePrintKeyCodes(void) { 1537 - char quit[4]; 1538 - 1539 - printf("Linenoise key codes debugging mode.\n" 1540 - "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); 1541 - if (enableRawMode(STDIN_FILENO) == -1) return; 1542 - memset(quit,' ',4); 1543 - while(1) { 1544 - char c; 1545 - int nread; 1546 - 1547 - nread = read(STDIN_FILENO,&c,1); 1548 - if (nread <= 0) continue; 1549 - memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ 1550 - quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ 1551 - if (memcmp(quit,"quit",sizeof(quit)) == 0) break; 1552 - 1553 - printf("'%c' %02x (%d) (type quit to exit)\n", 1554 - isprint(c) ? c : '?', (int)c, (int)c); 1555 - printf("\r"); /* Go left edge manually, we are in raw mode. */ 1556 - fflush(stdout); 1557 - } 1558 - disableRawMode(STDIN_FILENO); 1559 - } 1560 - 1561 - /* This function is called when linenoise() is called with the standard 1562 - * input file descriptor not attached to a TTY. So for example when the 1563 - * program using linenoise is called in pipe or with a file redirected 1564 - * to its standard input. In this case, we want to be able to return the 1565 - * line regardless of its length (by default we are limited to 4k). */ 1566 - static char *linenoiseNoTTY(void) { 1567 - char *line = NULL; 1568 - size_t len = 0, maxlen = 0; 1569 - 1570 - while(1) { 1571 - if (len == maxlen) { 1572 - if (maxlen == 0) maxlen = 16; 1573 - maxlen *= 2; 1574 - char *oldval = line; 1575 - line = realloc(line,maxlen); 1576 - if (line == NULL) { 1577 - if (oldval) free(oldval); 1578 - return NULL; 1579 - } 1580 - } 1581 - int c = fgetc(stdin); 1582 - if (c == EOF || c == '\n') { 1583 - if (c == EOF && len == 0) { 1584 - free(line); 1585 - return NULL; 1586 - } else { 1587 - line[len] = '\0'; 1588 - return line; 1589 - } 1590 - } else { 1591 - line[len] = c; 1592 - len++; 1593 - } 1594 - } 1595 - } 1596 - 1597 - /* The high level function that is the main API of the linenoise library. 1598 - * This function checks if the terminal has basic capabilities, just checking 1599 - * for a blacklist of stupid terminals, and later either calls the line 1600 - * editing function or uses dummy fgets() so that you will be able to type 1601 - * something even in the most desperate of the conditions. */ 1602 - char *linenoise(const char *prompt) { 1603 - char buf[LINENOISE_MAX_LINE]; 1604 - 1605 - if (!isatty(STDIN_FILENO) && !getenv("LINENOISE_ASSUME_TTY")) { 1606 - /* Not a tty: read from file / pipe. In this mode we don't want any 1607 - * limit to the line size, so we call a function to handle that. */ 1608 - return linenoiseNoTTY(); 1609 - } else if (isUnsupportedTerm()) { 1610 - size_t len; 1611 - 1612 - printf("%s",prompt); 1613 - fflush(stdout); 1614 - if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; 1615 - len = strlen(buf); 1616 - while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { 1617 - len--; 1618 - buf[len] = '\0'; 1619 - } 1620 - return strdup(buf); 1621 - } else { 1622 - char *retval = linenoiseBlockingEdit(STDIN_FILENO,STDOUT_FILENO,buf,LINENOISE_MAX_LINE,prompt); 1623 - return retval; 1624 - } 1625 - } 1626 - 1627 - /* This is just a wrapper the user may want to call in order to make sure 1628 - * the linenoise returned buffer is freed with the same allocator it was 1629 - * created with. Useful when the main program is using an alternative 1630 - * allocator. */ 1631 - void linenoiseFree(void *ptr) { 1632 - if (ptr == linenoiseEditMore) return; // Protect from API misuse. 1633 - free(ptr); 1634 - } 1635 - 1636 - /* ================================ History ================================= */ 1637 - 1638 - /* Free the history, but does not reset it. Only used when we have to 1639 - * exit() to avoid memory leaks are reported by valgrind & co. */ 1640 - static void freeHistory(void) { 1641 - if (history) { 1642 - int j; 1643 - 1644 - for (j = 0; j < history_len; j++) 1645 - free(history[j]); 1646 - free(history); 1647 - } 1648 - } 1649 - 1650 - /* At exit we'll try to fix the terminal to the initial conditions. */ 1651 - static void linenoiseAtExit(void) { 1652 - disableRawMode(STDIN_FILENO); 1653 - freeHistory(); 1654 - } 1655 - 1656 - /* This is the API call to add a new entry in the linenoise history. 1657 - * It uses a fixed array of char pointers that are shifted (memmoved) 1658 - * when the history max length is reached in order to remove the older 1659 - * entry and make room for the new one, so it is not exactly suitable for huge 1660 - * histories, but will work well for a few hundred of entries. 1661 - * 1662 - * Using a circular buffer is smarter, but a bit more complex to handle. */ 1663 - int linenoiseHistoryAdd(const char *line) { 1664 - char *linecopy; 1665 - 1666 - if (history_max_len == 0) return 0; 1667 - 1668 - /* Initialization on first call. */ 1669 - if (history == NULL) { 1670 - history = malloc(sizeof(char*)*history_max_len); 1671 - if (history == NULL) return 0; 1672 - memset(history,0,(sizeof(char*)*history_max_len)); 1673 - } 1674 - 1675 - /* Don't add duplicated lines. */ 1676 - if (history_len && !strcmp(history[history_len-1], line)) return 0; 1677 - 1678 - /* Add an heap allocated copy of the line in the history. 1679 - * If we reached the max length, remove the older line. */ 1680 - linecopy = strdup(line); 1681 - if (!linecopy) return 0; 1682 - if (history_len == history_max_len) { 1683 - free(history[0]); 1684 - memmove(history,history+1,sizeof(char*)*(history_max_len-1)); 1685 - history_len--; 1686 - } 1687 - history[history_len] = linecopy; 1688 - history_len++; 1689 - return 1; 1690 - } 1691 - 1692 - /* Set the maximum length for the history. This function can be called even 1693 - * if there is already some history, the function will make sure to retain 1694 - * just the latest 'len' elements if the new history length value is smaller 1695 - * than the amount of items already inside the history. */ 1696 - int linenoiseHistorySetMaxLen(int len) { 1697 - char **new; 1698 - 1699 - if (len < 1) return 0; 1700 - if (history) { 1701 - int tocopy = history_len; 1702 - 1703 - new = malloc(sizeof(char*)*len); 1704 - if (new == NULL) return 0; 1705 - 1706 - /* If we can't copy everything, free the elements we'll not use. */ 1707 - if (len < tocopy) { 1708 - int j; 1709 - 1710 - for (j = 0; j < tocopy-len; j++) free(history[j]); 1711 - tocopy = len; 1712 - } 1713 - memset(new,0,sizeof(char*)*len); 1714 - memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); 1715 - free(history); 1716 - history = new; 1717 - } 1718 - history_max_len = len; 1719 - if (history_len > history_max_len) 1720 - history_len = history_max_len; 1721 - return 1; 1722 - } 1723 - 1724 - /* Save the history in the specified file. On success 0 is returned 1725 - * otherwise -1 is returned. */ 1726 - int linenoiseHistorySave(const char *filename) { 1727 - mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 1728 - FILE *fp; 1729 - int j; 1730 - 1731 - fp = fopen(filename,"w"); 1732 - umask(old_umask); 1733 - if (fp == NULL) return -1; 1734 - fchmod(fileno(fp),S_IRUSR|S_IWUSR); 1735 - for (j = 0; j < history_len; j++) 1736 - fprintf(fp,"%s\n",history[j]); 1737 - fclose(fp); 1738 - return 0; 1739 - } 1740 - 1741 - /* Load the history from the specified file. If the file does not exist 1742 - * zero is returned and no operation is performed. 1743 - * 1744 - * If the file exists and the operation succeeded 0 is returned, otherwise 1745 - * on error -1 is returned. */ 1746 - int linenoiseHistoryLoad(const char *filename) { 1747 - FILE *fp = fopen(filename,"r"); 1748 - char buf[LINENOISE_MAX_LINE]; 1749 - 1750 - if (fp == NULL) return -1; 1751 - 1752 - while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { 1753 - char *p; 1754 - 1755 - p = strchr(buf,'\r'); 1756 - if (!p) p = strchr(buf,'\n'); 1757 - if (p) *p = '\0'; 1758 - linenoiseHistoryAdd(buf); 1759 - } 1760 - fclose(fp); 1761 - return 0; 1762 - }