git: 2265349803d0 - stable/13 - libedit: import snapshot 2021-09-10

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Wed, 12 Oct 2022 08:49:16 UTC
The branch stable/13 has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=2265349803d08ca0fe63a02b88affb70d0f4bbac

commit 2265349803d08ca0fe63a02b88affb70d0f4bbac
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2021-09-22 08:19:43 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2022-10-12 08:46:08 +0000

    libedit: import snapshot 2021-09-10
    
    (cherry picked from commit 91f764172e197c82efa97a66cfbc13d2c744b02b)
---
 contrib/libedit/ChangeLog           |   6 +
 contrib/libedit/TEST/fuzz1.c        |  63 +++++++
 contrib/libedit/editline.3          |  18 +-
 contrib/libedit/el.c                |  17 +-
 contrib/libedit/el.h                |  15 +-
 contrib/libedit/eln.c               |   6 +-
 contrib/libedit/filecomplete.c      |   7 +-
 contrib/libedit/histedit.h          |   3 +-
 contrib/libedit/map.c               |   8 +-
 contrib/libedit/read.c              |   6 +-
 contrib/libedit/readline.c          | 366 ++++++++++++++++++++++--------------
 contrib/libedit/readline/readline.h |  19 +-
 contrib/libedit/refresh.c           |  20 +-
 contrib/libedit/terminal.c          |   7 +-
 contrib/libedit/tty.c               |  12 +-
 contrib/libedit/tty.h               |   4 +-
 contrib/libedit/vi.c                |   9 +-
 17 files changed, 396 insertions(+), 190 deletions(-)

diff --git a/contrib/libedit/ChangeLog b/contrib/libedit/ChangeLog
new file mode 100644
index 000000000000..35c9205a43d8
--- /dev/null
+++ b/contrib/libedit/ChangeLog
@@ -0,0 +1,6 @@
+2021-08-29	Christos Zoulas	<christos@netbsd.org>
+
+	Change default mappings:
+	    map ^W to ed-delete-prev-word and not kill-region
+	    map ^R to em-inc-search-next and not redisplay
+
diff --git a/contrib/libedit/TEST/fuzz1.c b/contrib/libedit/TEST/fuzz1.c
new file mode 100644
index 000000000000..e31d26538885
--- /dev/null
+++ b/contrib/libedit/TEST/fuzz1.c
@@ -0,0 +1,63 @@
+/*
+ * build:
+ *   CC=clang CXX=clang++ CFLAGS="-fsanitize=address,fuzzer-no-link -g" \
+ *   	CXXFLAGS="-fsanitize=address,fuzzer-no-link -g" ./configure && make
+ * run:
+ *   LD_LIBRARY_PATH=../src/.libs/ .libs/fuzz1 -max_len=32 \
+ *	-use_value_profile=1 -only_ascii=1
+ */
+#include <readline/readline.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int init = 0;
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  if (!Size)
+    return 0;
+
+  if (!init) {
+    setlocale(LC_CTYPE, "");
+    stifle_history(7);
+    init = 1;
+  }
+
+  clear_history();
+
+  size_t lasti = 0;
+
+  for (size_t i = 0;; ++i) {
+    if (i == Size || Data[i] == '\n') {
+      if (i - lasti) {
+        char *s = (char *)malloc(i - lasti + 1);
+        memcpy(s, &Data[lasti], i - lasti);
+        s[i - lasti] = '\0';
+
+        char *expansion;
+        int result;
+
+#ifdef DEBUG
+        fprintf(stderr, "Calling history_expand: >%s<\n", s);
+#endif
+        result = history_expand(s, &expansion);
+
+        if (result < 0 || result == 2) {
+          /* Errors ignored */
+        } else {
+          add_history(expansion);
+        }
+        free(expansion);
+        free(s);
+      }
+      lasti = i + 1;
+    }
+
+    if (i == Size)
+      break;
+  }
+
+  return 0;
+}
diff --git a/contrib/libedit/editline.3 b/contrib/libedit/editline.3
index feec9a6cac25..f104dcc07c7a 100644
--- a/contrib/libedit/editline.3
+++ b/contrib/libedit/editline.3
@@ -1,4 +1,4 @@
-.\"	$NetBSD: editline.3,v 1.99 2018/11/18 17:09:39 christos Exp $
+.\"	$NetBSD: editline.3,v 1.101 2021/08/15 10:12:54 wiz Exp $
 .\"
 .\" Copyright (c) 1997-2014 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -26,7 +26,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 9, 2018
