diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..73a7256
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required (VERSION 3.5)
+project (mg)
+
+set (MG_SRC autoexec.c basic.c bell.c buffer.c cinfo.c dir.c display.c
+	echo.c extend.c file.c fileio.c funmap.c help.c kbd.c keymap.c
+	line.c macro.c main.c match.c modes.c paragraph.c
+	re_search.c region.c search.c spawn.c tty.c ttyio.c ttykbd.c
+	undo.c util.c version.c window.c word.c yank.c
+	cmode.c cscope.c dired.c grep.c tags.c
+)
+
+add_executable (mg ${MG_SRC})
+
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (NCURSES REQUIRED ncurses)
+string (REPLACE ";" " -I" NCURSES_FLAGS "${NCURSES_INCLUDE_DIRS}")
+set (NCURSES_FLAGS "-I${NCURSES_FLAGS}")
+target_link_libraries (mg ${NCURSES_LIBRARIES} util)
+
+if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+  pkg_check_modules (BSD REQUIRED libbsd-overlay)
+  link_directories (${BSD_LIBRARY_DIRS})
+  string (REPLACE ";" " " LIBBSD_FLAGS "${BSD_CFLAGS}")
+  target_link_libraries (mg ${BSD_LIBRARIES})
+endif()
+
+set (CMAKE_C_FLAGS "-Wall -DREGEX ${LIBBSD_FLAGS} ${NCURSES_FLAGS} -L${NCURSES_LIBRARY_DIRS}")
diff --git a/CVS/Entries b/CVS/Entries
index 62e17cb..e84eb57 100644
--- a/CVS/Entries
+++ b/CVS/Entries
@@ -1,51 +1,54 @@
-/ttyio.c/1.37/Wed Mar 25 18:47:22 2015//
-/region.c/1.37/Result of merge//
-/main.c/1.84/Result of merge//
 /tags.c/1.16/Result of merge//
 /re_search.c/1.33/Mon Aug 28 19:36:58 2017//
-/fileio.c/1.105/Result of merge+Thu Apr 19 17:50:42 2018//
-/Makefile/1.33/Tue Apr 10 04:57:08 2018//
-/README/1.14/Tue Apr 10 04:57:08 2018//
-/autoexec.c/1.17/Tue Apr 10 04:57:08 2018//
-/basic.c/1.47/Tue Apr 10 04:57:08 2018//
-/bell.c/1.4/Tue Apr 10 04:57:08 2018//
-/buffer.c/1.104/Tue Apr 10 04:57:08 2018//
-/chrdef.h/1.10/Tue Apr 10 04:57:08 2018//
-/cinfo.c/1.18/Tue Apr 10 04:57:08 2018//
-/cmode.c/1.16/Tue Apr 10 04:57:08 2018//
-/cscope.c/1.17/Tue Apr 10 04:57:08 2018//
-/dir.c/1.30/Tue Apr 10 04:57:08 2018//
-/dired.c/1.83/Tue Apr 10 04:57:08 2018//
-/display.c/1.48/Tue Apr 10 04:57:08 2018//
-/echo.c/1.66/Tue Apr 10 04:57:08 2018//
-/extend.c/1.64/Tue Apr 10 04:57:08 2018//
-/file.c/1.100/Tue Apr 10 04:57:08 2018//
-/funmap.h/1.7/Tue Apr 10 04:57:08 2018//
-/grep.c/1.46/Tue Apr 10 04:57:08 2018//
-/help.c/1.35/Tue Apr 10 04:57:08 2018//
-/kbd.c/1.30/Tue Apr 10 04:57:08 2018//
-/kbd.h/1.19/Tue Apr 10 04:57:08 2018//
-/key.h/1.5/Tue Apr 10 04:57:08 2018//
-/keymap.c/1.58/Tue Apr 10 04:57:08 2018//
-/macro.c/1.16/Tue Apr 10 04:57:08 2018//
-/macro.h/1.7/Tue Apr 10 04:57:08 2018//
-/match.c/1.19/Tue Apr 10 04:57:08 2018//
-/modes.c/1.21/Tue Apr 10 04:57:08 2018//
-/paragraph.c/1.45/Tue Apr 10 04:57:08 2018//
-/pathnames.h/1.1/Tue Apr 10 04:57:08 2018//
-/search.c/1.47/Sat Aug 11 17:34:08 2018//
-/spawn.c/1.12/Tue Apr 10 04:57:08 2018//
-/tty.c/1.36/Tue Apr 10 04:57:08 2018//
-/ttykbd.c/1.19/Tue Apr 10 04:57:08 2018//
-/tutorial/1.18/Mon May 28 16:13:00 2018//
 /undo.c/1.58/Tue Apr 10 04:57:08 2018//
-/util.c/1.38/Tue Apr 10 04:57:08 2018//
-/version.c/1.10/Tue Apr 10 04:57:08 2018//
-/window.c/1.36/Tue Apr 10 04:57:08 2018//
-/word.c/1.19/Tue Apr 10 04:57:08 2018//
-/yank.c/1.14/Tue Apr 10 04:57:08 2018//
-/def.h/1.156/Result of merge//
-/funmap.c/1.54/Tue Sep 25 17:50:41 2018//
-/line.c/1.61/Tue Sep 25 17:50:41 2018//
-/mg.1/1.107/Tue Sep 25 17:50:41 2018//
+/region.c/1.38/Result of merge//
+/Makefile/1.35/Result of merge//
+/README/1.14/Mon Aug 12 23:34:18 2019//
+/autoexec.c/1.17/Mon Aug 12 23:34:18 2019//
+/basic.c/1.49/Mon Aug 12 23:34:18 2019//
+/bell.c/1.5/Mon Aug 12 23:34:28 2019//
+/buffer.c/1.107/Mon Aug 12 23:34:28 2019//
+/chrdef.h/1.10/Mon Aug 12 23:34:18 2019//
+/cinfo.c/1.18/Mon Aug 12 23:34:18 2019//
+/cmode.c/1.17/Mon Aug 12 23:34:28 2019//
+/cscope.c/1.18/Mon Aug 12 23:34:28 2019//
+/def.h/1.165/Result of merge//
+/dir.c/1.31/Mon Aug 12 23:34:28 2019//
+/dired.c/1.93/Result of merge//
+/display.c/1.48/Mon Aug 12 23:34:18 2019//
+/echo.c/1.66/Mon Aug 12 23:34:18 2019//
+/extend.c/1.71/Mon Aug 12 23:34:28 2019//
+/file.c/1.102/Mon Aug 12 23:34:28 2019//
+/fileio.c/1.106/Result of merge//
+/funmap.c/1.59/Mon Aug 12 23:34:28 2019//
+/funmap.h/1.8/Mon Aug 12 23:34:28 2019//
+/grep.c/1.48/Mon Aug 12 23:34:28 2019//
+/help.c/1.35/Mon Aug 12 23:34:18 2019//
+/interpreter.c/1.5/Sat Jul 20 11:06:33 2019//
+/kbd.c/1.33/Mon Aug 12 23:34:28 2019//
+/kbd.h/1.19/Mon Aug 12 23:34:18 2019//
+/key.h/1.6/Mon Aug 12 23:34:28 2019//
+/keymap.c/1.58/Mon Aug 12 23:34:18 2019//
+/line.c/1.61/Mon Aug 12 23:34:18 2019//
+/log.c/1.11/Mon Aug 12 23:34:29 2019//
+/log.h/1.5/Mon Aug 12 23:34:29 2019//
+/macro.c/1.16/Mon Aug 12 23:34:18 2019//
+/macro.h/1.7/Mon Aug 12 23:34:18 2019//
+/main.c/1.87/Result of merge//
+/match.c/1.21/Mon Aug 12 23:34:29 2019//
+/mg.1/1.117/Mon Aug 12 23:34:29 2019//
+/modes.c/1.21/Mon Aug 12 23:34:18 2019//
+/paragraph.c/1.46/Mon Aug 12 23:34:18 2019//
+/pathnames.h/1.1/Mon Aug 12 23:34:18 2019//
+/search.c/1.47/Mon Aug 12 23:34:18 2019//
+/spawn.c/1.12/Mon Aug 12 23:34:18 2019//
+/tty.c/1.36/Mon Aug 12 23:34:18 2019//
+/ttyio.c/1.38/Result of merge//
+/ttykbd.c/1.19/Mon Aug 12 23:34:18 2019//
+/tutorial/1.18/Mon Aug 12 23:34:18 2019//
+/util.c/1.42/Mon Aug 12 23:34:29 2019//
+/version.c/1.10/Mon Aug 12 23:34:18 2019//
+/window.c/1.36/Mon Aug 12 23:34:18 2019//
+/word.c/1.19/Mon Aug 12 23:34:18 2019//
+/yank.c/1.14/Mon Aug 12 23:34:18 2019//
 D
diff --git a/GNUmakefile b/GNUmakefile
index f351e8e..3739df7 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -52,8 +52,8 @@ LIBS=		$(CURSES_LIBS) $(BSD_LIBS)
 
 
 OBJS=	autoexec.o basic.o bell.o buffer.o cinfo.o dir.o display.o \
-	echo.o extend.o file.o fileio.o funmap.o help.o kbd.o keymap.o \
-	line.o macro.o main.o match.o modes.o paragraph.o \
+	echo.o extend.o file.o fileio.o funmap.o interpreter.o help.o \
+	kbd.o keymap.o line.o macro.o main.o match.o modes.o paragraph.o \
 	re_search.o region.o search.o spawn.o tty.o ttyio.o ttykbd.o \
 	undo.o util.o version.o window.o word.o yank.o
 OBJS+=	cmode.o cscope.o dired.o grep.o tags.o
diff --git a/Makefile b/Makefile
index f1448fb..df4dd9f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,15 @@
-# $OpenBSD: Makefile,v 1.33 2016/09/16 17:17:40 tedu Exp $
+# $OpenBSD: Makefile,v 1.35 2019/07/18 05:57:48 lum Exp $
 
 PROG=	mg
 
 LDADD+=	`pkg-config --libs ncurses` -lutil
-DPADD+=	${LIBNCURSES} ${LIBUTIL}
+DPADD+=	${LIBUTIL}
 
 # (Common) compile-time options:
 #
 #	REGEX		-- create regular expression functions.
 #	STARTUPFILE	-- look for and handle initialization file.
+#	MGLOG		-- debug mg internals to a log file.
 #
 CFLAGS+=-Wall -DREGEX `pkg-config --cflags-only-I ncurses`
 
@@ -21,13 +22,12 @@ SRCS=	autoexec.c basic.c bell.c buffer.c cinfo.c dir.c display.c \
 #
 # More or less standalone extensions.
 #
-SRCS+=	cmode.c cscope.c dired.c grep.c tags.c
+SRCS+=	cmode.c cscope.c dired.c grep.c interpreter.c tags.c
 
-OS!=	uname
-
-.if ${OS:MDragonFly}
-SRCS+=	reallocarray.c
-.endif
+#
+# -DMGLOG source file.
+#
+#SRCS+=	log.c
 
 afterinstall:
 	${INSTALL} -d -o root -g wheel ${DESTDIR}${DOCDIR}/mg
