···11+let () =
22+ Fmt_tty.setup_std_outputs ();
33+ let rec loop sys_break =
44+ let prompt =
55+ if sys_break then Fmt.str "[%a] >> " Fmt.(styled (`Fg `Red) int) 130
66+ else ">> "
77+ in
88+ match Bruit.bruit prompt with
99+ | String (Some s) ->
1010+ Fmt.pr "%s\n%!" s;
1111+ loop false
1212+ | String None -> ()
1313+ | Ctrl_c -> loop true
1414+ in
1515+ loop false
+312
src/bruit.ml
···11+(* See the end of the file for the original license of Linenoise. *)
22+33+let max_line = 2048
44+55+type key =
66+ | Enter
77+ | Ctrl_a
88+ | Ctrl_b
99+ | Ctrl_c
1010+ | Ctrl_d
1111+ | Ctrl_e
1212+ | Backspace
1313+ | Escape_sequence
1414+ | Tab
1515+ | Unknown of Uchar.t
1616+1717+let key_of_char c =
1818+ match Char.code c with
1919+ | 1 -> Ctrl_a
2020+ | 2 -> Ctrl_b
2121+ | 3 -> Ctrl_c
2222+ | 4 -> Ctrl_d
2323+ | 5 -> Ctrl_e
2424+ | 9 -> Tab
2525+ | 13 -> Enter
2626+ | 27 -> Escape_sequence
2727+ | 127 -> Backspace
2828+ | _ -> Unknown (Uchar.of_char c)
2929+3030+module State = struct
3131+ type t = {
3232+ in_completion : int;
3333+ ifd : Unix.file_descr;
3434+ ofd : Unix.file_descr;
3535+ buf : bytes;
3636+ buf_len : int;
3737+ prompt : bytes;
3838+ plen : int;
3939+ old_pos : int;
4040+ pos : int;
4141+ len : int;
4242+ (* Update later *)
4343+ cols : int;
4444+ old_rows : int;
4545+ old_row_pos : int;
4646+ history_index : int;
4747+ read_buf : Bytes.t;
4848+ }
4949+5050+ let make ?(in_completion = 0) ?(old_pos = 0) ?(pos = 0) ?(len = 0)
5151+ ?(ifd = Unix.stdin) ?(ofd = Unix.stdout) ~prompt buf =
5252+ {
5353+ in_completion;
5454+ ifd;
5555+ ofd;
5656+ buf;
5757+ buf_len = Bytes.length buf;
5858+ prompt;
5959+ plen = Bytes.length prompt;
6060+ old_pos;
6161+ pos;
6262+ len;
6363+ cols = 0;
6464+ old_row_pos = 1;
6565+ old_rows = 0;
6666+ history_index = 0;
6767+ read_buf = Bytes.create 1 (* For reading a character *);
6868+ }
6969+7070+ let override ?in_completion ?ifd ?ofd ?buf ?buf_len ?prompt ?plen ?old_pos
7171+ ?pos ?len ?cols ?old_rows ?old_row_pos ?history_index (t : t) =
7272+ {
7373+ in_completion = Option.value ~default:t.in_completion in_completion;
7474+ ifd = Option.value ~default:t.ifd ifd;
7575+ ofd = Option.value ~default:t.ofd ofd;
7676+ buf = Option.value ~default:t.buf buf;
7777+ buf_len = Option.value ~default:t.buf_len buf_len;
7878+ prompt = Option.value ~default:t.prompt prompt;
7979+ plen = Option.value ~default:t.plen plen;
8080+ old_pos = Option.value ~default:t.old_pos old_pos;
8181+ pos = Option.value ~default:t.pos pos;
8282+ len = Option.value ~default:t.len len;
8383+ cols = Option.value ~default:t.cols cols;
8484+ old_rows = Option.value ~default:t.old_rows old_rows;
8585+ old_row_pos = Option.value ~default:t.old_row_pos old_row_pos;
8686+ history_index = Option.value ~default:t.history_index history_index;
8787+ read_buf = t.read_buf;
8888+ }
8989+end
9090+9191+let get_columns () =
9292+ match Terminal.Size.get_columns () with Some n -> n | None -> 80
9393+9494+let with_raw_mode (state : State.t) fn =
9595+ let saved_tio = Unix.tcgetattr state.ifd in
9696+ let tio : Unix.terminal_io =
9797+ {
9898+ saved_tio with
9999+ c_brkint = false;
100100+ c_icrnl = false;
101101+ c_inpck = false;
102102+ c_istrip = false;
103103+ c_ixon = false;
104104+ c_opost = false;
105105+ c_csize = 8;
106106+ c_echo = false;
107107+ c_icanon = false;
108108+ c_isig = false;
109109+ c_vtime = 0;
110110+ c_vmin = 1;
111111+ }
112112+ in
113113+ Unix.tcsetattr state.ifd TCSAFLUSH tio;
114114+ Fun.protect
115115+ ~finally:(fun () -> Unix.tcsetattr state.ifd TCSADRAIN saved_tio)
116116+ fn
117117+118118+let write_bytes fd s =
119119+ let len = Bytes.length s in
120120+ let wrote = Unix.write fd s 0 len in
121121+ assert (Int.equal len wrote)
122122+123123+let write_uchar fd u =
124124+ let b_len = Uchar.utf_8_byte_length u in
125125+ let bs = Bytes.create b_len in
126126+ let wrote = Bytes.set_utf_8_uchar bs 0 u in
127127+ assert (Int.equal b_len wrote);
128128+ write_bytes fd bs
129129+130130+type edit = Editing of State.t | Finished of bytes option | Ctrl_c
131131+132132+let read_char state =
133133+ try
134134+ let read = Unix.read state.State.ifd state.read_buf 0 1 in
135135+ if read = 0 then `None else `Some (Bytes.unsafe_get state.read_buf 0)
136136+ with Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) -> `Editing
137137+138138+let edit_start ~stdin:_ ~stdout:_ state fn =
139139+ with_raw_mode state @@ fun () ->
140140+ let cols = get_columns () in
141141+ Bytes.set state.buf 0 '\000';
142142+ let state = State.override ~cols ~buf_len:(state.buf_len - 1) state in
143143+ write_bytes state.ofd state.prompt;
144144+ fn state
145145+146146+let utf8_display_width b len =
147147+ let s = Bytes.to_string b in
148148+ Terminal.guess_printed_width (String.sub s 0 len)
149149+150150+let utf8_next_char_len s off =
151151+ Bytes.get_utf_8_uchar s off
152152+ |> Uchar.utf_decode_uchar |> Uchar.utf_8_byte_length
153153+154154+let utf8_prev_char_len s off =
155155+ let rec loop pos =
156156+ if pos < 0 || pos < off - 4 then
157157+ invalid_arg "UTF8 previous character length";
158158+ let decode = Bytes.get_utf_8_uchar s off in
159159+ if Uchar.utf_decode_is_valid decode then
160160+ Uchar.utf_decode_uchar decode |> Uchar.utf_8_byte_length
161161+ else loop (pos - 1)
162162+ in
163163+ loop (off - 1)
164164+165165+type refresh_flag = Rewrite
166166+167167+let refresh_single_line ?(flags = []) (state : State.t) =
168168+ let pwidth = utf8_display_width state.prompt state.plen in
169169+ let poscol = ref @@ utf8_display_width state.buf state.pos in
170170+ let lencol = ref @@ utf8_display_width state.buf state.len in
171171+172172+ let rec loop (state : State.t) =
173173+ if pwidth + !poscol >= state.cols then begin
174174+ let clen = utf8_next_char_len state.buf 0 in
175175+ let c_width =
176176+ Uchar.utf_8_byte_length
177177+ (Bytes.get_utf_8_uchar state.buf clen |> Uchar.utf_decode_uchar)
178178+ in
179179+ poscol := !poscol - c_width;
180180+ lencol := !lencol - c_width;
181181+ let state =
182182+ State.override ~len:(state.len - clen) ~pos:(state.pos - clen) state
183183+ in
184184+ loop state
185185+ end
186186+ else state
187187+ in
188188+ let state = loop state in
189189+ let ab = Buffer.create 0 in
190190+ (* Clear line *)
191191+ Buffer.add_char ab '\r';
192192+193193+ (* Add prompt *)
194194+ if List.mem Rewrite flags then begin
195195+ Buffer.add_bytes ab state.prompt;
196196+ Buffer.add_bytes ab (Bytes.sub state.buf 0 state.len)
197197+ end;
198198+199199+ (* Erase to the right *)
200200+ Buffer.add_string ab "\x1b[0K";
201201+202202+ (* Cursor to the original position *)
203203+ if List.mem Rewrite flags then begin
204204+ Buffer.add_string ab (Format.sprintf "\r\x1b[%dC" (!poscol + pwidth))
205205+ end;
206206+ write_bytes state.ofd (Buffer.to_bytes ab);
207207+ state
208208+209209+let refresh_line state = refresh_single_line ~flags:[ Rewrite ] state
210210+211211+let edit_insert (state : State.t) c =
212212+ let clen = Uchar.utf_8_byte_length c in
213213+ (* At the end of the line *)
214214+ if Int.equal state.len state.pos then begin
215215+ let _ : int = Bytes.set_utf_8_uchar state.buf state.pos c in
216216+ let state =
217217+ State.override ~pos:(state.pos + clen) ~len:(state.len + clen) state
218218+ in
219219+ if
220220+ utf8_display_width state.prompt state.plen
221221+ + utf8_display_width state.buf state.len
222222+ < state.cols
223223+ then begin
224224+ write_uchar state.ofd c;
225225+ state
226226+ end
227227+ else refresh_line state
228228+ end
229229+ else begin
230230+ assert false
231231+ end
232232+233233+let edit_backspace (state : State.t) =
234234+ let state =
235235+ if state.pos > 0 && state.len > 0 then begin
236236+ let clen = utf8_prev_char_len state.buf state.pos in
237237+ let dst = state.pos - clen in
238238+ let src = state.pos in
239239+ let len = state.len - state.pos in
240240+ Bytes.blit state.buf src state.buf dst len;
241241+ State.override ~pos:(state.pos - clen) ~len:(state.len - clen) state
242242+ end
243243+ else state
244244+ in
245245+ refresh_line state
246246+247247+let edit_feed state =
248248+ match read_char state with
249249+ | `Editing -> Editing state
250250+ | `None -> Finished None
251251+ | `Some c -> (
252252+ (* TODO: line completion *)
253253+ match key_of_char c with
254254+ | Enter -> Finished (Some state.buf)
255255+ | Unknown i ->
256256+ let state = edit_insert state i in
257257+ Editing state
258258+ | Ctrl_d -> if Int.equal state.len 0 then Finished None else assert false
259259+ | Ctrl_c -> Ctrl_c
260260+ | Backspace -> Editing (edit_backspace state)
261261+ | _ -> assert false)
262262+263263+type result = String of string option | Ctrl_c
264264+265265+let blocking_edit ~stdin ~stdout buf ~prompt =
266266+ let state = State.make ~prompt buf in
267267+ let res =
268268+ edit_start ~stdin ~stdout state @@ fun state ->
269269+ let rec loop = function
270270+ | Editing state -> loop (edit_feed state)
271271+ | Finished s -> String (Option.map Bytes.to_string s)
272272+ | Ctrl_c -> Ctrl_c
273273+ in
274274+ loop (edit_feed state)
275275+ in
276276+ Format.printf "\n%!";
277277+ res
278278+279279+let bruit prompt =
280280+ let prompt = Bytes.of_string prompt in
281281+ let buf = Bytes.create max_line in
282282+ if not (Unix.isatty Unix.stdin) then failwith "Stdin is not a tty"
283283+ else blocking_edit ~stdin:Unix.stdin ~stdout:Unix.stdout buf ~prompt
284284+285285+(*
286286+ * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
287287+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
288288+ *
289289+ * All rights reserved.
290290+ *
291291+ * Redistribution and use in source and binary forms, with or without
292292+ * modification, are permitted provided that the following conditions are
293293+ * met:
294294+ *
295295+ * * Redistributions of source code must retain the above copyright
296296+ * notice, this list of conditions and the following disclaimer.
297297+ *
298298+ * * Redistributions in binary form must reproduce the above copyright
299299+ * notice, this list of conditions and the following disclaimer in the
300300+ * documentation and/or other materials provided with the distribution.
301301+ *
302302+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
303303+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
304304+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
305305+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
306306+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
307307+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
308308+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
309309+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
310310+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
311311+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
312312+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *)
···11+/* linenoise.c -- guerrilla line editing library against the idea that a
22+ * line editing lib needs to be 20,000 lines of C code.
33+ *
44+ * You can find the latest source code at:
55+ *
66+ * http://github.com/antirez/linenoise
77+ *
88+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
99+ * the 2010 UNIX computers around.
1010+ *
1111+ * ------------------------------------------------------------------------
1212+ *
1313+ * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
1414+ * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
1515+ *
1616+ * All rights reserved.
1717+ *
1818+ * Redistribution and use in source and binary forms, with or without
1919+ * modification, are permitted provided that the following conditions are
2020+ * met:
2121+ *
2222+ * * Redistributions of source code must retain the above copyright
2323+ * notice, this list of conditions and the following disclaimer.
2424+ *
2525+ * * Redistributions in binary form must reproduce the above copyright
2626+ * notice, this list of conditions and the following disclaimer in the
2727+ * documentation and/or other materials provided with the distribution.
2828+ *
2929+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3030+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3131+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3232+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3333+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3434+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3535+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3636+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3737+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3838+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3939+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4040+ *
4141+ * ------------------------------------------------------------------------
4242+ *
4343+ * References:
4444+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
4545+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
4646+ *
4747+ * Todo list:
4848+ * - Filter bogus Ctrl+<char> combinations.
4949+ * - Win32 support
5050+ *
5151+ * Bloat:
5252+ * - History search like Ctrl+r in readline?
5353+ *
5454+ * List of escape sequences used by this program, we do everything just
5555+ * with three sequences. In order to be so cheap we may have some
5656+ * flickering effect with some slow terminal, but the lesser sequences
5757+ * the more compatible.
5858+ *
5959+ * EL (Erase Line)
6060+ * Sequence: ESC [ n K
6161+ * Effect: if n is 0 or missing, clear from cursor to end of line
6262+ * Effect: if n is 1, clear from beginning of line to cursor
6363+ * Effect: if n is 2, clear entire line
6464+ *
6565+ * CUF (CUrsor Forward)
6666+ * Sequence: ESC [ n C
6767+ * Effect: moves cursor forward n chars
6868+ *
6969+ * CUB (CUrsor Backward)
7070+ * Sequence: ESC [ n D
7171+ * Effect: moves cursor backward n chars
7272+ *
7373+ * The following is used to get the terminal width if getting
7474+ * the width with the TIOCGWINSZ ioctl fails
7575+ *
7676+ * DSR (Device Status Report)
7777+ * Sequence: ESC [ 6 n
7878+ * Effect: reports the current cusor position as ESC [ n ; m R
7979+ * where n is the row and m is the column
8080+ *
8181+ * When multi line mode is enabled, we also use an additional escape
8282+ * sequence. However multi line editing is disabled by default.
8383+ *
8484+ * CUU (Cursor Up)
8585+ * Sequence: ESC [ n A
8686+ * Effect: moves cursor up of n chars.
8787+ *
8888+ * CUD (Cursor Down)
8989+ * Sequence: ESC [ n B
9090+ * Effect: moves cursor down of n chars.
9191+ *
9292+ * When linenoiseClearScreen() is called, two additional escape sequences
9393+ * are used in order to clear the screen and position the cursor at home
9494+ * position.
9595+ *
9696+ * CUP (Cursor position)
9797+ * Sequence: ESC [ H
9898+ * Effect: moves the cursor to upper left corner
9999+ *
100100+ * ED (Erase display)
101101+ * Sequence: ESC [ 2 J
102102+ * Effect: clear the whole screen
103103+ *
104104+ */
105105+106106+#include <termios.h>
107107+#include <unistd.h>
108108+#include <stdlib.h>
109109+#include <stdio.h>
110110+#include <errno.h>
111111+#include <string.h>
112112+#include <stdlib.h>
113113+#include <ctype.h>
114114+#include <sys/stat.h>
115115+#include <sys/types.h>
116116+#include <sys/ioctl.h>
117117+#include <unistd.h>
118118+#include <stdint.h>
119119+#include "linenoise.h"
120120+121121+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
122122+#define LINENOISE_MAX_LINE 4096
123123+static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
124124+static linenoiseCompletionCallback *completionCallback = NULL;
125125+static linenoiseHintsCallback *hintsCallback = NULL;
126126+static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
127127+static char *linenoiseNoTTY(void);
128128+static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags);
129129+static void refreshLineWithFlags(struct linenoiseState *l, int flags);
130130+131131+static struct termios orig_termios; /* In order to restore at exit.*/
132132+static int maskmode = 0; /* Show "***" instead of input. For passwords. */
133133+static int rawmode = 0; /* For atexit() function to check if restore is needed*/
134134+static int mlmode = 0; /* Multi line mode. Default is single line. */
135135+static int atexit_registered = 0; /* Register atexit just 1 time. */
136136+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
137137+static int history_len = 0;
138138+static char **history = NULL;
139139+140140+/* =========================== UTF-8 support ================================ */
141141+142142+/* Return the number of bytes that compose the UTF-8 character starting at
143143+ * 'c'. This function assumes a valid UTF-8 encoding and handles the four
144144+ * standard byte patterns:
145145+ * 0xxxxxxx -> 1 byte (ASCII)
146146+ * 110xxxxx -> 2 bytes
147147+ * 1110xxxx -> 3 bytes
148148+ * 11110xxx -> 4 bytes */
149149+static int utf8ByteLen(char c) {
150150+ unsigned char uc = (unsigned char)c;
151151+ if ((uc & 0x80) == 0) return 1; /* 0xxxxxxx: ASCII */
152152+ if ((uc & 0xE0) == 0xC0) return 2; /* 110xxxxx: 2-byte seq */
153153+ if ((uc & 0xF0) == 0xE0) return 3; /* 1110xxxx: 3-byte seq */
154154+ if ((uc & 0xF8) == 0xF0) return 4; /* 11110xxx: 4-byte seq */
155155+ return 1; /* Fallback for invalid encoding, treat as single byte. */
156156+}
157157+158158+/* Decode a UTF-8 sequence starting at 's' into a Unicode codepoint.
159159+ * Returns the codepoint value. Assumes valid UTF-8 encoding. */
160160+static uint32_t utf8DecodeChar(const char *s, size_t *len) {
161161+ unsigned char *p = (unsigned char *)s;
162162+ uint32_t cp;
163163+164164+ if ((*p & 0x80) == 0) {
165165+ *len = 1;
166166+ return *p;
167167+ } else if ((*p & 0xE0) == 0xC0) {
168168+ *len = 2;
169169+ cp = (*p & 0x1F) << 6;
170170+ cp |= (p[1] & 0x3F);
171171+ return cp;
172172+ } else if ((*p & 0xF0) == 0xE0) {
173173+ *len = 3;
174174+ cp = (*p & 0x0F) << 12;
175175+ cp |= (p[1] & 0x3F) << 6;
176176+ cp |= (p[2] & 0x3F);
177177+ return cp;
178178+ } else if ((*p & 0xF8) == 0xF0) {
179179+ *len = 4;
180180+ cp = (*p & 0x07) << 18;
181181+ cp |= (p[1] & 0x3F) << 12;
182182+ cp |= (p[2] & 0x3F) << 6;
183183+ cp |= (p[3] & 0x3F);
184184+ return cp;
185185+ }
186186+ *len = 1;
187187+ return *p; /* Fallback for invalid sequences. */
188188+}
189189+190190+/* Check if codepoint is a variation selector (emoji style modifiers). */
191191+static int isVariationSelector(uint32_t cp) {
192192+ return cp == 0xFE0E || cp == 0xFE0F; /* Text/emoji style */
193193+}
194194+195195+/* Check if codepoint is a skin tone modifier. */
196196+static int isSkinToneModifier(uint32_t cp) {
197197+ return cp >= 0x1F3FB && cp <= 0x1F3FF;
198198+}
199199+200200+/* Check if codepoint is Zero Width Joiner. */
201201+static int isZWJ(uint32_t cp) {
202202+ return cp == 0x200D;
203203+}
204204+205205+/* Check if codepoint is a Regional Indicator (for flag emoji). */
206206+static int isRegionalIndicator(uint32_t cp) {
207207+ return cp >= 0x1F1E6 && cp <= 0x1F1FF;
208208+}
209209+210210+/* Check if codepoint is a combining mark or other zero-width character. */
211211+static int isCombiningMark(uint32_t cp) {
212212+ return (cp >= 0x0300 && cp <= 0x036F) || /* Combining Diacriticals */
213213+ (cp >= 0x1AB0 && cp <= 0x1AFF) || /* Combining Diacriticals Extended */
214214+ (cp >= 0x1DC0 && cp <= 0x1DFF) || /* Combining Diacriticals Supplement */
215215+ (cp >= 0x20D0 && cp <= 0x20FF) || /* Combining Diacriticals for Symbols */
216216+ (cp >= 0xFE20 && cp <= 0xFE2F); /* Combining Half Marks */
217217+}
218218+219219+/* Check if codepoint extends the previous character (doesn't start a new grapheme). */
220220+static int isGraphemeExtend(uint32_t cp) {
221221+ return isVariationSelector(cp) || isSkinToneModifier(cp) ||
222222+ isZWJ(cp) || isCombiningMark(cp);
223223+}
224224+225225+/* Decode the UTF-8 codepoint ending at position 'pos' (exclusive) and
226226+ * return its value. Also sets *cplen to the byte length of the codepoint. */
227227+static uint32_t utf8DecodePrev(const char *buf, size_t pos, size_t *cplen) {
228228+ if (pos == 0) {
229229+ *cplen = 0;
230230+ return 0;
231231+ }
232232+ /* Scan backwards to find the start byte. */
233233+ size_t i = pos;
234234+ do {
235235+ i--;
236236+ } while (i > 0 && (pos - i) < 4 && ((unsigned char)buf[i] & 0xC0) == 0x80);
237237+ *cplen = pos - i;
238238+ size_t dummy;
239239+ return utf8DecodeChar(buf + i, &dummy);
240240+}
241241+242242+/* Given a buffer and a position, return the byte length of the grapheme
243243+ * cluster before that position. A grapheme cluster includes:
244244+ * - The base character
245245+ * - Any following variation selectors, skin tone modifiers
246246+ * - ZWJ sequences (emoji joined by Zero Width Joiner)
247247+ * - Regional indicator pairs (flag emoji) */
248248+static size_t utf8PrevCharLen(const char *buf, size_t pos) {
249249+ if (pos == 0) return 0;
250250+251251+ size_t total = 0;
252252+ size_t curpos = pos;
253253+254254+ /* First, get the last codepoint. */
255255+ size_t cplen;
256256+ uint32_t cp = utf8DecodePrev(buf, curpos, &cplen);
257257+ if (cplen == 0) return 0;
258258+ total += cplen;
259259+ curpos -= cplen;
260260+261261+ /* If we're at an extending character, we need to find what it extends.
262262+ * Keep going back through the grapheme cluster. */
263263+ while (curpos > 0) {
264264+ size_t prevlen;
265265+ uint32_t prevcp = utf8DecodePrev(buf, curpos, &prevlen);
266266+ if (prevlen == 0) break;
267267+268268+ if (isZWJ(prevcp)) {
269269+ /* ZWJ joins two emoji. Include the ZWJ and continue to get
270270+ * the preceding character. */
271271+ total += prevlen;
272272+ curpos -= prevlen;
273273+ /* Now get the character before ZWJ. */
274274+ prevcp = utf8DecodePrev(buf, curpos, &prevlen);
275275+ if (prevlen == 0) break;
276276+ total += prevlen;
277277+ curpos -= prevlen;
278278+ cp = prevcp;
279279+ continue; /* Check if there's more extending before this. */
280280+ } else if (isGraphemeExtend(cp)) {
281281+ /* Current cp is an extending character; include previous. */
282282+ total += prevlen;
283283+ curpos -= prevlen;
284284+ cp = prevcp;
285285+ continue;
286286+ } else if (isRegionalIndicator(cp) && isRegionalIndicator(prevcp)) {
287287+ /* Two regional indicators form a flag. But we need to be careful:
288288+ * flags are always pairs, so only join if we're at an even boundary.
289289+ * For simplicity, just join one pair. */
290290+ total += prevlen;
291291+ curpos -= prevlen;
292292+ break;
293293+ } else {
294294+ /* No more extending; we've found the start of the cluster. */
295295+ break;
296296+ }
297297+ }
298298+299299+ return total;
300300+}
301301+302302+/* Given a buffer, position and total length, return the byte length of the
303303+ * grapheme cluster at the current position. */
304304+static size_t utf8NextCharLen(const char *buf, size_t pos, size_t len) {
305305+ if (pos >= len) return 0;
306306+307307+ size_t total = 0;
308308+ size_t curpos = pos;
309309+310310+ /* Get the first codepoint. */
311311+ size_t cplen;
312312+ uint32_t cp = utf8DecodeChar(buf + curpos, &cplen);
313313+ total += cplen;
314314+ curpos += cplen;
315315+316316+ int isRI = isRegionalIndicator(cp);
317317+318318+ /* Consume any extending characters that follow. */
319319+ while (curpos < len) {
320320+ size_t nextlen;
321321+ uint32_t nextcp = utf8DecodeChar(buf + curpos, &nextlen);
322322+323323+ if (isZWJ(nextcp) && curpos + nextlen < len) {
324324+ /* ZWJ: include it and the following character. */
325325+ total += nextlen;
326326+ curpos += nextlen;
327327+ /* Get the character after ZWJ. */
328328+ nextcp = utf8DecodeChar(buf + curpos, &nextlen);
329329+ total += nextlen;
330330+ curpos += nextlen;
331331+ continue; /* Check for more extending after the joined char. */
332332+ } else if (isGraphemeExtend(nextcp)) {
333333+ /* Variation selector, skin tone, combining mark, etc. */
334334+ total += nextlen;
335335+ curpos += nextlen;
336336+ continue;
337337+ } else if (isRI && isRegionalIndicator(nextcp)) {
338338+ /* Second regional indicator for a flag pair. */
339339+ total += nextlen;
340340+ curpos += nextlen;
341341+ isRI = 0; /* Only pair once. */
342342+ continue;
343343+ } else {
344344+ break;
345345+ }
346346+ }
347347+348348+ return total;
349349+}
350350+351351+/* Return the display width of a Unicode codepoint. This is a heuristic
352352+ * that works for most common cases:
353353+ * - Control chars and zero-width: 0 columns
354354+ * - Grapheme-extending chars (VS, skin tone, ZWJ): 0 columns
355355+ * - ASCII printable: 1 column
356356+ * - Wide chars (CJK, emoji, fullwidth): 2 columns
357357+ * - Everything else: 1 column
358358+ *
359359+ * This is not a full wcwidth() implementation, but a minimal heuristic
360360+ * that handles emoji and CJK characters reasonably well. */
361361+static int utf8CharWidth(uint32_t cp) {
362362+ /* Control characters and combining marks: zero width. */
363363+ if (cp < 32 || (cp >= 0x7F && cp < 0xA0)) return 0;
364364+ if (isCombiningMark(cp)) return 0;
365365+366366+ /* Grapheme-extending characters: zero width.
367367+ * These modify the preceding character rather than taking space. */
368368+ if (isVariationSelector(cp)) return 0;
369369+ if (isSkinToneModifier(cp)) return 0;
370370+ if (isZWJ(cp)) return 0;
371371+372372+ /* Wide character ranges - these display as 2 columns:
373373+ * - CJK Unified Ideographs and Extensions
374374+ * - Fullwidth forms
375375+ * - Various emoji ranges */
376376+ if (cp >= 0x1100 &&
377377+ (cp <= 0x115F || /* Hangul Jamo */
378378+ cp == 0x2329 || cp == 0x232A || /* Angle brackets */
379379+ (cp >= 0x231A && cp <= 0x231B) || /* Watch, Hourglass */
380380+ (cp >= 0x23E9 && cp <= 0x23F3) || /* Various symbols */
381381+ (cp >= 0x23F8 && cp <= 0x23FA) || /* Various symbols */
382382+ (cp >= 0x25AA && cp <= 0x25AB) || /* Small squares */
383383+ (cp >= 0x25B6 && cp <= 0x25C0) || /* Play/reverse buttons */
384384+ (cp >= 0x25FB && cp <= 0x25FE) || /* Squares */
385385+ (cp >= 0x2600 && cp <= 0x26FF) || /* Misc Symbols (sun, cloud, etc) */
386386+ (cp >= 0x2700 && cp <= 0x27BF) || /* Dingbats (❤, ✂, etc) */
387387+ (cp >= 0x2934 && cp <= 0x2935) || /* Arrows */
388388+ (cp >= 0x2B05 && cp <= 0x2B07) || /* Arrows */
389389+ (cp >= 0x2B1B && cp <= 0x2B1C) || /* Squares */
390390+ cp == 0x2B50 || cp == 0x2B55 || /* Star, circle */
391391+ (cp >= 0x2E80 && cp <= 0xA4CF &&
392392+ cp != 0x303F) || /* CJK ... Yi */
393393+ (cp >= 0xAC00 && cp <= 0xD7A3) || /* Hangul Syllables */
394394+ (cp >= 0xF900 && cp <= 0xFAFF) || /* CJK Compatibility Ideographs */
395395+ (cp >= 0xFE10 && cp <= 0xFE1F) || /* Vertical forms */
396396+ (cp >= 0xFE30 && cp <= 0xFE6F) || /* CJK Compatibility Forms */
397397+ (cp >= 0xFF00 && cp <= 0xFF60) || /* Fullwidth Forms */
398398+ (cp >= 0xFFE0 && cp <= 0xFFE6) || /* Fullwidth Signs */
399399+ (cp >= 0x1F1E6 && cp <= 0x1F1FF) || /* Regional Indicators (flags) */
400400+ (cp >= 0x1F300 && cp <= 0x1F64F) || /* Misc Symbols and Emoticons */
401401+ (cp >= 0x1F680 && cp <= 0x1F6FF) || /* Transport and Map Symbols */
402402+ (cp >= 0x1F900 && cp <= 0x1F9FF) || /* Supplemental Symbols */
403403+ (cp >= 0x1FA00 && cp <= 0x1FAFF) || /* Chess, Extended-A */
404404+ (cp >= 0x20000 && cp <= 0x2FFFF))) /* CJK Extension B and beyond */
405405+ return 2;
406406+407407+ return 1; /* Default: single width */
408408+}
409409+410410+/* Calculate the display width of a UTF-8 string of 'len' bytes.
411411+ * This is used for cursor positioning in the terminal.
412412+ * Handles grapheme clusters: characters joined by ZWJ contribute 0 width
413413+ * after the first character in the sequence. */
414414+static size_t utf8StrWidth(const char *s, size_t len) {
415415+ size_t width = 0;
416416+ size_t i = 0;
417417+ int after_zwj = 0; /* Track if previous char was ZWJ */
418418+419419+ while (i < len) {
420420+ size_t clen;
421421+ uint32_t cp = utf8DecodeChar(s + i, &clen);
422422+423423+ if (after_zwj) {
424424+ /* Character after ZWJ: don't add width, it's joined.
425425+ * But do check for extending chars after it. */
426426+ after_zwj = 0;
427427+ } else {
428428+ width += utf8CharWidth(cp);
429429+ }
430430+431431+ /* Check if this is a ZWJ - next char will be joined. */
432432+ if (isZWJ(cp)) {
433433+ after_zwj = 1;
434434+ }
435435+436436+ i += clen;
437437+ }
438438+ return width;
439439+}
440440+441441+/* Return the display width of a single UTF-8 character at position 's'. */
442442+static int utf8SingleCharWidth(const char *s, size_t len) {
443443+ if (len == 0) return 0;
444444+ size_t clen;
445445+ uint32_t cp = utf8DecodeChar(s, &clen);
446446+ return utf8CharWidth(cp);
447447+}
448448+449449+enum KEY_ACTION{
450450+ KEY_NULL = 0, /* NULL */
451451+ CTRL_A = 1, /* Ctrl+a */
452452+ CTRL_B = 2, /* Ctrl-b */
453453+ CTRL_C = 3, /* Ctrl-c */
454454+ CTRL_D = 4, /* Ctrl-d */
455455+ CTRL_E = 5, /* Ctrl-e */
456456+ CTRL_F = 6, /* Ctrl-f */
457457+ CTRL_H = 8, /* Ctrl-h */
458458+ TAB = 9, /* Tab */
459459+ CTRL_K = 11, /* Ctrl+k */
460460+ CTRL_L = 12, /* Ctrl+l */
461461+ ENTER = 13, /* Enter */
462462+ CTRL_N = 14, /* Ctrl-n */
463463+ CTRL_P = 16, /* Ctrl-p */
464464+ CTRL_T = 20, /* Ctrl-t */
465465+ CTRL_U = 21, /* Ctrl+u */
466466+ CTRL_W = 23, /* Ctrl+w */
467467+ ESC = 27, /* Escape */
468468+ BACKSPACE = 127 /* Backspace */
469469+};
470470+471471+static void linenoiseAtExit(void);
472472+int linenoiseHistoryAdd(const char *line);
473473+#define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen
474474+#define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen.
475475+#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
476476+static void refreshLine(struct linenoiseState *l);
477477+478478+/* Debugging macro. */
479479+#if 0
480480+FILE *lndebug_fp = NULL;
481481+#define lndebug(...) \
482482+ do { \
483483+ if (lndebug_fp == NULL) { \
484484+ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
485485+ fprintf(lndebug_fp, \
486486+ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
487487+ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
488488+ (int)l->oldrows,old_rows); \
489489+ } \
490490+ fprintf(lndebug_fp, ", " __VA_ARGS__); \
491491+ fflush(lndebug_fp); \
492492+ } while (0)
493493+#else
494494+#define lndebug(fmt, ...)
495495+#endif
496496+497497+/* ======================= Low level terminal handling ====================== */
498498+499499+/* Enable "mask mode". When it is enabled, instead of the input that
500500+ * the user is typing, the terminal will just display a corresponding
501501+ * number of asterisks, like "****". This is useful for passwords and other
502502+ * secrets that should not be displayed. */
503503+void linenoiseMaskModeEnable(void) {
504504+ maskmode = 1;
505505+}
506506+507507+/* Disable mask mode. */
508508+void linenoiseMaskModeDisable(void) {
509509+ maskmode = 0;
510510+}
511511+512512+/* Set if to use or not the multi line mode. */
513513+void linenoiseSetMultiLine(int ml) {
514514+ mlmode = ml;
515515+}
516516+517517+/* Return true if the terminal name is in the list of terminals we know are
518518+ * not able to understand basic escape sequences. */
519519+static int isUnsupportedTerm(void) {
520520+ char *term = getenv("TERM");
521521+ int j;
522522+523523+ if (term == NULL) return 0;
524524+ for (j = 0; unsupported_term[j]; j++)
525525+ if (!strcasecmp(term,unsupported_term[j])) return 1;
526526+ return 0;
527527+}
528528+529529+/* Raw mode: 1960 magic shit. */
530530+static int enableRawMode(int fd) {
531531+ struct termios raw;
532532+533533+ /* Test mode: when LINENOISE_ASSUME_TTY is set, skip terminal setup.
534534+ * This allows testing via pipes without a real terminal. */
535535+ if (getenv("LINENOISE_ASSUME_TTY")) {
536536+ rawmode = 1;
537537+ return 0;
538538+ }
539539+540540+ if (!isatty(STDIN_FILENO)) goto fatal;
541541+ if (!atexit_registered) {
542542+ atexit(linenoiseAtExit);
543543+ atexit_registered = 1;
544544+ }
545545+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
546546+547547+ raw = orig_termios; /* modify the original mode */
548548+ /* input modes: no break, no CR to NL, no parity check, no strip char,
549549+ * no start/stop output control. */
550550+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
551551+ /* output modes - disable post processing */
552552+ raw.c_oflag &= ~(OPOST);
553553+ /* control modes - set 8 bit chars */
554554+ raw.c_cflag |= (CS8);
555555+ /* local modes - choing off, canonical off, no extended functions,
556556+ * no signal chars (^Z,^C) */
557557+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
558558+ /* control chars - set return condition: min number of bytes and timer.
559559+ * We want read to return every single byte, without timeout. */
560560+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
561561+562562+ /* put terminal in raw mode after flushing */
563563+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
564564+ rawmode = 1;
565565+ return 0;
566566+567567+fatal:
568568+ errno = ENOTTY;
569569+ return -1;
570570+}
571571+572572+static void disableRawMode(int fd) {
573573+ /* Test mode: nothing to restore. */
574574+ if (getenv("LINENOISE_ASSUME_TTY")) {
575575+ rawmode = 0;
576576+ return;
577577+ }
578578+ /* Don't even check the return value as it's too late. */
579579+ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
580580+ rawmode = 0;
581581+}
582582+583583+/* Use the ESC [6n escape sequence to query the horizontal cursor position
584584+ * and return it. On error -1 is returned, on success the position of the
585585+ * cursor. */
586586+static int getCursorPosition(int ifd, int ofd) {
587587+ char buf[32];
588588+ int cols, rows;
589589+ unsigned int i = 0;
590590+591591+ /* Report cursor location */
592592+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
593593+594594+ /* Read the response: ESC [ rows ; cols R */
595595+ while (i < sizeof(buf)-1) {
596596+ if (read(ifd,buf+i,1) != 1) break;
597597+ if (buf[i] == 'R') break;
598598+ i++;
599599+ }
600600+ buf[i] = '\0';
601601+602602+ /* Parse it. */
603603+ if (buf[0] != ESC || buf[1] != '[') return -1;
604604+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
605605+ return cols;
606606+}
607607+608608+/* Try to get the number of columns in the current terminal, or assume 80
609609+ * if it fails. */
610610+static int getColumns(int ifd, int ofd) {
611611+ struct winsize ws;
612612+613613+ /* Test mode: use LINENOISE_COLS env var for fixed width. */
614614+ char *cols_env = getenv("LINENOISE_COLS");
615615+ if (cols_env) return atoi(cols_env);
616616+617617+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
618618+ /* ioctl() failed. Try to query the terminal itself. */
619619+ int start, cols;
620620+621621+ /* Get the initial position so we can restore it later. */
622622+ start = getCursorPosition(ifd,ofd);
623623+ if (start == -1) goto failed;
624624+625625+ /* Go to right margin and get position. */
626626+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
627627+ cols = getCursorPosition(ifd,ofd);
628628+ if (cols == -1) goto failed;
629629+630630+ /* Restore position. */
631631+ if (cols > start) {
632632+ char seq[32];
633633+ snprintf(seq,32,"\x1b[%dD",cols-start);
634634+ if (write(ofd,seq,strlen(seq)) == -1) {
635635+ /* Can't recover... */
636636+ }
637637+ }
638638+ return cols;
639639+ } else {
640640+ return ws.ws_col;
641641+ }
642642+643643+failed:
644644+ return 80;
645645+}
646646+647647+/* Clear the screen. Used to handle ctrl+l */
648648+void linenoiseClearScreen(void) {
649649+ if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
650650+ /* nothing to do, just to avoid warning. */
651651+ }
652652+}
653653+654654+/* Beep, used for completion when there is nothing to complete or when all
655655+ * the choices were already shown. */
656656+static void linenoiseBeep(void) {
657657+ fprintf(stderr, "\x7");
658658+ fflush(stderr);
659659+}
660660+661661+/* ============================== Completion ================================ */
662662+663663+/* Free a list of completion option populated by linenoiseAddCompletion(). */
664664+static void freeCompletions(linenoiseCompletions *lc) {
665665+ size_t i;
666666+ for (i = 0; i < lc->len; i++)
667667+ free(lc->cvec[i]);
668668+ if (lc->cvec != NULL)
669669+ free(lc->cvec);
670670+}
671671+672672+/* Called by completeLine() and linenoiseShow() to render the current
673673+ * edited line with the proposed completion. If the current completion table
674674+ * is already available, it is passed as second argument, otherwise the
675675+ * function will use the callback to obtain it.
676676+ *
677677+ * Flags are the same as refreshLine*(), that is REFRESH_* macros. */
678678+static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) {
679679+ /* Obtain the table of completions if the caller didn't provide one. */
680680+ linenoiseCompletions ctable = { 0, NULL };
681681+ if (lc == NULL) {
682682+ completionCallback(ls->buf,&ctable);
683683+ lc = &ctable;
684684+ }
685685+686686+ /* Show the edited line with completion if possible, or just refresh. */
687687+ if (ls->completion_idx < lc->len) {
688688+ struct linenoiseState saved = *ls;
689689+ ls->len = ls->pos = strlen(lc->cvec[ls->completion_idx]);
690690+ ls->buf = lc->cvec[ls->completion_idx];
691691+ refreshLineWithFlags(ls,flags);
692692+ ls->len = saved.len;
693693+ ls->pos = saved.pos;
694694+ ls->buf = saved.buf;
695695+ } else {
696696+ refreshLineWithFlags(ls,flags);
697697+ }
698698+699699+ /* Free the completions table if needed. */
700700+ if (lc != &ctable) freeCompletions(&ctable);
701701+}
702702+703703+/* This is an helper function for linenoiseEdit*() and is called when the
704704+ * user types the <tab> key in order to complete the string currently in the
705705+ * input.
706706+ *
707707+ * The state of the editing is encapsulated into the pointed linenoiseState
708708+ * structure as described in the structure definition.
709709+ *
710710+ * If the function returns non-zero, the caller should handle the
711711+ * returned value as a byte read from the standard input, and process
712712+ * it as usually: this basically means that the function may return a byte
713713+ * read from the termianl but not processed. Otherwise, if zero is returned,
714714+ * the input was consumed by the completeLine() function to navigate the
715715+ * possible completions, and the caller should read for the next characters
716716+ * from stdin. */
717717+static int completeLine(struct linenoiseState *ls, int keypressed) {
718718+ linenoiseCompletions lc = { 0, NULL };
719719+ int nwritten;
720720+ char c = keypressed;
721721+722722+ completionCallback(ls->buf,&lc);
723723+ if (lc.len == 0) {
724724+ linenoiseBeep();
725725+ ls->in_completion = 0;
726726+ } else {
727727+ switch(c) {
728728+ case 9: /* tab */
729729+ if (ls->in_completion == 0) {
730730+ ls->in_completion = 1;
731731+ ls->completion_idx = 0;
732732+ } else {
733733+ ls->completion_idx = (ls->completion_idx+1) % (lc.len+1);
734734+ if (ls->completion_idx == lc.len) linenoiseBeep();
735735+ }
736736+ c = 0;
737737+ break;
738738+ case 27: /* escape */
739739+ /* Re-show original buffer */
740740+ if (ls->completion_idx < lc.len) refreshLine(ls);
741741+ ls->in_completion = 0;
742742+ c = 0;
743743+ break;
744744+ default:
745745+ /* Update buffer and return */
746746+ if (ls->completion_idx < lc.len) {
747747+ nwritten = snprintf(ls->buf,ls->buflen,"%s",
748748+ lc.cvec[ls->completion_idx]);
749749+ ls->len = ls->pos = nwritten;
750750+ }
751751+ ls->in_completion = 0;
752752+ break;
753753+ }
754754+755755+ /* Show completion or original buffer */
756756+ if (ls->in_completion && ls->completion_idx < lc.len) {
757757+ refreshLineWithCompletion(ls,&lc,REFRESH_ALL);
758758+ } else {
759759+ refreshLine(ls);
760760+ }
761761+ }
762762+763763+ freeCompletions(&lc);
764764+ return c; /* Return last read character */
765765+}
766766+767767+/* Register a callback function to be called for tab-completion. */
768768+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
769769+ completionCallback = fn;
770770+}
771771+772772+/* Register a hits function to be called to show hits to the user at the
773773+ * right of the prompt. */
774774+void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
775775+ hintsCallback = fn;
776776+}
777777+778778+/* Register a function to free the hints returned by the hints callback
779779+ * registered with linenoiseSetHintsCallback(). */
780780+void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
781781+ freeHintsCallback = fn;
782782+}
783783+784784+/* This function is used by the callback function registered by the user
785785+ * in order to add completion options given the input string when the
786786+ * user typed <tab>. See the example.c source code for a very easy to
787787+ * understand example. */
788788+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
789789+ size_t len = strlen(str);
790790+ char *copy, **cvec;
791791+792792+ copy = malloc(len+1);
793793+ if (copy == NULL) return;
794794+ memcpy(copy,str,len+1);
795795+ cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
796796+ if (cvec == NULL) {
797797+ free(copy);
798798+ return;
799799+ }
800800+ lc->cvec = cvec;
801801+ lc->cvec[lc->len++] = copy;
802802+}
803803+804804+/* =========================== Line editing ================================= */
805805+806806+/* We define a very simple "append buffer" structure, that is an heap
807807+ * allocated string where we can append to. This is useful in order to
808808+ * write all the escape sequences in a buffer and flush them to the standard
809809+ * output in a single call, to avoid flickering effects. */
810810+struct abuf {
811811+ char *b;
812812+ int len;
813813+};
814814+815815+static void abInit(struct abuf *ab) {
816816+ ab->b = NULL;
817817+ ab->len = 0;
818818+}
819819+820820+static void abAppend(struct abuf *ab, const char *s, int len) {
821821+ char *new = realloc(ab->b,ab->len+len);
822822+823823+ if (new == NULL) return;
824824+ memcpy(new+ab->len,s,len);
825825+ ab->b = new;
826826+ ab->len += len;
827827+}
828828+829829+static void abFree(struct abuf *ab) {
830830+ free(ab->b);
831831+}
832832+833833+/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
834834+ * to the right of the prompt. Now uses display widths for proper UTF-8. */
835835+void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pwidth) {
836836+ char seq[64];
837837+ size_t bufwidth = utf8StrWidth(l->buf, l->len);
838838+ if (hintsCallback && pwidth + bufwidth < l->cols) {
839839+ int color = -1, bold = 0;
840840+ char *hint = hintsCallback(l->buf,&color,&bold);
841841+ if (hint) {
842842+ size_t hintlen = strlen(hint);
843843+ size_t hintwidth = utf8StrWidth(hint, hintlen);
844844+ size_t hintmaxwidth = l->cols - (pwidth + bufwidth);
845845+ /* Truncate hint to fit, respecting UTF-8 boundaries. */
846846+ if (hintwidth > hintmaxwidth) {
847847+ size_t i = 0, w = 0;
848848+ while (i < hintlen) {
849849+ size_t clen = utf8NextCharLen(hint, i, hintlen);
850850+ int cwidth = utf8SingleCharWidth(hint + i, clen);
851851+ if (w + cwidth > hintmaxwidth) break;
852852+ w += cwidth;
853853+ i += clen;
854854+ }
855855+ hintlen = i;
856856+ }
857857+ if (bold == 1 && color == -1) color = 37;
858858+ if (color != -1 || bold != 0)
859859+ snprintf(seq,64,"\033[%d;%d;49m",bold,color);
860860+ else
861861+ seq[0] = '\0';
862862+ abAppend(ab,seq,strlen(seq));
863863+ abAppend(ab,hint,hintlen);
864864+ if (color != -1 || bold != 0)
865865+ abAppend(ab,"\033[0m",4);
866866+ /* Call the function to free the hint returned. */
867867+ if (freeHintsCallback) freeHintsCallback(hint);
868868+ }
869869+ }
870870+}
871871+872872+/* Single line low level line refresh.
873873+ *
874874+ * Rewrite the currently edited line accordingly to the buffer content,
875875+ * cursor position, and number of columns of the terminal.
876876+ *
877877+ * Flags is REFRESH_* macros. The function can just remove the old
878878+ * prompt, just write it, or both.
879879+ *
880880+ * This function is UTF-8 aware and uses display widths (not byte counts)
881881+ * for cursor positioning and horizontal scrolling. */
882882+static void refreshSingleLine(struct linenoiseState *l, int flags) {
883883+ char seq[64];
884884+ size_t pwidth = utf8StrWidth(l->prompt, l->plen); /* Prompt display width */
885885+ int fd = l->ofd;
886886+ char *buf = l->buf;
887887+ size_t len = l->len; /* Byte length of buffer to display */
888888+ size_t pos = l->pos; /* Byte position of cursor */
889889+ size_t poscol; /* Display column of cursor */
890890+ size_t lencol; /* Display width of buffer */
891891+ struct abuf ab;
892892+893893+ /* Calculate the display width up to cursor and total display width. */
894894+ poscol = utf8StrWidth(buf, pos);
895895+ lencol = utf8StrWidth(buf, len);
896896+897897+ /* Scroll the buffer horizontally if cursor is past the right edge.
898898+ * We need to trim full UTF-8 characters from the left until the
899899+ * cursor position fits within the terminal width. */
900900+ while (pwidth + poscol >= l->cols) {
901901+ size_t clen = utf8NextCharLen(buf, 0, len);
902902+ int cwidth = utf8SingleCharWidth(buf, clen);
903903+ buf += clen;
904904+ len -= clen;
905905+ pos -= clen;
906906+ poscol -= cwidth;
907907+ lencol -= cwidth;
908908+ }
909909+910910+ /* Trim from the right if the line still doesn't fit. */
911911+ while (pwidth + lencol > l->cols) {
912912+ size_t clen = utf8PrevCharLen(buf, len);
913913+ int cwidth = utf8SingleCharWidth(buf + len - clen, clen);
914914+ len -= clen;
915915+ lencol -= cwidth;
916916+ }
917917+918918+ abInit(&ab);
919919+ /* Cursor to left edge */
920920+ snprintf(seq,sizeof(seq),"\r");
921921+ abAppend(&ab,seq,strlen(seq));
922922+923923+ if (flags & REFRESH_WRITE) {
924924+ /* Write the prompt and the current buffer content */
925925+ abAppend(&ab,l->prompt,l->plen);
926926+ if (maskmode == 1) {
927927+ /* In mask mode, we output one '*' per UTF-8 character, not byte */
928928+ size_t i = 0;
929929+ while (i < len) {
930930+ abAppend(&ab,"*",1);
931931+ i += utf8NextCharLen(buf, i, len);
932932+ }
933933+ } else {
934934+ abAppend(&ab,buf,len);
935935+ }
936936+ /* Show hints if any. */
937937+ refreshShowHints(&ab,l,pwidth);
938938+ }
939939+940940+ /* Erase to right */
941941+ snprintf(seq,sizeof(seq),"\x1b[0K");
942942+ abAppend(&ab,seq,strlen(seq));
943943+944944+ if (flags & REFRESH_WRITE) {
945945+ /* Move cursor to original position (using display column, not byte). */
946946+ snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(poscol+pwidth));
947947+ abAppend(&ab,seq,strlen(seq));
948948+ }
949949+950950+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
951951+ abFree(&ab);
952952+}
953953+954954+/* Multi line low level line refresh.
955955+ *
956956+ * Rewrite the currently edited line accordingly to the buffer content,
957957+ * cursor position, and number of columns of the terminal.
958958+ *
959959+ * Flags is REFRESH_* macros. The function can just remove the old
960960+ * prompt, just write it, or both.
961961+ *
962962+ * This function is UTF-8 aware and uses display widths for positioning. */
963963+static void refreshMultiLine(struct linenoiseState *l, int flags) {
964964+ char seq[64];
965965+ size_t pwidth = utf8StrWidth(l->prompt, l->plen); /* Prompt display width */
966966+ size_t bufwidth = utf8StrWidth(l->buf, l->len); /* Buffer display width */
967967+ size_t poswidth = utf8StrWidth(l->buf, l->pos); /* Cursor display width */
968968+ int rows = (pwidth+bufwidth+l->cols-1)/l->cols; /* rows used by current buf. */
969969+ int rpos = l->oldrpos; /* cursor relative row from previous refresh. */
970970+ int rpos2; /* rpos after refresh. */
971971+ int col; /* column position, zero-based. */
972972+ int old_rows = l->oldrows;
973973+ int fd = l->ofd, j;
974974+ struct abuf ab;
975975+976976+ l->oldrows = rows;
977977+978978+ /* First step: clear all the lines used before. To do so start by
979979+ * going to the last row. */
980980+ abInit(&ab);
981981+982982+ if (flags & REFRESH_CLEAN) {
983983+ if (old_rows-rpos > 0) {
984984+ lndebug("go down %d", old_rows-rpos);
985985+ snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
986986+ abAppend(&ab,seq,strlen(seq));
987987+ }
988988+989989+ /* Now for every row clear it, go up. */
990990+ for (j = 0; j < old_rows-1; j++) {
991991+ lndebug("clear+up");
992992+ snprintf(seq,64,"\r\x1b[0K\x1b[1A");
993993+ abAppend(&ab,seq,strlen(seq));
994994+ }
995995+ }
996996+997997+ if (flags & REFRESH_ALL) {
998998+ /* Clean the top line. */
999999+ lndebug("clear");
10001000+ snprintf(seq,64,"\r\x1b[0K");
10011001+ abAppend(&ab,seq,strlen(seq));
10021002+ }
10031003+10041004+ if (flags & REFRESH_WRITE) {
10051005+ /* Write the prompt and the current buffer content */
10061006+ abAppend(&ab,l->prompt,l->plen);
10071007+ if (maskmode == 1) {
10081008+ /* In mask mode, output one '*' per UTF-8 character, not byte */
10091009+ size_t i = 0;
10101010+ while (i < l->len) {
10111011+ abAppend(&ab,"*",1);
10121012+ i += utf8NextCharLen(l->buf, i, l->len);
10131013+ }
10141014+ } else {
10151015+ abAppend(&ab,l->buf,l->len);
10161016+ }
10171017+10181018+ /* Show hints if any. */
10191019+ refreshShowHints(&ab,l,pwidth);
10201020+10211021+ /* If we are at the very end of the screen with our prompt, we need to
10221022+ * emit a newline and move the prompt to the first column. */
10231023+ if (l->pos &&
10241024+ l->pos == l->len &&
10251025+ (poswidth+pwidth) % l->cols == 0)
10261026+ {
10271027+ lndebug("<newline>");
10281028+ abAppend(&ab,"\n",1);
10291029+ snprintf(seq,64,"\r");
10301030+ abAppend(&ab,seq,strlen(seq));
10311031+ rows++;
10321032+ if (rows > (int)l->oldrows) l->oldrows = rows;
10331033+ }
10341034+10351035+ /* Move cursor to right position. */
10361036+ rpos2 = (pwidth+poswidth+l->cols)/l->cols; /* Current cursor relative row */
10371037+ lndebug("rpos2 %d", rpos2);
10381038+10391039+ /* Go up till we reach the expected position. */
10401040+ if (rows-rpos2 > 0) {
10411041+ lndebug("go-up %d", rows-rpos2);
10421042+ snprintf(seq,64,"\x1b[%dA", rows-rpos2);
10431043+ abAppend(&ab,seq,strlen(seq));
10441044+ }
10451045+10461046+ /* Set column. */
10471047+ col = (pwidth+poswidth) % l->cols;
10481048+ lndebug("set col %d", 1+col);
10491049+ if (col)
10501050+ snprintf(seq,64,"\r\x1b[%dC", col);
10511051+ else
10521052+ snprintf(seq,64,"\r");
10531053+ abAppend(&ab,seq,strlen(seq));
10541054+ }
10551055+10561056+ lndebug("\n");
10571057+ l->oldpos = l->pos;
10581058+ if (flags & REFRESH_WRITE) l->oldrpos = rpos2;
10591059+10601060+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
10611061+ abFree(&ab);
10621062+}
10631063+10641064+/* Calls the two low level functions refreshSingleLine() or
10651065+ * refreshMultiLine() according to the selected mode. */
10661066+static void refreshLineWithFlags(struct linenoiseState *l, int flags) {
10671067+ if (mlmode)
10681068+ refreshMultiLine(l,flags);
10691069+ else
10701070+ refreshSingleLine(l,flags);
10711071+}
10721072+10731073+/* Utility function to avoid specifying REFRESH_ALL all the times. */
10741074+static void refreshLine(struct linenoiseState *l) {
10751075+ refreshLineWithFlags(l,REFRESH_ALL);
10761076+}
10771077+10781078+/* Hide the current line, when using the multiplexing API. */
10791079+void linenoiseHide(struct linenoiseState *l) {
10801080+ if (mlmode)
10811081+ refreshMultiLine(l,REFRESH_CLEAN);
10821082+ else
10831083+ refreshSingleLine(l,REFRESH_CLEAN);
10841084+}
10851085+10861086+/* Show the current line, when using the multiplexing API. */
10871087+void linenoiseShow(struct linenoiseState *l) {
10881088+ if (l->in_completion) {
10891089+ refreshLineWithCompletion(l,NULL,REFRESH_WRITE);
10901090+ } else {
10911091+ refreshLineWithFlags(l,REFRESH_WRITE);
10921092+ }
10931093+}
10941094+10951095+/* Insert the character(s) 'c' of length 'clen' at cursor current position.
10961096+ * This handles both single-byte ASCII and multi-byte UTF-8 sequences.
10971097+ *
10981098+ * On error writing to the terminal -1 is returned, otherwise 0. */
10991099+int linenoiseEditInsert(struct linenoiseState *l, const char *c, size_t clen) {
11001100+ if (l->len + clen <= l->buflen) {
11011101+ if (l->len == l->pos) {
11021102+ /* Append at end of line. */
11031103+ memcpy(l->buf+l->pos, c, clen);
11041104+ l->pos += clen;
11051105+ l->len += clen;
11061106+ l->buf[l->len] = '\0';
11071107+ if ((!mlmode &&
11081108+ utf8StrWidth(l->prompt,l->plen)+utf8StrWidth(l->buf,l->len) < l->cols &&
11091109+ !hintsCallback)) {
11101110+ /* Avoid a full update of the line in the trivial case:
11111111+ * single-width char, no hints, fits in one line. */
11121112+ if (maskmode == 1) {
11131113+ if (write(l->ofd,"*",1) == -1) return -1;
11141114+ } else {
11151115+ if (write(l->ofd,c,clen) == -1) return -1;
11161116+ }
11171117+ } else {
11181118+ refreshLine(l);
11191119+ }
11201120+ } else {
11211121+ /* Insert in the middle of the line. */
11221122+ memmove(l->buf+l->pos+clen, l->buf+l->pos, l->len-l->pos);
11231123+ memcpy(l->buf+l->pos, c, clen);
11241124+ l->len += clen;
11251125+ l->pos += clen;
11261126+ l->buf[l->len] = '\0';
11271127+ refreshLine(l);
11281128+ }
11291129+ }
11301130+ return 0;
11311131+}
11321132+11331133+/* Move cursor on the left. Moves by one UTF-8 character, not byte. */
11341134+void linenoiseEditMoveLeft(struct linenoiseState *l) {
11351135+ if (l->pos > 0) {
11361136+ l->pos -= utf8PrevCharLen(l->buf, l->pos);
11371137+ refreshLine(l);
11381138+ }
11391139+}
11401140+11411141+/* Move cursor on the right. Moves by one UTF-8 character, not byte. */
11421142+void linenoiseEditMoveRight(struct linenoiseState *l) {
11431143+ if (l->pos != l->len) {
11441144+ l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
11451145+ refreshLine(l);
11461146+ }
11471147+}
11481148+11491149+/* Move cursor to the start of the line. */
11501150+void linenoiseEditMoveHome(struct linenoiseState *l) {
11511151+ if (l->pos != 0) {
11521152+ l->pos = 0;
11531153+ refreshLine(l);
11541154+ }
11551155+}
11561156+11571157+/* Move cursor to the end of the line. */
11581158+void linenoiseEditMoveEnd(struct linenoiseState *l) {
11591159+ if (l->pos != l->len) {
11601160+ l->pos = l->len;
11611161+ refreshLine(l);
11621162+ }
11631163+}
11641164+11651165+/* Substitute the currently edited line with the next or previous history
11661166+ * entry as specified by 'dir'. */
11671167+#define LINENOISE_HISTORY_NEXT 0
11681168+#define LINENOISE_HISTORY_PREV 1
11691169+void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
11701170+ if (history_len > 1) {
11711171+ /* Update the current history entry before to
11721172+ * overwrite it with the next one. */
11731173+ free(history[history_len - 1 - l->history_index]);
11741174+ history[history_len - 1 - l->history_index] = strdup(l->buf);
11751175+ /* Show the new entry */
11761176+ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
11771177+ if (l->history_index < 0) {
11781178+ l->history_index = 0;
11791179+ return;
11801180+ } else if (l->history_index >= history_len) {
11811181+ l->history_index = history_len-1;
11821182+ return;
11831183+ }
11841184+ strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
11851185+ l->buf[l->buflen-1] = '\0';
11861186+ l->len = l->pos = strlen(l->buf);
11871187+ refreshLine(l);
11881188+ }
11891189+}
11901190+11911191+/* Delete the character at the right of the cursor without altering the cursor
11921192+ * position. Basically this is what happens with the "Delete" keyboard key.
11931193+ * Now handles multi-byte UTF-8 characters. */
11941194+void linenoiseEditDelete(struct linenoiseState *l) {
11951195+ if (l->len > 0 && l->pos < l->len) {
11961196+ size_t clen = utf8NextCharLen(l->buf, l->pos, l->len);
11971197+ memmove(l->buf+l->pos, l->buf+l->pos+clen, l->len-l->pos-clen);
11981198+ l->len -= clen;
11991199+ l->buf[l->len] = '\0';
12001200+ refreshLine(l);
12011201+ }
12021202+}
12031203+12041204+/* Backspace implementation. Deletes the UTF-8 character before the cursor. */
12051205+void linenoiseEditBackspace(struct linenoiseState *l) {
12061206+ if (l->pos > 0 && l->len > 0) {
12071207+ size_t clen = utf8PrevCharLen(l->buf, l->pos);
12081208+ memmove(l->buf+l->pos-clen, l->buf+l->pos, l->len-l->pos);
12091209+ l->pos -= clen;
12101210+ l->len -= clen;
12111211+ l->buf[l->len] = '\0';
12121212+ refreshLine(l);
12131213+ }
12141214+}
12151215+12161216+/* Delete the previous word, maintaining the cursor at the start of the
12171217+ * current word. Handles UTF-8 by moving character-by-character. */
12181218+void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
12191219+ size_t old_pos = l->pos;
12201220+ size_t diff;
12211221+12221222+ /* Skip spaces before the word (move backwards by UTF-8 chars). */
12231223+ while (l->pos > 0 && l->buf[l->pos-1] == ' ')
12241224+ l->pos -= utf8PrevCharLen(l->buf, l->pos);
12251225+ /* Skip non-space characters (move backwards by UTF-8 chars). */
12261226+ while (l->pos > 0 && l->buf[l->pos-1] != ' ')
12271227+ l->pos -= utf8PrevCharLen(l->buf, l->pos);
12281228+ diff = old_pos - l->pos;
12291229+ memmove(l->buf+l->pos, l->buf+old_pos, l->len-old_pos+1);
12301230+ l->len -= diff;
12311231+ refreshLine(l);
12321232+}
12331233+12341234+/* This function is part of the multiplexed API of Linenoise, that is used
12351235+ * in order to implement the blocking variant of the API but can also be
12361236+ * called by the user directly in an event driven program. It will:
12371237+ *
12381238+ * 1. Initialize the linenoise state passed by the user.
12391239+ * 2. Put the terminal in RAW mode.
12401240+ * 3. Show the prompt.
12411241+ * 4. Return control to the user, that will have to call linenoiseEditFeed()
12421242+ * each time there is some data arriving in the standard input.
12431243+ *
12441244+ * The user can also call linenoiseEditHide() and linenoiseEditShow() if it
12451245+ * is required to show some input arriving asyncronously, without mixing
12461246+ * it with the currently edited line.
12471247+ *
12481248+ * When linenoiseEditFeed() returns non-NULL, the user finished with the
12491249+ * line editing session (pressed enter CTRL-D/C): in this case the caller
12501250+ * needs to call linenoiseEditStop() to put back the terminal in normal
12511251+ * mode. This will not destroy the buffer, as long as the linenoiseState
12521252+ * is still valid in the context of the caller.
12531253+ *
12541254+ * The function returns 0 on success, or -1 if writing to standard output
12551255+ * fails. If stdin_fd or stdout_fd are set to -1, the default is to use
12561256+ * STDIN_FILENO and STDOUT_FILENO.
12571257+ */
12581258+int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) {
12591259+ /* Populate the linenoise state that we pass to functions implementing
12601260+ * specific editing functionalities. */
12611261+ l->in_completion = 0;
12621262+ l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
12631263+ l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
12641264+ l->buf = buf;
12651265+ l->buflen = buflen;
12661266+ l->prompt = prompt;
12671267+ l->plen = strlen(prompt);
12681268+ l->oldpos = l->pos = 0;
12691269+ l->len = 0;
12701270+12711271+ /* Enter raw mode. */
12721272+ if (enableRawMode(l->ifd) == -1) return -1;
12731273+12741274+ l->cols = getColumns(stdin_fd, stdout_fd);
12751275+ l->oldrows = 0;
12761276+ l->oldrpos = 1; /* Cursor starts on row 1. */
12771277+ l->history_index = 0;
12781278+12791279+ /* Buffer starts empty. */
12801280+ l->buf[0] = '\0';
12811281+ l->buflen--; /* Make sure there is always space for the nulterm */
12821282+12831283+ /* If stdin is not a tty, stop here with the initialization. We
12841284+ * will actually just read a line from standard input in blocking
12851285+ * mode later, in linenoiseEditFeed(). */
12861286+ if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return 0;
12871287+12881288+ /* The latest history entry is always our current buffer, that
12891289+ * initially is just an empty string. */
12901290+ linenoiseHistoryAdd("");
12911291+12921292+ if (write(l->ofd,prompt,l->plen) == -1) return -1;
12931293+ return 0;
12941294+}
12951295+12961296+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.";
12971297+12981298+/* This function is part of the multiplexed API of linenoise, see the top
12991299+ * comment on linenoiseEditStart() for more information. Call this function
13001300+ * each time there is some data to read from the standard input file
13011301+ * descriptor. In the case of blocking operations, this function can just be
13021302+ * called in a loop, and block.
13031303+ *
13041304+ * The function returns linenoiseEditMore to signal that line editing is still
13051305+ * in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise
13061306+ * the function returns the pointer to the heap-allocated buffer with the
13071307+ * edited line, that the user should free with linenoiseFree().
13081308+ *
13091309+ * On special conditions, NULL is returned and errno is populated:
13101310+ *
13111311+ * EAGAIN if the user pressed Ctrl-C
13121312+ * ENOENT if the user pressed Ctrl-D
13131313+ *
13141314+ * Some other errno: I/O error.
13151315+ */
13161316+char *linenoiseEditFeed(struct linenoiseState *l) {
13171317+ /* Not a TTY, pass control to line reading without character
13181318+ * count limits. */
13191319+ if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return linenoiseNoTTY();
13201320+13211321+ char c;
13221322+ int nread;
13231323+ char seq[3];
13241324+13251325+ nread = read(l->ifd,&c,1);
13261326+ if (nread < 0) {
13271327+ return (errno == EAGAIN || errno == EWOULDBLOCK) ? linenoiseEditMore : NULL;
13281328+ } else if (nread == 0) {
13291329+ return NULL;
13301330+ }
13311331+13321332+ /* Only autocomplete when the callback is set. It returns < 0 when
13331333+ * there was an error reading from fd. Otherwise it will return the
13341334+ * character that should be handled next. */
13351335+ if ((l->in_completion || c == 9) && completionCallback != NULL) {
13361336+ c = completeLine(l,c);
13371337+ /* Return on errors */
13381338+ if (c < 0) return NULL;
13391339+ /* Read next character when 0 */
13401340+ if (c == 0) return linenoiseEditMore;
13411341+ }
13421342+13431343+ switch(c) {
13441344+ case ENTER: /* enter */
13451345+ history_len--;
13461346+ free(history[history_len]);
13471347+ if (mlmode) linenoiseEditMoveEnd(l);
13481348+ if (hintsCallback) {
13491349+ /* Force a refresh without hints to leave the previous
13501350+ * line as the user typed it after a newline. */
13511351+ linenoiseHintsCallback *hc = hintsCallback;
13521352+ hintsCallback = NULL;
13531353+ refreshLine(l);
13541354+ hintsCallback = hc;
13551355+ }
13561356+ return strdup(l->buf);
13571357+ case CTRL_C: /* ctrl-c */
13581358+ errno = EAGAIN;
13591359+ return NULL;
13601360+ case BACKSPACE: /* backspace */
13611361+ case 8: /* ctrl-h */
13621362+ linenoiseEditBackspace(l);
13631363+ break;
13641364+ case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
13651365+ line is empty, act as end-of-file. */
13661366+ if (l->len > 0) {
13671367+ linenoiseEditDelete(l);
13681368+ } else {
13691369+ history_len--;
13701370+ free(history[history_len]);
13711371+ errno = ENOENT;
13721372+ return NULL;
13731373+ }
13741374+ break;
13751375+ case CTRL_T: /* ctrl-t, swaps current character with previous. */
13761376+ /* Handle UTF-8: swap the two UTF-8 characters around cursor. */
13771377+ if (l->pos > 0 && l->pos < l->len) {
13781378+ char tmp[32];
13791379+ size_t prevlen = utf8PrevCharLen(l->buf, l->pos);
13801380+ size_t currlen = utf8NextCharLen(l->buf, l->pos, l->len);
13811381+ size_t prevstart = l->pos - prevlen;
13821382+ /* Copy current char to tmp, move previous char right, paste tmp. */
13831383+ memcpy(tmp, l->buf + l->pos, currlen);
13841384+ memmove(l->buf + prevstart + currlen, l->buf + prevstart, prevlen);
13851385+ memcpy(l->buf + prevstart, tmp, currlen);
13861386+ if (l->pos + currlen <= l->len) l->pos += currlen;
13871387+ refreshLine(l);
13881388+ }
13891389+ break;
13901390+ case CTRL_B: /* ctrl-b */
13911391+ linenoiseEditMoveLeft(l);
13921392+ break;
13931393+ case CTRL_F: /* ctrl-f */
13941394+ linenoiseEditMoveRight(l);
13951395+ break;
13961396+ case CTRL_P: /* ctrl-p */
13971397+ linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
13981398+ break;
13991399+ case CTRL_N: /* ctrl-n */
14001400+ linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
14011401+ break;
14021402+ case ESC: /* escape sequence */
14031403+ /* Read the next two bytes representing the escape sequence.
14041404+ * Use two calls to handle slow terminals returning the two
14051405+ * chars at different times. */
14061406+ if (read(l->ifd,seq,1) == -1) break;
14071407+ if (read(l->ifd,seq+1,1) == -1) break;
14081408+14091409+ /* ESC [ sequences. */
14101410+ if (seq[0] == '[') {
14111411+ if (seq[1] >= '0' && seq[1] <= '9') {
14121412+ /* Extended escape, read additional byte. */
14131413+ if (read(l->ifd,seq+2,1) == -1) break;
14141414+ if (seq[2] == '~') {
14151415+ switch(seq[1]) {
14161416+ case '3': /* Delete key. */
14171417+ linenoiseEditDelete(l);
14181418+ break;
14191419+ }
14201420+ }
14211421+ } else {
14221422+ switch(seq[1]) {
14231423+ case 'A': /* Up */
14241424+ linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
14251425+ break;
14261426+ case 'B': /* Down */
14271427+ linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
14281428+ break;
14291429+ case 'C': /* Right */
14301430+ linenoiseEditMoveRight(l);
14311431+ break;
14321432+ case 'D': /* Left */
14331433+ linenoiseEditMoveLeft(l);
14341434+ break;
14351435+ case 'H': /* Home */
14361436+ linenoiseEditMoveHome(l);
14371437+ break;
14381438+ case 'F': /* End*/
14391439+ linenoiseEditMoveEnd(l);
14401440+ break;
14411441+ }
14421442+ }
14431443+ }
14441444+14451445+ /* ESC O sequences. */
14461446+ else if (seq[0] == 'O') {
14471447+ switch(seq[1]) {
14481448+ case 'H': /* Home */
14491449+ linenoiseEditMoveHome(l);
14501450+ break;
14511451+ case 'F': /* End*/
14521452+ linenoiseEditMoveEnd(l);
14531453+ break;
14541454+ }
14551455+ }
14561456+ break;
14571457+ default:
14581458+ /* Handle UTF-8 multi-byte sequences. When we receive the first byte
14591459+ * of a multi-byte UTF-8 character, read the remaining bytes to
14601460+ * complete the sequence before inserting. */
14611461+ {
14621462+ char utf8[4];
14631463+ int utf8len = utf8ByteLen(c);
14641464+ utf8[0] = c;
14651465+ if (utf8len > 1) {
14661466+ /* Read remaining bytes of the UTF-8 sequence. */
14671467+ int i;
14681468+ for (i = 1; i < utf8len; i++) {
14691469+ if (read(l->ifd, utf8+i, 1) != 1) break;
14701470+ }
14711471+ }
14721472+ if (linenoiseEditInsert(l, utf8, utf8len)) return NULL;
14731473+ }
14741474+ break;
14751475+ case CTRL_U: /* Ctrl+u, delete the whole line. */
14761476+ l->buf[0] = '\0';
14771477+ l->pos = l->len = 0;
14781478+ refreshLine(l);
14791479+ break;
14801480+ case CTRL_K: /* Ctrl+k, delete from current to end of line. */
14811481+ l->buf[l->pos] = '\0';
14821482+ l->len = l->pos;
14831483+ refreshLine(l);
14841484+ break;
14851485+ case CTRL_A: /* Ctrl+a, go to the start of the line */
14861486+ linenoiseEditMoveHome(l);
14871487+ break;
14881488+ case CTRL_E: /* ctrl+e, go to the end of the line */
14891489+ linenoiseEditMoveEnd(l);
14901490+ break;
14911491+ case CTRL_L: /* ctrl+l, clear screen */
14921492+ linenoiseClearScreen();
14931493+ refreshLine(l);
14941494+ break;
14951495+ case CTRL_W: /* ctrl+w, delete previous word */
14961496+ linenoiseEditDeletePrevWord(l);
14971497+ break;
14981498+ }
14991499+ return linenoiseEditMore;
15001500+}
15011501+15021502+/* This is part of the multiplexed linenoise API. See linenoiseEditStart()
15031503+ * for more information. This function is called when linenoiseEditFeed()
15041504+ * returns something different than NULL. At this point the user input
15051505+ * is in the buffer, and we can restore the terminal in normal mode. */
15061506+void linenoiseEditStop(struct linenoiseState *l) {
15071507+ if (!isatty(l->ifd) && !getenv("LINENOISE_ASSUME_TTY")) return;
15081508+ disableRawMode(l->ifd);
15091509+ printf("\n");
15101510+}
15111511+15121512+/* This just implements a blocking loop for the multiplexed API.
15131513+ * In many applications that are not event-drivern, we can just call
15141514+ * the blocking linenoise API, wait for the user to complete the editing
15151515+ * and return the buffer. */
15161516+static char *linenoiseBlockingEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
15171517+{
15181518+ struct linenoiseState l;
15191519+15201520+ /* Editing without a buffer is invalid. */
15211521+ if (buflen == 0) {
15221522+ errno = EINVAL;
15231523+ return NULL;
15241524+ }
15251525+15261526+ linenoiseEditStart(&l,stdin_fd,stdout_fd,buf,buflen,prompt);
15271527+ char *res;
15281528+ while((res = linenoiseEditFeed(&l)) == linenoiseEditMore);
15291529+ linenoiseEditStop(&l);
15301530+ return res;
15311531+}
15321532+15331533+/* This special mode is used by linenoise in order to print scan codes
15341534+ * on screen for debugging / development purposes. It is implemented
15351535+ * by the linenoise_example program using the --keycodes option. */
15361536+void linenoisePrintKeyCodes(void) {
15371537+ char quit[4];
15381538+15391539+ printf("Linenoise key codes debugging mode.\n"
15401540+ "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
15411541+ if (enableRawMode(STDIN_FILENO) == -1) return;
15421542+ memset(quit,' ',4);
15431543+ while(1) {
15441544+ char c;
15451545+ int nread;
15461546+15471547+ nread = read(STDIN_FILENO,&c,1);
15481548+ if (nread <= 0) continue;
15491549+ memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
15501550+ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
15511551+ if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
15521552+15531553+ printf("'%c' %02x (%d) (type quit to exit)\n",
15541554+ isprint(c) ? c : '?', (int)c, (int)c);
15551555+ printf("\r"); /* Go left edge manually, we are in raw mode. */
15561556+ fflush(stdout);
15571557+ }
15581558+ disableRawMode(STDIN_FILENO);
15591559+}
15601560+15611561+/* This function is called when linenoise() is called with the standard
15621562+ * input file descriptor not attached to a TTY. So for example when the
15631563+ * program using linenoise is called in pipe or with a file redirected
15641564+ * to its standard input. In this case, we want to be able to return the
15651565+ * line regardless of its length (by default we are limited to 4k). */
15661566+static char *linenoiseNoTTY(void) {
15671567+ char *line = NULL;
15681568+ size_t len = 0, maxlen = 0;
15691569+15701570+ while(1) {
15711571+ if (len == maxlen) {
15721572+ if (maxlen == 0) maxlen = 16;
15731573+ maxlen *= 2;
15741574+ char *oldval = line;
15751575+ line = realloc(line,maxlen);
15761576+ if (line == NULL) {
15771577+ if (oldval) free(oldval);
15781578+ return NULL;
15791579+ }
15801580+ }
15811581+ int c = fgetc(stdin);
15821582+ if (c == EOF || c == '\n') {
15831583+ if (c == EOF && len == 0) {
15841584+ free(line);
15851585+ return NULL;
15861586+ } else {
15871587+ line[len] = '\0';
15881588+ return line;
15891589+ }
15901590+ } else {
15911591+ line[len] = c;
15921592+ len++;
15931593+ }
15941594+ }
15951595+}
15961596+15971597+/* The high level function that is the main API of the linenoise library.
15981598+ * This function checks if the terminal has basic capabilities, just checking
15991599+ * for a blacklist of stupid terminals, and later either calls the line
16001600+ * editing function or uses dummy fgets() so that you will be able to type
16011601+ * something even in the most desperate of the conditions. */
16021602+char *linenoise(const char *prompt) {
16031603+ char buf[LINENOISE_MAX_LINE];
16041604+16051605+ if (!isatty(STDIN_FILENO) && !getenv("LINENOISE_ASSUME_TTY")) {
16061606+ /* Not a tty: read from file / pipe. In this mode we don't want any
16071607+ * limit to the line size, so we call a function to handle that. */
16081608+ return linenoiseNoTTY();
16091609+ } else if (isUnsupportedTerm()) {
16101610+ size_t len;
16111611+16121612+ printf("%s",prompt);
16131613+ fflush(stdout);
16141614+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
16151615+ len = strlen(buf);
16161616+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
16171617+ len--;
16181618+ buf[len] = '\0';
16191619+ }
16201620+ return strdup(buf);
16211621+ } else {
16221622+ char *retval = linenoiseBlockingEdit(STDIN_FILENO,STDOUT_FILENO,buf,LINENOISE_MAX_LINE,prompt);
16231623+ return retval;
16241624+ }
16251625+}
16261626+16271627+/* This is just a wrapper the user may want to call in order to make sure
16281628+ * the linenoise returned buffer is freed with the same allocator it was
16291629+ * created with. Useful when the main program is using an alternative
16301630+ * allocator. */
16311631+void linenoiseFree(void *ptr) {
16321632+ if (ptr == linenoiseEditMore) return; // Protect from API misuse.
16331633+ free(ptr);
16341634+}
16351635+16361636+/* ================================ History ================================= */
16371637+16381638+/* Free the history, but does not reset it. Only used when we have to
16391639+ * exit() to avoid memory leaks are reported by valgrind & co. */
16401640+static void freeHistory(void) {
16411641+ if (history) {
16421642+ int j;
16431643+16441644+ for (j = 0; j < history_len; j++)
16451645+ free(history[j]);
16461646+ free(history);
16471647+ }
16481648+}
16491649+16501650+/* At exit we'll try to fix the terminal to the initial conditions. */
16511651+static void linenoiseAtExit(void) {
16521652+ disableRawMode(STDIN_FILENO);
16531653+ freeHistory();
16541654+}
16551655+16561656+/* This is the API call to add a new entry in the linenoise history.
16571657+ * It uses a fixed array of char pointers that are shifted (memmoved)
16581658+ * when the history max length is reached in order to remove the older
16591659+ * entry and make room for the new one, so it is not exactly suitable for huge
16601660+ * histories, but will work well for a few hundred of entries.
16611661+ *
16621662+ * Using a circular buffer is smarter, but a bit more complex to handle. */
16631663+int linenoiseHistoryAdd(const char *line) {
16641664+ char *linecopy;
16651665+16661666+ if (history_max_len == 0) return 0;
16671667+16681668+ /* Initialization on first call. */
16691669+ if (history == NULL) {
16701670+ history = malloc(sizeof(char*)*history_max_len);
16711671+ if (history == NULL) return 0;
16721672+ memset(history,0,(sizeof(char*)*history_max_len));
16731673+ }
16741674+16751675+ /* Don't add duplicated lines. */
16761676+ if (history_len && !strcmp(history[history_len-1], line)) return 0;
16771677+16781678+ /* Add an heap allocated copy of the line in the history.
16791679+ * If we reached the max length, remove the older line. */
16801680+ linecopy = strdup(line);
16811681+ if (!linecopy) return 0;
16821682+ if (history_len == history_max_len) {
16831683+ free(history[0]);
16841684+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
16851685+ history_len--;
16861686+ }
16871687+ history[history_len] = linecopy;
16881688+ history_len++;
16891689+ return 1;
16901690+}
16911691+16921692+/* Set the maximum length for the history. This function can be called even
16931693+ * if there is already some history, the function will make sure to retain
16941694+ * just the latest 'len' elements if the new history length value is smaller
16951695+ * than the amount of items already inside the history. */
16961696+int linenoiseHistorySetMaxLen(int len) {
16971697+ char **new;
16981698+16991699+ if (len < 1) return 0;
17001700+ if (history) {
17011701+ int tocopy = history_len;
17021702+17031703+ new = malloc(sizeof(char*)*len);
17041704+ if (new == NULL) return 0;
17051705+17061706+ /* If we can't copy everything, free the elements we'll not use. */
17071707+ if (len < tocopy) {
17081708+ int j;
17091709+17101710+ for (j = 0; j < tocopy-len; j++) free(history[j]);
17111711+ tocopy = len;
17121712+ }
17131713+ memset(new,0,sizeof(char*)*len);
17141714+ memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
17151715+ free(history);
17161716+ history = new;
17171717+ }
17181718+ history_max_len = len;
17191719+ if (history_len > history_max_len)
17201720+ history_len = history_max_len;
17211721+ return 1;
17221722+}
17231723+17241724+/* Save the history in the specified file. On success 0 is returned
17251725+ * otherwise -1 is returned. */
17261726+int linenoiseHistorySave(const char *filename) {
17271727+ mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
17281728+ FILE *fp;
17291729+ int j;
17301730+17311731+ fp = fopen(filename,"w");
17321732+ umask(old_umask);
17331733+ if (fp == NULL) return -1;
17341734+ fchmod(fileno(fp),S_IRUSR|S_IWUSR);
17351735+ for (j = 0; j < history_len; j++)
17361736+ fprintf(fp,"%s\n",history[j]);
17371737+ fclose(fp);
17381738+ return 0;
17391739+}
17401740+17411741+/* Load the history from the specified file. If the file does not exist
17421742+ * zero is returned and no operation is performed.
17431743+ *
17441744+ * If the file exists and the operation succeeded 0 is returned, otherwise
17451745+ * on error -1 is returned. */
17461746+int linenoiseHistoryLoad(const char *filename) {
17471747+ FILE *fp = fopen(filename,"r");
17481748+ char buf[LINENOISE_MAX_LINE];
17491749+17501750+ if (fp == NULL) return -1;
17511751+17521752+ while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
17531753+ char *p;
17541754+17551755+ p = strchr(buf,'\r');
17561756+ if (!p) p = strchr(buf,'\n');
17571757+ if (p) *p = '\0';
17581758+ linenoiseHistoryAdd(buf);
17591759+ }
17601760+ fclose(fp);
17611761+ return 0;
17621762+}