+.Dd August 15, 2021
 .Dt EDITLINE 3
 .Os
 .Sh NAME
@@ -507,6 +507,16 @@ unbuffered mode is disabled (the default).
 In unbuffered mode,
 .Fn el_gets
 will return immediately after processing a single character.
+.It Dv EL_SAFEREAD , Fa "int flag"
+If the
+.Fa flag
+argument is non-zero, then
+.Nm editline
+attempts to recover from read errors, ignoring the first interrrupted
+error, and trying to reset the input file descriptor to reset non-blocking I/O.
+This is disabled by default, and desirable only when
+.Nm editline
+is used in shell-like applications.
 .It Dv EL_GETCFN , Fa "el_rfunc_t f"
 Whenever reading a character, use the function
 .Bd -ragged -offset indent -compact
@@ -634,6 +644,10 @@ call.
 Set
 .Fa c
 to non-zero if unbuffered mode is enabled.
+.It Dv EL_SAFEREAD , Fa "int *c"
+Set
+.Fa c
+to non-zero if safe read is set.
 .It Dv EL_GETFP , Fa "int fd", Fa "FILE **fp"
 Set
 .Fa fp
diff --git a/contrib/libedit/el.c b/contrib/libedit/el.c
index 9ae7af3533d3..47b76d7a5302 100644
--- a/contrib/libedit/el.c
+++ b/contrib/libedit/el.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: el.c,v 1.99 2019/07/23 10:18:52 christos Exp $	*/
+/*	$NetBSD: el.c,v 1.100 2021/08/15 10:08:41 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)el.c	8.2 (Berkeley) 1/3/94";
 #else
-__RCSID("$NetBSD: el.c,v 1.99 2019/07/23 10:18:52 christos Exp $");
+__RCSID("$NetBSD: el.c,v 1.100 2021/08/15 10:08:41 christos Exp $");
 #endif
 #endif /* not lint && not SCCSID */
 
@@ -300,6 +300,14 @@ el_wset(EditLine *el, int op, ...)
 		break;
 	}
 
+	case EL_SAFEREAD:
+		if (va_arg(ap, int))
+			el->el_flags |= FIXIO;
+		else
+			el->el_flags &= ~FIXIO;
+		rv = 0;
+		break;
+
 	case EL_EDITMODE:
 		if (va_arg(ap, int))
 			el->el_flags &= ~EDIT_DISABLED;
@@ -429,6 +437,11 @@ el_wget(EditLine *el, int op, ...)
 		rv = 0;
 		break;
 
+	case EL_SAFEREAD:
+		*va_arg(ap, int *) = (el->el_flags & FIXIO);
+		rv = 0;
+		break;
+
 	case EL_TERMINAL:
 		terminal_get(el, va_arg(ap, const char **));
 		rv = 0;
diff --git a/contrib/libedit/el.h b/contrib/libedit/el.h
index b0d174eb62f6..757137839eb8 100644
--- a/contrib/libedit/el.h
+++ b/contrib/libedit/el.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: el.h,v 1.45 2019/07/23 10:18:52 christos Exp $	*/
+/*	$NetBSD: el.h,v 1.46 2021/08/15 10:08:41 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -51,12 +51,13 @@
 
 #define	EL_BUFSIZ	((size_t)1024)	/* Maximum line size		*/
 
-#define	HANDLE_SIGNALS	0x01
-#define	NO_TTY		0x02
-#define	EDIT_DISABLED	0x04
-#define	UNBUFFERED	0x08
-#define	NARROW_HISTORY	0x40
-#define	NO_RESET	0x80
+#define	HANDLE_SIGNALS	0x001
+#define	NO_TTY		0x002
+#define	EDIT_DISABLED	0x004
+#define	UNBUFFERED	0x008
+#define	NARROW_HISTORY	0x040
+#define	NO_RESET	0x080
+#define	FIXIO		0x100
 
 typedef unsigned char el_action_t;	/* Index to command array	*/
 