diff --git a/README.md b/README.md
index cc46359..93d6537 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,23 @@ make
 sudo make install
 ```
 
+## USING CMAKE
+
+You can also build mg with cmake, it goes like this:
+
+ - Get the libbsd and libncurses dev packages installed.
+ - Run the following commands:
+
+```
+mkdir build
+cd build
+cmake ..
+make
+sudo make install
+```
+
+*Kudos to Leonid Bobrov(@mazocomp) for adding CMAKE support.*
+
 ## STATIC BUILDS
 
 I recently figured out how to make really portable static builds: On an
diff --git a/basic.c b/basic.c
index 85d9f70..57d65a6 100644
--- a/basic.c
+++ b/basic.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: basic.c,v 1.47 2015/10/10 09:13:14 lum Exp $	*/
+/*	$OpenBSD: basic.c,v 1.49 2019/06/17 11:39:26 lum Exp $	*/
 
 /* This file is in the public domain */
 
@@ -21,6 +21,8 @@
 
 #include "def.h"
 
+#define percint(n1, n2)		((n1 * (int) n2) * 0.1)
+
 /*
  * Go to beginning of line.
  */
@@ -115,46 +117,63 @@ forwchar(int f, int n)
 }
 
 /*
- * Go to the beginning of the
- * buffer. Setting WFFULL is conservative,
- * but almost always the case.
+ * Go to the beginning of the buffer. Setting WFFULL is conservative,
+ * but almost always the case. A universal argument of higher than 9
+ * puts the cursor back to the end of buffer.
  */
 int
 gotobob(int f, int n)
 {
-	(void) setmark(f, n);
+	if (!curwp->w_markp)
+		(void) setmark(f, n);
 	curwp->w_dotp = bfirstlp(curbp);
 	curwp->w_doto = 0;
 	curwp->w_rflag |= WFFULL;
 	curwp->w_dotline = 1;
+	if (f & FFOTHARG && n > 0) {
+		if (n > 9)
+			gotoeob(0, 0);
+		else
+			forwline(f, percint(curwp->w_bufp->b_lines, n) - 1);
+	}
 	return (TRUE);
 }
 
 /*
  * Go to the end of the buffer. Leave dot 3 lines from the bottom of the
  * window if buffer length is longer than window length; same as emacs.
- * Setting WFFULL is conservative, but almost always the case.
+ * Setting WFFULL is conservative, but almost always the case. A universal
+ * argument of higher than 9 puts the cursor back to the start of buffer.
  */
 int
 gotoeob(int f, int n)
 {
+	int		 ln;
 	struct line	*lp;
 
-	(void) setmark(f, n);
+	if (!curwp->w_markp)
+		(void) setmark(f, n);
 	curwp->w_dotp = blastlp(curbp);
 	curwp->w_doto = llength(curwp->w_dotp);
 	curwp->w_dotline = curwp->w_bufp->b_lines;
 
 	lp = curwp->w_dotp;
-	n = curwp->w_ntrows - 3;
+	ln = curwp->w_ntrows - 3;
 
-	if (n < curwp->w_bufp->b_lines && n >= 3) {
-		while (n--)
+	if (ln < curwp->w_bufp->b_lines && ln >= 3) {
+		while (ln--)
 			curwp->w_dotp = lback(curwp->w_dotp);
 
 		curwp->w_linep = curwp->w_dotp;
 		curwp->w_dotp = lp;
 	}
+	if (f & FFOTHARG && n > 0) {
+		if (n > 9)
+			gotobob(0, 0);
+		else
+			backline(f, percint(curwp->w_bufp->b_lines, n));
+	}
+
 	curwp->w_rflag |= WFFULL;
 	return (TRUE);
 }
diff --git a/bell.c b/bell.c
index 7c1753d..8cec125 100644
--- a/bell.c
+++ b/bell.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: bell.c,v 1.4 2016/01/03 19:37:08 lum Exp $	*/
+/*	$OpenBSD: bell.c,v 1.5 2019/07/17 18:18:37 lum Exp $	*/
 
 /*
  * This file is in the public domain.
@@ -26,6 +26,22 @@ bellinit(void)
 	dovisiblebell = 0;
 }
 
+int
+dobeep_msgs(const char *msg, const char *s)
+{
+	ewprintf("%s %s", msg, s);
+	dobeep();
+	return (FALSE);
+}
+
+int
+dobeep_msg(const char *msg)
+{
+	ewprintf("%s", msg);
+	dobeep();
+	return (FALSE);
+}
+
 void
 dobeep(void)
 {
diff --git a/buffer.c b/buffer.c
index 3057882..57ef738 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: buffer.c,v 1.104 2017/08/06 04:39:45 bcallah Exp $	*/
+
 
 /* This file is in the public domain. */
 
@@ -29,6 +29,28 @@ static int usebufname(const char *);
 /* Flag for global working dir */
 extern int globalwd;
 
+/* ARGSUSED */
+int
+togglereadonlyall(int f, int n)
+{
+	struct buffer *bp = NULL;
+	int len = 0;
+
+	allbro = !allbro;
+	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+		len = strlen(bp->b_bname);
+		if (bp->b_bname[0] != '*' && bp->b_bname[len - 1] != '*') {
+			if (allbro)
+				bp->b_flag |= BFREADONLY;
+			else
+				bp->b_flag &= ~BFREADONLY;
+		}
+	}
+	curwp->w_rflag |= WFMODE;
+
+	return (TRUE);
+}
+
 /* ARGSUSED */
 int
 togglereadonly(int f, int n)
@@ -90,7 +112,7 @@ usebuffer(int f, int n)
 		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
 
 	if (bufp == NULL)
-		return FALSE;
+		return (ABORT);
 
 	return (usebufname(bufp));
 }
@@ -270,7 +292,7 @@ static struct KEYMAPE (2) listbufmap = {
  * in two parts. The "makelist" routine figures out
  * the text, and puts it in a buffer. "popbuf"
  * then pops the data onto the screen. Bound to
- * "C-X C-B".
+ * "C-x C-b".
  */
 /* ARGSUSED */
 int
diff --git a/cmode.c b/cmode.c
index a238285..6c0ef5b 100644
--- a/cmode.c
+++ b/cmode.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmode.c,v 1.16 2015/09/26 21:51:58 jasper Exp $ */
+/* $OpenBSD: cmode.c,v 1.17 2019/07/11 18:20:18 lum Exp $ */
 /*
  * This file is in the public domain.
  *
@@ -91,12 +91,12 @@ static struct KEYMAPE (3) cmodemap = {
 void
 cmode_init(void)
 {
-	funmap_add(cmode, "c-mode");
-	funmap_add(cc_char, "c-handle-special-char");
-	funmap_add(cc_brace, "c-handle-special-brace");
-	funmap_add(cc_tab, "c-tab-or-indent");
-	funmap_add(cc_indent, "c-indent");
-	funmap_add(cc_lfindent, "c-indent-and-newline");
+	funmap_add(cmode, "c-mode", 0);
+	funmap_add(cc_char, "c-handle-special-char", 0);
+	funmap_add(cc_brace, "c-handle-special-brace", 0);
+	funmap_add(cc_tab, "c-tab-or-indent", 0);
+	funmap_add(cc_indent, "c-indent", 0);
+	funmap_add(cc_lfindent, "c-indent-and-newline", 0);
 	maps_add((KEYMAP *)&cmodemap, "c");
 }
 
diff --git a/cscope.c b/cscope.c
index 72aaa64..18042d3 100644
--- a/cscope.c
+++ b/cscope.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: cscope.c,v 1.17 2017/10/12 14:12:00 florian Exp $	*/
+/*	$OpenBSD: cscope.c,v 1.18 2019/07/03 03:24:02 deraadt Exp $	*/
 
 /*
  * This file is in the public domain.
@@ -634,7 +634,7 @@ csexists(const char *cmd)
 			dir[--dlen] = '\0';     /* strip trailing '/' */
 
 		len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
-		if (len == -1 || len >= sizeof(fname)) {
+		if (len < 0 || len >= sizeof(fname)) {
 			dobeep();
 			ewprintf("path too long");
 			goto cleanup;
diff --git a/debian/changelog b/debian/changelog
index a89c505..258ef9e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+mg (20180927+git20190813.0dde641-1) UNRELEASED; urgency=medium
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 27 Aug 2019 22:42:23 +0000
+
 mg (20180927-1) unstable; urgency=medium
 
   * New upstream version 20180927
diff --git a/def.h b/def.h
index 003addf..7784946 100644
--- a/def.h
+++ b/def.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: def.h,v 1.156 2018/08/29 07:50:16 reyk Exp $	*/
+/*	$OpenBSD: def.h,v 1.165 2019/07/18 10:55:11 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -53,7 +53,7 @@ typedef int	(*PF)(int, int);	/* generally useful type */
  * flag controls the clearing versus appending
  * of data in the kill buffer.
  */
-#define CFCPCN	0x0001		/* Last command was C-P, C-N	 */
+#define CFCPCN	0x0001		/* Last command was C-p or C-n	 */
 #define CFKILL	0x0002		/* Last command was a kill	 */
 #define CFINS	0x0004		/* Last command was self-insert	 */
 
@@ -105,6 +105,7 @@ typedef int	(*PF)(int, int);	/* generally useful type */
 
 #define MAX_TOKEN 64
 
+#define	BUFSIZE	128	/* Size of line contents in extend.c  */
 /*
  * Previously from sysdef.h
  */
@@ -428,6 +429,7 @@ int		 delwind(int, int);
 
 /* buffer.c */
 int		 togglereadonly(int, int);
+int		 togglereadonlyall(int, int);
 struct buffer   *bfind(const char *, int);
 int		 poptobuffer(int, int);
 int		 killbuffer(struct buffer *);
@@ -453,12 +455,12 @@ int		 diffbuffer(int, int);
 struct buffer	*findbuffer(char *);
 
 /* display.c */
-int		vtresize(int, int, int);
-void		vtinit(void);
-void		vttidy(void);
-void		update(int);
-int		linenotoggle(int, int);
-int		colnotoggle(int, int);
+int		 vtresize(int, int, int);
+void		 vtinit(void);
+void		 vttidy(void);
+void		 update(int);
+int		 linenotoggle(int, int);
+int		 colnotoggle(int, int);
 
 /* echo.c X */
 void		 eerase(void);
@@ -476,7 +478,7 @@ int		 ffropen(FILE **, const char *, struct buffer *);
 void		 ffstat(FILE *, struct buffer *);
 int		 ffwopen(FILE **, const char *, struct buffer *);
 int		 ffclose(FILE *, struct buffer *);
-int		 ffputbuf(FILE *, struct buffer *);
+int		 ffputbuf(FILE *, struct buffer *, int);
 int		 ffgetline(FILE *, char *, int, int *);
 int		 fbackupfile(const char *);
 char		*adjustname(const char *, int);
@@ -500,6 +502,7 @@ int		 rescan(int, int);
 int		 universal_argument(int, int);
 int		 digit_argument(int, int);
 int		 negative_argument(int, int);
+int		 ask_selfinsert(int, int);
 int		 selfinsert(int, int);
 int		 quote(int, int);
 
@@ -536,7 +539,7 @@ int		 swapmark(int, int);
 int		 gotoline(int, int);
 int		 setlineno(int);
 
-/* random.c X */
+/* util.c X */
 int		 showcpos(int, int);
 int		 getcolpos(struct mgwin *);
 int		 twiddle(int, int);
@@ -562,19 +565,19 @@ int		 tagsvisit(int, int);
 int		 curtoken(int, int, char *);
 
 /* cscope.c */
-int		cssymbol(int, int);
-int		csdefinition(int, int);
-int		csfuncalled(int, int);
-int		cscallerfuncs(int, int);
-int		csfindtext(int, int);
-int		csegrep(int, int);
-int		csfindfile(int, int);
-int		csfindinc(int, int);
-int		csnextfile(int, int);
-int		csnextmatch(int, int);
-int		csprevfile(int, int);
-int		csprevmatch(int, int);
-int		cscreatelist(int, int);
+int		 cssymbol(int, int);
+int		 csdefinition(int, int);
+int		 csfuncalled(int, int);
+int		 cscallerfuncs(int, int);
+int		 csfindtext(int, int);
+int		 csegrep(int, int);
+int		 csfindfile(int, int);
+int		 csfindinc(int, int);
+int		 csnextfile(int, int);
+int		 csnextmatch(int, int);
+int		 csprevfile(int, int);
+int		 csprevmatch(int, int);
+int		 cscreatelist(int, int);
 
 /* extend.c X */
 int		 insert(int, int);
@@ -589,6 +592,7 @@ int		 evalbuffer(int, int);
 int		 evalfile(int, int);
 int		 load(const char *);
 int		 excline(char *);
+char		*skipwhite(char *);
 
 /* help.c X */
 int		 desckey(int, int);
@@ -720,8 +724,14 @@ int		 compile(int, int);
 void		 bellinit(void);
 int		 toggleaudiblebell(int, int);
 int		 togglevisiblebell(int, int);
+int		 dobeep_msgs(const char *, const char *);
+int		 dobeep_msg(const char *);
 void		 dobeep(void);
 
+/* interpreter.c */
+int		 foundparen(char *);
+int		 clearvars(void);
+
 /*
  * Externals.
  */
@@ -748,6 +758,7 @@ extern int		 defb_flag;
 extern int		 doaudiblebell;
 extern int		 dovisiblebell;
 extern int		 dblspace;
+extern int		 allbro;
 extern char	 	 cinfo[];
 extern char		*keystrings[];
 extern char		 pat[NPAT];
diff --git a/dir.c b/dir.c
index 0caaf16..0674f63 100644
--- a/dir.c
+++ b/dir.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dir.c,v 1.30 2017/05/30 07:05:22 florian Exp $	*/
+/*	$OpenBSD: dir.c,v 1.31 2019/06/28 13:35:02 deraadt Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -153,7 +153,7 @@ do_makedir(char *path)
 		}
 
 		if (mkdir(path, finished ? f_mode : dir_mode) == 0) {
-			if (f_mode > 0777 && chmod(path, f_mode) < 0) {
+			if (f_mode > 0777 && chmod(path, f_mode) == -1) {
 				umask(oumask);
 				return (ABORT);
 			}
diff --git a/dired.c b/dired.c
index 60fb815..b5bf411 100644
--- a/dired.c
+++ b/dired.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dired.c,v 1.83 2016/10/07 00:17:20 jsg Exp $	*/
+/*	$OpenBSD: dired.c,v 1.93 2019/07/11 18:20:18 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -16,7 +16,6 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <libgen.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -52,6 +51,7 @@ static int	 d_backline(int, int);
 static int	 d_killbuffer_cmd(int, int);
 static int	 d_refreshbuffer(int, int);
 static int	 d_filevisitalt(int, int);
+static int	 d_gotofile(int, int);
 static void	 reaper(int);
 static struct buffer	*refreshbuffer(struct buffer *);
 static int	 createlist(struct buffer *);
@@ -128,7 +128,10 @@ static PF direda[] = {
 	d_del,			/* d */
 	d_findfile,		/* e */
 	d_findfile,		/* f */
-	d_refreshbuffer		/* g */
+	d_refreshbuffer,	/* g */
+	rescan,			/* h */
+	rescan,			/* i */
+	d_gotofile		/* j */
 };
 
 static PF diredn[] = {
@@ -187,7 +190,7 @@ static struct KEYMAPE (7) diredmap = {
 			CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
 		},
 		{
-			'a', 'g', direda, NULL
+			'a', 'j', direda, NULL
 		},
 		{
 			'n', 'x', diredn, NULL
@@ -201,22 +204,24 @@ static struct KEYMAPE (7) diredmap = {
 void
 dired_init(void)
 {
-	funmap_add(dired, "dired");
-	funmap_add(d_undelbak, "dired-unmark-backward");
-	funmap_add(d_create_directory, "dired-create-directory");
-	funmap_add(d_copy, "dired-do-copy");
-	funmap_add(d_expunge, "dired-do-flagged-delete");
-	funmap_add(d_findfile, "dired-find-file");
-	funmap_add(d_ffotherwindow, "dired-find-file-other-window");
-	funmap_add(d_del, "dired-flag-file-deletion");
-	funmap_add(d_forwline, "dired-next-line");
-	funmap_add(d_otherwindow, "dired-other-window");
-	funmap_add(d_backline, "dired-previous-line");
-	funmap_add(d_rename, "dired-do-rename");
-	funmap_add(d_backpage, "dired-scroll-down");
-	funmap_add(d_forwpage, "dired-scroll-up");
-	funmap_add(d_undel, "dired-unmark");
-	funmap_add(d_killbuffer_cmd, "quit-window");
+	funmap_add(dired, "dired", 1);
+	funmap_add(d_create_directory, "dired-create-directory", 1);
+	funmap_add(d_copy, "dired-do-copy", 1);
+	funmap_add(d_expunge, "dired-do-flagged-delete", 0);
+	funmap_add(d_rename, "dired-do-rename", 1);
+	funmap_add(d_findfile, "dired-find-file", 1);
+	funmap_add(d_ffotherwindow, "dired-find-file-other-window", 1);
+	funmap_add(d_del, "dired-flag-file-deletion", 0);
+	funmap_add(d_gotofile, "dired-goto-file", 1);
+	funmap_add(d_forwline, "dired-next-line", 0);
+	funmap_add(d_otherwindow, "dired-other-window", 0);
+	funmap_add(d_backline, "dired-previous-line", 0);
+	funmap_add(d_refreshbuffer, "dired-revert", 0);
+	funmap_add(d_backpage, "dired-scroll-down", 0);
+	funmap_add(d_forwpage, "dired-scroll-up", 0);
+	funmap_add(d_undel, "dired-unmark", 0);
+	funmap_add(d_undelbak, "dired-unmark-backward", 0);
+	funmap_add(d_killbuffer_cmd, "quit-window", 0);
 	maps_add((KEYMAP *)&diredmap, "dired");
 	dobindkey(fundamental_map, "dired", "^Xd");
 }
@@ -406,7 +411,7 @@ d_expunge(int f, int n)
 				curwp->w_dotline = tmp;
 				return (FALSE);
 			case FALSE:
-				if (unlink(fname) < 0) {
+				if (unlink(fname) == -1) {
 					(void)xbasename(sname, fname, NFILEN);
 					dobeep();
 					ewprintf("Could not delete '%s'", sname);
@@ -415,7 +420,7 @@ d_expunge(int f, int n)
 				}
 				break;
 			case TRUE:
-				if (rmdir(fname) < 0) {
+				if (rmdir(fname) == -1) {
 					(void)xbasename(sname, fname, NFILEN);
 					dobeep();
 					ewprintf("Could not delete directory "
@@ -445,6 +450,7 @@ d_expunge(int f, int n)
 int
 d_copy(int f, int n)
 {
+	struct stat      statbuf;
 	char		 frname[NFILEN], toname[NFILEN], sname[NFILEN];
 	char		*topath, *bufp;
 	int		 ret;
@@ -471,11 +477,29 @@ d_copy(int f, int n)
 		return (FALSE);
 
 	topath = adjustname(toname, TRUE);
+	if (stat(topath, &statbuf) == 0) {
+		if (S_ISDIR(statbuf.st_mode)) {
+			off = snprintf(toname, sizeof(toname), "%s/%s",
+			    topath, sname);
+			if (off < 0 || off >= sizeof(toname) - 1) {
+				dobeep();
+				ewprintf("Directory name too long");
+				return (FALSE);
+			}
+			topath = adjustname(toname, TRUE);
+		}
+	}
+	if (strcmp(frname, topath) == 0) {
+		ewprintf("Cannot copy to same file: %s", frname);
+		return (TRUE);
+	}
 	ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
 	if (ret != TRUE)
 		return (ret);
 	if ((bp = refreshbuffer(curbp)) == NULL)
 		return (FALSE);
+
+	ewprintf("Copy: 1 file");
 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
 }
 
@@ -483,6 +507,7 @@ d_copy(int f, int n)
 int
 d_rename(int f, int n)
 {
+	struct stat      statbuf;
 	char		 frname[NFILEN], toname[NFILEN];
 	char		*topath, *bufp;
 	int		 ret;
@@ -510,11 +535,29 @@ d_rename(int f, int n)
 		return (FALSE);
 
 	topath = adjustname(toname, TRUE);
+	if (stat(topath, &statbuf) == 0) {
+		if (S_ISDIR(statbuf.st_mode)) {
+			off = snprintf(toname, sizeof(toname), "%s/%s",
+			    topath, sname);
+			if (off < 0 || off >= sizeof(toname) - 1) {
+				dobeep();
+				ewprintf("Directory name too long");
+				return (FALSE);
+			}
+			topath = adjustname(toname, TRUE);
+		}
+	}
+	if (strcmp(frname, topath) == 0) {
+		ewprintf("Cannot move to same file: %s", frname);
+		return (TRUE);
+	}
 	ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
 	if (ret != TRUE)
 		return (ret);
 	if ((bp = refreshbuffer(curbp)) == NULL)
 		return (FALSE);
+
+	ewprintf("Move: 1 file");
 	return (showbuffer(bp, curwp, WFFULL | WFMODE));
 }
 
@@ -1039,6 +1082,58 @@ createlist(struct buffer *bp)
 	return (ret);
 }
 
+int
+d_gotofile(int f, int n)
+{
+	struct line	*lp, *nlp;
+	struct buffer   *curbp;
+	size_t		 lenfpath;
+	char		 fpath[NFILEN], fname[NFILEN];
+	char		*p, *fpth, *fnp = NULL;
+	int		 tmp;
+
+	if (getbufcwd(fpath, sizeof(fpath)) != TRUE)
+		fpath[0] = '\0';
+	lenfpath = strlen(fpath);
+	fnp = eread("Goto file: ", fpath, NFILEN,
+	    EFNEW | EFCR | EFFILE | EFDEF);
+	if (fnp == NULL)
+		return (ABORT);
+	else if (fnp[0] == '\0')
+		return (FALSE);
+
+	fpth = adjustname(fpath, TRUE);		/* Removes last '/' if	*/
+	if (strlen(fpth) == lenfpath - 1) {	/* directory, hence -1.	*/
+		ewprintf("No file to find");	/* Current directory given so  */
+		return (TRUE);			/* return at present location. */
+	}
+	(void)xbasename(fname, fpth, NFILEN);
+	curbp = curwp->w_bufp;
+	tmp = 0;
+	for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
+		tmp++;
+		if ((p = findfname(lp, p)) == NULL) {
+			nlp = lforw(lp);
+			continue;
+		}
+		if (strcmp(fname, p) == 0) {
+			curwp->w_dotp = lp;
+			curwp->w_dotline = tmp;
+			(void)d_warpdot(curwp->w_dotp, &curwp->w_doto);
+			tmp--;
+			break;
+		}
+		nlp = lforw(lp);
+	}
+	if (tmp == curbp->b_lines - 1) {
+		ewprintf("File not found %s", fname);
+		return (FALSE);
+	} else {
+		ewprintf("");
+		return (TRUE);
+	}
+}
+
 /*
  * Look for and extract a file name on a dired buffer line.
  */
diff --git a/extend.c b/extend.c
index db04cea..0898da9 100644
--- a/extend.c
+++ b/extend.c
@@ -1,13 +1,13 @@
-/*	$OpenBSD: extend.c,v 1.64 2016/09/01 21:06:09 lum Exp $	*/
-
+/*	$OpenBSD: extend.c,v 1.71 2019/07/18 15:52:11 lum Exp $	*/
 /* This file is in the public domain. */
 
 /*
- *	Extended (M-X) commands, rebinding, and	startup file processing.
+ *	Extended (M-x) commands, rebinding, and	startup file processing.
  */
 
 #include <sys/queue.h>
 #include <sys/types.h>
+#include <regex.h>
 #include <ctype.h>
 #include <limits.h>
 #include <signal.h>
@@ -26,7 +26,6 @@ static int	 remap(KEYMAP *, int, PF, KEYMAP *);
 static KEYMAP	*reallocmap(KEYMAP *);
 static void	 fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
 static int	 dobind(KEYMAP *, const char *, int);
-static char	*skipwhite(char *);
 static char	*parsetoken(char *);
 static int	 bindkey(KEYMAP **, const char *, KCHAR *, int);
 
@@ -37,7 +36,7 @@ static int	 bindkey(KEYMAP **, const char *, KCHAR *, int);
 int
 insert(int f, int n)
 {
-	char	 buf[128], *bufp, *cp;
+	char	 buf[BUFSIZE], *bufp, *cp;
 	int	 count, c;
 
 	if (inmacro) {
@@ -110,11 +109,9 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 		if (n1 <= MAPELEDEF && n1 <= n2) {
 			ele--;
 			if ((pfp = calloc(c - ele->k_base + 1,
-			    sizeof(PF))) == NULL) {
-				dobeep();
-				ewprintf("Out of memory");
-				return (FALSE);
-			}
+			    sizeof(PF))) == NULL)
+				return (dobeep_msg("Out of memory"));
+
 			nold = ele->k_num - ele->k_base + 1;
 			for (i = 0; i < nold; i++)
 				pfp[i] = ele->k_funcp[i];
@@ -125,11 +122,9 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 			ele->k_funcp = pfp;
 		} else if (n2 <= MAPELEDEF) {
 			if ((pfp = calloc(ele->k_num - c + 1,
-			    sizeof(PF))) == NULL) {
-				dobeep();
-				ewprintf("Out of memory");
-				return (FALSE);
-			}
+			    sizeof(PF))) == NULL)
+				return (dobeep_msg("Out of memory"));
+
 			nold = ele->k_num - ele->k_base + 1;
 			for (i = 0; i < nold; i++)
 				pfp[i + n2] = ele->k_funcp[i];
@@ -144,11 +139,9 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 					return (FALSE);
 				curmap = newmap;
 			}
-			if ((pfp = malloc(sizeof(PF))) == NULL) {
-				dobeep();
-				ewprintf("Out of memory");
-				return (FALSE);
-			}
+			if ((pfp = malloc(sizeof(PF))) == NULL)
+				return (dobeep_msg("Out of memory"));
+
 			pfp[0] = funct;
 			for (mep = &curmap->map_element[curmap->map_num];
 			    mep > ele; mep--) {
@@ -169,8 +162,7 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 			else {
 				if ((mp = malloc(sizeof(KEYMAP) +
 				    (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
-					dobeep();
-					ewprintf("Out of memory");
+					(void)dobeep_msg("Out of memory");
 					ele->k_funcp[c - ele->k_base] =
 					    curmap->map_default;
 					return (FALSE);
@@ -199,8 +191,7 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 					if ((mp = malloc(sizeof(KEYMAP) +
 					    (MAPINIT - 1) *
 					    sizeof(struct map_element))) == NULL) {
-						dobeep();
-						ewprintf("Out of memory");
+						(void)dobeep_msg("Out of memory");
 						ele->k_funcp[c - ele->k_base] =
 						    curmap->map_default;
 						return (FALSE);
@@ -226,11 +217,9 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 				curmap = newmap;
 			}
 			if ((pfp = calloc(ele->k_num - c + !n2,
-			    sizeof(PF))) == NULL) {
-				dobeep();
-				ewprintf("Out of memory");
-				return (FALSE);
-			}
+			    sizeof(PF))) == NULL)
+				return (dobeep_msg("Out of memory"));
+
 			ele->k_funcp[n1] = NULL;
 			for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++)
 				pfp[i - n1 - n2] = ele->k_funcp[i];
@@ -250,8 +239,7 @@ remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
 			if (pref_map == NULL) {
 				if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1)
 				    * sizeof(struct map_element))) == NULL) {
-					dobeep();
-					ewprintf("Out of memory");
+					(void)dobeep_msg("Out of memory");
 					ele->k_funcp[c - ele->k_base] =
 					    curmap->map_default;
 					return (FALSE);
@@ -279,14 +267,12 @@ reallocmap(KEYMAP *curmap)
 	int	 i;
 
 	if (curmap->map_max > SHRT_MAX - MAPGROW) {
-		dobeep();
-		ewprintf("keymap too large");
+		(void)dobeep_msg("keymap too large");
 		return (NULL);
 	}
 	if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
 	    sizeof(struct map_element))) == NULL) {
-		dobeep();
-		ewprintf("Out of memory");
+		(void)dobeep_msg("Out of memory");
 		return (NULL);
 	}
 	mp->map_num = curmap->map_num;
@@ -343,9 +329,7 @@ dobind(KEYMAP *curmap, const char *p, int unbind)
 		 * Keystrokes aren't collected. Not hard, but pretty useless.
 		 * Would not work for function keys in any case.
 		 */
-		dobeep();
-		ewprintf("Can't rebind key in macro");
-		return (FALSE);
+		return (dobeep_msg("Can't rebind key in macro"));
 	}
 	if (inmacro) {
 		for (s = 0; s < maclcur->l_used - 1; s++) {
@@ -383,11 +367,9 @@ dobind(KEYMAP *curmap, const char *p, int unbind)
 		else if (bufp[0] == '\0')
 			return (FALSE);
 		if (((funct = name_function(bprompt)) == NULL) ?
-		    (pref_map = name_map(bprompt)) == NULL : funct == NULL) {
-			dobeep();
-			ewprintf("[No match]");
-			return (FALSE);
-		}
+		    (pref_map = name_map(bprompt)) == NULL : funct == NULL)
+			return (dobeep_msg("[No match]"));
+
 	}
 	return (remap(curmap, c, funct, pref_map));
 }
@@ -512,11 +494,9 @@ redefine_key(int f, int n)
 	else if (bufp[0] == '\0')
 		return (FALSE);
 	(void)strlcat(buf, tmp, sizeof(buf));
-	if ((mp = name_map(tmp)) == NULL) {
-		dobeep();
-		ewprintf("Unknown map %s", tmp);
-		return (FALSE);
-	}
+	if ((mp = name_map(tmp)) == NULL)
+		return (dobeep_msgs("Unknown map ", tmp));
+
 	if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
 		return (FALSE);
 
@@ -568,9 +548,7 @@ extend(int f, int n)
 		}
 		return ((*funct)(f, n));
 	}
-	dobeep();
-	ewprintf("[No match]");
-	return (FALSE);
+	return (dobeep_msg("[No match]"));
 }
 
 /*
@@ -595,7 +573,7 @@ extend(int f, int n)
 int
 evalexpr(int f, int n)
 {
-	char	 exbuf[128], *bufp;
+	char	 exbuf[BUFSIZE], *bufp;
 
 	if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
 	    EFNEW | EFCR)) == NULL)
@@ -616,18 +594,21 @@ evalbuffer(int f, int n)
 	struct line		*lp;
 	struct buffer		*bp = curbp;
 	int		 s;
-	static char	 excbuf[128];
+	static char	 excbuf[BUFSIZE];
 
 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
-		if (llength(lp) >= 128)
+		if (llength(lp) >= BUFSIZE)
 			return (FALSE);
 		(void)strncpy(excbuf, ltext(lp), llength(lp));
 
 		/* make sure it's terminated */
 		excbuf[llength(lp)] = '\0';
-		if ((s = excline(excbuf)) != TRUE)
+		if ((s = excline(excbuf)) != TRUE) {
+			(void) clearvars();
 			return (s);
+		}
 	}
+	(void) clearvars();
 	return (TRUE);
 }
 
@@ -657,7 +638,7 @@ load(const char *fname)
 {
 	int	 s = TRUE, line, ret;
 	int	 nbytes = 0;
-	char	 excbuf[128];
+	char	 excbuf[BUFSIZE], fncpy[NFILEN];
 	FILE    *ffp;
 
 	if ((fname = adjustname(fname, TRUE)) == NULL)
@@ -671,6 +652,8 @@ load(const char *fname)
 		return (FALSE);
 	}
 
+	/* keep a note of fname incase of errors in loaded file. */
+	(void)strlcpy(fncpy, fname, sizeof(fncpy));
 	line = 0;
 	while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
 	    == FIOSUC) {
@@ -679,7 +662,7 @@ load(const char *fname)
 		if (excline(excbuf) != TRUE) {
 			s = FIOERR;
 			dobeep();
-			ewprintf("Error loading file %s at line %d", fname, line);
+			ewprintf("Error loading file %s at line %d", fncpy, line);
 			break;
 		}
 	}
@@ -712,16 +695,16 @@ excline(char *line)
 
 	lp = NULL;
 
-	if (macrodef || inmacro) {
-		dobeep();
-		ewprintf("Not now!");
-		return (FALSE);
-	}
+	if (macrodef || inmacro)
+		return (dobeep_msg("Not now!"));
+
 	f = 0;
 	n = 1;
 	funcp = skipwhite(line);
 	if (*funcp == '\0')
 		return (TRUE);	/* No error on blank lines */
+	if (*funcp == '(')
+		return (foundparen(funcp));
 	line = parsetoken(funcp);
 	if (*line != '\0') {
 		*line++ = '\0';
@@ -740,11 +723,9 @@ excline(char *line)
 			return (FALSE);
 		n = (int)nl;
 	}
-	if ((fp = name_function(funcp)) == NULL) {
-		dobeep();
-		ewprintf("Unknown function: %s", funcp);
-		return (FALSE);
-	}
+	if ((fp = name_function(funcp)) == NULL)
+		return (dobeep_msgs("Unknown function: ", funcp));
+
 	if (fp == bindtokey || fp == unbindtokey) {
 		bind = BINDARG;
 		curmap = fundamental_map;
@@ -870,8 +851,7 @@ excline(char *line)
 		case BINDNEXT:
 			lp->l_text[lp->l_used] = '\0';
 			if ((curmap = name_map(lp->l_text)) == NULL) {
-				dobeep();
-				ewprintf("No such mode: %s", lp->l_text);
+				(void)dobeep_msgs("No such mode: ", lp->l_text);
 				status = FALSE;
 				free(lp);
 				goto cleanup;
@@ -888,8 +868,7 @@ excline(char *line)
 	}
 	switch (bind) {
 	default:
-		dobeep();
-		ewprintf("Bad args to set key");
+		(void)dobeep_msg("Bad args to set key");
 		status = FALSE;
 		break;
 	case BINDDO:
@@ -923,10 +902,10 @@ cleanup:
 /*
  * a pair of utility functions for the above
  */
-static char *
+char *
 skipwhite(char *s)
 {
-	while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
+	while (*s == ' ' || *s == '\t')
 		s++;
 	if ((*s == ';') || (*s == '#'))
 		*s = '\0';
diff --git a/file.c b/file.c
index 6483b6b..d6e0c8d 100644
--- a/file.c
+++ b/file.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: file.c,v 1.100 2016/01/02 10:39:19 lum Exp $	*/
+/*	$OpenBSD: file.c,v 1.102 2019/06/22 15:03:43 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -668,9 +668,10 @@ makebkfile(int f, int n)
 int
 writeout(FILE ** ffp, struct buffer *bp, char *fn)
 {
-	struct stat	statbuf;
-	int	 s;
-	char     dp[NFILEN];
+	struct stat	 statbuf;
+	struct line	*lpend;
+	int		 s, eobnl;
+	char		 dp[NFILEN];
 
 	if (stat(fn, &statbuf) == -1 && errno == ENOENT) {
 		errno = 0;
@@ -686,10 +687,17 @@ writeout(FILE ** ffp, struct buffer *bp, char *fn)
 			return (FIOERR);
 		}
         }
+	lpend = bp->b_headp;
+	eobnl = 0;
+	if (llength(lback(lpend)) != 0) {
+		eobnl = eyorn("No newline at end of file, add one");
+		if (eobnl != TRUE && eobnl != FALSE)
+			return (eobnl); /* abort */
+	}
 	/* open writes message */
 	if ((s = ffwopen(ffp, fn, bp)) != FIOSUC)
 		return (FALSE);
-	s = ffputbuf(*ffp, bp);
+	s = ffputbuf(*ffp, bp, eobnl);
 	if (s == FIOSUC) {
 		/* no write error */
 		s = ffclose(*ffp, bp);
diff --git a/fileio.c b/fileio.c
index c0e472d..a59e090 100644
--- a/fileio.c
+++ b/fileio.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: fileio.c,v 1.105 2018/04/13 14:11:37 florian Exp $	*/
+/*	$OpenBSD: fileio.c,v 1.106 2019/06/22 10:21:57 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -160,11 +160,12 @@ ffclose(FILE *ffp, struct buffer *bp)
  * buffer. Return the status.
  */
 int
-ffputbuf(FILE *ffp, struct buffer *bp)
+ffputbuf(FILE *ffp, struct buffer *bp, int eobnl)
 {
-	struct line   *lp, *lpend;
+	struct line	*lp, *lpend;
 
 	lpend = bp->b_headp;
+
 	for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
 		if (fwrite(ltext(lp), 1, llength(lp), ffp) != llength(lp)) {
 			dobeep();
@@ -174,14 +175,9 @@ ffputbuf(FILE *ffp, struct buffer *bp)
 		if (lforw(lp) != lpend)		/* no implied \n on last line */
 			putc('\n', ffp);
 	}
-	/*
-	 * XXX should be variable controlled (once we have variables)
-	 */
-	if (llength(lback(lpend)) != 0) {
-		if (eyorn("No newline at end of file, add one") == TRUE) {
-			lnewline_at(lback(lpend), llength(lback(lpend)));
-			putc('\n', ffp);
-		}
+	if (eobnl) {
+		lnewline_at(lback(lpend), llength(lback(lpend)));
+		putc('\n', ffp);
 	}
 	return (FIOSUC);
 }
diff --git a/funmap.c b/funmap.c
index def4047..20adbf6 100644
--- a/funmap.c
+++ b/funmap.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: funmap.c,v 1.54 2018/08/29 07:50:16 reyk Exp $	*/
+/*	$OpenBSD: funmap.c,v 1.59 2019/07/11 18:20:18 lum Exp $	*/
 
 /* This file is in the public domain */
 
@@ -13,211 +13,228 @@
 #include "kbd.h"
 
 /*
- * If the function is NULL, it must be listed with the
- * same name in the map_table.
+ * funmap structure: a list of functions and their command-names/#parameters.
+ *
+ * If the function is NULL, it must be listed with the same name in the
+ * map_table.
  */
-
 struct funmap {
 	PF		 fn_funct;
 	const		 char *fn_name;
+	int		 fn_nparams;
 	struct funmap	*fn_next;
 };
-
 static struct funmap *funs;
 
+/*
+ * 3rd column in the functnames structure indicates how many parameters the
+ * function takes in 'normal' usage. This column is only used to identify
+ * function profiles when lines of a buffer are being evaluated via excline().
+ *
+ *  0 = a toggle, non-modifiable insert/delete, region modifier, etc
+ *  1 = value can be string or number value (like: file/buf name, search string)
+ *  2 = multiple type value required, see auto-execute, or global-set-key, etc
+ * -1 = variable length # parameters (unused at moment)
+ *
+ * Some functions when used interactively may ask for a 'y' or 'n' (or another
+ * character) to continue, in excline, a 'y' is assumed. Functions like this
+ * have '0' in the 3rd column below.
+ */
 static struct funmap functnames[] = {
-	{apropos_command, "apropos",},
-	{toggleaudiblebell, "audible-bell",},
-	{auto_execute, "auto-execute",},
-	{fillmode, "auto-fill-mode",},
-	{indentmode, "auto-indent-mode",},
-	{backtoindent, "back-to-indentation",},
-	{backuptohomedir, "backup-to-home-directory",},
-	{backchar, "backward-char",},
-	{delbword, "backward-kill-word",},
-	{gotobop, "backward-paragraph",},
-	{backword, "backward-word",},
-	{gotobob, "beginning-of-buffer",},
-	{gotobol, "beginning-of-line",},
-	{showmatch, "blink-and-insert",},
-	{bsmap, "bsmap-mode",},
-	{NULL, "c-x 4 prefix",},
-	{NULL, "c-x prefix",},
-	{executemacro, "call-last-kbd-macro",},
-	{capword, "capitalize-word",},
-	{changedir, "cd",},
-	{clearmark, "clear-mark",},
-	{colnotoggle, "column-number-mode",},
-	{copyregion, "copy-region-as-kill",},
+	{apropos_command, "apropos", 1},
+	{toggleaudiblebell, "audible-bell", 0},
+	{auto_execute, "auto-execute", 2},
+	{fillmode, "auto-fill-mode", 0},
+	{indentmode, "auto-indent-mode", 0},
+	{backtoindent, "back-to-indentation", 0},
+	{backuptohomedir, "backup-to-home-directory", 0},
+	{backchar, "backward-char", 0},
+	{delbword, "backward-kill-word", 0},
+	{gotobop, "backward-paragraph", 0},
+	{backword, "backward-word", 0},
+	{gotobob, "beginning-of-buffer", 0},
+	{gotobol, "beginning-of-line", 0},
+	{showmatch, "blink-and-insert", 1},		/* startup only	*/
+	{bsmap, "bsmap-mode", 0},
+	{NULL, "c-x 4 prefix", 0},			/* internal	*/
+	{NULL, "c-x prefix", 0},			/* internal	*/
+	{executemacro, "call-last-kbd-macro", 0},
+	{capword, "capitalize-word", 0},
+	{changedir, "cd", 1},
+	{clearmark, "clear-mark", 0},
+	{colnotoggle, "column-number-mode", 0},
+	{copyregion, "copy-region-as-kill", 0},
 #ifdef	REGEX
-	{cntmatchlines, "count-matches",},
-	{cntnonmatchlines, "count-non-matches",},
+	{cntmatchlines, "count-matches", 1},
+	{cntnonmatchlines, "count-non-matches", 1},
 #endif /* REGEX */
-	{cscreatelist, "cscope-create-list-of-files-to-index",},
-	{csfuncalled, "cscope-find-called-functions",},
-	{csegrep, "cscope-find-egrep-pattern",},
-	{csfindinc, "cscope-find-files-including-file",},
-	{cscallerfuncs, "cscope-find-functions-calling-this-function",},
-	{csdefinition, "cscope-find-global-definition",},
-	{csfindfile, "cscope-find-this-file",},
-	{cssymbol, "cscope-find-this-symbol",},
-	{csfindtext, "cscope-find-this-text-string",},
-	{csnextfile, "cscope-next-file",},
-	{csnextmatch, "cscope-next-symbol",},
-	{csprevfile, "cscope-prev-file",},
-	{csprevmatch, "cscope-prev-symbol",},
-	{redefine_key, "define-key",},
-	{backdel, "delete-backward-char",},
-	{deblank, "delete-blank-lines",},
-	{forwdel, "delete-char",},
-	{delwhite, "delete-horizontal-space",},
-	{delleadwhite, "delete-leading-space",},
+	{cscreatelist, "cscope-create-list-of-files-to-index", 1},
+	{csfuncalled, "cscope-find-called-functions", 1},
+	{csegrep, "cscope-find-egrep-pattern", 1},
+	{csfindinc, "cscope-find-files-including-file", 1},
+	{cscallerfuncs, "cscope-find-functions-calling-this-function", 1},
+	{csdefinition, "cscope-find-global-definition", 1},
+	{csfindfile, "cscope-find-this-file", 1},
+	{cssymbol, "cscope-find-this-symbol", 1},
+	{csfindtext, "cscope-find-this-text-string", 1},
+	{csnextfile, "cscope-next-file", 0},
+	{csnextmatch, "cscope-next-symbol", 0},
+	{csprevfile, "cscope-prev-file", 0},
+	{csprevmatch, "cscope-prev-symbol", 0},
+	{redefine_key, "define-key", 3},
+	{backdel, "delete-backward-char", 0},
+	{deblank, "delete-blank-lines", 0},
+	{forwdel, "delete-char", 0},
+	{delwhite, "delete-horizontal-space", 0},
+	{delleadwhite, "delete-leading-space", 0},
 #ifdef	REGEX
-	{delmatchlines, "delete-matching-lines",},
-	{delnonmatchlines, "delete-non-matching-lines",},
+	{delmatchlines, "delete-matching-lines", 1},
+	{delnonmatchlines, "delete-non-matching-lines", 1},
 #endif /* REGEX */
-	{onlywind, "delete-other-windows",},
-	{deltrailwhite, "delete-trailing-space",},
-	{delwind, "delete-window",},
-	{wallchart, "describe-bindings",},
-	{desckey, "describe-key-briefly",},
-	{diffbuffer, "diff-buffer-with-file",},
-	{digit_argument, "digit-argument",},
-	{lowerregion, "downcase-region",},
-	{lowerword, "downcase-word",},
-	{showversion, "emacs-version",},
-	{finishmacro, "end-kbd-macro",},
-	{gotoeob, "end-of-buffer",},
-	{gotoeol, "end-of-line",},
-	{enlargewind, "enlarge-window",},
-	{NULL, "esc prefix",},
-	{evalbuffer, "eval-current-buffer",},
-	{evalexpr, "eval-expression",},
-	{swapmark, "exchange-point-and-mark",},
-	{extend, "execute-extended-command",},
-	{fillpara, "fill-paragraph",},
-	{filevisitalt, "find-alternate-file",},
-	{filevisit, "find-file",},
-	{poptofile, "find-file-other-window",},
-	{filevisitro, "find-file-read-only",},
-	{findtag, "find-tag",},
-	{forwchar, "forward-char",},
-	{gotoeop, "forward-paragraph",},
-	{forwword, "forward-word",},
-	{bindtokey, "global-set-key",},
-	{unbindtokey, "global-unset-key",},
-	{globalwdtoggle, "global-wd-mode",},
-	{gotoline, "goto-line",},
-	{help_help, "help-help",},
-	{indent, "indent-current-line",},
-	{insert, "insert",},
-	{bufferinsert, "insert-buffer",},
-	{fileinsert, "insert-file",},
-	{fillword, "insert-with-wrap",},
-	{backisearch, "isearch-backward",},
-	{forwisearch, "isearch-forward",},
-	{joinline, "join-line",},
-	{justone, "just-one-space",},
-	{ctrlg, "keyboard-quit",},
-	{killbuffer_cmd, "kill-buffer",},
-	{killline, "kill-line",},
-	{killpara, "kill-paragraph",},
-	{killregion, "kill-region",},
-	{delfword, "kill-word",},
-	{toggleleavetmp, "leave-tmpdir-backups",},
-	{linenotoggle, "line-number-mode",},
-	{listbuffers, "list-buffers",},
-	{evalfile, "load",},
-	{localbind, "local-set-key",},
-	{localunbind, "local-unset-key",},
-	{makebkfile, "make-backup-files",},
-	{makedir, "make-directory",},
-	{markpara, "mark-paragraph",},
-	{markbuffer, "mark-whole-buffer",},
-	{do_meta, "meta-key-mode",},	/* better name, anyone? */
-	{negative_argument, "negative-argument",},
-	{enewline, "newline",},
-	{lfindent, "newline-and-indent",},
-	{forwline, "next-line",},
+	{onlywind, "delete-other-windows", 0},
+	{deltrailwhite, "delete-trailing-space", 0},
+	{delwind, "delete-window", 0},
+	{wallchart, "describe-bindings", 0},
+	{desckey, "describe-key-briefly", 1},
+	{diffbuffer, "diff-buffer-with-file", 0},
+	{digit_argument, "digit-argument", 1},
+	{lowerregion, "downcase-region", 0},
+	{lowerword, "downcase-word", 0},
+	{showversion, "emacs-version", 0},
+	{finishmacro, "end-kbd-macro", 0},
+	{gotoeob, "end-of-buffer", 0},
+	{gotoeol, "end-of-line", 0},
+	{enlargewind, "enlarge-window", 0},
+	{NULL, "esc prefix", 0},			/* internal	*/
+	{evalbuffer, "eval-current-buffer", 0},
+	{evalexpr, "eval-expression", 0},
+	{swapmark, "exchange-point-and-mark", 0},
+	{extend, "execute-extended-command", 1},
+	{fillpara, "fill-paragraph", 0},
+	{filevisitalt, "find-alternate-file", 1},
+	{filevisit, "find-file", 1},
+	{poptofile, "find-file-other-window", 1},
+	{filevisitro, "find-file-read-only", 1},
+	{findtag, "find-tag", 1},
+	{forwchar, "forward-char", 0},
+	{gotoeop, "forward-paragraph", 0},
+	{forwword, "forward-word", 0},
+	{bindtokey, "global-set-key", 2},
+	{unbindtokey, "global-unset-key", 1},
+	{globalwdtoggle, "global-wd-mode", 0},
+	{gotoline, "goto-line", 1},
+	{help_help, "help-help", 0},
+	{indent, "indent-current-line", 0},
+	{insert, "insert", 1},
+	{bufferinsert, "insert-buffer", 1},
+	{fileinsert, "insert-file", 1},
+	{fillword, "insert-with-wrap", 1},		/* startup only */
+	{backisearch, "isearch-backward", 1},
+	{forwisearch, "isearch-forward", 1},
+	{joinline, "join-line", 0},
+	{justone, "just-one-space", 0},
+	{ctrlg, "keyboard-quit", 0},
+	{killbuffer_cmd, "kill-buffer", 1},
+	{killline, "kill-line", 0},
+	{killpara, "kill-paragraph", 0},
+	{killregion, "kill-region", 0},
+	{delfword, "kill-word", 0},
+	{toggleleavetmp, "leave-tmpdir-backups", 0},
+	{linenotoggle, "line-number-mode", 0},
+	{listbuffers, "list-buffers", 0},
+	{evalfile, "load", 1},
+	{localbind, "local-set-key", 1},
+	{localunbind, "local-unset-key", 1},
+	{makebkfile, "make-backup-files", 0},
+	{makedir, "make-directory", 1},
+	{markpara, "mark-paragraph", 0},
+	{markbuffer, "mark-whole-buffer", 0},
+	{do_meta, "meta-key-mode", 0},	/* better name, anyone? */
+	{negative_argument, "negative-argument", 1},
+	{enewline, "newline", 0},
+	{lfindent, "newline-and-indent", 0},
+	{forwline, "next-line", 0},
 #ifdef NOTAB
-	{notabmode, "no-tab-mode",},
+	{notabmode, "no-tab-mode", 0},
 #endif /* NOTAB */
-	{notmodified, "not-modified",},
-	{openline, "open-line",},
-	{nextwind, "other-window",},
-	{overwrite_mode, "overwrite-mode",},
-	{poptag, "pop-tag-mark",},
-	{prefixregion, "prefix-region",},
-	{backline, "previous-line",},
-	{prevwind, "previous-window",},
-	{spawncli, "push-shell",},
-	{showcwdir, "pwd",},
-	{queryrepl, "query-replace",},
+	{notmodified, "not-modified", 0},
+	{openline, "open-line", 0},
+	{nextwind, "other-window", 0},
+	{overwrite_mode, "overwrite-mode", 0},
+	{poptag, "pop-tag-mark", 0},
+	{prefixregion, "prefix-region", 0},
+	{backline, "previous-line", 0},
+	{prevwind, "previous-window", 0},
+	{spawncli, "push-shell", 0},
+	{showcwdir, "pwd", 0},
+	{queryrepl, "query-replace", 1},
 #ifdef REGEX
-	{re_queryrepl, "query-replace-regexp",},
+	{re_queryrepl, "query-replace-regexp", 1},
 #endif /* REGEX */
-	{quote, "quoted-insert",},
+	{quote, "quoted-insert", 1},
 #ifdef REGEX
-	{re_searchagain, "re-search-again",},
-	{re_backsearch, "re-search-backward",},
-	{re_forwsearch, "re-search-forward",},
+	{re_searchagain, "re-search-again", 0},
+	{re_backsearch, "re-search-backward", 0},
+	{re_forwsearch, "re-search-forward", 0},
 #endif /* REGEX */
-	{reposition, "recenter",},
-	{redraw, "redraw-display",},
+	{reposition, "recenter", 0},
+	{redraw, "redraw-display", 0},
 #ifdef REGEX
-	{replstr, "replace-string",},
+	{replstr, "replace-string", 2},
 #endif /* REGEX */
-	{revertbuffer, "revert-buffer",},
-	{filesave, "save-buffer",},
-	{quit, "save-buffers-kill-emacs",},
-	{savebuffers, "save-some-buffers",},
-	{backpage, "scroll-down",},
-	{back1page, "scroll-one-line-down",},
-	{forw1page, "scroll-one-line-up",},
-	{pagenext, "scroll-other-window",},
-	{forwpage, "scroll-up",},
-	{searchagain, "search-again",},
-	{backsearch, "search-backward",},
-	{forwsearch, "search-forward",},
-	{selfinsert, "self-insert-command",},
-	{sentencespace, "sentence-end-double-space",},
+	{revertbuffer, "revert-buffer", 0},
+	{filesave, "save-buffer", 0},
+	{quit, "save-buffers-kill-emacs", 0},
+	{savebuffers, "save-some-buffers", 0},
+	{backpage, "scroll-down", 0},
+	{back1page, "scroll-one-line-down", 0},
+	{forw1page, "scroll-one-line-up", 0},
+	{pagenext, "scroll-other-window", 0},
+	{forwpage, "scroll-up", 0},
+	{searchagain, "search-again", 0},
+	{backsearch, "search-backward", 0},
+	{forwsearch, "search-forward", 0},
+	{ask_selfinsert, "self-insert-char", 1},
+	{selfinsert, "self-insert-command", 1},		/* startup only */
+	{sentencespace, "sentence-end-double-space", 0},
 #ifdef REGEX
-	{setcasefold, "set-case-fold-search",},
+	{setcasefold, "set-case-fold-search", 0},
 #endif /* REGEX */
-	{setcasereplace, "set-case-replace",},
-	{set_default_mode, "set-default-mode",},
-	{setfillcol, "set-fill-column",},
-	{setmark, "set-mark-command",},
-	{setprefix, "set-prefix-string",},
-	{shellcommand, "shell-command",},
-	{piperegion, "shell-command-on-region",},
-	{shrinkwind, "shrink-window",},
+	{setcasereplace, "set-case-replace", 0},
+	{set_default_mode, "set-default-mode", 1},
+	{setfillcol, "set-fill-column", 1},
+	{setmark, "set-mark-command", 0},
+	{setprefix, "set-prefix-string", 1},
+	{shellcommand, "shell-command", 1},
+	{piperegion, "shell-command-on-region", 0},
+	{shrinkwind, "shrink-window", 0},
 #ifdef NOTAB
-	{space_to_tabstop, "space-to-tabstop",},
+	{space_to_tabstop, "space-to-tabstop", 0},
 #endif /* NOTAB */
-	{splitwind, "split-window-vertically",},
-	{definemacro, "start-kbd-macro",},
-	{spawncli, "suspend-emacs",},
-	{usebuffer, "switch-to-buffer",},
-	{poptobuffer, "switch-to-buffer-other-window",},
-	{togglereadonly, "toggle-read-only" },
-	{twiddle, "transpose-chars",},
-	{transposepara, "transpose-paragraphs",},
-	{transposeword, "transpose-words",},
-	{undo, "undo",},
-	{undo_add_boundary, "undo-boundary",},
-	{undo_boundary_enable, "undo-boundary-toggle",},
-	{undo_enable, "undo-enable",},
-	{undo_dump, "undo-list",},
-	{universal_argument, "universal-argument",},
-	{upperregion, "upcase-region",},
-	{upperword, "upcase-word",},
-	{togglevisiblebell, "visible-bell",},
-	{tagsvisit, "visit-tags-table",},
-	{showcpos, "what-cursor-position",},
-	{filewrite, "write-file",},
-	{yank, "yank",},
-	{NULL, NULL,}
+	{splitwind, "split-window-vertically", 0},
+	{definemacro, "start-kbd-macro", 0},
+	{spawncli, "suspend-emacs", 0},
+	{usebuffer, "switch-to-buffer", 1},
+	{poptobuffer, "switch-to-buffer-other-window", 1},
+	{togglereadonly, "toggle-read-only", 0},
+	{togglereadonlyall, "toggle-read-only-all", 0},
+	{twiddle, "transpose-chars", 0},
+	{transposepara, "transpose-paragraphs", 0},
+	{transposeword, "transpose-words", 0},
+	{undo, "undo", 0},
+	{undo_add_boundary, "undo-boundary", 0},
+	{undo_boundary_enable, "undo-boundary-toggle", 0},
+	{undo_enable, "undo-enable", 0},
+	{undo_dump, "undo-list", 0},
+	{universal_argument, "universal-argument", 1},
+	{upperregion, "upcase-region", 0},
+	{upperword, "upcase-word", 0},
+	{togglevisiblebell, "visible-bell", 0},
+	{tagsvisit, "visit-tags-table", 0},
+	{showcpos, "what-cursor-position", 0},
+	{filewrite, "write-file", 1},
+	{yank, "yank", 0},
+	{NULL, NULL, 0}
 };
 
 void
@@ -232,7 +249,7 @@ funmap_init(void)
 }
 
 int
-funmap_add(PF fun, const char *fname)
+funmap_add(PF fun, const char *fname, int fparams)
 {
 	struct funmap *fn;
 
@@ -241,6 +258,7 @@ funmap_add(PF fun, const char *fname)
 
 	fn->fn_funct = fun;
 	fn->fn_name = fname;
+	fn->fn_nparams = fparams;
 	fn->fn_next = funs;
 
 	funs = fn;
@@ -299,3 +317,18 @@ complete_function_list(const char *fname)
 	}
 	return (head);
 }
+
+/*
+ * Find number of parameters for function name.
+ */
+int
+numparams_function(PF fun)
+{
+	struct funmap *fn;
+
+	for (fn = funs; fn != NULL; fn = fn->fn_next) {
+		if (fn->fn_funct == fun)
+			return (fn->fn_nparams);
+	}
+	return (FALSE);
+}
diff --git a/funmap.h b/funmap.h
index acc24ae..b5bca71 100644
--- a/funmap.h
+++ b/funmap.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: funmap.h,v 1.7 2008/06/10 00:19:31 kjell Exp $	*/
+/*	$OpenBSD: funmap.h,v 1.8 2019/07/11 18:20:18 lum Exp $	*/
 
 /* This file is in the public domain */
 
@@ -6,4 +6,5 @@ void		 funmap_init(void);
 PF		 name_function(const char *);
 const char	*function_name(PF);
 struct list	*complete_function_list(const char *);
-int		 funmap_add(PF, const char *);
+int		 funmap_add(PF, const char *, int);
+int		 numparams_function(PF);
diff --git a/grep.c b/grep.c
index d2c257f..97d22c6 100644
--- a/grep.c
+++ b/grep.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: grep.c,v 1.46 2018/01/09 17:59:29 cheloha Exp $	*/
+/*	$OpenBSD: grep.c,v 1.48 2019/07/11 18:20:18 lum Exp $	*/
 
 /* This file is in the public domain */
 
@@ -7,7 +7,6 @@
 #include <sys/wait.h>
 
 #include <ctype.h>
-#include <libgen.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
@@ -54,11 +53,11 @@ static struct KEYMAPE (1) compilemap = {
 void
 grep_init(void)
 {
-	funmap_add(compile_goto_error, "compile-goto-error");
-	funmap_add(next_error, "next-error");
-	funmap_add(grep, "grep");
-	funmap_add(compile, "compile");
-	funmap_add(gid, "gid");
+	funmap_add(compile_goto_error, "compile-goto-error", 0);
+	funmap_add(next_error, "next-error", 0);
+	funmap_add(grep, "grep", 1);
+	funmap_add(compile, "compile", 0);
+	funmap_add(gid, "gid", 1);
 	maps_add((KEYMAP *)&compilemap, "compile");
 }
 
diff --git a/interpreter.c b/interpreter.c
new file mode 100644
index 0000000..6c954d3
--- /dev/null
+++ b/interpreter.c
@@ -0,0 +1,421 @@
+/*      $OpenBSD: interpreter.c,v 1.5 2019/07/20 11:06:33 lum Exp $	*/
+/*
+ * This file is in the public domain.
+ *
+ * Author: Mark Lumsden <mark@showcomplex.com>
+ */
+
+/*
+ * This file attempts to add some 'scripting' functionality into mg.
+ *
+ * The initial goal is to give mg the ability to use it's existing functions
+ * and structures in a linked-up way. Hopefully resulting in user definable
+ * functions. The syntax is 'scheme' like but currently it is not a scheme
+ * interpreter.
+ *
+ * At the moment there is no manual page reference to this file. The code below
+ * is liable to change, so use at your own risk!
+ *
+ * If you do want to do some testing, you can add some lines to your .mg file
+ * like:
+ * 
+ * 1. Give multiple arguments to a function that usually would accept only one:
+ * (find-file a.txt b.txt. c.txt)
+ *
+ * 2. Define a list:
+ * (define myfiles(list d.txt e.txt))
+ *
+ * 3. Use the previously defined list:
+ * (find-file myfiles)
+ *
+ * To do:
+ * 1. multiline parsing - currently only single lines supported.
+ * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
+ * 3. conditional execution.
+ * 4. define single value variables (define i 0)
+ * 5. deal with quotes around a string: "x x"
+ * 6. oh so many things....
+ * [...]
+ * n. implement user definable functions.
+ */
+#include <sys/queue.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "funmap.h"
+
+#ifdef  MGLOG
+#include "kbd.h"
+#include "log.h"
+#endif
+
+static int	 multiarg(char *);
+static int	 isvar(char **, char **, int);
+static int	 foundvar(char *);
+static int	 foundlist(char *);
+
+
+/*
+ * Structure for variables during buffer evaluation.
+ */
+struct varentry {
+	SLIST_ENTRY(varentry) entry;
+	char	*name;
+	char	*vals;
+	int	 count;
+};
+SLIST_HEAD(vlisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead);
+
+/*
+ * Pass a list of arguments to a function.
+ */
+static int
+multiarg(char *funstr)
+{
+	regex_t  regex_buff;
+	PF	 funcp;
+	char	 excbuf[BUFSIZE], argbuf[BUFSIZE], *contbuf, tmpbuf[BUFSIZE];
+	char	*cmdp, *argp, *fendp, *endp, *p, *t, *s = " ";
+	int	 singlecmd = 0, spc, numparams, numspc;
+	int	 inlist, foundlst = 0, eolst, rpar, sizof, fin;
+	
+	contbuf = NULL;
+	endp = strrchr(funstr, ')');
+	if (endp == NULL) {
+		ewprintf("No closing parenthesis found");
+		return(FALSE);
+	}
+	p = endp + 1;
+	if (*p != '\0')
+		*p = '\0';
+	/* we now know that string starts with '(' and ends with ')' */
+	if (regcomp(&regex_buff, "^[(][\t ]*[)]$", REG_EXTENDED)) {
+		regfree(&regex_buff);
+		return (dobeep_msg("Could not compile regex"));
+	}
+	if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+		regfree(&regex_buff);
+		return (dobeep_msg("No command found"));
+	}
+	/* currently there are no mg commands that don't have a letter */
+	if (regcomp(&regex_buff, "^[(][\t ]*[A-Za-z-]+[\t ]*[)]$",
+	    REG_EXTENDED)) {
+		regfree(&regex_buff);
+		return (dobeep_msg("Could not compile regex"));
+	}
+	if (!regexec(&regex_buff, funstr, 0, NULL, 0))
+		singlecmd = 1;
+
+	regfree(&regex_buff);
+	p = funstr + 1;		/* move past first '(' char.	*/
+	cmdp = skipwhite(p);	/* find first char of command.	*/
+
+	if (singlecmd) {
+		/* remove ')', then check for spaces at the end */
+		cmdp[strlen(cmdp) - 1] = '\0'; 
+		if ((fendp = strchr(cmdp, ' ')) != NULL)
+			*fendp = '\0';
+		else if ((fendp = strchr(cmdp, '\t')) != NULL)
+			*fendp = '\0';
+		return(excline(cmdp));
+	}
+	if ((fendp = strchr(cmdp, ' ')) == NULL) 
+		fendp = strchr(cmdp, '\t');
+
+	*fendp = '\0';
+	/*
+	 * If no extant mg command found, just return.
+	 */
+	if ((funcp = name_function(cmdp)) == NULL)
+		return (dobeep_msgs("Unknown command: ", cmdp));
+
+	numparams = numparams_function(funcp);
+	if (numparams == 0)
+		return (dobeep_msgs("Command takes no arguments: ", cmdp));
+
+	/* now find the first argument */
+	p = fendp + 1;
+	p = skipwhite(p);
+	if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
+		return (dobeep_msg("strlcpy error"));
+	argp = argbuf;
+	numspc = spc = 1; /* initially fake a space so we find first argument */
+	inlist = eolst = fin = rpar = 0;
+
+	for (p = argp; fin == 0; p++) {
+#ifdef  MGLOG
+		mglog_execbuf("", excbuf, argbuf, argp, eolst, inlist, cmdp,
+		    p, contbuf);
+#endif
+		if (foundlst) {
+			foundlst = 0;
+			p--;	/* otherwise 1st arg is missed from list. */
+		}
+		if (*p == ')') {
+			rpar = 1;
+			*p = '\0';
+		}
+		if (*p == ' ' || *p == '\t' || *p == '\0') {
+			if (spc == 1)
+				continue;
+			if (spc == 0 && (numspc % numparams == 0)) {
+				if (*p == '\0')
+					eolst = 1;
+				else
+					eolst = 0;
+				*p = '\0'; 	/* terminate arg string */
+				endp = p + 1;			
+				excbuf[0] = '\0';
+				/* Is arg a var? */
+				if (!inlist) {
+					sizof = sizeof(tmpbuf);
+					t = tmpbuf;
+					if (isvar(&argp, &t, sizof)) {
+						if ((contbuf = strndup(endp,
+						    BUFSIZE)) == NULL)
+							return(FALSE);
+						*p = ' ';
+						(void)(strlcpy(argbuf, tmpbuf,
+						    sizof) >= sizof);
+						p = argp = argbuf;
+						spc = 1;
+						foundlst = inlist = 1;
+						continue;
+					}
+				}				
+				if (strlcpy(excbuf, cmdp, sizeof(excbuf))
+				     >= sizeof(excbuf))
+					return (dobeep_msg("strlcpy error"));
+				if (strlcat(excbuf, s, sizeof(excbuf))
+				    >= sizeof(excbuf))
+					return (dobeep_msg("strlcat error"));
+				if (strlcat(excbuf, argp, sizeof(excbuf))
+				    >= sizeof(excbuf))
+					return (dobeep_msg("strlcat error"));
+
+				excline(excbuf);
+#ifdef  MGLOG
+				mglog_execbuf("  ", excbuf, argbuf, argp,
+				    eolst, inlist, cmdp, p, contbuf);
+#endif
+				*p = ' ';	/* so 'for' loop can continue */
+				if (eolst) {
+					if (contbuf != NULL) {
+						(void)strlcpy(argbuf, contbuf,
+						    sizeof(argbuf));
+						free(contbuf);
+						contbuf = NULL;
+						p = argp = argbuf;
+						foundlst = 1;
+						inlist = 0;
+						if (rpar)
+							fin = 1;
+						continue;
+					}
+					spc = 1;
+					inlist = 0;
+				}
+				if (eolst && rpar)
+					fin = 1;
+			}
+			numspc++;
+			spc = 1;
+		} else {
+			if (spc == 1)
+				if ((numparams == 1) ||
+				    ((numspc + 1) % numparams) == 0)
+					argp = p;
+			spc = 0;
+		}
+	}
+	return (TRUE);
+}
+
+
+/*
+ * Is an item a value or a variable?
+ */
+static int
+isvar(char **argp, char **tmpbuf, int sizof)
+{
+	struct varentry *v1 = NULL;
+
+	if (SLIST_EMPTY(&varhead))
+		return (FALSE);
+#ifdef  MGLOG
+	mglog_isvar(*tmpbuf, *argp, sizof);
+#endif
+	SLIST_FOREACH(v1, &varhead, entry) {
+		if (strcmp(*argp, v1->name) == 0) {
+			(void)(strlcpy(*tmpbuf, v1->vals, sizof) >= sizof);
+			return (TRUE);
+		}
+	}
+	return (FALSE);
+}
+
+
+/*
+ * The (define string _must_ adhere to the regex in foundparen.
+ * This is not the correct way to do parsing but it does highlight
+ * the issues.
+ */
+static int
+foundlist(char *defstr)
+{
+	struct varentry *vt, *v1 = NULL;
+	const char	 e[1] = "e", t[1] = "t";
+	char		*p, *vnamep, *vendp = NULL, *valp, *o;
+	int		 spc;
+
+
+	p = defstr + 1;         /* move past first '(' char.    */
+	p = skipwhite(p);    	/* find first char of 'define'. */
+	p = strstr(p, e);	/* find first 'e' in 'define'.	*/
+	p = strstr(++p, e);	/* find second 'e' in 'define'.	*/
+	p++;			/* move past second 'e'.	*/
+	vnamep = skipwhite(p);  /* find first char of var name. */
+	vendp = vnamep;
+
+	/* now find the end of the list name */
+	while (1) {
+		++vendp;
+		if (*vendp == '(' || *vendp == ' ' || *vendp == '\t')
+			break;
+	}
+	*vendp = '\0';
+	/*
+	 * Check list name is not an existing function.
+	 * Although could this be allowed? Shouldn't context dictate?
+	 */
+	if (name_function(vnamep) != NULL)
+		return(dobeep_msgs("Variable/function name clash:", vnamep));
+
+	p = ++vendp;
+	p = strstr(p, t);	/* find 't' in 'list'.	*/
+	valp = skipwhite(++p);	/* find first value	*/
+	/*
+	 * Now we have the name of the list starting at 'vnamep',
+	 * and the first value is at 'valp', record the details
+	 * in a linked list. But first remove variable, if existing already.
+	 */
+	if (!SLIST_EMPTY(&varhead)) {
+		SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
+			if (strcmp(vnamep, v1->name) == 0)
+				SLIST_REMOVE(&varhead, v1, varentry, entry);
+		}
+	}
+	if ((v1 = malloc(sizeof(struct varentry))) == NULL)
+		return (ABORT);
+	SLIST_INSERT_HEAD(&varhead, v1, entry);
+	if ((v1->name = strndup(vnamep, BUFSIZE)) == NULL)
+		return(dobeep_msg("strndup error"));
+	v1->count = 0;
+	vendp = NULL;
+	
+	/* initially fake a space so we find first value */
+	spc = 1;
+	/* now loop through values in list value string while counting them */
+	for (p = valp; *p != '\0'; p++) {
+		if (*p == ' ' || *p == '\t') {
+			if (spc == 0)
+				vendp = p;
+			spc = 1;
+		} else if (*p == ')') {
+			o = p - 1;
+			if (*o != ' ' && *o != '\t')
+				vendp = p;
+			break;
+		} else {
+			if (spc == 1)
+				v1->count++;
+			spc = 0;
+		}
+	}
+	*vendp = '\0';
+	if ((v1->vals = strndup(valp, BUFSIZE)) == NULL)
+		return(dobeep_msg("strndup error"));
+
+	return (TRUE);
+}
+
+
+/*
+ * to do
+ */
+static int
+foundvar(char *funstr)
+{
+	ewprintf("to do");
+	return (TRUE);
+}
+
+/*
+ * Finished with evaluation, so clean up any vars.
+ */
+int
+clearvars(void)
+{
+	struct varentry	*v1 = NULL;
+
+	while (!SLIST_EMPTY(&varhead)) {
+		v1 = SLIST_FIRST(&varhead);
+		SLIST_REMOVE_HEAD(&varhead, entry);
+		free(v1->vals);
+		free(v1->name);
+		free(v1);
+	}
+	return (FALSE);
+}
+
+/*
+ * Line has a '(' as the first non-white char.
+ * Do some very basic parsing of line with '(' as the first character.
+ * Multi-line not supported at the moment, To do.
+ */
+int
+foundparen(char *funstr)
+{
+	regex_t  regex_buff;
+	char	*regs;
+
+	/* Does the line have a list 'define' like: */
+	/* (define alist(list 1 2 3 4)) */
+	regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[(][\t ]*list[\t ]+"\
+		"[^\t ]+.*[)][\t ]*[)]";
+	if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+		regfree(&regex_buff);
+		return(dobeep_msg("Could not compile regex"));
+	}
+	if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+		regfree(&regex_buff);
+		return(foundlist(funstr));
+	}
+	/* Does the line have a single variable 'define' like: */
+	/* (define i 0) */
+	regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[^\t (]+[\t ]*[)]";
+	if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+		regfree(&regex_buff);
+		return(dobeep_msg("Could not compile regex"));
+	}
+	if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+		regfree(&regex_buff);
+		return(foundvar(funstr));
+	}
+	/* Does the line have an unrecognised 'define' */
+	regs = "^[(][\t ]*define[\t ]+";
+	if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+		regfree(&regex_buff);
+		return(dobeep_msg("Could not compile regex"));
+	}
+	if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+		regfree(&regex_buff);
+		return(dobeep_msg("Invalid use of define"));
+	}
+	regfree(&regex_buff);
+	return(multiarg(funstr));
+}
diff --git a/kbd.c b/kbd.c
index 1d7a1a2..06d6c9f 100644
--- a/kbd.c
+++ b/kbd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: kbd.c,v 1.30 2015/09/26 21:51:58 jasper Exp $	*/
+/*	$OpenBSD: kbd.c,v 1.33 2019/07/03 18:11:07 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -15,6 +15,10 @@
 #include "key.h"
 #include "macro.h"
 
+#ifdef  MGLOG
+#include "log.h"
+#endif
+
 #define METABIT 0x80
 
 #define PROMPTL 80
@@ -152,6 +156,11 @@ doin(void)
 	    getkey(TRUE)), &curmap)) == NULL)
 		/* nothing */;
 
+#ifdef  MGLOG
+	if (!mglog(funct, curmap))
+		ewprintf("Problem with logging");
+#endif
+
 	if (macrodef && macrocount < MAXMACRO)
 		macro[macrocount++].m_funct = funct;
 
@@ -380,6 +389,29 @@ selfinsert(int f, int n)
 	return (linsert(n, c));
 }
 
+/*
+ * selfinsert() can't be called directly from a startup file or by
+ * 'eval-current-buffer' since it is by design, meant to be called interactively
+ * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to
+ * be used by excline(). Having ask_selfinsert() helps with regression testing.
+ * No manual page entry since use case is a bit obscure. See 'insert' command.
+ */
+int
+ask_selfinsert(int f, int n)
+{
+	char	*c, cbuf[2];
+
+	if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf),
+	    EFNEW)) == NULL || (c[0] == '\0'))
+		return (ABORT);
+
+	key.k_chars[0] = *c;
+	key.k_chars[1] = '\0';
+	key.k_count = 1;
+
+	return (selfinsert(FFRAND, 1));
+}
+
 /*
  * This could be implemented as a keymap with everything defined as self-insert.
  */
diff --git a/key.h b/key.h
index 3b2a7cf..234d6e8 100644
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: key.h,v 1.5 2005/06/14 18:14:40 kjell Exp $	*/
+/*	$OpenBSD: key.h,v 1.6 2019/06/22 15:38:15 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -6,7 +6,7 @@
 
 #define MAXKEY	8			/* maximum number of prefix chars */
 
-struct key {				/* the chacter sequence in a key */
+struct key {				/* the character sequence in a key */
 	int	k_count;		/* number of chars */
 	KCHAR	k_chars[MAXKEY];	/* chars */
 };
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..0b5a914
--- /dev/null
+++ b/log.c
@@ -0,0 +1,466 @@
+/*	$OpenBSD: log.c,v 1.11 2019/07/18 10:50:24 lum Exp $	*/
+
+/* 
+ * This file is in the public domain.
+ *
+ * Author: Mark Lumsden <mark@showcomplex.com>
+ *
+ */
+
+/*
+ * Record a history of an mg session for temporal debugging.
+ * Sometimes pressing a key will set the scene for a bug only visible 
+ * dozens of keystrokes later. gdb has its limitations in this scenario.
+ *
+ * Note this file is not compiled into mg by default, you will need to
+ * amend the 'Makefile' for that to happen. Because of this, the code
+ * is subject to bit-rot. However, I know myself and others have 
+ * written similar functionally often enough, that recording the below 
+ * in a code repository could aid the developement efforts of mg, even
+ * if it requires a bit of effort to get working. The current code is
+ * written in the spirit of debugging (quickly and perhaps not ideal,
+ * but it does what is required well enough). Should debugging become
+ * more formalised within mg, then I would expect that to change.
+ *
+ * If you open a file with long lines to run through this debugging 
+ * code, you may run into problems with the 1st fprintf statement in
+ * in the mglog_lines() function. mg sometimes segvs at a strlen call
+ * in fprintf - possibly something to do with the format string?
+ * 	"%s%p b^%p f.%p %d %d\t%c|%s\n"
+ * When I get time I will look into it. But since my debugging 
+ * generally revolves around a file like:
+ * 
+ * abc
+ * def
+ * ghk
+ *
+ * I don't experience this bug. Just note it for future investigation.
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "key.h"
+#include "kbd.h"
+#include "funmap.h"
+#include "chrdef.h"
+
+#include "log.h"
+
+static char	*mglogfiles_create(char *);
+static int	 mglog_lines(PF);
+static int	 mglog_undo(void);
+static int	 mglog_window(void);
+static int	 mglog_key(KEYMAP *map);
+
+char		*mglogdir;
+extern char	*mglogpath_lines;
+extern char	*mglogpath_undo;
+extern char	*mglogpath_window;
+extern char	*mglogpath_key;
+extern char     *mglogpath_interpreter;
+int		 mgloglevel;
+
+int
+mglog(PF funct, KEYMAP *map)
+{
+	if(!mglog_lines(funct))
+		ewprintf("Problem logging lines");
+	if(!mglog_undo())
+		ewprintf("Problem logging undo");
+	if(!mglog_window())
+		ewprintf("Problem logging window");
+	if(!mglog_key(map))
+		ewprintf("Problem logging key");
+
+	return (TRUE);
+}
+
+
+static int
+mglog_key(KEYMAP *map)
+{
+	struct stat      sb;
+	FILE            *fd;
+	PF		*pfp;
+
+	if(stat(mglogpath_key, &sb))
+		 return (FALSE);
+	fd = fopen(mglogpath_key, "a");
+
+	if (ISWORD(*key.k_chars)) {
+		if (fprintf(fd, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count,
+		    *key.k_chars, CHARMASK(*key.k_chars)) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+	} else {
+		if (fprintf(fd, "k_count:%d k_chars:%hd\t\t", key.k_count,
+		    *key.k_chars) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+	}
+	if (fprintf(fd, "map:%p %d %d %p %hd %hd\n",
+	    map,
+	    map->map_num,
+	    map->map_max,
+	    map->map_default,
+	    map->map_element->k_base,
+	    map->map_element->k_num
+	    ) == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	for (pfp = map->map_element->k_funcp; *pfp != '\0'; pfp++)
+		fprintf(fd, "%s ", function_name(*pfp));
+
+	fprintf(fd, "\n\n");
+	fclose(fd);
+	return (TRUE);
+}
+
+static int
+mglog_window(void)
+{
+	struct mgwin	*wp;
+	struct stat	 sb;
+	FILE		*fd;
+	int		 i;
+
+	if(stat(mglogpath_window, &sb))
+		return (FALSE);
+	fd = fopen(mglogpath_window, "a");
+
+	for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) {
+		if (fprintf(fd,
+		    "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \
+		    " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \
+		    " wmkl%d\n",
+		    i,
+		    wp,
+		    &wp->w_list,
+		    wp->w_bufp,
+		    wp->w_linep,
+		    wp->w_dotp,
+		    wp->w_markp,
+		    wp->w_doto,
+		    wp->w_marko,
+		    wp->w_toprow,
+		    wp->w_ntrows,
+		    wp->w_frame,
+		    wp->w_rflag,
+		    wp->w_flag,
+		    wp->w_wrapline,
+		    wp->w_dotline,
+		    wp->w_markline) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+	}
+	fclose(fd);
+	return (TRUE);
+}
+
+static int
+mglog_undo(void)
+{
+	struct undo_rec	*rec;
+	struct stat	 sb;
+	FILE		*fd;
+	char		 buf[4096], tmp[1024];
+	int      	 num;
+	char		*jptr;
+
+	jptr = "^J"; /* :) */
+
+	if(stat(mglogpath_undo, &sb))
+		return (FALSE);
+	fd = fopen(mglogpath_undo, "a");
+	
+	/*
+	 * From undo_dump()
+	 */
+	num = 0;
+	TAILQ_FOREACH(rec, &curbp->b_undo, next) {
+		num++;
+		if (fprintf(fd, "%d:\t %s at %d ", num,
+		    (rec->type == DELETE) ? "DELETE":
+		    (rec->type == DELREG) ? "DELREGION":
+		    (rec->type == INSERT) ? "INSERT":
+		    (rec->type == BOUNDARY) ? "----" :
+		    (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
+		    rec->pos) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+		if (rec->content) {
+			(void)strlcat(buf, "\"", sizeof(buf));
+			snprintf(tmp, sizeof(tmp), "%.*s",
+			    *rec->content == '\n' ? 2 : rec->region.r_size,
+			    *rec->content == '\n' ? jptr : rec->content);
+			(void)strlcat(buf, tmp, sizeof(buf));
+			(void)strlcat(buf, "\"", sizeof(buf));
+		}
+		snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
+		if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
+			dobeep();
+			ewprintf("Undo record too large. Aborted.");
+			return (FALSE);
+		}
+		if (fprintf(fd, "%s\n", buf) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+		tmp[0] = buf[0] = '\0';
+	}
+	if (fprintf(fd, "\t [end-of-undo]\n\n") == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	fclose(fd);
+
+	return (TRUE);
+}
+
+static int
+mglog_lines(PF funct)
+{
+	struct line     *lp;
+	struct stat      sb;
+	char		*curline, *tmp, o;
+	FILE            *fd;
+	int		 i;
+
+	i = 0;
+
+	if(stat(mglogpath_lines, &sb))
+		return (FALSE);
+
+	fd = fopen(mglogpath_lines, "a");
+	if (fprintf(fd, "%s\n", function_name(funct)) == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	lp = bfirstlp(curbp);
+
+	for(;;) {
+		i++;
+		curline = " ";
+		o = ' ';
+		if (i == curwp->w_dotline) {
+			curline = ">";
+			if (lp->l_used > 0 && curwp->w_doto < lp->l_used)
+				o = lp->l_text[curwp->w_doto];
+			else
+				o = '-';
+		}
+		if (lp->l_size == 0)
+			tmp = " ";
+		else
+			tmp = lp->l_text;
+
+		/* segv on fprintf below with long lines */
+		if (fprintf(fd, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline,
+		    lp, lp->l_bp, lp->l_fp,
+		    lp->l_size, lp->l_used, o, tmp) == -1) {
+			fclose(fd);
+			return (FALSE);
+		}
+		lp = lforw(lp);
+		if (lp == curbp->b_headp) {
+			if (fprintf(fd, " %p b^%p f.%p [bhead]\n(EOB)\n",
+			    lp, lp->l_bp, lp->l_fp) == -1) {
+				fclose(fd);
+        	                return (FALSE);
+			}
+			if (fprintf(fd, "lines:raw:%d buf:%d wdot:%d\n\n",
+			    i, curbp->b_lines, curwp->w_dotline) == -1) {
+				fclose(fd);
+        	                return (FALSE);
+			}
+			break;
+		}
+	}
+	fclose(fd);
+
+	return (TRUE);
+}
+
+/*
+ * See what the eval variable code is up to.
+ */
+int
+mglog_isvar(
+	const char* const argbuf,
+	const char* const argp,
+	const int 	  sizof
+)
+{
+	FILE		*fd;
+
+	fd = fopen(mglogpath_interpreter, "a");
+
+	if (fprintf(fd, " argbuf:%s,argp:%s,sizof:%d<\n",
+	    argbuf,
+	    argp,
+	    sizof
+	    ) == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	fclose(fd);
+	return (TRUE);
+}
+
+/*
+ * See what the eval line code is up to.
+ */
+int
+mglog_execbuf(
+	const char* const pre,
+	const char* const excbuf,
+	const char* const argbuf,
+    	const char* const argp,
+	const int 	  last,
+	const int	  inlist,
+    	const char* const cmdp,
+	const char* const p,
+	const char* const contbuf
+)
+{
+	FILE		*fd;
+
+	fd = fopen(mglogpath_interpreter, "a");
+
+	if (fprintf(fd, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\
+	    "cmdp:%s,p:%s,contbuf:%s<\n",
+	    pre,
+	    excbuf,
+	    argbuf,
+	    argp,
+	    last,
+	    inlist,
+	    cmdp,
+	    p,
+	    contbuf
+	    ) == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	fclose(fd);
+	return (TRUE);
+}
+
+/*
+ * Make sure logging to log files can happen.
+ */
+int
+mgloginit(void)
+{
+	struct stat	 sb;
+	mode_t           dir_mode, f_mode, oumask;
+	char		*mglogfile_lines, *mglogfile_undo, *mglogfile_window;
+	char		*mglogfile_key, *mglogfile_interpreter;
+
+	mglogdir = "./log/";
+	mglogfile_lines = "line.log";
+	mglogfile_undo = "undo.log";
+	mglogfile_window = "window.log";
+	mglogfile_key = "key.log";
+	mglogfile_interpreter = "interpreter.log";
+
+	/* 
+	 * Change mgloglevel for desired level of logging.
+	 * log.h has relevant level info.
+	 */
+	mgloglevel = 1;
+
+	oumask = umask(0);
+	f_mode = 0777& ~oumask;
+	dir_mode = f_mode | S_IWUSR | S_IXUSR;
+
+	if(stat(mglogdir, &sb)) {
+		if (mkdir(mglogdir, dir_mode) != 0)
+			return (FALSE);
+		if (chmod(mglogdir, f_mode) == -1)
+			return (FALSE);
+	}
+	mglogpath_lines = mglogfiles_create(mglogfile_lines);
+	if (mglogpath_lines == NULL)
+		return (FALSE);
+	mglogpath_undo = mglogfiles_create(mglogfile_undo);
+	if (mglogpath_undo == NULL)
+		return (FALSE);
+	mglogpath_window = mglogfiles_create(mglogfile_window);
+	if (mglogpath_window == NULL)
+		return (FALSE);
+	mglogpath_key = mglogfiles_create(mglogfile_key);
+	if (mglogpath_key == NULL)
+		return (FALSE);
+	mglogpath_interpreter = mglogfiles_create(mglogfile_interpreter);
+	if (mglogpath_interpreter == NULL)
+		return (FALSE);
+
+	return (TRUE);
+}	
+
+
+static char *
+mglogfiles_create(char *mglogfile)
+{
+	struct stat	 sb;
+	char		 tmp[NFILEN], *tmp2;
+	int     	 fd;
+
+	if (strlcpy(tmp, mglogdir, sizeof(tmp)) >
+	    sizeof(tmp))
+		return (NULL);
+	if (strlcat(tmp, mglogfile, sizeof(tmp)) >
+	    sizeof(tmp))
+		return (NULL);
+	if ((tmp2 = strndup(tmp, NFILEN)) == NULL)
+		return (NULL);
+
+	if(stat(tmp2, &sb))
+		fd = open(tmp2, O_RDWR | O_CREAT | O_TRUNC, 0644);
+	else
+		fd = open(tmp2, O_RDWR | O_TRUNC, 0644);
+
+	if (fd == -1)
+		return (NULL);
+
+	close(fd);	
+
+	return (tmp2);
+}
+
+/*
+ * Template log function.
+ */
+/*
+int
+mglog_?(void)
+{
+	struct stat      sb;
+	FILE            *fd;
+
+	if(stat(mglogpath_?, &sb))
+	fd = fopen(mglogpath_?, "a");
+
+	if (fprintf(fd, "%?", ??) == -1) {
+		fclose(fd);
+		return (FALSE);
+	}
+	fclose(fd);
+	return (TRUE);
+}
+*/
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..75c41f0
--- /dev/null
+++ b/log.h
@@ -0,0 +1,31 @@
+/*      $OpenBSD: log.h,v 1.5 2019/07/18 10:50:24 lum Exp $   */
+
+/* This file is in the public domain. */
+
+/*
+ * Specifically for mg logging functionality.
+ *
+ */
+int	 mglog(PF, KEYMAP *);
+int	 mgloginit(void);
+int	 mglog_execbuf(	const char* const,
+			const char* const,
+			const char* const,
+			const char* const,
+	     		const int,
+			const int,
+			const char* const,
+			const char* const,
+			const char* const
+			);
+
+int	 mglog_isvar(	const char* const,
+			const char* const,
+			const int
+			);
+
+char 			*mglogpath_lines;
+char 			*mglogpath_undo;
+char 			*mglogpath_window;
+char 			*mglogpath_key;
+char			*mglogpath_interpreter;
diff --git a/main.c b/main.c
index 5189fdc..98d5111 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: main.c,v 1.84 2016/09/16 17:17:40 tedu Exp $	*/
+/*	$OpenBSD: main.c,v 1.87 2019/06/22 15:38:15 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -21,6 +21,10 @@
 #include "funmap.h"
 #include "macro.h"
 
+#ifdef  MGLOG
+#include "log.h"
+#endif
+
 int		 thisflag;			/* flags, this command	*/
 int		 lastflag;			/* flags, last command	*/
 int		 curgoal;			/* goal column		*/
@@ -28,6 +32,7 @@ int		 startrow;			/* row to start		*/
 int		 doaudiblebell;			/* audible bell toggle	*/
 int		 dovisiblebell;			/* visible bell toggle	*/
 int		 dblspace;			/* sentence end #spaces	*/
+int		 allbro;			/* all buffs read-only	*/
 struct buffer	*curbp;				/* current buffer	*/
 struct buffer	*bheadp;			/* BUFFER list head	*/
 struct mgwin	*curwp;				/* current window	*/
@@ -35,7 +40,7 @@ struct mgwin	*wheadp;			/* MGWIN listhead	*/
 char		 pat[NPAT];			/* pattern		*/
 
 #ifndef __dead
-#define __dead __dead2
+#define __dead __attribute__ ((__noreturn__))
 #endif
 
 static void	 edinit(struct buffer *);
@@ -58,7 +63,7 @@ main(int argc, char **argv)
 	char		*cp, *init_fcn_name = NULL;
 	PF		 init_fcn = NULL;
 	int	 	 o, i, nfiles;
-	int	  	 nobackups = 0, bro = 0;
+	int	  	 nobackups = 0;
 	struct buffer	*bp = NULL;
 
 #if defined(__OpenBSD__)
@@ -70,7 +75,7 @@ main(int argc, char **argv)
 	while ((o = getopt(argc, argv, "nRf:")) != -1)
 		switch (o) {
 		case 'R':
-			bro = 1;
+			allbro = 1;
 			break;
 		case 'n':
 			nobackups = 1;
@@ -92,6 +97,11 @@ main(int argc, char **argv)
 	maps_init();		/* Keymaps and modes.		*/
 	funmap_init();		/* Functions.			*/
 
+#ifdef  MGLOG
+	if (!mgloginit())
+		errx(1, "Unable to create logging environment.");
+#endif
+
 	/*
 	 * This is where we initialize standalone extensions that should
 	 * be loaded dynamically sometime in the future.
@@ -180,7 +190,7 @@ notnum:
 						init_fcn(FFOTHARG, 1);
 					nfiles++;
 				}
-				if (bro)
+				if (allbro)
 					curbp->b_flag |= BFREADONLY;
 			}
 		}
@@ -245,7 +255,7 @@ edinit(struct buffer *bp)
 
 /*
  * Quit command.  If an argument, always quit.  Otherwise confirm if a buffer
- * has been changed and not written out.  Normally bound to "C-X C-C".
+ * has been changed and not written out.  Normally bound to "C-x C-c".
  */
 /* ARGSUSED */
 int
diff --git a/match.c b/match.c
index e73a291..9798557 100644
--- a/match.c
+++ b/match.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: match.c,v 1.19 2015/06/03 23:40:01 bcallah Exp $	*/
+/*	$OpenBSD: match.c,v 1.21 2019/07/02 16:25:39 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -37,7 +37,9 @@ static struct balance {
 
 /*
  * Hack to show matching paren.  Self-insert character, then show matching
- * character, if any.  Bound to "blink-and-insert".
+ * character, if any.  Bound to "blink-and-insert".  Used in the mg startup
+ * file to amend the default cursor behaviour of a key press, e.g:
+ *   global-set-key "%" blink-and-insert
  */
 int
 showmatch(int f, int n)
diff --git a/mg.1 b/mg.1
index 7241e09..22604c1 100644
--- a/mg.1
+++ b/mg.1
@@ -1,7 +1,7 @@
-.\"	$OpenBSD: mg.1,v 1.107 2018/08/29 07:50:16 reyk Exp $
+.\"	$OpenBSD: mg.1,v 1.117 2019/07/02 16:25:39 lum Exp $
 .\" This file is in the public domain.
 .\"
-.Dd $Mdocdate: August 29 2018 $
+.Dd $Mdocdate: July 2 2019 $
 .Dt MG 1
 .Os
 .Sh NAME
@@ -405,6 +405,10 @@ Paragraphs are delimited by <NL><NL> or <NL><TAB> or <NL><SPACE>.
 Move cursor backwards by the specified number of words.
 .It beginning-of-buffer
 Move cursor to the top of the buffer.
+If set, keep mark's position, otherwise set at current position.
+A numeric argument
+.Va n
+will move n/10th of the way from the top.
 .It beginning-of-line
 Move cursor to the beginning of the line.
 .It blink-and-insert
@@ -413,6 +417,7 @@ matching delimiter.
 For delimiters other than
 parenthesis, brackets, and braces, the character itself
 is used as its own match.
+Can be used in the startup file with the global-set-key command.
 .It bsmap-mode
 Toggle bsmap mode, where DEL and C-h are swapped.
 .It c-mode
@@ -526,6 +531,10 @@ version string.
 Stop defining a keyboard macro.
 .It end-of-buffer
 Move cursor to the end of the buffer.
+If set, keep mark's position, otherwise set at current position.
+A numeric argument
+.Va n
+will move n/10th of the way from the end.
 .It end-of-line
 Move cursor to the end of the line.
 .It enlarge-window
@@ -884,6 +893,10 @@ Prompt and switch to a new buffer in the current window.
 Switch to buffer in another window.
 .It toggle-read-only
 Toggle the read-only flag on the current buffer.
+.It toggle-read-only-all
+Toggle the read-only flag on all non-ephemeral buffers.
+A simple toggle that switches a global read-only flag either on
+or off.
 .It transpose-chars
 Transpose the two characters in front of and under dot,
 then move forward one character.
@@ -957,7 +970,7 @@ Specific key bindings are available in dired mode.
 dired-unmark-backward
 .It RET, e, f and C-m
 dired-find-file
-.It SPC
+.It SPC, n
 dired-next-line
 .It !
 dired-shell-command
@@ -971,8 +984,8 @@ dired-do-copy
 dired-flag-file-deletion
 .It g
 dired-revert
-.It n
-dired-next-line
+.It j
+dired-goto-file
 .It o
 dired-find-file-other-window
 .It p
@@ -1015,6 +1028,8 @@ is executed.
 .It dired-find-file-other-window
 Open the file on the current line of the dired buffer in a
 different window.
+.It dired-goto-file
+Move the cursor to a file name in the dired buffer.
 .It dired-next-line
 Move the cursor to the next line.
 .It dired-other-window
@@ -1023,7 +1038,7 @@ dired buffer in another window.
 .It dired-previous-line
 Move the cursor to the previous line.
 .It dired-revert
-Refresh the dired buffer.
+Refresh the dired buffer while retaining any flags.
 .It dired-scroll-down
 Scroll down the dired buffer.
 .It dired-scroll-up
@@ -1066,9 +1081,9 @@ auto-execute *.c c-mode
 .Ed
 .Pp
 Comments can be added to the startup files by placing
-.Dq \;
+.Sq ;\&
 or
-.Dq #
+.Sq #
 as the first character of a line.
 .Sh FILES
 .Bl -tag -width /usr/share/doc/mg/tutorial -compact
diff --git a/paragraph.c b/paragraph.c
index df8b733..694fcc9 100644
--- a/paragraph.c
+++ b/paragraph.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: paragraph.c,v 1.45 2016/09/06 16:25:47 lum Exp $	*/
+/*	$OpenBSD: paragraph.c,v 1.46 2018/11/17 09:52:34 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -338,6 +338,8 @@ transposepara(int f, int n)
 	if (n == 0)
 		return (TRUE);
 
+	undo_boundary_enable(FFRAND, 0);
+
 	/* find a paragraph, set mark, then goto the end */
 	gotobop(FFRAND, 1);
 	curwp->w_markp = curwp->w_dotp;
@@ -365,6 +367,8 @@ transposepara(int f, int n)
 	}
 	(void)yank(FFRAND, 1);
 
+	undo_boundary_enable(FFRAND, 1);
+
 	return (TRUE);
 }
 
diff --git a/reallocarray.c b/reallocarray.c
deleted file mode 100644
index 43f0b69..0000000
--- a/reallocarray.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*	$OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $	*/
-/*
- * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-/*
- * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
- * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
- */
-#define MUL_NO_OVERFLOW	((size_t)1 << (sizeof(size_t) * 4))
-
-void *
-reallocarray(void *optr, size_t nmemb, size_t size)
-{
-	if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
-	    nmemb > 0 && SIZE_MAX / nmemb < size) {
-		errno = ENOMEM;
-		return NULL;
-	}
-	return realloc(optr, size * nmemb);
-}
diff --git a/region.c b/region.c
index 5896ba2..a7a8df3 100644
--- a/region.c
+++ b/region.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: region.c,v 1.37 2016/09/09 06:05:51 lum Exp $	*/
+/*	$OpenBSD: region.c,v 1.38 2019/06/17 11:39:26 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -405,6 +405,7 @@ markbuffer(int f, int n)
 {
 	if (gotoeob(f,n) == FALSE)
 		return (FALSE);
+	(void) clearmark(f, n);
 	if (gotobob(f,n) == FALSE)
 		return (FALSE);
 	return (TRUE);
diff --git a/ttyio.c b/ttyio.c
index 44941b4..612037e 100644
--- a/ttyio.c
+++ b/ttyio.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ttyio.c,v 1.37 2015/03/23 12:31:19 bcallah Exp $	*/
+/*	$OpenBSD: ttyio.c,v 1.38 2019/06/28 13:35:02 deraadt Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -67,7 +67,7 @@ ttopen(void)
 int
 ttraw(void)
 {
-	if (tcgetattr(0, &oldtty) < 0) {
+	if (tcgetattr(0, &oldtty) == -1) {
 		dobeep();
 		ewprintf("ttopen can't get terminal attributes");
 		return (FALSE);
@@ -81,7 +81,7 @@ ttraw(void)
 	newtty.c_oflag &= ~OPOST;
 	newtty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
 
-	if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) < 0) {
+	if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) == -1) {
 		dobeep();
 		ewprintf("ttopen can't tcsetattr");
 		return (FALSE);
@@ -115,7 +115,7 @@ int
 ttcooked(void)
 {
 	ttflush();
-	if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) < 0) {
+	if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) == -1) {
 		dobeep();
 		ewprintf("ttclose can't tcsetattr");
 		return (FALSE);
@@ -193,7 +193,7 @@ charswaiting(void)
 {
 	int	x;
 
-	return ((ioctl(0, FIONREAD, &x) < 0) ? 0 : x);
+	return ((ioctl(0, FIONREAD, &x) == -1) ? 0 : x);
 }
 
 /*
diff --git a/util.c b/util.c
index 9357d4d..9e3cdf0 100644
--- a/util.c
+++ b/util.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: util.c,v 1.38 2015/11/18 18:21:06 jasper Exp $	*/
+/*	$OpenBSD: util.c,v 1.42 2019/06/22 15:38:15 lum Exp $	*/
 
 /* This file is in the public domain. */
 
@@ -20,15 +20,19 @@
  * Display a bunch of useful information about the current location of dot.
  * The character under the cursor (in octal), the current line, row, and
  * column, and approximate position of the cursor in the file (as a
- * percentage) is displayed.  The column position assumes an infinite
+ * percentage) is displayed.
+ * Also included at the moment are some values in parenthesis for debugging
+ * explicit newline inclusion into the buffer.
+ * The column position assumes an infinite
  * position display; it does not truncate just because the screen does.
- * This is normally bound to "C-X =".
+ * This is normally bound to "C-x =".
  */
 /* ARGSUSED */
 int
 showcpos(int f, int n)
 {
 	struct line	*clp;
+	char		*msg;
 	long	 nchar, cchar;
 	int	 nline, row;
 	int	 cline, cbyte;		/* Current line/char/byte */
@@ -36,32 +40,41 @@ showcpos(int f, int n)
 
 	/* collect the data */
 	clp = bfirstlp(curbp);
+	msg = "Char:";
 	cchar = 0;
 	cline = 0;
 	cbyte = 0;
 	nchar = 0;
 	nline = 0;
 	for (;;) {
-		/* count this line */
+		/* count lines and display total as (raw) 'lines' and
+		   compare with b_lines */
 		++nline;
 		if (clp == curwp->w_dotp) {
-			/* mark line */
+			/* obtain (raw) dot line # and compare with w_dotline */
 			cline = nline;
 			cchar = nchar + curwp->w_doto;
 			if (curwp->w_doto == llength(clp))
+				/* fake a \n at end of line */
 				cbyte = '\n';
 			else
 				cbyte = lgetc(clp, curwp->w_doto);
 		}
-		/* now count the chars */
+		/* include # of chars in this line for point-thru-buff ratio */
 		nchar += llength(clp);
 		clp = lforw(clp);
-		if (clp == curbp->b_headp)
+		if (clp == curbp->b_headp) {
+			if (cbyte == '\n' && cline == curbp->b_lines) {
+				/* swap faked \n for EOB msg */
+				cbyte = EOF;
+				msg = "(EOB)";
+			}
 			break;
-		/* count the newline */
+		}
+		/* count the implied newline */
 		nchar++;
 	}
-	/* determine row */
+	/* determine row # within current window */
 	row = curwp->w_toprow + 1;
 	clp = curwp->w_linep;
 	while (clp != curbp->b_headp && clp != curwp->w_dotp) {
@@ -69,8 +82,10 @@ showcpos(int f, int n)
 		clp = lforw(clp);
 	}
 	ratio = nchar ? (100L * cchar) / nchar : 100;
-	ewprintf("Char: %c (0%o)  point=%ld(%d%%)  line=%d  row=%d  col=%d",
-	    cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp));
+	ewprintf("%s %c (0%o)  point=%ld(%d%%)  line=%d  row=%d  col=%d" \
+            "  (blines=%d rlines=%d l_size=%d)", msg,
+	    cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp),
+	    curbp->b_lines, nline, clp->l_size);
 	return (TRUE);
 }
 
@@ -162,7 +177,7 @@ twiddle(int f, int n)
  * Open up some blank space.  The basic plan is to insert a bunch of
  * newlines, and then back up over them.  Everything is done by the
  * subcommand processors.  They even handle the looping.  Normally this
- * is bound to "C-O".
+ * is bound to "C-o".
  */
 /* ARGSUSED */
 int
@@ -213,7 +228,7 @@ enewline(int f, int n)
  * sitting on a blank line. If dot is sitting on a blank line, this command
  * deletes all the blank lines above and below the current line. If it is
  * sitting on a non blank line then it deletes all of the blank lines after
- * the line. Normally this command is bound to "C-X C-O". Any argument is
+ * the line. Normally this command is bound to "C-x C-o". Any argument is
  * ignored.
  */
 /* ARGSUSED */
@@ -334,7 +349,7 @@ deltrailwhite(int f, int n)
  * simple.  Figure out the indentation of the current line.  Insert a newline
  * by calling the standard routine.  Insert the indentation by inserting the
  * right number of tabs and spaces.  Return TRUE if all ok.  Return FALSE if
- * one of the subcommands failed. Normally bound to "C-M".
+ * one of the subcommands failed. Normally bound to "C-m".
  */
 /* ARGSUSED */
 int
@@ -411,7 +426,7 @@ indent(int f, int n)
  * Delete forward.  This is real easy, because the basic delete routine does
  * all of the work.  Watches for negative arguments, and does the right thing.
  * If any argument is present, it kills rather than deletes, to prevent loss
- * of text if typed with a big argument.  Normally bound to "C-D".
+ * of text if typed with a big argument.  Normally bound to "C-d".
  */
 /* ARGSUSED */
 int