Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/lib/libedit/readline.c,v rcsdiff: /ftp/cvs/cvsroot/src/lib/libedit/readline.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.105.2.4 retrieving revision 1.133 diff -u -p -r1.105.2.4 -r1.133 --- src/lib/libedit/readline.c 2014/08/20 00:02:17 1.105.2.4 +++ src/lib/libedit/readline.c 2016/05/13 15:55:59 1.133 @@ -1,4 +1,4 @@ -/* $NetBSD: readline.c,v 1.105.2.4 2014/08/20 00:02:17 tls Exp $ */ +/* $NetBSD: readline.c,v 1.133 2016/05/13 15:55:59 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -31,28 +31,28 @@ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: readline.c,v 1.105.2.4 2014/08/20 00:02:17 tls Exp $"); +__RCSID("$NetBSD: readline.c,v 1.133 2016/05/13 15:55:59 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include -#include -#include -#include -#include #include -#include -#include -#include +#include #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include "readline/readline.h" #include "el.h" -#include "fcns.h" /* for EL_NUM_FCNS */ -#include "histedit.h" +#include "fcns.h" #include "filecomplete.h" void rl_prep_terminal(int); @@ -78,7 +78,7 @@ FILE *rl_outstream = NULL; int rl_point = 0; int rl_end = 0; char *rl_line_buffer = NULL; -VCPFunction *rl_linefunc = NULL; +rl_vcpfunc_t *rl_linefunc = NULL; int rl_done = 0; VFunction *rl_event_hook = NULL; KEYMAP_ENTRY_ARRAY emacs_standard_keymap, @@ -107,9 +107,9 @@ int rl_attempted_completion_over = 0; char *rl_basic_word_break_characters = break_chars; char *rl_completer_word_break_characters = NULL; char *rl_completer_quote_characters = NULL; -Function *rl_completion_entry_function = NULL; +rl_compentry_func_t *rl_completion_entry_function = NULL; char *(*rl_completion_word_break_hook)(void) = NULL; -CPPFunction *rl_attempted_completion_function = NULL; +rl_completion_func_t *rl_attempted_completion_function = NULL; Function *rl_pre_input_hook = NULL; Function *rl_startup1_hook = NULL; int (*rl_getc_function)(FILE *) = NULL; @@ -156,24 +156,32 @@ char *rl_special_prefixes = NULL; */ int rl_completion_append_character = ' '; +/* + * When the history cursor is on the newest element and next_history() + * is called, GNU readline moves the cursor beyond the newest element. + * The editline library does not provide data structures to express + * that state, so we need a local flag. + */ +static int current_history_valid = 1; + /* stuff below is used internally by libedit for readline emulation */ static History *h = NULL; static EditLine *e = NULL; -static Function *map[256]; +static rl_command_func_t *map[256]; static jmp_buf topbuf; /* internal functions */ static unsigned char _el_rl_complete(EditLine *, int); static unsigned char _el_rl_tstp(EditLine *, int); static char *_get_prompt(EditLine *); -static int _getc_function(EditLine *, char *); +static int _getc_function(EditLine *, wchar_t *); static HIST_ENTRY *_move_history(int); static int _history_expand_command(const char *, size_t, size_t, char **); static char *_rl_compat_sub(const char *, const char *, const char *, int); -static int _rl_event_read_char(EditLine *, char *); +static int _rl_event_read_char(EditLine *, wchar_t *); static void _rl_update_pos(void); @@ -210,14 +218,14 @@ _move_history(int op) */ static int /*ARGSUSED*/ -_getc_function(EditLine *el __attribute__((__unused__)), char *c) +_getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c) { int i; i = (*rl_getc_function)(NULL); if (i == -1) return 0; - *c = (char)i; + *c = (wchar_t)i; return 1; } @@ -267,7 +275,7 @@ rl_set_prompt(const char *prompt) if (!prompt) prompt = ""; - if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) + if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) return 0; if (rl_prompt) el_free(rl_prompt); @@ -291,6 +299,8 @@ rl_initialize(void) int editmode = 1; struct termios t; + current_history_valid = 1; + if (e != NULL) el_end(e); if (h != NULL) @@ -338,7 +348,7 @@ rl_initialize(void) el_set(e, EL_SIGNAL, rl_catch_signals); /* set default mode to "emacs"-style and read setting afterwards */ - /* so this can be overriden */ + /* so this can be overridden */ el_set(e, EL_EDITOR, "emacs"); if (rl_terminal_name != NULL) el_set(e, EL_TERMINAL, rl_terminal_name); @@ -361,7 +371,38 @@ rl_initialize(void) "ReadLine compatible suspend function", _el_rl_tstp); el_set(e, EL_BIND, "^Z", "rl_tstp", NULL); - + + /* + * Set some readline compatible key-bindings. + */ + el_set(e, EL_BIND, "^R", "em-inc-search-prev", NULL); + + /* + * Allow the use of Home/End keys. + */ + el_set(e, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[4~", "ed-move-to-end", NULL); + el_set(e, EL_BIND, "\\e[7~", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[8~", "ed-move-to-end", NULL); + el_set(e, EL_BIND, "\\e[H", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[F", "ed-move-to-end", NULL); + + /* + * Allow the use of the Delete/Insert keys. + */ + el_set(e, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL); + el_set(e, EL_BIND, "\\e[2~", "ed-quoted-insert", NULL); + + /* + * Ctrl-left-arrow and Ctrl-right-arrow for word moving. + */ + el_set(e, EL_BIND, "\\e[1;5C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); + el_set(e, EL_BIND, "\\e[5C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e[5D", "ed-prev-word", NULL); + el_set(e, EL_BIND, "\\e\\e[C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); + /* read settings from configuration file */ el_source(e, NULL); @@ -634,7 +675,7 @@ get_history_event(const char *cmd, int * * returns 0 if data was not modified, 1 if it was and 2 if the string * should be only printed and not executed; in case of error, * returns -1 and *result points to NULL - * it's callers responsibility to free() string returned in *result + * it's the caller's responsibility to free() the string returned in *result */ static int _history_expand_command(const char *command, size_t offs, size_t cmdlen, @@ -948,7 +989,8 @@ loop: for (; str[j]; j++) { if (str[j] == '\\' && str[j + 1] == history_expansion_char) { - (void)strcpy(&str[j], &str[j + 1]); + len = strlen(&str[j + 1]) + 1; + memmove(&str[j], &str[j + 1], len); continue; } if (!loop_again) { @@ -1136,12 +1178,24 @@ void stifle_history(int max) { HistEvent ev; + HIST_ENTRY *he; + int i, len; if (h == NULL || e == NULL) rl_initialize(); - if (history(h, &ev, H_SETSIZE, max) == 0) + len = history_length; + if (history(h, &ev, H_SETSIZE, max) == 0) { max_input_history = max; + if (max < len) + history_base += len - max; + for (i = 0; i < len - max; i++) { + he = remove_history(i); + el_free(he->data); + el_free((void *)(unsigned long)he->line); + el_free(he); + } + } } @@ -1356,25 +1410,37 @@ history_get(int num) if (h == NULL || e == NULL) rl_initialize(); + if (num < history_base) + return NULL; + /* save current position */ if (history(h, &ev, H_CURR) != 0) return NULL; curr_num = ev.num; - /* start from the oldest */ - if (history(h, &ev, H_LAST) != 0) - return NULL; /* error */ - - /* look forwards for event matching specified offset */ - if (history(h, &ev, H_NEXT_EVDATA, num, &she.data)) - return NULL; + /* + * use H_DELDATA to set to nth history (without delete) by passing + * (void **)-1 -- as in history_set_pos + */ + if (history(h, &ev, H_DELDATA, num - history_base, (void **)-1) != 0) + goto out; + /* get current entry */ + if (history(h, &ev, H_CURR) != 0) + goto out; + if (history(h, &ev, H_NEXT_EVDATA, ev.num, &she.data) != 0) + goto out; she.line = ev.str; /* restore pointer to where it was */ (void)history(h, &ev, H_SET, curr_num); return &she; + +out: + /* restore pointer to where it was */ + (void)history(h, &ev, H_SET, curr_num); + return NULL; } @@ -1395,6 +1461,7 @@ add_history(const char *line) (void)history(h, &ev, H_ENTER, line); if (history(h, &ev, H_GETSIZE) == 0) history_length = ev.num; + current_history_valid = 1; return !(history_length > 0); /* return 0 if all is okay */ } @@ -1487,6 +1554,7 @@ clear_history(void) (void)history(h, &ev, H_CLEAR); history_length = 0; + current_history_valid = 1; } @@ -1503,9 +1571,12 @@ where_history(void) return 0; curr_num = ev.num; - (void)history(h, &ev, H_FIRST); - off = 1; - while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) + /* start from the oldest */ + (void)history(h, &ev, H_LAST); + + /* position is zero-based */ + off = 0; + while (ev.num != curr_num && history(h, &ev, H_PREV) == 0) off++; return off; @@ -1519,7 +1590,7 @@ HIST_ENTRY * current_history(void) { - return _move_history(H_CURR); + return current_history_valid ? _move_history(H_CURR) : NULL; } @@ -1560,10 +1631,11 @@ history_set_pos(int pos) int curr_num; if (pos >= history_length || pos < 0) - return -1; + return 0; (void)history(h, &ev, H_CURR); curr_num = ev.num; + current_history_valid = 1; /* * use H_DELDATA to set to nth history (without delete) by passing @@ -1571,20 +1643,25 @@ history_set_pos(int pos) */ if (history(h, &ev, H_DELDATA, pos, (void **)-1)) { (void)history(h, &ev, H_SET, curr_num); - return -1; + return 0; } - return 0; + return 1; } /* * returns previous event in history and shifts pointer accordingly + * Note that readline and editline define directions in opposite ways. */ HIST_ENTRY * previous_history(void) { - return _move_history(H_PREV); + if (current_history_valid == 0) { + current_history_valid = 1; + return _move_history(H_CURR); + } + return _move_history(H_NEXT); } @@ -1594,8 +1671,12 @@ previous_history(void) HIST_ENTRY * next_history(void) { + HIST_ENTRY *he; - return _move_history(H_NEXT); + he = _move_history(H_PREV); + if (he == NULL) + current_history_valid = 0; + return he; } @@ -1656,7 +1737,7 @@ history_search_pos(const char *str, return -1; curr_num = ev.num; - if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) + if (!history_set_pos(off) || history(h, &ev, H_CURR) != 0) return -1; for (;;) { @@ -1694,7 +1775,7 @@ filename_completion_function(const char * which starts with supplied text * text contains a partial username preceded by random character * (usually '~'); state resets search from start (??? should we do that anyway) - * it's callers responsibility to free returned value + * it's the caller's responsibility to free the returned value */ char * username_completion_function(const char *text, int state) @@ -1774,9 +1855,7 @@ _rl_completion_append_character_function int rl_complete(int ignore __attribute__((__unused__)), int invoking_key) { -#ifdef WIDECHAR static ct_buffer_t wbreak_conv, sprefix_conv; -#endif char *breakchars; if (h == NULL || e == NULL) @@ -1795,9 +1874,11 @@ rl_complete(int ignore __attribute__((__ else breakchars = rl_basic_word_break_characters; + _rl_update_pos(); + /* Just look at how many global variables modify this operation! */ return fn_complete(e, - (CPFunction *)rl_completion_entry_function, + (rl_compentry_func_t *)rl_completion_entry_function, rl_attempted_completion_function, ct_decode_string(rl_basic_word_break_characters, &wbreak_conv), ct_decode_string(breakchars, &sprefix_conv), @@ -1926,7 +2007,7 @@ rl_bind_wrapper(EditLine *el __attribute _rl_update_pos(); - (*map[c])(NULL, c); + (*map[c])(1, c); /* If rl_done was set by the above call, deal with it here */ if (rl_done) @@ -1936,7 +2017,7 @@ rl_bind_wrapper(EditLine *el __attribute } int -rl_add_defun(const char *name, Function *fun, int c) +rl_add_defun(const char *name, rl_command_func_t *fun, int c) { char dest[8]; if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) @@ -1974,8 +2055,8 @@ rl_callback_read_char(void) } } -void -rl_callback_handler_install(const char *prompt, VCPFunction *linefunc) +void +rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc) { if (e == NULL) { rl_initialize(); @@ -1983,9 +2064,9 @@ rl_callback_handler_install(const char * (void)rl_set_prompt(prompt); rl_linefunc = linefunc; el_set(e, EL_UNBUFFERED, 1); -} +} -void +void rl_callback_handler_remove(void) { el_set(e, EL_UNBUFFERED, 0); @@ -2066,12 +2147,14 @@ rl_stuff_char(int c) } static int -_rl_event_read_char(EditLine *el, char *cp) +_rl_event_read_char(EditLine *el, wchar_t *wc) { + char ch; int n; ssize_t num_read = 0; - *cp = '\0'; + ch = '\0'; + *wc = L'\0'; while (rl_event_hook) { (*rl_event_hook)(); @@ -2080,7 +2163,7 @@ _rl_event_read_char(EditLine *el, char * if (ioctl(el->el_infd, FIONREAD, &n) < 0) return -1; if (n) - num_read = read(el->el_infd, cp, (size_t)1); + num_read = read(el->el_infd, &ch, (size_t)1); else num_read = 0; #elif defined(F_SETFL) && defined(O_NDELAY) @@ -2088,12 +2171,12 @@ _rl_event_read_char(EditLine *el, char * return -1; if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0) return -1; - num_read = read(el->el_infd, cp, 1); + num_read = read(el->el_infd, &ch, 1); if (fcntl(el->el_infd, F_SETFL, n)) return -1; #else /* not non-blocking, but what you gonna do? */ - num_read = read(el->el_infd, cp, 1); + num_read = read(el->el_infd, &ch, 1); return -1; #endif @@ -2105,6 +2188,7 @@ _rl_event_read_char(EditLine *el, char * } if (!rl_event_hook) el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN); + *wc = (wchar_t)ch; return (int)num_read; } @@ -2167,7 +2251,7 @@ rl_completion_matches(const char *str, r } qsort(&list[1], len - 1, sizeof(*list), (int (*)(const void *, const void *)) strcmp); - min = SIZE_T_MAX; + min = SIZE_MAX; for (i = 1, a = list[i]; i < len - 1; i++, a = b) { b = list[i + 1]; for (j = 0; a[j] && a[j] == b[j]; j++) @@ -2185,7 +2269,7 @@ rl_completion_matches(const char *str, r list[0][min] = '\0'; } return list; - + out: el_free(list); return NULL; @@ -2289,3 +2373,10 @@ void rl_free_line_state(void) { } + +int +/*ARGSUSED*/ +rl_set_keyboard_input_timeout(int u __attribute__((__unused__))) +{ + return 0; +}