diff --git a/contrib/libedit/eln.c b/contrib/libedit/eln.c
index e980bc5f9d2f..f432a2187c0d 100644
--- a/contrib/libedit/eln.c
+++ b/contrib/libedit/eln.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: eln.c,v 1.35 2019/04/26 16:56:57 christos Exp $	*/
+/*	$NetBSD: eln.c,v 1.36 2021/08/15 10:08:41 christos Exp $	*/
 
 /*-
  * Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 #include "config.h"
 #if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: eln.c,v 1.35 2019/04/26 16:56:57 christos Exp $");
+__RCSID("$NetBSD: eln.c,v 1.36 2021/08/15 10:08:41 christos Exp $");
 #endif /* not lint && not SCCSID */
 
 #include <errno.h>
@@ -153,6 +153,7 @@ el_set(EditLine *el, int op, ...)
 
 	case EL_SIGNAL:         /* int */
 	case EL_EDITMODE:
+	case EL_SAFEREAD:
 	case EL_UNBUFFERED:
 	case EL_PREP_TERM:
 		ret = el_wset(el, op, va_arg(ap, int));
@@ -315,6 +316,7 @@ el_get(EditLine *el, int op, ...)
 
 	case EL_SIGNAL:         /* int * */
 	case EL_EDITMODE:
+	case EL_SAFEREAD:
 	case EL_UNBUFFERED:
 	case EL_PREP_TERM:
 		ret = el_wget(el, op, va_arg(ap, int *));
diff --git a/contrib/libedit/filecomplete.c b/contrib/libedit/filecomplete.c
index 8279d7ff82b6..6dc7cff1d055 100644
--- a/contrib/libedit/filecomplete.c
+++ b/contrib/libedit/filecomplete.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: filecomplete.c,v 1.67 2021/03/28 13:39:39 christos Exp $	*/
+/*	$NetBSD: filecomplete.c,v 1.68 2021/05/05 14:49:59 christos Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include "config.h"
 #if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: filecomplete.c,v 1.67 2021/03/28 13:39:39 christos Exp $");
+__RCSID("$NetBSD: filecomplete.c,v 1.68 2021/05/05 14:49:59 christos Exp $");
 #endif /* not lint && not SCCSID */
 
 #include <sys/types.h>
