diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2015-09-22 21:59:33 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2015-09-22 21:59:33 -0700 |
commit | eed5dd5b038d947f8288111503c6bf48e7e30f17 (patch) | |
tree | 698a95256f39b7b0e624553f281fbf6c643d29e0 /linenoise | |
parent | 78e12d9c43b606f7402100a7c3b3367057d103d9 (diff) | |
download | txr-eed5dd5b038d947f8288111503c6bf48e7e30f17.tar.gz txr-eed5dd5b038d947f8288111503c6bf48e7e30f17.tar.bz2 txr-eed5dd5b038d947f8288111503c6bf48e7e30f17.zip |
linenoise: switch to wide characters, support Unicode.
* lib.c (chk_wrealloc): New function.
* lib.h (mem_t): Wrap with ifndef block.
(MEM_T_DEFINED): New preprocessor symbol.
(chk_wrealloc): Declared.
* linenoise/linenoise.c (LINENOISE_MAX_DISP): Adjust to a
reasonable value; just twice the number of abstract
characters. The 8 factor had been chosen to cover the worst
case that every character is mapped to a tab.
(struct lino_state): Almost everything char typed turns to
wchar_t. The TTY isn't referenced with Unix file descriptors,
ifd and ofd, but abstract stream handles tty_ifs and tty_ofs.
The ifs member isn't required any more since plain mode is
handled via the tty_ifs stream.
(mem_t): Declaration removed; now in linenoise.h.
(chk_malloc, chk_realloc, chk_strdup_utf8): Declarations
removed.
(lino_os): New static structure.
(nelem): New macro.
(wcsnprintf): New static function.
(enable_raw_mode, disable_raw_mode): Get Unix FD from stream
using lino_os interface.
(get_cursor_position, get_columns, handle_resize, record_undo,
remove_noop_undo, restore_undo, undo_renumber_hist_idx,
compare_completions, complete_line, lino_add_completion,
next_hist_match, history_search, show_help,
struct abuf, ab_append, ab_free, sync_data_to_buf,
refresh_singleline, screen_rows, col_offset_in_str,
refresh_multiline, scan_match_rev, scan_match_fwd, scan_fwd,
find_nearest_paren, usec_delay, flash, yank_sel, delete_sel,
edit_insert, edit_insert_str, edit_move_eol,
edit_history_next, edit_delete, edit_backspace,
edit_delete_prev_all, edit_delete_to_eol, edit_delete_line,
edit_in_editor, edit, linenoise, lino_make, lino_cleanup.
lino_free, free_hist, lino_hist_add, lino_hist_save,
lino_set_result): Revised using streams, wide chars and
lino_os interface.
(lino_init): New function.
* linenoise/linenoise.h (LINO_PAD_CHAR): New preprocessor
symbol.
(mem_t): Defined here.
(MEM_T_DEFINED): New preprocessor symbol.
(struct lino_os, lino_os_t): New structure.
(lino_os_init): New macro.
(struct lino_completions, lino_compl_cb_t, lino_atom_cb_t,
lino_enter_cb_t): Switch to wchar_t.
(lino_init): New function.
(lino_add_completion, lino_make, linenoise, lino_hist_add,
lino_hist_save, lino_hist_load, lino_set_result)
* parser.c (find_matching_syms, provide_completions,
provide_atom, is_balanced_line, repl): Adapt to
wide character linenoise.
(lino_fileno, lino_puts, lino_getch, lino_getl, lino_gets,
lino_feof, lino_open, lino_open8, lino_fdopen, lino_close):
New static functions.
(linenoise_txr_binding): New static structure.
(parse_init): Call lino_init, passing OS binding.
* txr.1: Update text about the listener's limitations.
Diffstat (limited to 'linenoise')
-rw-r--r-- | linenoise/linenoise.c | 713 | ||||
-rw-r--r-- | linenoise/linenoise.h | 63 |
2 files changed, 418 insertions, 358 deletions
diff --git a/linenoise/linenoise.c b/linenoise/linenoise.c index 09bc4c10..005cdb1d 100644 --- a/linenoise/linenoise.c +++ b/linenoise/linenoise.c @@ -44,16 +44,19 @@ #include <termios.h> #include <unistd.h> #include <stddef.h> +#include <wchar.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #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 "config.h" #if HAVE_POLL #include <poll.h> @@ -74,7 +77,7 @@ #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 1024 -#define LINENOISE_MAX_DISP (LINENOISE_MAX_LINE * 8) +#define LINENOISE_MAX_DISP (LINENOISE_MAX_LINE * 2) #define LINENOISE_PAREN_DELAY 400000 #define LINENOISE_FLASH_DELAY 200000 #define LINENOISE_MAX_UNDO 200 @@ -97,19 +100,18 @@ struct lino_state { int mlmode; /* Multi line mode. Default is single line. */ int history_max_len; int history_len; - char **history; - char *clip; /* Selection */ - char *result; /* Previous command result. */ - int ifd; /* Terminal stdin file descriptor. */ - int ofd; /* Terminal stdout file descriptor. */ + wchar_t **history; + wchar_t *clip; /* Selection */ + wchar_t *result; /* Previous command result. */ + 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 */ - FILE *ifs; /* Input stream, used for non-tty mode */ /* Volatile state pertaining to just one linenoise call */ - char buf[LINENOISE_MAX_DISP]; /* Displayed line bufer. */ - char data[LINENOISE_MAX_LINE]; /* True data corresponding to display */ - const char *prompt; /* Prompt to display. */ - const char *suffix; /* Suffix when creating temp file. */ + wchar_t buf[LINENOISE_MAX_DISP]; /* Displayed line bufer. */ + 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. */ int plen; /* Prompt length. */ int pos; /* Current cursor position. */ int sel; /* Selection start in terms of display. */ @@ -135,7 +137,7 @@ struct lino_state { struct lino_undo { struct lino_undo *next; int triv; - char *data; + wchar_t *data; int dpos; int hist_index; }; @@ -149,15 +151,25 @@ enum key_action { BACKSPACE = 127 }; -typedef unsigned char mem_t; -mem_t *chk_malloc(size_t n); -mem_t *chk_realloc(mem_t *old, size_t size); -char *chk_strdup_utf8(const char *str); - +static lino_os_t lino_os; static lino_t lino_list = { &lino_list, &lino_list }; volatile sig_atomic_t lino_list_busy; static int atexit_registered = 0; /* Register atexit just 1 time. */ +#define nelem(array) (sizeof (array) / sizeof (array)[0]) + +static int wcsnprintf(wchar_t *s, size_t nchar, const wchar_t *fmt, ...) +{ + int ret; + va_list vl; + va_start (vl, fmt); + ret = vswprintf(s, nchar, fmt, vl); + va_end (vl); + if (ret >= 0) + return ret; + return wcslen(s); +} + /* ======================= Low level terminal handling ====================== */ /* Set if to use or not the multi line mode. */ @@ -205,15 +217,18 @@ static void atexit_handler(void); /* Raw mode: 1960 magic shit. */ static int enable_raw_mode(lino_t *ls) { struct termios raw; + int ifd = lino_os.fileno_fn(ls->tty_ifs); - if (!isatty(ls->ifd)) goto fatal; + if (!isatty(ifd)) + goto fatal; if (!atexit_registered) { atexit(atexit_handler); atexit_registered = 1; } - if (tcgetattr(ls->ifd,&ls->orig_termios) == -1) goto fatal; + if (tcgetattr(ifd, &ls->orig_termios) == -1) + goto fatal; raw = ls->orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, @@ -230,7 +245,9 @@ static int enable_raw_mode(lino_t *ls) { raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ - if (tcsetattr(ls->ifd,TCSANOW,&raw) < 0) goto fatal; + if (tcsetattr(ifd, TCSANOW, &raw) < 0) + goto fatal; + ls->rawmode = 1; return 0; @@ -240,43 +257,53 @@ fatal: } static void disable_raw_mode(lino_t *ls) { + int ifd = lino_os.fileno_fn(ls->tty_ifs); + /* Don't even check the return value as it's too late. */ - if (ls->rawmode && tcsetattr(ls->ifd,TCSANOW,&ls->orig_termios) != -1) + if (ls->rawmode && tcsetattr(ifd, TCSANOW, &ls->orig_termios) != -1) ls->rawmode = 0; } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ -static int get_cursor_position(int ifd, int ofd) { - char buf[32]; +static int get_cursor_position(mem_t *ifs, mem_t *ofs) { + wchar_t buf[32]; int cols, rows; unsigned int i = 0; /* Report cursor location */ - if (write(ofd, "\x1b[6n", 4) != 4) return -1; + if (!lino_os.puts_fn(ofs, L"\x1b[6n")) + return -1; /* Read the response: ESC [ rows ; cols R */ - while (i < sizeof(buf)-1) { - if (read(ifd,buf+i,1) != 1) break; - if (buf[i] == 'R') break; + while (i < nelem(buf) - 1) { + wint_t ch = lino_os.getch_fn(ifs); + if (ch == WEOF) + break; + buf[i] = ch; + if (ch == 'R') + break; i++; } - buf[i] = '\0'; + buf[i] = 0; /* Parse it. */ - if (buf[0] != ESC || buf[1] != '[') return -1; - if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; + if (buf[0] != ESC || buf[1] != '[') + return -1; + if (swscanf(buf + 2, L"%d;%d", &rows, &cols) != 2) + return -1; return cols; } /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ -static int get_columns(int ifd, int ofd) { +static int get_columns(mem_t *ifs, mem_t *ofs) { + int ofd = lino_os.fileno_fn(ofs); #if HAVE_WINSIZE struct winsize ws; - if (ioctl(1, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) + if (ioctl(ofd, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) return ws.ws_col; #endif @@ -286,21 +313,20 @@ static int get_columns(int ifd, int ofd) { int start, cols; /* Get the initial position so we can restore it later. */ - start = get_cursor_position(ifd,ofd); + start = get_cursor_position(ifs, ofs); if (start == -1) goto failed; /* Go to right margin and get position. */ - if (write(ofd,"\x1b[999C",6) != 6) goto failed; - cols = get_cursor_position(ifd,ofd); + if (!lino_os.puts_fn(ofs, L"\x1b[999C")) + goto failed; + cols = get_cursor_position(ifs, ofs); if (cols == -1) goto failed; /* Restore position. */ if (cols > start) { - char seq[32]; - snprintf(seq,32,"\x1b[%dD",cols-start); - if (write(ofd,seq,strlen(seq)) == -1) { - /* Can't recover... */ - } + wchar_t seq[32]; + wcsnprintf(seq, nelem(seq), L"\x1b[%dD", cols - start); + (void) lino_os.puts_fn(ofs, seq); } return cols; } @@ -312,7 +338,7 @@ failed: /* Clear the screen. Used to handle ctrl+l */ int lino_clear_screen(lino_t *ls) { ls->maxrows = 0; - return (write(ls->ofd,"\x1b[H\x1b[2J",7) > 0); + return (lino_os.puts_fn(ls->tty_ofs, L"\x1b[H\x1b[2J") >= 0); } static void refresh_line(lino_t *l); @@ -323,7 +349,7 @@ static void handle_resize(lino_t *ls, lino_t *lc) ls->need_resize = 0; if (lc != 0) lc->need_resize = 0; - ls->cols = get_columns(ls->ifd, ls->ofd); + ls->cols = get_columns(ls->tty_ifs, ls->tty_ofs); if (lc) lc->cols = ls->cols; refresh_line(ls); @@ -333,7 +359,7 @@ static void handle_resize(lino_t *ls, lino_t *lc) /* Beep, used for completion when there is nothing to complete or when all * the choices were already shown. */ static int generate_beep(lino_t *ls) { - return write(ls->ofd, "\x7", 1) > 0; + return lino_os.puts_fn(ls->tty_ofs, L"\x7"); } static void delete_undo(struct lino_undo **pundo) @@ -342,9 +368,9 @@ static void delete_undo(struct lino_undo **pundo) if (u) { *pundo = u->next; - free(u->data); + lino_os.free_fn(u->data); u->data = 0; - free(u); + lino_os.free_fn(u); } } @@ -356,15 +382,13 @@ static void free_undo_stack(lino_t *l) static void record_undo(lino_t *l) { - struct lino_undo *rec = coerce(struct lino_undo *, - chk_malloc(sizeof *rec)); - struct lino_undo *iter; - char *data = coerce(char *, chk_strdup_utf8(l->data)); + 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); int count; if (rec == 0 || data == 0) { - free(rec); - free(data); + lino_os.free_fn(rec); + lino_os.free_fn(data); return; } @@ -406,7 +430,7 @@ static void remove_noop_undo(lino_t *l) if (top != 0 && (top->hist_index == INT_MAX || top->hist_index == l->history_index) && - strcmp(l->data, top->data) == 0) + wcscmp(l->data, top->data) == 0) { delete_undo(&l->undo_stack); } @@ -421,18 +445,18 @@ static void restore_undo(lino_t *l) int hidx = top->hist_index; if (hidx == INT_MAX || hidx == l->history_index) { - int dlen = strlen(top->data); + int dlen = wcslen(top->data); if (dlen) { - strcpy(l->data, top->data); + wcscpy(l->data, top->data); l->dlen = dlen; l->dpos = top->dpos; l->need_refresh = 1; if (hidx == l->history_index) { int history_pos = l->history_len - 1 - l->history_index; - free(l->history[history_pos]); - l->history[history_pos] = chk_strdup_utf8(l->data); + lino_os.free_fn(l->history[history_pos]); + l->history[history_pos] = lino_os.wstrdup_fn(l->data); } delete_undo(ptop); break; @@ -470,27 +494,27 @@ static void undo_renumber_hist_idx(lino_t *l, int delta) static void free_completions(lino_completions_t *lc) { size_t i; for (i = 0; i < lc->len; i++) - free(lc->cvec[i]); + lino_os.free_fn(lc->cvec[i]); if (lc->cvec != NULL) - free(lc->cvec); + lino_os.free_fn(lc->cvec); } static void sync_data_to_buf(lino_t *l); static int compare_completions(const void *larg, const void *rarg) { - const char * const *lelem = convert(const char * const *, larg); - const char * const *relem = convert(const char * const *, rarg); - const char *lstr = *lelem, *rstr = *relem; - size_t llen = strlen(lstr); - size_t rlen = strlen(rstr); + const wchar_t * const *lelem = convert(const wchar_t * const *, larg); + const wchar_t * const *relem = convert(const wchar_t * const *, rarg); + const wchar_t *lstr = *lelem, *rstr = *relem; + size_t llen = wcslen(lstr); + size_t rlen = wcslen(rstr); if (llen < rlen) return -1; if (llen > rlen) return 1; - return strcmp(lstr, rstr); + return wcscmp(lstr, rstr); } /* This is an helper function for edit() and is called when the @@ -501,9 +525,8 @@ static int compare_completions(const void *larg, const void *rarg) * structure as described in the structure definition. */ static int complete_line(lino_t *ls, int substring) { lino_completions_t lc = { 0, NULL, 0 }; - int nread; - char c = 0; - char save = ls->data[ls->dpos]; + wint_t c = 0; + wchar_t save = ls->data[ls->dpos]; lino_t *lt = lino_copy(ls); lc.substring = substring; @@ -526,26 +549,26 @@ static int complete_line(lino_t *ls, int substring) { while(!stop) { /* Show completion or original buffer */ if (i < lc.len) { - int n = snprintf(lt->data, sizeof lt->data, - "%s%s", lc.cvec[i], ls->data + ls->dpos); + int n = wcsnprintf(lt->data, nelem(lt->data), + L"%ls%ls", lc.cvec[i], ls->data + ls->dpos); lt->dlen = n; - lt->dpos = strlen(lc.cvec[i]); + lt->dpos = wcslen(lc.cvec[i]); sync_data_to_buf(lt); refresh_line(lt); } else { refresh_line(ls); } - nread = read(ls->ifd,&c,1); + c = lino_os.getch_fn(ls->tty_ifs); - if (nread <= 0) { + if (c < 0) { if (errno == EINTR) { handle_resize(ls, lt); continue; } free_completions(&lc); - free(lt); - ls->error = (nread < 0 ? lino_ioerr : lino_eof); + lino_os.free_fn(lt); + ls->error = (lino_os.eof_fn(ls->tty_ifs) ? lino_eof : lino_ioerr); return -1; } @@ -565,7 +588,7 @@ static int complete_line(lino_t *ls, int substring) { if (i < lc.len) { ls->dpos = lt->dpos; ls->dlen = lt->dlen; - strcpy(ls->data, lt->data); + wcscpy(ls->data, lt->data); sync_data_to_buf(ls); refresh_line(ls); } @@ -590,24 +613,24 @@ void lino_set_completion_cb(lino_t *ls, lino_compl_cb_t *fn, void *ctx) { * in order to add completion options given the input string when the * user typed <tab>. See the example.c source code for a very easy to * understand example. */ -void lino_add_completion(lino_completions_t *lc, const char *str) { - size_t len = strlen(str); - char *copy, **cvec; +void lino_add_completion(lino_completions_t *lc, const wchar_t *str) { + size_t len = wcslen(str); + wchar_t *copy, **cvec; - copy = coerce(char *, chk_malloc(len+1)); + copy = coerce(wchar_t *, lino_os.wmalloc_fn(len + 1)); if (copy == NULL) return; - memcpy(copy,str,len+1); - cvec = coerce(char **, chk_realloc(coerce(mem_t *, lc->cvec), - (lc->len+1) * sizeof *cvec)); + wmemcpy(copy, str, len + 1); + cvec = coerce(wchar_t **, lino_os.realloc_fn(coerce(mem_t *, lc->cvec), + (lc->len+1) * sizeof *cvec)); if (cvec == NULL) { - free(copy); + lino_os.free_fn(copy); return; } lc->cvec = cvec; lc->cvec[lc->len++] = copy; } -static int next_hist_match(lino_t *l, char *pat, int cur, int *offs) +static int next_hist_match(lino_t *l, wchar_t *pat, int cur, int *offs) { int i; @@ -615,8 +638,8 @@ static int next_hist_match(lino_t *l, char *pat, int cur, int *offs) cur = l->history_len - 1; for (i = cur; i >= 0; i--) { - char *hline = l->history[i]; - char *pmatch = strstr(hline, pat); + wchar_t *hline = l->history[i]; + wchar_t *pmatch = wcsstr(hline, pat); if (pmatch != 0) { *offs = pmatch - hline; return i; @@ -629,23 +652,23 @@ static void copy_display_params(lino_t *, const lino_t *); static int history_search(lino_t *l) { - char hpat[128] = ""; + wchar_t hpat[128] = L""; int hi = l->history_len - l->history_index - 2 + (l->history_index == 0); int hp = hi, hl = 0, stop = 0; int dp = l->dpos; - const char *fmt = "[%s]%s"; - int ex = strlen(fmt) - 2*strlen("%s"); + const wchar_t *fmt = L"[%ls]%ls"; + int ex = wcslen(fmt) - 2*wcslen(L"%ls"); lino_t *lc = lino_copy(l), *ld = lino_copy(l); int c = -1; if (lc == 0 || ld == 0) goto out; - lc->prompt = "search:"; + lc->prompt = L"search:"; while (!stop) { - int nw = snprintf(lc->data, sizeof lc->data, fmt, hpat, l->data); int vb = 0; + int nw = wcsnprintf(lc->data, nelem(lc->data), fmt, hpat, l->data); lc->dlen = nw; lc->dpos = dp + hl + ex; @@ -655,19 +678,15 @@ static int history_search(lino_t *l) refresh_line(lc); for (;;) { - unsigned char byte; - int nread = read(lc->ifd, &byte, 1); + c = lino_os.getch_fn(lc->tty_ifs); - if (nread <= 0) { + if (c < 0) { if (errno == EINTR) { handle_resize(lc, l); continue; } - c = nread; stop = 1; } else { - c = byte; - if (vb) goto verbatim; @@ -676,7 +695,7 @@ static int history_search(lino_t *l) if (c < 32) continue; verbatim: - if (hl >= convert(int, sizeof hpat)) + if (hl >= convert(int, nelem(hpat))) break; hpat[hl++] = c; /* fallthrough */ @@ -697,13 +716,13 @@ static int history_search(lino_t *l) sp = ni - 1; } while (ni >= 0 && hi < l->history_len && ni != hi && - strcmp(l->history[hi], l->history[ni]) == 0); + wcscmp(l->history[hi], l->history[ni]) == 0); if (ni < 0) break; hi = ni; - strcpy(l->data, l->history[hi]); - l->dpos = l->dlen = strlen(l->data); + wcscpy(l->data, l->history[hi]); + l->dpos = l->dlen = wcslen(l->data); } break; case BACKSPACE: case CTL('H'): @@ -716,7 +735,7 @@ static int history_search(lino_t *l) stop = 1; break; case CTL('C'): - strcpy(l->data, ld->data); + wcscpy(l->data, ld->data); l->dpos = ld->dpos; l->dlen = ld->dlen; stop = 1; @@ -760,33 +779,33 @@ out: static void show_help(lino_t *l) { lino_t *lc = lino_copy(l); - unsigned char byte; - int nread, i; - static const char *help[] = { - "^B left ^A start buf/ln ^T char swap ^U del ln beg ^R hist srch\r" - "^F forward ^E end buf/ln ^D del right ^K del ln end Tab complete\r" - "^5 parmatch ^] parmatch ^W del word left ^V next ch verbatim [p. 1/3]", - - "^L refresh ^P hist prev ^S select ^Q paste ^J multi ln toggle\r" - "^C cancel ^N hist next ^^ sel endpt swp ^D sel cut\r" - "^Z suspend ^O undo ^Y yank ^W sel + word cut [p. 2/3]", - - "^X^V verbatim ins mode ^X^A ins prev ln atom ^X^E extrn editor\r" - "^X^R ins prev ln ^X+Enter submit; keep hist pos ^X^Q exch clip/sel\r" - "^X^W ins prev ln word ^X+Tab substring complete [p. 3/3]" + int i; + static const wchar_t *help[] = { + L"^B left ^A start buf/ln ^T char swap ^U del ln beg ^R hist srch\r" + L"^F forward ^E end buf/ln ^D del right ^K del ln end Tab complete\r" + L"^5 parmatch ^] parmatch ^W del word left ^V next ch verbatim [p. 1/3]", + + L"^L refresh ^P hist prev ^S select ^Q paste ^J multi ln toggle\r" + L"^C cancel ^N hist next ^^ sel endpt swp ^D sel cut\r" + L"^Z suspend ^O undo ^Y yank ^W sel + word cut [p. 2/3]", + + L"^X^V verbatim ins mode ^X^A ins prev ln atom ^X^E extrn editor\r" + L"^X^R ins prev ln ^X+Enter submit; keep hist pos ^X^Q exch clip/sel\r" + L"^X^W ins prev ln word ^X+Tab substring complete [p. 3/3]" }; lc->mlmode = 1; - lc->prompt = ""; + lc->prompt = L""; for (i = 0; i < 3; i++) { - unsigned char seq[3]; - lc->dlen = snprintf(lc->data, sizeof lc->data, "%s", help[i]); + wint_t c; + wint_t seq[3]; + lc->dlen = wcsnprintf(lc->data, nelem(lc->data), L"%ls", help[i]); lc->dpos = lc->dlen; lc->need_refresh = 1; refresh_line(lc); - nread = read(l->ifd, &byte, 1); - if (byte == CTL('C')) + c = lino_os.getch_fn(l->tty_ifs); + if (c == CTL('C')) break; - switch (byte) { + switch (c) { case CTL('C'): break; back: @@ -797,12 +816,15 @@ static void show_help(lino_t *l) break; continue; case ESC: - if (read(l->ifd,seq,1) == -1) break; - if (read(l->ifd,seq+1,1) == -1) break; + if ((seq[0] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; + if ((seq[1] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { - if (read(l->ifd,seq+2,1) == -1) break; + if ((seq[2] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ @@ -862,7 +884,6 @@ static void show_help(lino_t *l) lc->need_refresh = 1; refresh_line(lc); lino_free(lc); - (void) nread; } /* =========================== Line editing ================================= */ @@ -872,7 +893,7 @@ static void show_help(lino_t *l) * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { - char *b; + wchar_t *b; int len; }; @@ -881,37 +902,38 @@ static void ab_init(struct abuf *ab) { ab->len = 0; } -static void ab_append(struct abuf *ab, const char *s, int len) { - char *ns = coerce(char *, - chk_realloc(coerce(mem_t *, ab->b), ab->len+len)); +static void ab_append(struct abuf *ab, + const wchar_t *s, int len) { + wchar_t *ns = lino_os.wrealloc_fn(ab->b, ab->len + len + 1); if (ns == NULL) return; - memcpy(ns+ab->len,s,len); + wmemcpy(ns+ab->len, s, len); ab->b = ns; ab->len += len; + ab->b[ab->len] = 0; } static void ab_free(struct abuf *ab) { - free(ab->b); + lino_os.free_fn(ab->b); } /* Convert raw data to display data, and recalculate display length and position. */ static void sync_data_to_buf(lino_t *l) { - char *dptr = l->data, *bptr = l->buf; - int col = strlen(l->prompt); + wchar_t *dptr = l->data, *bptr = l->buf; + int col = wcslen(l->prompt); int rev = l->dsel > l->dend; if (l->mlmode) { - bptr += snprintf(l->buf, sizeof l->buf, "%s", - l->prompt); + bptr += wcsnprintf(l->buf, nelem(l->buf), L"%ls", + l->prompt); } - while (bptr - l->buf < convert(ptrdiff_t, sizeof l->buf) - 1) { + while (bptr - l->buf < convert(ptrdiff_t, nelem (l->buf)) - 1) { int dpos = dptr - l->data; int pos = bptr - l->buf; - char ch = *dptr++; + wchar_t ch = *dptr++; if (l->dpos == dpos) l->pos = pos; @@ -940,9 +962,16 @@ static void sync_data_to_buf(lino_t *l) *bptr++ = '^'; *bptr++ = '?'; col += 2; + } else if (ch < 256) { + *bptr++ = ch; + col++; } else { *bptr++ = ch; col++; + if (lino_os.wide_display_fn(ch)) { + *bptr++ = LINO_PAD_CHAR; + col++; + } } continue; @@ -967,10 +996,9 @@ static void copy_display_params(lino_t *to, const lino_t *from) * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refresh_singleline(lino_t *l) { - char seq[64]; - int plen = strlen(l->prompt); - int fd = l->ofd; - char *buf = l->buf; + wchar_t seq[64]; + int plen = wcslen(l->prompt); + wchar_t *buf = l->buf; int len = l->len; int pos = l->pos; struct abuf ab; @@ -1002,31 +1030,31 @@ static void refresh_singleline(lino_t *l) { ab_init(&ab); /* Cursor to left edge */ - snprintf(seq,64,"\r"); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\r"); + ab_append(&ab, seq, wcslen(seq)); /* Write the prompt and the current buffer content */ - ab_append(&ab,l->prompt,strlen(l->prompt)); + ab_append(&ab, l->prompt, wcslen(l->prompt)); if (!l->selmode) { ab_append(&ab, buf, len); } else { if (sel > 0) ab_append(&ab, buf, sel); if (end - sel > 0) { - ab_append(&ab, "\x1b[7m", 4); + ab_append(&ab, L"\x1b[7m", 4); ab_append(&ab, buf + sel, end - sel); - ab_append(&ab, "\x1b[m", 3); + ab_append(&ab, L"\x1b[m", 3); } if (len - end > 0) ab_append(&ab, buf + end, len - end); } /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\x1b[0K"); + ab_append(&ab, seq, wcslen(seq)); /* Move cursor to original position. */ - snprintf(seq,64,"\r\x1b[%dC", pos + plen); - ab_append(&ab,seq,strlen(seq)); - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + wcsnprintf(seq, 64, L"\r\x1b[%dC", pos + plen); + ab_append(&ab, seq, wcslen(seq)); + (void) lino_os.puts_fn(l->tty_ofs, ab.b); ab_free(&ab); } @@ -1034,14 +1062,14 @@ struct row_values { int rows[2]; }; -static struct row_values screen_rows(const char *str, int pos, int cols) +static struct row_values screen_rows(const wchar_t *str, int pos, int cols) { - const char *start = str; + const wchar_t *start = str; int col; struct row_values out = { { 1, 1 } }; for (col = 0; ; str++) { - int ch = *str; + wchar_t ch = *str; int atpos = (str - start == convert(ptrdiff_t, pos)); switch (ch) { @@ -1071,7 +1099,7 @@ static struct row_values screen_rows(const char *str, int pos, int cols) return out; } -static int col_offset_in_str(const char *str, int pos) +static int col_offset_in_str(const wchar_t *str, int pos) { int offs = 0; while (pos > 0 && str[--pos] != '\n') @@ -1084,12 +1112,12 @@ static int col_offset_in_str(const char *str, int pos) * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refresh_multiline(lino_t *l) { - char seq[64]; + wchar_t seq[64]; struct row_values r = screen_rows(l->buf, l->pos, l->cols); int rows = r.rows[0]; /* rows used by current buf. */ int nrow = r.rows[1]; /* cursor row after refresh. */ int oldmaxrows = l->maxrows; - int fd = l->ofd, j; + int j; struct abuf ab; /* Update maxrows if needed. */ @@ -1100,19 +1128,19 @@ static void refresh_multiline(lino_t *l) { * going to the last row. */ ab_init(&ab); if (oldmaxrows - l->oldrow > 0) { - snprintf(seq,64,"\x1b[%dB", oldmaxrows - l->oldrow); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\x1b[%dB", oldmaxrows - l->oldrow); + ab_append(&ab, seq, wcslen(seq)); } /* Now for every row clear it, go up. */ for (j = 0; j < oldmaxrows - 1; j++) { - snprintf(seq,64,"\r\x1b[0K\x1b[1A"); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\r\x1b[0K\x1b[1A"); + ab_append(&ab, seq, wcslen(seq)); } /* Clean the top line. */ - snprintf(seq,64,"\r\x1b[0K"); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\r\x1b[0K"); + ab_append(&ab, seq, wcslen(seq)); /* Write the current buffer content which includes the prompt */ if (!l->selmode) { @@ -1131,9 +1159,9 @@ static void refresh_multiline(lino_t *l) { if (sel > 0) ab_append(&ab, l->buf, sel); if (end - sel > 0) { - ab_append(&ab, "\x1b[7m", 4); + ab_append(&ab, L"\x1b[7m", 4); ab_append(&ab, l->buf + sel, end - sel); - ab_append(&ab, "\x1b[m", 3); + ab_append(&ab, L"\x1b[m", 3); } if (len - end > 0) ab_append(&ab, l->buf + end, len - end); @@ -1141,7 +1169,7 @@ static void refresh_multiline(lino_t *l) { /* Cursor has wrapped to new row beyond rows. */ if (nrow > rows) { - ab_append(&ab, "\r\n", 2); + ab_append(&ab, L"\r\n", 2); rows++; if (rows > l->maxrows) l->maxrows = rows; @@ -1153,23 +1181,23 @@ static void refresh_multiline(lino_t *l) { /* Go up till we reach the expected positon. */ if (rows - nrow > 0) { - snprintf(seq,64,"\x1b[%dA", rows - nrow); - ab_append(&ab,seq,strlen(seq)); + wcsnprintf(seq, 64, L"\x1b[%dA", rows - nrow); + ab_append(&ab, seq, wcslen(seq)); } /* Set column. */ if (ccol) - snprintf(seq,64,"\r\x1b[%dC", ccol); + wcsnprintf(seq, 64, L"\r\x1b[%dC", ccol); else - snprintf(seq,64,"\r"); + wcsnprintf(seq, 64, L"\r"); - ab_append(&ab,seq,strlen(seq)); + ab_append(&ab, seq, wcslen(seq)); } /* Remember current cursor row for next time */ l->oldrow = nrow; - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + (void) lino_os.puts_fn(l->tty_ofs, ab.b); ab_free(&ab); } @@ -1185,7 +1213,7 @@ static void refresh_line(lino_t *ls) { refresh_singleline(ls); } -static int scan_match_rev(const char *s, int i, int mch) +static int scan_match_rev(const wchar_t *s, int i, wchar_t mch) { while (i > 0) { int ch = s[--i]; @@ -1216,7 +1244,7 @@ static int scan_match_rev(const char *s, int i, int mch) return -1; } -static int scan_rev(const char *s, int i) +static int scan_rev(const wchar_t *s, int i) { switch (s[i]) { case ')': @@ -1230,7 +1258,7 @@ static int scan_rev(const char *s, int i) } } -static int scan_match_fwd(const char *s, int i, int mch) +static int scan_match_fwd(const wchar_t *s, int i, wchar_t mch) { while (s[++i]) { int ch = s[i]; @@ -1261,7 +1289,7 @@ static int scan_match_fwd(const char *s, int i, int mch) return -1; } -static int scan_fwd(const char *s, int i) +static int scan_fwd(const wchar_t *s, int i) { switch (s[i]) { case '(': @@ -1275,21 +1303,21 @@ static int scan_fwd(const char *s, int i) } } -static int find_nearest_paren(const char *s, int i) +static int find_nearest_paren(const wchar_t *s, int i) { - static const char *ope = "([{"; - static const char *clo = ")]}"; + static const wchar_t *ope = L"([{"; + static const wchar_t *clo = L")]}"; int pre = -1, nxt = -1, j; for (j = i; j != -1; j--) { - if (s[j] && (strchr(ope, s[j]) || strchr(clo, s[j]))) { + if (s[j] && (wcschr(ope, s[j]) || wcschr(clo, s[j]))) { pre = j; break; } } for (j = i; s[j] != 0; j++) { - if (strchr(ope, s[j]) || strchr(clo, s[j])) { + if (wcschr(ope, s[j]) || wcschr(clo, s[j])) { nxt = j; break; } @@ -1307,10 +1335,10 @@ static int find_nearest_paren(const char *s, int i) if (i - pre < nxt - i) return pre; - if (strchr(ope, s[pre]) && strchr(ope, s[nxt])) + if (wcschr(ope, s[pre]) && wcschr(ope, s[nxt])) return nxt; - if (strchr(clo, s[pre]) && strchr(clo, s[nxt])) + if (wcschr(clo, s[pre]) && wcschr(clo, s[nxt])) return pre; return nxt; @@ -1320,7 +1348,7 @@ static void usec_delay(lino_t *l, long usec) { #if HAVE_POLL struct pollfd pfd; - pfd.fd = l->ifd; + pfd.fd = lino_os.fileno_fn(l->tty_ifs); pfd.events = POLLIN; poll(&pfd, 1, usec/1000); #elif HAVE_POSIX_NANOSLEEP @@ -1360,7 +1388,7 @@ static void flash(lino_t *l, int ch) { int i; - if (l->dlen >= (int) sizeof l->data - 1) + if (l->dlen >= (int) nelem (l->data) - 1) return; for (i = 0; i < 2; i++) { @@ -1405,9 +1433,9 @@ static void yank_sel(lino_t *l) end++; if (end - sel > 0) { - free(l->clip); - l->clip = coerce(char *, chk_malloc(end - sel + 1)); - memcpy(l->clip, l->data + sel, end - sel); + lino_os.free_fn(l->clip); + l->clip = coerce(wchar_t *, lino_os.alloc_fn(end - sel + 1)); + wmemcpy(l->clip, l->data + sel, end - sel); l->clip[end - sel] = 0; l->dpos = sel; } @@ -1426,7 +1454,7 @@ static void delete_sel(lino_t *l) end++; if (len - end > 0) - memmove(l->data + sel, l->data + end, len - end); + wmemmove(l->data + sel, l->data + end, len - end); len -= (end - sel); l->data[len] = 0; @@ -1441,8 +1469,8 @@ static void delete_sel(lino_t *l) /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ -static int edit_insert(lino_t *l, char c) { - if (l->dlen < (int) sizeof l->data - 1) { +static int edit_insert(lino_t *l, wchar_t c) { + if (l->dlen < (int) nelem(l->data) - 1) { record_triv_undo(l); delete_sel(l); if (l->dpos == l->dlen) { @@ -1450,15 +1478,17 @@ static int edit_insert(lino_t *l, char c) { l->dpos++; l->dlen++; l->data[l->dlen] = '\0'; - if ((!l->mlmode && l->len == l->dlen && l->plen+l->len < l->cols) /* || mlmode */) { + if ((!l->mlmode && l->len == l->dlen && l->plen+l->len < l->cols)) { /* Avoid a full update of the line in the * trivial case. */ - if (write(l->ofd,&c,1) == -1) return -1; + wchar_t str[2] = { c }; + if (!lino_os.puts_fn(l->tty_ofs, str)) + return -1; } else { l->need_refresh = 1; } } else { - memmove(l->data + l->dpos+1, l->data + l->dpos, l->dlen-l->dpos); + wmemmove(l->data + l->dpos+1, l->data + l->dpos, l->dlen-l->dpos); l->data[l->dpos] = c; l->dlen++; l->dpos++; @@ -1469,15 +1499,15 @@ static int edit_insert(lino_t *l, char c) { return 0; } -static int edit_insert_str(lino_t *l, const char *s, int nchar) +static int edit_insert_str(lino_t *l, const wchar_t *s, int nchar) { - if (l->dlen < (int) sizeof l->data - nchar) { + if (l->dlen < (int) nelem (l->data) - nchar) { record_undo(l); delete_sel(l); if (l->dpos < l->dlen) - memmove(l->data + l->dpos + nchar, l->data + l->dpos, l->dlen - l->dpos); - memcpy(l->data + l->dpos, s, nchar); + wmemmove(l->data + l->dpos + nchar, l->data + l->dpos, l->dlen - l->dpos); + wmemcpy(l->data + l->dpos, s, nchar); l->dpos += nchar; l->dlen += nchar; l->data[l->dlen] = 0; @@ -1543,7 +1573,7 @@ static void edit_move_eol(lino_t *l) { } else { int dpos = l->dpos; - dpos += strcspn(l->data + dpos, "\r"); + dpos += wcscspn(l->data + dpos, L"\r"); if (l->dpos != dpos) { l->dpos = dpos; @@ -1586,8 +1616,8 @@ static void edit_history_next(lino_t *l, int dir) { if (l->history_len > 1) { /* Update the current history entry before to * overwrite it with the next one. */ - free(l->history[l->history_len - 1 - l->history_index]); - l->history[l->history_len - 1 - l->history_index] = chk_strdup_utf8(l->data); + lino_os.free_fn(l->history[l->history_len - 1 - l->history_index]); + l->history[l->history_len - 1 - l->history_index] = lino_os.wstrdup_fn(l->data); /* Show the new entry */ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; if (l->history_index < 0) { @@ -1597,9 +1627,9 @@ static void edit_history_next(lino_t *l, int dir) { l->history_index = l->history_len-1; return; } - strncpy(l->data,l->history[l->history_len - 1 - l->history_index], sizeof l->data); - l->data[sizeof l->data - 1] = 0; - l->dpos = l->dlen = strlen(l->data); + wcsncpy(l->data, l->history[l->history_len - 1 - l->history_index], nelem(l->data)); + l->data[nelem(l->data) - 1] = 0; + l->dpos = l->dlen = wcslen(l->data); l->need_refresh = 1; } } @@ -1615,7 +1645,7 @@ static void edit_delete(lino_t *l) { if (l->dlen > 0 && l->dpos < l->dlen) { record_undo(l); - memmove(l->data + l->dpos, l->data + l->dpos + 1, l->dlen - l->dpos - 1); + wmemmove(l->data + l->dpos, l->data + l->dpos + 1, l->dlen - l->dpos - 1); l->dlen--; l->data[l->dlen] = '\0'; l->need_refresh = 1; @@ -1635,7 +1665,7 @@ static void edit_backspace(lino_t *l) { delete_sel(l); if (l->dpos > 0 && l->dlen > 0) { - memmove(l->data + l->dpos - 1, l->data + l->dpos, l->dlen - l->dpos); + wmemmove(l->data + l->dpos - 1, l->data + l->dpos, l->dlen - l->dpos); l->dpos--; l->dlen--; l->data[l->dlen] = '\0'; @@ -1653,13 +1683,13 @@ static void edit_delete_prev_all(lino_t *l) if (!l->mlmode) { if (l->dpos > 0) { record_undo(l); - memmove(l->data, l->data + l->dpos, l->dlen - l->dpos + 1); + wmemmove(l->data, l->data + l->dpos, l->dlen - l->dpos + 1); l->dlen -= l->dpos; l->dpos = 0; l->need_refresh = 1; } } else { - char *e = l->data + l->dpos, *s = e; + wchar_t *e = l->data + l->dpos, *s = e; int delta; while (s > l->data && s[-1] != '\r') @@ -1669,7 +1699,7 @@ static void edit_delete_prev_all(lino_t *l) if (delta > 0) { record_undo(l); - memmove(s, e, l->data + l->dlen - e); + wmemmove(s, e, l->data + l->dlen - e); l->dlen -= delta; l->dpos -= delta; l->data[l->dlen] = 0; @@ -1689,12 +1719,12 @@ static void edit_delete_to_eol(lino_t *l) l->dlen = l->dpos; l->need_refresh = 1; } else { - int delsize = strcspn(l->data + l->dpos, "\r"); + int delsize = wcscspn(l->data + l->dpos, L"\r"); if (delsize != 0) { record_undo(l); if (l->dlen - delsize) - memmove(l->data + l->dpos, l->data + l->dpos + delsize, - l->dlen - l->dpos - delsize); + wmemmove(l->data + l->dpos, l->data + l->dpos + delsize, + l->dlen - l->dpos - delsize); l->dlen -= delsize; l->data[l->dlen] = 0; l->need_refresh = 1; @@ -1707,19 +1737,19 @@ static void edit_delete_to_eol(lino_t *l) * current word. */ static void edit_delete_prev_word(lino_t *l) { int odpos, diff; - static const char *space = "\r\t "; + static const wchar_t *space = L"\r\t "; delete_sel(l); odpos = l->dpos; - while (l->dpos > 0 && strchr(space, l->data[l->dpos - 1])) + while (l->dpos > 0 && wcschr(space, l->data[l->dpos - 1])) l->dpos--; - while (l->dpos > 0 && strchr(space, l->data[l->dpos - 1]) == 0) + while (l->dpos > 0 && wcschr(space, l->data[l->dpos - 1]) == 0) l->dpos--; diff = odpos - l->dpos; if (diff != 0) { record_undo(l); - memmove(l->data + l->dpos, l->data + odpos, l->dlen - odpos + 1); + wmemmove(l->data + l->dpos, l->data + odpos, l->dlen - odpos + 1); l->dlen -= diff; l->need_refresh = 1; } @@ -1730,12 +1760,12 @@ static void edit_delete_line(lino_t *l) clear_sel(l); if (l->mlmode) { - char *e = l->data + l->dpos, *s = e; + wchar_t *e = l->data + l->dpos, *s = e; int delta; while (s > l->data && s[-1] != '\r') s--; - e += strcspn(e, "\r"); + e += wcscspn(e, L"\r"); if (*e == '\r') e++; @@ -1743,7 +1773,7 @@ static void edit_delete_line(lino_t *l) if (delta > 0) { record_undo(l); - memmove(s, e, l->data + l->dlen - e); + wmemmove(s, e, l->data + l->dlen - e); l->dlen -= delta; l->dpos = s - l->data; l->data[l->dlen] = 0; @@ -1752,7 +1782,7 @@ static void edit_delete_line(lino_t *l) } } -static void tr(char *s, int find, int rep) +static void tr(wchar_t *s, wchar_t find, wchar_t rep) { for (; *s; s++) if (*s == find) @@ -1775,7 +1805,7 @@ static const char *get_home(void) static void edit_in_editor(lino_t *l) { const char *templ = ".linotmpXXXXXX"; - FILE *fo = 0; + mem_t *fo = 0; char *ed = getenv("EDITOR"); char path[128]; @@ -1795,10 +1825,10 @@ static void edit_in_editor(lino_t *l) { #if HAVE_MKSTEMPS if ((fd = mkstemps(path, strlen(suffix))) != -1) - fo = fdopen(fd, "w"); + fo = lino_os.fdopen_fn(fd, L"w"); #else if ((fd = mkstemp(path)) != -1) - fo = fdopen(fd, "w"); + fo = lino_os.fdopen_fn(fd, L"w"); #endif if (!fo && fd != -1) @@ -1809,25 +1839,25 @@ static void edit_in_editor(lino_t *l) { char cmd[256]; snprintf(cmd, sizeof cmd, "%s %s", ed, path); tr(l->data, '\r', '\n'); - if (fputs(l->data, fo) != EOF && putc('\n', fo) != EOF && - fflush(fo) == 0) - { - FILE *fi; - int nread; - fclose(fo); + if (lino_os.puts_fn(fo, l->data) && lino_os.puts_fn(fo, L"\n")) + { + mem_t *fi; + lino_os.close_fn(fo); fo = 0; - if (system(cmd) == 0 && (fi = fopen(path, "r")) != 0) { - nread = fread(l->data, 1, sizeof l->data - 1, fi); - fclose(fi); + if (system(cmd) == 0 && (fi = lino_os.open8_fn(path, L"r")) != 0) { + size_t len; + (void) lino_os.gets_fn(fi, l->data, nelem(l->data)); + lino_os.close_fn(fi); + len = wcslen(l->data); record_undo(l); - l->data[nread] = 0; - if (nread > 0 && l->data[nread - 1] == '\n') - l->data[--nread] = 0; - l->dpos = l->dlen = nread; + l->data[len] = 0; + if (len > 0 && l->data[len - 1] == '\n') + l->data[--len] = 0; + l->dpos = l->dlen = len; tr(l->data, '\n', '\r'); l->need_refresh = 1; remove_noop_undo(l); @@ -1835,7 +1865,7 @@ static void edit_in_editor(lino_t *l) { } if (fo != 0) - fclose(fo); + lino_os.close_fn(fo); remove(path); clear_sel(l); @@ -1850,7 +1880,7 @@ static void edit_in_editor(lino_t *l) { * when ctrl+d is typed. * * The function returns the length of the current buffer. */ -static int edit(lino_t *l, const char *prompt) +static int edit(lino_t *l, const wchar_t *prompt) { int verbatim = 0, extended = 0, paste = 0, extend_num = -1; int ret = -1; @@ -1858,10 +1888,10 @@ static int edit(lino_t *l, const char *prompt) /* Populate the linenoise state that we pass to functions implementing * specific editing functionalities. */ l->prompt = prompt; - l->plen = strlen(prompt); + l->plen = wcslen(prompt); l->pos = l->len = 0; l->dpos = l->dlen = 0; - l->cols = get_columns(l->ifd, l->ofd); + l->cols = get_columns(l->tty_ifs, l->tty_ofs); l->oldrow = l->maxrows = 0; l->history_index = 0; clear_sel(l); @@ -1871,9 +1901,9 @@ static int edit(lino_t *l, const char *prompt) /* The latest history entry is always our current buffer, that * initially is just an empty string. */ - lino_hist_add(l, ""); + lino_hist_add(l, L""); - if (write(l->ofd,prompt,l->plen) == -1) { + if (!lino_os.puts_fn(l->tty_ofs, prompt)) { l->error = lino_ioerr; return ret; } @@ -1882,16 +1912,14 @@ static int edit(lino_t *l, const char *prompt) int hi = l->history_len - l->save_hist_idx - 1; l->history_index = l->save_hist_idx; l->save_hist_idx = 0; - strcpy(l->data, l->history[hi]); - l->dpos = l->dlen = strlen(l->data); + wcscpy(l->data, l->history[hi]); + l->dpos = l->dlen = wcslen(l->data); l->need_refresh = 1; } while(1) { - unsigned char byte; - int c; - int nread; - char seq[3]; + wint_t c; + wint_t seq[3]; update_sel(l); @@ -1900,20 +1928,18 @@ static int edit(lino_t *l, const char *prompt) refresh_line(l); } - nread = read(l->ifd,&byte,1); + c = lino_os.getch_fn(l->tty_ifs); - if (nread < 0 && errno == EINTR) { + if (c < 0 && errno == EINTR) { handle_resize(l, 0); continue; } - if (nread <= 0) { + if (c < 0) { ret = l->len ? l->len : -1; goto out; } - c = byte; - if (paste && (c == CTL('X'))) { paste = 0; continue; @@ -1947,21 +1973,20 @@ static int edit(lino_t *l, const char *prompt) case CTL('W'): case 'w': extended = 0; if (l->history_len > 1 + l->history_index && extend_num != 0) { - char *prev_line = l->history[l->history_len - 2 - - l->history_index]; - char *word_end = prev_line + strlen(prev_line); - char *word_start = word_end; + wchar_t *prev_line = l->history[l->history_len - 2 - l->history_index]; + wchar_t *word_end = prev_line + wcslen(prev_line); + wchar_t *word_start = word_end; if (extend_num < 0) extend_num = 1; for (; extend_num--; word_end = word_start) { - while (word_end > prev_line && isspace(convert(unsigned char, word_end[-1]))) + while (word_end > prev_line && iswspace(convert(wint_t, word_end[-1]))) word_end--; word_start = word_end; - while (word_start > prev_line && !isspace(convert(unsigned char, word_start[-1]))) + while (word_start > prev_line && !iswspace(convert(wint_t, word_start[-1]))) word_start--; if (extend_num == 0) @@ -1980,15 +2005,14 @@ static int edit(lino_t *l, const char *prompt) extend_num = 1; if (l->history_len > 1 + l->history_index && l->atom_callback) { - char *prev_line = l->history[l->history_len - 2 - - l->history_index]; - char *word = l->atom_callback(l, prev_line, - extend_num, l->ca_ctx); + wchar_t *prev_line = l->history[l->history_len - 2 + - l->history_index]; + wchar_t *word = l->atom_callback(l, prev_line, extend_num, l->ca_ctx); int res = 0; if (word != 0) { - res = edit_insert_str(l, word, strlen(word)); - free(word); + res = edit_insert_str(l, word, wcslen(word)); + lino_os.free_fn(word); } if (res) { @@ -2002,10 +2026,10 @@ static int edit(lino_t *l, const char *prompt) if (extend_num < 0) extend_num = 1; if (l->history_len > extend_num + l->history_index) { - char *prev_line = l->history[l->history_len - 1 - - extend_num - - l->history_index]; - int res = edit_insert_str(l, prev_line, strlen(prev_line)); + wchar_t *prev_line = l->history[l->history_len - 1 + - extend_num + - l->history_index]; + int res = edit_insert_str(l, prev_line, wcslen(prev_line)); if (res) { l->error = lino_ioerr; goto out; @@ -2015,7 +2039,7 @@ static int edit(lino_t *l, const char *prompt) case CTL('P'): case 'p': extended = 0; if (l->result) { - int res = edit_insert_str(l, l->result, strlen(l->result)); + int res = edit_insert_str(l, l->result, wcslen(l->result)); if (res) { l->error = lino_ioerr; goto out; @@ -2025,12 +2049,12 @@ static int edit(lino_t *l, const char *prompt) case CTL('Q'): extended = 0; { - char *clip = l->clip; + wchar_t *clip = l->clip; l->clip = 0; yank_sel(l); if (clip != 0) { - edit_insert_str(l, clip, strlen(clip)); - free(clip); + edit_insert_str(l, clip, wcslen(clip)); + lino_os.free_fn(clip); } clear_sel(l); } @@ -2172,14 +2196,17 @@ static int edit(lino_t *l, const char *prompt) /* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */ - if (read(l->ifd,seq,1) == -1) break; - if (read(l->ifd,seq+1,1) == -1) break; + if ((seq[0] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; + if ((seq[1] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; /* ESC [ sequences. */ if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ - if (read(l->ifd,seq+2,1) == -1) break; + if ((seq[2] = lino_os.getch_fn(l->tty_ifs)) < 0) + break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ @@ -2270,7 +2297,7 @@ static int edit(lino_t *l, const char *prompt) break; case CTL('Q'): if (l->clip != 0) - edit_insert_str(l, l->clip, strlen(l->clip)); + edit_insert_str(l, l->clip, wcslen(l->clip)); break; case CTL('K'): edit_delete_to_eol(l); @@ -2293,8 +2320,8 @@ static int edit(lino_t *l, const char *prompt) break; case CTL('J'): if (l->mlmode) { - snprintf(l->buf, sizeof l->buf, "%s", l->prompt); - l->pos = l->len = strlen(l->buf); + wcsnprintf(l->buf, nelem(l->buf), L"%ls", l->prompt); + l->pos = l->len = wcslen(l->buf); refresh_multiline(l); } @@ -2324,7 +2351,7 @@ static int edit(lino_t *l, const char *prompt) out: if (l->history_len > 0) { l->history_len--; - free(l->history[l->history_len]); + lino_os.free_fn(l->history[l->history_len]); l->history[l->history_len] = 0; undo_subst_hist_idx(l, INT_MAX, 0); undo_renumber_hist_idx(l, -1); @@ -2350,39 +2377,25 @@ static void sigwinch_handler(int sig) * a standard I/O stream on it and reading lines * without any prompting. TTY input is handled using * the edit function. */ -char *linenoise(lino_t *ls, const char *prompt) +wchar_t *linenoise(lino_t *ls, const wchar_t *prompt) { int count; + int ifd = lino_os.fileno_fn(ls->tty_ifs); - if (ls->ifs || ls->noninteractive || !isatty(ls->ifd)) { - if (!ls->ifs) { - int fd = dup(ls->ifd); - FILE *fi = (fd > 0) ? fdopen(fd, "r") : 0; - - if (!fi) { - ls->error = lino_error; - if (fd > 0) - close(fd); - return 0; - } - - ls->ifs = fi; - } - - + if ( ls->noninteractive || !isatty(ifd)) { /* Not a tty: read from file / pipe. */ - if (fgets(ls->data, sizeof ls->data, ls->ifs) == NULL) { - ls->error = (ferror(ls->ifs) ? lino_ioerr : lino_eof); + 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; } - count = strlen(ls->data); + count = wcslen(ls->data); if (count && ls->data[count-1] == '\n') ls->data[count-1] = '\0'; - return chk_strdup_utf8(ls->data); + return lino_os.wstrdup_fn(ls->data); } else { - char *ret = 0; + wchar_t *ret = 0; #ifdef SIGWINCH static struct sigaction blank; struct sigaction sa = blank, oa; @@ -2396,13 +2409,14 @@ char *linenoise(lino_t *ls, const char *prompt) count = edit(ls, prompt); disable_raw_mode(ls); if (count != -1 || ls->error == lino_eof) { - char nl = '\n'; - if (write(ls->ofd, &nl, 1) < 0) + if (!lino_os.puts_fn(ls->tty_ofs, L"\n")) { + ls->error = lino_ioerr; return 0; + } } if (count == -1) return 0; - ret = chk_strdup_utf8(ls->data); + ret = lino_os.wstrdup_fn(ls->data); #ifdef SIGWINCH sigaction(SIGWINCH, &oa, 0); @@ -2430,15 +2444,15 @@ static void unlink_from_list(lino_t *ls) lino_list_busy = 0; } -lino_t *lino_make(int ifd, int ofd) +lino_t *lino_make(mem_t *ifs, mem_t *ofs) { - lino_t *ls = coerce(lino_t *, chk_malloc(sizeof *ls)); + lino_t *ls = coerce(lino_t *, lino_os.alloc_fn(sizeof *ls)); if (ls) { memset(ls, 0, sizeof *ls); ls->history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; - ls->ifd = ifd; - ls->ofd = ofd; + ls->tty_ifs = ifs; + ls->tty_ofs = ofs; link_into_list(&lino_list, ls); } @@ -2448,7 +2462,7 @@ lino_t *lino_make(int ifd, int ofd) lino_t *lino_copy(lino_t *le) { - lino_t *ls = coerce(lino_t *, chk_malloc(sizeof *ls)); + lino_t *ls = coerce(lino_t *, lino_os.alloc_fn(sizeof *ls)); if (ls != 0) { *ls = *le; @@ -2473,12 +2487,10 @@ static void lino_cleanup(lino_t *ls) disable_raw_mode(ls); free_hist(ls); free_undo_stack(ls); - free(ls->clip); + lino_os.free_fn(ls->clip); ls->clip = 0; - free(ls->result); + lino_os.free_fn(ls->result); ls->result = 0; - if (ls->ifs) - fclose(ls->ifs); } void lino_free(lino_t *ls) @@ -2486,7 +2498,7 @@ void lino_free(lino_t *ls) if (ls != 0) { unlink_from_list(ls); lino_cleanup(ls); - free(ls); + lino_os.free_fn(ls); } } @@ -2516,10 +2528,10 @@ static void free_hist(lino_t *ls) { int j; for (j = 0; j < ls->history_len; j++) { - free(ls->history[j]); + lino_os.free_fn(ls->history[j]); ls->history[j] = 0; } - free(ls->history); + lino_os.free_fn(ls->history); ls->history = 0; } } @@ -2539,30 +2551,30 @@ static void atexit_handler(void) { * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ -int lino_hist_add(lino_t *ls, const char *line) { - char *linecopy; +int lino_hist_add(lino_t *ls, const wchar_t *line) { + wchar_t *linecopy; if (ls->history_max_len == 0) return 0; /* Initialization on first call. */ if (ls->history == NULL) { size_t size = ls->history_max_len * sizeof *ls->history; - ls->history = coerce(char **, chk_malloc(size)); + ls->history = coerce(wchar_t **, lino_os.alloc_fn(size)); if (ls->history == NULL) return 0; memset(ls->history, 0, size); } /* Don't add duplicated lines, unless we are resubmitting historic lines. */ if (ls->history_len && !ls->save_hist_idx && - !strcmp(ls->history[ls->history_len-1], line)) + !wcscmp(ls->history[ls->history_len-1], line)) return 0; /* Add an heap allocated copy of the line in the history. * If we reached the max length, remove the older line. */ - linecopy = chk_strdup_utf8(line); + linecopy = lino_os.wstrdup_fn(line); if (!linecopy) return 0; if (ls->history_len == ls->history_max_len) { - free(ls->history[0]); + lino_os.free_fn(ls->history[0]); memmove(ls->history,ls->history+1,(ls->history_max_len-1)*sizeof *ls->history); ls->history_len--; } @@ -2577,25 +2589,26 @@ int lino_hist_add(lino_t *ls, const char *line) { * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int lino_hist_set_max_len(lino_t *ls, int len) { - char **nsv; + wchar_t **nsv; if (len < 1) return 0; if (ls->history) { int tocopy = ls->history_len; - nsv = coerce(char **, chk_malloc(len * sizeof *nsv)); + nsv = coerce(wchar_t **, lino_os.alloc_fn(len * sizeof *nsv)); if (nsv == NULL) return 0; /* If we can't copy everything, free the elements we'll not use. */ if (len < tocopy) { int j; - for (j = 0; j < tocopy-len; j++) free(ls->history[j]); + for (j = 0; j < tocopy-len; j++) + lino_os.free_fn(ls->history[j]); tocopy = len; } - memset(nsv,0,sizeof(char*)*len); - memcpy(nsv,ls->history+(ls->history_len-tocopy), sizeof *ls->history * tocopy); - free(ls->history); + memset(nsv, 0, sizeof *nsv * len); + memcpy(nsv, ls->history+(ls->history_len-tocopy), sizeof *ls->history * tocopy); + lino_os.free_fn(ls->history); ls->history = nsv; ls->history_len = tocopy; } @@ -2605,8 +2618,8 @@ int lino_hist_set_max_len(lino_t *ls, int len) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -int lino_hist_save(lino_t *ls, const char *filename) { - FILE *fp = fopen(filename,"w"); +int lino_hist_save(lino_t *ls, const wchar_t *filename) { + mem_t *fp = lino_os.open_fn(filename, L"w"); int j; if (fp == NULL) { @@ -2614,9 +2627,12 @@ int lino_hist_save(lino_t *ls, const char *filename) { return -1; } - for (j = 0; j < ls->history_len; j++) - fprintf(fp,"%s\n",ls->history[j]); - fclose(fp); + for (j = 0; j < ls->history_len; j++) { + lino_os.puts_fn(fp, ls->history[j]); + lino_os.puts_fn(fp, L"\n"); + } + + lino_os.close_fn(fp); return 0; } @@ -2625,28 +2641,35 @@ int lino_hist_save(lino_t *ls, const char *filename) { * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ -int lino_hist_load(lino_t *ls, const char *filename) { - FILE *fp = fopen(filename,"r"); - char buf[LINENOISE_MAX_LINE]; +int lino_hist_load(lino_t *ls, const wchar_t *filename) { + mem_t *fp = lino_os.open_fn(filename, L"r"); + wchar_t buf[LINENOISE_MAX_LINE]; if (fp == NULL) { ls->error = lino_error; return -1; } - while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { - char *p = strchr(buf,'\n'); - if (p) *p = '\0'; + while (lino_os.getl_fn(fp, buf, LINENOISE_MAX_LINE) != NULL) { + wchar_t *p = wcschr(buf, '\n'); + if (p) + *p = '\0'; lino_hist_add(ls, buf); } - fclose(fp); + + lino_os.close_fn(fp); return 0; } -void lino_set_result(lino_t *ls, char *res) +void lino_set_result(lino_t *ls, wchar_t *res) { - free(ls->result); + lino_os.free_fn(ls->result); ls->result = res; - while ((res = strchr(res, '\n')) != 0) + while ((res = wcschr(res, '\n')) != 0) *res = '\r'; } + +void lino_init(lino_os_t *os) +{ + lino_os = *os; +} diff --git a/linenoise/linenoise.h b/linenoise/linenoise.h index cf2ddba6..8cc0a234 100644 --- a/linenoise/linenoise.h +++ b/linenoise/linenoise.h @@ -45,31 +45,68 @@ typedef enum lino_error { lino_intr /* Line innput terminated by Ctrl-C or interrupt */ } lino_error_t; +#define LINO_PAD_CHAR 0xFFFF + typedef struct lino_state lino_t; +#ifndef MEM_T_DEFINED +typedef unsigned char mem_t; +#define MEM_T_DEFINED +#endif + +typedef struct lino_os { + mem_t *(*alloc_fn)(size_t n); + mem_t *(*realloc_fn)(mem_t *old, size_t size); + wchar_t *(*wmalloc_fn)(size_t nwchar); + wchar_t *(*wrealloc_fn)(wchar_t *, size_t nwchar); + wchar_t *(*wstrdup_fn)(const wchar_t *str); + void (*free_fn)(void *); + int (*fileno_fn)(mem_t *stream); + int (*puts_fn)(mem_t *stream, const wchar_t *str); + wint_t (*getch_fn)(mem_t *stream); + wchar_t *(*getl_fn)(mem_t *stream, wchar_t *buf, size_t nchar); + wchar_t *(*gets_fn)(mem_t *stream, wchar_t *buf, size_t nchar); + int (*eof_fn)(mem_t *stream); + mem_t *(*open_fn)(const wchar_t *name, const wchar_t *mode); + mem_t *(*open8_fn)(const char *name, const wchar_t *mode); + mem_t *(*fdopen_fn)(int fd, const wchar_t *mode); + void (*close_fn)(mem_t *stream); + int (*wide_display_fn)(wchar_t); +} lino_os_t; + +#define lino_os_init(alloc, realloc, wmalloc, wrealloc, wstrdup, free, \ + fileno, puts, getch, getl, gets, eof, \ + open, open8, fdopen, close, wide_disp) \ +{ \ + alloc, realloc, wmalloc, wrealloc, wstrdup, free, \ + fileno, puts, getch, getl, gets, eof, open, open8, fdopen, close, \ + wide_disp \ +} + typedef struct lino_completions { - size_t len; - char **cvec; - int substring; + size_t len; + wchar_t **cvec; + int substring; } lino_completions_t; -typedef void lino_compl_cb_t(const char *, lino_completions_t *, void *ctx); +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 char *); +void lino_add_completion(lino_completions_t *, const wchar_t *); -lino_t *lino_make(int ifd, int ofd); +void lino_init(lino_os_t *); +lino_t *lino_make(mem_t *istream, mem_t *ostream); lino_t *lino_copy(lino_t *); void lino_free(lino_t *); -char *linenoise(lino_t *, const char *prompt); +wchar_t *linenoise(lino_t *, const wchar_t *prompt); void lino_set_tempfile_suffix(lino_t *, const char *); lino_error_t lino_get_error(lino_t *); lino_error_t lino_set_error(lino_t *, lino_error_t); /* returns old */ -int lino_hist_add(lino_t *, const char *line); +int lino_hist_add(lino_t *, const wchar_t *line); int lino_hist_set_max_len(lino_t *, int len); -int lino_hist_save(lino_t *, const char *filename); -int lino_hist_load(lino_t *, const char *filename); -void lino_set_result(lino_t *, char *); /* takes ownership of malloced mem; modifies it */ +int lino_hist_save(lino_t *, const wchar_t *filename); +int lino_hist_load(lino_t *, const wchar_t *filename); +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); int lino_get_multiline(lino_t *); @@ -78,8 +115,8 @@ int lino_get_selinculsive(lino_t *); void lino_set_noninteractive(lino_t *, int ni); int lino_get_noninteractive(lino_t *); -typedef char *lino_atom_cb_t(lino_t *, const char *line, int n, void *ctx); +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); -typedef int lino_enter_cb_t(const char *line, void *ctx); +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); |