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.100.2.1 retrieving revision 1.141 diff -u -p -r1.100.2.1 -r1.141 --- src/lib/libedit/readline.c 2012/06/05 20:22:14 1.100.2.1 +++ src/lib/libedit/readline.c 2017/04/21 05:38:03 1.141 @@ -1,4 +1,4 @@ -/* $NetBSD: readline.c,v 1.100.2.1 2012/06/05 20:22:14 bouyer Exp $ */ +/* $NetBSD: readline.c,v 1.141 2017/04/21 05:38:03 abhinav 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.100.2.1 2012/06/05 20:22:14 bouyer Exp $"); +__RCSID("$NetBSD: readline.c,v 1.141 2017/04/21 05:38:03 abhinav 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,15 +78,24 @@ 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, emacs_meta_keymap, emacs_ctlx_keymap; +/* + * The following is not implemented; we always catch signals in the + * libedit fashion: set handlers on entry to el_gets() and clear them + * on the way out. This simplistic approach works for most cases; if + * it does not work for your application, please let us know. + */ +int rl_catch_signals = 1; +int rl_catch_sigwinch = 1; int history_base = 1; /* probably never subject to change */ int history_length = 0; +int history_offset = 0; int max_input_history = 0; char history_expansion_char = '!'; char history_subst_char = '^'; @@ -99,8 +108,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; -CPPFunction *rl_attempted_completion_function = NULL; +rl_compentry_func_t *rl_completion_entry_function = NULL; +char *(*rl_completion_word_break_hook)(void) = 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; @@ -108,7 +118,6 @@ char *rl_terminal_name = NULL; int rl_already_prompted = 0; int rl_filename_completion_desired = 0; int rl_ignore_completion_duplicates = 0; -int rl_catch_signals = 1; int readline_echoing_p = 1; int _rl_print_completions_horizontally = 0; VFunction *rl_redisplay_function = NULL; @@ -152,22 +161,22 @@ int rl_completion_append_character = ' ' 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 HIST_ENTRY *_move_history(int); +static int _getc_function(EditLine *, wchar_t *); 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); +static HIST_ENTRY rl_he; /* ARGSUSED */ static char * @@ -179,37 +188,18 @@ _get_prompt(EditLine *el __attribute__(( /* - * generic function for moving around history - */ -static HIST_ENTRY * -_move_history(int op) -{ - HistEvent ev; - static HIST_ENTRY rl_he; - - if (history(h, &ev, op) != 0) - return NULL; - - rl_he.line = ev.str; - rl_he.data = NULL; - - return &rl_he; -} - - -/* * read one key from user defined input function */ 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); + i = (*rl_getc_function)(rl_instream); if (i == -1) return 0; - *c = (char)i; + *c = (wchar_t)i; return 1; } @@ -228,13 +218,20 @@ static const char * _default_history_file(void) { struct passwd *p; - static char path[PATH_MAX]; + static char *path; + size_t len; - if (*path) + if (path) return path; + if ((p = getpwuid(getuid())) == NULL) return NULL; - (void)snprintf(path, sizeof(path), "%s/.history", p->pw_dir); + + len = strlen(p->pw_dir) + sizeof("/.history"); + if ((path = malloc(len)) == NULL) + return NULL; + + (void)snprintf(path, len, "%s/.history", p->pw_dir); return path; } @@ -252,7 +249,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); @@ -323,7 +320,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); @@ -346,7 +343,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); @@ -438,6 +466,7 @@ using_history(void) { if (h == NULL || e == NULL) rl_initialize(); + history_offset = history_length; } @@ -519,7 +548,7 @@ get_history_event(const char *cmd, int * } if ('0' <= cmd[idx] && cmd[idx] <= '9') { - HIST_ENTRY *rl_he; + HIST_ENTRY *he; num = 0; while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') { @@ -527,13 +556,13 @@ get_history_event(const char *cmd, int * idx++; } if (sign) - num = history_length - num + 1; + num = history_length - num + history_base; - if (!(rl_he = history_get(num))) + if (!(he = history_get(num))) return NULL; *cindex = idx; - return rl_he->line; + return he->line; } sub = 0; if (cmd[idx] == '?') { @@ -619,7 +648,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, @@ -933,7 +962,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) { @@ -1121,12 +1151,22 @@ void stifle_history(int max) { HistEvent ev; + HIST_ENTRY *he; if (h == NULL || e == NULL) rl_initialize(); - if (history(h, &ev, H_SETSIZE, max) == 0) + if (history(h, &ev, H_SETSIZE, max) == 0) { max_input_history = max; + if (history_length > max) + history_base = history_length - max; + while (history_length > max) { + he = remove_history(0); + el_free(he->data); + el_free((void *)(unsigned long)he->line); + el_free(he); + } + } } @@ -1341,25 +1381,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; } @@ -1371,17 +1423,18 @@ add_history(const char *line) { HistEvent ev; - if (line == NULL) - return 0; - if (h == NULL || e == NULL) rl_initialize(); - (void)history(h, &ev, H_ENTER, line); - if (history(h, &ev, H_GETSIZE) == 0) - history_length = ev.num; + if (history(h, &ev, H_ENTER, line) == -1) + return 0; - return !(history_length > 0); /* return 0 if all is okay */ + (void)history(h, &ev, H_GETSIZE); + if (ev.num == history_length) + history_base++; + else + history_length = ev.num; + return 0; } @@ -1467,8 +1520,11 @@ clear_history(void) { HistEvent ev; + if (h == NULL || e == NULL) + rl_initialize(); + (void)history(h, &ev, H_CLEAR); - history_length = 0; + history_offset = history_length = 0; } @@ -1478,21 +1534,42 @@ clear_history(void) int where_history(void) { + return history_offset; +} + +static HIST_ENTRY **_history_listp; +static HIST_ENTRY *_history_list; + +HIST_ENTRY ** +history_list(void) +{ HistEvent ev; - int curr_num, off; + HIST_ENTRY **nlp, *nl; + int i; - if (history(h, &ev, H_CURR) != 0) - return 0; - curr_num = ev.num; + if (history(h, &ev, H_LAST) != 0) + return NULL; - (void)history(h, &ev, H_FIRST); - off = 1; - while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) - off++; + if ((nlp = el_realloc(_history_listp, + (size_t)history_length * sizeof(*nlp))) == NULL) + return NULL; + _history_listp = nlp; - return off; -} + if ((nl = el_realloc(_history_list, + (size_t)history_length * sizeof(*nl))) == NULL) + return NULL; + _history_list = nl; + i = 0; + do { + _history_listp[i] = &_history_list[i]; + _history_list[i].line = ev.str; + _history_list[i].data = NULL; + if (i++ == history_length) + abort(); + } while (history(h, &ev, H_PREV) == 0); + return _history_listp; +} /* * returns current history event or NULL if there is no such event @@ -1500,8 +1577,14 @@ where_history(void) HIST_ENTRY * current_history(void) { + HistEvent ev; + + if (history(h, &ev, H_PREV_EVENT, history_offset + 1) != 0) + return NULL; - return _move_history(H_CURR); + rl_he.line = ev.str; + rl_he.data = NULL; + return &rl_he; } @@ -1538,35 +1621,31 @@ history_total_bytes(void) int history_set_pos(int pos) { - HistEvent ev; - int curr_num; - if (pos >= history_length || pos < 0) - return -1; - - (void)history(h, &ev, H_CURR); - curr_num = ev.num; + return 0; - /* - * use H_DELDATA to set to nth history (without delete) by passing - * (void **)-1 - */ - if (history(h, &ev, H_DELDATA, pos, (void **)-1)) { - (void)history(h, &ev, H_SET, curr_num); - return -1; - } - return 0; + history_offset = pos; + 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) { + HistEvent ev; + + if (history_offset == 0) + return NULL; - return _move_history(H_PREV); + if (history(h, &ev, H_LAST) != 0) + return NULL; + + history_offset--; + return current_history(); } @@ -1576,8 +1655,16 @@ previous_history(void) HIST_ENTRY * next_history(void) { + HistEvent ev; - return _move_history(H_NEXT); + if (history_offset >= history_length) + return NULL; + + if (history(h, &ev, H_LAST) != 0) + return NULL; + + history_offset++; + return current_history(); } @@ -1638,7 +1725,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 (;;) { @@ -1676,7 +1763,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) @@ -1725,18 +1812,6 @@ _el_rl_tstp(EditLine *el __attribute__(( return CC_NORM; } -/* - * Display list of strings in columnar format on readline's output stream. - * 'matches' is list of strings, 'len' is number of strings in 'matches', - * 'max' is maximum length of string in 'matches'. - */ -void -rl_display_match_list(char **matches, int len, int max) -{ - - fn_display_match_list(e, matches, (size_t)len, (size_t)max); -} - static const char * /*ARGSUSED*/ _rl_completion_append_character_function(const char *dummy @@ -1750,15 +1825,27 @@ _rl_completion_append_character_function /* + * Display list of strings in columnar format on readline's output stream. + * 'matches' is list of strings, 'len' is number of strings in 'matches', + * 'max' is maximum length of string in 'matches'. + */ +void +rl_display_match_list(char **matches, int len, int max) +{ + + fn_display_match_list(e, matches, (size_t)len, (size_t)max, + _rl_completion_append_character_function); +} + +/* * complete word at current point */ /* ARGSUSED */ 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) rl_initialize(); @@ -1771,12 +1858,19 @@ rl_complete(int ignore __attribute__((__ return CC_REFRESH; } + if (rl_completion_word_break_hook != NULL) + breakchars = (*rl_completion_word_break_hook)(); + 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(rl_special_prefixes, &sprefix_conv), + ct_decode_string(breakchars, &sprefix_conv), _rl_completion_append_character_function, (size_t)rl_completion_query_items, &rl_completion_type, &rl_attempted_completion_over, @@ -1902,7 +1996,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) @@ -1912,7 +2006,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) @@ -1920,12 +2014,12 @@ rl_add_defun(const char *name, Function map[(unsigned char)c] = fun; el_set(e, EL_ADDFN, name, name, rl_bind_wrapper); vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0); - el_set(e, EL_BIND, dest, name); + el_set(e, EL_BIND, dest, name, NULL); return 0; } void -rl_callback_read_char() +rl_callback_read_char(void) { int count = 0, done = 0; const char *buf = el_gets(e, &count); @@ -1946,12 +2040,12 @@ rl_callback_read_char() } else wbuf = NULL; (*(void (*)(const char *))rl_linefunc)(wbuf); - //el_set(e, EL_UNBUFFERED, 1); + el_set(e, EL_UNBUFFERED, 1); } } -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(); @@ -1959,9 +2053,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); @@ -2028,7 +2122,7 @@ rl_variable_bind(const char *var, const * The proper return value is undocument, but this is what the * readline source seems to do. */ - return el_set(e, EL_BIND, "", var, value) == -1 ? 1 : 0; + return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0; } void @@ -2042,12 +2136,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)(); @@ -2056,7 +2152,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) @@ -2064,12 +2160,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 @@ -2081,6 +2177,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; } @@ -2097,9 +2194,9 @@ void rl_get_screen_size(int *rows, int *cols) { if (rows) - el_get(e, EL_GETTC, "li", rows); + el_get(e, EL_GETTC, "li", rows, (void *)0); if (cols) - el_get(e, EL_GETTC, "co", cols); + el_get(e, EL_GETTC, "co", cols, (void *)0); } void @@ -2107,9 +2204,9 @@ rl_set_screen_size(int rows, int cols) { char buf[64]; (void)snprintf(buf, sizeof(buf), "%d", rows); - el_set(e, EL_SETTC, "li", buf); + el_set(e, EL_SETTC, "li", buf, NULL); (void)snprintf(buf, sizeof(buf), "%d", cols); - el_set(e, EL_SETTC, "co", buf); + el_set(e, EL_SETTC, "co", buf, NULL); } char ** @@ -2143,7 +2240,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++) @@ -2161,7 +2258,7 @@ rl_completion_matches(const char *str, r list[0][min] = '\0'; } return list; - + out: el_free(list); return NULL; @@ -2260,3 +2357,15 @@ rl_on_new_line(void) { return 0; } + +void +rl_free_line_state(void) +{ +} + +int +/*ARGSUSED*/ +rl_set_keyboard_input_timeout(int u __attribute__((__unused__))) +{ + return 0; +}