@@ -727,7 +727,7 @@ fn_complete2(EditLine *el,
 		else
 			completion = strdup(matches[0]);
 		if (completion == NULL)
-			goto out;
+			goto out2;
 
 		/*
 		 * Replace the completed string with the common part of
@@ -810,6 +810,7 @@ fn_complete2(EditLine *el,
 	}
 
 	/* free elements of array and the array itself */
+out2:
 	for (i = 0; matches[i]; i++)
 		el_free(matches[i]);
 	el_free(matches);
diff --git a/contrib/libedit/histedit.h b/contrib/libedit/histedit.h
index 56bb3ef9ea6a..511750e0137e 100644
--- a/contrib/libedit/histedit.h
+++ b/contrib/libedit/histedit.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: histedit.h,v 1.57 2017/09/01 10:19:10 christos Exp $	*/
+/*	$NetBSD: histedit.h,v 1.58 2021/08/15 10:08:41 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -158,6 +158,7 @@ unsigned char	_el_fn_sh_complete(EditLine *, int);
 #define	EL_RPROMPT_ESC	22	/* , prompt_func, Char);	      set/get */
 #define	EL_RESIZE	23	/* , el_zfunc_t, void *);	      set     */
 #define	EL_ALIAS_TEXT	24	/* , el_afunc_t, void *);	      set     */
+#define	EL_SAFEREAD	25	/* , int);			      set/get */
 
 #define	EL_BUILTIN_GETCFN	(NULL)
 
diff --git a/contrib/libedit/map.c b/contrib/libedit/map.c
index 46bf6f1c9472..321bb3539222 100644
--- a/contrib/libedit/map.c
+++ b/contrib/libedit/map.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: map.c,v 1.53 2020/03/30 06:54:37 ryo Exp $	*/
+/*	$NetBSD: map.c,v 1.54 2021/08/29 09:41:59 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)map.c	8.1 (Berkeley) 6/4/93";
 #else
-__RCSID("$NetBSD: map.c,v 1.53 2020/03/30 06:54:37 ryo Exp $");
+__RCSID("$NetBSD: map.c,v 1.54 2021/08/29 09:41:59 christos Exp $");
 #endif
 #endif /* not lint && not SCCSID */
 
@@ -85,12 +85,12 @@ static const el_action_t  el_map_emacs[] = {
 	/*  15 */	ED_IGNORE,		/* ^O */
 	/*  16 */	ED_PREV_HISTORY,	/* ^P */
 	/*  17 */	ED_IGNORE,		/* ^Q */
-	/*  18 */	ED_REDISPLAY,		/* ^R */
+	/*  18 */	EM_INC_SEARCH_PREV,	/* ^R */
 	/*  19 */	ED_IGNORE,		/* ^S */
 	/*  20 */	ED_TRANSPOSE_CHARS,	/* ^T */
 	/*  21 */	EM_KILL_LINE,		/* ^U */
 	/*  22 */	ED_QUOTED_INSERT,	/* ^V */
-	/*  23 */	EM_KILL_REGION,		/* ^W */
+	/*  23 */	ED_DELETE_PREV_WORD,	/* ^W */
 	/*  24 */	ED_SEQUENCE_LEAD_IN,	/* ^X */
 	/*  25 */	EM_YANK,		/* ^Y */
 	/*  26 */	ED_IGNORE,		/* ^Z */
diff --git a/contrib/libedit/read.c b/contrib/libedit/read.c
index d2095a244efa..a49a304de971 100644
--- a/contrib/libedit/read.c
+++ b/contrib/libedit/read.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: read.c,v 1.106 2019/07/23 10:18:52 christos Exp $	*/
+/*	$NetBSD: read.c,v 1.107 2021/08/15 10:08:41 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)read.c	8.1 (Berkeley) 6/4/93";
 #else
-__RCSID("$NetBSD: read.c,v 1.106 2019/07/23 10:18:52 christos Exp $");
+__RCSID("$NetBSD: read.c,v 1.107 2021/08/15 10:08:41 christos Exp $");
 #endif
 #endif /* not lint && not SCCSID */
 
@@ -277,7 +277,7 @@ static int
 read_char(EditLine *el, wchar_t *cp)
 {
 	ssize_t num_read;
-	int tried = 0;
+	int tried = (el->el_flags & FIXIO) == 0;
 	char cbuf[MB_LEN_MAX];
 	size_t cbp = 0;
 	int save_errno = errno;
diff --git a/contrib/libedit/readline.c b/contrib/libedit/readline.c
index 792dddbaf70b..c12eb7481bf4 100644
--- a/contrib/libedit/readline.c
+++ b/contrib/libedit/readline.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: readline.c,v 1.159 2019/10/09 14:31:07 christos Exp $	*/
+/*	$NetBSD: readline.c,v 1.168 2021/09/10 18:51:36 rillig Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include "config.h"
 #if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: readline.c,v 1.159 2019/10/09 14:31:07 christos Exp $");
+__RCSID("$NetBSD: readline.c,v 1.168 2021/09/10 18:51:36 rillig Exp $");
 #endif /* not lint && not SCCSID */
 
 #include <sys/types.h>
@@ -108,6 +108,7 @@ int rl_attempted_completion_over = 0;
 const char *rl_basic_word_break_characters = break_chars;
 char *rl_completer_word_break_characters = NULL;
 const char *rl_completer_quote_characters = NULL;
+const char *rl_basic_quote_characters = "\"'";
 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;
@@ -122,11 +123,19 @@ int readline_echoing_p = 1;
 int _rl_print_completions_horizontally = 0;
 VFunction *rl_redisplay_function = NULL;
 Function *rl_startup_hook = NULL;
-int rl_did_startup_hook = 0;
 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;
+unsigned long rl_readline_state;
+int _rl_complete_mark_directories;
+rl_icppfunc_t *rl_directory_completion_hook;
+int rl_completion_suppress_append;
+int rl_sort_completion_matches;
+int _rl_completion_prefix_display_length;
+int _rl_echoing_p;
+int history_max_entries;
+char *rl_display_prompt;
 
 /*
  * The current prompt string.
@@ -208,11 +217,10 @@ static void
 _resize_fun(EditLine *el, void *a)
 {
 	const LineInfo *li;
-	char **ap = a;
+	const 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);
+	*ap = li->buffer;
 }
 
 static const char *
@@ -416,8 +424,7 @@ readline(const char *p)
 
 	if (e == NULL || h == NULL)
 		rl_initialize();
-	if (rl_did_startup_hook == 0 && rl_startup_hook) {
-		rl_did_startup_hook = 1;
+	if (rl_startup_hook) {
 		(*rl_startup_hook)(NULL, 0);
 	}
 	tty_init(e);
@@ -451,14 +458,10 @@ readline(const char *p)
 	ret = el_gets(e, &count);
 
 	if (ret && count > 0) {
-		int lastidx;
-
 		buf = strdup(ret);
 		if (buf == NULL)
 			goto out;
-		lastidx = count - 1;
-		if (buf[lastidx] == '\n')
-			buf[lastidx] = '\0';
+		buf[strcspn(buf, "\n")] = '\0';
 	} else
 		buf = NULL;
 
@@ -593,7 +596,7 @@ get_history_event(const char *cmd, int *cindex, int qchar)
 		if (sub && cmd[idx] == '?')
 			break;
 		if (!sub && (cmd[idx] == ':' || cmd[idx] == ' '
-				    || cmd[idx] == '\t' || cmd[idx] == qchar))
+		    || cmd[idx] == '\t' || cmd[idx] == qchar))
 			break;
 		idx++;
 	}
@@ -619,8 +622,7 @@ get_history_event(const char *cmd, int *cindex, int qchar)
 
 	if (sub) {
 		if (pat != last_search_pat) {
-			if (last_search_pat)
-				el_free(last_search_pat);
+			el_free(last_search_pat);
 			last_search_pat = pat;
 		}
 		ret = history_search(pat, -1);
@@ -637,9 +639,8 @@ get_history_event(const char *cmd, int *cindex, int qchar)
 	}
 
 	if (sub && len) {
-		if (last_search_match && last_search_match != pat)
-			el_free(last_search_match);
-		last_search_match = pat;
+		el_free(last_search_match);
+		last_search_match = strdup(pat);
 	}
 
 	if (pat != last_search_pat)
@@ -656,6 +657,123 @@ get_history_event(const char *cmd, int *cindex, int qchar)
 	return rptr;
 }
 
+static int
+getfrom(const char **cmdp, char **fromp, const char *search, int delim)
+{
+	size_t size = 16;
+	size_t len = 0;
+	const char *cmd = *cmdp;
+	char *what = el_realloc(*fromp, size * sizeof(*what));
+	if (what == NULL){
+		el_free(*fromp);
+		*fromp = NULL;
+		return 0;
+	}
+	for (; *cmd && *cmd != delim; cmd++) {
+		if (*cmd == '\\' && cmd[1] == delim)
+			cmd++;
+		if (len - 1 >= size) {
+			char *nwhat;
+			nwhat = el_realloc(what, (size <<= 1) * sizeof(*nwhat));
+			if (nwhat == NULL) {
+				el_free(what);
+				el_free(*fromp);
+				*cmdp = cmd;
+				*fromp = NULL;
+				return 0;
+			}
+			what = nwhat;
+		}
+		what[len++] = *cmd;
+	}
+	what[len] = '\0';
+	*fromp = what;
+	*cmdp = cmd;
+	if (*what == '\0') {
+		el_free(what);
+		if (search) {
+			*fromp = strdup(search);
+			if (*fromp == NULL) {
+				return 0;
+			}
+		} else {
+			*fromp = NULL;
+			return -1;
+		}
+	}
+	if (!*cmd) {
+		el_free(what);
+		*fromp = NULL;
+		return -1;
+	}
+
+	cmd++;	/* shift after delim */
+	*cmdp = cmd;
+
+	if (!*cmd) {
+		el_free(what);
+		*fromp = NULL;
+		return -1;
+	}
+	return 1;
+}
+
+static int
+getto(const char **cmdp, char **top, const char *from, int delim)
+{
+	size_t size = 16;
+	size_t len = 0;
+	size_t from_len = strlen(from);
+	const char *cmd = *cmdp;
+	char *with = el_realloc(*top, size * sizeof(*with));
+	*top = NULL;
+	if (with == NULL)
+		goto out;
+
+	for (; *cmd && *cmd != delim; cmd++) {
+		if (len + from_len + 1 >= size) {
+			char *nwith;
+			size += from_len + 1;
+			nwith = el_realloc(with, size * sizeof(*nwith));
+			if (nwith == NULL)
+				goto out;
+			with = nwith;
+		}
+		if (*cmd == '&') {
+			/* safe */
+			strcpy(&with[len], from);
+			len += from_len;
+			continue;
+		}
+		if (*cmd == '\\' && (*(cmd + 1) == delim || *(cmd + 1) == '&'))
+			cmd++;
+		with[len++] = *cmd;
+	}
+	if (!*cmd)
+		goto out;
+	with[len] = '\0';
+	*top = with;
+	*cmdp = cmd;
+	return 1;
+out:
+	el_free(with);
+	el_free(*top);
+	*top = NULL;
+	*cmdp = cmd;
+	return -1;
+}
+
+static void
+replace(char **tmp, int c)
+{
+	char *aptr;
+	if ((aptr = strrchr(*tmp, c)) == NULL)
+		return;
+	aptr = strdup(aptr + 1); // XXX: check
+	el_free(*tmp);
+	*tmp = aptr;
+}
+
 /*
  * the real function doing history expansion - takes as argument command
  * to do and data upon which the command should be executed
@@ -670,11 +788,11 @@ static int
 _history_expand_command(const char *command, size_t offs, size_t cmdlen,
     char **result)
 {
-	char *tmp, *search = NULL, *aptr;
+	char *tmp, *search = NULL, *aptr, delim;
 	const char *ptr, *cmd;
 	static char *from = NULL, *to = NULL;
 	int start, end, idx, has_mods = 0;
-	int p_on = 0, g_on = 0;
+	int p_on = 0, g_on = 0, ev;
 
 	*result = NULL;
 	aptr = NULL;
@@ -706,7 +824,8 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
 		} else {
 			int	qchar;
 
-			qchar = (offs > 0 && command[offs - 1] == '"')? '"':0;
+			qchar = (offs > 0 && command[offs - 1] == '"')
+			    ? '"' : '\0';
 			ptr = get_history_event(command + offs, &idx, qchar);
 		}
 		has_mods = command[offs + (size_t)idx] == ':';
@@ -729,7 +848,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
 	/* Now parse any word designators */
 
 	if (*cmd == '%')	/* last word matched by ?pat? */
-		tmp = strdup(last_search_match? last_search_match:"");
+		tmp = strdup(last_search_match ? last_search_match : "");
 	else if (strchr("^*$-0123456789", *cmd)) {
 		start = end = -1;
 		if (*cmd == '^')
@@ -738,7 +857,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
 			start = -1, cmd++;
 		else if (*cmd == '*')
 			start = 1, cmd++;
-	       else if (*cmd == '-' || isdigit((unsigned char) *cmd)) {
+		else if (*cmd == '-' || isdigit((unsigned char) *cmd)) {
 			start = 0;
 			while (*cmd && '0' <= *cmd && *cmd <= '9')
 				start = start * 10 + *cmd++ - '0';
@@ -781,132 +900,58 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
 	}
 
 	for (; *cmd; cmd++) {
-		if (*cmd == ':')
+		switch (*cmd) {
+		case ':':
 			continue;
-		else if (*cmd == 'h') {		/* remove trailing path */
+		case 'h':	/* remove trailing path */
 			if ((aptr = strrchr(tmp, '/')) != NULL)
 				*aptr = '\0';
-		} else if (*cmd == 't') {	/* remove leading path */
-			if ((aptr = strrchr(tmp, '/')) != NULL) {
-				aptr = strdup(aptr + 1);
-				el_free(tmp);
-				tmp = aptr;
-			}
-		} else if (*cmd == 'r') {	/* remove trailing suffix */
+			continue;
+		case 't':	/* remove leading path */
+			replace(&tmp, '/');
+			continue;
+		case 'r':	/* remove trailing suffix */
 			if ((aptr = strrchr(tmp, '.')) != NULL)
 				*aptr = '\0';
-		} else if (*cmd == 'e') {	/* remove all but suffix */
-			if ((aptr = strrchr(tmp, '.')) != NULL) {
-				aptr = strdup(aptr);
-				el_free(tmp);
-				tmp = aptr;
-			}
-		} else if (*cmd == 'p')		/* print only */
+			continue;
+		case 'e':	/* remove all but suffix */
+			replace(&tmp, '.');
+			continue;
+		case 'p':	/* print only */
 			p_on = 1;
-		else if (*cmd == 'g')
+			continue;
+		case 'g':
 			g_on = 2;
-		else if (*cmd == 's' || *cmd == '&') {
-			char *what, *with, delim;
-			size_t len, from_len;
-			size_t size;
-
-			if (*cmd == '&' && (from == NULL || to == NULL))
+			continue;
+		case '&':
+			if (from == NULL || to == NULL)
 				continue;
-			else if (*cmd == 's') {
-				delim = *(++cmd), cmd++;
-				size = 16;
-				what = el_realloc(from, size * sizeof(*what));
-				if (what == NULL) {
-					el_free(from);
-					el_free(tmp);
-					return 0;
-				}
-				len = 0;
-				for (; *cmd && *cmd != delim; cmd++) {
-					if (*cmd == '\\' && cmd[1] == delim)
-						cmd++;
-					if (len >= size) {
-						char *nwhat;
-						nwhat = el_realloc(what,
-						    (size <<= 1) *
-						    sizeof(*nwhat));
-						if (nwhat == NULL) {
-							el_free(what);
-							el_free(tmp);
-							return 0;
-						}
-						what = nwhat;
-					}
-					what[len++] = *cmd;
-				}
-				what[len] = '\0';
-				from = what;
-				if (*what == '\0') {
-					el_free(what);
-					if (search) {
-						from = strdup(search);
-						if (from == NULL) {
-							el_free(tmp);
-							return 0;
-						}
-					} else {
-						from = NULL;
-						el_free(tmp);
-						return -1;
-					}
-				}
-				cmd++;	/* shift after delim */
-				if (!*cmd)
-					continue;
-
-				size = 16;
-				with = el_realloc(to, size * sizeof(*with));
-				if (with == NULL) {
-					el_free(to);
-					el_free(tmp);
-					return -1;
-				}
-				len = 0;
-				from_len = strlen(from);
-				for (; *cmd && *cmd != delim; cmd++) {
-					if (len + from_len + 1 >= size) {
-						char *nwith;
-						size += from_len + 1;
-						nwith = el_realloc(with,
-						    size * sizeof(*nwith));
-						if (nwith == NULL) {
-							el_free(with);
-							el_free(tmp);
-							return -1;
-						}
-						with = nwith;
-					}
-					if (*cmd == '&') {
-						/* safe */
-						(void)strcpy(&with[len], from);
-						len += from_len;
-						continue;
-					}
-					if (*cmd == '\\'
-					    && (*(cmd + 1) == delim
-						|| *(cmd + 1) == '&'))
-						cmd++;
-					with[len++] = *cmd;
-				}
-				with[len] = '\0';
-				to = with;
-			}
-
+			/*FALLTHROUGH*/
+		case 's':
+			ev = -1;
+			delim = *++cmd;
+			if (delim == '\0' || *++cmd == '\0')
+				goto out;
+			if ((ev = getfrom(&cmd, &from, search, delim)) != 1)
+				goto out;
+			if ((ev = getto(&cmd, &to, from, delim)) != 1)
+				goto out;
 			aptr = _rl_compat_sub(tmp, from, to, g_on);
 			if (aptr) {
 				el_free(tmp);
 				tmp = aptr;
 			}
 			g_on = 0;
+			cmd--;
+			continue;
 		}
 	}
 	*result = tmp;
-	return p_on? 2:1;
+	return p_on ? 2 : 1;
+out:
+	el_free(tmp);
+	return ev;
+	
 }
 
 
@@ -952,8 +997,7 @@ history_expand(char *str, char **output)
 			    (size += len + 1) * sizeof(*nresult));	\
 			if (nresult == NULL) {				\
 				el_free(*output);			\
-				if (/*CONSTCOND*/fr)			\
-					el_free(tmp);			\
+				el_free(fr);				\
 				return 0;				\
 			}						\
 			result = nresult;				\
@@ -1003,11 +1047,11 @@ loop:
 			goto loop;
 		}
 		len = i - start;
-		ADD_STRING(&str[start], len, 0);
+		ADD_STRING(&str[start], len, NULL);
 
 		if (str[i] == '\0' || str[i] != history_expansion_char) {
 			len = j - i;
-			ADD_STRING(&str[i], len, 0);
+			ADD_STRING(&str[i], len, NULL);
 			if (start == 0)
 				ret = 0;
 			else
@@ -1017,7 +1061,7 @@ loop:
 		ret = _history_expand_command (str, i, (j - i), &tmp);
 		if (ret > 0 && tmp) {
 			len = strlen(tmp);
-			ADD_STRING(tmp, len, 1);
+			ADD_STRING(tmp, len, tmp);
 		}
 		if (tmp) {
 			el_free(tmp);
@@ -1359,7 +1403,7 @@ read_history(const char *filename)
 		return errno;
 	errno = 0;
 	if (history(h, &ev, H_LOAD, filename) == -1)
-	    return errno ? errno : EINVAL;
+		return errno ? errno : EINVAL;
 	if (history(h, &ev, H_GETSIZE) == 0)
 		history_length = ev.num;
 	if (history_length < 0)
@@ -2037,7 +2081,7 @@ static unsigned char
 rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c)
 {
 	if (map[c] == NULL)
-	    return CC_ERROR;
+		return CC_ERROR;
 
 	_rl_update_pos();
 
@@ -2070,6 +2114,7 @@ rl_callback_read_char(void)
 	const char *buf = el_gets(e, &count);
 	char *wbuf;
 
+	el_set(e, EL_UNBUFFERED, 1);
 	if (buf == NULL || count-- <= 0)
 		return;
 	if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF])
@@ -2085,7 +2130,6 @@ rl_callback_read_char(void)
 		} else
 			wbuf = NULL;
 		(*(void (*)(const char *))rl_linefunc)(wbuf);
-		el_set(e, EL_UNBUFFERED, 1);
 	}
 }
 
