···11+let complete s =
22+ match String.get s 0 with
33+ | 'h' -> [ "hello"; "hello there" ]
44+ | _ | (exception Invalid_argument _) -> []
55+16let () =
27 Fmt_tty.setup_std_outputs ();
38 let rec loop sys_break =
44- let prompt = if sys_break then "[\x1b[31m130\x1b[0m] >> " else ">> " in
55- match Bruit.bruit prompt with
99+ let prompt =
1010+ if sys_break then "[\x1b[31m130\x1b[0m] \x1b[33m>>\x1b[0m "
1111+ else "\x1b[33m>>\x1b[0m "
1212+ in
1313+ match Bruit.bruit ~complete prompt with
614 | String (Some s) ->
715 Fmt.pr "%s\n%!" s;
816 loop false
+91-19
src/bruit.ml
···2828 | _ -> Unknown (Uchar.of_char c)
29293030module State = struct
3131+ type completion = string -> string list
3232+3133 type t = {
3232- in_completion : int;
3334 ifd : Unix.file_descr;
3435 ofd : Unix.file_descr;
3536 buf : bytes;
···4546 old_row_pos : int;
4647 history_index : int;
4748 read_buf : Bytes.t;
4949+ in_completion : bool;
5050+ completion_idx : int;
5151+ complete : completion option;
4852 }
49535050- let make ?(in_completion = 0) ?(old_pos = 0) ?(pos = 0) ?(len = 0)
5151- ?(ifd = Unix.stdin) ?(ofd = Unix.stdout) ~prompt buf =
5454+ let make ?(in_completion = false) ?(completion_idx = 0) ?complete
5555+ ?(old_pos = 0) ?(pos = 0) ?(len = 0) ?(ifd = Unix.stdin)
5656+ ?(ofd = Unix.stdout) ~prompt buf =
5257 {
5358 in_completion;
5459 ifd;
···6469 old_row_pos = 1;
6570 old_rows = 0;
6671 history_index = 0;
7272+ complete;
7373+ completion_idx;
6774 read_buf = Bytes.create 1 (* For reading a character *);
6875 }
69767070- 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) =
7777+ let override ?in_completion ?completion_idx ?complete ?ifd ?ofd ?buf ?buf_len
7878+ ?prompt ?plen ?old_pos ?pos ?len ?cols ?old_rows ?old_row_pos
7979+ ?history_index (t : t) =
7280 {
7381 in_completion = Option.value ~default:t.in_completion in_completion;
7482 ifd = Option.value ~default:t.ifd ifd;
···8492 old_rows = Option.value ~default:t.old_rows old_rows;
8593 old_row_pos = Option.value ~default:t.old_row_pos old_row_pos;
8694 history_index = Option.value ~default:t.history_index history_index;
9595+ complete = (match complete with Some f -> Some f | None -> t.complete);
8796 read_buf = t.read_buf;
9797+ completion_idx = Option.value ~default:t.completion_idx completion_idx;
8898 }
8999end
90100···208218209219let refresh_line state = refresh_single_line ~flags:[ Rewrite ] state
210220221221+let refresh_line_with_completions (state : State.t) lcs =
222222+ if state.completion_idx < List.length lcs then
223223+ let saved_state = state in
224224+ let saved_buf = Bytes.copy state.buf in
225225+ let state =
226226+ State.override
227227+ ~len:(Bytes.length (List.nth lcs state.completion_idx))
228228+ ~pos:(Bytes.length (List.nth lcs state.completion_idx))
229229+ ~buf:(List.nth lcs state.completion_idx)
230230+ state
231231+ in
232232+ let state : State.t = refresh_line state in
233233+ State.override state ~len:saved_state.len ~pos:saved_state.pos
234234+ ~buf:saved_buf
235235+ else state
236236+211237let edit_insert (state : State.t) c =
212238 let clen = Uchar.utf_8_byte_length c in
213239 (* At the end of the line *)
···244270 in
245271 refresh_line state
246272273273+let complete_line (state : State.t) c cn =
274274+ match cn (String.of_bytes state.buf) with
275275+ | [] -> (State.override ~in_completion:false state, `Char c)
276276+ | xs ->
277277+ let state, c =
278278+ match key_of_char c with
279279+ | Tab ->
280280+ if not state.in_completion then
281281+ ( State.override ~in_completion:true ~completion_idx:0 state,
282282+ `Edit_more )
283283+ else
284284+ ( State.override
285285+ ~completion_idx:
286286+ ((state.completion_idx + 1) mod (List.length xs + 1))
287287+ state,
288288+ `Edit_more )
289289+ | _ ->
290290+ let state =
291291+ if state.completion_idx < List.length xs then begin
292292+ let to_write = List.nth xs state.completion_idx in
293293+ let to_write_len = String.length to_write in
294294+ Bytes.blit_string to_write 0 state.buf 0 to_write_len;
295295+ State.override ~len:to_write_len ~pos:to_write_len state
296296+ end
297297+ else state
298298+ in
299299+ (State.override ~in_completion:false state, `Char c)
300300+ in
301301+ if state.in_completion && state.completion_idx < List.length xs then begin
302302+ (refresh_line_with_completions state (List.map Bytes.of_string xs), c)
303303+ end
304304+ else begin
305305+ (refresh_line state, c)
306306+ end
307307+247308let edit_feed state =
248309 match read_char state with
249310 | `Editing -> Editing state
250311 | `None -> Finished None
251312 | `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)
313313+ let uc = Uchar.of_char c in
314314+ let state, c =
315315+ if state.in_completion || key_of_char c = Tab then
316316+ match state.complete with
317317+ | None -> (state, `Char c)
318318+ | Some cn -> complete_line state c cn
319319+ else (state, `Char c)
320320+ in
321321+ match c with
322322+ | `Edit_more -> Editing state
323323+ | `Char c -> (
324324+ match key_of_char c with
325325+ | Enter -> Finished (Some state.buf)
326326+ | Ctrl_d ->
327327+ if Int.equal state.len 0 then Finished None else assert false
328328+ | Ctrl_c -> Ctrl_c
329329+ | Backspace -> Editing (edit_backspace state)
330330+ | Tab -> Editing state
331331+ | Unknown _ | _ ->
332332+ let state = edit_insert state uc in
333333+ Editing state))
262334263335type result = String of string option | Ctrl_c
264336265265-let blocking_edit ~stdin ~stdout buf ~prompt =
266266- let state = State.make ~prompt buf in
337337+let blocking_edit ?complete ~stdin ~stdout buf ~prompt =
338338+ let state = State.make ?complete ~prompt buf in
267339 let res =
268340 edit_start ~stdin ~stdout state @@ fun state ->
269341 let rec loop = function
···276348 Format.printf "\n%!";
277349 res
278350279279-let bruit prompt =
351351+let bruit ?complete prompt =
280352 let prompt = Bytes.of_string prompt in
281353 let buf = Bytes.create max_line in
282354 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
355355+ else blocking_edit ?complete ~stdin:Unix.stdin ~stdout:Unix.stdout buf ~prompt
284356285357(*
286358 * Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
+5-3
src/bruit.mli
···6677type result = String of string option | Ctrl_c
8899-val bruit : string -> result
1010-(** [bruit prompt] reads from [stdin] and returns the read string if any, and on
1111- [ctrl+c] returns {! Ctrl_c}. *)
99+val bruit : ?complete:(string -> string list) -> string -> result
1010+(** [bruit ?complete prompt] reads from [stdin] and returns the read string if
1111+ any, and on [ctrl+c] returns {! Ctrl_c}.
1212+1313+ @param complete Used for completions, defaults to [None]. *)
-1762
src/linenoise.c
···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-}