diff options
Diffstat (limited to 'linenoise')
-rw-r--r-- | linenoise/linenoise.c | 198 | ||||
-rw-r--r-- | linenoise/linenoise.h | 11 |
2 files changed, 170 insertions, 39 deletions
diff --git a/linenoise/linenoise.c b/linenoise/linenoise.c index e61ba49d..ca75c39c 100644 --- a/linenoise/linenoise.c +++ b/linenoise/linenoise.c @@ -14,7 +14,7 @@ * * Copyright (c) 2010-2015, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> - * Copyright (c) 2015-2020, Kaz Kylheku <kaz at kylheku dot com> + * Copyright (c) 2015-2024, Kaz Kylheku <kaz at kylheku dot com> * * All rights reserved. * @@ -42,8 +42,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <termios.h> -#include <unistd.h> #include <stddef.h> #include <wchar.h> #include <stdlib.h> @@ -52,13 +50,16 @@ #include <string.h> #include <ctype.h> #include <wctype.h> -#include <sys/types.h> -#include <sys/ioctl.h> #include <signal.h> #include <limits.h> #include <assert.h> #include <stdarg.h> +#include <unistd.h> #include "config.h" +#if CONFIG_FULL_REPL +#include <termios.h> +#include <sys/types.h> +#include <sys/ioctl.h> #if HAVE_POLL #include <poll.h> #endif @@ -67,6 +68,7 @@ #include <sys/fcntl.h> #include <io.h> #endif +#endif #include "linenoise.h" #ifdef __cplusplus @@ -92,33 +94,46 @@ struct lino_state { lino_t *next, *prev; /* Links for global list: must be first */ /* Lifetime enduring state */ +#if CONFIG_FULL_REPL lino_compl_cb_t *completion_callback; +#endif void *cb_ctx; /* User context for completion callback */ +#if CONFIG_FULL_REPL lino_atom_cb_t *atom_callback; void *ca_ctx; /* User context for atom callback */ +#endif lino_enter_cb_t *enter_callback; void *ce_ctx; /* User context for enter callback */ +#if CONFIG_FULL_REPL struct termios orig_termios; /* In order to restore at exit.*/ +#endif #ifdef __CYGWIN__ int orig_imode, orig_omode; #endif +#if CONFIG_FULL_REPL int rawmode; /* For atexit() function to check if restore is needed*/ int mlmode; /* Multi line mode. Default is single line. */ +#endif int history_max_len; int history_len; int loaded_lines; /* How many lines come from load. */ wchar_t **history; +#if CONFIG_FULL_REPL wchar_t *clip; /* Selection */ wchar_t *result; /* Previous command result. */ +#endif mem_t *tty_ifs; /* Terminal input file stream. */ mem_t *tty_ofs; /* Terminal output file stream. */ int save_hist_idx; /* Jump to history position on entry into edit */ /* Volatile state pertaining to just one linenoise call */ - wchar_t buf[LINENOISE_MAX_DISP]; /* Displayed line bufer. */ +#if CONFIG_FULL_REPL + wchar_t buf[LINENOISE_MAX_DISP]; /* Displayed line buffer. */ +#endif wchar_t data[LINENOISE_MAX_LINE]; /* True data corresponding to display */ const wchar_t *prompt; /* Prompt to display. */ const char *suffix; /* Suffix when creating temp file. */ +#if CONFIG_FULL_REPL int plen; /* Prompt length. */ int pos; /* Current cursor position. */ int sel; /* Selection start in terms of display. */ @@ -137,7 +152,11 @@ struct lino_state { int selmode; /* Visual selection being made. */ int selinclusive; /* Selections include character right of endpoint. */ int noninteractive; /* No character editing, even if input is tty. */ +#endif + int show_prompt; /* Show prompting in non-interactive mode. */ +#if CONFIG_FULL_REPL struct lino_undo *undo_stack; +#endif lino_error_t error; /* Most recent error. */ }; @@ -159,12 +178,16 @@ enum key_action { }; static lino_os_t lino_os; -static lino_t lino_list = { &lino_list, &lino_list }; +static lino_t lino_list; volatile sig_atomic_t lino_list_busy; +#if CONFIG_FULL_REPL static int atexit_registered = 0; /* Register atexit just 1 time. */ +#endif #define nelem(array) (sizeof (array) / sizeof (array)[0]) +#if CONFIG_FULL_REPL + static int wcsnprintf(wchar_t *s, size_t nchar, const wchar_t *fmt, ...) { int ret; @@ -177,8 +200,11 @@ static int wcsnprintf(wchar_t *s, size_t nchar, const wchar_t *fmt, ...) return wcslen(s); } +#endif + /* ======================= Low level terminal handling ====================== */ +#if CONFIG_FULL_REPL /* Set if to use or not the multi line mode. */ void lino_set_multiline(lino_t *ls, int ml) { ls->mlmode = ml; @@ -197,6 +223,7 @@ int lino_get_selinculsive(lino_t *ls) return ls->selinclusive; } + void lino_set_noninteractive(lino_t *ls, int ni) { ls->noninteractive = ni; @@ -207,18 +234,31 @@ int lino_get_noninteractive(lino_t *ls) return ls->noninteractive; } +#endif + +void lino_enable_noninteractive_prompt(lino_t *ls, int enable) +{ + ls->show_prompt = enable; +} + +#if CONFIG_FULL_REPL + void lino_set_atom_cb(lino_t *l, lino_atom_cb_t *cb, void *ctx) { l->atom_callback = cb; l->ca_ctx = ctx; } +#endif + void lino_set_enter_cb(lino_t *l, lino_enter_cb_t *cb, void *ctx) { l->enter_callback = cb; l->ce_ctx = ctx; } +#if CONFIG_FULL_REPL + static void atexit_handler(void); /* Raw mode: 1960 magic shit. */ @@ -243,13 +283,13 @@ static int enable_raw_mode(lino_t *ls) { raw = ls->orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_iflag &= convert(tcflag_t, ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)); /* we don't change any output modes (c_oflag) */ /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_lflag &= convert(tcflag_t, ~(ECHO | ICANON | IEXTEN | ISIG)); /* control chars - set return condition: min number of bytes and timer. * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ @@ -406,8 +446,10 @@ static void free_undo_stack(lino_t *l) static void record_undo(lino_t *l) { - struct lino_undo *rec = (struct lino_undo *) lino_os.alloc_fn(sizeof *rec), *iter; - wchar_t *data = (wchar_t *) lino_os.wstrdup_fn(l->data); + struct lino_undo *rec = coerce(struct lino_undo *, + lino_os.alloc_fn(sizeof *rec)); + struct lino_undo *iter; + wchar_t *data = lino_os.wstrdup_fn(l->data); int count; if (rec == 0 || data == 0) { @@ -721,6 +763,8 @@ static int history_search(lino_t *l) verbatim: if (hl >= convert(int, nelem(hpat))) break; + if (c == CTL('J')) + c = '\r'; hpat[hl++] = c; /* fallthrough */ if (0) { @@ -784,7 +828,7 @@ static int history_search(lino_t *l) break; case CTL('Z'): disable_raw_mode(l); - raise(SIGTSTP); + kill(0, SIGTSTP); enable_raw_mode(l); } } @@ -840,14 +884,14 @@ static void show_help(lino_t *l) break; continue; case ESC: - if ((seq[0] = lino_os.getch_fn(l->tty_ifs)) < 0) + if ((seq[0] = lino_os.getch_fn(l->tty_ifs)) == WEOF) break; - if ((seq[1] = lino_os.getch_fn(l->tty_ifs)) < 0) + if ((seq[1] = lino_os.getch_fn(l->tty_ifs)) == WEOF) break; if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { - if ((seq[2] = lino_os.getch_fn(l->tty_ifs)) < 0) + if ((seq[2] = lino_os.getch_fn(l->tty_ifs)) == WEOF) break; if (seq[2] == '~') { switch(seq[1]) { @@ -886,7 +930,7 @@ static void show_help(lino_t *l) continue; case CTL('Z'): disable_raw_mode(l); - raise(SIGTSTP); + kill(0, SIGTSTP); enable_raw_mode(l); i -= 1; continue; @@ -965,7 +1009,7 @@ static void sync_data_to_buf(lino_t *l) l->sel = pos; if (l->dend == dpos) l->end = pos; - if (l->dsel == dpos - 1 && rev && l->selinclusive && ch && ch != '\r') + if (l->dsel == dpos - 1 && rev && l->selinclusive) l->sel = pos; if (ch) { @@ -1303,7 +1347,7 @@ static void move_cursor(lino_t *l, int npos) static int scan_match_rev(const wchar_t *s, int i, wchar_t mch) { while (i > 0) { - int ch = s[--i]; + wchar_t ch = s[--i]; if (ch == mch) return i; @@ -1348,7 +1392,7 @@ static int scan_rev(const wchar_t *s, int i) static int scan_match_fwd(const wchar_t *s, int i, wchar_t mch) { while (s[++i]) { - int ch = s[i]; + wchar_t ch = s[i]; if (ch == mch) return i; @@ -1483,7 +1527,7 @@ static void flash(lino_t *l, int ch) wchar_t on[2] = { ch }; const wchar_t *off = L"\b \b"; - if (l->dlen >= (int) nelem (l->data) - 1) + if (l->dlen >= convert(int, nelem (l->data)) - 1) return; for (i = 0; i < 2 && !cancel; i++) { @@ -1572,7 +1616,7 @@ static void delete_sel(lino_t *l) * * On error writing to the terminal -1 is returned, otherwise 0. */ static int edit_insert(lino_t *l, wchar_t c) { - if (l->dlen < (int) nelem(l->data) - 1) { + if (l->dlen < convert(int, nelem(l->data)) - 1) { record_triv_undo(l); delete_sel(l); if (l->dpos == l->dlen) { @@ -1631,7 +1675,7 @@ static int edit_insert(lino_t *l, wchar_t c) { static int edit_insert_str(lino_t *l, const wchar_t *s, int nchar) { - if (l->dlen < (int) nelem (l->data) - nchar) { + if (l->dlen < convert(int, nelem (l->data)) - nchar) { record_undo(l); delete_sel(l); @@ -1940,7 +1984,7 @@ static void edit_in_editor(lino_t *l) { char *ed = getenv("EDITOR"); char path[128]; - if (ed) { + if (ed && ed[0] != '\0') { const char *ho = get_home(); int fd; #if HAVE_MKSTEMPS @@ -1968,8 +2012,9 @@ static void edit_in_editor(lino_t *l) { if (fo) { char cmd[256]; - snprintf(cmd, sizeof cmd, "%s %s", ed, path); int preserve = 0; + + snprintf(cmd, sizeof cmd, "%s %s", ed, path); tr(l->data, '\r', '\n'); if (lino_os.puts_file_fn(fo, l->data) && lino_os.puts_file_fn(fo, L"\n")) @@ -2086,6 +2131,8 @@ static int edit(lino_t *l, const wchar_t *prompt) if (verbatim || (paste && c != ESC && c != BACKSPACE && c != CTL('H'))) { + if (verbatim && c == CTL('J')) + c = '\r'; if (edit_insert(l,c)) { l->error = lino_ioerr; goto out; @@ -2212,12 +2259,14 @@ static int edit(lino_t *l, const wchar_t *prompt) } break; } + l->save_hist_idx = l->history_index; + /* fallthrough */ + case CTL('F'): + ret = l->len; if (l->mlmode) edit_move_end(l); if (l->need_refresh) refresh_line(l); - ret = l->len; - l->save_hist_idx = l->history_index; goto out; case '?': extended = 0; @@ -2259,7 +2308,7 @@ static int edit(lino_t *l, const wchar_t *prompt) break; } - if (c < 0) + if (c == WEOF) goto out; if (c == 0) continue; @@ -2477,7 +2526,7 @@ static int edit(lino_t *l, const wchar_t *prompt) if (l->need_refresh) refresh_line(l); disable_raw_mode(l); - raise(SIGTSTP); + kill(0, SIGTSTP); enable_raw_mode(l); l->maxrows = 0; l->dpos = dpos; @@ -2505,6 +2554,8 @@ static void sigwinch_handler(int sig) { lino_t *li; + (void) sig; + if (lino_list_busy) return; @@ -2513,6 +2564,8 @@ static void sigwinch_handler(int sig) } #endif +#endif + /* The main function of the linenoise library * handles a non-TTY input file descriptor by opening * a standard I/O stream on it and reading lines @@ -2520,23 +2573,74 @@ static void sigwinch_handler(int sig) * the edit function. */ wchar_t *linenoise(lino_t *ls, const wchar_t *prompt) { - int count; int ifd = lino_os.fileno_fn(ls->tty_ifs); - if ( ls->noninteractive || !isatty(ifd)) { - /* Not a tty: read from file / pipe. */ - if (lino_os.getl_fn(ls->tty_ifs, ls->data, nelem(ls->data)) == 0) { - ls->error = (lino_os.eof_fn(ls->tty_ifs) ? lino_eof : lino_ioerr); - return 0; +#if CONFIG_FULL_REPL + int noninteractive = ls->noninteractive; + int plain = noninteractive || !isatty(ifd); +#else + int noninteractive = 1; + int plain = 1; +#endif + + if (plain) { + wchar_t *ret = 0; + size_t len = 0; + const wchar_t *condensed_prompt = prompt + wcslen(prompt); + int show_prompt = ls->show_prompt || (noninteractive && isatty(ifd)); + + if (show_prompt) { + while (condensed_prompt > prompt && + (*condensed_prompt == 0 || *condensed_prompt == ' ')) + { + condensed_prompt--; + } } - count = wcslen(ls->data); + for (;;) { + size_t nlen; + + if (show_prompt) + lino_os.puts_fn(ls->tty_ofs, ret ? condensed_prompt : prompt); + + /* Not a tty: read from file / pipe. */ + if (lino_os.getl_fn(ls->tty_ifs, ls->data, nelem(ls->data)) == 0) { + ls->error = (lino_os.eof_fn(ls->tty_ifs) ? lino_eof : lino_ioerr); + if (!lino_os.puts_fn(ls->tty_ofs, L"\n")) + ls->error = lino_ioerr; + break; + } + + nlen = wcslen(ls->data); - if (count && ls->data[count-1] == '\n') - ls->data[count-1] = '\0'; - return lino_os.wstrdup_fn(ls->data); + { + wchar_t *nret = lino_os.wrealloc_fn(ret, len + nlen + 1); + if (nret == 0) { + lino_os.free_fn(ret); + return 0; + } + wmemcpy(nret + len, ls->data, nlen + 1); + ret = nret; + len = len + nlen; + + if (len && ret[len-1] == '\n') + ret[len-1] = '\r'; + } + + if (!ls->enter_callback || ls->enter_callback(ret, ls->ce_ctx)) + break; + } + + if (ret != 0) { + if (len && ret[len - 1] == '\n') + ret[len-1] = '\0'; + } + + return ret; } else { wchar_t *ret = 0; +#if CONFIG_FULL_REPL + int count; #ifdef SIGWINCH static struct sigaction blank; struct sigaction sa = blank, oa; @@ -2562,6 +2666,7 @@ wchar_t *linenoise(lino_t *ls, const wchar_t *prompt) #ifdef SIGWINCH sigaction(SIGWINCH, &oa, 0); #endif +#endif return ret; } } @@ -2609,10 +2714,12 @@ lino_t *lino_copy(lino_t *le) *ls = *le; ls->history_len = 0; ls->history = 0; +#if CONFIG_FULL_REPL ls->rawmode = 0; ls->clip = 0; ls->result = 0; ls->undo_stack = 0; +#endif link_into_list(&lino_list, ls); } @@ -2625,13 +2732,17 @@ static void free_hist(lino_t *ls); static void lino_cleanup(lino_t *ls) { +#if CONFIG_FULL_REPL disable_raw_mode(ls); +#endif free_hist(ls); +#if CONFIG_FULL_REPL free_undo_stack(ls); lino_os.free_fn(ls->clip); ls->clip = 0; lino_os.free_fn(ls->result); ls->result = 0; +#endif } void lino_free(lino_t *ls) @@ -2677,6 +2788,8 @@ static void free_hist(lino_t *ls) { } } +#if CONFIG_FULL_REPL + /* At exit we'll try to fix the terminal to the initial conditions. */ static void atexit_handler(void) { lino_t *ls; @@ -2685,6 +2798,8 @@ static void atexit_handler(void) { lino_cleanup(ls); } +#endif + /* This is the API call to add a new entry in the linenoise history. * It uses a fixed array of char pointers that are shifted (memmoved) * when the history max length is reached in order to remove the older @@ -2724,7 +2839,9 @@ int lino_hist_add(lino_t *ls, const wchar_t *line) { } ls->history[ls->history_len] = linecopy; ls->history_len++; +#if CONFIG_FULL_REPL undo_renumber_hist_idx(ls, 1); +#endif return 1; } @@ -2825,6 +2942,8 @@ int lino_have_new_lines(lino_t *ls) return ls->history_len > ls->loaded_lines; } +#if CONFIG_FULL_REPL + void lino_set_result(lino_t *ls, wchar_t *res) { lino_os.free_fn(ls->result); @@ -2833,7 +2952,10 @@ void lino_set_result(lino_t *ls, wchar_t *res) *res = '\r'; } +#endif + void lino_init(lino_os_t *os) { lino_os = *os; + lino_list.next = lino_list.prev = &lino_list; } diff --git a/linenoise/linenoise.h b/linenoise/linenoise.h index da6e93de..34d0a993 100644 --- a/linenoise/linenoise.h +++ b/linenoise/linenoise.h @@ -9,7 +9,7 @@ * * Copyright (c) 2010-2015, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> - * Copyright (c) 2015-2020, Kaz Kylheku <kaz at kylheku dot com> + * Copyright (c) 2015-2024, Kaz Kylheku <kaz at kylheku dot com> * * All rights reserved. * @@ -92,6 +92,8 @@ typedef struct lino_os { wide_disp \ } +#if CONFIG_FULL_REPL + typedef struct lino_completions { size_t len; wchar_t **cvec; @@ -102,6 +104,8 @@ typedef void lino_compl_cb_t(const wchar_t *, lino_completions_t *, void *ctx); void lino_set_completion_cb(lino_t *, lino_compl_cb_t *, void *ctx); void lino_add_completion(lino_completions_t *, const wchar_t *); +#endif + void lino_init(lino_os_t *); lino_t *lino_make(mem_t *istream, mem_t *ostream); lino_t *lino_copy(lino_t *); @@ -116,6 +120,7 @@ int lino_hist_set_max_len(lino_t *, int len); int lino_hist_save(lino_t *, const wchar_t *filename, int new_only); int lino_hist_load(lino_t *, const wchar_t *filename); int lino_have_new_lines(lino_t *); +#if HAVE_TERMIOS void lino_set_result(lino_t *, wchar_t *); /* takes ownership of malloced mem; modifies it */ int lino_clear_screen(lino_t *); void lino_set_multiline(lino_t *, int ml); @@ -124,9 +129,13 @@ void lino_set_selinclusive(lino_t *, int si); int lino_get_selinculsive(lino_t *); void lino_set_noninteractive(lino_t *, int ni); int lino_get_noninteractive(lino_t *); +#endif +void lino_enable_noninteractive_prompt(lino_t *, int enable); +#if HAVE_TERMIOS typedef wchar_t *lino_atom_cb_t(lino_t *, const wchar_t *line, int n, void *ctx); void lino_set_atom_cb(lino_t *, lino_atom_cb_t *, void *ctx); +#endif typedef int lino_enter_cb_t(const wchar_t *line, void *ctx); void lino_set_enter_cb(lino_t *, lino_enter_cb_t *, void *ctx); |