@@ -2103,9 +2147,8 @@ rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc)
 void
 rl_callback_handler_remove(void)
 {
+	el_set(e, EL_UNBUFFERED, 0);
 	rl_linefunc = NULL;
-	el_end(e);
-	e = NULL;
 }
 
 void
@@ -2439,3 +2482,40 @@ rl_echo_signal_char(int sig)
 		return;
 	re_putc(e, c, 0);
 }
+
+int
+rl_crlf(void)
+{
+	re_putc(e, '\n', 0);
+	return 0;
+}
+
+int
+rl_ding(void)
+{
+	re_putc(e, '\a', 0);
+	return 0;
+}
+
+int
+rl_abort(int count, int key)
+{
+	return count && key ? 0 : 0;
+}
+
+int
+rl_set_keymap_name(const char *name, Keymap k)
+{
+	return name && k ? 0 : 0;
+}
+
+histdata_t
+free_history_entry(HIST_ENTRY *he)
+{
+	return he ? NULL : NULL;
+}
+
+void
+_rl_erase_entire_line(void)
+{
+}
diff --git a/contrib/libedit/readline/readline.h b/contrib/libedit/readline/readline.h
index 7e53f417bc25..e9f941aeb249 100644
--- a/contrib/libedit/readline/readline.h
+++ b/contrib/libedit/readline/readline.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: readline.h,v 1.46 2019/06/07 15:19:29 christos Exp $	*/
+/*	$NetBSD: readline.h,v 1.47 2021/08/21 12:34:59 christos Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -45,6 +45,7 @@ typedef char	**rl_completion_func_t(const char *, int, int);
 typedef char     *rl_compentry_func_t(const char *, int);
 typedef int	  rl_command_func_t(int, int);
 typedef int	  rl_hook_func_t(void);
+typedef int       rl_icppfunc_t(char **);
 
 /* only supports length */
 typedef struct {
@@ -106,6 +107,7 @@ extern char		*rl_line_buffer;
 extern int		 rl_point, rl_end;
 extern int		 history_base, history_length;
 extern int		 max_input_history;
+extern const char	*rl_basic_quote_characters;
 extern const char	*rl_basic_word_break_characters;
 extern char		*rl_completer_word_break_characters;
 extern const char	*rl_completer_quote_characters;
@@ -127,6 +129,7 @@ extern int		rl_done;
*** 254 LINES SKIPPED ***