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.69 retrieving revision 1.132 diff -u -p -r1.69 -r1.132 --- src/lib/libedit/readline.c 2006/08/21 12:45:30 1.69 +++ src/lib/libedit/readline.c 2016/05/09 21:27:55 1.132 @@ -1,4 +1,4 @@ -/* $NetBSD: readline.c,v 1.69 2006/08/21 12:45:30 christos Exp $ */ +/* $NetBSD: readline.c,v 1.132 2016/05/09 21:27:55 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -15,9 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -34,33 +31,28 @@ #include "config.h" #if !defined(lint) && !defined(SCCSID) -__RCSID("$NetBSD: readline.c,v 1.69 2006/08/21 12:45:30 christos Exp $"); +__RCSID("$NetBSD: readline.c,v 1.132 2016/05/09 21:27:55 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 -#ifdef HAVE_VIS_H #include -#else -#include "np/vis.h" -#endif -#ifdef HAVE_ALLOCA_H -#include -#endif -#include "el.h" -#include "fcns.h" /* for EL_NUM_FCNS */ -#include "histedit.h" + #include "readline/readline.h" +#include "el.h" +#include "fcns.h" #include "filecomplete.h" void rl_prep_terminal(int); @@ -75,6 +67,7 @@ void rl_deprep_terminal(void); /* readline compatibility stuff - look at readline sources/documentation */ /* to see what these variables mean */ const char *rl_library_version = "EditLine wrapper"; +int rl_readline_version = RL_READLINE_VERSION; static char empty[] = { '\0' }; static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' }; static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', @@ -85,9 +78,20 @@ 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; @@ -103,21 +107,24 @@ 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; -Function *rl_getc_function = NULL; +int (*rl_getc_function)(FILE *) = NULL; 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; Function *rl_startup_hook = NULL; VFunction *rl_completion_display_matches_hook = NULL; VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal; VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal; +KEYMAP_ENTRY_ARRAY emacs_meta_keymap; /* * The current prompt string. @@ -149,23 +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); @@ -174,7 +190,7 @@ static char * _get_prompt(EditLine *el __attribute__((__unused__))) { rl_already_prompted = 1; - return (rl_prompt); + return rl_prompt; } @@ -188,12 +204,12 @@ _move_history(int op) static HIST_ENTRY rl_he; if (history(h, &ev, op) != 0) - return (HIST_ENTRY *) NULL; + return NULL; rl_he.line = ev.str; rl_he.data = NULL; - return (&rl_he); + return &rl_he; } @@ -202,33 +218,89 @@ _move_history(int op) */ static int /*ARGSUSED*/ -_getc_function(EditLine *el, char *c) +_getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c) { int i; - i = (*rl_getc_function)(NULL, 0); + i = (*rl_getc_function)(NULL); if (i == -1) return 0; - *c = i; + *c = (wchar_t)i; return 1; } +static void +_resize_fun(EditLine *el, void *a) +{ + const LineInfo *li; + char **ap = a; + + li = el_line(el); + /* a cheesy way to get rid of const cast. */ + *ap = memchr(li->buffer, *li->buffer, (size_t)1); +} + +static const char * +_default_history_file(void) +{ + struct passwd *p; + static char *path; + size_t len; + + if (path) + return path; + + if ((p = getpwuid(getuid())) == NULL) + return NULL; + + 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; +} /* * READLINE compatibility stuff */ /* + * Set the prompt + */ +int +rl_set_prompt(const char *prompt) +{ + char *p; + + if (!prompt) + prompt = ""; + if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) + return 0; + if (rl_prompt) + el_free(rl_prompt); + rl_prompt = strdup(prompt); + if (rl_prompt == NULL) + return -1; + + while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL) + *p = RL_PROMPT_START_IGNORE; + + return 0; +} + +/* * initialize rl compat stuff */ int rl_initialize(void) { HistEvent ev; - const LineInfo *li; int editmode = 1; struct termios t; + current_history_valid = 1; + if (e != NULL) el_end(e); if (h != NULL) @@ -252,29 +324,31 @@ rl_initialize(void) h = history_init(); if (!e || !h) - return (-1); + return -1; history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */ history_length = 0; max_input_history = INT_MAX; el_set(e, EL_HIST, history, h); + /* Setup resize function */ + el_set(e, EL_RESIZE, _resize_fun, &rl_line_buffer); + /* setup getc function if valid */ if (rl_getc_function) el_set(e, EL_GETCFN, _getc_function); /* for proper prompt printing in readline() */ - rl_prompt = strdup(""); - if (rl_prompt == NULL) { + if (rl_set_prompt("") == -1) { history_end(h); el_end(e); return -1; } - el_set(e, EL_PROMPT, _get_prompt); + el_set(e, EL_PROMPT, _get_prompt, RL_PROMPT_START_IGNORE); 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); @@ -297,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); @@ -305,15 +410,13 @@ rl_initialize(void) * Unfortunately, some applications really do use rl_point * and rl_line_buffer directly. */ - li = el_line(e); - /* a cheesy way to get rid of const cast. */ - rl_line_buffer = memchr(li->buffer, *li->buffer, 1); + _resize_fun(e, &rl_line_buffer); _rl_update_pos(); if (rl_startup_hook) (*rl_startup_hook)(NULL, 0); - return (0); + return 0; } @@ -322,9 +425,10 @@ rl_initialize(void) * trailing newline (if there is any) */ char * -readline(const char *prompt) +readline(const char *p) { HistEvent ev; + const char * volatile prompt = p; int count; const char *ret; char *buf; @@ -335,15 +439,11 @@ readline(const char *prompt) rl_done = 0; + (void)setjmp(topbuf); + /* update prompt accordingly to what has been passed */ - if (!prompt) - prompt = ""; - if (strcmp(rl_prompt, prompt) != 0) { - free(rl_prompt); - rl_prompt = strdup(prompt); - if (rl_prompt == NULL) - return NULL; - } + if (rl_set_prompt(prompt) == -1) + return NULL; if (rl_pre_input_hook) (*rl_pre_input_hook)(NULL, 0); @@ -425,7 +525,7 @@ _rl_compat_sub(const char *str, const ch } else s++; } - r = result = malloc(len + 1); + r = result = el_malloc((len + 1) * sizeof(*r)); if (result == NULL) return NULL; s = str; @@ -436,13 +536,13 @@ _rl_compat_sub(const char *str, const ch s += what_len; if (!globally) { (void)strcpy(r, s); - return(result); + return result; } } else *r++ = *s++; } - *r = 0; - return(result); + *r = '\0'; + return result; } static char *last_search_pat; /* last !?pat[?] search pattern */ @@ -459,14 +559,14 @@ get_history_event(const char *cmd, int * idx = *cindex; if (cmd[idx++] != history_expansion_char) - return(NULL); + return NULL; /* find out which event to take */ - if (cmd[idx] == history_expansion_char || cmd[idx] == 0) { + if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') { if (history(h, &ev, H_FIRST) != 0) - return(NULL); + return NULL; *cindex = cmd[idx]? (idx + 1):idx; - return(ev.str); + return ev.str; } sign = 0; if (cmd[idx] == '-') { @@ -486,10 +586,10 @@ get_history_event(const char *cmd, int * num = history_length - num + 1; if (!(rl_he = history_get(num))) - return(NULL); + return NULL; *cindex = idx; - return(rl_he->line); + return rl_he->line; } sub = 0; if (cmd[idx] == '?') { @@ -507,15 +607,15 @@ get_history_event(const char *cmd, int * break; idx++; } - len = idx - begin; + len = (size_t)idx - (size_t)begin; if (sub && cmd[idx] == '?') idx++; if (sub && len == 0 && last_search_pat && *last_search_pat) pat = last_search_pat; else if (len == 0) - return(NULL); + return NULL; else { - if ((pat = malloc(len + 1)) == NULL) + if ((pat = el_malloc((len + 1) * sizeof(*pat))) == NULL) return NULL; (void)strncpy(pat, cmd + begin, len); pat[len] = '\0'; @@ -523,15 +623,15 @@ get_history_event(const char *cmd, int * if (history(h, &ev, H_CURR) != 0) { if (pat != last_search_pat) - free(pat); - return (NULL); + el_free(pat); + return NULL; } num = ev.num; if (sub) { if (pat != last_search_pat) { if (last_search_pat) - free(last_search_pat); + el_free(last_search_pat); last_search_pat = pat; } ret = history_search(pat, -1); @@ -543,21 +643,21 @@ get_history_event(const char *cmd, int * history(h, &ev, H_FIRST); (void)fprintf(rl_outstream, "%s: Event not found\n", pat); if (pat != last_search_pat) - free(pat); - return(NULL); + el_free(pat); + return NULL; } if (sub && len) { if (last_search_match && last_search_match != pat) - free(last_search_match); + el_free(last_search_match); last_search_match = pat; } if (pat != last_search_pat) - free(pat); + el_free(pat); if (history(h, &ev, H_CURR) != 0) - return(NULL); + return NULL; *cindex = idx; rptr = ev.str; @@ -575,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, @@ -609,7 +709,8 @@ _history_expand_command(const char *comm } else { if (command[offs + 1] == '#') { /* use command so far */ - if ((aptr = malloc(offs + 1)) == NULL) + if ((aptr = el_malloc((offs + 1) * sizeof(*aptr))) + == NULL) return -1; (void)strncpy(aptr, command, offs); aptr[offs] = '\0'; @@ -620,17 +721,19 @@ _history_expand_command(const char *comm qchar = (offs > 0 && command[offs - 1] == '"')? '"':0; ptr = get_history_event(command + offs, &idx, qchar); } - has_mods = command[offs + idx] == ':'; + has_mods = command[offs + (size_t)idx] == ':'; } if (ptr == NULL && aptr == NULL) - return(-1); + return -1; if (!has_mods) { - *result = strdup(aptr? aptr : ptr); + *result = strdup(aptr ? aptr : ptr); if (aptr) - free(aptr); - return(1); + el_free(aptr); + if (*result == NULL) + return -1; + return 1; } cmd = command + offs + idx + 1; @@ -675,18 +778,18 @@ _history_expand_command(const char *comm (void)fprintf(rl_outstream, "%s: Bad word specifier", command + offs + idx); if (aptr) - free(aptr); - return(-1); + el_free(aptr); + return -1; } } else tmp = strdup(aptr? aptr:ptr); if (aptr) - free(aptr); + el_free(aptr); - if (*cmd == 0 || (cmd - (command + offs) >= cmdlen)) { + if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) { *result = tmp; - return(1); + return 1; } for (; *cmd; cmd++) { @@ -694,20 +797,20 @@ _history_expand_command(const char *comm continue; else if (*cmd == 'h') { /* remove trailing path */ if ((aptr = strrchr(tmp, '/')) != NULL) - *aptr = 0; + *aptr = '\0'; } else if (*cmd == 't') { /* remove leading path */ if ((aptr = strrchr(tmp, '/')) != NULL) { aptr = strdup(aptr + 1); - free(tmp); + el_free(tmp); tmp = aptr; } } else if (*cmd == 'r') { /* remove trailing suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) - *aptr = 0; + *aptr = '\0'; } else if (*cmd == 'e') { /* remove all but suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) { aptr = strdup(aptr); - free(tmp); + el_free(tmp); tmp = aptr; } } else if (*cmd == 'p') /* print only */ @@ -724,10 +827,10 @@ _history_expand_command(const char *comm else if (*cmd == 's') { delim = *(++cmd), cmd++; size = 16; - what = realloc(from, size); + what = el_realloc(from, size * sizeof(*what)); if (what == NULL) { - free(from); - free(tmp); + el_free(from); + el_free(tmp); return 0; } len = 0; @@ -736,11 +839,12 @@ _history_expand_command(const char *comm cmd++; if (len >= size) { char *nwhat; - nwhat = realloc(what, - (size <<= 1)); + nwhat = el_realloc(what, + (size <<= 1) * + sizeof(*nwhat)); if (nwhat == NULL) { - free(what); - free(tmp); + el_free(what); + el_free(tmp); return 0; } what = nwhat; @@ -750,17 +854,17 @@ _history_expand_command(const char *comm what[len] = '\0'; from = what; if (*what == '\0') { - free(what); + el_free(what); if (search) { from = strdup(search); if (from == NULL) { - free(tmp); + el_free(tmp); return 0; } } else { from = NULL; - free(tmp); - return (-1); + el_free(tmp); + return -1; } } cmd++; /* shift after delim */ @@ -768,10 +872,10 @@ _history_expand_command(const char *comm continue; size = 16; - with = realloc(to, size); + with = el_realloc(to, size * sizeof(*with)); if (with == NULL) { - free(to); - free(tmp); + el_free(to); + el_free(tmp); return -1; } len = 0; @@ -780,10 +884,11 @@ _history_expand_command(const char *comm if (len + from_len + 1 >= size) { char *nwith; size += from_len + 1; - nwith = realloc(with, size); + nwith = el_realloc(with, + size * sizeof(*nwith)); if (nwith == NULL) { - free(with); - free(tmp); + el_free(with); + el_free(tmp); return -1; } with = nwith; @@ -806,14 +911,14 @@ _history_expand_command(const char *comm aptr = _rl_compat_sub(tmp, from, to, g_on); if (aptr) { - free(tmp); + el_free(tmp); tmp = aptr; } g_on = 0; } } *result = tmp; - return (p_on? 2:1); + return p_on? 2:1; } @@ -832,13 +937,13 @@ history_expand(char *str, char **output) if (history_expansion_char == 0) { *output = strdup(str); - return(0); + return 0; } *output = NULL; if (str[0] == history_subst_char) { /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ - *output = malloc(strlen(str) + 4 + 1); + *output = el_malloc((strlen(str) + 4 + 1) * sizeof(**output)); if (*output == NULL) return 0; (*output)[0] = (*output)[1] = history_expansion_char; @@ -855,11 +960,12 @@ history_expand(char *str, char **output) #define ADD_STRING(what, len, fr) \ { \ if (idx + len + 1 > size) { \ - char *nresult = realloc(result, (size += len + 1));\ + char *nresult = el_realloc(result, \ + (size += len + 1) * sizeof(*nresult)); \ if (nresult == NULL) { \ - free(*output); \ + el_free(*output); \ if (/*CONSTCOND*/fr) \ - free(tmp); \ + el_free(tmp); \ return 0; \ } \ result = nresult; \ @@ -883,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) { @@ -926,7 +1033,7 @@ loop: ADD_STRING(tmp, len, 1); } if (tmp) { - free(tmp); + el_free(tmp); tmp = NULL; } i = j; @@ -943,10 +1050,10 @@ loop: ret = -1; #endif } - free(*output); + el_free(*output); *output = result; - return (ret); + return ret; } /* @@ -956,52 +1063,52 @@ char * history_arg_extract(int start, int end, const char *str) { size_t i, len, max; - char **arr, *result; + char **arr, *result = NULL; arr = history_tokenize(str); if (!arr) - return(NULL); - if (arr && *arr == NULL) { - free(arr); - return(NULL); - } + return NULL; + if (arr && *arr == NULL) + goto out; for (max = 0; arr[max]; max++) continue; max--; if (start == '$') - start = max; + start = (int)max; if (end == '$') - end = max; + end = (int)max; if (end < 0) - end = max + end + 1; + end = (int)max + end + 1; if (start < 0) start = end; - if (start < 0 || end < 0 || start > max || end > max || start > end) - return(NULL); + if (start < 0 || end < 0 || (size_t)start > max || + (size_t)end > max || start > end) + goto out; - for (i = start, len = 0; i <= end; i++) + for (i = (size_t)start, len = 0; i <= (size_t)end; i++) len += strlen(arr[i]) + 1; len++; - result = malloc(len); + result = el_malloc(len * sizeof(*result)); if (result == NULL) - return NULL; + goto out; - for (i = start, len = 0; i <= end; i++) { + for (i = (size_t)start, len = 0; i <= (size_t)end; i++) { (void)strcpy(result + len, arr[i]); len += strlen(arr[i]); - if (i < end) + if (i < (size_t)end) result[len++] = ' '; } - result[len] = 0; + result[len] = '\0'; +out: for (i = 0; arr[i]; i++) - free(arr[i]); - free(arr); + el_free(arr[i]); + el_free(arr); - return(result); + return result; } /* @@ -1038,19 +1145,19 @@ history_tokenize(const char *str) if (idx + 2 >= size) { char **nresult; size <<= 1; - nresult = realloc(result, size * sizeof(char *)); + nresult = el_realloc(result, (size_t)size * sizeof(*nresult)); if (nresult == NULL) { - free(result); + el_free(result); return NULL; } result = nresult; } - len = i - start; - temp = malloc(len + 1); + len = (size_t)i - (size_t)start; + temp = el_malloc((size_t)(len + 1) * sizeof(*temp)); if (temp == NULL) { for (i = 0; i < idx; i++) - free(result[i]); - free(result); + el_free(result[i]); + el_free(result); return NULL; } (void)strncpy(temp, &str[start], len); @@ -1060,7 +1167,7 @@ history_tokenize(const char *str) if (str[i]) i++; } - return (result); + return result; } @@ -1071,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); + } + } } @@ -1092,7 +1211,7 @@ unstifle_history(void) history(h, &ev, H_SETSIZE, INT_MAX); omax = max_input_history; max_input_history = INT_MAX; - return (omax); /* some value _must_ be returned */ + return omax; /* some value _must_ be returned */ } @@ -1101,7 +1220,144 @@ history_is_stifled(void) { /* cannot return true answer */ - return (max_input_history != INT_MAX); + return max_input_history != INT_MAX; +} + +static const char _history_tmp_template[] = "/tmp/.historyXXXXXX"; + +int +history_truncate_file (const char *filename, int nlines) +{ + int ret = 0; + FILE *fp, *tp; + char template[sizeof(_history_tmp_template)]; + char buf[4096]; + int fd; + char *cp; + off_t off; + int count = 0; + ssize_t left = 0; + + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + if ((fp = fopen(filename, "r+")) == NULL) + return errno; + strcpy(template, _history_tmp_template); + if ((fd = mkstemp(template)) == -1) { + ret = errno; + goto out1; + } + + if ((tp = fdopen(fd, "r+")) == NULL) { + close(fd); + ret = errno; + goto out2; + } + + for(;;) { + if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) { + if (ferror(fp)) { + ret = errno; + break; + } + if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) == + (off_t)-1) { + ret = errno; + break; + } + left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp); + if (ferror(fp)) { + ret = errno; + break; + } + if (left == 0) { + count--; + left = sizeof(buf); + } else if (fwrite(buf, (size_t)left, (size_t)1, tp) + != 1) { + ret = errno; + break; + } + fflush(tp); + break; + } + if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) { + ret = errno; + break; + } + count++; + } + if (ret) + goto out3; + cp = buf + left - 1; + if(*cp != '\n') + cp++; + for(;;) { + while (--cp >= buf) { + if (*cp == '\n') { + if (--nlines == 0) { + if (++cp >= buf + sizeof(buf)) { + count++; + cp = buf; + } + break; + } + } + } + if (nlines <= 0 || count == 0) + break; + count--; + if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) { + ret = errno; + break; + } + if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) { + if (ferror(tp)) { + ret = errno; + break; + } + ret = EAGAIN; + break; + } + cp = buf + sizeof(buf); + } + + if (ret || nlines > 0) + goto out3; + + if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) { + ret = errno; + goto out3; + } + + if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) == + (off_t)-1) { + ret = errno; + goto out3; + } + + for(;;) { + if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) { + if (ferror(fp)) + ret = errno; + break; + } + if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) { + ret = errno; + break; + } + } + fflush(fp); + if((off = ftello(fp)) > 0) + (void)ftruncate(fileno(fp), off); +out3: + fclose(tp); +out2: + unlink(template); +out1: + fclose(fp); + + return ret; } @@ -1115,7 +1371,10 @@ read_history(const char *filename) if (h == NULL || e == NULL) rl_initialize(); - return (history(h, &ev, H_LOAD, filename) == -1); + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + return history(h, &ev, H_LOAD, filename) == -1 ? + (errno ? errno : EINVAL) : 0; } @@ -1129,7 +1388,10 @@ write_history(const char *filename) if (h == NULL || e == NULL) rl_initialize(); - return (history(h, &ev, H_SAVE, filename) == -1); + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + return history(h, &ev, H_SAVE, filename) == -1 ? + (errno ? errno : EINVAL) : 0; } @@ -1148,26 +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); + return NULL; curr_num = ev.num; - /* start from most recent */ - if (history(h, &ev, H_FIRST) != 0) - return (NULL); /* error */ - - /* look backwards for event matching specified offset */ - if (history(h, &ev, H_NEXT_EVENT, num)) - 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; - she.data = NULL; /* restore pointer to where it was */ (void)history(h, &ev, H_SET, curr_num); - return (&she); + return &she; + +out: + /* restore pointer to where it was */ + (void)history(h, &ev, H_SET, curr_num); + return NULL; } @@ -1179,14 +1452,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; + current_history_valid = 1; - return (!(history_length > 0)); /* return 0 if all is okay */ + return !(history_length > 0); /* return 0 if all is okay */ } @@ -1196,23 +1473,75 @@ add_history(const char *line) HIST_ENTRY * remove_history(int num) { - static HIST_ENTRY she; + HIST_ENTRY *he; HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); - if (history(h, &ev, H_DEL, num) != 0) + if ((he = el_malloc(sizeof(*he))) == NULL) return NULL; - she.line = ev.str; - she.data = NULL; + if (history(h, &ev, H_DELDATA, num, &he->data) != 0) { + el_free(he); + return NULL; + } - return &she; + he->line = ev.str; + if (history(h, &ev, H_GETSIZE) == 0) + history_length = ev.num; + + return he; } /* + * replace the line and data of the num-th entry + */ +HIST_ENTRY * +replace_history_entry(int num, const char *line, histdata_t data) +{ + HIST_ENTRY *he; + HistEvent ev; + int curr_num; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* 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 */ + + if ((he = el_malloc(sizeof(*he))) == NULL) + return NULL; + + /* look forwards for event matching specified offset */ + if (history(h, &ev, H_NEXT_EVDATA, num, &he->data)) + goto out; + + he->line = strdup(ev.str); + if (he->line == NULL) + goto out; + + if (history(h, &ev, H_REPLACE, line, data)) + goto out; + + /* restore pointer to where it was */ + if (history(h, &ev, H_SET, curr_num)) + goto out; + + return he; +out: + el_free(he); + return NULL; +} + +/* * clear the history list - delete all entries */ void @@ -1220,7 +1549,12 @@ clear_history(void) { HistEvent ev; - history(h, &ev, H_CLEAR); + if (h == NULL || e == NULL) + rl_initialize(); + + (void)history(h, &ev, H_CLEAR); + history_length = 0; + current_history_valid = 1; } @@ -1234,15 +1568,15 @@ where_history(void) int curr_num, off; if (history(h, &ev, H_CURR) != 0) - return (0); + return 0; curr_num = ev.num; - history(h, &ev, H_FIRST); + (void)history(h, &ev, H_FIRST); off = 1; while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) off++; - return (off); + return off; } @@ -1253,7 +1587,7 @@ HIST_ENTRY * current_history(void) { - return (_move_history(H_CURR)); + return current_history_valid ? _move_history(H_CURR) : NULL; } @@ -1264,22 +1598,23 @@ int history_total_bytes(void) { HistEvent ev; - int curr_num, size; + int curr_num; + size_t size; if (history(h, &ev, H_CURR) != 0) - return (-1); + return -1; curr_num = ev.num; - history(h, &ev, H_FIRST); + (void)history(h, &ev, H_FIRST); size = 0; do - size += strlen(ev.str); + size += strlen(ev.str) * sizeof(*ev.str); while (history(h, &ev, H_NEXT) == 0); /* get to the same position as before */ history(h, &ev, H_PREV_EVENT, curr_num); - return (size); + return (int)size; } @@ -1292,28 +1627,38 @@ history_set_pos(int pos) HistEvent ev; int curr_num; - if (pos > history_length || pos < 0) - return (-1); + if (pos >= history_length || pos < 0) + return 0; - history(h, &ev, H_CURR); + (void)history(h, &ev, H_CURR); curr_num = ev.num; + current_history_valid = 1; - if (history(h, &ev, H_SET, pos)) { - history(h, &ev, H_SET, curr_num); - return(-1); + /* + * 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 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); } @@ -1323,8 +1668,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; } @@ -1339,17 +1688,17 @@ history_search(const char *str, int dire int curr_num; if (history(h, &ev, H_CURR) != 0) - return (-1); + return -1; curr_num = ev.num; for (;;) { if ((strp = strstr(ev.str, str)) != NULL) - return (int) (strp - ev.str); + return (int)(strp - ev.str); if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0) break; } - history(h, &ev, H_SET, curr_num); - return (-1); + (void)history(h, &ev, H_SET, curr_num); + return -1; } @@ -1361,7 +1710,8 @@ history_search_prefix(const char *str, i { HistEvent ev; - return (history(h, &ev, direction < 0? H_PREV_STR:H_NEXT_STR, str)); + return (history(h, &ev, direction < 0 ? + H_PREV_STR : H_NEXT_STR, str)); } @@ -1381,24 +1731,24 @@ history_search_pos(const char *str, pos = (pos > 0) ? 1 : -1; if (history(h, &ev, H_CURR) != 0) - return (-1); + return -1; curr_num = ev.num; - if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) - return (-1); - + if (!history_set_pos(off) || history(h, &ev, H_CURR) != 0) + return -1; for (;;) { if (strstr(ev.str, str)) - return (off); + return off; if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) break; } /* set "current" pointer back to previous state */ - history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); + (void)history(h, &ev, + pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); - return (-1); + return -1; } @@ -1421,17 +1771,20 @@ filename_completion_function(const char * a completion generator for usernames; returns _first_ username * which starts with supplied text * text contains a partial username preceded by random character - * (usually '~'); state is ignored - * it's callers responsibility to free returned value + * (usually '~'); state resets search from start (??? should we do that anyway) + * it's the caller's responsibility to free the returned value */ char * username_completion_function(const char *text, int state) { - struct passwd *pwd, pwres; +#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) + struct passwd pwres; char pwbuf[1024]; +#endif + struct passwd *pass = NULL; if (text[0] == '\0') - return (NULL); + return NULL; if (*text == '~') text++; @@ -1439,15 +1792,21 @@ username_completion_function(const char if (state == 0) setpwent(); - while (getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pwd) == 0 - && pwd != NULL && text[0] == pwd->pw_name[0] - && strcmp(text, pwd->pw_name) == 0); + while ( +#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) + getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pass) == 0 && pass != NULL +#else + (pass = getpwent()) != NULL +#endif + && text[0] == pass->pw_name[0] + && strcmp(text, pass->pw_name) == 0) + continue; - if (pwd == NULL) { + if (pass == NULL) { endpwent(); - return (NULL); + return NULL; } - return (strdup(pwd->pw_name)); + return strdup(pass->pw_name); } @@ -1471,7 +1830,7 @@ void rl_display_match_list(char **matches, int len, int max) { - fn_display_match_list(e, matches, len, max); + fn_display_match_list(e, matches, (size_t)len, (size_t)max); } static const char * @@ -1480,7 +1839,8 @@ _rl_completion_append_character_function __attribute__((__unused__))) { static char buf[2]; - buf[1] = rl_completion_append_character; + buf[0] = (char)rl_completion_append_character; + buf[1] = '\0'; return buf; } @@ -1492,6 +1852,9 @@ _rl_completion_append_character_function int rl_complete(int ignore __attribute__((__unused__)), int invoking_key) { + static ct_buffer_t wbreak_conv, sprefix_conv; + char *breakchars; + if (h == NULL || e == NULL) rl_initialize(); @@ -1500,17 +1863,28 @@ rl_complete(int ignore __attribute__((__ arr[0] = (char)invoking_key; arr[1] = '\0'; el_insertstr(e, arr); - return (CC_REFRESH); + 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, - rl_basic_word_break_characters, rl_special_prefixes, - _rl_completion_append_character_function, rl_completion_query_items, + ct_decode_string(rl_basic_word_break_characters, &wbreak_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, &rl_point, &rl_end); + + } @@ -1529,7 +1903,7 @@ _el_rl_complete(EditLine *el __attribute * bind key c to readline-type function func */ int -rl_bind_key(int c, int func(int, int)) +rl_bind_key(int c, rl_command_func_t *func) { int retval = -1; @@ -1541,7 +1915,7 @@ rl_bind_key(int c, int func(int, int)) e->el_map.key[c] = ED_INSERT; retval = 0; } - return (retval); + return retval; } @@ -1557,7 +1931,7 @@ rl_read_key(void) if (e == NULL || h == NULL) rl_initialize(); - return (el_getc(e, fooarr)); + return el_getc(e, fooarr); } @@ -1587,18 +1961,33 @@ rl_insert(int count, int c) rl_initialize(); /* XXX - int -> char conversion can lose on multichars */ - arr[0] = c; + arr[0] = (char)c; arr[1] = '\0'; for (; count > 0; count--) el_push(e, arr); - return (0); + return 0; +} + +int +rl_insert_text(const char *text) +{ + if (!text || *text == 0) + return 0; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (el_insertstr(e, text) < 0) + return 0; + return (int)strlen(text); } /*ARGSUSED*/ int -rl_newline(int count, int c) +rl_newline(int count __attribute__((__unused__)), + int c __attribute__((__unused__))) { /* * Readline-4.0 appears to ignore the args. @@ -1608,14 +1997,14 @@ rl_newline(int count, int c) /*ARGSUSED*/ static unsigned char -rl_bind_wrapper(EditLine *el, unsigned char c) +rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c) { if (map[c] == NULL) return CC_ERROR; _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) @@ -1625,20 +2014,20 @@ rl_bind_wrapper(EditLine *el, unsigned c } 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 (c >= sizeof(map) / sizeof(map[0]) || c < 0) + if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) return -1; 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); @@ -1663,30 +2052,29 @@ rl_callback_read_char() } } -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(); } - if (rl_prompt) - free(rl_prompt); - rl_prompt = prompt ? strdup(strchr(prompt, *prompt)) : NULL; + (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); + rl_linefunc = NULL; } void rl_redisplay(void) { char a[2]; - a[0] = e->el_tty.t_c[TS_IO][C_REPRINT]; + a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT]; a[1] = '\0'; el_push(e, a); } @@ -1695,7 +2083,7 @@ int rl_get_previous_history(int count, int key) { char a[2]; - a[0] = key; + a[0] = (char)key; a[1] = '\0'; while (count--) el_push(e, a); @@ -1704,7 +2092,7 @@ rl_get_previous_history(int count, int k void /*ARGSUSED*/ -rl_prep_terminal(int meta_flag) +rl_prep_terminal(int meta_flag __attribute__((__unused__))) { el_set(e, EL_PREP_TERM, 1); } @@ -1718,7 +2106,7 @@ rl_deprep_terminal(void) int rl_read_init_file(const char *s) { - return(el_source(e, s)); + return el_source(e, s); } int @@ -1732,7 +2120,7 @@ rl_parse_and_bind(const char *line) tok_str(tok, line, &argc, &argv); argc = el_parse(e, argc, argv); tok_end(tok); - return (argc ? 1 : 0); + return argc ? 1 : 0; } int @@ -1742,7 +2130,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 @@ -1750,40 +2138,43 @@ rl_stuff_char(int c) { char buf[2]; - buf[0] = c; + buf[0] = (char)c; buf[1] = '\0'; el_insertstr(e, buf); } static int -_rl_event_read_char(EditLine *el, char *cp) +_rl_event_read_char(EditLine *el, wchar_t *wc) { - int n, num_read = 0; + char ch; + int n; + ssize_t num_read = 0; - *cp = 0; + ch = '\0'; + *wc = L'\0'; while (rl_event_hook) { (*rl_event_hook)(); #if defined(FIONREAD) if (ioctl(el->el_infd, FIONREAD, &n) < 0) - return(-1); + return -1; if (n) - num_read = read(el->el_infd, cp, 1); + num_read = read(el->el_infd, &ch, (size_t)1); else num_read = 0; #elif defined(F_SETFL) && defined(O_NDELAY) if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0) - return(-1); + return -1; if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0) - return(-1); - num_read = read(el->el_infd, cp, 1); + return -1; + num_read = read(el->el_infd, &ch, 1); if (fcntl(el->el_infd, F_SETFL, n)) - return(-1); + return -1; #else /* not non-blocking, but what you gonna do? */ - num_read = read(el->el_infd, cp, 1); - return(-1); + num_read = read(el->el_infd, &ch, 1); + return -1; #endif if (num_read < 0 && errno == EAGAIN) @@ -1794,7 +2185,8 @@ _rl_event_read_char(EditLine *el, char * } if (!rl_event_hook) el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN); - return(num_read); + *wc = (wchar_t)ch; + return (int)num_read; } static void @@ -1802,6 +2194,186 @@ _rl_update_pos(void) { const LineInfo *li = el_line(e); - rl_point = li->cursor - li->buffer; - rl_end = li->lastchar - li->buffer; + rl_point = (int)(li->cursor - li->buffer); + rl_end = (int)(li->lastchar - li->buffer); +} + +void +rl_get_screen_size(int *rows, int *cols) +{ + if (rows) + el_get(e, EL_GETTC, "li", rows, (void *)0); + if (cols) + el_get(e, EL_GETTC, "co", cols, (void *)0); +} + +void +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, NULL); + (void)snprintf(buf, sizeof(buf), "%d", cols); + el_set(e, EL_SETTC, "co", buf, NULL); +} + +char ** +rl_completion_matches(const char *str, rl_compentry_func_t *fun) +{ + size_t len, max, i, j, min; + char **list, *match, *a, *b; + + len = 1; + max = 10; + if ((list = el_malloc(max * sizeof(*list))) == NULL) + return NULL; + + while ((match = (*fun)(str, (int)(len - 1))) != NULL) { + list[len++] = match; + if (len == max) { + char **nl; + max += 10; + if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL) + goto out; + list = nl; + } + } + if (len == 1) + goto out; + list[len] = NULL; + if (len == 2) { + if ((list[0] = strdup(list[1])) == NULL) + goto out; + return list; + } + qsort(&list[1], len - 1, sizeof(*list), + (int (*)(const void *, const void *)) strcmp); + 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++) + continue; + if (min > j) + min = j; + } + if (min == 0 && *str) { + if ((list[0] = strdup(str)) == NULL) + goto out; + } else { + if ((list[0] = el_malloc((min + 1) * sizeof(*list[0]))) == NULL) + goto out; + (void)memcpy(list[0], list[1], min); + list[0][min] = '\0'; + } + return list; + +out: + el_free(list); + return NULL; +} + +char * +rl_filename_completion_function (const char *text, int state) +{ + return fn_filename_completion_function(text, state); +} + +void +rl_forced_update_display(void) +{ + el_set(e, EL_REFRESH); +} + +int +_rl_abort_internal(void) +{ + el_beep(e); + longjmp(topbuf, 1); + /*NOTREACHED*/ +} + +int +_rl_qsort_string_compare(char **s1, char **s2) +{ + return strcoll(*s1, *s2); +} + +HISTORY_STATE * +history_get_history_state(void) +{ + HISTORY_STATE *hs; + + if ((hs = el_malloc(sizeof(*hs))) == NULL) + return NULL; + hs->length = history_length; + return hs; +} + +int +/*ARGSUSED*/ +rl_kill_text(int from __attribute__((__unused__)), + int to __attribute__((__unused__))) +{ + return 0; +} + +Keymap +rl_make_bare_keymap(void) +{ + return NULL; +} + +Keymap +rl_get_keymap(void) +{ + return NULL; +} + +void +/*ARGSUSED*/ +rl_set_keymap(Keymap k __attribute__((__unused__))) +{ +} + +int +/*ARGSUSED*/ +rl_generic_bind(int type __attribute__((__unused__)), + const char * keyseq __attribute__((__unused__)), + const char * data __attribute__((__unused__)), + Keymap k __attribute__((__unused__))) +{ + return 0; +} + +int +/*ARGSUSED*/ +rl_bind_key_in_map(int key __attribute__((__unused__)), + rl_command_func_t *fun __attribute__((__unused__)), + Keymap k __attribute__((__unused__))) +{ + return 0; +} + +/* unsupported, but needed by python */ +void +rl_cleanup_after_signal(void) +{ +} + +int +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; }