Index: head/contrib/nvi/CMakeLists.txt
===================================================================
--- head/contrib/nvi/CMakeLists.txt	(revision 366308)
+++ head/contrib/nvi/CMakeLists.txt	(revision 366309)
@@ -1,190 +1,210 @@
 cmake_minimum_required(VERSION 3.9)
 
 get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 if(is_multi_config)
     set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING
         "Semicolon separated list of supported configuration types")
     mark_as_advanced(CMAKE_CONFIGURATION_TYPES)
 elseif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_C_FLAGS)
     message(WARNING "No CMAKE_BUILD_TYPE is selected")
 endif()
 
 project(nvi2 C)
 
 include(CheckIncludeFiles)
 include(CheckFunctionExists)
+include(CheckStructHasMember)
 include(CheckCSourceCompiles)
 
 mark_as_advanced(CMAKE_INSTALL_PREFIX)
 
 option(USE_WIDECHAR "Enable wide character support" ON)
 option(USE_ICONV "Enable iconv support" ON)
 
 add_compile_options(-fcolor-diagnostics)
 add_compile_options($<$<CONFIG:Debug>:-Wall>)
 add_compile_options($<$<CONFIG:Debug>:-Wno-parentheses>)
 add_compile_options($<$<CONFIG:Debug>:-Wno-uninitialized>)
 add_compile_options($<$<CONFIG:Debug>:-Wmissing-prototypes>)
 add_compile_options($<$<CONFIG:Debug>:-Wsystem-headers>)
 add_compile_options($<$<CONFIG:Release>:-Wuninitialized>)
 add_compile_options($<$<CONFIG:Release>:-Wno-dangling-else>)
 add_compile_options(-Wstack-protector -fstack-protector)
 add_compile_options(-Wstrict-aliasing -fstrict-aliasing)
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 set(MAIN_PROTOS
     cl/extern.h common/extern.h ex/extern.h vi/extern.h
     common/options_def.h ex/ex_def.h ex/version.h)
 
 set(CL_SRCS
     cl/cl_funcs.c cl/cl_main.c cl/cl_read.c cl/cl_screen.c cl/cl_term.c)
 
 set(COMMON_SRCS
     common/conv.c common/cut.c common/delete.c common/encoding.c common/exf.c
     common/key.c common/line.c common/log.c common/main.c common/mark.c
     common/msg.c common/options.c common/options_f.c common/put.c
     common/recover.c common/screen.c common/search.c common/seq.c
     common/util.c)
 
 set(EX_SRCS
     ex/ex.c ex/ex_abbrev.c ex/ex_append.c ex/ex_args.c ex/ex_argv.c ex/ex_at.c
     ex/ex_bang.c ex/ex_cd.c ex/ex_cmd.c ex/ex_cscope.c ex/ex_delete.c
     ex/ex_display.c ex/ex_edit.c ex/ex_equal.c ex/ex_file.c ex/ex_filter.c
     ex/ex_global.c ex/ex_init.c ex/ex_join.c ex/ex_map.c ex/ex_mark.c
     ex/ex_mkexrc.c ex/ex_move.c ex/ex_open.c ex/ex_preserve.c ex/ex_print.c
     ex/ex_put.c ex/ex_quit.c ex/ex_read.c ex/ex_screen.c ex/ex_script.c
     ex/ex_set.c ex/ex_shell.c ex/ex_shift.c ex/ex_source.c ex/ex_stop.c
     ex/ex_subst.c ex/ex_tag.c ex/ex_txt.c ex/ex_undo.c ex/ex_usage.c
     ex/ex_util.c ex/ex_version.c ex/ex_visual.c ex/ex_write.c ex/ex_yank.c
     ex/ex_z.c)
 
 set(VI_SRCS
     vi/getc.c vi/v_at.c vi/v_ch.c vi/v_cmd.c vi/v_delete.c vi/v_ex.c
     vi/v_increment.c vi/v_init.c vi/v_itxt.c vi/v_left.c vi/v_mark.c
     vi/v_match.c vi/v_paragraph.c vi/v_put.c vi/v_redraw.c vi/v_replace.c
     vi/v_right.c vi/v_screen.c vi/v_scroll.c vi/v_search.c vi/v_section.c
     vi/v_sentence.c vi/v_status.c vi/v_txt.c vi/v_ulcase.c vi/v_undo.c
     vi/v_util.c vi/v_word.c vi/v_xchar.c vi/v_yank.c vi/v_z.c vi/v_zexit.c
     vi/vi.c vi/vs_line.c vi/vs_msg.c vi/vs_refresh.c vi/vs_relative.c
     vi/vs_smap.c vi/vs_split.c)
 
 set(REGEX_SRCS
     regex/regcomp.c regex/regerror.c regex/regexec.c regex/regfree.c)
 
 # commands to generate the public headers
 set(extract_protos sed -n 's/^ \\* PUBLIC: \\\(.*\\\)/\\1/p')
 set(extract_version sed -n
     's/^.*version \\\([^\)]*\)\\\).*/\#define VI_VERSION \\\"\\1\\\"/p')
 
 add_custom_command(OUTPUT cl/extern.h
                    COMMAND ${extract_protos} ${CL_SRCS} > cl/extern.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS ${CL_SRCS})
 add_custom_command(OUTPUT common/extern.h
                    COMMAND ${extract_protos} ${COMMON_SRCS} > common/extern.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS ${COMMON_SRCS})
 add_custom_command(OUTPUT ex/extern.h
                    COMMAND ${extract_protos} ${EX_SRCS} > ex/extern.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS ${EX_SRCS})
 add_custom_command(OUTPUT vi/extern.h
                    COMMAND ${extract_protos} ${VI_SRCS} > vi/extern.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS ${VI_SRCS})
 add_custom_command(OUTPUT common/options_def.h
                    COMMAND awk -f common/options.awk
                            common/options.c > common/options_def.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS common/options.c)
 add_custom_command(OUTPUT ex/ex_def.h
                    COMMAND awk -f ex/ex.awk ex/ex_cmd.c > ex/ex_def.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS ex/ex_cmd.c)
 add_custom_command(OUTPUT ex/version.h
                    COMMAND ${extract_version} README > ex/version.h
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    DEPENDS README)
 
 add_executable(nvi)
 target_sources(nvi PRIVATE ${MAIN_PROTOS} ${CL_SRCS} ${COMMON_SRCS}
                            ${EX_SRCS} ${VI_SRCS})
 target_compile_definitions(nvi PRIVATE $<$<CONFIG:Debug>:DEBUG>
                                        $<$<CONFIG:Debug>:COMLOG>)
 
 check_function_exists(openpty UTIL_IN_LIBC)
 if(NOT UTIL_IN_LIBC)
     find_library(UTIL_LIBRARY util)
     target_link_libraries(nvi PRIVATE ${UTIL_LIBRARY})
 endif()
 
 check_function_exists(__b64_ntop RESOLV_IN_LIBC)
 if(NOT RESOLV_IN_LIBC)
     find_library(RESOLV_LIBRARY resolv)
     target_link_libraries(nvi PRIVATE ${RESOLV_LIBRARY})
 endif()
 
 if(USE_WIDECHAR)
     find_library(CURSES_LIBRARY NAMES ncursesw cursesw curses HINTS /usr/lib)
+    find_library(TERMINFO_LIBRARY NAMES tinfow terminfo HINTS /usr/lib)
 
     # link to the wchar_t awared BSD libregex.a
     add_library(regex STATIC)
     target_sources(regex PRIVATE ${REGEX_SRCS})
     target_include_directories(regex PUBLIC regex)
     target_compile_definitions(regex PUBLIC __REGEX_PRIVATE)
     target_link_libraries(nvi PRIVATE regex)
 else()
     find_library(CURSES_LIBRARY NAMES ncurses curses HINTS /usr/lib)
+    find_library(TERMINFO_LIBRARY NAMES tinfo terminfo HINTS /usr/lib)
     target_compile_options(nvi PRIVATE -Wno-pointer-sign)
 endif()
 
-target_link_libraries(nvi PRIVATE ${CURSES_LIBRARY})
+target_link_libraries(nvi PRIVATE ${CURSES_LIBRARY} ${TERMINFO_LIBRARY})
 
 if(USE_ICONV)
-    check_function_exists(__iconv ICONV_IN_LIBC)
+    check_function_exists(iconv ICONV_IN_LIBC)
     if(NOT ICONV_IN_LIBC)
         find_path(ICONV_INCLUDE_DIR iconv.h)
         find_library(ICONV_LIBRARY iconv)
     endif()
 
     # detect the prototype of iconv(3)
     set(CMAKE_C_FLAGS_BACKUP "${CMAKE_C_FLAGS}")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
     set(CMAKE_REQUIRED_INCLUDES "${ICONV_INCLUDE_DIR}")
     set(CMAKE_REQUIRED_LIBRARIES "${ICONV_LIBRARY}")
     check_c_source_compiles("
     #include <iconv.h>
     int main() {
         iconv_t conv = 0;
         char* in = 0;
         size_t ilen = 0;
         char* out = 0;
         size_t olen = 0;
         iconv(conv, &in, &ilen, &out, &olen);
         return 0;
     }
     " ICONV_TRADITIONAL)
     set(CMAKE_REQUIRED_INCLUDES)
     set(CMAKE_REQUIRED_LIBRARIES)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_BACKUP}")
 
     target_include_directories(nvi PRIVATE ${ICONV_INCLUDE_DIR})
     target_link_libraries(nvi PRIVATE ${ICONV_LIBRARY})
 endif()
 
+check_function_exists(getprogname GETPROGNAME_IN_LIBC)
+check_function_exists(strlcpy STRLCPY_IN_LIBC)
+if(NOT GETPROGNAME_IN_LIBC OR NOT STRLCPY_IN_LIBC)
+    find_package(PkgConfig REQUIRED)
+    pkg_check_modules(LIBBSD libbsd-overlay)
+    add_definitions(${LIBBSD_CFLAGS})
+    target_link_libraries(nvi PRIVATE ${LIBBSD_LIBRARIES})
+endif()
+
+check_function_exists(dbopen DBOPEN_IN_LIBC)
+if(NOT DBOPEN_IN_LIBC)
+    target_link_libraries(nvi PRIVATE db1)
+endif()
+
 check_include_files(libutil.h HAVE_LIBUTIL_H)
 check_include_files(ncurses.h HAVE_NCURSES_H)
+check_include_files(ncursesw/ncurses.h HAVE_NCURSESW_NCURSES_H)
+check_include_files(pty.h HAVE_PTY_H)
 check_include_files(term.h HAVE_TERM_H)
+check_struct_has_member("struct dirent" d_namlen dirent.h HAVE_DIRENT_D_NAMLEN LANGUAGE C)
 
 configure_file(files/config.h.in config.h)
 
 set(vi_cv_path_preserve /var/tmp/vi.recover/)
 if(APPLE)
     set(vi_cv_path_msgcat /usr/local/share/vi/catalog/)
 else()
     set(vi_cv_path_msgcat /usr/share/vi/catalog/)
 endif()
 
 configure_file(files/pathnames.h.in pathnames.h)
 configure_file(files/recover.in recover @ONLY)
Index: head/contrib/nvi/catalog/dump.c
===================================================================
--- head/contrib/nvi/catalog/dump.c	(revision 366308)
+++ head/contrib/nvi/catalog/dump.c	(revision 366309)
@@ -1,98 +1,98 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <ctype.h>
 #include <ctype.h>
 #include <stdio.h>
 
 static void
 parse(FILE *fp)
 {
 	int ch, s1, s2, s3;
 
-#define	TESTD(s) {							\
+#define	TESTD(s) do {							\
 	if ((s = getc(fp)) == EOF)					\
 		return;							\
 	if (!isdigit(s))						\
 		continue;						\
-}
-#define	TESTP {								\
+} while (0)
+#define	TESTP do {							\
 	if ((ch = getc(fp)) == EOF)					\
 		return;							\
 	if (ch != '|')							\
 		continue;						\
-}
-#define	MOVEC(t) {							\
+} while (0)
+#define	MOVEC(t) do {							\
 	do {								\
 		if ((ch = getc(fp)) == EOF)				\
 			return;						\
 	} while (ch != (t));						\
-}
+} while (0)
 	for (;;) {
 		MOVEC('"');
 		TESTD(s1);
 		TESTD(s2);
 		TESTD(s3);
 		TESTP;
 		putchar('"');
 		putchar(s1);
 		putchar(s2);
 		putchar(s3);
 		putchar('|');
 		for (;;) {		/* dump to end quote. */
 			if ((ch = getc(fp)) == EOF)
 				return;
 			putchar(ch);
 			if (ch == '"')
 				break;
 			if (ch == '\\') {
 				if ((ch = getc(fp)) == EOF)
 					return;
 				putchar(ch);
 			}
 		}
 		putchar('\n');
 	}
 }
 
 int
 main(int argc, char *argv[])
 {
 	FILE *fp;
 
 	for (; *argv != NULL; ++argv) {
 		if ((fp = fopen(*argv, "r")) == NULL) {
 			perror(*argv);
 			return (1);
 		}
 		parse(fp);
 		(void)fclose(fp);
 	}
 	return (0);
 }
Index: head/contrib/nvi/cl/cl.h
===================================================================
--- head/contrib/nvi/cl/cl.h	(revision 366308)
+++ head/contrib/nvi/cl/cl.h	(revision 366309)
@@ -1,81 +1,83 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #ifdef USE_WIDECHAR
 #define _XOPEN_SOURCE_EXTENDED
 #endif
-#ifdef HAVE_NCURSES_H
+#ifdef HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#elif defined HAVE_NCURSES_H
 #include <ncurses.h>
 #else
 #include <curses.h>
 #endif
 
 typedef struct _cl_private {
 	char	 ibuf[256];	/* Input keys. */
 
 	size_t	 skip;		/* Remaining keys. */
 
 	CONVWIN	 cw;		/* Conversion buffer. */
 
 	int	 eof_count;	/* EOF count. */
 
 	struct termios orig;	/* Original terminal values. */
 	struct termios ex_enter;/* Terminal values to enter ex. */
 	struct termios vi_enter;/* Terminal values to enter vi. */
 
 	char	*el;		/* Clear to EOL terminal string. */
 	char	*cup;		/* Cursor movement terminal string. */
 	char	*cuu1;		/* Cursor up terminal string. */
 	char	*rmso, *smso;	/* Inverse video terminal strings. */
 	char	*smcup, *rmcup;	/* Terminal start/stop strings. */
 
 	char	*oname;		/* Original screen window name. */
 
 	SCR	*focus;		/* Screen that has the "focus". */
 
 	int	 killersig;	/* Killer signal. */
 #define	INDX_HUP	0
 #define	INDX_INT	1
 #define	INDX_TERM	2
 #define	INDX_WINCH	3
 #define	INDX_MAX	4	/* Original signal information. */
 	struct sigaction oact[INDX_MAX];
 
 	enum {			/* Tty group write mode. */
 	    TGW_UNKNOWN=0, TGW_SET, TGW_UNSET } tgw;
 
 	enum {			/* Terminal initialization strings. */
 	    TE_SENT=0, TI_SENT } ti_te;
 
 #define	CL_IN_EX	0x0001	/* Currently running ex. */
 #define	CL_LAYOUT	0x0002	/* Screen layout changed. */
 #define	CL_RENAME	0x0004	/* X11 xterm icon/window renamed. */
 #define	CL_RENAME_OK	0x0008	/* User wants the windows renamed. */
 #define	CL_SCR_EX_INIT	0x0010	/* Ex screen initialized. */
 #define	CL_SCR_VI_INIT	0x0020	/* Vi screen initialized. */
 #define	CL_SIGHUP	0x0040	/* SIGHUP arrived. */
 #define	CL_SIGINT	0x0080	/* SIGINT arrived. */
 #define	CL_SIGTERM	0x0100	/* SIGTERM arrived. */
 #define	CL_SIGWINCH	0x0200	/* SIGWINCH arrived. */
 #define	CL_STDIN_TTY	0x0400	/* Talking to a terminal. */
 	u_int32_t flags;
 } CL_PRIVATE;
 
 #define	CLP(sp)		((CL_PRIVATE *)((sp)->gp->cl_private))
 #define	GCLP(gp)	((CL_PRIVATE *)gp->cl_private)
 #define	CLSP(sp)	((WINDOW *)((sp)->cl_private))
 
 /* Return possibilities from the keyboard read routine. */
 typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_INTR, INP_TIMEOUT } input_t;
 
 /* The screen position relative to a specific window. */
 #define	RCNO(sp, cno)	(cno)
 #define	RLNO(sp, lno)	(lno)
 
 #include "extern.h"
Index: head/contrib/nvi/cl/cl_read.c
===================================================================
--- head/contrib/nvi/cl/cl_read.c	(revision 366308)
+++ head/contrib/nvi/cl/cl_read.c	(revision 366309)
@@ -1,326 +1,327 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/select.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <termios.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "../ex/script.h"
 #include "cl.h"
 
 /* Pollution by Solaris curses. */
 #undef columns
 #undef lines  
 
 static input_t	cl_read(SCR *,
     u_int32_t, char *, size_t, int *, struct timeval *);
 static int	cl_resize(SCR *, size_t, size_t);
 
 /*
  * cl_event --
  *	Return a single event.
  *
  * PUBLIC: int cl_event(SCR *, EVENT *, u_int32_t, int);
  */
 int
 cl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
 {
 	struct timeval t, *tp;
 	CL_PRIVATE *clp;
 	size_t lines, columns;
 	int changed, nr = 0;
 	CHAR_T *wp;
 	size_t wlen;
 	int rc;
 
 	/*
 	 * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
 	 * so that we just keep returning them until the editor dies.
 	 */
 	clp = CLP(sp);
 retest:	if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
 		if (F_ISSET(clp, CL_SIGINT)) {
 			F_CLR(clp, CL_SIGINT);
 			evp->e_event = E_INTERRUPT;
 		} else
 			evp->e_event = E_TIMEOUT;
 		return (0);
 	}
 	if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
 		if (F_ISSET(clp, CL_SIGHUP)) {
 			evp->e_event = E_SIGHUP;
 			return (0);
 		}
 		if (F_ISSET(clp, CL_SIGTERM)) {
 			evp->e_event = E_SIGTERM;
 			return (0);
 		}
 		if (F_ISSET(clp, CL_SIGWINCH)) {
 			F_CLR(clp, CL_SIGWINCH);
 			if (cl_ssize(sp, 1, &lines, &columns, &changed))
 				return (1);
 			if (changed) {
 				(void)cl_resize(sp, lines, columns);
 				evp->e_event = E_WRESIZE;
 				return (0);
 			}
 			/* No real change, ignore the signal. */
 		}
 	}
 
 	/* Set timer. */
 	if (ms == 0)
 		tp = NULL;
 	else {
 		t.tv_sec = ms / 1000;
 		t.tv_usec = (ms % 1000) * 1000;
 		tp = &t;
 	}
 
 	/* Read input characters. */
 read:
 	switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
 	    clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) {
 	case INP_OK:
 		rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip, 
 				wp, wlen);
 		evp->e_csp = wp;
 		evp->e_len = wlen;
 		evp->e_event = E_STRING;
 		if (rc < 0) {
 		    int n = -rc;
 		    memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n);
 		    clp->skip = n;
 		    if (wlen == 0)
 			goto read;
 		} else if (rc == 0)
 		    clp->skip = 0;
 		else
 		    msgq(sp, M_ERR, "323|Invalid input. Truncated.");
 		break;
 	case INP_EOF:
 		evp->e_event = E_EOF;
 		break;
 	case INP_ERR:
 		evp->e_event = E_ERR;
 		break;
 	case INP_INTR:
 		goto retest;
 	case INP_TIMEOUT:
 		evp->e_event = E_TIMEOUT;
 		break;
 	default:
 		abort();
 	}
 	return (0);
 }
 
 /*
  * cl_read --
  *	Read characters from the input.
  */
 static input_t
 cl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp,
     struct timeval *tp)
 {
 	struct termios term1, term2;
 	CL_PRIVATE *clp;
 	GS *gp;
 	fd_set rdfd;
 	input_t rval;
 	int maxfd, nr, term_reset;
 
 	gp = sp->gp;
 	clp = CLP(sp);
 	term_reset = 0;
 
 	/*
 	 * 1: A read from a file or a pipe.  In this case, the reads
 	 *    never timeout regardless.  This means that we can hang
 	 *    when trying to complete a map, but we're going to hang
 	 *    on the next read anyway.
 	 */
 	if (!F_ISSET(clp, CL_STDIN_TTY)) {
 		switch (nr = read(STDIN_FILENO, bp, blen)) {
 		case 0:
 			return (INP_EOF);
 		case -1:
 			goto err;
 		default:
 			*nrp = nr;
 			return (INP_OK);
 		}
 		/* NOTREACHED */
 	}
 
 	/*
 	 * 2: A read with an associated timeout, e.g., trying to complete
 	 *    a map sequence.  If input exists, we fall into #3.
 	 */
 	if (tp != NULL) {
 		FD_ZERO(&rdfd);
 		FD_SET(STDIN_FILENO, &rdfd);
 		switch (select(STDIN_FILENO + 1, &rdfd, NULL, NULL, tp)) {
 		case 0:
 			return (INP_TIMEOUT);
 		case -1:
 			goto err;
 		default:
 			break;
 		}
 	}
 	
 	/*
 	 * The user can enter a key in the editor to quote a character.  If we
 	 * get here and the next key is supposed to be quoted, do what we can.
 	 * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
 	 * obvious race here, when the key has already been entered, but there's
 	 * nothing that we can do to fix that problem.
 	 *
 	 * The editor can ask for the next literal character even thought it's
 	 * generally running in line-at-a-time mode.  Do what we can.
 	 */
 	if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
 		term_reset = 1;
 		if (LF_ISSET(EC_QUOTED)) {
 			term2 = term1;
 			term2.c_lflag &= ~ISIG;
 			term2.c_iflag &= ~(IXON | IXOFF);
 			(void)tcsetattr(STDIN_FILENO,
 			    TCSASOFT | TCSADRAIN, &term2);
 		} else
 			(void)tcsetattr(STDIN_FILENO,
 			    TCSASOFT | TCSADRAIN, &clp->vi_enter);
 	}
 
 	/*
 	 * 3: Wait for input.
 	 *
 	 * Select on the command input and scripting window file descriptors.
 	 * It's ugly that we wait on scripting file descriptors here, but it's
 	 * the only way to keep from locking out scripting windows.
 	 */
 	if (F_ISSET(gp, G_SCRWIN)) {
 loop:		FD_ZERO(&rdfd);
 		FD_SET(STDIN_FILENO, &rdfd);
 		maxfd = STDIN_FILENO;
 		if (F_ISSET(sp, SC_SCRIPT)) {
 			FD_SET(sp->script->sh_master, &rdfd);
 			if (sp->script->sh_master > maxfd)
 				maxfd = sp->script->sh_master;
 		}
 		switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
 		case 0:
 			abort();
 		case -1:
 			goto err;
 		default:
 			break;
 		}
 		if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
 			if (sscr_input(sp))
 				return (INP_ERR);
 			goto loop;
 		}
 	}
 
 	/*
 	 * 4: Read the input.
 	 *
 	 * !!!
 	 * What's going on here is some scary stuff.  Ex runs the terminal in
 	 * canonical mode.  So, the <newline> character terminating a line of
 	 * input is returned in the buffer, but a trailing <EOF> character is
 	 * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
 	 * commands, it has to see the trailing <EOF> characters to determine
 	 * the difference between the user entering "0ab" and "0<EOF>ab".  We
 	 * leave an extra slot in the buffer, so that we can add a trailing
 	 * <EOF> character if the buffer isn't terminated by a <newline>.  We
 	 * lose if the buffer is too small for the line and exactly N characters
 	 * are entered followed by an <EOF> character.
 	 */
 #define	ONE_FOR_EOF	1
 	switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
 	case  0:				/* EOF. */
 		/*
 		 * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
 		 * a valid command, but we don't want to loop forever because
 		 * the terminal driver is returning EOF because the user has
 		 * disconnected. The editor will almost certainly try to write
 		 * something before this fires, which should kill us, but You
 		 * Never Know.
 		 */
 		if (++clp->eof_count < 50) {
 			bp[0] = clp->orig.c_cc[VEOF];
 			*nrp = 1;
 			rval = INP_OK;
 
 		} else
 			rval = INP_EOF;
 		break;
 	case -1:				/* Error or interrupt. */
 err:		if (errno == EINTR)
 			rval = INP_INTR;
 		else {
 			rval = INP_ERR;
 			msgq(sp, M_SYSERR, "input");
 		}
 		break;
 	default:				/* Input characters. */
 		if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
 			bp[nr++] = clp->orig.c_cc[VEOF];
 		*nrp = nr;
 		clp->eof_count = 0;
 		rval = INP_OK;
 		break;
 	}
 
 	/* Restore the terminal state if it was modified. */
 	if (term_reset)
 		(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
 	return (rval);
 }
 
 /* 
  * cl_resize --
  *	Reset the options for a resize event.
  */
 static int
 cl_resize(SCR *sp, size_t lines, size_t columns)
 {
 	ARGS *argv[2], a, b;
 	CHAR_T b1[1024];
 
 	a.bp = b1;
 	b.bp = NULL;
 	a.len = b.len = 0;
 	argv[0] = &a;
 	argv[1] = &b;
 
 	a.len = SPRINTF(b1, sizeof(b1), L("lines=%lu"), (u_long)lines);
 	if (opts_set(sp, argv, NULL))
 		return (1);
 	a.len = SPRINTF(b1, sizeof(b1), L("columns=%lu"), (u_long)columns);
 	if (opts_set(sp, argv, NULL))
 		return (1);
 	return (0);
 }
Index: head/contrib/nvi/cl/cl_term.c
===================================================================
--- head/contrib/nvi/cl/cl_term.c	(revision 366308)
+++ head/contrib/nvi/cl/cl_term.c	(revision 366309)
@@ -1,490 +1,492 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #ifdef HAVE_TERM_H
 #include <term.h>
 #endif
 #include <termios.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "cl.h"
 
 static int cl_pfmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t);
 static size_t atoz_or(const char *, size_t);
 
 /*
  * XXX
  * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
  */
 typedef struct _tklist {
 	char	*ts;			/* Key's termcap string. */
 	char	*output;		/* Corresponding vi command. */
 	char	*name;			/* Name. */
 	u_char	 value;			/* Special value (for lookup). */
 } TKLIST;
 static TKLIST const c_tklist[] = {	/* Command mappings. */
 	{"kil1",	"O",	"insert line"},
 	{"kdch1",	"x",	"delete character"},
 	{"kcud1",	"j",	"cursor down"},
 	{"kel",		"D",	"delete to eol"},
 	{"kind",     "\004",	"scroll down"},			/* ^D */
 	{"kll",		"$",	"go to eol"},
 	{"kend",	"$",	"go to eol"},
 	{"khome",	"^",	"go to sol"},
 	{"kich1",	"i",	"insert at cursor"},
 	{"kdl1",       "dd",	"delete line"},
 	{"kcub1",	"h",	"cursor left"},
 	{"knp",	     "\006",	"page down"},			/* ^F */
 	{"kpp",	     "\002",	"page up"},			/* ^B */
 	{"kri",	     "\025",	"scroll up"},			/* ^U */
 	{"ked",	       "dG",	"delete to end of screen"},
 	{"kcuf1",	"l",	"cursor right"},
 	{"kcuu1",	"k",	"cursor up"},
 	{NULL},
 };
 static TKLIST const m1_tklist[] = {	/* Input mappings (lookup). */
 	{NULL},
 };
 static TKLIST const m2_tklist[] = {	/* Input mappings (set or delete). */
 	{"kcud1",  "\033ja",	"cursor down"},			/* ^[ja */
 	{"kcub1",  "\033ha",	"cursor left"},			/* ^[ha */
 	{"kcuu1",  "\033ka",	"cursor up"},			/* ^[ka */
 	{"kcuf1",  "\033la",	"cursor right"},		/* ^[la */
 	{NULL},
 };
 
 /*
  * cl_term_init --
  *	Initialize the special keys defined by the termcap/terminfo entry.
  *
  * PUBLIC: int cl_term_init(SCR *);
  */
 int
 cl_term_init(SCR *sp)
 {
 	KEYLIST *kp;
 	SEQ *qp;
 	TKLIST const *tkp;
 	char *t;
 	CHAR_T name[60];
 	CHAR_T output[5];
 	CHAR_T ts[20];
 	CHAR_T *wp;
 	size_t wlen;
 
 	/* Command mappings. */
 	for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
 		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
 			continue;
 		CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
 		MEMCPY(name, wp, wlen);
 		CHAR2INT(sp, t, strlen(t), wp, wlen);
 		MEMCPY(ts, wp, wlen);
 		CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
 		MEMCPY(output, wp, wlen);
 		if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
 		    output, strlen(tkp->output), SEQ_COMMAND,
 		    SEQ_NOOVERWRITE | SEQ_SCREEN))
 			return (1);
 	}
 
 	/* Input mappings needing to be looked up. */
 	for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
 		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
 			continue;
 		for (kp = keylist;; ++kp)
 			if (kp->value == tkp->value)
 				break;
 		if (kp == NULL)
 			continue;
 		CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
 		MEMCPY(name, wp, wlen);
 		CHAR2INT(sp, t, strlen(t), wp, wlen);
 		MEMCPY(ts, wp, wlen);
 		output[0] = (UCHAR_T)kp->ch;
 		if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
 		    output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
 			return (1);
 	}
 
 	/* Input mappings that are already set or are text deletions. */
 	for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
 		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
 			continue;
 		/*
 		 * !!!
 		 * Some terminals' <cursor_left> keys send single <backspace>
 		 * characters.  This is okay in command mapping, but not okay
 		 * in input mapping.  That combination is the only one we'll
 		 * ever see, hopefully, so kluge it here for now.
 		 */
 		if (!strcmp(t, "\b"))
 			continue;
 		if (tkp->output == NULL) {
 			CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
 			MEMCPY(name, wp, wlen);
 			CHAR2INT(sp, t, strlen(t), wp, wlen);
 			MEMCPY(ts, wp, wlen);
 			if (seq_set(sp, name, strlen(tkp->name),
 			    ts, strlen(t), NULL, 0,
 			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
 				return (1);
 		} else {
 			CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
 			MEMCPY(name, wp, wlen);
 			CHAR2INT(sp, t, strlen(t), wp, wlen);
 			MEMCPY(ts, wp, wlen);
 			CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
 			MEMCPY(output, wp, wlen);
 			if (seq_set(sp, name, strlen(tkp->name),
 			    ts, strlen(t), output, strlen(tkp->output),
 			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
 				return (1);
 		}
 	}
 
 	/*
 	 * Rework any function key mappings that were set before the
 	 * screen was initialized.
 	 */
 	SLIST_FOREACH(qp, sp->gp->seqq, q)
 		if (F_ISSET(qp, SEQ_FUNCMAP))
 			(void)cl_pfmap(sp, qp->stype,
 			    qp->input, qp->ilen, qp->output, qp->olen);
 	return (0);
 }
 
 /*
  * cl_term_end --
  *	End the special keys defined by the termcap/terminfo entry.
  *
  * PUBLIC: int cl_term_end(GS *);
  */
 int
 cl_term_end(GS *gp)
 {
 	SEQ *qp, *nqp, *pre_qp = NULL;
 
 	/* Delete screen specific mappings. */
 	SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp)
 		if (F_ISSET(qp, SEQ_SCREEN)) {
 			if (qp == SLIST_FIRST(gp->seqq))
 				SLIST_REMOVE_HEAD(gp->seqq, q);
 			else
 				SLIST_REMOVE_AFTER(pre_qp, q);
 			(void)seq_free(qp);
 		} else
 			pre_qp = qp;
 	return (0);
 }
 
 /*
  * cl_fmap --
  *	Map a function key.
  *
  * PUBLIC: int cl_fmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t);
  */
 int
 cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
 {
 	/* Ignore until the screen is running, do the real work then. */
 	if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
 		return (0);
 	if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
 		return (0);
 
 	return (cl_pfmap(sp, stype, from, flen, to, tlen));
 }
 
 /*
  * cl_pfmap --
  *	Map a function key (private version).
  */
 static int
 cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
 {
 	size_t nlen;
 	char *p;
 	char name[64];
 	CHAR_T keyname[64];
 	CHAR_T ts[20];
 	CHAR_T *wp;
 	size_t wlen;
 
 	(void)snprintf(name, sizeof(name), "kf%d", 
 			(int)STRTOL(from+1,NULL,10));
 	if ((p = tigetstr(name)) == NULL ||
 	    p == (char *)-1 || strlen(p) == 0)
 		p = NULL;
 	if (p == NULL) {
 		msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key");
 		return (1);
 	}
 
 	nlen = SPRINTF(keyname,
 	    SIZE(keyname), L("function key %d"), 
 			(int)STRTOL(from+1,NULL,10));
 	CHAR2INT(sp, p, strlen(p), wp, wlen);
 	MEMCPY(ts, wp, wlen);
 	return (seq_set(sp, keyname, nlen,
 	    ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
 }
 
 /*
  * cl_optchange --
  *	Curses screen specific "option changed" routine.
  *
  * PUBLIC: int cl_optchange(SCR *, int, char *, u_long *);
  */
 int
 cl_optchange(SCR *sp, int opt, char *str, u_long *valp)
 {
 	CL_PRIVATE *clp;
 
 	clp = CLP(sp);
 
 	switch (opt) {
 	case O_TERM:
 		F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
 		/* FALLTHROUGH */
 	case O_COLUMNS:
 	case O_LINES:
 		/*
 		 * Changing the terminal type requires that we reinitialize
 		 * curses, while resizing does not.
 		 */
 		F_SET(sp->gp, G_SRESTART);
 		break;
 	case O_MESG:
 		(void)cl_omesg(sp, clp, *valp);
 		break;
 	case O_WINDOWNAME:
 		if (*valp) {
 			F_SET(clp, CL_RENAME_OK);
 
 			/*
 			 * If the screen is live, i.e. we're not reading the
 			 * .exrc file, update the window.
 			 */
 			if (sp->frp != NULL && sp->frp->name != NULL)
 				(void)cl_rename(sp, sp->frp->name, 1);
 		} else {
 			F_CLR(clp, CL_RENAME_OK);
 
 			(void)cl_rename(sp, NULL, 0);
 		}
 		break;
 	}
 	return (0);
 }
 
 /*
  * cl_omesg --
  *	Turn the tty write permission on or off.
  *
  * PUBLIC: int cl_omesg(SCR *, CL_PRIVATE *, int);
  */
 int
 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
 {
 	struct stat sb;
 	char *tty;
 
 	/* Find the tty, get the current permissions. */
 	if ((tty = ttyname(STDERR_FILENO)) == NULL) {
 		if (sp != NULL)
 			msgq(sp, M_SYSERR, "stderr");
 		return (1);
 	}
 	if (stat(tty, &sb) < 0) {
 		if (sp != NULL)
 			msgq(sp, M_SYSERR, "%s", tty);
 		return (1);
 	}
 
 	/* Save the original status if it's unknown. */
 	if (clp->tgw == TGW_UNKNOWN)
 		clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
 
 	/* Toggle the permissions. */
 	if (on) {
 		if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
 			if (sp != NULL)
 				msgq(sp, M_SYSERR,
 				    "046|messages not turned on: %s", tty);
 			return (1);
 		}
 	} else
 		if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
 			if (sp != NULL)
 				msgq(sp, M_SYSERR,
 				    "045|messages not turned off: %s", tty);
 			return (1);
 		}
 	return (0);
 }
 
 /*
  * cl_ssize --
  *	Return the terminal size.
  *
  * PUBLIC: int cl_ssize(SCR *, int, size_t *, size_t *, int *);
  */
 int
 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
 {
 	struct winsize win;
 	size_t col, row;
 	int rval;
 	char *p;
 
 	/* Assume it's changed. */
 	if (changedp != NULL)
 		*changedp = 1;
 
 	/*
 	 * !!!
 	 * sp may be NULL.
 	 *
 	 * Get the screen rows and columns.  If the values are wrong, it's
 	 * not a big deal -- as soon as the user sets them explicitly the
 	 * environment will be set and the screen package will use the new
 	 * values.
 	 *
 	 * Try TIOCGWINSZ.
 	 */
 	row = col = 0;
 	if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
 		row = win.ws_row;
 		col = win.ws_col;
 	}
 	/* If here because of suspend or a signal, only trust TIOCGWINSZ. */
 	if (sigwinch) {
 		/*
 		 * Somebody didn't get TIOCGWINSZ right, or has suspend
 		 * without window resizing support.  The user just lost,
 		 * but there's nothing we can do.
 		 */
 		if (row == 0 || col == 0) {
 			if (changedp != NULL)
 				*changedp = 0;
 			return (0);
 		}
 
 		/*
 		 * SunOS systems deliver SIGWINCH when windows are uncovered
 		 * as well as when they change size.  In addition, we call
 		 * here when continuing after being suspended since the window
 		 * may have changed size.  Since we don't want to background
 		 * all of the screens just because the window was uncovered,
 		 * ignore the signal if there's no change.
 		 */
 		if (sp != NULL &&
 		    row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
 			if (changedp != NULL)
 				*changedp = 0;
 			return (0);
 		}
 
 		if (rowp != NULL)
 			*rowp = row;
 		if (colp != NULL)
 			*colp = col;
 		return (0);
 	}
 
 	/*
 	 * !!!
 	 * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
 	 * routine is called before any termcap or terminal information
 	 * has been set up.  If there's no TERM environmental variable set,
 	 * let it go, at least ex can run.
 	 */
 	if (row == 0 || col == 0) {
 		if ((p = getenv("TERM")) == NULL)
 			goto noterm;
-		if (row == 0)
+		if (row == 0) {
 			if ((rval = tigetnum("lines")) < 0)
 				msgq(sp, M_SYSERR, "tigetnum: lines");
 			else
 				row = rval;
-		if (col == 0)
+		}
+		if (col == 0) {
 			if ((rval = tigetnum("cols")) < 0)
 				msgq(sp, M_SYSERR, "tigetnum: cols");
 			else
 				col = rval;
+		}
 	}
 
 	/* If nothing else, well, it's probably a VT100. */
 noterm:	if (row == 0)
 		row = 24;
 	if (col == 0)
 		col = 80;
 
 	/*
 	 * !!!
 	 * POSIX 1003.2 requires the environment to override everything.
 	 * Often, people can get nvi to stop messing up their screen by
 	 * deleting the LINES and COLUMNS environment variables from their
 	 * dot-files.
 	 */
 	if ((p = getenv("LINES")) != NULL)
 		row = atoz_or(p, row);
 	if ((p = getenv("COLUMNS")) != NULL)
 		col = atoz_or(p, col);
 
 	if (rowp != NULL)
 		*rowp = row;
 	if (colp != NULL)
 		*colp = col;
 	return (0);
 }
 
 /*
  * atoz_or --
  *	Parse non-zero positive decimal with a fallback.
  */
 static size_t
 atoz_or(const char *s, size_t y)
 {
 	char *ep;
 	long x = strtol(s, &ep, 10);
 
 	if (*ep == '\0' && (0 < x && x < INT_MAX))
 		return (size_t)x;
 	else
 		return y;
 }
 
 /*
  * cl_putchar --
  *	Function version of putchar, for tputs.
  *
  * PUBLIC: int cl_putchar(int);
  */
 int
 cl_putchar(int ch)
 {
 	return (putchar(ch));
 }
Index: head/contrib/nvi/common/common.h
===================================================================
--- head/contrib/nvi/common/common.h	(revision 366308)
+++ head/contrib/nvi/common/common.h	(revision 366309)
@@ -1,86 +1,94 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
-#include <db.h>			/* Only include db1. */
+#ifndef TCSASOFT
+#define TCSASOFT 0
+#endif
+
+#ifdef __linux__
+#include "/usr/include/db1/db.h"	/* Only include db1. */
+#else
+#include "/usr/include/db.h"	/* Only include db1. */
+#endif
 #include <regex.h>		/* May refer to the bundled regex. */
 
 /*
  * Forward structure declarations.  Not pretty, but the include files
  * are far too interrelated for a clean solution.
  */
 typedef struct _cb		CB;
 typedef struct _csc		CSC;
 typedef struct _conv		CONV;
 typedef struct _conv_win	CONVWIN;
 typedef struct _event		EVENT;
 typedef struct _excmd		EXCMD;
 typedef struct _exf		EXF;
 typedef struct _fref		FREF;
 typedef struct _gs		GS;
 typedef struct _lmark		LMARK;
 typedef struct _mark		MARK;
 typedef struct _msg		MSGS;
 typedef struct _option		OPTION;
 typedef struct _optlist		OPTLIST;
 typedef struct _scr		SCR;
 typedef struct _script		SCRIPT;
 typedef struct _seq		SEQ;
 typedef struct _tag		TAG;
 typedef struct _tagf		TAGF;
 typedef struct _tagq		TAGQ;
 typedef struct _text		TEXT;
 
 /* Autoindent state. */
 typedef enum { C_NOTSET, C_CARATSET, C_ZEROSET } carat_t;
 
 /* Busy message types. */
 typedef enum { BUSY_ON = 1, BUSY_OFF, BUSY_UPDATE } busy_t;
 
 /*
  * Routines that return a confirmation return:
  *
  *	CONF_NO		User answered no.
  *	CONF_QUIT	User answered quit, eof or an error.
  *	CONF_YES	User answered yes.
  */
 typedef enum { CONF_NO, CONF_QUIT, CONF_YES } conf_t;
 
 /* Directions. */
 typedef enum { NOTSET, FORWARD, BACKWARD } dir_t;
 
 /* Line operations. */
 typedef enum { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET } lnop_t;
 
 /* Lock return values. */
 typedef enum { LOCK_FAILED, LOCK_SUCCESS, LOCK_UNAVAIL } lockr_t;
 
 /* Sequence types. */
 typedef enum { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT } seq_t;
 
 /*
  * Local includes.
  */
 #include "key.h"		/* Required by args.h. */
 #include "args.h"		/* Required by options.h. */
 #include "options.h"		/* Required by screen.h. */
 
 #include "msg.h"		/* Required by gs.h. */
 #include "cut.h"		/* Required by gs.h. */
 #include "seq.h"		/* Required by screen.h. */
 #include "util.h"		/* Required by ex.h. */
 #include "mark.h"		/* Required by gs.h. */
 #include "conv.h"		/* Required by ex.h and screen.h */
 #include "../ex/ex.h"		/* Required by gs.h. */
 #include "gs.h"			/* Required by screen.h. */
 #include "screen.h"		/* Required by exf.h. */
 #include "exf.h"
 #include "log.h"
 #include "mem.h"
 
 #include "extern.h"
Index: head/contrib/nvi/common/cut.h
===================================================================
--- head/contrib/nvi/common/cut.h	(revision 366308)
+++ head/contrib/nvi/common/cut.h	(revision 366309)
@@ -1,77 +1,77 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 typedef struct _texth TEXTH;		/* TEXT list head structure. */
 TAILQ_HEAD(_texth, _text);
 
 /* Cut buffers. */
 struct _cb {
 	SLIST_ENTRY(_cb) q;		/* Linked list of cut buffers. */
 	TEXTH	 textq[1];		/* Linked list of TEXT structures. */
 	/* XXXX Needed ? Can non ascii-chars be cut buffer names ? */
 	CHAR_T	 name;			/* Cut buffer name. */
 	size_t	 len;			/* Total length of cut text. */
 
 #define	CB_LMODE	0x01		/* Cut was in line mode. */
 	u_int8_t flags;
 };
 
 /* Lines/blocks of text. */
 struct _text {				/* Text: a linked list of lines. */
 	TAILQ_ENTRY(_text) q;		/* Linked list of text structures. */
 	CHAR_T	*lb;			/* Line buffer. */
 	size_t	 lb_len;		/* Line buffer length. */
 	size_t	 len;			/* Line length. */
 
 	/* These fields are used by the vi text input routine. */
 	recno_t	 lno;			/* 1-N: file line. */
 
 #define	ENTIRE_LINE	((size_t)-1)	/* cno: end of the line. */
 	size_t	 cno;			/* 0-N: file character in line. */
 	size_t	 ai;			/* 0-N: autoindent bytes. */
 	size_t	 insert;		/* 0-N: bytes to insert (push). */
 	size_t	 offset;		/* 0-N: initial, unerasable chars. */
 	size_t	 owrite;		/* 0-N: chars to overwrite. */
 	size_t	 R_erase;		/* 0-N: 'R' erase count. */
 	size_t	 sv_cno;		/* 0-N: Saved line cursor. */
 	size_t	 sv_len;		/* 0-N: Saved line length. */
 
 	/*
 	 * These fields returns information from the vi text input routine.
 	 *
 	 * The termination condition.  Note, this field is only valid if the
 	 * text input routine returns success.
 	 *	TERM_BS:	User backspaced over the prompt.
 	 *	TERM_CEDIT:	User entered <edit-char>.
 	 *	TERM_CR:	User entered <carriage-return>; no data.
 	 *	TERM_ESC:	User entered <escape>; no data.
 	 *	TERM_OK:	Data available.
 	 *	TERM_SEARCH:	Incremental search.
 	 */
 	enum {
 	    TERM_BS, TERM_CEDIT, TERM_CR, TERM_ESC, TERM_OK, TERM_SEARCH
 	} term;
 };
 
 /*
  * Get named buffer 'name'.
  * Translate upper-case buffer names to lower-case buffer names.
  */
-#define	CBNAME(sp, cbp, nch) {						\
+#define	CBNAME(sp, cbp, nch) do {					\
 	CHAR_T L__name;							\
 	L__name = isupper(nch) ? tolower(nch) : (nch);			\
 	SLIST_FOREACH(cbp, sp->gp->cutq, q)				\
 		if (cbp->name == L__name)				\
 			break;						\
-}
+} while (0)
 
 /* Flags to the cut() routine. */
 #define	CUT_LINEMODE	0x01		/* Cut in line mode. */
 #define	CUT_NUMOPT	0x02		/* Numeric buffer: optional. */
 #define	CUT_NUMREQ	0x04		/* Numeric buffer: required. */
Index: head/contrib/nvi/common/exf.c
===================================================================
--- head/contrib/nvi/common/exf.c	(revision 366308)
+++ head/contrib/nvi/common/exf.c	(revision 366309)
@@ -1,1476 +1,1479 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 
 /*
  * We include <sys/file.h>, because the flock(2) and open(2) #defines
  * were found there on historical systems.  We also include <fcntl.h>
  * because the open(2) #defines are found there on newer systems.
  */
 #include <sys/file.h>
 
 #include <bitstring.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "common.h"
 
 static int	file_backup(SCR *, char *, char *);
 static void	file_cinit(SCR *);
 static void	file_encinit(SCR *);
 static void	file_comment(SCR *);
 static int	file_spath(SCR *, FREF *, struct stat *, int *);
 
 /*
  * file_add --
  *	Insert a file name into the FREF list, if it doesn't already
  *	appear in it.
  *
  * !!!
  * The "if it doesn't already appear" changes vi's semantics slightly.  If
  * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
  * will reflect the line/column of the previous edit session.  Historic nvi
  * did not do this.  The change is a logical extension of the change where
  * vi now remembers the last location in any file that it has ever edited,
  * not just the previously edited file.
  *
  * PUBLIC: FREF *file_add(SCR *, char *);
  */
 FREF *
 file_add(SCR *sp, char *name)
 {
 	GS *gp;
 	FREF *frp, *tfrp;
 
 	/*
 	 * Return it if it already exists.  Note that we test against the
 	 * user's name, whatever that happens to be, including if it's a
 	 * temporary file.
 	 *
 	 * If the user added a file but was unable to initialize it, there
 	 * can be file list entries where the name field is NULL.  Discard
 	 * them the next time we see them.
 	 */
 	gp = sp->gp;
 	if (name != NULL)
 		TAILQ_FOREACH_SAFE(frp, gp->frefq, q, tfrp) {
 			if (frp->name == NULL) {
 				TAILQ_REMOVE(gp->frefq, frp, q);
 				free(frp->name);
 				free(frp);
 				continue;
 			}
 			if (!strcmp(frp->name, name))
 				return (frp);
 		}
 
 	/* Allocate and initialize the FREF structure. */
 	CALLOC(sp, frp, 1, sizeof(FREF));
 	if (frp == NULL)
 		return (NULL);
 
 	/*
 	 * If no file name specified, or if the file name is a request
 	 * for something temporary, file_init() will allocate the file
 	 * name.  Temporary files are always ignored.
 	 */
 	if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
 	    (frp->name = strdup(name)) == NULL) {
 		free(frp);
 		msgq(sp, M_SYSERR, NULL);
 		return (NULL);
 	}
 
 	/* Append into the chain of file names. */
 	TAILQ_INSERT_TAIL(gp->frefq, frp, q);
 
 	return (frp);
 }
 
 /*
  * file_init --
  *	Start editing a file, based on the FREF structure.  If successsful,
  *	let go of any previous file.  Don't release the previous file until
  *	absolutely sure we have the new one.
  *
  * PUBLIC: int file_init(SCR *, FREF *, char *, int);
  */
 int
 file_init(SCR *sp, FREF *frp, char *rcv_name, int flags)
 {
 	EXF *ep;
 	RECNOINFO oinfo = { 0 };
 	struct stat sb;
 	size_t psize;
 	int fd, exists, open_err, readonly;
 	char *oname, *tname;
 
 	open_err = readonly = 0;
 
 	/*
 	 * If the file is a recovery file, let the recovery code handle it.
 	 * Clear the FR_RECOVER flag first -- the recovery code does set up,
 	 * and then calls us!  If the recovery call fails, it's probably
 	 * because the named file doesn't exist.  So, move boldly forward,
 	 * presuming that there's an error message the user will get to see.
 	 */
 	if (F_ISSET(frp, FR_RECOVER)) {
 		F_CLR(frp, FR_RECOVER);
 		return (rcv_read(sp, frp));
 	}
 
 	/*
 	 * Required FRP initialization; the only flag we keep is the
 	 * cursor information.
 	 */
 	F_CLR(frp, ~FR_CURSORSET);
 
 	/*
 	 * Required EXF initialization:
 	 *	Flush the line caches.
 	 *	Default recover mail file fd to -1.
 	 *	Set initial EXF flag bits.
 	 */
 	CALLOC_RET(sp, ep, 1, sizeof(EXF));
 	ep->c_lno = ep->c_nlines = OOBLNO;
 	ep->rcv_fd = -1;
 	F_SET(ep, F_FIRSTMODIFY);
 
 	/*
 	 * Scan the user's path to find the file that we're going to
 	 * try and open.
 	 */
 	if (file_spath(sp, frp, &sb, &exists))
 		return (1);
 
 	/*
 	 * If no name or backing file, for whatever reason, create a backing
 	 * temporary file, saving the temp file name so we can later unlink
 	 * it.  If the user never named this file, copy the temporary file name
 	 * to the real name (we display that until the user renames it).
 	 */
 	oname = frp->name;
 	if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
 		struct stat sb;
 
 		if (opts_empty(sp, O_TMPDIR, 0))
 			goto err;
 		if ((tname =
 		    join(O_STR(sp, O_TMPDIR), "vi.XXXXXXXXXX")) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			goto err;
 		}
 		if ((fd = mkstemp(tname)) == -1 || fstat(fd, &sb)) {
 			free(tname);
 			msgq(sp, M_SYSERR,
 			    "237|Unable to create temporary file");
 			goto err;
 		}
 		(void)close(fd);
 
 		frp->tname = tname;
 		if (frp->name == NULL) {
 			F_SET(frp, FR_TMPFILE);
 			if ((frp->name = strdup(tname)) == NULL) {
 				msgq(sp, M_SYSERR, NULL);
 				goto err;
 			}
 		}
 		oname = frp->tname;
 		psize = 1024;
 		if (!LF_ISSET(FS_OPENERR))
 			F_SET(frp, FR_NEWFILE);
 
-		ep->mtim = sb.st_mtimespec;
+		ep->mtim = sb.st_mtim;
 	} else {
 		/*
 		 * XXX
 		 * A seat of the pants calculation: try to keep the file in
 		 * 15 pages or less.  Don't use a page size larger than 16K
 		 * (vi should have good locality) or smaller than 1K.
 		 */
 		psize = ((sb.st_size / 15) + 1023) / 1024;
 		if (psize > 16)
 			psize = 16;
 		if (psize == 0)
 			psize = 1;
 		psize = p2roundup(psize) << 10;
 
 		F_SET(ep, F_DEVSET);
 		ep->mdev = sb.st_dev;
 		ep->minode = sb.st_ino;
 
-		ep->mtim = sb.st_mtimespec;
+		ep->mtim = sb.st_mtim;
 
 		if (!S_ISREG(sb.st_mode))
 			msgq_str(sp, M_ERR, oname,
 			    "238|Warning: %s is not a regular file");
 	}
 
 	/* Set up recovery. */
 	oinfo.bval = '\n';			/* Always set. */
 	oinfo.psize = psize;
 	oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
 	if (rcv_name == NULL) {
 		if (!rcv_tmp(sp, ep, frp->name))
 			oinfo.bfname = ep->rcv_path;
 	} else {
 		if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			goto err;
 		}
 		oinfo.bfname = ep->rcv_path;
 		F_SET(ep, F_MODIFIED);
 	}
 
 	/* Open a db structure. */
 	if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
 	    O_NONBLOCK | O_RDONLY,
 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
 	    DB_RECNO, &oinfo)) == NULL) {
 		msgq_str(sp,
 		    M_SYSERR, rcv_name == NULL ? oname : rcv_name, "%s");
 		if (F_ISSET(frp, FR_NEWFILE))
 			goto err;
 		/*
 		 * !!!
 		 * Historically, vi permitted users to edit files that couldn't
 		 * be read.  This isn't useful for single files from a command
 		 * line, but it's quite useful for "vi *.c", since you can skip
 		 * past files that you can't read.
 		 */ 
 		open_err = 1;
 		goto oerr;
 	}
 
 	/*
 	 * Do the remaining things that can cause failure of the new file,
 	 * mark and logging initialization.
 	 */
 	if (mark_init(sp, ep) || log_init(sp, ep))
 		goto err;
 
 	/*
 	 * Set the alternate file name to be the file we're discarding.
 	 *
 	 * !!!
 	 * Temporary files can't become alternate files, so there's no file
 	 * name.  This matches historical practice, although it could only
 	 * happen in historical vi as the result of the initial command, i.e.
 	 * if vi was executed without a file name.
 	 */
 	if (LF_ISSET(FS_SETALT))
 		set_alt_name(sp, sp->frp == NULL ||
 		    F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name);
 
 	/*
 	 * Close the previous file; if that fails, close the new one and run
 	 * for the border.
 	 *
 	 * !!!
 	 * There's a nasty special case.  If the user edits a temporary file,
 	 * and then does an ":e! %", we need to re-initialize the backing
 	 * file, but we can't change the name.  (It's worse -- we're dealing
 	 * with *names* here, we can't even detect that it happened.)  Set a
 	 * flag so that the file_end routine ignores the backing information
 	 * of the old file if it happens to be the same as the new one.
 	 *
 	 * !!!
 	 * Side-effect: after the call to file_end(), sp->frp may be NULL.
 	 */
 	if (sp->ep != NULL) {
 		F_SET(frp, FR_DONTDELETE);
 		if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) {
 			(void)file_end(sp, ep, 1);
 			goto err;
 		}
 		F_CLR(frp, FR_DONTDELETE);
 	}
 
 	/*
 	 * Lock the file; if it's a recovery file, it should already be
 	 * locked.  Note, we acquire the lock after the previous file
 	 * has been ended, so that we don't get an "already locked" error
 	 * for ":edit!".
 	 *
 	 * XXX
 	 * While the user can't interrupt us between the open and here,
 	 * there's a race between the dbopen() and the lock.  Not much
 	 * we can do about it.
 	 *
 	 * XXX
 	 * We don't make a big deal of not being able to lock the file.  As
 	 * locking rarely works over NFS, and often fails if the file was
 	 * mmap(2)'d, it's far too common to do anything like print an error
 	 * message, let alone make the file readonly.  At some future time,
 	 * when locking is a little more reliable, this should change to be
 	 * an error.
 	 */
 	if (rcv_name == NULL)
 		switch (file_lock(sp, oname, ep->db->fd(ep->db), 0)) {
 		case LOCK_FAILED:
 			F_SET(frp, FR_UNLOCKED);
 			break;
 		case LOCK_UNAVAIL:
 			readonly = 1;
 			if (F_ISSET(sp, SC_READONLY))
 				break;
 			msgq_str(sp, M_INFO, oname,
 			    "239|%s already locked, session is read-only");
 			break;
 		case LOCK_SUCCESS:
 			break;
 		}
 
 	/*
          * Historically, the readonly edit option was set per edit buffer in
          * vi, unless the -R command-line option was specified or the program
          * was executed as "view".  (Well, to be truthful, if the letter 'w'
          * occurred anywhere in the program name, but let's not get into that.)
 	 * So, the persistent readonly state has to be stored in the screen
 	 * structure, and the edit option value toggles with the contents of
 	 * the edit buffer.  If the persistent readonly flag is set, set the
 	 * readonly edit option.
 	 *
 	 * Otherwise, try and figure out if a file is readonly.  This is a
 	 * dangerous thing to do.  The kernel is the only arbiter of whether
 	 * or not a file is writeable, and the best that a user program can
 	 * do is guess.  Obvious loopholes are files that are on a file system
 	 * mounted readonly (access catches this one on a few systems), or
 	 * alternate protection mechanisms, ACL's for example, that we can't
 	 * portably check.  Lots of fun, and only here because users whined.
 	 *
 	 * !!!
 	 * Historic vi displayed the readonly message if none of the file
 	 * write bits were set, or if an an access(2) call on the path
 	 * failed.  This seems reasonable.  If the file is mode 444, root
 	 * users may want to know that the owner of the file did not expect
 	 * it to be written.
 	 *
 	 * Historic vi set the readonly bit if no write bits were set for
 	 * a file, even if the access call would have succeeded.  This makes
 	 * the superuser force the write even when vi expects that it will
 	 * succeed.  I'm less supportive of this semantic, but it's historic
 	 * practice and the conservative approach to vi'ing files as root.
 	 *
 	 * It would be nice if there was some way to update this when the user
 	 * does a "^Z; chmod ...".  The problem is that we'd first have to
 	 * distinguish between readonly bits set because of file permissions
 	 * and those set for other reasons.  That's not too hard, but deciding
 	 * when to reevaluate the permissions is trickier.  An alternative
 	 * might be to turn off the readonly bit if the user forces a write
 	 * and it succeeds.
 	 *
 	 * XXX
 	 * Access(2) doesn't consider the effective uid/gid values.  This
 	 * probably isn't a problem for vi when it's running standalone.
 	 */
 	if (readonly || F_ISSET(sp, SC_READONLY) ||
 	    (!F_ISSET(frp, FR_NEWFILE) &&
 	    (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
 	    access(frp->name, W_OK))))
 		O_SET(sp, O_READONLY);
 	else
 		O_CLR(sp, O_READONLY);
 
 	/* Switch... */
 	++ep->refcnt;
 	sp->ep = ep;
 	sp->frp = frp;
 
 	/* Detect and set the file encoding */
 	file_encinit(sp);
 
 	/* Set the initial cursor position, queue initial command. */
 	file_cinit(sp);
 
 	/* Redraw the screen from scratch, schedule a welcome message. */
 	F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
 
 	return (0);
 
 err:	free(frp->name);
 	frp->name = NULL;
 	if (frp->tname != NULL) {
 		(void)unlink(frp->tname);
 		free(frp->tname);
 		frp->tname = NULL;
 	}
 
 oerr:	if (F_ISSET(ep, F_RCV_ON))
 		(void)unlink(ep->rcv_path);
 	free(ep->rcv_path);
 	ep->rcv_path = NULL;
 
 	if (ep->db != NULL)
 		(void)ep->db->close(ep->db);
 	free(ep);
 
 	return (open_err ?
 	    file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1);
 }
 
 /*
  * file_spath --
  *	Scan the user's path to find the file that we're going to
  *	try and open.
  */
 static int
 file_spath(SCR *sp, FREF *frp, struct stat *sbp, int *existsp)
 {
 	int savech;
 	size_t len;
 	int found;
 	char *name, *p, *t, *path;
 
 	/*
 	 * If the name is NULL or an explicit reference (i.e., the first
 	 * component is . or ..) ignore the O_PATH option.
 	 */
 	name = frp->name;
 	if (name == NULL) {
 		*existsp = 0;
 		return (0);
 	}
 	if (name[0] == '/' || (name[0] == '.' &&
 	    (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) {
 		*existsp = !stat(name, sbp);
 		return (0);
 	}
 
 	/* Try . */
 	if (!stat(name, sbp)) {
 		*existsp = 1;
 		return (0);
 	}
 
 	/* Try the O_PATH option values. */
 	for (found = 0, p = t = O_STR(sp, O_PATH);; ++p)
 		if (*p == ':' || *p == '\0') {
 			/*
 			 * Ignore the empty strings and ".", since we've already
 			 * tried the current directory.
 			 */
 			if (t < p && (p - t != 1 || *t != '.')) {
 				savech = *p;
 				*p = '\0';
 				if ((path = join(t, name)) == NULL) {
 					msgq(sp, M_SYSERR, NULL);
 					break;
 				}
 				len = strlen(path);
 				*p = savech;
 				if (!stat(path, sbp)) {
 					found = 1;
 					break;
 				}
 				free(path);
 			}
 			t = p + 1;
 			if (*p == '\0')
 				break;
 		}
 
 	/* If we found it, build a new pathname and discard the old one. */
 	if (found) {
 		free(frp->name);
 		frp->name = path;
 	}
 	*existsp = found;
 	return (0);
 }
 
 /*
  * file_cinit --
  *	Set up the initial cursor position.
  */
 static void
 file_cinit(SCR *sp)
 {
 	GS *gp;
 	MARK m;
 	size_t len;
 	int nb;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/* Set some basic defaults. */
 	sp->lno = 1;
 	sp->cno = 0;
 
 	/*
 	 * Historically, initial commands (the -c option) weren't executed
 	 * until a file was loaded, e.g. "vi +10 nofile", followed by an
 	 * :edit or :tag command, would execute the +10 on the file loaded
 	 * by the subsequent command, (assuming that it existed).  This
 	 * applied as well to files loaded using the tag commands, and we
 	 * follow that historic practice.  Also, all initial commands were
 	 * ex commands and were always executed on the last line of the file.
 	 *
 	 * Otherwise, if no initial command for this file:
 	 *    If in ex mode, move to the last line, first nonblank character.
 	 *    If the file has previously been edited, move to the last known
 	 *	  position, and check it for validity.
 	 *    Otherwise, move to the first line, first nonblank.
 	 *
 	 * This gets called by the file init code, because we may be in a
 	 * file of ex commands and we want to execute them from the right
 	 * location in the file.
 	 */
 	nb = 0;
 	gp = sp->gp;
 	if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) {
 		if (db_last(sp, &sp->lno))
 			return;
 		if (sp->lno == 0) {
 			sp->lno = 1;
 			sp->cno = 0;
 		}
 		CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1,
 			 wp, wlen);
 		if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 0))
 			return;
 		gp->c_option = NULL;
 	} else if (F_ISSET(sp, SC_EX)) {
 		if (db_last(sp, &sp->lno))
 			return;
 		if (sp->lno == 0) {
 			sp->lno = 1;
 			sp->cno = 0;
 			return;
 		}
 		nb = 1;
 	} else {
 		if (F_ISSET(sp->frp, FR_CURSORSET)) {
 			sp->lno = sp->frp->lno;
 			sp->cno = sp->frp->cno;
 
 			/* If returning to a file in vi, center the line. */
 			 F_SET(sp, SC_SCR_CENTER);
 		} else {
 			if (O_ISSET(sp, O_COMMENT))
 				file_comment(sp);
 			else
 				sp->lno = 1;
 			nb = 1;
 		}
 		if (db_get(sp, sp->lno, 0, NULL, &len)) {
 			sp->lno = 1;
 			sp->cno = 0;
 			return;
 		}
 		if (!nb && sp->cno > len)
 			nb = 1;
 	}
 	if (nb) {
 		sp->cno = 0;
 		(void)nonblank(sp, sp->lno, &sp->cno);
 	}
 
 	/*
 	 * !!!
 	 * The initial column is also the most attractive column.
 	 */
 	sp->rcm = sp->cno;
 
 	/*
 	 * !!!
 	 * Historically, vi initialized the absolute mark, but ex did not.
 	 * Which meant, that if the first command in ex mode was "visual",
 	 * or if an ex command was executed first (e.g. vi +10 file) vi was
 	 * entered without the mark being initialized.  For consistency, if
 	 * the file isn't empty, we initialize it for everyone, believing
 	 * that it can't hurt, and is generally useful.  Not initializing it
 	 * if the file is empty is historic practice, although it has always
 	 * been possible to set (and use) marks in empty vi files.
 	 */
 	m.lno = sp->lno;
 	m.cno = sp->cno;
 	(void)mark_set(sp, ABSMARK1, &m, 0);
 }
 
 /*
  * file_end --
  *	Stop editing a file.
  *
  * PUBLIC: int file_end(SCR *, EXF *, int);
  */
 int
 file_end(SCR *sp, EXF *ep, int force)
 {
 	FREF *frp;
 
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 * (If argument ep is NULL, use sp->ep.)
 	 *
 	 * If multiply referenced, just decrement the count and return.
 	 */
 	if (ep == NULL)
 		ep = sp->ep;
 	if (--ep->refcnt != 0)
 		return (0);
 
 	/*
 	 *
 	 * Clean up the FREF structure.
 	 *
 	 * Save the cursor location.
 	 *
 	 * XXX
 	 * It would be cleaner to do this somewhere else, but by the time
 	 * ex or vi knows that we're changing files it's already happened.
 	 */
 	frp = sp->frp;
 	frp->lno = sp->lno;
 	frp->cno = sp->cno;
 	F_SET(frp, FR_CURSORSET);
 
 	/*
 	 * We may no longer need the temporary backing file, so clean it
 	 * up.  We don't need the FREF structure either, if the file was
 	 * never named, so lose it.
 	 *
 	 * !!!
 	 * Re: FR_DONTDELETE, see the comment above in file_init().
 	 */
 	if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
 		if (unlink(frp->tname))
 			msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove");
 		free(frp->tname);
 		frp->tname = NULL;
 		if (F_ISSET(frp, FR_TMPFILE)) {
 			TAILQ_REMOVE(sp->gp->frefq, frp, q);
 			free(frp->name);
 			free(frp);
 		}
 		sp->frp = NULL;
 	}
 
 	/*
 	 * Clean up the EXF structure.
 	 *
 	 * Close the db structure.
 	 */
 	if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
 		msgq_str(sp, M_SYSERR, frp->name, "241|%s: close");
 		++ep->refcnt;
 		return (1);
 	}
 
 	/* COMMITTED TO THE CLOSE.  THERE'S NO GOING BACK... */
 
 	/* Stop logging. */
 	(void)log_end(sp, ep);
 
 	/* Free up any marks. */
 	(void)mark_end(sp, ep);
 
 	/*
 	 * Delete recovery files, close the open descriptor, free recovery
 	 * memory.  See recover.c for a description of the protocol.
 	 *
 	 * XXX
 	 * Unlink backup file first, we can detect that the recovery file
 	 * doesn't reference anything when the user tries to recover it.
 	 * There's a race, here, obviously, but it's fairly small.
 	 */
 	if (!F_ISSET(ep, F_RCV_NORM)) {
 		if (ep->rcv_path != NULL && unlink(ep->rcv_path))
 			msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove");
 		if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
 			msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove");
 	}
 	if (ep->rcv_fd != -1)
 		(void)close(ep->rcv_fd);
 	free(ep->rcv_path);
 	free(ep->rcv_mpath);
 	if (ep->c_blen > 0)
 		free(ep->c_lp);
 
 	free(ep);
 	return (0);
 }
 
 /*
  * file_write --
  *	Write the file to disk.  Historic vi had fairly convoluted
  *	semantics for whether or not writes would happen.  That's
  *	why all the flags.
  *
  * PUBLIC: int file_write(SCR *, MARK *, MARK *, char *, int);
  */
 int
 file_write(SCR *sp, MARK *fm, MARK *tm, char *name, int flags)
 {
 	enum { NEWFILE, OLDFILE } mtype;
 	struct stat sb;
 	EXF *ep;
 	FILE *fp;
 	FREF *frp;
 	MARK from, to;
 	size_t len;
 	u_long nlno, nch;
 	int fd, nf, noname, oflags, rval;
 	char *p, *s, *t, buf[1024];
 	const char *msgstr;
 
 	ep = sp->ep;
 	frp = sp->frp;
 
 	/*
 	 * Writing '%', or naming the current file explicitly, has the
 	 * same semantics as writing without a name.
 	 */
 	if (name == NULL || !strcmp(name, frp->name)) {
 		noname = 1;
 		name = frp->name;
 	} else
 		noname = 0;
 
 	/* Can't write files marked read-only, unless forced. */
 	if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) {
 		msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
 		    "244|Read-only file, not written; use ! to override" :
 		    "245|Read-only file, not written");
 		return (1);
 	}
 
 	/* If not forced, not appending, and "writeany" not set ... */
 	if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
 		/* Don't overwrite anything but the original file. */
 		if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
 		    !stat(name, &sb)) {
 			msgq_str(sp, M_ERR, name,
 			    LF_ISSET(FS_POSSIBLE) ?
 			    "246|%s exists, not written; use ! to override" :
 			    "247|%s exists, not written");
 			return (1);
 		}
 
 		/*
 		 * Don't write part of any existing file.  Only test for the
 		 * original file, the previous test catches anything else.
 		 */
 		if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
 			msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
 			    "248|Partial file, not written; use ! to override" :
 			    "249|Partial file, not written");
 			return (1);
 		}
 	}
 
 	/*
 	 * Figure out if the file already exists -- if it doesn't, we display
 	 * the "new file" message.  The stat might not be necessary, but we
 	 * just repeat it because it's easier than hacking the previous tests.
 	 * The information is only used for the user message and modification
 	 * time test, so we can ignore the obvious race condition.
 	 *
 	 * One final test.  If we're not forcing or appending the current file,
 	 * and we have a saved modification time, object if the file changed
 	 * since we last edited or wrote it, and make them force it.
 	 */
 	if (stat(name, &sb))
 		mtype = NEWFILE;
 	else {
 		if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) &&
 		    ((F_ISSET(ep, F_DEVSET) &&
 		    (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) ||
-		    timespeccmp(&sb.st_mtimespec, &ep->mtim, !=))) {
+		    timespeccmp(&sb.st_mtim, &ep->mtim, !=))) {
 			msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ?
 "250|%s: file modified more recently than this copy; use ! to override" :
 "251|%s: file modified more recently than this copy");
 			return (1);
 		}
 
 		mtype = OLDFILE;
 	}
 
 	/* Set flags to create, write, and either append or truncate. */
 	oflags = O_CREAT | O_WRONLY |
 	    (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC);
 
 	/* Backup the file if requested. */
 	if (!opts_empty(sp, O_BACKUP, 1) &&
 	    file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE))
 		return (1);
 
 	/* Open the file. */
 	if ((fd = open(name, oflags,
 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) {
 		if (errno == EACCES && LF_ISSET(FS_FORCE)) {
 			/*
 			 * If the user owns the file but does not
 			 * have write permission on it, grant it
 			 * automatically for the duration of the
 			 * opening of the file, if possible.
 			 */
 			struct stat sb;
 			mode_t fmode;
 
 			if (stat(name, &sb) != 0)
 				goto fail_open;
 			fmode = sb.st_mode;
 			if (!(sb.st_mode & S_IWUSR) && sb.st_uid == getuid())
 				fmode |= S_IWUSR;
 			else
 				goto fail_open;
 			if (chmod(name, fmode) != 0)
 				goto fail_open;
 			fd = open(name, oflags, S_IRUSR | S_IWUSR |
 			    S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 			if (fd == -1)
 				goto fail_open;
 			(void)fchmod(fd, sb.st_mode);
 			goto success_open;
 		fail_open:
 			errno = EACCES;
 		}
 		msgq_str(sp, M_SYSERR, name, "%s");
 		return (1);
 	}
 success_open:
 
 	/* Try and get a lock. */
 	if (!noname && file_lock(sp, NULL, fd, 0) == LOCK_UNAVAIL)
 		msgq_str(sp, M_ERR, name,
 		    "252|%s: write lock was unavailable");
 
 	/*
 	 * Use stdio for buffering.
 	 *
 	 * XXX
 	 * SVR4.2 requires the fdopen mode exactly match the original open
 	 * mode, i.e. you have to open with "a" if appending.
 	 */
 	if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) {
 		msgq_str(sp, M_SYSERR, name, "%s");
 		(void)close(fd);
 		return (1);
 	}
 
 	/* Build fake addresses, if necessary. */
 	if (fm == NULL) {
 		from.lno = 1;
 		from.cno = 0;
 		fm = &from;
 		if (db_last(sp, &to.lno))
 			return (1);
 		to.cno = 0;
 		tm = &to;
 	}
 
 	rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0);
 
 	/*
 	 * Save the new last modification time -- even if the write fails
 	 * we re-init the time.  That way the user can clean up the disk
 	 * and rewrite without having to force it.
 	 */
-	if (noname)
+	if (noname) {
 		if (stat(name, &sb))
 			timepoint_system(&ep->mtim);
 		else {
 			F_SET(ep, F_DEVSET);
 			ep->mdev = sb.st_dev;
 			ep->minode = sb.st_ino;
 
-			ep->mtim = sb.st_mtimespec;
+			ep->mtim = sb.st_mtim;
 		}
+	}
 
 	/*
 	 * If the write failed, complain loudly.  ex_writefp() has already
 	 * complained about the actual error, reinforce it if data was lost.
 	 */
 	if (rval) {
 		if (!LF_ISSET(FS_APPEND))
 			msgq_str(sp, M_ERR, name,
 			    "254|%s: WARNING: FILE TRUNCATED");
 		return (1);
 	}
 
 	/*
 	 * Once we've actually written the file, it doesn't matter that the
 	 * file name was changed -- if it was, we've already whacked it.
 	 */
 	F_CLR(frp, FR_NAMECHANGE);
 
 	/*
 	 * If wrote the entire file, and it wasn't by appending it to a file,
 	 * clear the modified bit.  If the file was written to the original
 	 * file name and the file is a temporary, set the "no exit" bit.  This
 	 * permits the user to write the file and use it in the context of the
 	 * filesystem, but still keeps them from discarding their changes by
 	 * exiting.
 	 */
 	if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) {
 		F_CLR(ep, F_MODIFIED);
-		if (F_ISSET(frp, FR_TMPFILE))
+		if (F_ISSET(frp, FR_TMPFILE)) {
 			if (noname)
 				F_SET(frp, FR_TMPEXIT);
 			else
 				F_CLR(frp, FR_TMPEXIT);
+		}
 	}
 
 	p = msg_print(sp, name, &nf);
 	switch (mtype) {
 	case NEWFILE:
 		msgstr = msg_cat(sp,
 		    "256|%s: new file: %lu lines, %lu characters", NULL);
 		len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
 		break;
 	case OLDFILE:
 		msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ?
 		    "315|%s: appended: %lu lines, %lu characters" :
 		    "257|%s: %lu lines, %lu characters", NULL);
 		len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
 		break;
 	default:
 		abort();
 	}
 
 	/*
 	 * There's a nasty problem with long path names.  Cscope and tags files
 	 * can result in long paths and vi will request a continuation key from
 	 * the user.  Unfortunately, the user has typed ahead, and chaos will
 	 * result.  If we assume that the characters in the filenames only take
 	 * a single screen column each, we can trim the filename.
 	 */
 	s = buf;
 	if (len >= sp->cols) {
 		for (s = buf, t = buf + strlen(p); s < t &&
 		    (*s != '/' || len >= sp->cols - 3); ++s, --len);
 		if (s == t)
 			s = buf;
 		else {
 			*--s = '.';		/* Leading ellipses. */
 			*--s = '.';
 			*--s = '.';
 		}
 	}
 	msgq(sp, M_INFO, "%s", s);
 	if (nf)
 		FREE_SPACE(sp, p, 0);
 	return (0);
 }
 
 /*
  * file_backup --
  *	Backup the about-to-be-written file.
  *
  * XXX
  * We do the backup by copying the entire file.  It would be nice to do
  * a rename instead, but: (1) both files may not fit and we want to fail
  * before doing the rename; (2) the backup file may not be on the same
  * disk partition as the file being written; (3) there may be optional
  * file information (MACs, DACs, whatever) that we won't get right if we
  * recreate the file.  So, let's not risk it.
  */
 static int
 file_backup(SCR *sp, char *name, char *bname)
 {
 	struct dirent *dp;
 	struct stat sb;
 	DIR *dirp;
 	EXCMD cmd;
 	off_t off;
 	size_t blen;
 	int flags, maxnum, nr, num, nw, rfd, wfd, version;
 	char *bp, *estr, *p, *pct, *slash, *t, *wfname, buf[8192];
 	CHAR_T *wp;
 	size_t wlen;
 	size_t nlen;
 	char *d = NULL;
 
 	rfd = wfd = -1;
 	bp = estr = wfname = NULL;
 
 	/*
 	 * Open the current file for reading.  Do this first, so that
 	 * we don't exec a shell before the most likely failure point.
 	 * If it doesn't exist, it's okay, there's just nothing to back
 	 * up.
 	 */
 	errno = 0;
 	if ((rfd = open(name, O_RDONLY, 0)) < 0) {
 		if (errno == ENOENT)
 			return (0);
 		estr = name;
 		goto err;
 	}
 
 	/*
 	 * If the name starts with an 'N' character, add a version number
 	 * to the name.  Strip the leading N from the string passed to the
 	 * expansion routines, for no particular reason.  It would be nice
 	 * to permit users to put the version number anywhere in the backup
 	 * name, but there isn't a special character that we can use in the
 	 * name, and giving a new character a special meaning leads to ugly
 	 * hacks both here and in the supporting ex routines.
 	 *
 	 * Shell and file name expand the option's value.
 	 */
 	ex_cinit(sp, &cmd, 0, 0, 0, 0, 0);
 	if (bname[0] == 'N') {
 		version = 1;
 		++bname;
 	} else
 		version = 0;
 	CHAR2INT(sp, bname, strlen(bname), wp, wlen);
 	if ((wp = v_wstrdup(sp, wp, wlen)) == NULL)
 		return (1);
 	if (argv_exp2(sp, &cmd, wp, wlen)) {
 		free(wp);
 		return (1);
 	}
 	free(wp);
 
 	/*
 	 *  0 args: impossible.
 	 *  1 args: use it.
 	 * >1 args: object, too many args.
 	 */
 	if (cmd.argc != 1) {
 		msgq_str(sp, M_ERR, bname,
 		    "258|%s expanded into too many file names");
 		(void)close(rfd);
 		return (1);
 	}
 
 	/*
 	 * If appending a version number, read through the directory, looking
 	 * for file names that match the name followed by a number.  Make all
 	 * of the other % characters in name literal, so the user doesn't get
 	 * surprised and sscanf doesn't drop core indirecting through pointers
 	 * that don't exist.  If any such files are found, increment its number
 	 * by one.
 	 */
 	if (version) {
 		GET_SPACE_GOTOC(sp, bp, blen, cmd.argv[0]->len * 2 + 50);
 		INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
 			 p, nlen); 
 		d = strdup(p);
 		p = d;
 		for (t = bp, slash = NULL;
 		     p[0] != '\0'; *t++ = *p++)
 			if (p[0] == '%') {
 				if (p[1] != '%')
 					*t++ = '%';
 			} else if (p[0] == '/')
 				slash = t;
 		pct = t;
 		*t++ = '%';
 		*t++ = 'd';
 		*t = '\0';
 
 		if (slash == NULL) {
 			dirp = opendir(".");
 			p = bp;
 		} else {
 			*slash = '\0';
 			dirp = opendir(bp);
 			*slash = '/';
 			p = slash + 1;
 		}
 		if (dirp == NULL) {
 			INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
 				estr, nlen);
 			goto err;
 		}
 
 		for (maxnum = 0; (dp = readdir(dirp)) != NULL;)
 			if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum)
 				maxnum = num;
 		(void)closedir(dirp);
 
 		/* Format the backup file name. */
 		(void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1);
 		wfname = bp;
 	} else {
 		bp = NULL;
 		INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
 			wfname, nlen);
 	}
 	
 	/* Open the backup file, avoiding lurkers. */
 	if (stat(wfname, &sb) == 0) {
 		if (!S_ISREG(sb.st_mode)) {
 			msgq_str(sp, M_ERR, bname,
 			    "259|%s: not a regular file");
 			goto err;
 		}
 		if (sb.st_uid != getuid()) {
 			msgq_str(sp, M_ERR, bname, "260|%s: not owned by you");
 			goto err;
 		}
 		if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
 			msgq_str(sp, M_ERR, bname,
 			   "261|%s: accessible by a user other than the owner");
 			goto err;
 		}
 		flags = O_TRUNC;
 	} else
 		flags = O_CREAT | O_EXCL;
 	if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
 		estr = bname;
 		goto err;
 	}
 
 	/* Copy the file's current contents to its backup value. */
 	while ((nr = read(rfd, buf, sizeof(buf))) > 0)
 		for (off = 0; nr != 0; nr -= nw, off += nw)
 			if ((nw = write(wfd, buf + off, nr)) < 0) {
 				estr = wfname;
 				goto err;
 			}
 	if (nr < 0) {
 		estr = name;
 		goto err;
 	}
 
 	if (close(rfd)) {
 		estr = name;
 		goto err;
 	}
 	if (close(wfd)) {
 		estr = wfname;
 		goto err;
 	}
 	free(d);
 	if (bp != NULL)
 		FREE_SPACE(sp, bp, blen);
 	return (0);
 
 alloc_err:
 err:	if (rfd != -1)
 		(void)close(rfd);
 	if (wfd != -1) {
 		(void)unlink(wfname);
 		(void)close(wfd);
 	}
 	if (estr)
 		msgq_str(sp, M_SYSERR, estr, "%s");
 	free(d);
 	if (bp != NULL)
 		FREE_SPACE(sp, bp, blen);
 	return (1);
 }
 
 /*
  * file_encinit --
  *	Read the first line and set the O_FILEENCODING.
  */
 static void
 file_encinit(SCR *sp)
 {
 #if defined(USE_WIDECHAR) && defined(USE_ICONV)
 	size_t len;
 	char *p;
 	size_t blen = 0;
 	char buf[4096];	/* not need to be '\0'-terminated */
 	recno_t ln = 1;
 	EXF *ep;
 
 	ep = sp->ep;
 
 	while (!db_rget(sp, ln++, &p, &len)) {
 		if (blen + len > sizeof(buf))
 			len = sizeof(buf) - blen;
 		memcpy(buf + blen, p, len);
 		blen += len;
 		if (blen == sizeof(buf))
 			break;
 		else
 			buf[blen++] = '\n';
 	}
 
 	/*
 	 * Detect UTF-8 and fallback to the locale/preset encoding.
 	 *
 	 * XXX
 	 * A manually set O_FILEENCODING indicates the "fallback
 	 * encoding", but UTF-8, which can be safely detected, is not
 	 * inherited from the old screen.
 	 */
 	if (looks_utf8(buf, blen) > 1)
 		o_set(sp, O_FILEENCODING, OS_STRDUP, "utf-8", 0);
 	else if (!O_ISSET(sp, O_FILEENCODING) ||
 	    !strcasecmp(O_STR(sp, O_FILEENCODING), "utf-8"))
 		o_set(sp, O_FILEENCODING, OS_STRDUP, codeset(), 0);
 
 	conv_enc(sp, O_FILEENCODING, 0);
 #endif
 }
 
 /*
  * file_comment --
  *	Skip the first comment.
  */
 static void
 file_comment(SCR *sp)
 {
 	recno_t lno;
 	size_t len;
 	CHAR_T *p;
 
 	for (lno = 1; !db_get(sp, lno, 0, &p, &len) && len == 0; ++lno);
 	if (p == NULL)
 		return;
 	if (p[0] == '#') {
 		F_SET(sp, SC_SCR_TOP);
 		while (!db_get(sp, ++lno, 0, &p, &len))
 			if (len < 1 || p[0] != '#') {
 				sp->lno = lno;
 				return;
 			}
 	} else if (len > 1 && p[0] == '/' && p[1] == '*') {
 		F_SET(sp, SC_SCR_TOP);
 		do {
 			for (; len > 1; --len, ++p)
 				if (p[0] == '*' && p[1] == '/') {
 					sp->lno = lno;
 					return;
 				}
 		} while (!db_get(sp, ++lno, 0, &p, &len));
 	} else if (len > 1 && p[0] == '/' && p[1] == '/') {
 		F_SET(sp, SC_SCR_TOP);
 		p += 2;
 		len -= 2;
 		do {
 			for (; len > 1; --len, ++p)
 				if (p[0] == '/' && p[1] == '/') {
 					sp->lno = lno;
 					return;
 				}
 		} while (!db_get(sp, ++lno, 0, &p, &len));
 	}
 }
 
 /*
  * file_m1 --
  * 	First modification check routine.  The :next, :prev, :rewind, :tag,
  *	:tagpush, :tagpop, ^^ modifications check.
  *
  * PUBLIC: int file_m1(SCR *, int, int);
  */
 int
 file_m1(SCR *sp, int force, int flags)
 {
 	EXF *ep;
 
 	ep = sp->ep;
 
 	/* If no file loaded, return no modifications. */
 	if (ep == NULL)
 		return (0);
 
 	/*
 	 * If the file has been modified, we'll want to write it back or
 	 * fail.  If autowrite is set, we'll write it back automatically,
 	 * unless force is also set.  Otherwise, we fail unless forced or
 	 * there's another open screen on this file.
 	 */
-	if (F_ISSET(ep, F_MODIFIED))
+	if (F_ISSET(ep, F_MODIFIED)) {
 		if (O_ISSET(sp, O_AUTOWRITE)) {
 			if (!force && file_aw(sp, flags))
 				return (1);
 		} else if (ep->refcnt <= 1 && !force) {
 			msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
 "262|File modified since last complete write; write or use ! to override" :
 "263|File modified since last complete write; write or use :edit! to override");
 			return (1);
 		}
+	}
 
 	return (file_m3(sp, force));
 }
 
 /*
  * file_m2 --
  * 	Second modification check routine.  The :edit, :quit, :recover
  *	modifications check.
  *
  * PUBLIC: int file_m2(SCR *, int);
  */
 int
 file_m2(SCR *sp, int force)
 {
 	EXF *ep;
 
 	ep = sp->ep;
 
 	/* If no file loaded, return no modifications. */
 	if (ep == NULL)
 		return (0);
 
 	/*
 	 * If the file has been modified, we'll want to fail, unless forced
 	 * or there's another open screen on this file.
 	 */
 	if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
 		msgq(sp, M_ERR,
 "264|File modified since last complete write; write or use ! to override");
 		return (1);
 	}
 
 	return (file_m3(sp, force));
 }
 
 /*
  * file_m3 --
  * 	Third modification check routine.
  *
  * PUBLIC: int file_m3(SCR *, int);
  */
 int
 file_m3(SCR *sp, int force)
 {
 	EXF *ep;
 
 	ep = sp->ep;
 
 	/* If no file loaded, return no modifications. */
 	if (ep == NULL)
 		return (0);
 
 	/*
 	 * Don't exit while in a temporary files if the file was ever modified.
 	 * The problem is that if the user does a ":wq", we write and quit,
 	 * unlinking the temporary file.  Not what the user had in mind at all.
 	 * We permit writing to temporary files, so that user maps using file
 	 * system names work with temporary files.
 	 */
 	if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) {
 		msgq(sp, M_ERR,
 		    "265|File is a temporary; exit will discard modifications");
 		return (1);
 	}
 	return (0);
 }
 
 /*
  * file_aw --
  *	Autowrite routine.  If modified, autowrite is set and the readonly bit
  *	is not set, write the file.  A routine so there's a place to put the
  *	comment.
  *
  * PUBLIC: int file_aw(SCR *, int);
  */
 int
 file_aw(SCR *sp, int flags)
 {
 	if (!F_ISSET(sp->ep, F_MODIFIED))
 		return (0);
 	if (!O_ISSET(sp, O_AUTOWRITE))
 		return (0);
 
 	/*
 	 * !!!
 	 * Historic 4BSD vi attempted to write the file if autowrite was set,
 	 * regardless of the writeability of the file (as defined by the file
 	 * readonly flag).  System V changed this as some point, not attempting
 	 * autowrite if the file was readonly.  This feels like a bug fix to
 	 * me (e.g. the principle of least surprise is violated if readonly is
 	 * set and vi writes the file), so I'm compatible with System V.
 	 */
 	if (O_ISSET(sp, O_READONLY)) {
 		msgq(sp, M_INFO,
 		    "266|File readonly, modifications not auto-written");
 		return (1);
 	}
 	return (file_write(sp, NULL, NULL, NULL, flags));
 }
 
 /*
  * set_alt_name --
  *	Set the alternate pathname.
  *
  * Set the alternate pathname.  It's a routine because I wanted some place
  * to hang this comment.  The alternate pathname (normally referenced using
  * the special character '#' during file expansion and in the vi ^^ command)
  * is set by almost all ex commands that take file names as arguments.  The
  * rules go something like this:
  *
  *    1: If any ex command takes a file name as an argument (except for the
  *	 :next command), the alternate pathname is set to that file name.
  *	 This excludes the command ":e" and ":w !command" as no file name
  *       was specified.  Note, historically, the :source command did not set
  *	 the alternate pathname.  It does in nvi, for consistency.
  *
  *    2: However, if any ex command sets the current pathname, e.g. the
  *	 ":e file" or ":rew" commands succeed, then the alternate pathname
  *	 is set to the previous file's current pathname, if it had one.
  *	 This includes the ":file" command and excludes the ":e" command.
  *	 So, by rule #1 and rule #2, if ":edit foo" fails, the alternate
  *	 pathname will be "foo", if it succeeds, the alternate pathname will
  *	 be the previous current pathname.  The ":e" command will not set
  *       the alternate or current pathnames regardless.
  *
  *    3: However, if it's a read or write command with a file argument and
  *	 the current pathname has not yet been set, the file name becomes
  *	 the current pathname, and the alternate pathname is unchanged.
  *
  * If the user edits a temporary file, there may be times when there is no
  * alternative file name.  A name argument of NULL turns it off.
  *
  * PUBLIC: void set_alt_name(SCR *, char *);
  */
 void
 set_alt_name(SCR *sp, char *name)
 {
 	free(sp->alt_name);
 	if (name == NULL)
 		sp->alt_name = NULL;
 	else if ((sp->alt_name = strdup(name)) == NULL)
 		msgq(sp, M_SYSERR, NULL);
 }
 
 /*
  * file_lock --
  *	Get an exclusive lock on a file.
  *
  * PUBLIC: lockr_t file_lock(SCR *, char *, int, int);
  */
 lockr_t
 file_lock(SCR *sp, char *name, int fd, int iswrite)
 {
 	if (!O_ISSET(sp, O_LOCKFILES))
 		return (LOCK_SUCCESS);
 	
 	/*
 	 * !!!
 	 * We need to distinguish a lock not being available for the file
 	 * from the file system not supporting locking.  Flock is documented
 	 * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume
 	 * they are the former.  There's no portable way to do this.
 	 */
 	errno = 0;
 	if (!flock(fd, LOCK_EX | LOCK_NB)) {
 		fcntl(fd, F_SETFD, 1);
 		return (LOCK_SUCCESS);
 	}
 	return (errno == EAGAIN
 #ifdef EWOULDBLOCK
 	    || errno == EWOULDBLOCK
 #endif
 	    ? LOCK_UNAVAIL : LOCK_FAILED);
 }
Index: head/contrib/nvi/common/key.c
===================================================================
--- head/contrib/nvi/common/key.c	(revision 366308)
+++ head/contrib/nvi/common/key.c	(revision 366309)
@@ -1,839 +1,839 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
 
 #include "common.h"
 #include "../vi/vi.h"
 
 static int	v_event_append(SCR *, EVENT *);
 static int	v_event_grow(SCR *, int);
 static int	v_key_cmp(const void *, const void *);
 static void	v_keyval(SCR *, int, scr_keyval_t);
 static void	v_sync(SCR *, int);
 
 /*
  * !!!
  * Historic vi always used:
  *
  *	^D: autoindent deletion
  *	^H: last character deletion
  *	^W: last word deletion
  *	^Q: quote the next character (if not used in flow control).
  *	^V: quote the next character
  *
  * regardless of the user's choices for these characters.  The user's erase
  * and kill characters worked in addition to these characters.  Nvi wires
  * down the above characters, but in addition permits the VEOF, VERASE, VKILL
  * and VWERASE characters described by the user's termios structure.
  *
  * Ex was not consistent with this scheme, as it historically ran in tty
  * cooked mode.  This meant that the scroll command and autoindent erase
  * characters were mapped to the user's EOF character, and the character
  * and word deletion characters were the user's tty character and word
  * deletion characters.  This implementation makes it all consistent, as
  * described above for vi.
  *
  * !!!
  * This means that all screens share a special key set.
  */
 KEYLIST keylist[] = {
 	{K_BACKSLASH,	  '\\'},	/*  \ */
 	{K_CARAT,	   '^'},	/*  ^ */
 	{K_CNTRLD,	'\004'},	/* ^D */
 	{K_CNTRLR,	'\022'},	/* ^R */
 	{K_CNTRLT,	'\024'},	/* ^T */
 	{K_CNTRLZ,	'\032'},	/* ^Z */
 	{K_COLON,	   ':'},	/*  : */
 	{K_CR,		  '\r'},	/* \r */
 	{K_ESCAPE,	'\033'},	/* ^[ */
 	{K_FORMFEED,	  '\f'},	/* \f */
 	{K_HEXCHAR,	'\030'},	/* ^X */
 	{K_NL,		  '\n'},	/* \n */
 	{K_RIGHTBRACE,	   '}'},	/*  } */
 	{K_RIGHTPAREN,	   ')'},	/*  ) */
 	{K_TAB,		  '\t'},	/* \t */
 	{K_VERASE,	  '\b'},	/* \b */
 	{K_VKILL,	'\025'},	/* ^U */
 	{K_VLNEXT,	'\021'},	/* ^Q */
 	{K_VLNEXT,	'\026'},	/* ^V */
 	{K_VWERASE,	'\027'},	/* ^W */
 	{K_ZERO,	   '0'},	/*  0 */
 
 #define	ADDITIONAL_CHARACTERS	4
 	{K_NOTUSED, 0},			/* VEOF, VERASE, VKILL, VWERASE */
 	{K_NOTUSED, 0},
 	{K_NOTUSED, 0},
 	{K_NOTUSED, 0},
 };
 static int nkeylist =
     (sizeof(keylist) / sizeof(keylist[0])) - ADDITIONAL_CHARACTERS;
 
 /*
  * v_key_init --
  *	Initialize the special key lookup table.
  *
  * PUBLIC: int v_key_init(SCR *);
  */
 int
 v_key_init(SCR *sp)
 {
 	int ch;
 	GS *gp;
 	KEYLIST *kp;
 	int cnt;
 
 	gp = sp->gp;
 
 	v_key_ilookup(sp);
 
 	v_keyval(sp, K_CNTRLD, KEY_VEOF);
 	v_keyval(sp, K_VERASE, KEY_VERASE);
 	v_keyval(sp, K_VKILL, KEY_VKILL);
 	v_keyval(sp, K_VWERASE, KEY_VWERASE);
 
 	/* Sort the special key list. */
 	qsort(keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
 
 	/* Initialize the fast lookup table. */
 	for (kp = keylist, cnt = nkeylist; cnt--; ++kp)
 		gp->special_key[kp->ch] = kp->value;
 
 	/* Find a non-printable character to use as a message separator. */
 	for (ch = 1; ch <= UCHAR_MAX; ++ch)
 		if (!isprint(ch)) {
 			gp->noprint = ch;
 			break;
 		}
 	if (ch != gp->noprint) {
 		msgq(sp, M_ERR, "079|No non-printable character found");
 		return (1);
 	}
 	return (0);
 }
 
 /*
  * v_keyval --
  *	Set key values.
  *
  * We've left some open slots in the keylist table, and if these values exist,
  * we put them into place.  Note, they may reset (or duplicate) values already
  * in the table, so we check for that first.
  */
 static void
 v_keyval(SCR *sp, int val, scr_keyval_t name)
 {
 	KEYLIST *kp;
 	CHAR_T ch;
 	int dne;
 
 	/* Get the key's value from the screen. */
 	if (sp->gp->scr_keyval(sp, name, &ch, &dne))
 		return;
 	if (dne)
 		return;
 
 	/* Check for duplication. */
 	for (kp = keylist; kp->value != K_NOTUSED; ++kp)
 		if (kp->ch == ch) {
 			kp->value = val;
 			return;
 		}
 
 	/* Add a new entry. */
 	if (kp->value == K_NOTUSED) {
 		keylist[nkeylist].ch = ch;
 		keylist[nkeylist].value = val;
 		++nkeylist;
 	}
 }
 
 /*
  * v_key_ilookup --
  *	Build the fast-lookup key display array.
  *
  * PUBLIC: void v_key_ilookup(SCR *);
  */
 void
 v_key_ilookup(SCR *sp)
 {
 	UCHAR_T ch;
 	char *p, *t;
 	GS *gp;
 	size_t len;
 
 	for (gp = sp->gp, ch = 0;; ++ch) {
 		for (p = gp->cname[ch].name, t = v_key_name(sp, ch),
 		    len = gp->cname[ch].len = sp->clen; len--;)
 			*p++ = *t++;
 		if (ch == MAX_FAST_KEY)
 			break;
 	}
 }
 
 /*
  * v_key_len --
  *	Return the length of the string that will display the key.
  *	This routine is the backup for the KEY_LEN() macro.
  *
  * PUBLIC: size_t v_key_len(SCR *, ARG_CHAR_T);
  */
 size_t
 v_key_len(SCR *sp, ARG_CHAR_T ch)
 {
 	(void)v_key_name(sp, ch);
 	return (sp->clen);
 }
 
 /*
  * v_key_name --
  *	Return the string that will display the key.  This routine
  *	is the backup for the KEY_NAME() macro.
  *
  * PUBLIC: char *v_key_name(SCR *, ARG_CHAR_T);
  */
 char *
 v_key_name(SCR *sp, ARG_CHAR_T ach)
 {
 	static const char hexdigit[] = "0123456789abcdef";
 	static const char octdigit[] = "01234567";
 	int ch;
 	size_t len;
 	char *chp;
 
 	/*
 	 * Cache the last checked character.  It won't be a problem
 	 * since nvi will rescan the mapping when settings changed.
 	 */
 	if (ach && sp->lastc == ach)
 		return (sp->cname);
 	sp->lastc = ach;
 
 #ifdef USE_WIDECHAR
 	len = wctomb(sp->cname, ach);
 	if (len > MB_CUR_MAX)
 #endif
 		sp->cname[(len = 1)-1] = (u_char)ach;
 
 	ch = (u_char)sp->cname[0];
 	sp->cname[len] = '\0';
 
 	/* See if the character was explicitly declared printable or not. */
 	if ((chp = O_STR(sp, O_PRINT)) != NULL)
 		if (strstr(chp, sp->cname) != NULL)
 			goto done;
 	if ((chp = O_STR(sp, O_NOPRINT)) != NULL)
 		if (strstr(chp, sp->cname) != NULL)
 			goto nopr;
 
 	/*
 	 * Historical (ARPA standard) mappings.  Printable characters are left
 	 * alone.  Control characters less than 0x20 are represented as '^'
 	 * followed by the character offset from the '@' character in the ASCII
 	 * character set.  Del (0x7f) is represented as '^' followed by '?'.
 	 *
 	 * XXX
 	 * The following code depends on the current locale being identical to
 	 * the ASCII map from 0x40 to 0x5f (since 0x1f + 0x40 == 0x5f).  I'm
 	 * told that this is a reasonable assumption...
 	 *
 	 * XXX
 	 * The code prints non-printable wide characters in 4 or 5 digits
 	 * Unicode escape sequences, so only supports plane 0 to 15.
 	 */
 	if (CAN_PRINT(sp, ach))
 		goto done;
 nopr:	if (iscntrl(ch) && (ch < 0x20 || ch == 0x7f)) {
 		sp->cname[0] = '^';
 		sp->cname[1] = ch == 0x7f ? '?' : '@' + ch;
 		len = 2;
 		goto done;
 	}
 #ifdef USE_WIDECHAR
 	if (INTISWIDE(ach)) {
 		int uc = -1;
 
 		if (!strcmp(codeset(), "UTF-8"))
 			uc = decode_utf8(sp->cname);
 #ifdef USE_ICONV
 		else {
 			char buf[sizeof(sp->cname)] = "";
 			size_t left = sizeof(sp->cname);
 			char *in = sp->cname;
 			char *out = buf;
 			iconv(sp->conv.id[IC_IE_TO_UTF16],
 			    (iconv_src_t)&in, &len, &out, &left);
 			iconv(sp->conv.id[IC_IE_TO_UTF16],
 			    NULL, NULL, NULL, NULL);
 			uc = decode_utf16(buf, 1);
 		}
 #endif
 		if (uc >= 0) {
 			len = snprintf(sp->cname, sizeof(sp->cname),
 			    uc < 0x10000 ? "\\u%04x" : "\\U%05X", uc);
 			goto done;
 		}
 	}
 #endif
 	if (O_ISSET(sp, O_OCTAL)) {
 		sp->cname[0] = '\\';
 		sp->cname[1] = octdigit[(ch & 0300) >> 6];
 		sp->cname[2] = octdigit[(ch &  070) >> 3];
 		sp->cname[3] = octdigit[ ch &   07      ];
 	} else {
 		sp->cname[0] = '\\';
 		sp->cname[1] = 'x';
 		sp->cname[2] = hexdigit[(ch & 0xf0) >> 4];
 		sp->cname[3] = hexdigit[ ch & 0x0f      ];
 	}
 	len = 4;
 done:	sp->cname[sp->clen = len] = '\0';
 	return (sp->cname);
 }
 
 /*
  * v_key_val --
  *	Fill in the value for a key.  This routine is the backup
  *	for the KEY_VAL() macro.
  *
  * PUBLIC: e_key_t v_key_val(SCR *, ARG_CHAR_T);
  */
 e_key_t
 v_key_val(SCR *sp, ARG_CHAR_T ch)
 {
 	KEYLIST k, *kp;
 
 	k.ch = ch;
 	kp = bsearch(&k, keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
 	return (kp == NULL ? K_NOTUSED : kp->value);
 }
 
 /*
  * v_event_push --
  *	Push events/keys onto the front of the buffer.
  *
  * There is a single input buffer in ex/vi.  Characters are put onto the
  * end of the buffer by the terminal input routines, and pushed onto the
  * front of the buffer by various other functions in ex/vi.  Each key has
  * an associated flag value, which indicates if it has already been quoted,
  * and if it is the result of a mapping or an abbreviation.
  *
  * PUBLIC: int v_event_push(SCR *, EVENT *, CHAR_T *, size_t, u_int);
  */
 int
 v_event_push(SCR *sp,
 	EVENT *p_evp,			/* Push event. */
 	CHAR_T *p_s,			/* Push characters. */
 	size_t nitems,			/* Number of items to push. */
 	u_int flags)			/* CH_* flags. */
 {
 	EVENT *evp;
 	GS *gp;
 	size_t total;
 
 	/* If we have room, stuff the items into the buffer. */
 	gp = sp->gp;
 	if (nitems <= gp->i_next ||
 	    (gp->i_event != NULL && gp->i_cnt == 0 && nitems <= gp->i_nelem)) {
 		if (gp->i_cnt != 0)
 			gp->i_next -= nitems;
 		goto copy;
 	}
 
 	/*
 	 * If there are currently items in the queue, shift them up,
 	 * leaving some extra room.  Get enough space plus a little
 	 * extra.
 	 */
 #define	TERM_PUSH_SHIFT	30
 	total = gp->i_cnt + gp->i_next + nitems + TERM_PUSH_SHIFT;
 	if (total >= gp->i_nelem && v_event_grow(sp, MAX(total, 64)))
 		return (1);
 	if (gp->i_cnt)
 		memmove(gp->i_event + TERM_PUSH_SHIFT + nitems,
 		    gp->i_event + gp->i_next, gp->i_cnt * sizeof(EVENT));
 	gp->i_next = TERM_PUSH_SHIFT;
 
 	/* Put the new items into the queue. */
 copy:	gp->i_cnt += nitems;
 	for (evp = gp->i_event + gp->i_next; nitems--; ++evp) {
 		if (p_evp != NULL)
 			*evp = *p_evp++;
 		else {
 			evp->e_event = E_CHARACTER;
 			evp->e_c = *p_s++;
 			evp->e_value = KEY_VAL(sp, evp->e_c);
 			F_INIT(&evp->e_ch, flags);
 		}
 	}
 	return (0);
 }
 
 /*
  * v_event_append --
  *	Append events onto the tail of the buffer.
  */
 static int
 v_event_append(SCR *sp, EVENT *argp)
 {
 	CHAR_T *s;			/* Characters. */
 	EVENT *evp;
 	GS *gp;
 	size_t nevents;			/* Number of events. */
 
 	/* Grow the buffer as necessary. */
 	nevents = argp->e_event == E_STRING ? argp->e_len : 1;
 	gp = sp->gp;
 	if (gp->i_event == NULL ||
 	    nevents > gp->i_nelem - (gp->i_next + gp->i_cnt))
 		v_event_grow(sp, MAX(nevents, 64));
 	evp = gp->i_event + gp->i_next + gp->i_cnt;
 	gp->i_cnt += nevents;
 
 	/* Transform strings of characters into single events. */
 	if (argp->e_event == E_STRING)
 		for (s = argp->e_csp; nevents--; ++evp) {
 			evp->e_event = E_CHARACTER;
 			evp->e_c = *s++;
 			evp->e_value = KEY_VAL(sp, evp->e_c);
 			evp->e_flags = 0;
 		}
 	else
 		*evp = *argp;
 	return (0);
 }
 
 /* Remove events from the queue. */
-#define	QREM(len) {							\
+#define	QREM(len) do {							\
 	if ((gp->i_cnt -= len) == 0)					\
 		gp->i_next = 0;						\
 	else								\
 		gp->i_next += len;					\
-}
+} while (0)
 
 /*
  * v_event_get --
  *	Return the next event.
  *
  * !!!
  * The flag EC_NODIGIT probably needs some explanation.  First, the idea of
  * mapping keys is that one or more keystrokes act like a function key.
  * What's going on is that vi is reading a number, and the character following
  * the number may or may not be mapped (EC_MAPCOMMAND).  For example, if the
  * user is entering the z command, a valid command is "z40+", and we don't want
  * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it
  * into "z40xxx".  However, if the user enters "35x", we want to put all of the
  * characters through the mapping code.
  *
  * Historical practice is a bit muddled here.  (Surprise!)  It always permitted
  * mapping digits as long as they weren't the first character of the map, e.g.
  * ":map ^A1 xxx" was okay.  It also permitted the mapping of the digits 1-9
  * (the digit 0 was a special case as it doesn't indicate the start of a count)
  * as the first character of the map, but then ignored those mappings.  While
  * it's probably stupid to map digits, vi isn't your mother.
  *
  * The way this works is that the EC_MAPNODIGIT causes term_key to return the
  * end-of-digit without "looking" at the next character, i.e. leaving it as the
  * user entered it.  Presumably, the next term_key call will tell us how the
  * user wants it handled.
  *
  * There is one more complication.  Users might map keys to digits, and, as
  * it's described above, the commands:
  *
  *	:map g 1G
  *	d2g
  *
  * would return the keys "d2<end-of-digits>1G", when the user probably wanted
  * "d21<end-of-digits>G".  So, if a map starts off with a digit we continue as
  * before, otherwise, we pretend we haven't mapped the character, and return
  * <end-of-digits>.
  *
  * Now that that's out of the way, let's talk about Energizer Bunny macros.
  * It's easy to create macros that expand to a loop, e.g. map x 3x.  It's
  * fairly easy to detect this example, because it's all internal to term_key.
  * If we're expanding a macro and it gets big enough, at some point we can
  * assume it's looping and kill it.  The examples that are tough are the ones
  * where the parser is involved, e.g. map x "ayyx"byy.  We do an expansion
  * on 'x', and get "ayyx"byy.  We then return the first 4 characters, and then
  * find the looping macro again.  There is no way that we can detect this
  * without doing a full parse of the command, because the character that might
  * cause the loop (in this case 'x') may be a literal character, e.g. the map
  * map x "ayy"xyy"byy is perfectly legal and won't cause a loop.
  *
  * Historic vi tried to detect looping macros by disallowing obvious cases in
  * the map command, maps that that ended with the same letter as they started
  * (which wrongly disallowed "map x 'x"), and detecting macros that expanded
  * too many times before keys were returned to the command parser.  It didn't
  * get many (most?) of the tricky cases right, however, and it was certainly
  * possible to create macros that ran forever.  And, even if it did figure out
  * what was going on, the user was usually tossed into ex mode.  Finally, any
  * changes made before vi realized that the macro was recursing were left in
  * place.  We recover gracefully, but the only recourse the user has in an
  * infinite macro loop is to interrupt.
  *
  * !!!
  * It is historic practice that mapping characters to themselves as the first
  * part of the mapped string was legal, and did not cause infinite loops, i.e.
  * ":map! { {^M^T" and ":map n nz." were known to work.  The initial, matching
  * characters were returned instead of being remapped.
  *
  * !!!
  * It is also historic practice that the macro "map ] ]]^" caused a single ]
  * keypress to behave as the command ]] (the ^ got the map past the vi check
  * for "tail recursion").  Conversely, the mapping "map n nn^" went recursive.
  * What happened was that, in the historic vi, maps were expanded as the keys
  * were retrieved, but not all at once and not centrally.  So, the keypress ]
  * pushed ]]^ on the stack, and then the first ] from the stack was passed to
  * the ]] command code.  The ]] command then retrieved a key without entering
  * the mapping code.  This could bite us anytime a user has a map that depends
  * on secondary keys NOT being mapped.  I can't see any possible way to make
  * this work in here without the complete abandonment of Rationality Itself.
  *
  * XXX
  * The final issue is recovery.  It would be possible to undo all of the work
  * that was done by the macro if we entered a record into the log so that we
  * knew when the macro started, and, in fact, this might be worth doing at some
  * point.  Given that this might make the log grow unacceptably (consider that
  * cursor keys are done with maps), for now we leave any changes made in place.
  *
  * PUBLIC: int v_event_get(SCR *, EVENT *, int, u_int32_t);
  */
 int
 v_event_get(SCR *sp, EVENT *argp, int timeout, u_int32_t flags)
 {
 	EVENT *evp, ev;
 	GS *gp;
 	SEQ *qp;
 	int init_nomap, ispartial, istimeout, remap_cnt;
 
 	gp = sp->gp;
 
 	/* If simply checking for interrupts, argp may be NULL. */
 	if (argp == NULL)
 		argp = &ev;
 
 retry:	istimeout = remap_cnt = 0;
 
 	/*
 	 * If the queue isn't empty and we're timing out for characters,
 	 * return immediately.
 	 */
 	if (gp->i_cnt != 0 && LF_ISSET(EC_TIMEOUT))
 		return (0);
 
 	/*
 	 * If the queue is empty, we're checking for interrupts, or we're
 	 * timing out for characters, get more events.
 	 */
 	if (gp->i_cnt == 0 || LF_ISSET(EC_INTERRUPT | EC_TIMEOUT)) {
 		/*
 		 * If we're reading new characters, check any scripting
 		 * windows for input.
 		 */
 		if (F_ISSET(gp, G_SCRWIN) && sscr_input(sp))
 			return (1);
 loop:		if (gp->scr_event(sp, argp,
 		    LF_ISSET(EC_INTERRUPT | EC_QUOTED | EC_RAW), timeout))
 			return (1);
 		switch (argp->e_event) {
 		case E_ERR:
 		case E_SIGHUP:
 		case E_SIGTERM:
 			/*
 			 * Fatal conditions cause the file to be synced to
 			 * disk immediately.
 			 */
 			v_sync(sp, RCV_ENDSESSION | RCV_PRESERVE |
 			    (argp->e_event == E_SIGTERM ? 0: RCV_EMAIL));
 			return (1);
 		case E_TIMEOUT:
 			istimeout = 1;
 			break;
 		case E_INTERRUPT:
 			/* Set the global interrupt flag. */
 			F_SET(sp->gp, G_INTERRUPTED);
 
 			/*
 			 * If the caller was interested in interrupts, return
 			 * immediately.
 			 */
 			if (LF_ISSET(EC_INTERRUPT))
 				return (0);
 			goto append;
 		default:
 append:			if (v_event_append(sp, argp))
 				return (1);
 			break;
 		}
 	}
 
 	/*
 	 * If the caller was only interested in interrupts or timeouts, return
 	 * immediately.  (We may have gotten characters, and that's okay, they
 	 * were queued up for later use.)
 	 */
 	if (LF_ISSET(EC_INTERRUPT | EC_TIMEOUT))
 		return (0);
 	 
 newmap:	evp = &gp->i_event[gp->i_next];
 
 	/* 
 	 * If the next event in the queue isn't a character event, return
 	 * it, we're done.
 	 */
 	if (evp->e_event != E_CHARACTER) {
 		*argp = *evp;
 		QREM(1);
 		return (0);
 	}
 	
 	/*
 	 * If the key isn't mappable because:
 	 *
 	 *	+ ... the timeout has expired
 	 *	+ ... it's not a mappable key
 	 *	+ ... neither the command or input map flags are set
 	 *	+ ... there are no maps that can apply to it
 	 *
 	 * return it forthwith.
 	 */
 	if (istimeout || F_ISSET(&evp->e_ch, CH_NOMAP) ||
 	    !LF_ISSET(EC_MAPCOMMAND | EC_MAPINPUT) ||
 	    ((evp->e_c & ~MAX_BIT_SEQ) == 0 &&
 	    !bit_test(gp->seqb, evp->e_c)))
 		goto nomap;
 
 	/* Search the map. */
 	qp = seq_find(sp, NULL, evp, NULL, gp->i_cnt,
 	    LF_ISSET(EC_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, &ispartial);
 
 	/*
 	 * If get a partial match, get more characters and retry the map.
 	 * If time out without further characters, return the characters
 	 * unmapped.
 	 *
 	 * !!!
 	 * <escape> characters are a problem.  Cursor keys start with <escape>
 	 * characters, so there's almost always a map in place that begins with
 	 * an <escape> character.  If we timeout <escape> keys in the same way
 	 * that we timeout other keys, the user will get a noticeable pause as
 	 * they enter <escape> to terminate input mode.  If key timeout is set
 	 * for a slow link, users will get an even longer pause.  Nvi used to
 	 * simply timeout <escape> characters at 1/10th of a second, but this
 	 * loses over PPP links where the latency is greater than 100Ms.
 	 */
 	if (ispartial) {
 		if (O_ISSET(sp, O_TIMEOUT))
 			timeout = (evp->e_value == K_ESCAPE ?
 			    O_VAL(sp, O_ESCAPETIME) :
 			    O_VAL(sp, O_KEYTIME)) * 100;
 		else
 			timeout = 0;
 		goto loop;
 	}
 
 	/* If no map, return the character. */
 	if (qp == NULL) {
 nomap:		if (!ISDIGIT(evp->e_c) && LF_ISSET(EC_MAPNODIGIT))
 			goto not_digit;
 		*argp = *evp;
 		QREM(1);
 		return (0);
 	}
 
 	/*
 	 * If looking for the end of a digit string, and the first character
 	 * of the map is it, pretend we haven't seen the character.
 	 */
 	if (LF_ISSET(EC_MAPNODIGIT) &&
 	    qp->output != NULL && !ISDIGIT(qp->output[0])) {
 not_digit:	argp->e_c = CH_NOT_DIGIT;
 		argp->e_value = K_NOTUSED;
 		argp->e_event = E_CHARACTER;
 		F_INIT(&argp->e_ch, 0);
 		return (0);
 	}
 
 	/* Find out if the initial segments are identical. */
 	init_nomap = !e_memcmp(qp->output, &gp->i_event[gp->i_next], qp->ilen);
 
 	/* Delete the mapped characters from the queue. */
 	QREM(qp->ilen);
 
 	/* If keys mapped to nothing, go get more. */
 	if (qp->output == NULL)
 		goto retry;
 
 	/* If remapping characters... */
 	if (O_ISSET(sp, O_REMAP)) {
 		/*
 		 * Periodically check for interrupts.  Always check the first
 		 * time through, because it's possible to set up a map that
 		 * will return a character every time, but will expand to more,
 		 * e.g. "map! a aaaa" will always return a 'a', but we'll never
 		 * get anywhere useful.
 		 */
 		if ((++remap_cnt == 1 || remap_cnt % 10 == 0) &&
 		    (gp->scr_event(sp, &ev,
 		    EC_INTERRUPT, 0) || ev.e_event == E_INTERRUPT)) {
 			F_SET(sp->gp, G_INTERRUPTED);
 			argp->e_event = E_INTERRUPT;
 			return (0);
 		}
 
 		/*
 		 * If an initial part of the characters mapped, they are not
 		 * further remapped -- return the first one.  Push the rest
 		 * of the characters, or all of the characters if no initial
 		 * part mapped, back on the queue.
 		 */
 		if (init_nomap) {
 			if (v_event_push(sp, NULL, qp->output + qp->ilen,
 			    qp->olen - qp->ilen, CH_MAPPED))
 				return (1);
 			if (v_event_push(sp, NULL,
 			    qp->output, qp->ilen, CH_NOMAP | CH_MAPPED))
 				return (1);
 			evp = &gp->i_event[gp->i_next];
 			goto nomap;
 		}
 		if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED))
 			return (1);
 		goto newmap;
 	}
 
 	/* Else, push the characters on the queue and return one. */
 	if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED | CH_NOMAP))
 		return (1);
 
 	goto nomap;
 }
 
 /*
  * v_sync --
  *	Walk the screen lists, sync'ing files to their backup copies.
  */
 static void
 v_sync(SCR *sp, int flags)
 {
 	GS *gp;
 
 	gp = sp->gp;
 	TAILQ_FOREACH(sp, gp->dq, q)
 		rcv_sync(sp, flags);
 	TAILQ_FOREACH(sp, gp->hq, q)
 		rcv_sync(sp, flags);
 }
 
 /*
  * v_event_err --
  *	Unexpected event.
  *
  * PUBLIC: void v_event_err(SCR *, EVENT *);
  */
 void
 v_event_err(SCR *sp, EVENT *evp)
 {
 	switch (evp->e_event) {
 	case E_CHARACTER:
 		msgq(sp, M_ERR, "276|Unexpected character event");
 		break;
 	case E_EOF:
 		msgq(sp, M_ERR, "277|Unexpected end-of-file event");
 		break;
 	case E_INTERRUPT:
 		msgq(sp, M_ERR, "279|Unexpected interrupt event");
 		break;
 	case E_REPAINT:
 		msgq(sp, M_ERR, "281|Unexpected repaint event");
 		break;
 	case E_STRING:
 		msgq(sp, M_ERR, "285|Unexpected string event");
 		break;
 	case E_TIMEOUT:
 		msgq(sp, M_ERR, "286|Unexpected timeout event");
 		break;
 	case E_WRESIZE:
 		msgq(sp, M_ERR, "316|Unexpected resize event");
 		break;
 
 	/*
 	 * Theoretically, none of these can occur, as they're handled at the
 	 * top editor level.
 	 */
 	case E_ERR:
 	case E_SIGHUP:
 	case E_SIGTERM:
 	default:
 		abort();
 	}
 
 	/* Free any allocated memory. */
 	free(evp->e_asp);
 }
 
 /*
  * v_event_flush --
  *	Flush any flagged keys, returning if any keys were flushed.
  *
  * PUBLIC: int v_event_flush(SCR *, u_int);
  */
 int
 v_event_flush(SCR *sp, u_int flags)
 {
 	GS *gp;
 	int rval;
 
 	for (rval = 0, gp = sp->gp; gp->i_cnt != 0 &&
 	    F_ISSET(&gp->i_event[gp->i_next].e_ch, flags); rval = 1)
 		QREM(1);
 	return (rval);
 }
 
 /*
  * v_event_grow --
  *	Grow the terminal queue.
  */
 static int
 v_event_grow(SCR *sp, int add)
 {
 	GS *gp;
 	size_t new_nelem, olen;
 
 	gp = sp->gp;
 	new_nelem = gp->i_nelem + add;
 	olen = gp->i_nelem * sizeof(gp->i_event[0]);
 	BINC_RET(sp, EVENT, gp->i_event, olen, new_nelem * sizeof(gp->i_event[0]));
 	gp->i_nelem = olen / sizeof(gp->i_event[0]);
 	return (0);
 }
 
 /*
  * v_key_cmp --
  *	Compare two keys for sorting.
  */
 static int
 v_key_cmp(const void *ap, const void *bp)
 {
 	return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
 }
Index: head/contrib/nvi/common/log.c
===================================================================
--- head/contrib/nvi/common/log.c	(revision 366308)
+++ head/contrib/nvi/common/log.c	(revision 366309)
@@ -1,736 +1,736 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 
 /*
  * The log consists of records, each containing a type byte and a variable
  * length byte string, as follows:
  *
  *	LOG_CURSOR_INIT		MARK
  *	LOG_CURSOR_END		MARK
  *	LOG_LINE_APPEND 	recno_t		char *
  *	LOG_LINE_DELETE		recno_t		char *
  *	LOG_LINE_INSERT		recno_t		char *
  *	LOG_LINE_RESET_F	recno_t		char *
  *	LOG_LINE_RESET_B	recno_t		char *
  *	LOG_MARK		LMARK
  *
  * We do before image physical logging.  This means that the editor layer
  * MAY NOT modify records in place, even if simply deleting or overwriting
  * characters.  Since the smallest unit of logging is a line, we're using
  * up lots of space.  This may eventually have to be reduced, probably by
  * doing logical logging, which is a much cooler database phrase.
  *
  * The implementation of the historic vi 'u' command, using roll-forward and
  * roll-back, is simple.  Each set of changes has a LOG_CURSOR_INIT record,
  * followed by a number of other records, followed by a LOG_CURSOR_END record.
  * LOG_LINE_RESET records come in pairs.  The first is a LOG_LINE_RESET_B
  * record, and is the line before the change.  The second is LOG_LINE_RESET_F,
  * and is the line after the change.  Roll-back is done by backing up to the
  * first LOG_CURSOR_INIT record before a change.  Roll-forward is done in a
  * similar fashion.
  *
  * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
  * record for a line different from the current one.  It should be noted that
  * this means that a subsequent 'u' command will make a change based on the
  * new position of the log's cursor.  This is okay, and, in fact, historic vi
  * behaved that way.
  */
 
 static int	log_cursor1(SCR *, int);
 static void	log_err(SCR *, char *, int);
 #if defined(DEBUG) && 0
 static void	log_trace(SCR *, char *, recno_t, u_char *);
 #endif
 static int	apply_with(int (*)(SCR *, recno_t, CHAR_T *, size_t),
 					SCR *, recno_t, u_char *, size_t);
 
 /* Try and restart the log on failure, i.e. if we run out of memory. */
-#define	LOG_ERR {							\
+#define	LOG_ERR do {							\
 	log_err(sp, __FILE__, __LINE__);				\
 	return (1);							\
-}
+} while (0)
 
 /* offset of CHAR_T string in log needs to be aligned on some systems
  * because it is passed to db_set as a string
  */
 typedef struct {
 	char    data[sizeof(u_char) /* type */ + sizeof(recno_t)];
 	CHAR_T  str[1];
 } log_t;
 #define CHAR_T_OFFSET ((char *)(((log_t*)0)->str) - (char *)0)
 
 /*
  * log_init --
  *	Initialize the logging subsystem.
  *
  * PUBLIC: int log_init(SCR *, EXF *);
  */
 int
 log_init(SCR *sp, EXF *ep)
 {
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 *
 	 * Initialize the buffer.  The logging subsystem has its own
 	 * buffers because the global ones are almost by definition
 	 * going to be in use when the log runs.
 	 */
 	ep->l_lp = NULL;
 	ep->l_len = 0;
 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
 	ep->l_cursor.cno = 0;
 	ep->l_high = ep->l_cur = 1;
 
 	ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
 	    S_IRUSR | S_IWUSR, DB_RECNO, NULL);
 	if (ep->log == NULL) {
 		msgq(sp, M_SYSERR, "009|Log file");
 		F_SET(ep, F_NOLOG);
 		return (1);
 	}
 
 	return (0);
 }
 
 /*
  * log_end --
  *	Close the logging subsystem.
  *
  * PUBLIC: int log_end(SCR *, EXF *);
  */
 int
 log_end(SCR *sp, EXF *ep)
 {
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 */
 	if (ep->log != NULL) {
 		(void)(ep->log->close)(ep->log);
 		ep->log = NULL;
 	}
 	free(ep->l_lp);
 	ep->l_lp = NULL;
 	ep->l_len = 0;
 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
 	ep->l_cursor.cno = 0;
 	ep->l_high = ep->l_cur = 1;
 	return (0);
 }
 
 /*
  * log_cursor --
  *	Log the current cursor position, starting an event.
  *
  * PUBLIC: int log_cursor(SCR *);
  */
 int
 log_cursor(SCR *sp)
 {
 	EXF *ep;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG))
 		return (0);
 
 	/*
 	 * If any changes were made since the last cursor init,
 	 * put out the ending cursor record.
 	 */
 	if (ep->l_cursor.lno == OOBLNO) {
 		ep->l_cursor.lno = sp->lno;
 		ep->l_cursor.cno = sp->cno;
 		return (log_cursor1(sp, LOG_CURSOR_END));
 	}
 	ep->l_cursor.lno = sp->lno;
 	ep->l_cursor.cno = sp->cno;
 	return (0);
 }
 
 /*
  * log_cursor1 --
  *	Actually push a cursor record out.
  */
 static int
 log_cursor1(SCR *sp, int type)
 {
 	DBT data, key;
 	EXF *ep;
 
 	ep = sp->ep;
 
 	BINC_RETC(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
 	ep->l_lp[0] = type;
 	memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
 
 	key.data = &ep->l_cur;
 	key.size = sizeof(recno_t);
 	data.data = ep->l_lp;
 	data.size = sizeof(u_char) + sizeof(MARK);
 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
 		LOG_ERR;
 
 #if defined(DEBUG) && 0
 	TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
 	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
 	    sp->lno, sp->cno);
 #endif
 	/* Reset high water mark. */
 	ep->l_high = ++ep->l_cur;
 
 	return (0);
 }
 
 /*
  * log_line --
  *	Log a line change.
  *
  * PUBLIC: int log_line(SCR *, recno_t, u_int);
  */
 int
 log_line(SCR *sp, recno_t lno, u_int action)
 {
 	DBT data, key;
 	EXF *ep;
 	size_t len;
 	CHAR_T *lp;
 	recno_t lcur;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG))
 		return (0);
 
 	/*
 	 * XXX
 	 *
 	 * Kluge for vi.  Clear the EXF undo flag so that the
 	 * next 'u' command does a roll-back, regardless.
 	 */
 	F_CLR(ep, F_UNDO);
 
 	/* Put out one initial cursor record per set of changes. */
 	if (ep->l_cursor.lno != OOBLNO) {
 		if (log_cursor1(sp, LOG_CURSOR_INIT))
 			return (1);
 		ep->l_cursor.lno = OOBLNO;
 	}
 
 	/*
 	 * Put out the changes.  If it's a LOG_LINE_RESET_B call, it's a
 	 * special case, avoid the caches.  Also, if it fails and it's
 	 * line 1, it just means that the user started with an empty file,
 	 * so fake an empty length line.
 	 */
 	if (action == LOG_LINE_RESET_B) {
 		if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
 			if (lno != 1) {
 				db_err(sp, lno);
 				return (1);
 			}
 			len = 0;
 			lp = L("");
 		}
 	} else
 		if (db_get(sp, lno, DBG_FATAL, &lp, &len))
 			return (1);
 	BINC_RETC(sp,
 	    ep->l_lp, ep->l_len,
 	    len * sizeof(CHAR_T) + CHAR_T_OFFSET);
 	ep->l_lp[0] = action;
 	memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
 	memmove(ep->l_lp + CHAR_T_OFFSET, lp, len * sizeof(CHAR_T));
 
 	lcur = ep->l_cur;
 	key.data = &lcur;
 	key.size = sizeof(recno_t);
 	data.data = ep->l_lp;
 	data.size = len * sizeof(CHAR_T) + CHAR_T_OFFSET;
 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
 		LOG_ERR;
 
 #if defined(DEBUG) && 0
 	switch (action) {
 	case LOG_LINE_APPEND:
 		TRACE(sp, "%lu: log_line: append: %lu {%u}\n",
 		    ep->l_cur, lno, len);
 		break;
 	case LOG_LINE_DELETE:
 		TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
 		    ep->l_cur, lno, len);
 		break;
 	case LOG_LINE_INSERT:
 		TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
 		    ep->l_cur, lno, len);
 		break;
 	case LOG_LINE_RESET_F:
 		TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
 		    ep->l_cur, lno, len);
 		break;
 	case LOG_LINE_RESET_B:
 		TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
 		    ep->l_cur, lno, len);
 		break;
 	}
 #endif
 	/* Reset high water mark. */
 	ep->l_high = ++ep->l_cur;
 
 	return (0);
 }
 
 /*
  * log_mark --
  *	Log a mark position.  For the log to work, we assume that there
  *	aren't any operations that just put out a log record -- this
  *	would mean that undo operations would only reset marks, and not
  *	cause any other change.
  *
  * PUBLIC: int log_mark(SCR *, LMARK *);
  */
 int
 log_mark(SCR *sp, LMARK *lmp)
 {
 	DBT data, key;
 	EXF *ep;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG))
 		return (0);
 
 	/* Put out one initial cursor record per set of changes. */
 	if (ep->l_cursor.lno != OOBLNO) {
 		if (log_cursor1(sp, LOG_CURSOR_INIT))
 			return (1);
 		ep->l_cursor.lno = OOBLNO;
 	}
 
 	BINC_RETC(sp, ep->l_lp,
 	    ep->l_len, sizeof(u_char) + sizeof(LMARK));
 	ep->l_lp[0] = LOG_MARK;
 	memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
 
 	key.data = &ep->l_cur;
 	key.size = sizeof(recno_t);
 	data.data = ep->l_lp;
 	data.size = sizeof(u_char) + sizeof(LMARK);
 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
 		LOG_ERR;
 
 #if defined(DEBUG) && 0
 	TRACE(sp, "%lu: mark %c: %lu/%u\n",
 	    ep->l_cur, lmp->name, lmp->lno, lmp->cno);
 #endif
 	/* Reset high water mark. */
 	ep->l_high = ++ep->l_cur;
 	return (0);
 }
 
 /*
  * Log_backward --
  *	Roll the log backward one operation.
  *
  * PUBLIC: int log_backward(SCR *, MARK *);
  */
 int
 log_backward(SCR *sp, MARK *rp)
 {
 	DBT key, data;
 	EXF *ep;
 	LMARK lm;
 	MARK m;
 	recno_t lno;
 	int didop;
 	u_char *p;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG)) {
 		msgq(sp, M_ERR,
 		    "010|Logging not being performed, undo not possible");
 		return (1);
 	}
 
 	if (ep->l_cur == 1) {
 		msgq(sp, M_BERR, "011|No changes to undo");
 		return (1);
 	}
 
 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
 
 	key.data = &ep->l_cur;		/* Initialize db request. */
 	key.size = sizeof(recno_t);
 	for (didop = 0;;) {
 		--ep->l_cur;
 		if (ep->log->get(ep->log, &key, &data, 0))
 			LOG_ERR;
 #if defined(DEBUG) && 0
 		log_trace(sp, "log_backward", ep->l_cur, data.data);
 #endif
 		switch (*(p = (u_char *)data.data)) {
 		case LOG_CURSOR_INIT:
 			if (didop) {
 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
 				F_CLR(ep, F_NOLOG);
 				return (0);
 			}
 			break;
 		case LOG_CURSOR_END:
 			break;
 		case LOG_LINE_APPEND:
 		case LOG_LINE_INSERT:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (db_delete(sp, lno))
 				goto err;
 			++sp->rptlines[L_DELETED];
 			break;
 		case LOG_LINE_DELETE:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (apply_with(db_insert, sp, lno,
 				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
 				goto err;
 			++sp->rptlines[L_ADDED];
 			break;
 		case LOG_LINE_RESET_F:
 			break;
 		case LOG_LINE_RESET_B:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (apply_with(db_set, sp, lno,
 				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
 				goto err;
 			if (sp->rptlchange != lno) {
 				sp->rptlchange = lno;
 				++sp->rptlines[L_CHANGED];
 			}
 			break;
 		case LOG_MARK:
 			didop = 1;
 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
 			m.lno = lm.lno;
 			m.cno = lm.cno;
 			if (mark_set(sp, lm.name, &m, 0))
 				goto err;
 			break;
 		default:
 			abort();
 		}
 	}
 
 err:	F_CLR(ep, F_NOLOG);
 	return (1);
 }
 
 /*
  * Log_setline --
  *	Reset the line to its original appearance.
  *
  * XXX
  * There's a bug in this code due to our not logging cursor movements
  * unless a change was made.  If you do a change, move off the line,
  * then move back on and do a 'U', the line will be restored to the way
  * it was before the original change.
  *
  * PUBLIC: int log_setline(SCR *);
  */
 int
 log_setline(SCR *sp)
 {
 	DBT key, data;
 	EXF *ep;
 	LMARK lm;
 	MARK m;
 	recno_t lno;
 	u_char *p;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG)) {
 		msgq(sp, M_ERR,
 		    "012|Logging not being performed, undo not possible");
 		return (1);
 	}
 
 	if (ep->l_cur == 1)
 		return (1);
 
 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
 
 	key.data = &ep->l_cur;		/* Initialize db request. */
 	key.size = sizeof(recno_t);
 	for (;;) {
 		--ep->l_cur;
 		if (ep->log->get(ep->log, &key, &data, 0))
 			LOG_ERR;
 #if defined(DEBUG) && 0
 		log_trace(sp, "log_setline", ep->l_cur, data.data);
 #endif
 		switch (*(p = (u_char *)data.data)) {
 		case LOG_CURSOR_INIT:
 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
 			if (m.lno != sp->lno || ep->l_cur == 1) {
 				F_CLR(ep, F_NOLOG);
 				return (0);
 			}
 			break;
 		case LOG_CURSOR_END:
 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
 			if (m.lno != sp->lno) {
 				++ep->l_cur;
 				F_CLR(ep, F_NOLOG);
 				return (0);
 			}
 			break;
 		case LOG_LINE_APPEND:
 		case LOG_LINE_INSERT:
 		case LOG_LINE_DELETE:
 		case LOG_LINE_RESET_F:
 			break;
 		case LOG_LINE_RESET_B:
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (lno == sp->lno &&
 				apply_with(db_set, sp, lno,
 				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
 				goto err;
 			if (sp->rptlchange != lno) {
 				sp->rptlchange = lno;
 				++sp->rptlines[L_CHANGED];
 			}
 		case LOG_MARK:
 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
 			m.lno = lm.lno;
 			m.cno = lm.cno;
 			if (mark_set(sp, lm.name, &m, 0))
 				goto err;
 			break;
 		default:
 			abort();
 		}
 	}
 
 err:	F_CLR(ep, F_NOLOG);
 	return (1);
 }
 
 /*
  * Log_forward --
  *	Roll the log forward one operation.
  *
  * PUBLIC: int log_forward(SCR *, MARK *);
  */
 int
 log_forward(SCR *sp, MARK *rp)
 {
 	DBT key, data;
 	EXF *ep;
 	LMARK lm;
 	MARK m;
 	recno_t lno;
 	int didop;
 	u_char *p;
 
 	ep = sp->ep;
 	if (F_ISSET(ep, F_NOLOG)) {
 		msgq(sp, M_ERR,
 	    "013|Logging not being performed, roll-forward not possible");
 		return (1);
 	}
 
 	if (ep->l_cur == ep->l_high) {
 		msgq(sp, M_BERR, "014|No changes to re-do");
 		return (1);
 	}
 
 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
 
 	key.data = &ep->l_cur;		/* Initialize db request. */
 	key.size = sizeof(recno_t);
 	for (didop = 0;;) {
 		++ep->l_cur;
 		if (ep->log->get(ep->log, &key, &data, 0))
 			LOG_ERR;
 #if defined(DEBUG) && 0
 		log_trace(sp, "log_forward", ep->l_cur, data.data);
 #endif
 		switch (*(p = (u_char *)data.data)) {
 		case LOG_CURSOR_END:
 			if (didop) {
 				++ep->l_cur;
 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
 				F_CLR(ep, F_NOLOG);
 				return (0);
 			}
 			break;
 		case LOG_CURSOR_INIT:
 			break;
 		case LOG_LINE_APPEND:
 		case LOG_LINE_INSERT:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (apply_with(db_insert, sp, lno,
 				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
 				goto err;
 			++sp->rptlines[L_ADDED];
 			break;
 		case LOG_LINE_DELETE:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (db_delete(sp, lno))
 				goto err;
 			++sp->rptlines[L_DELETED];
 			break;
 		case LOG_LINE_RESET_B:
 			break;
 		case LOG_LINE_RESET_F:
 			didop = 1;
 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 			if (apply_with(db_set, sp, lno,
 				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
 				goto err;
 			if (sp->rptlchange != lno) {
 				sp->rptlchange = lno;
 				++sp->rptlines[L_CHANGED];
 			}
 			break;
 		case LOG_MARK:
 			didop = 1;
 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
 			m.lno = lm.lno;
 			m.cno = lm.cno;
 			if (mark_set(sp, lm.name, &m, 0))
 				goto err;
 			break;
 		default:
 			abort();
 		}
 	}
 
 err:	F_CLR(ep, F_NOLOG);
 	return (1);
 }
 
 /*
  * log_err --
  *	Try and restart the log on failure, i.e. if we run out of memory.
  */
 static void
 log_err(SCR *sp, char *file, int line)
 {
 	EXF *ep;
 
 	msgq(sp, M_SYSERR, "015|%s/%d: log put error", basename(file), line);
 	ep = sp->ep;
 	(void)ep->log->close(ep->log);
 	if (!log_init(sp, ep))
 		msgq(sp, M_ERR, "267|Log restarted");
 }
 
 #if defined(DEBUG) && 0
 static void
 log_trace(SCR *sp, char *msg, recno_t rno, u_char *p)
 {
 	LMARK lm;
 	MARK m;
 	recno_t lno;
 
 	switch (*p) {
 	case LOG_CURSOR_INIT:
 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
 		TRACE(sp, "%lu: %s:  C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
 		break;
 	case LOG_CURSOR_END:
 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
 		TRACE(sp, "%lu: %s:   C_END: %u/%u\n", rno, msg, m.lno, m.cno);
 		break;
 	case LOG_LINE_APPEND:
 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 		TRACE(sp, "%lu: %s:  APPEND: %lu\n", rno, msg, lno);
 		break;
 	case LOG_LINE_INSERT:
 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 		TRACE(sp, "%lu: %s:  INSERT: %lu\n", rno, msg, lno);
 		break;
 	case LOG_LINE_DELETE:
 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 		TRACE(sp, "%lu: %s:  DELETE: %lu\n", rno, msg, lno);
 		break;
 	case LOG_LINE_RESET_F:
 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 		TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
 		break;
 	case LOG_LINE_RESET_B:
 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
 		TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
 		break;
 	case LOG_MARK:
 		memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
 		TRACE(sp,
 		    "%lu: %s:    MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
 		break;
 	default:
 		abort();
 	}
 }
 #endif
 
 /*
  * apply_with --
  *	Apply a realigned line from the log db to the file db.
  */
 static int
 apply_with(int (*db_func)(SCR *, recno_t, CHAR_T *, size_t), SCR *sp,
     recno_t lno, u_char *p, size_t len)
 {
 #ifdef USE_WIDECHAR
 	typedef unsigned long nword;
 
 	static size_t blen;
 	static nword *bp;
 	nword *lp = (nword *)((uintptr_t)p / sizeof(nword) * sizeof(nword));
 
 	if (lp != (nword *)p) {
 		int offl = ((uintptr_t)p - (uintptr_t)lp) << 3;
 		int offr = (sizeof(nword) << 3) - offl;
 		size_t i, cnt = (len + sizeof(nword) / 2) / sizeof(nword);
 
 		if (len > blen) {
 			blen = p2roundup(MAX(len, 512));
 			REALLOC(sp, bp, nword *, blen);
 			if (bp == NULL)
 				return (1);
 		}
 		for (i = 0; i < cnt; ++i)
 #if BYTE_ORDER == BIG_ENDIAN
 			bp[i] = (lp[i] << offl) ^ (lp[i+1] >> offr);
 #else
 			bp[i] = (lp[i] >> offl) ^ (lp[i+1] << offr);
 #endif
 		p = (u_char *)bp;
 	}
 #endif
 	return db_func(sp, lno, (CHAR_T *)p, len / sizeof(CHAR_T));
 }
Index: head/contrib/nvi/common/main.c
===================================================================
--- head/contrib/nvi/common/main.c	(revision 366308)
+++ head/contrib/nvi/common/main.c	(revision 366309)
@@ -1,575 +1,576 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "common.h"
 #include "../vi/vi.h"
 #include "pathnames.h"
 
 static void	 attach(GS *);
 static int	 v_obsolete(char *[]);
 
 /*
  * editor --
  *	Main editor routine.
  *
  * PUBLIC: int editor(GS *, int, char *[]);
  */
 int
 editor(GS *gp, int argc, char *argv[])
 {
 	extern int optind;
 	extern char *optarg;
 	const char *p;
 	EVENT ev;
 	FREF *frp;
 	SCR *sp;
 	size_t len;
 	u_int flags;
 	int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
-	char *tag_f, *wsizearg, path[256];
-	CHAR_T *w;
+	char *tag_f, *wsizearg;
+	CHAR_T *w, path[256];
 	size_t wlen;
 
 	/* Initialize the busy routine, if not defined by the screen. */
 	if (gp->scr_busy == NULL)
 		gp->scr_busy = vs_busy;
 	/* Initialize the message routine, if not defined by the screen. */
 	if (gp->scr_msg == NULL)
 		gp->scr_msg = vs_msg;
 	gp->catd = (nl_catd)-1;
 
 	/* Common global structure initialization. */
 	TAILQ_INIT(gp->dq);
 	TAILQ_INIT(gp->hq);
 	SLIST_INIT(gp->ecq);
 	SLIST_INSERT_HEAD(gp->ecq, &gp->excmd, q);
 	gp->noprint = DEFAULT_NOPRINT;
 
 	/* Structures shared by screens so stored in the GS structure. */
 	TAILQ_INIT(gp->frefq);
 	TAILQ_INIT(gp->dcb_store.textq);
 	SLIST_INIT(gp->cutq);
 	SLIST_INIT(gp->seqq);
 
 	/* Set initial screen type and mode based on the program name. */
 	readonly = 0;
 	if (!strcmp(getprogname(), "ex") || !strcmp(getprogname(), "nex"))
 		LF_INIT(SC_EX);
 	else {
 		/* Nview, view are readonly. */
 		if (!strcmp(getprogname(), "nview") ||
 		    !strcmp(getprogname(), "view"))
 			readonly = 1;
 		
 		/* Vi is the default. */
 		LF_INIT(SC_VI);
 	}
 
 	/* Convert old-style arguments into new-style ones. */
 	if (v_obsolete(argv))
 		return (1);
 
 	/* Parse the arguments. */
 	flagchk = '\0';
 	tag_f = wsizearg = NULL;
 	lflag = secure = silent = 0;
 	startup = 1;
 
 	/* Set the file snapshot flag. */
 	F_SET(gp, G_SNAPSHOT);
 
 #ifdef DEBUG
 	while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF)
 #else
 	while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF)
 #endif
 		switch (ch) {
 		case 'c':		/* Run the command. */
 			/*
 			 * XXX
 			 * We should support multiple -c options.
 			 */
 			if (gp->c_option != NULL) {
 				warnx("only one -c command may be specified.");
 				return (1);
 			}
 			gp->c_option = optarg;
 			break;
 #ifdef DEBUG
 		case 'D':
 			switch (optarg[0]) {
 			case 's':
 				startup = 0;
 				break;
 			case 'w':
 				attach(gp);
 				break;
 			default:
 				warnx("usage: -D requires s or w argument.");
 				return (1);
 			}
 			break;
 #endif
 		case 'e':		/* Ex mode. */
 			LF_CLR(SC_VI);
 			LF_SET(SC_EX);
 			break;
 		case 'F':		/* No snapshot. */
 			F_CLR(gp, G_SNAPSHOT);
 			break;
 		case 'l':		/* Set lisp, showmatch options. */
 			lflag = 1;
 			break;
 		case 'R':		/* Readonly. */
 			readonly = 1;
 			break;
 		case 'r':		/* Recover. */
 			if (flagchk == 't') {
 				warnx("only one of -r and -t may be specified.");
 				return (1);
 			}
 			flagchk = 'r';
 			break;
 		case 'S':
 			secure = 1;
 			break;
 		case 's':
 			silent = 1;
 			break;
 #ifdef DEBUG
 		case 'T':		/* Trace. */
 			if ((gp->tracefp = fopen(optarg, "w")) == NULL) {
 				warn("%s", optarg);
 				goto err;
 			}
 			(void)fprintf(gp->tracefp,
 			    "\n===\ntrace: open %s\n", optarg);
 			break;
 #endif
 		case 't':		/* Tag. */
 			if (flagchk == 'r') {
 				warnx("only one of -r and -t may be specified.");
 				return (1);
 			}
 			if (flagchk == 't') {
 				warnx("only one tag file may be specified.");
 				return (1);
 			}
 			flagchk = 't';
 			tag_f = optarg;
 			break;
 		case 'v':		/* Vi mode. */
 			LF_CLR(SC_EX);
 			LF_SET(SC_VI);
 			break;
 		case 'w':
 			wsizearg = optarg;
 			break;
 		case '?':
 		default:
 			(void)gp->scr_usage();
 			return (1);
 		}
 	argc -= optind;
 	argv += optind;
 
 	/*
 	 * -s option is only meaningful to ex.
 	 *
 	 * If not reading from a terminal, it's like -s was specified.
 	 */
 	if (silent && !LF_ISSET(SC_EX)) {
 		warnx("-s option is only applicable to ex.");
 		goto err;
 	}
 	if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
 		silent = 1;
 
 	/*
 	 * Build and initialize the first/current screen.  This is a bit
 	 * tricky.  If an error is returned, we may or may not have a
 	 * screen structure.  If we have a screen structure, put it on a
 	 * display queue so that the error messages get displayed.
 	 *
 	 * !!!
 	 * Everything we do until we go interactive is done in ex mode.
 	 */
 	if (screen_init(gp, NULL, &sp)) {
 		if (sp != NULL)
 			TAILQ_INSERT_HEAD(gp->dq, sp, q);
 		goto err;
 	}
 	F_SET(sp, SC_EX);
 	TAILQ_INSERT_HEAD(gp->dq, sp, q);
 
 	if (v_key_init(sp))		/* Special key initialization. */
 		goto err;
 
 	{ int oargs[5], *oargp = oargs;
 	if (lflag) {			/* Command-line options. */
 		*oargp++ = O_LISP;
 		*oargp++ = O_SHOWMATCH;
 	}
 	if (readonly)
 		*oargp++ = O_READONLY;
 	if (secure)
 		*oargp++ = O_SECURE;
 	*oargp = -1;			/* Options initialization. */
 	if (opts_init(sp, oargs))
 		goto err;
 	}
 	if (wsizearg != NULL) {
 		ARGS *av[2], a, b;
-		(void)snprintf(path, sizeof(path), "window=%s", wsizearg);
+		(void)SPRINTF(path, SIZE(path), L("window=%s"), wsizearg);
 		a.bp = (CHAR_T *)path;
-		a.len = strlen(path);
+		a.len = SIZE(path);
 		b.bp = NULL;
 		b.len = 0;
 		av[0] = &a;
 		av[1] = &b;
 		(void)opts_set(sp, av, NULL);
 	}
 	if (silent) {			/* Ex batch mode option values. */
 		O_CLR(sp, O_AUTOPRINT);
 		O_CLR(sp, O_PROMPT);
 		O_CLR(sp, O_VERBOSE);
 		O_CLR(sp, O_WARN);
 		F_SET(sp, SC_EX_SILENT);
 	}
 
 	sp->rows = O_VAL(sp, O_LINES);	/* Make ex formatting work. */
 	sp->cols = O_VAL(sp, O_COLUMNS);
 
 	if (!silent && startup) {	/* Read EXINIT, exrc files. */
 		if (ex_exrc(sp))
 			goto err;
 		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
 			if (screen_end(sp))
 				goto err;
 			goto done;
 		}
 	}
 
 	/*
 	 * List recovery files if -r specified without file arguments.
 	 * Note, options must be initialized and startup information
 	 * read before doing this.
 	 */
 	if (flagchk == 'r' && argv[0] == NULL) {
 		if (rcv_list(sp))
 			goto err;
 		if (screen_end(sp))
 			goto err;
 		goto done;
 	}
 
 	/*
 	 * !!!
 	 * Initialize the default ^D, ^U scrolling value here, after the
 	 * user has had every opportunity to set the window option.
 	 *
 	 * It's historic practice that changing the value of the window
 	 * option did not alter the default scrolling value, only giving
 	 * a count to ^D/^U did that.
 	 */
 	sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
 
 	/*
 	 * If we don't have a command-line option, switch into the right
 	 * editor now, so that we position default files correctly, and
 	 * so that any tags file file-already-locked messages are in the
 	 * vi screen, not the ex screen.
 	 *
 	 * XXX
 	 * If we have a command-line option, the error message can end
 	 * up in the wrong place, but I think that the combination is
 	 * unlikely.
 	 */
 	if (gp->c_option == NULL) {
 		F_CLR(sp, SC_EX | SC_VI);
 		F_SET(sp, LF_ISSET(SC_EX | SC_VI));
 	}
 
 	/* Open a tag file if specified. */
 	if (tag_f != NULL) {
 		CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen);
 		if (ex_tag_first(sp, w))
 			goto err;
 	}
 
 	/*
 	 * Append any remaining arguments as file names.  Files are recovery
 	 * files if -r specified.  If the tag option or ex startup commands
 	 * loaded a file, then any file arguments are going to come after it.
 	 */
 	if (*argv != NULL) {
 		if (sp->frp != NULL) {
 			/* Cheat -- we know we have an extra argv slot. */
 			*--argv = strdup(sp->frp->name);
 			if (*argv == NULL) {
 				warn(NULL);
 				goto err;
 			}
 		}
 		sp->argv = sp->cargv = argv;
 		F_SET(sp, SC_ARGNOFREE);
 		if (flagchk == 'r')
 			F_SET(sp, SC_ARGRECOVER);
 	}
 
 	/*
 	 * If the ex startup commands and or/the tag option haven't already
 	 * created a file, create one.  If no command-line files were given,
 	 * use a temporary file.
 	 */
 	if (sp->frp == NULL) {
 		if (sp->argv == NULL) {
 			if ((frp = file_add(sp, NULL)) == NULL)
 				goto err;
 		} else  {
 			if ((frp = file_add(sp, sp->argv[0])) == NULL)
 				goto err;
 			if (F_ISSET(sp, SC_ARGRECOVER))
 				F_SET(frp, FR_RECOVER);
 		}
 
 		if (file_init(sp, frp, NULL, 0))
 			goto err;
 		if (EXCMD_RUNNING(gp)) {
 			(void)ex_cmd(sp);
 			if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
 				if (screen_end(sp))
 					goto err;
 				goto done;
 			}
 		}
 	}
 
 	/*
 	 * Check to see if we need to wait for ex.  If SC_SCR_EX is set, ex
 	 * was forced to initialize the screen during startup.  We'd like to
 	 * wait for a single character from the user, but we can't because
 	 * we're not in raw mode.  We can't switch to raw mode because the
 	 * vi initialization will switch to xterm's alternate screen, causing
 	 * us to lose the messages we're pausing to make sure the user read.
 	 * So, wait for a complete line.  
 	 */
 	if (F_ISSET(sp, SC_SCR_EX)) {
 		p = msg_cmsg(sp, CMSG_CONT_R, &len);
 		(void)write(STDOUT_FILENO, p, len);
 		for (;;) {
 			if (v_event_get(sp, &ev, 0, 0))
 				goto err;
 			if (ev.e_event == E_INTERRUPT ||
 			    (ev.e_event == E_CHARACTER &&
 			     (ev.e_value == K_CR || ev.e_value == K_NL)))
 				break;
 			(void)gp->scr_bell(sp);
 		}
 	}
 
 	/* Switch into the right editor, regardless. */
 	F_CLR(sp, SC_EX | SC_VI);
 	F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
 
 	/*
 	 * Main edit loop.  Vi handles split screens itself, we only return
 	 * here when switching editor modes or restarting the screen.
 	 */
 	while (sp != NULL)
 		if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
 			goto err;
 
 done:	rval = 0;
 	if (0)
 err:		rval = 1;
 
 	/* Clean out the global structure. */
 	v_end(gp);
 
 	return (rval);
 }
 
 /*
  * v_end --
  *	End the program, discarding screens and most of the global area.
  *
  * PUBLIC: void v_end(GS *);
  */
 void
 v_end(gp)
 	GS *gp;
 {
 	MSGS *mp;
 	SCR *sp;
 
 	/* If there are any remaining screens, kill them off. */
 	if (gp->ccl_sp != NULL) {
 		(void)file_end(gp->ccl_sp, NULL, 1);
 		(void)screen_end(gp->ccl_sp);
 	}
 	while ((sp = TAILQ_FIRST(gp->dq)) != NULL)
 		(void)screen_end(sp);
 	while ((sp = TAILQ_FIRST(gp->hq)) != NULL)
 		(void)screen_end(sp);
 
 #if defined(DEBUG) || defined(PURIFY)
 	{ FREF *frp;
 		/* Free FREF's. */
 		while ((frp = TAILQ_FIRST(gp->frefq)) != NULL) {
 			TAILQ_REMOVE(gp->frefq, frp, q);
 			free(frp->name);
 			free(frp->tname);
 			free(frp);
 		}
 	}
 
 	/* Free key input queue. */
 	free(gp->i_event);
 
 	/* Free cut buffers. */
 	cut_close(gp);
 
 	/* Free map sequences. */
 	seq_close(gp);
 
 	/* Free default buffer storage. */
 	(void)text_lfree(gp->dcb_store.textq);
 
 	/* Close message catalogs. */
 	msg_close(gp);
 #endif
 
 	/* Ring the bell if scheduled. */
 	if (F_ISSET(gp, G_BELLSCHED))
 		(void)fprintf(stderr, "\07");		/* \a */
 
 	/*
 	 * Flush any remaining messages.  If a message is here, it's almost
 	 * certainly the message about the event that killed us (although
 	 * it's possible that the user is sourcing a file that exits from the
 	 * editor).
 	 */
 	while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
 		(void)fprintf(stderr, "%s%.*s",
 		    mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
 		SLIST_REMOVE_HEAD(gp->msgq, q);
 #if defined(DEBUG) || defined(PURIFY)
 		free(mp->buf);
 		free(mp);
 #endif
 	}
 
 #if defined(DEBUG) || defined(PURIFY)
 	/* Free any temporary space. */
 	free(gp->tmp_bp);
 
 #if defined(DEBUG)
 	/* Close debugging file descriptor. */
 	if (gp->tracefp != NULL)
 		(void)fclose(gp->tracefp);
 #endif
 #endif
 }
 
 /*
  * v_obsolete --
  *	Convert historic arguments into something getopt(3) will like.
  */
 static int
 v_obsolete(char *argv[])
 {
 	size_t len;
 	char *p;
 
 	/*
 	 * Translate old style arguments into something getopt will like.
 	 * Make sure it's not text space memory, because ex modifies the
 	 * strings.
 	 *	Change "+" into "-c$".
 	 *	Change "+<anything else>" into "-c<anything else>".
 	 *	Change "-" into "-s"
 	 *	The c, T, t and w options take arguments so they can't be
 	 *	    special arguments.
 	 *
 	 * Stop if we find "--" as an argument, the user may want to edit
 	 * a file named "+foo".
 	 */
 	while (*++argv && strcmp(argv[0], "--"))
 		if (argv[0][0] == '+') {
 			if (argv[0][1] == '\0') {
 				argv[0] = strdup("-c$");
 				if (argv[0] == NULL)
 					goto nomem;
 			} else  {
 				p = argv[0];
 				len = strlen(argv[0]);
 				argv[0] = malloc(len + 2);
 				if (argv[0] == NULL)
 					goto nomem;
 				argv[0][0] = '-';
 				argv[0][1] = 'c';
 				(void)strlcpy(argv[0] + 2, p + 1, len);
 			}
-		} else if (argv[0][0] == '-')
+		} else if (argv[0][0] == '-') {
 			if (argv[0][1] == '\0') {
 				argv[0] = strdup("-s");
 				if (argv[0] == NULL) {
 nomem:					warn(NULL);
 					return (1);
 				}
 			} else
 				if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
 				    argv[0][1] == 't' || argv[0][1] == 'w') &&
 				    argv[0][2] == '\0')
 					++argv;
+		}
 	return (0);
 }
 
 #ifdef DEBUG
 static void
 attach(GS *gp)
 {
 	int fd;
 	char ch;
 
 	if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
 		warn("%s", _PATH_TTY);
 		return;
 	}
 
 	(void)printf("process %lu waiting, enter <CR> to continue: ",
 	    (u_long)getpid());
 	(void)fflush(stdout);
 
 	do {
 		if (read(fd, &ch, 1) != 1) {
 			(void)close(fd);
 			return;
 		}
 	} while (ch != '\n' && ch != '\r');
 	(void)close(fd);
 }
 #endif
Index: head/contrib/nvi/common/mark.c
===================================================================
--- head/contrib/nvi/common/mark.c	(revision 366308)
+++ head/contrib/nvi/common/mark.c	(revision 366309)
@@ -1,256 +1,257 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 
 static LMARK *mark_find(SCR *, ARG_CHAR_T);
 
 /*
  * Marks are maintained in a key sorted singly linked list.  We can't
  * use arrays because we have no idea how big an index key could be.
  * The underlying assumption is that users don't have more than, say,
  * 10 marks at any one time, so this will be is fast enough.
  *
  * Marks are fixed, and modifications to the line don't update the mark's
  * position in the line.  This can be hard.  If you add text to the line,
  * place a mark in that text, undo the addition and use ` to move to the
  * mark, the location will have disappeared.  It's tempting to try to adjust
  * the mark with the changes in the line, but this is hard to do, especially
  * if we've given the line to v_ntext.c:v_ntext() for editing.  Historic vi
  * would move to the first non-blank on the line when the mark location was
  * past the end of the line.  This can be complicated by deleting to a mark
  * that has disappeared using the ` command.  Historic vi treated this as
  * a line-mode motion and deleted the line.  This implementation complains to
  * the user.
  *
  * In historic vi, marks returned if the operation was undone, unless the
  * mark had been subsequently reset.  Tricky.  This is hard to start with,
  * but in the presence of repeated undo it gets nasty.  When a line is
  * deleted, we delete (and log) any marks on that line.  An undo will create
  * the mark.  Any mark creations are noted as to whether the user created
  * it or if it was created by an undo.  The former cannot be reset by another
  * undo, but the latter may.
  *
  * All of these routines translate ABSMARK2 to ABSMARK1.  Setting either of
  * the absolute mark locations sets both, so that "m'" and "m`" work like
  * they, ah, for lack of a better word, "should".
  */
 
 /*
  * mark_init --
  *	Set up the marks.
  *
  * PUBLIC: int mark_init(SCR *, EXF *);
  */
 int
 mark_init(SCR *sp, EXF *ep)
 {
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 *
 	 * Set up the marks.
 	 */
 	SLIST_INIT(ep->marks);
 	return (0);
 }
 
 /*
  * mark_end --
  *	Free up the marks.
  *
  * PUBLIC: int mark_end(SCR *, EXF *);
  */
 int
 mark_end(SCR *sp, EXF *ep)
 {
 	LMARK *lmp;
 
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 */
 	while ((lmp = SLIST_FIRST(ep->marks)) != NULL) {
 		SLIST_REMOVE_HEAD(ep->marks, q);
 		free(lmp);
 	}
 	return (0);
 }
 
 /*
  * mark_get --
  *	Get the location referenced by a mark.
  *
  * PUBLIC: int mark_get(SCR *, ARG_CHAR_T, MARK *, mtype_t);
  */
 int
 mark_get(SCR *sp, ARG_CHAR_T key, MARK *mp, mtype_t mtype)
 {
 	LMARK *lmp;
 
 	if (key == ABSMARK2)
 		key = ABSMARK1;
 
 	lmp = mark_find(sp, key);
 	if (lmp == NULL || lmp->name != key) {
 		msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
 		return (1);
 	}
 	if (F_ISSET(lmp, MARK_DELETED)) {
 		msgq(sp, mtype,
 		    "018|Mark %s: the line was deleted", KEY_NAME(sp, key));
 		return (1);
 	}
 
 	/*
 	 * !!!
 	 * The absolute mark is initialized to lno 1/cno 0, and historically
 	 * you could use it in an empty file.  Make such a mark always work.
 	 */
 	if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
 		msgq(sp, mtype,
 		    "019|Mark %s: cursor position no longer exists",
 		    KEY_NAME(sp, key));
 		return (1);
 	}
 	mp->lno = lmp->lno;
 	mp->cno = lmp->cno;
 	return (0);
 }
 
 /*
  * mark_set --
  *	Set the location referenced by a mark.
  *
  * PUBLIC: int mark_set(SCR *, ARG_CHAR_T, MARK *, int);
  */
 int
 mark_set(SCR *sp, ARG_CHAR_T key, MARK *value, int userset)
 {
 	LMARK *lmp, *lmt;
 
 	if (key == ABSMARK2)
 		key = ABSMARK1;
 
 	/*
 	 * The rules are simple.  If the user is setting a mark (if it's a
 	 * new mark this is always true), it always happens.  If not, it's
 	 * an undo, and we set it if it's not already set or if it was set
 	 * by a previous undo.
 	 */
 	lmp = mark_find(sp, key);
 	if (lmp == NULL || lmp->name != key) {
 		MALLOC_RET(sp, lmt, sizeof(LMARK));
 		if (lmp == NULL) {
 			SLIST_INSERT_HEAD(sp->ep->marks, lmt, q);
 		} else
 			SLIST_INSERT_AFTER(lmp, lmt, q);
 		lmp = lmt;
 	} else if (!userset &&
 	    !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
 		return (0);
 
 	lmp->lno = value->lno;
 	lmp->cno = value->cno;
 	lmp->name = key;
 	lmp->flags = userset ? MARK_USERSET : 0;
 	return (0);
 }
 
 /*
  * mark_find --
  *	Find the requested mark, or, the slot immediately before
  *	where it would go.
  */
 static LMARK *
 mark_find(SCR *sp, ARG_CHAR_T key)
 {
 	LMARK *lmp, *lastlmp = NULL;
 
 	/*
 	 * Return the requested mark or the slot immediately before
 	 * where it should go.
 	 */
 	SLIST_FOREACH(lmp, sp->ep->marks, q) {
 		if (lmp->name >= key)
 			return (lmp->name == key ? lmp : lastlmp);
 		lastlmp = lmp;
 	}
 	return (lastlmp);
 }
 
 /*
  * mark_insdel --
  *	Update the marks based on an insertion or deletion.
  *
  * PUBLIC: int mark_insdel(SCR *, lnop_t, recno_t);
  */
 int
 mark_insdel(SCR *sp, lnop_t op, recno_t lno)
 {
 	LMARK *lmp;
 	recno_t lline;
 
 	switch (op) {
 	case LINE_APPEND:
 		/* All insert/append operations are done as inserts. */
 		abort();
 	case LINE_DELETE:
 		SLIST_FOREACH(lmp, sp->ep->marks, q)
-			if (lmp->lno >= lno)
+			if (lmp->lno >= lno) {
 				if (lmp->lno == lno) {
 					F_SET(lmp, MARK_DELETED);
 					(void)log_mark(sp, lmp);
 				} else
 					--lmp->lno;
+			}
 		break;
 	case LINE_INSERT:
 		/*
 		 * XXX
 		 * Very nasty special case.  If the file was empty, then we're
 		 * adding the first line, which is a replacement.  So, we don't
 		 * modify the marks.  This is a hack to make:
 		 *
 		 *	mz:r!echo foo<carriage-return>'z
 		 *
 		 * work, i.e. historically you could mark the "line" in an empty
 		 * file and replace it, and continue to use the mark.  Insane,
 		 * well, yes, I know, but someone complained.
 		 *
 		 * Check for line #2 before going to the end of the file.
 		 */
 		if (!db_exist(sp, 2)) {
 			if (db_last(sp, &lline))
 				return (1);
 			if (lline == 1)
 				return (0);
 		}
 
 		SLIST_FOREACH(lmp, sp->ep->marks, q)
 			if (lmp->lno >= lno)
 				++lmp->lno;
 		break;
 	case LINE_RESET:
 		break;
 	}
 	return (0);
 }
Index: head/contrib/nvi/common/mem.h
===================================================================
--- head/contrib/nvi/common/mem.h	(revision 366308)
+++ head/contrib/nvi/common/mem.h	(revision 366309)
@@ -1,217 +1,217 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #ifdef DEBUG
 #define CHECK_TYPE(type, var)						\
 	type L__lp __attribute__((unused)) = var;
 #else
 #define CHECK_TYPE(type, var)
 #endif
 
 /* Increase the size of a malloc'd buffer.  Two versions, one that
  * returns, one that jumps to an error label.
  */
-#define	BINC_GOTO(sp, type, lp, llen, nlen) {				\
+#define	BINC_GOTO(sp, type, lp, llen, nlen) do {			\
 	CHECK_TYPE(type *, lp)						\
 	void *L__bincp;							\
 	if ((nlen) > llen) {						\
 		if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL)	\
 			goto alloc_err;					\
 		/*							\
 		 * !!!							\
 		 * Possible pointer conversion.				\
 		 */							\
 		lp = L__bincp;						\
 	}								\
-}
+} while (0)
 #define	BINC_GOTOC(sp, lp, llen, nlen)					\
 	BINC_GOTO(sp, char, lp, llen, nlen)
 #define	BINC_GOTOW(sp, lp, llen, nlen)					\
 	BINC_GOTO(sp, CHAR_T, lp, llen, (nlen) * sizeof(CHAR_T))
-#define	BINC_RET(sp, type, lp, llen, nlen) {				\
+#define	BINC_RET(sp, type, lp, llen, nlen) do {				\
 	CHECK_TYPE(type *, lp)						\
 	void *L__bincp;							\
 	if ((nlen) > llen) {						\
 		if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL)	\
 			return (1);					\
 		/*							\
 		 * !!!							\
 		 * Possible pointer conversion.				\
 		 */							\
 		lp = L__bincp;						\
 	}								\
-}
+} while (0)
 #define	BINC_RETC(sp, lp, llen, nlen)					\
 	BINC_RET(sp, char, lp, llen, nlen)
 #define	BINC_RETW(sp, lp, llen, nlen)					\
 	BINC_RET(sp, CHAR_T, lp, llen, (nlen) * sizeof(CHAR_T))
 
 /*
  * Get some temporary space, preferably from the global temporary buffer,
  * from a malloc'd buffer otherwise.  Two versions, one that returns, one
  * that jumps to an error label.
  */
-#define	GET_SPACE_GOTO(sp, type, bp, blen, nlen) {			\
+#define	GET_SPACE_GOTO(sp, type, bp, blen, nlen) do {			\
 	CHECK_TYPE(type *, bp)						\
 	GS *L__gp = (sp) == NULL ? NULL : (sp)->gp;			\
 	if (L__gp == NULL || F_ISSET(L__gp, G_TMP_INUSE)) {		\
 		bp = NULL;						\
 		blen = 0;						\
 		BINC_GOTO(sp, type, bp, blen, nlen); 			\
 	} else {							\
 		BINC_GOTOC(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen);	\
 		bp = (type *) L__gp->tmp_bp;				\
 		blen = L__gp->tmp_blen;					\
 		F_SET(L__gp, G_TMP_INUSE);				\
 	}								\
-}
+} while (0)
 #define	GET_SPACE_GOTOC(sp, bp, blen, nlen)				\
 	GET_SPACE_GOTO(sp, char, bp, blen, nlen)
 #define	GET_SPACE_GOTOW(sp, bp, blen, nlen)				\
 	GET_SPACE_GOTO(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
-#define	GET_SPACE_RET(sp, type, bp, blen, nlen) {			\
+#define	GET_SPACE_RET(sp, type, bp, blen, nlen) do {			\
 	CHECK_TYPE(type *, bp)						\
 	GS *L__gp = (sp) == NULL ? NULL : (sp)->gp;			\
 	if (L__gp == NULL || F_ISSET(L__gp, G_TMP_INUSE)) {		\
 		bp = NULL;						\
 		blen = 0;						\
 		BINC_RET(sp, type, bp, blen, nlen);			\
 	} else {							\
 		BINC_RETC(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen);	\
 		bp = (type *) L__gp->tmp_bp;				\
 		blen = L__gp->tmp_blen;					\
 		F_SET(L__gp, G_TMP_INUSE);				\
 	}								\
-}
+} while (0)
 #define	GET_SPACE_RETC(sp, bp, blen, nlen)				\
 	GET_SPACE_RET(sp, char, bp, blen, nlen)
 #define	GET_SPACE_RETW(sp, bp, blen, nlen)				\
 	GET_SPACE_RET(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
 
 /*
  * Add space to a GET_SPACE returned buffer.  Two versions, one that
  * returns, one that jumps to an error label.
  */
-#define	ADD_SPACE_GOTO(sp, type, bp, blen, nlen) {			\
+#define	ADD_SPACE_GOTO(sp, type, bp, blen, nlen) do {			\
 	CHECK_TYPE(type *, bp)						\
 	GS *L__gp = (sp) == NULL ? NULL : (sp)->gp;			\
 	if (L__gp == NULL || bp == (type *)L__gp->tmp_bp) {		\
 		F_CLR(L__gp, G_TMP_INUSE);				\
 		BINC_GOTOC(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen);	\
 		bp = (type *) L__gp->tmp_bp;				\
 		blen = L__gp->tmp_blen;					\
 		F_SET(L__gp, G_TMP_INUSE);				\
 	} else								\
 		BINC_GOTO(sp, type, bp, blen, nlen);			\
-}
+} while (0)
 #define	ADD_SPACE_GOTOC(sp, bp, blen, nlen)				\
 	ADD_SPACE_GOTO(sp, char, bp, blen, nlen)
 #define	ADD_SPACE_GOTOW(sp, bp, blen, nlen)				\
 	ADD_SPACE_GOTO(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
-#define	ADD_SPACE_RET(sp, type, bp, blen, nlen) {			\
+#define	ADD_SPACE_RET(sp, type, bp, blen, nlen) do {			\
 	CHECK_TYPE(type *, bp)						\
 	GS *L__gp = (sp) == NULL ? NULL : (sp)->gp;			\
 	if (L__gp == NULL || bp == (type *)L__gp->tmp_bp) {		\
 		F_CLR(L__gp, G_TMP_INUSE);				\
 		BINC_RETC(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen);	\
 		bp = (type *) L__gp->tmp_bp;				\
 		blen = L__gp->tmp_blen;					\
 		F_SET(L__gp, G_TMP_INUSE);				\
 	} else								\
 		BINC_RET(sp, type, bp, blen, nlen);			\
-}
+} while (0)
 #define	ADD_SPACE_RETC(sp, bp, blen, nlen)				\
 	ADD_SPACE_RET(sp, char, bp, blen, nlen)
 #define	ADD_SPACE_RETW(sp, bp, blen, nlen)				\
 	ADD_SPACE_RET(sp, CHAR_T, bp, blen, (nlen) * sizeof(CHAR_T))
 
 /* Free a GET_SPACE returned buffer. */
-#define	FREE_SPACE(sp, bp, blen) {					\
+#define	FREE_SPACE(sp, bp, blen) do {					\
 	GS *L__gp = (sp) == NULL ? NULL : (sp)->gp;			\
 	if (L__gp != NULL && bp == L__gp->tmp_bp)			\
 		F_CLR(L__gp, G_TMP_INUSE);				\
 	else								\
 		free(bp);						\
-}
-#define	FREE_SPACEW(sp, bp, blen) {					\
+} while (0)
+#define	FREE_SPACEW(sp, bp, blen) do {					\
 	CHECK_TYPE(CHAR_T *, bp)					\
 	FREE_SPACE(sp, (char *)bp, blen);				\
-}
+} while (0)
 
 /*
  * Malloc a buffer, casting the return pointer.  Various versions.
  */
-#define	CALLOC(sp, p, nmemb, size) {					\
+#define	CALLOC(sp, p, nmemb, size) do {					\
 	if ((p = calloc(nmemb, size)) == NULL)				\
 		msgq(sp, M_SYSERR, NULL);				\
-}
-#define	CALLOC_GOTO(sp, p, nmemb, size) {				\
+} while (0)
+#define	CALLOC_GOTO(sp, p, nmemb, size) do {				\
 	if ((p = calloc(nmemb, size)) == NULL)				\
 		goto alloc_err;						\
-}
-#define	CALLOC_RET(sp, p, nmemb, size) {				\
+} while (0)
+#define	CALLOC_RET(sp, p, nmemb, size) do {				\
 	if ((p = calloc(nmemb, size)) == NULL) {			\
 		msgq(sp, M_SYSERR, NULL);				\
 		return (1);						\
 	}								\
-}
+} while (0)
 
-#define	MALLOC(sp, p, size) {						\
+#define	MALLOC(sp, p, size) do {					\
 	if ((p = malloc(size)) == NULL)					\
 		msgq(sp, M_SYSERR, NULL);				\
-}
-#define	MALLOC_GOTO(sp, p, size) {					\
+} while (0)
+#define	MALLOC_GOTO(sp, p, size) do {					\
 	if ((p = malloc(size)) == NULL)					\
 		goto alloc_err;						\
-}
-#define	MALLOC_RET(sp, p, size) {					\
+} while (0)
+#define	MALLOC_RET(sp, p, size) do {					\
 	if ((p = malloc(size)) == NULL) {				\
 		msgq(sp, M_SYSERR, NULL);				\
 		return (1);						\
 	}								\
-}
+} while (0)
 
 /*
  * Resize a buffer, free any already held memory if we can't get more.
  * FreeBSD's reallocf(3) does the same thing, but it's not portable yet.
  */
-#define	REALLOC(sp, p, cast, size) {					\
+#define	REALLOC(sp, p, cast, size) do {					\
 	cast newp;							\
 	if ((newp = realloc(p, size)) == NULL) {			\
 		free(p);						\
 		msgq(sp, M_SYSERR, NULL);				\
 	}								\
 	p = newp;							\
-}
+} while (0)
 
 /* 
  * p2roundup --
  *	Get next power of 2; convenient for realloc.
  *
  * Reference: FreeBSD /usr/src/lib/libc/stdio/getdelim.c
  */
 static __inline size_t
 p2roundup(size_t n)
 {
 	n--;
 	n |= n >> 1;
 	n |= n >> 2;
 	n |= n >> 4;
 	n |= n >> 8;
 	n |= n >> 16;
 #if SIZE_T_MAX > 0xffffffffU
 	n |= n >> 32;
 #endif
 	n++;
 	return (n);
 }
 
 /* Additional TAILQ helper. */
 #define TAILQ_ENTRY_ISVALID(elm, field)					\
 	((elm)->field.tqe_prev != NULL)
Index: head/contrib/nvi/common/msg.c
===================================================================
--- head/contrib/nvi/common/msg.c	(revision 366308)
+++ head/contrib/nvi/common/msg.c	(revision 366309)
@@ -1,883 +1,887 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <locale.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "common.h"
 #include "../vi/vi.h"
 
 /*
  * msgq --
  *	Display a message.
  *
  * PUBLIC: void msgq(SCR *, mtype_t, const char *, ...);
  */
 void
 msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
 {
 #ifndef NL_ARGMAX
 #define	__NL_ARGMAX	20		/* Set to 9 by System V. */
 	struct {
 		const char *str;	/* String pointer. */
 		size_t	 arg;		/* Argument number. */
 		size_t	 prefix;	/* Prefix string length. */
 		size_t	 skip;		/* Skipped string length. */
 		size_t	 suffix;	/* Suffix string length. */
 	} str[__NL_ARGMAX];
 #endif
 	static int reenter;		/* STATIC: Re-entrancy check. */
 	GS *gp;
 	size_t blen, len, mlen, nlen;
 	const char *p;
 	char *bp, *mp;
 	va_list ap;
 #ifndef NL_ARGMAX
 	int ch;
 	char *rbp, *s_rbp;
 	const char *t, *u;
 	size_t cnt1, cnt2, soff;
 #endif
 
 	/*
 	 * !!!
 	 * It's possible to enter msg when there's no screen to hold the
 	 * message.  If sp is NULL, ignore the special cases and put the
 	 * message out to stderr.
 	 */
 	if (sp == NULL) {
 		gp = NULL;
 		if (mt == M_BERR)
 			mt = M_ERR;
 		else if (mt == M_VINFO)
 			mt = M_INFO;
 	} else {
 		gp = sp->gp;
 		switch (mt) {
 		case M_BERR:
 			if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) {
 				F_SET(gp, G_BELLSCHED);
 				return;
 			}
 			mt = M_ERR;
 			break;
 		case M_VINFO:
 			if (!O_ISSET(sp, O_VERBOSE))
 				return;
 			mt = M_INFO;
 			/* FALLTHROUGH */
 		case M_INFO:
 			if (F_ISSET(sp, SC_EX_SILENT))
 				return;
 			break;
 		case M_ERR:
 		case M_SYSERR:
 			break;
 		default:
 			abort();
 		}
 	}
 
 	/*
 	 * It's possible to reenter msg when it allocates space.  We're
 	 * probably dead anyway, but there's no reason to drop core.
 	 *
 	 * XXX
 	 * Yes, there's a race, but it should only be two instructions.
 	 */
 	if (reenter++)
 		return;
 
 	/* Get space for the message. */
 	nlen = 1024;
 	if (0) {
 retry:		FREE_SPACE(sp, bp, blen);
 		nlen *= 2;
 	}
 	bp = NULL;
 	blen = 0;
 	GET_SPACE_GOTOC(sp, bp, blen, nlen);
 
 	/*
 	 * Error prefix.
 	 *
 	 * mp:	 pointer to the current next character to be written
 	 * mlen: length of the already written characters
 	 * blen: total length of the buffer
 	 */
 #define	REM	(blen - mlen)
 	mp = bp;
 	mlen = 0;
 	if (mt == M_SYSERR) {
 		p = msg_cat(sp, "020|Error: ", &len);
 		if (REM < len)
 			goto retry;
 		memcpy(mp, p, len);
 		mp += len;
 		mlen += len;
 	}
 
 	/*
 	 * If we're running an ex command that the user didn't enter, display
 	 * the file name and line number prefix.
 	 */
 	if ((mt == M_ERR || mt == M_SYSERR) &&
 	    sp != NULL && gp != NULL && gp->if_name != NULL) {
 		CHAR_T *wp;
 		size_t wlen;
 
 		CHAR2INT(sp, gp->if_name, strlen(gp->if_name) + 1, wp, wlen);
 		for (; *wp != '\0'; ++wp) {
 			len = snprintf(mp, REM, "%s", KEY_NAME(sp, *wp));
 			mp += len;
 			if ((mlen += len) > blen)
 				goto retry;
 		}
 		len = snprintf(mp, REM, ", %d: ", gp->if_lno);
 		mp += len;
 		if ((mlen += len) > blen)
 			goto retry;
 	}
 
 	/* If nothing to format, we're done. */
 	if (fmt == NULL)
 		goto nofmt;
 	fmt = msg_cat(sp, fmt, NULL);
 
 #ifndef NL_ARGMAX
 	/*
 	 * Nvi should run on machines that don't support the numbered argument
 	 * specifications (%[digit]*$).  We do this by reformatting the string
 	 * so that we can hand it to vsprintf(3) and it will use the arguments
 	 * in the right order.  When vsprintf returns, we put the string back
 	 * into the right order.  It's undefined, according to SVID III, to mix
 	 * numbered argument specifications with the standard style arguments,
 	 * so this should be safe.
 	 *
 	 * In addition, we also need a character that is known to not occur in
 	 * any vi message, for separating the parts of the string.  As callers
 	 * of msgq are responsible for making sure that all the non-printable
 	 * characters are formatted for printing before calling msgq, we use a
 	 * random non-printable character selected at terminal initialization
 	 * time.  This code isn't fast by any means, but as messages should be
 	 * relatively short and normally have only a few arguments, it won't be
 	 * too bad.  Regardless, nobody has come up with any other solution.
 	 *
 	 * The result of this loop is an array of pointers into the message
 	 * string, with associated lengths and argument numbers.  The array
 	 * is in the "correct" order, and the arg field contains the argument
 	 * order.
 	 */
 	for (p = fmt, soff = 0; soff < __NL_ARGMAX;) {
 		for (t = p; *p != '\0' && *p != '%'; ++p);
 		if (*p == '\0')
 			break;
 		++p;
 		if (!isdigit((u_char)*p)) {
 			if (*p == '%')
 				++p;
 			continue;
 		}
 		for (u = p; *++p != '\0' && isdigit((u_char)*p););
 		if (*p != '$')
 			continue;
 
 		/* Up to, and including the % character. */
 		str[soff].str = t;
 		str[soff].prefix = u - t;
 
 		/* Up to, and including the $ character. */
 		str[soff].arg = atoi(u);
 		str[soff].skip = (p - u) + 1;
 		if (str[soff].arg >= __NL_ARGMAX)
 			goto ret;
 
 		/* Up to, and including the conversion character. */
 		for (u = p; (ch = *++p) != '\0';)
 			if (isalpha(ch) &&
 			    strchr("diouxXfeEgGcspn", ch) != NULL)
 				break;
 		str[soff].suffix = p - u;
 		if (ch != '\0')
 			++p;
 		++soff;
 	}
 
 	/* If no magic strings, we're done. */
 	if (soff == 0)
 		goto format;
 
 	 /* Get space for the reordered strings. */
 	if ((rbp = malloc(nlen)) == NULL)
 		goto ret;
 	s_rbp = rbp;
 
 	/*
 	 * Reorder the strings into the message string based on argument
 	 * order.
 	 *
 	 * !!!
 	 * We ignore arguments that are out of order, i.e. if we don't find
 	 * an argument, we continue.  Assume (almost certainly incorrectly)
 	 * that whoever created the string knew what they were doing.
 	 *
 	 * !!!
 	 * Brute force "sort", but since we don't expect more than one or two
 	 * arguments in a string, the setup cost of a fast sort will be more
 	 * expensive than the loop.
 	 */
 	for (cnt1 = 1; cnt1 <= soff; ++cnt1)
 		for (cnt2 = 0; cnt2 < soff; ++cnt2)
 			if (cnt1 == str[cnt2].arg) {
 				memmove(s_rbp, str[cnt2].str, str[cnt2].prefix);
 				memmove(s_rbp + str[cnt2].prefix,
 				    str[cnt2].str + str[cnt2].prefix +
 				    str[cnt2].skip, str[cnt2].suffix);
 				s_rbp += str[cnt2].prefix + str[cnt2].suffix;
 				*s_rbp++ =
 				    gp == NULL ? DEFAULT_NOPRINT : gp->noprint;
 				break;
 			}
 	*s_rbp = '\0';
 	fmt = rbp;
 #endif
 
 #ifndef NL_ARGMAX
 format:	/* Format the arguments into the string. */
 #endif
 	va_start(ap, fmt);
 	len = vsnprintf(mp, REM, fmt, ap);
 	va_end(ap);
 	if (len >= nlen)
 		goto retry;
 
 #ifndef NL_ARGMAX
 	if (soff == 0)
 		goto nofmt;
 
 	/*
 	 * Go through the resulting string, and, for each separator character
 	 * separated string, enter its new starting position and length in the
 	 * array.
 	 */
 	for (p = t = mp, cnt1 = 1,
 	    ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p)
 		if (*p == ch) {
 			for (cnt2 = 0; cnt2 < soff; ++cnt2)
 				if (str[cnt2].arg == cnt1)
 					break;
 			str[cnt2].str = t;
 			str[cnt2].prefix = p - t;
 			t = p + 1;
 			++cnt1;
 		}
 
 	/*
 	 * Reorder the strings once again, putting them back into the
 	 * message buffer.
 	 *
 	 * !!!
 	 * Note, the length of the message gets decremented once for
 	 * each substring, when we discard the separator character.
 	 */
 	for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) {
 		memmove(rbp, str[cnt1].str, str[cnt1].prefix);
 		rbp += str[cnt1].prefix;
 		--len;
 	}
 	memmove(mp, s_rbp, rbp - s_rbp);
 
 	/* Free the reordered string memory. */
 	free(s_rbp);
 #endif
 
 nofmt:	mp += len;
 	if ((mlen += len) > blen)
 		goto retry;
 	if (mt == M_SYSERR) {
 		len = snprintf(mp, REM, ": %s", strerror(errno));
 		mp += len;
 		if ((mlen += len) > blen)
 			goto retry;
 		mt = M_ERR;
 	}
 
 	/* Add trailing newline. */
 	if ((mlen += 1) > blen)
 		goto retry;
 	*mp = '\n';
 
 	if (sp != NULL)
 		(void)ex_fflush(sp);
 	if (gp != NULL)
 		gp->scr_msg(sp, mt, bp, mlen);
 	else
 		(void)fprintf(stderr, "%.*s", (int)mlen, bp);
 
 	/* Cleanup. */
 #ifndef NL_ARGMAX
 ret:
 #endif
 	FREE_SPACE(sp, bp, blen);
 alloc_err:
 	reenter = 0;
 }
 
 /*
  * msgq_wstr --
  *	Display a message with an embedded string.
  *
  * PUBLIC: void msgq_wstr(SCR *, mtype_t, const CHAR_T *, const char *);
  */
 void
 msgq_wstr(SCR *sp, mtype_t mtype, const CHAR_T *str, const char *fmt)
 {
 	size_t nlen;
 	CONST char *nstr;
 
 	if (str == NULL) {
 		msgq(sp, mtype, "%s", fmt);
 		return;
 	}
 	INT2CHAR(sp, str, STRLEN(str) + 1, nstr, nlen);
 	msgq_str(sp, mtype, nstr, fmt);
 }
 
 /*
  * msgq_str --
  *	Display a message with an embedded string.
  *
  * PUBLIC: void msgq_str(SCR *, mtype_t, const char *, const char *);
  */
 void
 msgq_str(SCR *sp, mtype_t mtype, const char *str, const char *fmt)
 {
 	int nf, sv_errno;
 	char *p;
 
 	if (str == NULL) {
 		msgq(sp, mtype, "%s", fmt);
 		return;
 	}
 
 	sv_errno = errno;
 	p = msg_print(sp, str, &nf);
 	errno = sv_errno;
 	msgq(sp, mtype, fmt, p);
 	if (nf)
 		FREE_SPACE(sp, p, 0);
 }
 
 /*
  * mod_rpt --
  *	Report on the lines that changed.
  *
  * !!!
  * Historic vi documentation (USD:15-8) claimed that "The editor will also
  * always tell you when a change you make affects text which you cannot see."
  * This wasn't true -- edit a large file and do "100d|1".  We don't implement
  * this semantic since it requires tracking each line that changes during a
  * command instead of just keeping count.
  *
  * Line counts weren't right in historic vi, either.  For example, given the
  * file:
  *	abc
  *	def
  * the command 2d}, from the 'b' would report that two lines were deleted,
  * not one.
  *
  * PUBLIC: void mod_rpt(SCR *);
  */
 void
 mod_rpt(SCR *sp)
 {
 	static char * const action[] = {
 		"293|added",
 		"294|changed",
 		"295|deleted",
 		"296|joined",
 		"297|moved",
 		"298|shifted",
 		"299|yanked",
 	};
 	static char * const lines[] = {
 		"300|line",
 		"301|lines",
 	};
 	recno_t total;
 	u_long rptval;
 	int first, cnt;
 	size_t blen, len, tlen;
 	const char *t;
 	char * const *ap;
 	char *bp, *p;
 
 	/* Change reports are turned off in batch mode. */
 	if (F_ISSET(sp, SC_EX_SILENT))
 		return;
 
 	/* Reset changing line number. */
 	sp->rptlchange = OOBLNO;
 
 	/*
 	 * Don't build a message if not enough changed.
 	 *
 	 * !!!
 	 * And now, a vi clone test.  Historically, vi reported if the number
 	 * of changed lines was > than the value, not >=, unless it was a yank
 	 * command, which used >=.  No lie.  Furthermore, an action was never
 	 * reported for a single line action.  This is consistent for actions
 	 * other than yank, but yank didn't report single line actions even if
 	 * the report edit option was set to 1.  In addition, setting report to
 	 * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an
 	 * unknown reason (this bug was fixed in System III/V at some point).
 	 * I got complaints, so nvi conforms to System III/V historic practice
 	 * except that we report a yank of 1 line if report is set to 1.
 	 */
 #define	ARSIZE(a)	sizeof(a) / sizeof (*a)
 #define	MAXNUM		25
 	rptval = O_VAL(sp, O_REPORT);
 	for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt)
 		total += sp->rptlines[cnt];
 	if (total == 0)
 		return;
 	if (total <= rptval && sp->rptlines[L_YANKED] < rptval) {
 		for (cnt = 0; cnt < ARSIZE(action); ++cnt)
 			sp->rptlines[cnt] = 0;
 		return;
 	}
 
 	/* Build and display the message. */
 	GET_SPACE_GOTOC(sp, bp, blen, sizeof(action) * MAXNUM + 1);
 	for (p = bp, first = 1, tlen = 0,
 	    ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt)
 		if (sp->rptlines[cnt] != 0) {
 			if (first)
 				first = 0;
 			else {
 				*p++ = ';';
 				*p++ = ' ';
 				tlen += 2;
 			}
 			len = snprintf(p, MAXNUM, "%lu ",
 			    (u_long)sp->rptlines[cnt]);
 			p += len;
 			tlen += len;
 			t = msg_cat(sp,
 			    lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len);
 			memcpy(p, t, len);
 			p += len;
 			tlen += len;
 			*p++ = ' ';
 			++tlen;
 			t = msg_cat(sp, *ap, &len);
 			memcpy(p, t, len);
 			p += len;
 			tlen += len;
 			sp->rptlines[cnt] = 0;
 		}
 
 	/* Add trailing newline. */
 	*p = '\n';
 	++tlen;
 
 	(void)ex_fflush(sp);
 	sp->gp->scr_msg(sp, M_INFO, bp, tlen);
 
 	FREE_SPACE(sp, bp, blen);
 alloc_err:
 	return;
 
 #undef ARSIZE
 #undef MAXNUM
 }
 
 /*
  * msgq_status --
  *	Report on the file's status.
  *
  * PUBLIC: void msgq_status(SCR *, recno_t, u_int);
  */
 void
 msgq_status(SCR *sp, recno_t lno, u_int flags)
 {
 	recno_t last;
 	size_t blen, len;
 	int cnt, needsep;
 	const char *t;
 	char **ap, *bp, *np, *p, *s, *ep;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/* Get sufficient memory. */
 	len = strlen(sp->frp->name);
 	GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
 	p = bp;
 	ep = bp + blen;
 
 	/* Convert the filename. */
 	CHAR2INT(sp, sp->frp->name, len + 1, wp, wlen);
 
 	/* Copy in the filename. */
 	for (; *wp != '\0'; ++wp) {
 		len = KEY_LEN(sp, *wp);
 		memcpy(p, KEY_NAME(sp, *wp), len);
 		p += len;
 	}
 	np = p;
 	*p++ = ':';
 	*p++ = ' ';
 
 	/* Copy in the argument count. */
 	if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
 		for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
 		if (cnt > 1) {
 			(void)snprintf(p, ep - p,
 			    msg_cat(sp, "317|%d files to edit", NULL), cnt);
 			p += strlen(p);
 			*p++ = ':';
 			*p++ = ' ';
 		}
 		F_CLR(sp, SC_STATUS_CNT);
 	}
 
 	/*
 	 * See nvi/exf.c:file_init() for a description of how and when the
 	 * read-only bit is set.
 	 *
 	 * !!!
 	 * The historic display for "name changed" was "[Not edited]".
 	 */
 	needsep = 0;
 	if (F_ISSET(sp->frp, FR_NEWFILE)) {
 		F_CLR(sp->frp, FR_NEWFILE);
 		t = msg_cat(sp, "021|new file", &len);
 		memcpy(p, t, len);
 		p += len;
 		needsep = 1;
 	} else {
 		if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
 			t = msg_cat(sp, "022|name changed", &len);
 			memcpy(p, t, len);
 			p += len;
 			needsep = 1;
 		}
 		if (needsep) {
 			*p++ = ',';
 			*p++ = ' ';
 		}
 		if (F_ISSET(sp->ep, F_MODIFIED))
 			t = msg_cat(sp, "023|modified", &len);
 		else
 			t = msg_cat(sp, "024|unmodified", &len);
 		memcpy(p, t, len);
 		p += len;
 		needsep = 1;
 	}
 	if (F_ISSET(sp->frp, FR_UNLOCKED)) {
 		if (needsep) {
 			*p++ = ',';
 			*p++ = ' ';
 		}
 		t = msg_cat(sp, "025|UNLOCKED", &len);
 		memcpy(p, t, len);
 		p += len;
 		needsep = 1;
 	}
 	if (O_ISSET(sp, O_READONLY)) {
 		if (needsep) {
 			*p++ = ',';
 			*p++ = ' ';
 		}
 		t = msg_cat(sp, "026|readonly", &len);
 		memcpy(p, t, len);
 		p += len;
 		needsep = 1;
 	}
 	if (needsep) {
 		*p++ = ':';
 		*p++ = ' ';
 	}
 	if (LF_ISSET(MSTAT_SHOWLAST)) {
 		if (db_last(sp, &last))
 			return;
 		if (last == 0) {
 			t = msg_cat(sp, "028|empty file", &len);
 			memcpy(p, t, len);
 			p += len;
 		} else {
 			t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
 			(void)snprintf(p, ep - p, t, (u_long)lno, (u_long)last,
 			    ((u_long)lno * 100) / last);
 			p += strlen(p);
 		}
 	} else {
 		t = msg_cat(sp, "029|line %lu", &len);
 		(void)snprintf(p, ep - p, t, (u_long)lno);
 		p += strlen(p);
 	}
 #ifdef DEBUG
 	(void)snprintf(p, ep - p, " (pid %lu)", (u_long)getpid());
 	p += strlen(p);
 #endif
 	*p++ = '\n';
 	len = p - bp;
 
 	/*
 	 * There's a nasty problem with long path names.  Cscope and tags files
 	 * can result in long paths and vi will request a continuation key from
 	 * the user as soon as it starts the screen.  Unfortunately, the user
 	 * has already typed ahead, and chaos results.  If we assume that the
 	 * characters in the filenames and informational messages only take a
 	 * single screen column each, we can trim the filename.
 	 *
 	 * XXX
 	 * Status lines get put up at fairly awkward times.  For example, when
 	 * you do a filter read (e.g., :read ! echo foo) in the top screen of a
 	 * split screen, we have to repaint the status lines for all the screens
 	 * below the top screen.  We don't want users having to enter continue
 	 * characters for those screens.  Make it really hard to screw this up.
 	 */
 	s = bp;
 	if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
 		for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
 		if (s == np) {
 			s = p - (sp->cols - 5);
 			*--s = ' ';
 		}
 		*--s = '.';
 		*--s = '.';
 		*--s = '.';
 		len = p - s;
 	}
 
 	/* Flush any waiting ex messages. */
 	(void)ex_fflush(sp);
 
 	sp->gp->scr_msg(sp, M_INFO, s, len);
 
 	FREE_SPACE(sp, bp, blen);
 alloc_err:
 	return;
 }
 
 /*
  * msg_open --
  *	Open the message catalogs.
  *
  * PUBLIC: int msg_open(SCR *, char *);
  */
 int
 msg_open(SCR *sp, char *file)
 {
 	/*
 	 * !!!
 	 * Assume that the first file opened is the system default, and that
 	 * all subsequent ones user defined.  Only display error messages
 	 * if we can't open the user defined ones -- it's useful to know if
 	 * the system one wasn't there, but if nvi is being shipped with an
 	 * installed system, the file will be there, if it's not, then the
 	 * message will be repeated every time nvi is started up.
 	 */
 	static int first = 1;
 	nl_catd catd;
 	char *p;
 	int rval = 0;
 
 	if ((p = strrchr(file, '/')) != NULL && p[1] == '\0') {
 		/* Confirms to XPG4. */
 		if ((p = join(file, setlocale(LC_MESSAGES, NULL))) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			return (1);
 		}
 	} else {
 		/* Make sure it's recognized as a path by catopen(3). */
 		if ((p = join(".", file)) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			return (1);
 		}
 	}
 	errno = 0;
 	if ((catd = catopen(p, NL_CAT_LOCALE)) == (nl_catd)-1) {
 		if (first) {
 			first = 0;
 			rval = 1;
 			goto ret;
 		}
 
 		/*
 		 * POSIX.1-2008 gives no instruction on how to report a
 		 * corrupt catalog file.  Errno == 0 is not rare; add
 		 * EFTYPE, which is seen on FreeBSD, for a good measure.
 		 */
+#ifdef EFTYPE
 		if (errno == 0 || errno == EFTYPE)
+#else
+		if (errno == 0)
+#endif
 			msgq_str(sp, M_ERR, p,
 			    "030|The file %s is not a message catalog");
 		else
 			msgq_str(sp, M_SYSERR, p, "%s");
 		rval = 1;
 		goto ret;
 	}
 	first = 0;
 
 	msg_close(sp->gp);
 	sp->gp->catd = catd;
 ret:	free(p);
 	return (rval);
 }
 
 /*
  * msg_close --
  *	Close the message catalogs.
  *
  * PUBLIC: void msg_close(GS *);
  */
 void
 msg_close(GS *gp)
 {
 	if (gp->catd != (nl_catd)-1)
 		(void)catclose(gp->catd);
 }
 
 /*
  * msg_cont --
  *	Return common continuation messages.
  *
  * PUBLIC: const char *msg_cmsg(SCR *, cmsg_t, size_t *);
  */
 const char *
 msg_cmsg(SCR *sp, cmsg_t which, size_t *lenp)
 {
 	switch (which) {
 	case CMSG_CONF:
 		return (msg_cat(sp, "268|confirm? [ynq]", lenp));
 	case CMSG_CONT:
 		return (msg_cat(sp, "269|Press any key to continue: ", lenp));
 	case CMSG_CONT_EX:
 		return (msg_cat(sp,
 	    "270|Press any key to continue [: to enter more ex commands]: ",
 		    lenp));
 	case CMSG_CONT_R:
 		return (msg_cat(sp, "161|Press Enter to continue: ", lenp));
 	case CMSG_CONT_S:
 		return (msg_cat(sp, "275| cont?", lenp));
 	case CMSG_CONT_Q:
 		return (msg_cat(sp,
 		    "271|Press any key to continue [q to quit]: ", lenp));
 	default:
 		abort();
 	}
 	/* NOTREACHED */
 }
 
 /*
  * msg_cat --
  *	Return a single message from the catalog, plus its length.
  *
  * !!!
  * Only a single catalog message can be accessed at a time, if multiple
  * ones are needed, they must be copied into local memory.
  *
  * PUBLIC: const char *msg_cat(SCR *, const char *, size_t *);
  */
 const char *
 msg_cat(SCR *sp, const char *str, size_t *lenp)
 {
 	GS *gp;
 	char *p;
 	int msgno;
 
 	/*
 	 * If it's not a catalog message, i.e. has doesn't have a leading
 	 * number and '|' symbol, we're done.
 	 */
 	if (isdigit((u_char)str[0]) &&
 	    isdigit((u_char)str[1]) &&
 	    isdigit((u_char)str[2]) && str[3] == '|') {
 		msgno = atoi(str);
 		str = &str[4];
 
 		gp = sp == NULL ? NULL : sp->gp;
 		if (gp != NULL && gp->catd != (nl_catd)-1 &&
 		    (p = catgets(gp->catd, 1, msgno, str)) != NULL) {
 			if (lenp != NULL)
 				*lenp = strlen(p);
 			return (p);
 		}
 	}
 	if (lenp != NULL)
 		*lenp = strlen(str);
 	return (str);
 }
 
 /*
  * msg_print --
  *	Return a printable version of a string, in allocated memory.
  *
  * PUBLIC: char *msg_print(SCR *, const char *, int *);
  */
 char *
 msg_print(SCR *sp, const char *s, int *needfree)
 {
 	size_t blen, nlen;
 	char *bp, *ep, *p, *t;
 	CHAR_T *wp, *cp;
 	size_t wlen;
 
 	*needfree = 0;
 
 	/* XXX Not good for debugging ex_read & ex_filter.*/
 	CHAR2INT5(sp, EXP(sp)->ibcw, (char *)s, strlen(s) + 1, wp, wlen);
 	for (cp = wp; *cp != '\0'; ++cp)
 		if (!ISPRINT(*cp))
 			break;
 	if (*cp == '\0')
 		return ((char *)s);	/* SAFE: needfree set to 0. */
 
 	nlen = 0;
 	if (0) {
 retry:		if (sp == NULL)
 			free(bp);
 		else
 			FREE_SPACE(sp, bp, blen);
 		*needfree = 0;
 	}
 	nlen += 256;
 	if (sp == NULL) {
 		if ((bp = malloc(nlen)) == NULL)
 			goto alloc_err;
 	} else
 		GET_SPACE_GOTOC(sp, bp, blen, nlen);
 	if (0) {
 alloc_err:	return ("");
 	}
 	*needfree = 1;
 
 	for (p = bp, ep = (bp + blen) - 1; *wp != '\0' && p < ep; ++wp)
 		for (t = KEY_NAME(sp, *wp); *t != '\0' && p < ep; *p++ = *t++);
 	if (p == ep)
 		goto retry;
 	*p = '\0';
 	return (bp);
 }
Index: head/contrib/nvi/common/options.c
===================================================================
--- head/contrib/nvi/common/options.c	(revision 366308)
+++ head/contrib/nvi/common/options.c	(revision 366309)
@@ -1,1158 +1,1166 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unctrl.h>
 #include <unistd.h>
 
 #include "common.h"
 #include "../vi/vi.h"
 #include "pathnames.h"
 
 static int	 	 opts_abbcmp(const void *, const void *);
 static int	 	 opts_cmp(const void *, const void *);
 static int	 	 opts_print(SCR *, OPTLIST const *);
 
 #ifdef USE_WIDECHAR
 #define OPT_WC	    0
 #else
 #define OPT_WC	    (OPT_NOSAVE | OPT_NDISP)
 #endif
 
 /*
  * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
  * Fifth Edition, May 1992.  There's no way of knowing what systems they are
  * actually from.
  *
  * HPUX noted options and abbreviations are from "The Ultimate Guide to the
  * VI and EX Text Editors", 1990.
  */
 OPTLIST const optlist[] = {
 /* O_ALTWERASE	  4.4BSD */
 	{L("altwerase"),	f_altwerase,	OPT_0BOOL,	0},
 /* O_AUTOINDENT	    4BSD */
 	{L("autoindent"),	NULL,		OPT_0BOOL,	0},
 /* O_AUTOPRINT	    4BSD */
 	{L("autoprint"),	NULL,		OPT_1BOOL,	0},
 /* O_AUTOWRITE	    4BSD */
 	{L("autowrite"),	NULL,		OPT_0BOOL,	0},
 /* O_BACKUP	  4.4BSD */
 	{L("backup"),	NULL,		OPT_STR,	0},
 /* O_BEAUTIFY	    4BSD */
 	{L("beautify"),	NULL,		OPT_0BOOL,	0},
 /* O_CDPATH	  4.4BSD */
 	{L("cdpath"),	NULL,		OPT_STR,	0},
 /* O_CEDIT	  4.4BSD */
 	{L("cedit"),	NULL,		OPT_STR,	0},
 /* O_COLUMNS	  4.4BSD */
 	{L("columns"),	f_columns,	OPT_NUM,	OPT_NOSAVE},
 /* O_COMBINED */
 	{L("combined"),	NULL,		OPT_0BOOL,	OPT_NOSET|OPT_WC},
 /* O_COMMENT	  4.4BSD */
 	{L("comment"),	NULL,		OPT_0BOOL,	0},
 /* O_TMPDIR	    4BSD */
 	{L("directory"),	NULL,		OPT_STR,	0},
 /* O_EDCOMPATIBLE   4BSD */
 	{L("edcompatible"),NULL,		OPT_0BOOL,	0},
 /* O_ERRORBELLS	    4BSD */
 	{L("errorbells"),	NULL,		OPT_0BOOL,	0},
 /* O_ESCAPETIME	  4.4BSD */
 	{L("escapetime"),	NULL,		OPT_NUM,	0},
 /* O_EXPANDTAB	NetBSD 5.0 */
 	{L("expandtab"),	NULL,		OPT_0BOOL,	0},
 /* O_EXRC	System V (undocumented) */
 	{L("exrc"),	NULL,		OPT_0BOOL,	0},
 /* O_EXTENDED	  4.4BSD */
 	{L("extended"),	f_recompile,	OPT_0BOOL,	0},
 /* O_FILEC	  4.4BSD */
 	{L("filec"),	NULL,		OPT_STR,	0},
 /* O_FILEENCODING */
 	{L("fileencoding"),f_encoding,	OPT_STR,	OPT_WC},
 /* O_FLASH	    HPUX */
 	{L("flash"),	NULL,		OPT_1BOOL,	0},
 /* O_HARDTABS	    4BSD */
 	{L("hardtabs"),	NULL,		OPT_NUM,	0},
 /* O_ICLOWER	  4.4BSD */
 	{L("iclower"),	f_recompile,	OPT_0BOOL,	0},
 /* O_IGNORECASE	    4BSD */
 	{L("ignorecase"),	f_recompile,	OPT_0BOOL,	0},
 /* O_INPUTENCODING */
 	{L("inputencoding"),f_encoding,	OPT_STR,	OPT_WC},
 /* O_KEYTIME	  4.4BSD */
 	{L("keytime"),	NULL,		OPT_NUM,	0},
 /* O_LEFTRIGHT	  4.4BSD */
 	{L("leftright"),	f_reformat,	OPT_0BOOL,	0},
 /* O_LINES	  4.4BSD */
 	{L("lines"),	f_lines,	OPT_NUM,	OPT_NOSAVE},
 /* O_LISP	    4BSD
  *	XXX
  *	When the lisp option is implemented, delete the OPT_NOSAVE flag,
  *	so that :mkexrc dumps it.
  */
 	{L("lisp"),	f_lisp,		OPT_0BOOL,	OPT_NOSAVE},
 /* O_LIST	    4BSD */
 	{L("list"),	f_reformat,	OPT_0BOOL,	0},
 /* O_LOCKFILES	  4.4BSD
  *	XXX
  *	Locking isn't reliable enough over NFS to require it, in addition,
  *	it's a serious startup performance problem over some remote links.
  */
 	{L("lock"),	NULL,		OPT_1BOOL,	0},
 /* O_MAGIC	    4BSD */
 	{L("magic"),	NULL,		OPT_1BOOL,	0},
 /* O_MATCHCHARS	  NetBSD 2.0 */
 	{L("matchchars"),	NULL,		OPT_STR,	OPT_PAIRS},
 /* O_MATCHTIME	  4.4BSD */
 	{L("matchtime"),	NULL,		OPT_NUM,	0},
 /* O_MESG	    4BSD */
 	{L("mesg"),	NULL,		OPT_1BOOL,	0},
 /* O_MODELINE	    4BSD
  *	!!!
  *	This has been documented in historical systems as both "modeline"
  *	and as "modelines".  Regardless of the name, this option represents
  *	a security problem of mammoth proportions, not to mention a stunning
  *	example of what your intro CS professor referred to as the perils of
  *	mixing code and data.  Don't add it, or I will kill you.
  */
 	{L("modeline"),	NULL,		OPT_0BOOL,	OPT_NOSET},
 /* O_MSGCAT	  4.4BSD */
 	{L("msgcat"),	f_msgcat,	OPT_STR,	0},
 /* O_NOPRINT	  4.4BSD */
 	{L("noprint"),	f_print,	OPT_STR,	0},
 /* O_NUMBER	    4BSD */
 	{L("number"),	f_reformat,	OPT_0BOOL,	0},
 /* O_OCTAL	  4.4BSD */
 	{L("octal"),	f_print,	OPT_0BOOL,	0},
 /* O_OPEN	    4BSD */
 	{L("open"),	NULL,		OPT_1BOOL,	0},
 /* O_OPTIMIZE	    4BSD */
 	{L("optimize"),	NULL,		OPT_1BOOL,	0},
 /* O_PARAGRAPHS	    4BSD */
 	{L("paragraphs"),	NULL,		OPT_STR,	OPT_PAIRS},
 /* O_PATH	  4.4BSD */
 	{L("path"),	NULL,		OPT_STR,	0},
 /* O_PRINT	  4.4BSD */
 	{L("print"),	f_print,	OPT_STR,	0},
 /* O_PROMPT	    4BSD */
 	{L("prompt"),	NULL,		OPT_1BOOL,	0},
 /* O_READONLY	    4BSD (undocumented) */
 	{L("readonly"),	f_readonly,	OPT_0BOOL,	OPT_ALWAYS},
 /* O_RECDIR	  4.4BSD */
 	{L("recdir"),	NULL,		OPT_STR,	0},
 /* O_REDRAW	    4BSD */
 	{L("redraw"),	NULL,		OPT_0BOOL,	0},
 /* O_REMAP	    4BSD */
 	{L("remap"),	NULL,		OPT_1BOOL,	0},
 /* O_REPORT	    4BSD */
 	{L("report"),	NULL,		OPT_NUM,	0},
 /* O_RULER	  4.4BSD */
 	{L("ruler"),	NULL,		OPT_0BOOL,	0},
 /* O_SCROLL	    4BSD */
 	{L("scroll"),	NULL,		OPT_NUM,	0},
 /* O_SEARCHINCR	  4.4BSD */
 	{L("searchincr"),	NULL,		OPT_0BOOL,	0},
 /* O_SECTIONS	    4BSD */
 	{L("sections"),	NULL,		OPT_STR,	OPT_PAIRS},
 /* O_SECURE	  4.4BSD */
 	{L("secure"),	NULL,		OPT_0BOOL,	OPT_NOUNSET},
 /* O_SHELL	    4BSD */
 	{L("shell"),	NULL,		OPT_STR,	0},
 /* O_SHELLMETA	  4.4BSD */
 	{L("shellmeta"),	NULL,		OPT_STR,	0},
 /* O_SHIFTWIDTH	    4BSD */
 	{L("shiftwidth"),	NULL,		OPT_NUM,	OPT_NOZERO},
 /* O_SHOWMATCH	    4BSD */
 	{L("showmatch"),	NULL,		OPT_0BOOL,	0},
 /* O_SHOWMODE	  4.4BSD */
 	{L("showmode"),	NULL,		OPT_0BOOL,	0},
 /* O_SIDESCROLL	  4.4BSD */
 	{L("sidescroll"),	NULL,		OPT_NUM,	OPT_NOZERO},
 /* O_SLOWOPEN	    4BSD  */
 	{L("slowopen"),	NULL,		OPT_0BOOL,	0},
 /* O_SOURCEANY	    4BSD (undocumented)
  *	!!!
  *	Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
  *	were owned by the user.  The sourceany option was an undocumented
  *	feature of historic vi which permitted the startup source'ing of
  *	.exrc files the user didn't own.  This is an obvious security problem,
  *	and we ignore the option.
  */
 	{L("sourceany"),	NULL,		OPT_0BOOL,	OPT_NOSET},
 /* O_TABSTOP	    4BSD */
 	{L("tabstop"),	f_reformat,	OPT_NUM,	OPT_NOZERO},
 /* O_TAGLENGTH	    4BSD */
 	{L("taglength"),	NULL,		OPT_NUM,	0},
 /* O_TAGS	    4BSD */
 	{L("tags"),	NULL,		OPT_STR,	0},
 /* O_TERM	    4BSD
  *	!!!
  *	By default, the historic vi always displayed information about two
  *	options, redraw and term.  Term seems sufficient.
  */
 	{L("term"),	NULL,		OPT_STR,	OPT_ADISP|OPT_NOSAVE},
 /* O_TERSE	    4BSD */
 	{L("terse"),	NULL,		OPT_0BOOL,	0},
 /* O_TILDEOP      4.4BSD */
 	{L("tildeop"),	NULL,		OPT_0BOOL,	0},
 /* O_TIMEOUT	    4BSD (undocumented) */
 	{L("timeout"),	NULL,		OPT_1BOOL,	0},
 /* O_TTYWERASE	  4.4BSD */
 	{L("ttywerase"),	f_ttywerase,	OPT_0BOOL,	0},
 /* O_VERBOSE	  4.4BSD */
 	{L("verbose"),	NULL,		OPT_0BOOL,	0},
 /* O_W1200	    4BSD */
 	{L("w1200"),	f_w1200,	OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
 /* O_W300	    4BSD */
 	{L("w300"),	f_w300,		OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
 /* O_W9600	    4BSD */
 	{L("w9600"),	f_w9600,	OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
 /* O_WARN	    4BSD */
 	{L("warn"),	NULL,		OPT_1BOOL,	0},
 /* O_WINDOW	    4BSD */
 	{L("window"),	f_window,	OPT_NUM,	0},
 /* O_WINDOWNAME	    4BSD */
 	{L("windowname"),	NULL,		OPT_0BOOL,	0},
 /* O_WRAPLEN	  4.4BSD */
 	{L("wraplen"),	NULL,		OPT_NUM,	0},
 /* O_WRAPMARGIN	    4BSD */
 	{L("wrapmargin"),	NULL,		OPT_NUM,	0},
 /* O_WRAPSCAN	    4BSD */
 	{L("wrapscan"),	NULL,		OPT_1BOOL,	0},
 /* O_WRITEANY	    4BSD */
 	{L("writeany"),	NULL,		OPT_0BOOL,	0},
 	{NULL},
 };
 
 typedef struct abbrev {
 	CHAR_T *name;
 	int offset;
 } OABBREV;
 
 static OABBREV const abbrev[] = {
 	{L("ai"),	O_AUTOINDENT},		/*     4BSD */
 	{L("ap"),	O_AUTOPRINT},		/*     4BSD */
 	{L("aw"),	O_AUTOWRITE},		/*     4BSD */
 	{L("bf"),	O_BEAUTIFY},		/*     4BSD */
 	{L("co"),	O_COLUMNS},		/*   4.4BSD */
 	{L("dir"),	O_TMPDIR},		/*     4BSD */
 	{L("eb"),	O_ERRORBELLS},		/*     4BSD */
 	{L("ed"),	O_EDCOMPATIBLE},	/*     4BSD */
 	{L("et"),	O_EXPANDTAB},		/* NetBSD 5.0 */
 	{L("ex"),	O_EXRC},		/* System V (undocumented) */
 	{L("fe"),	O_FILEENCODING},
 	{L("ht"),	O_HARDTABS},		/*     4BSD */
 	{L("ic"),	O_IGNORECASE},		/*     4BSD */
 	{L("ie"),	O_INPUTENCODING},
 	{L("li"),	O_LINES},		/*   4.4BSD */
 	{L("modelines"),	O_MODELINE},		/*     HPUX */
 	{L("nu"),	O_NUMBER},		/*     4BSD */
 	{L("opt"),	O_OPTIMIZE},		/*     4BSD */
 	{L("para"),	O_PARAGRAPHS},		/*     4BSD */
 	{L("re"),	O_REDRAW},		/* O'Reilly */
 	{L("ro"),	O_READONLY},		/*     4BSD (undocumented) */
 	{L("scr"),	O_SCROLL},		/*     4BSD (undocumented) */
 	{L("sect"),	O_SECTIONS},		/* O'Reilly */
 	{L("sh"),	O_SHELL},		/*     4BSD */
 	{L("slow"),	O_SLOWOPEN},		/*     4BSD */
 	{L("sm"),	O_SHOWMATCH},		/*     4BSD */
 	{L("smd"),	O_SHOWMODE},		/*     4BSD */
 	{L("sw"),	O_SHIFTWIDTH},		/*     4BSD */
 	{L("tag"),	O_TAGS},		/*     4BSD (undocumented) */
 	{L("tl"),	O_TAGLENGTH},		/*     4BSD */
 	{L("to"),	O_TIMEOUT},		/*     4BSD (undocumented) */
 	{L("ts"),	O_TABSTOP},		/*     4BSD */
 	{L("tty"),	O_TERM},		/*     4BSD (undocumented) */
 	{L("ttytype"),	O_TERM},		/*     4BSD (undocumented) */
 	{L("w"),	O_WINDOW},		/* O'Reilly */
 	{L("wa"),	O_WRITEANY},		/*     4BSD */
 	{L("wi"),	O_WINDOW},		/*     4BSD (undocumented) */
 	{L("wl"),	O_WRAPLEN},		/*   4.4BSD */
 	{L("wm"),	O_WRAPMARGIN},		/*     4BSD */
 	{L("ws"),	O_WRAPSCAN},		/*     4BSD */
 	{NULL},
 };
 
 /*
  * opts_init --
  *	Initialize some of the options.
  *
  * PUBLIC: int opts_init(SCR *, int *);
  */
 int
 opts_init(SCR *sp, int *oargs)
 {
 	ARGS *argv[2], a, b;
 	OPTLIST const *op;
 	u_long v;
 	int cnt, optindx = 0;
 	char *s;
 	CHAR_T b2[1024];
 
 	a.bp = b2;
 	b.bp = NULL;
 	a.len = b.len = 0;
 	argv[0] = &a;
 	argv[1] = &b;
 
 	/* Set numeric and string default values. */
-#define	OI(indx, str) {							\
+#define	OI(indx, str) do {						\
 	a.len = STRLEN(str);						\
 	if ((CHAR_T*)str != b2)	  /* GCC puts strings in text-space. */	\
 		(void)MEMCPY(b2, str, a.len+1);				\
 	if (opts_set(sp, argv, NULL)) {					\
 		 optindx = indx;					\
 		goto err;						\
 	}								\
-}
+} while (0)
 	/*
 	 * Indirect global options to global space.  Specifically, set up
 	 * terminal, lines, columns first, they're used by other options.
 	 * Note, don't set the flags until we've set up the indirection.
 	 */
 	if (o_set(sp, O_TERM, 0, NULL, GO_TERM))
 		goto err;
 	F_SET(&sp->opts[O_TERM], OPT_GLOBAL);
 	if (o_set(sp, O_LINES, 0, NULL, GO_LINES))
 		goto err;
 	F_SET(&sp->opts[O_LINES], OPT_GLOBAL);
 	if (o_set(sp, O_COLUMNS, 0, NULL, GO_COLUMNS))
 		goto err;
 	F_SET(&sp->opts[O_COLUMNS], OPT_GLOBAL);
 	if (o_set(sp, O_SECURE, 0, NULL, GO_SECURE))
 		goto err;
 	F_SET(&sp->opts[O_SECURE], OPT_GLOBAL);
 
 	/* Initialize string values. */
 	(void)SPRINTF(b2, SIZE(b2),
 	    L("cdpath=%s"), (s = getenv("CDPATH")) == NULL ? ":" : s);
 	OI(O_CDPATH, b2);
 	OI(O_CEDIT, L("cedit=\033"));
 
 	/*
 	 * !!!
 	 * Vi historically stored temporary files in /var/tmp.  We store them
 	 * in /tmp by default, hoping it's a memory based file system.  There
 	 * are two ways to change this -- the user can set either the directory
 	 * option or the TMPDIR environmental variable.
 	 */
 	(void)SPRINTF(b2, SIZE(b2),
 	    L("directory=%s"), (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
 	OI(O_TMPDIR, b2);
 	OI(O_ESCAPETIME, L("escapetime=6"));
 	OI(O_FILEC, L("filec=\t"));
 	OI(O_KEYTIME, L("keytime=6"));
 	OI(O_MATCHCHARS, L("matchchars=()[]{}"));
 	OI(O_MATCHTIME, L("matchtime=7"));
 	(void)SPRINTF(b2, SIZE(b2), L("msgcat=%s"), _PATH_MSGCAT);
 	OI(O_MSGCAT, b2);
 	OI(O_REPORT, L("report=5"));
 	OI(O_PARAGRAPHS, L("paragraphs=IPLPPPQPP LIpplpipbp"));
 	(void)SPRINTF(b2, SIZE(b2), L("path=%s"), "");
 	OI(O_PATH, b2);
-	(void)SPRINTF(b2, SIZE(b2), L("recdir=%s"), _PATH_PRESERVE);
+	(void)SPRINTF(b2, SIZE(b2), L("recdir=%s"), NVI_PATH_PRESERVE);
 	OI(O_RECDIR, b2);
 	OI(O_SECTIONS, L("sections=NHSHH HUnhsh"));
 	(void)SPRINTF(b2, SIZE(b2),
 	    L("shell=%s"), (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
 	OI(O_SHELL, b2);
 	OI(O_SHELLMETA, L("shellmeta=~{[*?$`'\"\\"));
 	OI(O_SHIFTWIDTH, L("shiftwidth=8"));
 	OI(O_SIDESCROLL, L("sidescroll=16"));
 	OI(O_TABSTOP, L("tabstop=8"));
 	(void)SPRINTF(b2, SIZE(b2), L("tags=%s"), _PATH_TAGS);
 	OI(O_TAGS, b2);
 
 	/*
 	 * XXX
 	 * Initialize O_SCROLL here, after term; initializing term should
 	 * have created a LINES/COLUMNS value.
 	 */
 	if ((v = (O_VAL(sp, O_LINES) - 1) / 2) == 0)
 		v = 1;
 	(void)SPRINTF(b2, SIZE(b2), L("scroll=%ld"), v);
 	OI(O_SCROLL, b2);
 
 	/*
 	 * The default window option values are:
 	 *		8 if baud rate <=  600
 	 *	       16 if baud rate <= 1200
 	 *	LINES - 1 if baud rate  > 1200
 	 *
 	 * Note, the windows option code will correct any too-large value
 	 * or when the O_LINES value is 1.
 	 */
 	if (sp->gp->scr_baud(sp, &v))
 		return (1);
 	if (v <= 600)
 		v = 8;
 	else if (v <= 1200)
 		v = 16;
 	else if ((v = O_VAL(sp, O_LINES) - 1) == 0)
 		v = 1;
 
 	(void)SPRINTF(b2, SIZE(b2), L("window=%lu"), v);
 	OI(O_WINDOW, b2);
 
 	/*
 	 * Set boolean default values, and copy all settings into the default
 	 * information.  OS_NOFREE is set, we're copying, not replacing.
 	 */
 	for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) {
 		if (F_ISSET(op, OPT_GLOBAL))
 			continue;
 		switch (op->type) {
 		case OPT_0BOOL:
 			break;
 		case OPT_1BOOL:
 			O_SET(sp, cnt);
 			O_D_SET(sp, cnt);
 			break;
 		case OPT_NUM:
 			o_set(sp, cnt, OS_DEF, NULL, O_VAL(sp, cnt));
 			break;
 		case OPT_STR:
 			if (O_STR(sp, cnt) != NULL && o_set(sp, cnt,
 			    OS_DEF | OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0))
 				goto err;
 			break;
 		default:
 			abort();
 		}
 	}
 
 	/*
 	 * !!!
 	 * Some options can be initialized by the command name or the
 	 * command-line arguments.  They don't set the default values,
 	 * it's historic practice.
 	 */
 	for (; *oargs != -1; ++oargs)
 		OI(*oargs, optlist[*oargs].name);
 #undef OI
 	return (0);
 
 err:	msgq_wstr(sp, M_ERR, optlist[optindx].name,
 	    "031|Unable to set default %s option");
 	return (1);
 }
 
 /*
  * opts_set --
  *	Change the values of one or more options.
  *
  * PUBLIC: int opts_set(SCR *, ARGS *[], char *);
  */
 int
 opts_set(SCR *sp, ARGS *argv[], char *usage)
 {
 	enum optdisp disp;
 	enum nresult nret;
 	OPTLIST const *op;
 	OPTION *spo;
 	u_long isset, turnoff, value;
 	int ch, equals, nf, nf2, offset, qmark, rval;
 	CHAR_T *endp, *name, *p, *sep;
 	char *p2, *t2;
 	char *np;
 	size_t nlen;
 
 	disp = NO_DISPLAY;
 	for (rval = 0; argv[0]->len != 0; ++argv) {
 		/*
 		 * The historic vi dumped the options for each occurrence of
 		 * "all" in the set list.  Puhleeze.
 		 */
 		if (!STRCMP(argv[0]->bp, L("all"))) {
 			disp = ALL_DISPLAY;
 			continue;
 		}
 
 		/* Find equals sign or question mark. */
 		for (sep = NULL, equals = qmark = 0,
 		    p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
 			if (ch == '=' || ch == '?') {
 				if (p == name) {
 					if (usage != NULL)
 						msgq(sp, M_ERR,
 						    "032|Usage: %s", usage);
 					return (1);
 				}
 				sep = p;
 				if (ch == '=')
 					equals = 1;
 				else
 					qmark = 1;
 				break;
 			}
 
 		turnoff = 0;
 		op = NULL;
 		if (sep != NULL)
 			*sep++ = '\0';
 
 		/* Search for the name, then name without any leading "no". */
 		if ((op = opts_search(name)) == NULL &&
 		    name[0] == 'n' && name[1] == 'o') {
 			turnoff = 1;
 			name += 2;
 			op = opts_search(name);
 		}
 		if (op == NULL) {
 			opts_nomatch(sp, name);
 			rval = 1;
 			continue;
 		}
 
 		/* Find current option values. */
 		offset = op - optlist;
 		spo = sp->opts + offset;
 
 		/*
 		 * !!!
 		 * Historically, the question mark could be a separate
 		 * argument.
 		 */
 		if (!equals && !qmark &&
 		    argv[1]->len == 1 && argv[1]->bp[0] == '?') {
 			++argv;
 			qmark = 1;
 		}
 
 		/* Set name, value. */
 		switch (op->type) {
 		case OPT_0BOOL:
 		case OPT_1BOOL:
 			/* Some options may not be reset. */
 			if (F_ISSET(op, OPT_NOUNSET) && turnoff) {
 				msgq_wstr(sp, M_ERR, name,
 			    "291|set: the %s option may not be turned off");
 				rval = 1;
 				break;
 			}
 
 			/* Some options may not be set. */
 			if (F_ISSET(op, OPT_NOSET) && !turnoff) {
 				msgq_wstr(sp, M_ERR, name,
 			    "313|set: the %s option may never be turned on");
 				rval = 1;
 				break;
 			}
 
 			if (equals) {
 				msgq_wstr(sp, M_ERR, name,
 			    "034|set: [no]%s option doesn't take a value");
 				rval = 1;
 				break;
 			}
 			if (qmark) {
 				if (!disp)
 					disp = SELECT_DISPLAY;
 				F_SET(spo, OPT_SELECTED);
 				break;
 			}
 
 			/*
 			 * Do nothing if the value is unchanged, the underlying
 			 * functions can be expensive.
 			 */
 			isset = !turnoff;
-			if (!F_ISSET(op, OPT_ALWAYS))
+			if (!F_ISSET(op, OPT_ALWAYS)) {
 				if (isset) {
 					if (O_ISSET(sp, offset))
 						break;
 				} else
 					if (!O_ISSET(sp, offset))
 						break;
+			}
 
 			/* Report to subsystems. */
 			if ((op->func != NULL &&
 			    op->func(sp, spo, NULL, &isset)) ||
 			    ex_optchange(sp, offset, NULL, &isset) ||
 			    v_optchange(sp, offset, NULL, &isset) ||
 			    sp->gp->scr_optchange(sp, offset, NULL, &isset)) {
 				rval = 1;
 				break;
 			}
 
 			/* Set the value. */
 			if (isset)
 				O_SET(sp, offset);
 			else
 				O_CLR(sp, offset);
 			break;
 		case OPT_NUM:
 			if (turnoff) {
 				msgq_wstr(sp, M_ERR, name,
 				    "035|set: %s option isn't a boolean");
 				rval = 1;
 				break;
 			}
 			if (qmark || !equals) {
 				if (!disp)
 					disp = SELECT_DISPLAY;
 				F_SET(spo, OPT_SELECTED);
 				break;
 			}
 
 			if (!ISDIGIT(sep[0]))
 				goto badnum;
 			if ((nret =
 			    nget_uslong(&value, sep, &endp, 10)) != NUM_OK) {
 				INT2CHAR(sp, name, STRLEN(name) + 1, 
 					     np, nlen);
 				p2 = msg_print(sp, np, &nf);
 				INT2CHAR(sp, sep, STRLEN(sep) + 1, 
 					     np, nlen);
 				t2 = msg_print(sp, np, &nf2);
 				switch (nret) {
 				case NUM_ERR:
 					msgq(sp, M_SYSERR,
 					    "036|set: %s option: %s", p2, t2);
 					break;
 				case NUM_OVER:
 					msgq(sp, M_ERR,
 			    "037|set: %s option: %s: value overflow", p2, t2);
 					break;
 				case NUM_OK:
 				case NUM_UNDER:
 					abort();
 				}
 				if (nf)
 					FREE_SPACE(sp, p2, 0);
 				if (nf2)
 					FREE_SPACE(sp, t2, 0);
 				rval = 1;
 				break;
 			}
 			if (*endp && !cmdskip(*endp)) {
 badnum:				INT2CHAR(sp, name, STRLEN(name) + 1, 
 					     np, nlen);
 				p2 = msg_print(sp, np, &nf);
 				INT2CHAR(sp, sep, STRLEN(sep) + 1, 
 					     np, nlen);
 				t2 = msg_print(sp, np, &nf2);
 				msgq(sp, M_ERR,
 		    "038|set: %s option: %s is an illegal number", p2, t2);
 				if (nf)
 					FREE_SPACE(sp, p2, 0);
 				if (nf2)
 					FREE_SPACE(sp, t2, 0);
 				rval = 1;
 				break;
 			}
 
 			/* Some options may never be set to zero. */
 			if (F_ISSET(op, OPT_NOZERO) && value == 0) {
 				msgq_wstr(sp, M_ERR, name,
 			    "314|set: the %s option may never be set to 0");
 				rval = 1;
 				break;
 			}
 
 			/*
 			 * Do nothing if the value is unchanged, the underlying
 			 * functions can be expensive.
 			 */
 			if (!F_ISSET(op, OPT_ALWAYS) &&
 			    O_VAL(sp, offset) == value)
 				break;
 
 			/* Report to subsystems. */
 			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
 			if ((op->func != NULL &&
 			    op->func(sp, spo, np, &value)) ||
 			    ex_optchange(sp, offset, np, &value) ||
 			    v_optchange(sp, offset, np, &value) ||
 			    sp->gp->scr_optchange(sp, offset, np, &value)) {
 				rval = 1;
 				break;
 			}
 
 			/* Set the value. */
 			if (o_set(sp, offset, 0, NULL, value))
 				rval = 1;
 			break;
 		case OPT_STR:
 			if (turnoff) {
 				msgq_wstr(sp, M_ERR, name,
 				    "039|set: %s option isn't a boolean");
 				rval = 1;
 				break;
 			}
 			if (qmark || !equals) {
 				if (!disp)
 					disp = SELECT_DISPLAY;
 				F_SET(spo, OPT_SELECTED);
 				break;
 			}
 
 			/* Check for strings that must have even length. */
 			if (F_ISSET(op, OPT_PAIRS) && STRLEN(sep) & 1) {
 				msgq_wstr(sp, M_ERR, name,
 				    "047|The %s option must be in two character groups");
 				rval = 1;
 				break;
 			}
 
 			/*
 			 * Do nothing if the value is unchanged, the underlying
 			 * functions can be expensive.
 			 */
 			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
 			if (!F_ISSET(op, OPT_ALWAYS) &&
 			    O_STR(sp, offset) != NULL &&
 			    !strcmp(O_STR(sp, offset), np))
 				break;
 
 			/* Report to subsystems. */
 			if ((op->func != NULL &&
 			    op->func(sp, spo, np, NULL)) ||
 			    ex_optchange(sp, offset, np, NULL) ||
 			    v_optchange(sp, offset, np, NULL) ||
 			    sp->gp->scr_optchange(sp, offset, np, NULL)) {
 				rval = 1;
 				break;
 			}
 
 			/* Set the value. */
 			if (o_set(sp, offset, OS_STRDUP, np, 0))
 				rval = 1;
 			break;
 		default:
 			abort();
 		}
 	}
 	if (disp != NO_DISPLAY)
 		opts_dump(sp, disp);
 	return (rval);
 }
 
 /*
  * o_set --
  *	Set an option's value.
  *
  * PUBLIC: int o_set(SCR *, int, u_int, char *, u_long);
  */
 int
 o_set(SCR *sp, int opt, u_int flags, char *str, u_long val)
 {
 	OPTION *op;
 
 	/* Set a pointer to the options area. */
 	op = F_ISSET(&sp->opts[opt], OPT_GLOBAL) ?
 	    &sp->gp->opts[sp->opts[opt].o_cur.val] : &sp->opts[opt];
 
 	/* Copy the string, if requested. */
 	if (LF_ISSET(OS_STRDUP) && (str = strdup(str)) == NULL) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 
 	/* Free the previous string, if requested, and set the value. */
 	if LF_ISSET(OS_DEF)
 		if (LF_ISSET(OS_STR | OS_STRDUP)) {
 			if (!LF_ISSET(OS_NOFREE))
 				free(op->o_def.str);
 			op->o_def.str = str;
 		} else
 			op->o_def.val = val;
 	else
 		if (LF_ISSET(OS_STR | OS_STRDUP)) {
 			if (!LF_ISSET(OS_NOFREE))
 				free(op->o_cur.str);
 			op->o_cur.str = str;
 		} else
 			op->o_cur.val = val;
 	return (0);
 }
 
 /*
  * opts_empty --
  *	Return 1 if the string option is invalid, 0 if it's OK.
  *
  * PUBLIC: int opts_empty(SCR *, int, int);
  */
 int
 opts_empty(SCR *sp, int off, int silent)
 {
 	char *p;
 
 	if ((p = O_STR(sp, off)) == NULL || p[0] == '\0') {
 		if (!silent)
 			msgq_wstr(sp, M_ERR, optlist[off].name,
 			    "305|No %s edit option specified");
 		return (1);
 	}
 	return (0);
 }
 
 /*
  * opts_dump --
  *	List the current values of selected options.
  *
  * PUBLIC: void opts_dump(SCR *, enum optdisp);
  */
 void
 opts_dump(SCR *sp, enum optdisp type)
 {
 	OPTLIST const *op;
 	int base, b_num, cnt, col, colwidth, curlen, s_num;
 	int numcols, numrows, row;
 	int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
 	char nbuf[20];
 
 	/*
 	 * Options are output in two groups -- those that fit in a column and
 	 * those that don't.  Output is done on 6 character "tab" boundaries
 	 * for no particular reason.  (Since we don't output tab characters,
 	 * we can ignore the terminal's tab settings.)  Ignore the user's tab
 	 * setting because we have no idea how reasonable it is.
 	 *
 	 * Find a column width we can live with, testing from 10 columns to 1.
 	 */
 	for (numcols = 10; numcols > 1; --numcols) {
 		colwidth = sp->cols / numcols & ~(STANDARD_TAB - 1);
 		if (colwidth >= 10) {
 			colwidth =
 			    (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
 			numcols = sp->cols / colwidth;
 			break;
 		}
 		colwidth = 0;
 	}
 
 	/*
 	 * Get the set of options to list, entering them into
 	 * the column list or the overflow list.
 	 */
 	for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
 		cnt = op - optlist;
 
 		/* If OPT_NDISP set, it's never displayed. */
 		if (F_ISSET(op, OPT_NDISP))
 			continue;
 
 		switch (type) {
 		case ALL_DISPLAY:		/* Display all. */
 			break;
 		case CHANGED_DISPLAY:		/* Display changed. */
 			/* If OPT_ADISP set, it's always "changed". */
 			if (F_ISSET(op, OPT_ADISP))
 				break;
 			switch (op->type) {
 			case OPT_0BOOL:
 			case OPT_1BOOL:
 			case OPT_NUM:
 				if (O_VAL(sp, cnt) == O_D_VAL(sp, cnt))
 					continue;
 				break;
 			case OPT_STR:
 				if (O_STR(sp, cnt) == O_D_STR(sp, cnt) ||
 				    (O_D_STR(sp, cnt) != NULL &&
 				    !strcmp(O_STR(sp, cnt), O_D_STR(sp, cnt))))
 					continue;
 				break;
 			}
 			break;
 		case SELECT_DISPLAY:		/* Display selected. */
 			if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
 				continue;
 			break;
 		default:
 		case NO_DISPLAY:
 			abort();
 		}
 		F_CLR(&sp->opts[cnt], OPT_SELECTED);
 
 		curlen = STRLEN(op->name);
 		switch (op->type) {
 		case OPT_0BOOL:
 		case OPT_1BOOL:
 			if (!O_ISSET(sp, cnt))
 				curlen += 2;
 			break;
 		case OPT_NUM:
 			(void)snprintf(nbuf,
 			    sizeof(nbuf), "%ld", O_VAL(sp, cnt));
 			curlen += strlen(nbuf);
 			break;
 		case OPT_STR:
 			if (O_STR(sp, cnt) != NULL)
 				curlen += strlen(O_STR(sp, cnt));
 			curlen += 3;
 			break;
 		}
 		/* Offset by 2 so there's a gap. */
 		if (curlen <= colwidth - 2)
 			s_op[s_num++] = cnt;
 		else
 			b_op[b_num++] = cnt;
 	}
 
 	if (s_num > 0) {
 		/* Figure out the number of rows. */
 		if (s_num > numcols) {
 			numrows = s_num / numcols;
 			if (s_num % numcols)
 				++numrows;
 		} else
 			numrows = 1;
 
 		/* Display the options in sorted order. */
 		for (row = 0; row < numrows;) {
 			for (base = row, col = 0; col < numcols; ++col) {
 				cnt = opts_print(sp, &optlist[s_op[base]]);
 				if ((base += numrows) >= s_num)
 					break;
 				(void)ex_printf(sp, "%*s",
 				    (int)(colwidth - cnt), "");
 			}
 			if (++row < numrows || b_num)
 				(void)ex_puts(sp, "\n");
 		}
 	}
 
 	for (row = 0; row < b_num;) {
 		(void)opts_print(sp, &optlist[b_op[row]]);
 		if (++row < b_num)
 			(void)ex_puts(sp, "\n");
 	}
 	(void)ex_puts(sp, "\n");
 }
 
 /*
  * opts_print --
  *	Print out an option.
  */
 static int
 opts_print(SCR *sp, OPTLIST const *op)
 {
 	int curlen, offset;
+	const char *p;
 
 	curlen = 0;
 	offset = op - optlist;
 	switch (op->type) {
 	case OPT_0BOOL:
 	case OPT_1BOOL:
 		curlen += ex_printf(sp,
 		    "%s"WS, O_ISSET(sp, offset) ? "" : "no", op->name);
 		break;
 	case OPT_NUM:
 		curlen += ex_printf(sp, WS"=%ld", op->name, O_VAL(sp, offset));
 		break;
 	case OPT_STR:
-		curlen += ex_printf(sp, WS"=\"%s\"", op->name,
-		    O_STR(sp, offset) == NULL ? "" : O_STR(sp, offset));
+		curlen += ex_printf(sp, WS"=\"", op->name);
+		p = O_STR(sp, offset);
+		/* Keep correct count for unprintable character sequences */
+		if (p != NULL)
+			for (; *p != '\0'; ++p)
+				curlen += ex_puts(sp, unctrl(*p));
+		curlen += ex_puts(sp, "\"");
 		break;
 	}
 	return (curlen);
 }
 
 /*
  * opts_save --
  *	Write the current configuration to a file.
  *
  * PUBLIC: int opts_save(SCR *, FILE *);
  */
 int
 opts_save(SCR *sp, FILE *fp)
 {
 	OPTLIST const *op;
 	CHAR_T ch, *p;
 	char nch, *np;
 	int cnt;
 
 	for (op = optlist; op->name != NULL; ++op) {
 		if (F_ISSET(op, OPT_NOSAVE))
 			continue;
 		cnt = op - optlist;
 		switch (op->type) {
 		case OPT_0BOOL:
 		case OPT_1BOOL:
 			if (O_ISSET(sp, cnt))
 				(void)fprintf(fp, "set "WS"\n", op->name);
 			else
 				(void)fprintf(fp, "set no"WS"\n", op->name);
 			break;
 		case OPT_NUM:
 			(void)fprintf(fp,
 			    "set "WS"=%-3ld\n", op->name, O_VAL(sp, cnt));
 			break;
 		case OPT_STR:
 			if (O_STR(sp, cnt) == NULL)
 				break;
 			(void)fprintf(fp, "set ");
 			for (p = op->name; (ch = *p) != '\0'; ++p) {
 				if (cmdskip(ch) || ch == '\\')
 					(void)putc('\\', fp);
 				fprintf(fp, WC, ch);
 			}
 			(void)putc('=', fp);
 			for (np = O_STR(sp, cnt); (nch = *np) != '\0'; ++np) {
 				if (cmdskip(nch) || nch == '\\')
 					(void)putc('\\', fp);
 				(void)putc(nch, fp);
 			}
 			(void)putc('\n', fp);
 			break;
 		}
 		if (ferror(fp)) {
 			msgq(sp, M_SYSERR, NULL);
 			return (1);
 		}
 	}
 	return (0);
 }
 
 /* 
  * opts_search --
  *	Search for an option.
  *
  * PUBLIC: OPTLIST const *opts_search(CHAR_T *);
  */
 OPTLIST const *
 opts_search(CHAR_T *name)
 {
 	OPTLIST const *op, *found;
 	OABBREV atmp, *ap;
 	OPTLIST otmp;
 	size_t len;
 
 	/* Check list of abbreviations. */
 	atmp.name = name;
 	if ((ap = bsearch(&atmp, abbrev, sizeof(abbrev) / sizeof(OABBREV) - 1,
 	    sizeof(OABBREV), opts_abbcmp)) != NULL)
 		return (optlist + ap->offset);
 
 	/* Check list of options. */
 	otmp.name = name;
 	if ((op = bsearch(&otmp, optlist, sizeof(optlist) / sizeof(OPTLIST) - 1,
 	    sizeof(OPTLIST), opts_cmp)) != NULL)
 		return (op);
 		
 	/*
 	 * Check to see if the name is the prefix of one (and only one)
 	 * option.  If so, return the option.
 	 */
 	len = STRLEN(name);
 	for (found = NULL, op = optlist; op->name != NULL; ++op) {
 		if (op->name[0] < name[0])
 			continue;
 		if (op->name[0] > name[0])
 			break;
 		if (!MEMCMP(op->name, name, len)) {
 			if (found != NULL)
 				return (NULL);
 			found = op;
 		}
 	}
 	return (found);
 }
 
 /* 
  * opts_nomatch --
  *	Standard nomatch error message for options.
  *
  * PUBLIC: void opts_nomatch(SCR *, CHAR_T *);
  */
 void
 opts_nomatch(SCR *sp, CHAR_T *name)
 {
 	msgq_wstr(sp, M_ERR, name,
 	    "033|set: no %s option: 'set all' gives all option values");
 }
 
 static int
 opts_abbcmp(const void *a, const void *b)
 {
 	return(STRCMP(((OABBREV *)a)->name, ((OABBREV *)b)->name));
 }
 
 static int
 opts_cmp(const void *a, const void *b)
 {
 	return(STRCMP(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
 }
 
 /*
  * opts_copy --
  *	Copy a screen's OPTION array.
  *
  * PUBLIC: int opts_copy(SCR *, SCR *);
  */
 int
 opts_copy(SCR *orig, SCR *sp)
 {
 	int cnt, rval;
 
 	/* Copy most everything without change. */
 	memcpy(sp->opts, orig->opts, sizeof(orig->opts));
 
 	/* Copy the string edit options. */
 	for (cnt = rval = 0; cnt < O_OPTIONCOUNT; ++cnt) {
 		if (optlist[cnt].type != OPT_STR ||
 		    F_ISSET(&sp->opts[cnt], OPT_GLOBAL))
 			continue;
 		/*
 		 * If never set, or already failed, NULL out the entries --
 		 * have to continue after failure, otherwise would have two
 		 * screens referencing the same memory.
 		 */
 		if (rval || O_STR(sp, cnt) == NULL) {
 			o_set(sp, cnt, OS_NOFREE | OS_STR, NULL, 0);
 			o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
 			continue;
 		}
 
 		/* Copy the current string. */
 		if (o_set(sp, cnt, OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0)) {
 			o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
 			goto nomem;
 		}
 
 		/* Copy the default string. */
 		if (O_D_STR(sp, cnt) != NULL && o_set(sp, cnt,
 		    OS_DEF | OS_NOFREE | OS_STRDUP, O_D_STR(sp, cnt), 0)) {
 nomem:			msgq(orig, M_SYSERR, NULL);
 			rval = 1;
 		}
 	}
 	return (rval);
 }
 
 /*
  * opts_free --
  *	Free all option strings
  *
  * PUBLIC: void opts_free(SCR *);
  */
 void
 opts_free(SCR *sp)
 {
 	int cnt;
 
 	for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) {
 		if (optlist[cnt].type != OPT_STR ||
 		    F_ISSET(&sp->opts[cnt], OPT_GLOBAL))
 			continue;
 		free(O_STR(sp, cnt));
 		free(O_D_STR(sp, cnt));
 	}
 }
Index: head/contrib/nvi/common/put.c
===================================================================
--- head/contrib/nvi/common/put.c	(revision 366308)
+++ head/contrib/nvi/common/put.c	(revision 366309)
@@ -1,223 +1,224 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 
 /*
  * put --
  *	Put text buffer contents into the file.
  *
  * PUBLIC: int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int);
  */
 int
 put(SCR *sp, CB *cbp, CHAR_T *namep, MARK *cp, MARK *rp, int append)
 {
 	CHAR_T name;
 	TEXT *ltp, *tp;
 	recno_t lno;
 	size_t blen, clen, len;
 	int rval;
 	CHAR_T *bp, *t;
 	CHAR_T *p;
 
-	if (cbp == NULL)
+	if (cbp == NULL) {
 		if (namep == NULL) {
 			cbp = sp->gp->dcbp;
 			if (cbp == NULL) {
 				msgq(sp, M_ERR,
 				    "053|The default buffer is empty");
 				return (1);
 			}
 		} else {
 			name = *namep;
 			CBNAME(sp, cbp, name);
 			if (cbp == NULL) {
 				msgq(sp, M_ERR, "054|Buffer %s is empty",
 				    KEY_NAME(sp, name));
 				return (1);
 			}
 		}
+	}
 	tp = TAILQ_FIRST(cbp->textq);
 
 	/*
 	 * It's possible to do a put into an empty file, meaning that the cut
 	 * buffer simply becomes the file.  It's a special case so that we can
 	 * ignore it in general.
 	 *
 	 * !!!
 	 * Historically, pasting into a file with no lines in vi would preserve
 	 * the single blank line.  This is surely a result of the fact that the
 	 * historic vi couldn't deal with a file that had no lines in it.  This
 	 * implementation treats that as a bug, and does not retain the blank
 	 * line.
 	 *
 	 * Historical practice is that the cursor ends at the first character
 	 * in the file.
 	 */
 	if (cp->lno == 1) {
 		if (db_last(sp, &lno))
 			return (1);
 		if (lno == 0) {
 			for (; tp != NULL;
 			    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
 				if (db_append(sp, 1, lno, tp->lb, tp->len))
 					return (1);
 			rp->lno = 1;
 			rp->cno = 0;
 			return (0);
 		}
 	}
 
 	/* If a line mode buffer, append each new line into the file. */
 	if (F_ISSET(cbp, CB_LMODE)) {
 		lno = append ? cp->lno : cp->lno - 1;
 		rp->lno = lno + 1;
 		for (; tp != NULL;
 		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
 			if (db_append(sp, 1, lno, tp->lb, tp->len))
 				return (1);
 		rp->cno = 0;
 		(void)nonblank(sp, rp->lno, &rp->cno);
 		return (0);
 	}
 
 	/*
 	 * If buffer was cut in character mode, replace the current line with
 	 * one built from the portion of the first line to the left of the
 	 * split plus the first line in the CB.  Append each intermediate line
 	 * in the CB.  Append a line built from the portion of the first line
 	 * to the right of the split plus the last line in the CB.
 	 *
 	 * Get the first line.
 	 */
 	lno = cp->lno;
 	if (db_get(sp, lno, DBG_FATAL, &p, &len))
 		return (1);
 
 	GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
 	t = bp;
 
 	/* Original line, left of the split. */
 	if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
 		MEMCPY(bp, p, clen);
 		p += clen;
 		t += clen;
 	}
 
 	/* First line from the CB. */
 	if (tp->len != 0) {
 		MEMCPY(t, tp->lb, tp->len);
 		t += tp->len;
 	}
 
 	/* Calculate length left in the original line. */
 	clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
 
 	/*
 	 * !!!
 	 * In the historical 4BSD version of vi, character mode puts within
 	 * a single line have two cursor behaviors: if the put is from the
 	 * unnamed buffer, the cursor moves to the character inserted which
 	 * appears last in the file.  If the put is from a named buffer,
 	 * the cursor moves to the character inserted which appears first
 	 * in the file.  In System III/V, it was changed at some point and
 	 * the cursor always moves to the first character.  In both versions
 	 * of vi, character mode puts that cross line boundaries leave the
 	 * cursor on the first character.  Nvi implements the System III/V
 	 * behavior, and expect POSIX.2 to do so as well.
 	 */
 	rp->lno = lno;
 	rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);
 
 	/*
 	 * If no more lines in the CB, append the rest of the original
 	 * line and quit.  Otherwise, build the last line before doing
 	 * the intermediate lines, because the line changes will lose
 	 * the cached line.
 	 */
 	if (TAILQ_NEXT(tp, q) == NULL) {
 		if (clen > 0) {
 			MEMCPY(t, p, clen);
 			t += clen;
 		}
 		if (db_set(sp, lno, bp, t - bp))
 			goto err;
 		if (sp->rptlchange != lno) {
 			sp->rptlchange = lno;
 			++sp->rptlines[L_CHANGED];
 		}
 	} else {
 		/*
 		 * Have to build both the first and last lines of the
 		 * put before doing any sets or we'll lose the cached
 		 * line.  Build both the first and last lines in the
 		 * same buffer, so we don't have to have another buffer
 		 * floating around.
 		 *
 		 * Last part of original line; check for space, reset
 		 * the pointer into the buffer.
 		 */
 		ltp = TAILQ_LAST(cbp->textq, _texth);
 		len = t - bp;
 		ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
 		t = bp + len;
 
 		/* Add in last part of the CB. */
 		MEMCPY(t, ltp->lb, ltp->len);
 		if (clen)
 			MEMCPY(t + ltp->len, p, clen);
 		clen += ltp->len;
 
 		/*
 		 * Now: bp points to the first character of the first
 		 * line, t points to the last character of the last
 		 * line, t - bp is the length of the first line, and
 		 * clen is the length of the last.  Just figured you'd
 		 * want to know.
 		 *
 		 * Output the line replacing the original line.
 		 */
 		if (db_set(sp, lno, bp, t - bp))
 			goto err;
 		if (sp->rptlchange != lno) {
 			sp->rptlchange = lno;
 			++sp->rptlines[L_CHANGED];
 		}
 
 		/* Output any intermediate lines in the CB. */
 		for (tp = TAILQ_NEXT(tp, q); TAILQ_NEXT(tp, q) != NULL;
 		    ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q))
 			if (db_append(sp, 1, lno, tp->lb, tp->len))
 				goto err;
 
 		if (db_append(sp, 1, lno, t, clen))
 			goto err;
 		++sp->rptlines[L_ADDED];
 	}
 	rval = 0;
 
 	if (0)
 err:		rval = 1;
 
 	FREE_SPACEW(sp, bp, blen);
 	return (rval);
 }
Index: head/contrib/nvi/common/recover.c
===================================================================
--- head/contrib/nvi/common/recover.c	(revision 366308)
+++ head/contrib/nvi/common/recover.c	(revision 366309)
@@ -1,941 +1,940 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 /*
  * We include <sys/file.h>, because the open #defines were found there
  * on historical systems.  We also include <fcntl.h> because the open(2)
  * #defines are found there on newer systems.
  */
 #include <sys/file.h>
 
 #include <bitstring.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <pwd.h>
 #include <netinet/in.h>		/* Required by resolv.h. */
 #include <resolv.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "../ex/version.h"
 #include "common.h"
 #include "pathnames.h"
 
 /*
  * Recovery code.
  *
  * The basic scheme is as follows.  In the EXF structure, we maintain full
  * paths of a b+tree file and a mail recovery file.  The former is the file
  * used as backing store by the DB package.  The latter is the file that
  * contains an email message to be sent to the user if we crash.  The two
  * simple states of recovery are:
  *
  *	+ first starting the edit session:
  *		the b+tree file exists and is mode 700, the mail recovery
  *		file doesn't exist.
  *	+ after the file has been modified:
  *		the b+tree file exists and is mode 600, the mail recovery
  *		file exists, and is exclusively locked.
  *
  * In the EXF structure we maintain a file descriptor that is the locked
  * file descriptor for the mail recovery file.
  *
  * To find out if a recovery file/backing file pair are in use, try to get
  * a lock on the recovery file.
  *
  * To find out if a backing file can be deleted at boot time, check for an
  * owner execute bit.  (Yes, I know it's ugly, but it's either that or put
  * special stuff into the backing file itself, or correlate the files at
  * boot time, neither of which looks like fun.)  Note also that there's a
  * window between when the file is created and the X bit is set.  It's small,
  * but it's there.  To fix the window, check for 0 length files as well.
  *
  * To find out if a file can be recovered, check the F_RCV_ON bit.  Note,
  * this DOES NOT mean that any initialization has been done, only that we
  * haven't yet failed at setting up or doing recovery.
  *
  * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
  * If that bit is not set when ending a file session:
  *	If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
  *	they are unlink(2)'d, and free(3)'d.
  *	If the EXF file descriptor (rcv_fd) is not -1, it is closed.
  *
  * The backing b+tree file is set up when a file is first edited, so that
  * the DB package can use it for on-disk caching and/or to snapshot the
  * file.  When the file is first modified, the mail recovery file is created,
  * the backing file permissions are updated, the file is sync(2)'d to disk,
  * and the timer is started.  Then, at RCV_PERIOD second intervals, the
  * b+tree file is synced to disk.  RCV_PERIOD is measured using SIGALRM, which
  * means that the data structures (SCR, EXF, the underlying tree structures)
  * must be consistent when the signal arrives.
  *
  * The recovery mail file contains normal mail headers, with two additional
  *
  *	X-vi-data: <file|path>;<base64 encoded path>
  *
  * MIME headers; the folding character is limited to ' '.
  *
  * Btree files are named "vi.XXXXXX" and recovery files are named
  * "recover.XXXXXX".
  */
 
 #define	VI_DHEADER	"X-vi-data:"
 
 static int	 rcv_copy(SCR *, int, char *);
 static void	 rcv_email(SCR *, char *);
 static int	 rcv_mailfile(SCR *, int, char *);
 static int	 rcv_mktemp(SCR *, char *, char *);
 static int	 rcv_dlnwrite(SCR *, const char *, const char *, FILE *);
 static int	 rcv_dlnread(SCR *, char **, char **, FILE *);
 
 /*
  * rcv_tmp --
  *	Build a file name that will be used as the recovery file.
  *
  * PUBLIC: int rcv_tmp(SCR *, EXF *, char *);
  */
 int
 rcv_tmp(SCR *sp, EXF *ep, char *name)
 {
 	struct stat sb;
 	int fd;
 	char *dp, *path;
 
 	/*
 	 * !!!
 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
 	 *
 	 *
 	 * If the recovery directory doesn't exist, try and create it.  As
 	 * the recovery files are themselves protected from reading/writing
 	 * by other than the owner, the worst that can happen is that a user
 	 * would have permission to remove other user's recovery files.  If
 	 * the sticky bit has the BSD semantics, that too will be impossible.
 	 */
 	if (opts_empty(sp, O_RECDIR, 0))
 		goto err;
 	dp = O_STR(sp, O_RECDIR);
 	if (stat(dp, &sb)) {
 		if (errno != ENOENT || mkdir(dp, 0)) {
 			msgq(sp, M_SYSERR, "%s", dp);
 			goto err;
 		}
 		(void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
 	}
 
 	if ((path = join(dp, "vi.XXXXXX")) == NULL)
 		goto err;
 	if ((fd = rcv_mktemp(sp, path, dp)) == -1) {
 		free(path);
 		goto err;
 	}
 	(void)fchmod(fd, S_IRWXU);
 	(void)close(fd);
 
 	ep->rcv_path = path;
 	if (0) {
 err:		msgq(sp, M_ERR,
 		    "056|Modifications not recoverable if the session fails");
 		return (1);
 	}
 
 	/* We believe the file is recoverable. */
 	F_SET(ep, F_RCV_ON);
 	return (0);
 }
 
 /*
  * rcv_init --
  *	Force the file to be snapshotted for recovery.
  *
  * PUBLIC: int rcv_init(SCR *);
  */
 int
 rcv_init(SCR *sp)
 {
 	EXF *ep;
 	recno_t lno;
 
 	ep = sp->ep;
 
 	/* Only do this once. */
 	F_CLR(ep, F_FIRSTMODIFY);
 
 	/* If we already know the file isn't recoverable, we're done. */
 	if (!F_ISSET(ep, F_RCV_ON))
 		return (0);
 
 	/* Turn off recoverability until we figure out if this will work. */
 	F_CLR(ep, F_RCV_ON);
 
 	/* Test if we're recovering a file, not editing one. */
 	if (ep->rcv_mpath == NULL) {
 		/* Build a file to mail to the user. */
 		if (rcv_mailfile(sp, 0, NULL))
 			goto err;
 
 		/* Force a read of the entire file. */
 		if (db_last(sp, &lno))
 			goto err;
 
 		/* Turn on a busy message, and sync it to backing store. */
 		sp->gp->scr_busy(sp,
 		    "057|Copying file for recovery...", BUSY_ON);
 		if (ep->db->sync(ep->db, R_RECNOSYNC)) {
 			msgq_str(sp, M_SYSERR, ep->rcv_path,
 			    "058|Preservation failed: %s");
 			sp->gp->scr_busy(sp, NULL, BUSY_OFF);
 			goto err;
 		}
 		sp->gp->scr_busy(sp, NULL, BUSY_OFF);
 	}
 
 	/* Turn off the owner execute bit. */
 	(void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
 
 	/* We believe the file is recoverable. */
 	F_SET(ep, F_RCV_ON);
 	return (0);
 
 err:	msgq(sp, M_ERR,
 	    "059|Modifications not recoverable if the session fails");
 	return (1);
 }
 
 /*
  * rcv_sync --
  *	Sync the file, optionally:
  *		flagging the backup file to be preserved
  *		snapshotting the backup file and send email to the user
  *		sending email to the user if the file was modified
  *		ending the file session
  *
  * PUBLIC: int rcv_sync(SCR *, u_int);
  */
 int
 rcv_sync(SCR *sp, u_int flags)
 {
 	EXF *ep;
 	int fd, rval;
 	char *dp, *buf;
 
 	/* Make sure that there's something to recover/sync. */
 	ep = sp->ep;
 	if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
 		return (0);
 
 	/* Sync the file if it's been modified. */
 	if (F_ISSET(ep, F_MODIFIED)) {
 		if (ep->db->sync(ep->db, R_RECNOSYNC)) {
 			F_CLR(ep, F_RCV_ON | F_RCV_NORM);
 			msgq_str(sp, M_SYSERR,
 			    ep->rcv_path, "060|File backup failed: %s");
 			return (1);
 		}
 
 		/* REQUEST: don't remove backing file on exit. */
 		if (LF_ISSET(RCV_PRESERVE))
 			F_SET(ep, F_RCV_NORM);
 
 		/* REQUEST: send email. */
 		if (LF_ISSET(RCV_EMAIL))
 			rcv_email(sp, ep->rcv_mpath);
 	}
 
 	/*
 	 * !!!
 	 * Each time the user exec's :preserve, we have to snapshot all of
 	 * the recovery information, i.e. it's like the user re-edited the
 	 * file.  We copy the DB(3) backing file, and then create a new mail
 	 * recovery file, it's simpler than exiting and reopening all of the
 	 * underlying files.
 	 *
 	 * REQUEST: snapshot the file.
 	 */
 	rval = 0;
 	if (LF_ISSET(RCV_SNAPSHOT)) {
 		if (opts_empty(sp, O_RECDIR, 0))
 			goto err;
 		dp = O_STR(sp, O_RECDIR);
 		if ((buf = join(dp, "vi.XXXXXX")) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			goto err;
 		}
 		if ((fd = rcv_mktemp(sp, buf, dp)) == -1) {
 			free(buf);
 			goto err;
 		}
 		sp->gp->scr_busy(sp,
 		    "061|Copying file for recovery...", BUSY_ON);
 		if (rcv_copy(sp, fd, ep->rcv_path) ||
 		    close(fd) || rcv_mailfile(sp, 1, buf)) {
 			(void)unlink(buf);
 			(void)close(fd);
 			rval = 1;
 		}
 		free(buf);
 		sp->gp->scr_busy(sp, NULL, BUSY_OFF);
 	}
 	if (0) {
 err:		rval = 1;
 	}
 
 	/* REQUEST: end the file session. */
 	if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
 		rval = 1;
 
 	return (rval);
 }
 
 /*
  * rcv_mailfile --
  *	Build the file to mail to the user.
  */
 static int
 rcv_mailfile(SCR *sp, int issync, char *cp_path)
 {
 	EXF *ep;
 	GS *gp;
 	struct passwd *pw;
 	int len;
 	time_t now;
 	uid_t uid;
 	int fd;
 	FILE *fp;
 	char *dp, *p, *t, *qt, *buf, *mpath;
 	char *t1, *t2, *t3;
 	int st;
 
 	/*
 	 * XXX
 	 * MAXHOSTNAMELEN/HOST_NAME_MAX are deprecated. We try sysconf(3)
 	 * first, then fallback to _POSIX_HOST_NAME_MAX.
 	 */
 	char *host;
 	long hostmax = sysconf(_SC_HOST_NAME_MAX);
 	if (hostmax < 0)
 		hostmax = _POSIX_HOST_NAME_MAX;
 
 	gp = sp->gp;
 	if ((pw = getpwuid(uid = getuid())) == NULL) {
 		msgq(sp, M_ERR,
 		    "062|Information on user id %u not found", uid);
 		return (1);
 	}
 
 	if (opts_empty(sp, O_RECDIR, 0))
 		return (1);
 	dp = O_STR(sp, O_RECDIR);
 	if ((mpath = join(dp, "recover.XXXXXX")) == NULL) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 	if ((fd = rcv_mktemp(sp, mpath, dp)) == -1) {
 		free(mpath);
 		return (1);
 	}
 	if ((fp = fdopen(fd, "w")) == NULL) {
 		free(mpath);
 		close(fd);
 		return (1);
 	}
 
 	/*
 	 * XXX
 	 * We keep an open lock on the file so that the recover option can
 	 * distinguish between files that are live and those that need to
 	 * be recovered.  There's an obvious window between the mkstemp call
 	 * and the lock, but it's pretty small.
 	 */
 	ep = sp->ep;
 	if (file_lock(sp, NULL, fd, 1) != LOCK_SUCCESS)
 		msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
 	if (!issync) {
 		/* Save the recover file descriptor, and mail path. */
 		ep->rcv_fd = dup(fd);
 		ep->rcv_mpath = mpath;
 		cp_path = ep->rcv_path;
 	}
 
 	t = sp->frp->name;
 	if ((p = strrchr(t, '/')) == NULL)
 		p = t;
 	else
 		++p;
 	(void)time(&now);
 
 	if ((st = rcv_dlnwrite(sp, "file", t, fp))) {
 		if (st == 1)
 			goto werr;
 		goto err;
 	}
 	if ((st = rcv_dlnwrite(sp, "path", cp_path, fp))) {
 		if (st == 1)
 			goto werr;
 		goto err;
 	}
 
 	MALLOC(sp, host, hostmax + 1);
 	if (host == NULL)
 		goto err;
 	(void)gethostname(host, hostmax + 1);
 
 	len = fprintf(fp, "%s%s%s\n%s%s%s%s\n%s%.40s\n%s\n\n",
 	    "From: root@", host, " (Nvi recovery program)",
 	    "To: ", pw->pw_name, "@", host,
 	    "Subject: Nvi saved the file ", p,
 	    "Precedence: bulk");		/* For vacation(1). */
 	if (len < 0) {
 		free(host);
 		goto werr;
 	}
 
 	if ((qt = quote(t)) == NULL) {
 		free(host);
 		msgq(sp, M_SYSERR, NULL);
 		goto err;
 	}
 	len = asprintf(&buf, "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
 	    "On ", ctime(&now), ", the user ", pw->pw_name,
 	    " was editing a file named ", t, " on the machine ",
 	    host, ", when it was saved for recovery. ",
 	    "You can recover most, if not all, of the changes ",
 	    "to this file using the -r option to ", getprogname(), ":\n\n\t",
 	    getprogname(), " -r ", qt);
 	free(qt);
 	free(host);
-	if (buf == NULL) {
+	if (len == -1) {
 		msgq(sp, M_SYSERR, NULL);
 		goto err;
 	}
 
 	/*
 	 * Format the message.  (Yes, I know it's silly.)
 	 * Requires that the message end in a <newline>.
 	 */
 #define	FMTCOLS	60
 	for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
 		/* Check for a short length. */
 		if (len <= FMTCOLS) {
 			t2 = t1 + (len - 1);
 			goto wout;
 		}
 
 		/* Check for a required <newline>. */
 		t2 = strchr(t1, '\n');
 		if (t2 - t1 <= FMTCOLS)
 			goto wout;
 
 		/* Find the closest space, if any. */
 		for (t3 = t2; t2 > t1; --t2)
 			if (*t2 == ' ') {
 				if (t2 - t1 <= FMTCOLS)
 					goto wout;
 				t3 = t2;
 			}
 		t2 = t3;
 
 		/* t2 points to the last character to display. */
 wout:		*t2++ = '\n';
 
 		/* t2 points one after the last character to display. */
 		if (fwrite(t1, 1, t2 - t1, fp) != t2 - t1) {
 			free(buf);
 			goto werr;
 		}
 	}
 
 	if (issync) {
 		fflush(fp);
 		rcv_email(sp, mpath);
 		free(mpath);
 	}
 	if (fclose(fp)) {
 		free(buf);
 werr:		msgq(sp, M_SYSERR, "065|Recovery file");
 		goto err;
 	}
 	free(buf);
 	return (0);
 
 err:	if (!issync)
 		ep->rcv_fd = -1;
 	if (fp != NULL)
 		(void)fclose(fp);
 	return (1);
 }
 
 /*
  *	people making love
  *	never exactly the same
  *	just like a snowflake
  *
  * rcv_list --
  *	List the files that can be recovered by this user.
  *
  * PUBLIC: int rcv_list(SCR *);
  */
 int
 rcv_list(SCR *sp)
 {
 	struct dirent *dp;
 	struct stat sb;
 	DIR *dirp;
 	FILE *fp;
 	int found;
 	char *p, *file, *path;
 	char *dtype, *data;
 	int st;
 
 	/* Open the recovery directory for reading. */
 	if (opts_empty(sp, O_RECDIR, 0))
 		return (1);
 	p = O_STR(sp, O_RECDIR);
 	if (chdir(p) || (dirp = opendir(".")) == NULL) {
 		msgq_str(sp, M_SYSERR, p, "recdir: %s");
 		return (1);
 	}
 
 	/* Read the directory. */
 	for (found = 0; (dp = readdir(dirp)) != NULL;) {
 		if (strncmp(dp->d_name, "recover.", 8))
 			continue;
 
 		/* If it's readable, it's recoverable. */
 		if ((fp = fopen(dp->d_name, "r")) == NULL)
 			continue;
 
 		switch (file_lock(sp, NULL, fileno(fp), 1)) {
 		case LOCK_FAILED:
 			/*
 			 * XXX
 			 * Assume that a lock can't be acquired, but that we
 			 * should permit recovery anyway.  If this is wrong,
 			 * and someone else is using the file, we're going to
 			 * die horribly.
 			 */
 			break;
 		case LOCK_SUCCESS:
 			break;
 		case LOCK_UNAVAIL:
 			/* If it's locked, it's live. */
 			(void)fclose(fp);
 			continue;
 		}
 
 		/* Check the headers. */
 		for (file = NULL, path = NULL;
 		    file == NULL || path == NULL;) {
 			if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
 				if (st == 1)
 					msgq_str(sp, M_ERR, dp->d_name,
 					    "066|%s: malformed recovery file");
 				goto next;
 			}
 			if (dtype == NULL)
 				continue;
 			if (!strcmp(dtype, "file"))
 				file = data;
 			else if (!strcmp(dtype, "path"))
 				path = data;
 			else
 				free(data);
 		}
 
 		/*
 		 * If the file doesn't exist, it's an orphaned recovery file,
 		 * toss it.
 		 *
 		 * XXX
 		 * This can occur if the backup file was deleted and we crashed
 		 * before deleting the email file.
 		 */
 		errno = 0;
 		if (stat(path, &sb) &&
 		    errno == ENOENT) {
 			(void)unlink(dp->d_name);
 			goto next;
 		}
 
 		/* Get the last modification time and display. */
 		(void)fstat(fileno(fp), &sb);
 		(void)printf("%.24s: %s\n",
 		    ctime(&sb.st_mtime), file);
 		found = 1;
 
 		/* Close, discarding lock. */
 next:		(void)fclose(fp);
 		free(file);
 		free(path);
 	}
 	if (found == 0)
 		(void)printf("%s: No files to recover\n", getprogname());
 	(void)closedir(dirp);
 	return (0);
 }
 
 /*
  * rcv_read --
  *	Start a recovered file as the file to edit.
  *
  * PUBLIC: int rcv_read(SCR *, FREF *);
  */
 int
 rcv_read(SCR *sp, FREF *frp)
 {
 	struct dirent *dp;
 	struct stat sb;
 	DIR *dirp;
 	FILE *fp;
 	EXF *ep;
 	struct timespec rec_mtim = { 0, 0 };
 	int found, locked = 0, requested, sv_fd;
 	char *name, *p, *t, *rp, *recp, *pathp;
 	char *file, *path, *recpath;
 	char *dtype, *data;
 	int st;
 
 	if (opts_empty(sp, O_RECDIR, 0))
 		return (1);
 	rp = O_STR(sp, O_RECDIR);
 	if ((dirp = opendir(rp)) == NULL) {
 		msgq_str(sp, M_SYSERR, rp, "%s");
 		return (1);
 	}
 
 	name = frp->name;
 	sv_fd = -1;
 	recp = pathp = NULL;
 	for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
 		if (strncmp(dp->d_name, "recover.", 8))
 			continue;
 		if ((recpath = join(rp, dp->d_name)) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			continue;
 		}
 
 		/* If it's readable, it's recoverable. */
 		if ((fp = fopen(recpath, "r")) == NULL) {
 			free(recpath);
 			continue;
 		}
 
 		switch (file_lock(sp, NULL, fileno(fp), 1)) {
 		case LOCK_FAILED:
 			/*
 			 * XXX
 			 * Assume that a lock can't be acquired, but that we
 			 * should permit recovery anyway.  If this is wrong,
 			 * and someone else is using the file, we're going to
 			 * die horribly.
 			 */
 			locked = 0;
 			break;
 		case LOCK_SUCCESS:
 			locked = 1;
 			break;
 		case LOCK_UNAVAIL:
 			/* If it's locked, it's live. */
 			(void)fclose(fp);
 			continue;
 		}
 
 		/* Check the headers. */
 		for (file = NULL, path = NULL;
 		    file == NULL || path == NULL;) {
 			if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
 				if (st == 1)
 					msgq_str(sp, M_ERR, dp->d_name,
 					    "067|%s: malformed recovery file");
 				goto next;
 			}
 			if (dtype == NULL)
 				continue;
 			if (!strcmp(dtype, "file"))
 				file = data;
 			else if (!strcmp(dtype, "path"))
 				path = data;
 			else
 				free(data);
 		}
 		++found;
 
 		/*
 		 * If the file doesn't exist, it's an orphaned recovery file,
 		 * toss it.
 		 *
 		 * XXX
 		 * This can occur if the backup file was deleted and we crashed
 		 * before deleting the email file.
 		 */
 		errno = 0;
 		if (stat(path, &sb) &&
 		    errno == ENOENT) {
 			(void)unlink(dp->d_name);
 			goto next;
 		}
 
 		/* Check the file name. */
 		if (strcmp(file, name))
 			goto next;
 
 		++requested;
 
 		/* If we've found more than one, take the most recent. */
 		(void)fstat(fileno(fp), &sb);
 		if (recp == NULL ||
-		    timespeccmp(&rec_mtim, &sb.st_mtimespec, <)) {
+		    timespeccmp(&rec_mtim, &sb.st_mtim, <)) {
 			p = recp;
 			t = pathp;
 			recp = recpath;
 			pathp = path;
 			if (p != NULL) {
 				free(p);
 				free(t);
 			}
-			rec_mtim = sb.st_mtimespec;
+			rec_mtim = sb.st_mtim;
 			if (sv_fd != -1)
 				(void)close(sv_fd);
 			sv_fd = dup(fileno(fp));
 		} else {
 next:			free(recpath);
 			free(path);
 		}
 		(void)fclose(fp);
 		free(file);
 	}
 	(void)closedir(dirp);
 
 	if (recp == NULL) {
 		msgq_str(sp, M_INFO, name,
 		    "068|No files named %s, readable by you, to recover");
 		return (1);
 	}
 	if (found) {
 		if (requested > 1)
 			msgq(sp, M_INFO,
 	    "069|There are older versions of this file for you to recover");
 		if (found > requested)
 			msgq(sp, M_INFO,
 			    "070|There are other files for you to recover");
 	}
 
 	/*
 	 * Create the FREF structure, start the btree file.
 	 *
 	 * XXX
 	 * file_init() is going to set ep->rcv_path.
 	 */
 	if (file_init(sp, frp, pathp, 0)) {
 		free(recp);
 		free(pathp);
 		(void)close(sv_fd);
 		return (1);
 	}
 	free(pathp);
 
 	/*
 	 * We keep an open lock on the file so that the recover option can
 	 * distinguish between files that are live and those that need to
 	 * be recovered.  The lock is already acquired, just copy it.
 	 */
 	ep = sp->ep;
 	ep->rcv_mpath = recp;
 	ep->rcv_fd = sv_fd;
 	if (!locked)
 		F_SET(frp, FR_UNLOCKED);
 
 	/* We believe the file is recoverable. */
 	F_SET(ep, F_RCV_ON);
 	return (0);
 }
 
 /*
  * rcv_copy --
  *	Copy a recovery file.
  */
 static int
 rcv_copy(SCR *sp, int wfd, char *fname)
 {
 	int nr, nw, off, rfd;
 	char buf[8 * 1024];
 
 	if ((rfd = open(fname, O_RDONLY, 0)) == -1)
 		goto err;
 	while ((nr = read(rfd, buf, sizeof(buf))) > 0)
 		for (off = 0; nr; nr -= nw, off += nw)
 			if ((nw = write(wfd, buf + off, nr)) < 0)
 				goto err;
 	if (nr == 0)
 		return (0);
 
 err:	msgq_str(sp, M_SYSERR, fname, "%s");
 	return (1);
 }
 
 /*
  * rcv_mktemp --
  *	Paranoid make temporary file routine.
  */
 static int
 rcv_mktemp(SCR *sp, char *path, char *dname)
 {
 	int fd;
 
 	if ((fd = mkstemp(path)) == -1)
 		msgq_str(sp, M_SYSERR, dname, "%s");
 	return (fd);
 }
 
 /*
  * rcv_email --
  *	Send email.
  */
 static void
 rcv_email(SCR *sp, char *fname)
 {
 	char *buf;
 
-	(void)asprintf(&buf, _PATH_SENDMAIL " -odb -t < %s", fname);
-	if (buf == NULL) {
+	if (asprintf(&buf, _PATH_SENDMAIL " -odb -t < %s", fname) == -1) {
 		msgq_str(sp, M_ERR, strerror(errno),
 		    "071|not sending email: %s");
 		return;
 	}
 	(void)system(buf);
 	free(buf);
 }
 
 /*
  * rcv_dlnwrite --
  *	Encode a string into an X-vi-data line and write it.
  */
 static int
 rcv_dlnwrite(SCR *sp, const char *dtype, const char *src, FILE *fp)
 {
 	char *bp = NULL, *p;
 	size_t blen = 0;
 	size_t dlen, len;
 	int plen, xlen;
 
 	len = strlen(src);
 	dlen = strlen(dtype);
 	GET_SPACE_GOTOC(sp, bp, blen, (len + 2) / 3 * 4 + dlen + 2);
 	(void)memcpy(bp, dtype, dlen);
 	bp[dlen] = ';';
 	if ((xlen = b64_ntop((u_char *)src,
 	    len, bp + dlen + 1, blen)) == -1)
 		goto err;
 	xlen += dlen + 1;
 
 	/* Output as an MIME folding header. */
 	if ((plen = fprintf(fp, VI_DHEADER " %.*s\n",
 	    FMTCOLS - (int)sizeof(VI_DHEADER), bp)) < 0)
 		goto err;
 	plen -= (int)sizeof(VI_DHEADER) + 1;
 	for (p = bp, xlen -= plen; xlen > 0; xlen -= plen) {
 		p += plen;
 		if ((plen = fprintf(fp, " %.*s\n", FMTCOLS - 1, p)) < 0)
 			goto err;
 		plen -= 2;
 	}
 	FREE_SPACE(sp, bp, blen);
 	return (0);
 
 err:	FREE_SPACE(sp, bp, blen);
 	return (1);
 alloc_err:
 	msgq(sp, M_SYSERR, NULL);
 	return (-1);
 }
 
 /*
  * rcv_dlnread --
  *	Read an X-vi-data line and decode it.
  */
 static int
 rcv_dlnread(SCR *sp, char **dtypep,
 	char **datap,		/* free *datap if != NULL after use. */
 	FILE *fp)
 {
 	int ch;
 	char buf[1024];
 	char *bp = NULL, *p, *src;
 	size_t blen = 0;
 	size_t len, off, dlen;
 	char *dtype, *data;
 	int xlen;
 
 	if (fgets(buf, sizeof(buf), fp) == NULL)
 		return (1);
 	if (strncmp(buf, VI_DHEADER, sizeof(VI_DHEADER) - 1)) {
 		*dtypep = NULL;
 		*datap = NULL;
 		return (0);
 	}
 
 	/* Fetch an MIME folding header. */
 	len = strlen(buf) - sizeof(VI_DHEADER) + 1;
 	GET_SPACE_GOTOC(sp, bp, blen, len);
 	(void)memcpy(bp, buf + sizeof(VI_DHEADER) - 1, len);
 	p = bp + len;
 	while ((ch = fgetc(fp)) == ' ') {
 		if (fgets(buf, sizeof(buf), fp) == NULL)
 			goto err;
 		off = strlen(buf);
 		len += off;
 		ADD_SPACE_GOTOC(sp, bp, blen, len);
 		p = bp + len - off;
 		(void)memcpy(p, buf, off);
 	}
 	bp[len] = '\0';
 	(void)ungetc(ch, fp);
 
 	for (p = bp; *p == ' ' || *p == '\n'; p++);
 	if ((src = strchr(p, ';')) == NULL)
 		goto err;
 	dlen = src - p;
 	src += 1;
 	len -= src - bp;
 
 	/* Memory looks like: "<data>\0<dtype>\0". */
 	MALLOC(sp, data, dlen + len / 4 * 3 + 2);
 	if (data == NULL)
 		goto err;
 	if ((xlen = (b64_pton(p + dlen + 1,
 	    (u_char *)data, len / 4 * 3 + 1))) == -1) {
 		free(data);
 		goto err;
 	}
 	data[xlen] = '\0';
 	dtype = data + xlen + 1;
 	(void)memcpy(dtype, p, dlen);
 	dtype[dlen] = '\0';
 	FREE_SPACE(sp, bp, blen);
 	*dtypep = dtype;
 	*datap = data;
 	return (0);
 
 err: 	FREE_SPACE(sp, bp, blen);
 	return (1);
 alloc_err:
 	msgq(sp, M_SYSERR, NULL);
 	return (-1);
 }
Index: head/contrib/nvi/common/util.c
===================================================================
--- head/contrib/nvi/common/util.c	(revision 366308)
+++ head/contrib/nvi/common/util.c	(revision 366309)
@@ -1,377 +1,383 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 
 #ifdef __APPLE__
 #include <mach/clock.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
 #endif
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "common.h"
 
 /*
  * binc --
  *	Increase the size of a buffer.
  *
  * PUBLIC: void *binc(SCR *, void *, size_t *, size_t);
  */
 void *
 binc(SCR *sp,			/* sp MAY BE NULL!!! */
     void *bp, size_t *bsizep, size_t min)
 {
 	size_t csize;
 
 	/* If already larger than the minimum, just return. */
 	if (min && *bsizep >= min)
 		return (bp);
 
 	csize = p2roundup(MAX(min, 256));
 	REALLOC(sp, bp, void *, csize);
 
 	if (bp == NULL) {
 		*bsizep = 0;
 		return (NULL);
 	}
 	/*
 	 * Memory is guaranteed to be zero-filled, various parts of
 	 * nvi depend on this.
 	 */
 	memset((char *)bp + *bsizep, 0, csize - *bsizep);
 	*bsizep = csize;
 	return (bp);
 }
 
 /*
  * nonblank --
  *	Set the column number of the first non-blank character
  *	including or after the starting column.  On error, set
  *	the column to 0, it's safest.
  *
  * PUBLIC: int nonblank(SCR *, recno_t, size_t *);
  */
 int
 nonblank(SCR *sp, recno_t lno, size_t *cnop)
 {
 	CHAR_T *p;
 	size_t cnt, len, off;
 	int isempty;
 
 	/* Default. */
 	off = *cnop;
 	*cnop = 0;
 
 	/* Get the line, succeeding in an empty file. */
 	if (db_eget(sp, lno, &p, &len, &isempty))
 		return (!isempty);
 
 	/* Set the offset. */
 	if (len == 0 || off >= len)
 		return (0);
 
 	for (cnt = off, p = &p[off],
 	    len -= off; len && ISBLANK(*p); ++cnt, ++p, --len);
 
 	/* Set the return. */
 	*cnop = len ? cnt : cnt - 1;
 	return (0);
 }
 
 /*
  * join --
  *	Join two paths; need free.
  *
  * PUBLIC: char *join(char *, char *);
  */
 char *
 join(char *path1, char *path2)
 {
 	char *p;
 
 	if (path1[0] == '\0' || path2[0] == '/')
 		return strdup(path2);
-	(void)asprintf(&p, path1[strlen(path1)-1] == '/' ?
-	    "%s%s" : "%s/%s", path1, path2);
+	if (asprintf(&p, path1[strlen(path1)-1] == '/' ?
+	    "%s%s" : "%s/%s", path1, path2) == -1)
+		return NULL;
 	return p;
 }
 
 /*
  * expanduser --
  *	Return a "~" or "~user" expanded path; need free.
  *
  * PUBLIC: char *expanduser(char *);
  */
 char *
 expanduser(char *str)
 {
 	struct passwd *pwd;
 	char *p, *t, *u, *h;
 
 	/*
 	 * This function always expands the content between the
 	 * leading '~' and the first '/' or '\0' from the input.
 	 * Return NULL whenever we fail to do so.
 	 */
 	if (*str != '~')
 		return (NULL);
 	p = str + 1;
 	for (t = p; *t != '/' && *t != '\0'; ++t)
 		continue;
 	if (t == p) {
 		/* ~ */
+#ifdef __GLIBC__
+		extern char *secure_getenv(const char *);
+		if ((h = secure_getenv("HOME")) == NULL) {
+#else
 		if (issetugid() != 0 ||
 		    (h = getenv("HOME")) == NULL) {
+#endif
 			if (((h = getlogin()) != NULL &&
 			     (pwd = getpwnam(h)) != NULL) ||
 			    (pwd = getpwuid(getuid())) != NULL)
 				h = pwd->pw_dir;
 			else
 				return (NULL);
 		}
 	} else {
 		/* ~user */
 		if ((u = strndup(p, t - p)) == NULL)
 			return (NULL);
 		if ((pwd = getpwnam(u)) == NULL) {
 			free(u);
 			return (NULL);
 		} else
 			h = pwd->pw_dir;
 		free(u);
 	}
 
 	for (; *t == '/' && *t != '\0'; ++t)
 		continue;
 	return (join(h, t));
 }
 
 /*
  * quote --
  *	Return a escaped string for /bin/sh; need free.
  *
  * PUBLIC: char *quote(char *);
  */
 char *
 quote(char *str)
 {
 	char *p, *t;
 	size_t i = 0, n = 0;
 	int unsafe = 0;
 
 	for (p = str; *p != '\0'; p++, i++) {
 		if (*p == '\'')
 			n++;
 		if (unsafe)
 			continue;
 		if (isascii((u_char)*p)) {
 			if (isalnum((u_char)*p))
 				continue;
 			switch (*p) {
 			case '%': case '+': case ',': case '-': case '.':
 			case '/': case ':': case '=': case '@': case '_':
 				continue;
 			}
 		}
 		unsafe = 1;
 	}
 	if (!unsafe)
 		t = strdup(str);
 #define SQT "'\\''"
 	else if ((p = t = malloc(i + n * (sizeof(SQT) - 2) + 3)) != NULL) {
 		*p++ = '\'';
 		for (; *str != '\0'; str++) {
 			if (*str == '\'') {
 				(void)memcpy(p, SQT, sizeof(SQT) - 1);
 				p += sizeof(SQT) - 1;
 			} else
 				*p++ = *str;
 		}
 		*p++ = '\'';
 		*p = '\0';
 	}
 	return t;
 }
 
 /*
  * v_strdup --
  *	Strdup for 8-bit character strings with an associated length.
  *
  * PUBLIC: char *v_strdup(SCR *, const char *, size_t);
  */
 char *
 v_strdup(SCR *sp, const char *str, size_t len)
 {
 	char *copy;
 
 	MALLOC(sp, copy, len + 1);
 	if (copy == NULL)
 		return (NULL);
 	memcpy(copy, str, len);
 	copy[len] = '\0';
 	return (copy);
 }
 
 /*
  * v_wstrdup --
  *	Strdup for wide character strings with an associated length.
  *
  * PUBLIC: CHAR_T *v_wstrdup(SCR *, const CHAR_T *, size_t);
  */
 CHAR_T *
 v_wstrdup(SCR *sp, const CHAR_T *str, size_t len)
 {
 	CHAR_T *copy;
 
 	MALLOC(sp, copy, (len + 1) * sizeof(CHAR_T));
 	if (copy == NULL)
 		return (NULL);
 	MEMCPY(copy, str, len);
 	copy[len] = '\0';
 	return (copy);
 }
 
 /*
  * nget_uslong --
  *      Get an unsigned long, checking for overflow.
  *
  * PUBLIC: enum nresult nget_uslong(u_long *, const CHAR_T *, CHAR_T **, int);
  */
 enum nresult
 nget_uslong(u_long *valp, const CHAR_T *p, CHAR_T **endp, int base)
 {
 	errno = 0;
 	*valp = STRTOUL(p, endp, base);
 	if (errno == 0)
 		return (NUM_OK);
 	if (errno == ERANGE && *valp == ULONG_MAX)
 		return (NUM_OVER);
 	return (NUM_ERR);
 }
 
 /*
  * nget_slong --
  *      Convert a signed long, checking for overflow and underflow.
  *
  * PUBLIC: enum nresult nget_slong(long *, const CHAR_T *, CHAR_T **, int);
  */
 enum nresult
 nget_slong(long *valp, const CHAR_T *p, CHAR_T **endp, int base)
 {
 	errno = 0;
 	*valp = STRTOL(p, endp, base);
 	if (errno == 0)
 		return (NUM_OK);
 	if (errno == ERANGE) {
 		if (*valp == LONG_MAX)
 			return (NUM_OVER);
 		if (*valp == LONG_MIN)
 			return (NUM_UNDER);
 	}
 	return (NUM_ERR);
 }
 
 /*
  * timepoint_steady --
  *      Get a timestamp from a monotonic clock.
  *
  * PUBLIC: void timepoint_steady(struct timespec *);
  */
 void
 timepoint_steady(struct timespec *ts)
 {
 #ifdef __APPLE__
 	static mach_timebase_info_data_t base = { 0 };
 	uint64_t val;
 	uint64_t ns;
 
 	if (base.denom == 0)
 		(void)mach_timebase_info(&base);
 
 	val = mach_absolute_time();
 	ns = val * base.numer / base.denom;
 	ts->tv_sec = ns / 1000000000;
 	ts->tv_nsec = ns % 1000000000;
 #else
 #ifdef CLOCK_MONOTONIC_FAST
 	(void)clock_gettime(CLOCK_MONOTONIC_FAST, ts);
 #else
 	(void)clock_gettime(CLOCK_MONOTONIC, ts);
 #endif
 #endif
 }
 
 /*
  * timepoint_system --
  *      Get the current calendar time.
  *
  * PUBLIC: void timepoint_system(struct timespec *);
  */
 void
 timepoint_system(struct timespec *ts)
 {
 #ifdef __APPLE__
 	clock_serv_t clk;
 	mach_timespec_t mts;
 	kern_return_t kr;
 
 	kr = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk);
 	if (kr != KERN_SUCCESS)
 		return;
 	(void)clock_get_time(clk, &mts);
 	(void)mach_port_deallocate(mach_task_self(), clk);
 	ts->tv_sec = mts.tv_sec;
 	ts->tv_nsec = mts.tv_nsec;
 #else
 #ifdef CLOCK_REALTIME_FAST
 	(void)clock_gettime(CLOCK_REALTIME_FAST, ts);
 #else
 	(void)clock_gettime(CLOCK_REALTIME, ts);
 #endif
 #endif
 }
 
 #ifdef DEBUG
 #include <stdarg.h>
 
 /*
  * TRACE --
  *	debugging trace routine.
  *
  * PUBLIC: void TRACE(SCR *, const char *, ...);
  */
 void
 TRACE(SCR *sp, const char *fmt, ...)
 {
 	FILE *tfp;
 	va_list ap;
 
 	if ((tfp = sp->gp->tracefp) == NULL)
 		return;
 	va_start(ap, fmt);
 	(void)vfprintf(tfp, fmt, ap);
 	va_end(ap);
 
 	(void)fflush(tfp);
 }
 #endif
Index: head/contrib/nvi/ex/ex.c
===================================================================
--- head/contrib/nvi/ex/ex.c	(revision 366308)
+++ head/contrib/nvi/ex/ex.c	(revision 366309)
@@ -1,2364 +1,2367 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "../vi/vi.h"
 
 #if defined(DEBUG) && defined(COMLOG)
 static void	ex_comlog(SCR *, EXCMD *);
 #endif
 static EXCMDLIST const *
 		ex_comm_search(CHAR_T *, size_t);
 static int	ex_discard(SCR *);
 static int	ex_line(SCR *, EXCMD *, MARK *, int *, int *);
 static int	ex_load(SCR *);
 static void	ex_unknown(SCR *, CHAR_T *, size_t);
 
 /*
  * ex --
  *	Main ex loop.
  *
  * PUBLIC: int ex(SCR **);
  */
 int
 ex(SCR **spp)
 {
 	EX_PRIVATE *exp;
 	GS *gp;
 	MSGS *mp;
 	SCR *sp;
 	TEXT *tp;
 	u_int32_t flags;
 
 	sp = *spp;
 	gp = sp->gp;
 	exp = EXP(sp);
 
 	/* Start the ex screen. */
 	if (ex_init(sp))
 		return (1);
 
 	/* Flush any saved messages. */
 	while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
 		gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
 		SLIST_REMOVE_HEAD(gp->msgq, q);
 		free(mp->buf);
 		free(mp);
 	}
 
 	/* If reading from a file, errors should have name and line info. */
 	if (F_ISSET(gp, G_SCRIPTED)) {
 		gp->excmd.if_lno = 1;
 		gp->excmd.if_name = "script";
 	}
 
 	/*
 	 * !!!
 	 * Initialize the text flags.  The beautify edit option historically
 	 * applied to ex command input read from a file.  In addition, the
 	 * first time a ^H was discarded from the input, there was a message,
 	 * "^H discarded", that was displayed.  We don't bother.
 	 */
 	LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR);
 	for (;; ++gp->excmd.if_lno) {
 		/* Display status line and flush. */
 		if (F_ISSET(sp, SC_STATUS)) {
 			if (!F_ISSET(sp, SC_EX_SILENT))
 				msgq_status(sp, sp->lno, 0);
 			F_CLR(sp, SC_STATUS);
 		}
 		(void)ex_fflush(sp);
 
 		/* Set the flags the user can reset. */
 		if (O_ISSET(sp, O_BEAUTIFY))
 			LF_SET(TXT_BEAUTIFY);
 		if (O_ISSET(sp, O_PROMPT))
 			LF_SET(TXT_PROMPT);
 
 		/* Clear any current interrupts, and get a command. */
 		CLR_INTERRUPT(sp);
 		if (ex_txt(sp, sp->tiq, ':', flags))
 			return (1);
 		if (INTERRUPTED(sp)) {
 			(void)ex_puts(sp, "\n");
 			(void)ex_fflush(sp);
 			continue;
 		}
 
 		/* Initialize the command structure. */
 		CLEAR_EX_PARSER(&gp->excmd);
 
 		/*
 		 * If the user entered a single carriage return, send
 		 * ex_cmd() a separator -- it discards single newlines.
 		 */
 		tp = TAILQ_FIRST(sp->tiq);
 		if (tp->len == 0) {
 			gp->excmd.cp = L(" ");	/* __TK__ why not |? */
 			gp->excmd.clen = 1;
 		} else {
 			gp->excmd.cp = tp->lb;
 			gp->excmd.clen = tp->len;
 		}
 		F_INIT(&gp->excmd, E_NRSEP);
 
 		if (ex_cmd(sp) && F_ISSET(gp, G_SCRIPTED))
 			return (1);
 
 		if (INTERRUPTED(sp)) {
 			CLR_INTERRUPT(sp);
 			msgq(sp, M_ERR, "170|Interrupted");
 		}
 
 		/*
 		 * If the last command caused a restart, or switched screens
 		 * or into vi, return.
 		 */
 		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_SSWITCH | SC_VI)) {
 			*spp = sp;
 			break;
 		}
 
 		/* If the last command switched files, we don't care. */
 		F_CLR(sp, SC_FSWITCH);
 
 		/*
 		 * If we're exiting this screen, move to the next one.  By
 		 * definition, this means returning into vi, so return to the
 		 * main editor loop.  The ordering is careful, don't discard
 		 * the contents of sp until the end.
 		 */
 		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
 			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
 				return (1);
 			*spp = screen_next(sp);
 			return (screen_end(sp));
 		}
 	}
 	return (0);
 }
 
 /*
  * ex_cmd --
  *	The guts of the ex parser: parse and execute a string containing
  *	ex commands.
  *
  * !!!
  * This code MODIFIES the string that gets passed in, to delete quoting
  * characters, etc.  The string cannot be readonly/text space, nor should
  * you expect to use it again after ex_cmd() returns.
  *
  * !!!
  * For the fun of it, if you want to see if a vi clone got the ex argument
  * parsing right, try:
  *
  *	echo 'foo|bar' > file1; echo 'foo/bar' > file2;
  *	vi
  *	:edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
  *
  * or:	vi
  *	:set|file|append|set|file
  *
  * For extra credit, try them in a startup .exrc file.
  *
  * PUBLIC: int ex_cmd(SCR *);
  */
 int
 ex_cmd(SCR *sp)
 {
 	enum nresult nret;
 	EX_PRIVATE *exp;
 	EXCMD *ecp;
 	GS *gp;
 	MARK cur;
 	recno_t lno;
 	size_t arg1_len, discard, len;
 	u_int32_t flags;
 	long ltmp;
 	int at_found, gv_found;
 	int cnt, delim, isaddr, namelen;
 	int newscreen, notempty, tmp, vi_address;
 	CHAR_T *arg1, *s, *p, *t;
 	CHAR_T ch = '\0';
 	CHAR_T *n;
 	char *np;
 
 	gp = sp->gp;
 	exp = EXP(sp);
 
 	/*
 	 * We always start running the command on the top of the stack.
 	 * This means that *everything* must be resolved when we leave
 	 * this function for any reason.
 	 */
 loop:	ecp = SLIST_FIRST(gp->ecq);
 
 	/* If we're reading a command from a file, set up error information. */
 	if (ecp->if_name != NULL) {
 		gp->if_lno = ecp->if_lno;
 		gp->if_name = ecp->if_name;
 	}
 
 	/*
 	 * If a move to the end of the file is scheduled for this command,
 	 * do it now.
 	 */
 	if (F_ISSET(ecp, E_MOVETOEND)) {
 		if (db_last(sp, &sp->lno))
 			goto rfail;
 		sp->cno = 0;
 		F_CLR(ecp, E_MOVETOEND);
 	}
 
 	/* If we found a newline, increment the count now. */
 	if (F_ISSET(ecp, E_NEWLINE)) {
 		++gp->if_lno;
 		++ecp->if_lno;
 		F_CLR(ecp, E_NEWLINE);
 	}
 
 	/* (Re)initialize the EXCMD structure, preserving some flags. */
 	CLEAR_EX_CMD(ecp);
 
 	/* Initialize the argument structures. */
 	if (argv_init(sp, ecp))
 		goto err;
 
 	/* Initialize +cmd, saved command information. */
 	arg1 = NULL;
 	ecp->save_cmdlen = 0;
 
 	/* Skip <blank>s, empty lines.  */
 	for (notempty = 0; ecp->clen > 0; ++ecp->cp, --ecp->clen)
 		if ((ch = *ecp->cp) == '\n') {
 			++gp->if_lno;
 			++ecp->if_lno;
 		} else if (cmdskip(ch))
 			notempty = 1;
 		else
 			break;
 
 	/*
 	 * !!!
 	 * Permit extra colons at the start of the line.  Historically,
 	 * ex/vi allowed a single extra one.  It's simpler not to count.
 	 * The stripping is done here because, historically, any command
 	 * could have preceding colons, e.g. ":g/pattern/:p" worked.
 	 */
 	if (ecp->clen != 0 && ch == ':') {
 		notempty = 1;
 		while (--ecp->clen > 0 && (ch = *++ecp->cp) == ':');
 	}
 
 	/*
 	 * Command lines that start with a double-quote are comments.
 	 *
 	 * !!!
 	 * Historically, there was no escape or delimiter for a comment, e.g.
 	 * :"foo|set was a single comment and nothing was output.  Since nvi
 	 * permits users to escape <newline> characters into command lines, we
 	 * have to check for that case.
 	 */
 	if (ecp->clen != 0 && ch == '"') {
 		while (--ecp->clen > 0 && *++ecp->cp != '\n');
 		if (*ecp->cp == '\n') {
 			F_SET(ecp, E_NEWLINE);
 			++ecp->cp;
 			--ecp->clen;
 		}
 		goto loop;
 	}
 
 	/* Skip whitespace. */
 	for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
 		ch = *ecp->cp;
 		if (!cmdskip(ch))
 			break;
 	}
 
 	/*
 	 * The last point at which an empty line can mean do nothing.
 	 *
 	 * !!!
 	 * Historically, in ex mode, lines containing only <blank> characters
 	 * were the same as a single <carriage-return>, i.e. a default command.
 	 * In vi mode, they were ignored.  In .exrc files this was a serious
 	 * annoyance, as vi kept trying to treat them as print commands.  We
 	 * ignore backward compatibility in this case, discarding lines that
 	 * contain only <blank> characters from .exrc files.
 	 *
 	 * !!!
 	 * This is where you end up when you're done a command, i.e. clen has
 	 * gone to zero.  Continue if there are more commands to run.
 	 */
 	if (ecp->clen == 0 &&
 	    (!notempty || F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_BLIGNORE))) {
 		if (ex_load(sp))
 			goto rfail;
 		ecp = SLIST_FIRST(gp->ecq);
 		if (ecp->clen == 0)
 			goto rsuccess;
 		goto loop;
 	}
 
 	/*
 	 * Check to see if this is a command for which we may want to move
 	 * the cursor back up to the previous line.  (The command :1<CR>
 	 * wants a <newline> separator, but the command :<CR> wants to erase
 	 * the command line.)  If the line is empty except for <blank>s,
 	 * <carriage-return> or <eof>, we'll probably want to move up.  I
 	 * don't think there's any way to get <blank> characters *after* the
 	 * command character, but this is the ex parser, and I've been wrong
 	 * before.
 	 */
 	if (F_ISSET(ecp, E_NRSEP) &&
 	    ecp->clen != 0 && (ecp->clen != 1 || ecp->cp[0] != '\004'))
 		F_CLR(ecp, E_NRSEP);
 
 	/* Parse command addresses. */
 	if (ex_range(sp, ecp, &tmp))
 		goto rfail;
 	if (tmp)
 		goto err;
 
 	/*
 	 * Skip <blank>s and any more colons (the command :3,5:print
 	 * worked, historically).
 	 */
 	for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
 		ch = *ecp->cp;
 		if (!cmdskip(ch) && ch != ':')
 			break;
 	}
 
 	/*
 	 * If no command, ex does the last specified of p, l, or #, and vi
 	 * moves to the line.  Otherwise, determine the length of the command
 	 * name by looking for the first non-alphabetic character.  (There
 	 * are a few non-alphabetic characters in command names, but they're
 	 * all single character commands.)  This isn't a great test, because
 	 * it means that, for the command ":e +cut.c file", we'll report that
 	 * the command "cut" wasn't known.  However, it makes ":e+35 file" work
 	 * correctly.
 	 *
 	 * !!!
 	 * Historically, lines with multiple adjacent (or <blank> separated)
 	 * command separators were very strange.  For example, the command
 	 * |||<carriage-return>, when the cursor was on line 1, displayed
 	 * lines 2, 3 and 5 of the file.  In addition, the command "   |  "
 	 * would only display the line after the next line, instead of the
 	 * next two lines.  No ideas why.  It worked reasonably when executed
 	 * from vi mode, and displayed lines 2, 3, and 4, so we do a default
 	 * command for each separator.
 	 */
 #define	SINGLE_CHAR_COMMANDS	L("\004!#&*<=>@~")
 	newscreen = 0;
 	if (ecp->clen != 0 && ecp->cp[0] != '|' && ecp->cp[0] != '\n') {
 		if (STRCHR(SINGLE_CHAR_COMMANDS, *ecp->cp)) {
 			p = ecp->cp;
 			++ecp->cp;
 			--ecp->clen;
 			namelen = 1;
 		} else {
 			for (p = ecp->cp;
 			    ecp->clen > 0; --ecp->clen, ++ecp->cp)
 				if (!isazAZ(*ecp->cp))
 					break;
 			if ((namelen = ecp->cp - p) == 0) {
 				msgq(sp, M_ERR, "080|Unknown command name");
 				goto err;
 			}
 		}
 
 		/*
 		 * !!!
 		 * Historic vi permitted flags to immediately follow any
 		 * subset of the 'delete' command, but then did not permit
 		 * further arguments (flag, buffer, count).  Make it work.
 		 * Permit further arguments for the few shreds of dignity
 		 * it offers.
 		 *
 		 * Adding commands that start with 'd', and match "delete"
 		 * up to a l, p, +, - or # character can break this code.
 		 *
 		 * !!!
 		 * Capital letters beginning the command names ex, edit,
 		 * next, previous, tag and visual (in vi mode) indicate the
 		 * command should happen in a new screen.
 		 */
 		switch (p[0]) {
 		case 'd':
 			for (s = p,
 			    n = cmds[C_DELETE].name; *s == *n; ++s, ++n);
 			if (s[0] == 'l' || s[0] == 'p' || s[0] == '+' ||
 			    s[0] == '-' || s[0] == '^' || s[0] == '#') {
 				len = (ecp->cp - p) - (s - p);
 				ecp->cp -= len;
 				ecp->clen += len;
 				ecp->rcmd = cmds[C_DELETE];
 				ecp->rcmd.syntax = "1bca1";
 				ecp->cmd = &ecp->rcmd;
 				goto skip_srch;
 			}
 			break;
 		case 'E': case 'F': case 'N': case 'P': case 'T': case 'V':
 			newscreen = 1;
 			p[0] = tolower(p[0]);
 			break;
 		}
 
 		/*
 		 * Search the table for the command.
 		 *
 		 * !!!
 		 * Historic vi permitted the mark to immediately follow the
 		 * 'k' in the 'k' command.  Make it work.
 		 *
 		 * !!!
 		 * Historic vi permitted any flag to follow the s command, e.g.
 		 * "s/e/E/|s|sgc3p" was legal.  Make the command "sgc" work.
 		 * Since the following characters all have to be flags, i.e.
 		 * alphabetics, we can let the s command routine return errors
 		 * if it was some illegal command string.  This code will break
 		 * if an "sg" or similar command is ever added.  The substitute
 		 * code doesn't care if it's a "cgr" flag or a "#lp" flag that
 		 * follows the 's', but we limit the choices here to "cgr" so
 		 * that we get unknown command messages for wrong combinations.
 		 */
 		if ((ecp->cmd = ex_comm_search(p, namelen)) == NULL)
 			switch (p[0]) {
 			case 'k':
 				if (namelen == 2) {
 					ecp->cp -= namelen - 1;
 					ecp->clen += namelen - 1;
 					ecp->cmd = &cmds[C_K];
 					break;
 				}
 				goto unknown;
 			case 's':
 				for (s = p + 1, cnt = namelen; --cnt; ++s)
 					if (s[0] != 'c' &&
 					    s[0] != 'g' && s[0] != 'r')
 						break;
 				if (cnt == 0) {
 					ecp->cp -= namelen - 1;
 					ecp->clen += namelen - 1;
 					ecp->rcmd = cmds[C_SUBSTITUTE];
 					ecp->rcmd.fn = ex_subagain;
 					ecp->cmd = &ecp->rcmd;
 					break;
 				}
 				/* FALLTHROUGH */
 			default:
 unknown:			if (newscreen)
 					p[0] = toupper(p[0]);
 				ex_unknown(sp, p, namelen);
 				goto err;
 			}
 
 		/*
 		 * The visual command has a different syntax when called
 		 * from ex than when called from a vi colon command.  FMH.
 		 * Make the change now, before we test for the newscreen
 		 * semantic, so that we're testing the right one.
 		 */
 skip_srch:	if (ecp->cmd == &cmds[C_VISUAL_EX] && F_ISSET(sp, SC_VI))
 			ecp->cmd = &cmds[C_VISUAL_VI];
 
 		/*
 		 * !!!
 		 * Historic vi permitted a capital 'P' at the beginning of
 		 * any command that started with 'p'.  Probably wanted the
 		 * P[rint] command for backward compatibility, and the code
 		 * just made Preserve and Put work by accident.  Nvi uses
 		 * Previous to mean previous-in-a-new-screen, so be careful.
 		 */
 		if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN) &&
 		    (ecp->cmd == &cmds[C_PRINT] ||
 		    ecp->cmd == &cmds[C_PRESERVE]))
 			newscreen = 0;
 
 		/* Test for a newscreen associated with this command. */
 		if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN))
 			goto unknown;
 
 		/* Secure means no shell access. */
 		if (F_ISSET(ecp->cmd, E_SECURE) && O_ISSET(sp, O_SECURE)) {
 			ex_wemsg(sp, ecp->cmd->name, EXM_SECURE);
 			goto err;
 		}
 
 		/*
 		 * Multiple < and > characters; another "feature".  Note,
 		 * The string passed to the underlying function may not be
 		 * nul terminated in this case.
 		 */
 		if ((ecp->cmd == &cmds[C_SHIFTL] && *p == '<') ||
 		    (ecp->cmd == &cmds[C_SHIFTR] && *p == '>')) {
 			for (ch = *p;
 			    ecp->clen > 0; --ecp->clen, ++ecp->cp)
 				if (*ecp->cp != ch)
 					break;
 			if (argv_exp0(sp, ecp, p, ecp->cp - p))
 				goto err;
 		}
 
 		/* Set the format style flags for the next command. */
 		if (ecp->cmd == &cmds[C_HASH])
 			exp->fdef = E_C_HASH;
 		else if (ecp->cmd == &cmds[C_LIST])
 			exp->fdef = E_C_LIST;
 		else if (ecp->cmd == &cmds[C_PRINT])
 			exp->fdef = E_C_PRINT;
 		F_CLR(ecp, E_USELASTCMD);
 	} else {
 		/* Print is the default command. */
 		ecp->cmd = &cmds[C_PRINT];
 
 		/* Set the saved format flags. */
 		F_SET(ecp, exp->fdef);
 
 		/*
 		 * !!!
 		 * If no address was specified, and it's not a global command,
 		 * we up the address by one.  (I have no idea why globals are
 		 * exempted, but it's (ahem) historic practice.)
 		 */
 		if (ecp->addrcnt == 0 && !F_ISSET(sp, SC_EX_GLOBAL)) {
 			ecp->addrcnt = 1;
 			ecp->addr1.lno = sp->lno + 1;
 			ecp->addr1.cno = sp->cno;
 		}
 
 		F_SET(ecp, E_USELASTCMD);
 	}
 
 	/*
 	 * !!!
 	 * Historically, the number option applied to both ex and vi.  One
 	 * strangeness was that ex didn't switch display formats until a
 	 * command was entered, e.g. <CR>'s after the set didn't change to
 	 * the new format, but :1p would.
 	 */
 	if (O_ISSET(sp, O_NUMBER)) {
 		F_SET(ecp, E_OPTNUM);
 		FL_SET(ecp->iflags, E_C_HASH);
 	} else
 		F_CLR(ecp, E_OPTNUM);
 
 	/* Check for ex mode legality. */
 	if (F_ISSET(sp, SC_EX) && (F_ISSET(ecp->cmd, E_VIONLY) || newscreen)) {
 		msgq_wstr(sp, M_ERR, ecp->cmd->name,
 		    "082|%s: command not available in ex mode");
 		goto err;
 	}
 
 	/* Add standard command flags. */
 	F_SET(ecp, ecp->cmd->flags);
 	if (!newscreen)
 		F_CLR(ecp, E_NEWSCREEN);
 
 	/*
 	 * There are three normal termination cases for an ex command.  They
 	 * are the end of the string (ecp->clen), or unescaped (by <literal
 	 * next> characters) <newline> or '|' characters.  As we're now past
 	 * possible addresses, we can determine how long the command is, so we
 	 * don't have to look for all the possible terminations.  Naturally,
 	 * there are some exciting special cases:
 	 *
 	 * 1: The bang, global, v and the filter versions of the read and
 	 *    write commands are delimited by <newline>s (they can contain
 	 *    shell pipes).
 	 * 2: The ex, edit, next and visual in vi mode commands all take ex
 	 *    commands as their first arguments.
 	 * 3: The s command takes an RE as its first argument, and wants it
 	 *    to be specially delimited.
 	 *
 	 * Historically, '|' characters in the first argument of the ex, edit,
 	 * next, vi visual, and s commands didn't delimit the command.  And,
 	 * in the filter cases for read and write, and the bang, global and v
 	 * commands, they did not delimit the command at all.
 	 *
 	 * For example, the following commands were legal:
 	 *
 	 *	:edit +25|s/abc/ABC/ file.c
 	 *	:s/|/PIPE/
 	 *	:read !spell % | columnate
 	 *	:global/pattern/p|l
 	 *
 	 * It's not quite as simple as it sounds, however.  The command:
 	 *
 	 *	:s/a/b/|s/c/d|set
 	 *
 	 * was also legal, i.e. the historic ex parser (using the word loosely,
 	 * since "parser" implies some regularity of syntax) delimited the RE's
 	 * based on its delimiter and not anything so irretrievably vulgar as a
 	 * command syntax.
 	 *
 	 * Anyhow, the following code makes this all work.  First, for the
 	 * special cases we move past their special argument(s).  Then, we
 	 * do normal command processing on whatever is left.  Barf-O-Rama.
 	 */
 	discard = 0;		/* Characters discarded from the command. */
 	arg1_len = 0;
 	ecp->save_cmd = ecp->cp;
 	if (ecp->cmd == &cmds[C_EDIT] || ecp->cmd == &cmds[C_EX] ||
 	    ecp->cmd == &cmds[C_NEXT] || ecp->cmd == &cmds[C_VISUAL_VI] ||
 	    ecp->cmd == &cmds[C_VSPLIT]) {
 		/*
 		 * Move to the next non-whitespace character.  A '!'
 		 * immediately following the command is eaten as a
 		 * force flag.
 		 */
 		if (ecp->clen > 0 && *ecp->cp == '!') {
 			++ecp->cp;
 			--ecp->clen;
 			FL_SET(ecp->iflags, E_C_FORCE);
 
 			/* Reset, don't reparse. */
 			ecp->save_cmd = ecp->cp;
 		}
 		for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
 			if (!cmdskip(*ecp->cp))
 				break;
 		/*
 		 * QUOTING NOTE:
 		 *
 		 * The historic implementation ignored all escape characters
 		 * so there was no way to put a space or newline into the +cmd
 		 * field.  We do a simplistic job of fixing it by moving to the
 		 * first whitespace character that isn't escaped.  The escaping
 		 * characters are stripped as no longer useful.
 		 */
 		if (ecp->clen > 0 && *ecp->cp == '+') {
 			++ecp->cp;
 			--ecp->clen;
 			for (arg1 = p = ecp->cp;
 			    ecp->clen > 0; --ecp->clen, ++ecp->cp) {
 				ch = *ecp->cp;
 				if (IS_ESCAPE(sp, ecp, ch) &&
 				    ecp->clen > 1) {
 					++discard;
 					--ecp->clen;
 					ch = *++ecp->cp;
 				} else if (cmdskip(ch))
 					break;
 				*p++ = ch;
 			}
 			arg1_len = ecp->cp - arg1;
 
 			/* Reset, so the first argument isn't reparsed. */
 			ecp->save_cmd = ecp->cp;
 		}
 	} else if (ecp->cmd == &cmds[C_BANG] ||
 	    ecp->cmd == &cmds[C_GLOBAL] || ecp->cmd == &cmds[C_V]) {
 		/*
 		 * QUOTING NOTE:
 		 *
 		 * We use backslashes to escape <newline> characters, although
 		 * this wasn't historic practice for the bang command.  It was
 		 * for the global and v commands, and it's common usage when
 		 * doing text insert during the command.  Escaping characters
 		 * are stripped as no longer useful.
 		 */
 		for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
 			ch = *ecp->cp;
 			if (ch == '\\' && ecp->clen > 1 && ecp->cp[1] == '\n') {
 				++discard;
 				--ecp->clen;
 				ch = *++ecp->cp;
 
 				++gp->if_lno;
 				++ecp->if_lno;
 			} else if (ch == '\n')
 				break;
 			*p++ = ch;
 		}
 	} else if (ecp->cmd == &cmds[C_READ] || ecp->cmd == &cmds[C_WRITE]) {
 		/*
 		 * For write commands, if the next character is a <blank>, and
 		 * the next non-blank character is a '!', it's a filter command
 		 * and we want to eat everything up to the <newline>.  For read
 		 * commands, if the next non-blank character is a '!', it's a
 		 * filter command and we want to eat everything up to the next
 		 * <newline>.  Otherwise, we're done.
 		 */
 		for (tmp = 0; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
 			ch = *ecp->cp;
 			if (cmdskip(ch))
 				tmp = 1;
 			else
 				break;
 		}
 		if (ecp->clen > 0 && ch == '!' &&
 		    (ecp->cmd == &cmds[C_READ] || tmp))
 			for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
 				if (ecp->cp[0] == '\n')
 					break;
 	} else if (ecp->cmd == &cmds[C_SUBSTITUTE]) {
 		/*
 		 * Move to the next non-whitespace character, we'll use it as
 		 * the delimiter.  If the character isn't an alphanumeric or
 		 * a '|', it's the delimiter, so parse it.  Otherwise, we're
 		 * into something like ":s g", so use the special s command.
 		 */
 		for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
 			if (!cmdskip(ecp->cp[0]))
 				break;
 
 		if (is09azAZ(ecp->cp[0]) || ecp->cp[0] == '|') {
 			ecp->rcmd = cmds[C_SUBSTITUTE];
 			ecp->rcmd.fn = ex_subagain;
 			ecp->cmd = &ecp->rcmd;
 		} else if (ecp->clen > 0) {
 			/*
 			 * QUOTING NOTE:
 			 *
 			 * Backslashes quote delimiter characters for RE's.
 			 * The backslashes are NOT removed since they'll be
 			 * used by the RE code.  Move to the third delimiter
 			 * that's not escaped (or the end of the command).
 			 */
 			delim = *ecp->cp;
 			++ecp->cp;
 			--ecp->clen;
 			for (cnt = 2; ecp->clen > 0 &&
 			    cnt != 0; --ecp->clen, ++ecp->cp)
 				if (ecp->cp[0] == '\\' &&
 				    ecp->clen > 1) {
 					++ecp->cp;
 					--ecp->clen;
 				} else if (ecp->cp[0] == delim)
 					--cnt;
 		}
 	}
 
 	/*
 	 * Use normal quoting and termination rules to find the end of this
 	 * command.
 	 *
 	 * QUOTING NOTE:
 	 *
 	 * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
 	 * file.  It was almost certainly a bug, but that's what bug-for-bug
 	 * compatibility means, Grasshopper.  Also, ^V's escape the command
 	 * delimiters.  Literal next quote characters in front of the newlines,
 	 * '|' characters or literal next characters are stripped as they're
 	 * no longer useful.
 	 */
 	vi_address = ecp->clen != 0 && ecp->cp[0] != '\n';
 	for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
 		ch = ecp->cp[0];
 		if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
 			CHAR_T tmp = ecp->cp[1];
 			if (tmp == '\n' || tmp == '|') {
 				if (tmp == '\n') {
 					++gp->if_lno;
 					++ecp->if_lno;
 				}
 				++discard;
 				--ecp->clen;
 				++ecp->cp;
 				ch = tmp;
 			}
 		} else if (ch == '\n' || ch == '|') {
 			if (ch == '\n')
 				F_SET(ecp, E_NEWLINE);
 			--ecp->clen;
 			break;
 		}
 		*p++ = ch;
 	}
 
 	/*
 	 * Save off the next command information, go back to the
 	 * original start of the command.
 	 */
 	p = ecp->cp + 1;
 	ecp->cp = ecp->save_cmd;
 	ecp->save_cmd = p;
 	ecp->save_cmdlen = ecp->clen;
 	ecp->clen = ((ecp->save_cmd - ecp->cp) - 1) - discard;
 
 	/*
 	 * QUOTING NOTE:
 	 *
 	 * The "set tags" command historically used a backslash, not the
 	 * user's literal next character, to escape whitespace.  Handle
 	 * it here instead of complicating the argv_exp3() code.  Note,
 	 * this isn't a particularly complex trap, and if backslashes were
 	 * legal in set commands, this would have to be much more complicated.
 	 */
-	if (ecp->cmd == &cmds[C_SET])
+	if (ecp->cmd == &cmds[C_SET]) {
 		for (p = ecp->cp, len = ecp->clen; len > 0; --len, ++p)
 			if (IS_ESCAPE(sp, ecp, *p) && len > 1) {
 				--len;
 				++p;
 			} else if (*p == '\\')
 				*p = CH_LITERAL;
+	}
 
 	/*
 	 * Set the default addresses.  It's an error to specify an address for
 	 * a command that doesn't take them.  If two addresses are specified
 	 * for a command that only takes one, lose the first one.  Two special
 	 * cases here, some commands take 0 or 2 addresses.  For most of them
 	 * (the E_ADDR2_ALL flag), 0 defaults to the entire file.  For one
 	 * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
 	 *
 	 * Also, if the file is empty, some commands want to use an address of
 	 * 0, i.e. the entire file is 0 to 0, and the default first address is
 	 * 0.  Otherwise, an entire file is 1 to N and the default line is 1.
 	 * Note, we also add the E_ADDR_ZERO flag to the command flags, for the
 	 * case where the 0 address is only valid if it's a default address.
 	 *
 	 * Also, set a flag if we set the default addresses.  Some commands
 	 * (ex: z) care if the user specified an address or if we just used
 	 * the current cursor.
 	 */
 	switch (F_ISSET(ecp, E_ADDR1 | E_ADDR2 | E_ADDR2_ALL | E_ADDR2_NONE)) {
 	case E_ADDR1:				/* One address: */
 		switch (ecp->addrcnt) {
 		case 0:				/* Default cursor/empty file. */
 			ecp->addrcnt = 1;
 			F_SET(ecp, E_ADDR_DEF);
 			if (F_ISSET(ecp, E_ADDR_ZERODEF)) {
 				if (db_last(sp, &lno))
 					goto err;
 				if (lno == 0) {
 					ecp->addr1.lno = 0;
 					F_SET(ecp, E_ADDR_ZERO);
 				} else
 					ecp->addr1.lno = sp->lno;
 			} else
 				ecp->addr1.lno = sp->lno;
 			ecp->addr1.cno = sp->cno;
 			break;
 		case 1:
 			break;
 		case 2:				/* Lose the first address. */
 			ecp->addrcnt = 1;
 			ecp->addr1 = ecp->addr2;
 		}
 		break;
 	case E_ADDR2_NONE:			/* Zero/two addresses: */
 		if (ecp->addrcnt == 0)		/* Default to nothing. */
 			break;
 		goto two_addr;
 	case E_ADDR2_ALL:			/* Zero/two addresses: */
 		if (ecp->addrcnt == 0) {	/* Default entire/empty file. */
 			F_SET(ecp, E_ADDR_DEF);
 			ecp->addrcnt = 2;
 			if (sp->ep == NULL)
 				ecp->addr2.lno = 0;
 			else if (db_last(sp, &ecp->addr2.lno))
 				goto err;
 			if (F_ISSET(ecp, E_ADDR_ZERODEF) &&
 			    ecp->addr2.lno == 0) {
 				ecp->addr1.lno = 0;
 				F_SET(ecp, E_ADDR_ZERO);
 			} else
 				ecp->addr1.lno = 1;
 			ecp->addr1.cno = ecp->addr2.cno = 0;
 			F_SET(ecp, E_ADDR2_ALL);
 			break;
 		}
 		/* FALLTHROUGH */
 	case E_ADDR2:				/* Two addresses: */
 two_addr:	switch (ecp->addrcnt) {
 		case 0:				/* Default cursor/empty file. */
 			ecp->addrcnt = 2;
 			F_SET(ecp, E_ADDR_DEF);
 			if (sp->lno == 1 &&
 			    F_ISSET(ecp, E_ADDR_ZERODEF)) {
 				if (db_last(sp, &lno))
 					goto err;
 				if (lno == 0) {
 					ecp->addr1.lno = ecp->addr2.lno = 0;
 					F_SET(ecp, E_ADDR_ZERO);
 				} else
 					ecp->addr1.lno =
 					    ecp->addr2.lno = sp->lno;
 			} else
 				ecp->addr1.lno = ecp->addr2.lno = sp->lno;
 			ecp->addr1.cno = ecp->addr2.cno = sp->cno;
 			break;
 		case 1:				/* Default to first address. */
 			ecp->addrcnt = 2;
 			ecp->addr2 = ecp->addr1;
 			break;
 		case 2:
 			break;
 		}
 		break;
 	default:
 		if (ecp->addrcnt)		/* Error. */
 			goto usage;
 	}
 
 	/*
 	 * !!!
 	 * The ^D scroll command historically scrolled the value of the scroll
 	 * option or to EOF.  It was an error if the cursor was already at EOF.
 	 * (Leading addresses were permitted, but were then ignored.)
 	 */
 	if (ecp->cmd == &cmds[C_SCROLL]) {
 		ecp->addrcnt = 2;
 		ecp->addr1.lno = sp->lno + 1;
 		ecp->addr2.lno = sp->lno + O_VAL(sp, O_SCROLL);
 		ecp->addr1.cno = ecp->addr2.cno = sp->cno;
 		if (db_last(sp, &lno))
 			goto err;
 		if (lno != 0 && lno > sp->lno && ecp->addr2.lno > lno)
 			ecp->addr2.lno = lno;
 	}
 
 	ecp->flagoff = 0;
 	for (np = ecp->cmd->syntax; *np != '\0'; ++np) {
 		/*
 		 * The force flag is sensitive to leading whitespace, i.e.
 		 * "next !" is different from "next!".  Handle it before
 		 * skipping leading <blank>s.
 		 */
 		if (*np == '!') {
 			if (ecp->clen > 0 && *ecp->cp == '!') {
 				++ecp->cp;
 				--ecp->clen;
 				FL_SET(ecp->iflags, E_C_FORCE);
 			}
 			continue;
 		}
 
 		/* Skip leading <blank>s. */
 		for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
 			if (!cmdskip(*ecp->cp))
 				break;
 		if (ecp->clen == 0)
 			break;
 
 		switch (*np) {
 		case '1':				/* +, -, #, l, p */
 			/*
 			 * !!!
 			 * Historically, some flags were ignored depending
 			 * on where they occurred in the command line.  For
 			 * example, in the command, ":3+++p--#", historic vi
 			 * acted on the '#' flag, but ignored the '-' flags.
 			 * It's unambiguous what the flags mean, so we just
 			 * handle them regardless of the stupidity of their
 			 * location.
 			 */
 			for (; ecp->clen; --ecp->clen, ++ecp->cp)
 				switch (*ecp->cp) {
 				case '+':
 					++ecp->flagoff;
 					break;
 				case '-':
 				case '^':
 					--ecp->flagoff;
 					break;
 				case '#':
 					F_CLR(ecp, E_OPTNUM);
 					FL_SET(ecp->iflags, E_C_HASH);
 					exp->fdef |= E_C_HASH;
 					break;
 				case 'l':
 					FL_SET(ecp->iflags, E_C_LIST);
 					exp->fdef |= E_C_LIST;
 					break;
 				case 'p':
 					FL_SET(ecp->iflags, E_C_PRINT);
 					exp->fdef |= E_C_PRINT;
 					break;
 				default:
 					goto end_case1;
 				}
 end_case1:		break;
 		case '2':				/* -, ., +, ^ */
 		case '3':				/* -, ., +, ^, = */
 			for (; ecp->clen; --ecp->clen, ++ecp->cp)
 				switch (*ecp->cp) {
 				case '-':
 					FL_SET(ecp->iflags, E_C_DASH);
 					break;
 				case '.':
 					FL_SET(ecp->iflags, E_C_DOT);
 					break;
 				case '+':
 					FL_SET(ecp->iflags, E_C_PLUS);
 					break;
 				case '^':
 					FL_SET(ecp->iflags, E_C_CARAT);
 					break;
 				case '=':
 					if (*np == '3') {
 						FL_SET(ecp->iflags, E_C_EQUAL);
 						break;
 					}
 					/* FALLTHROUGH */
 				default:
 					goto end_case23;
 				}
 end_case23:		break;
 		case 'b':				/* buffer */
 			/*
 			 * !!!
 			 * Historically, "d #" was a delete with a flag, not a
 			 * delete into the '#' buffer.  If the current command
 			 * permits a flag, don't use one as a buffer.  However,
 			 * the 'l' and 'p' flags were legal buffer names in the
 			 * historic ex, and were used as buffers, not flags.
 			 */
 			if ((ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
 			    ecp->cp[0] == '^' || ecp->cp[0] == '#') &&
 			    strchr(np, '1') != NULL)
 				break;
 			/*
 			 * !!!
 			 * Digits can't be buffer names in ex commands, or the
 			 * command "d2" would be a delete into buffer '2', and
 			 * not a two-line deletion.
 			 */
 			if (!ISDIGIT(ecp->cp[0])) {
 				ecp->buffer = *ecp->cp;
 				++ecp->cp;
 				--ecp->clen;
 				FL_SET(ecp->iflags, E_C_BUFFER);
 			}
 			break;
 		case 'c':				/* count [01+a] */
 			++np;
 			/* Validate any signed value. */
 			if (!ISDIGIT(*ecp->cp) && (*np != '+' ||
 			    (*ecp->cp != '+' && *ecp->cp != '-')))
 				break;
 			/* If a signed value, set appropriate flags. */
 			if (*ecp->cp == '-')
 				FL_SET(ecp->iflags, E_C_COUNT_NEG);
 			else if (*ecp->cp == '+')
 				FL_SET(ecp->iflags, E_C_COUNT_POS);
 			if ((nret =
 			    nget_slong(&ltmp, ecp->cp, &t, 10)) != NUM_OK) {
 				ex_badaddr(sp, NULL, A_NOTSET, nret);
 				goto err;
 			}
 			if (ltmp == 0 && *np != '0') {
 				msgq(sp, M_ERR, "083|Count may not be zero");
 				goto err;
 			}
 			ecp->clen -= (t - ecp->cp);
 			ecp->cp = t;
 
 			/*
 			 * Counts as address offsets occur in commands taking
 			 * two addresses.  Historic vi practice was to use
 			 * the count as an offset from the *second* address.
 			 *
 			 * Set a count flag; some underlying commands (see
 			 * join) do different things with counts than with
 			 * line addresses.
 			 */
 			if (*np == 'a') {
 				ecp->addr1 = ecp->addr2;
 				ecp->addr2.lno = ecp->addr1.lno + ltmp - 1;
 			} else
 				ecp->count = ltmp;
 			FL_SET(ecp->iflags, E_C_COUNT);
 			break;
 		case 'f':				/* file */
 			if (argv_exp2(sp, ecp, ecp->cp, ecp->clen))
 				goto err;
 			goto arg_cnt_chk;
 		case 'l':				/* line */
 			/*
 			 * Get a line specification.
 			 *
 			 * If the line was a search expression, we may have
 			 * changed state during the call, and we're now
 			 * searching the file.  Push ourselves onto the state
 			 * stack.
 			 */
 			if (ex_line(sp, ecp, &cur, &isaddr, &tmp))
 				goto rfail;
 			if (tmp)
 				goto err;
 
 			/* Line specifications are always required. */
 			if (!isaddr) {
 				msgq_wstr(sp, M_ERR, ecp->cp,
 				     "084|%s: bad line specification");
 				goto err;
 			}
 			/*
 			 * The target line should exist for these commands,
 			 * but 0 is legal for them as well.
 			 */
 			if (cur.lno != 0 && !db_exist(sp, cur.lno)) {
 				ex_badaddr(sp, NULL, A_EOF, NUM_OK);
 				goto err;
 			}
 			ecp->lineno = cur.lno;
 			break;
 		case 'S':				/* string, file exp. */
 			if (ecp->clen != 0) {
 				if (argv_exp1(sp, ecp, ecp->cp,
 				    ecp->clen, ecp->cmd == &cmds[C_BANG]))
 					goto err;
 				goto addr_verify;
 			}
 			/* FALLTHROUGH */
 		case 's':				/* string */
 			if (argv_exp0(sp, ecp, ecp->cp, ecp->clen))
 				goto err;
 			goto addr_verify;
 		case 'W':				/* word string */
 			/*
 			 * QUOTING NOTE:
 			 *
 			 * Literal next characters escape the following
 			 * character.  Quoting characters are stripped here
 			 * since they are no longer useful.
 			 *
 			 * First there was the word.
 			 */
 			for (p = t = ecp->cp;
 			    ecp->clen > 0; --ecp->clen, ++ecp->cp) {
 				ch = *ecp->cp;
 				if (IS_ESCAPE(sp,
 				    ecp, ch) && ecp->clen > 1) {
 					--ecp->clen;
 					*p++ = *++ecp->cp;
 				} else if (cmdskip(ch)) {
 					++ecp->cp;
 					--ecp->clen;
 					break;
 				} else
 					*p++ = ch;
 			}
 			if (argv_exp0(sp, ecp, t, p - t))
 				goto err;
 
 			/* Delete intervening whitespace. */
 			for (; ecp->clen > 0;
 			    --ecp->clen, ++ecp->cp) {
 				ch = *ecp->cp;
 				if (!cmdskip(ch))
 					break;
 			}
 			if (ecp->clen == 0)
 				goto usage;
 
 			/* Followed by the string. */
 			for (p = t = ecp->cp; ecp->clen > 0;
 			    --ecp->clen, ++ecp->cp, ++p) {
 				ch = *ecp->cp;
 				if (IS_ESCAPE(sp,
 				    ecp, ch) && ecp->clen > 1) {
 					--ecp->clen;
 					*p = *++ecp->cp;
 				} else
 					*p = ch;
 			}
 			if (argv_exp0(sp, ecp, t, p - t))
 				goto err;
 			goto addr_verify;
 		case 'w':				/* word */
 			if (argv_exp3(sp, ecp, ecp->cp, ecp->clen))
 				goto err;
 arg_cnt_chk:		if (*++np != 'N') {		/* N */
 				/*
 				 * If a number is specified, must either be
 				 * 0 or that number, if optional, and that
 				 * number, if required.
 				 */
 				tmp = *np - '0';
 				if ((*++np != 'o' || exp->argsoff != 0) &&
 				    exp->argsoff != tmp)
 					goto usage;
 			}
 			goto addr_verify;
 		default: {
 			size_t nlen;
 			char *nstr;
 
 			INT2CHAR(sp, ecp->cmd->name, STRLEN(ecp->cmd->name) + 1,
 			    nstr, nlen);
 			msgq(sp, M_ERR,
 			    "085|Internal syntax table error (%s: %s)",
 			    nstr, KEY_NAME(sp, *np));
 		}
 		}
 	}
 
 	/* Skip trailing whitespace. */
 	for (; ecp->clen > 0; --ecp->clen) {
 		ch = *ecp->cp++;
 		if (!cmdskip(ch))
 			break;
 	}
 
 	/*
 	 * There shouldn't be anything left, and no more required fields,
 	 * i.e neither 'l' or 'r' in the syntax string.
 	 */
 	if (ecp->clen != 0 || strpbrk(np, "lr")) {
 usage:		msgq(sp, M_ERR, "086|Usage: %s", ecp->cmd->usage);
 		goto err;
 	}
 
 	/*
 	 * Verify that the addresses are legal.  Check the addresses here,
 	 * because this is a place where all ex addresses pass through.
 	 * (They don't all pass through ex_line(), for instance.)  We're
 	 * assuming that any non-existent line doesn't exist because it's
 	 * past the end-of-file.  That's a pretty good guess.
 	 *
 	 * If it's a "default vi command", an address of zero is okay.
 	 */
 addr_verify:
 	switch (ecp->addrcnt) {
 	case 2:
 		/*
 		 * Historic ex/vi permitted commands with counts to go past
 		 * EOF.  So, for example, if the file only had 5 lines, the
 		 * ex command "1,6>" would fail, but the command ">300"
 		 * would succeed.  Since we don't want to have to make all
 		 * of the underlying commands handle random line numbers,
 		 * fix it here.
 		 */
 		if (ecp->addr2.lno == 0) {
 			if (!F_ISSET(ecp, E_ADDR_ZERO) &&
 			    (F_ISSET(sp, SC_EX) ||
 			    !F_ISSET(ecp, E_USELASTCMD))) {
 				ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
 				goto err;
 			}
-		} else if (!db_exist(sp, ecp->addr2.lno))
+		} else if (!db_exist(sp, ecp->addr2.lno)) {
 			if (FL_ISSET(ecp->iflags, E_C_COUNT)) {
 				if (db_last(sp, &lno))
 					goto err;
 				ecp->addr2.lno = lno;
 			} else {
 				ex_badaddr(sp, NULL, A_EOF, NUM_OK);
 				goto err;
 			}
+		}
 		/* FALLTHROUGH */
 	case 1:
 		if (ecp->addr1.lno == 0) {
 			if (!F_ISSET(ecp, E_ADDR_ZERO) &&
 			    (F_ISSET(sp, SC_EX) ||
 			    !F_ISSET(ecp, E_USELASTCMD))) {
 				ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
 				goto err;
 			}
 		} else if (!db_exist(sp, ecp->addr1.lno)) {
 			ex_badaddr(sp, NULL, A_EOF, NUM_OK);
 			goto err;
 		}
 		break;
 	}
 
 	/*
 	 * If doing a default command and there's nothing left on the line,
 	 * vi just moves to the line.  For example, ":3" and ":'a,'b" just
 	 * move to line 3 and line 'b, respectively, but ":3|" prints line 3.
 	 *
 	 * !!!
 	 * In addition, IF THE LINE CHANGES, move to the first nonblank of
 	 * the line.
 	 *
 	 * !!!
 	 * This is done before the absolute mark gets set; historically,
 	 * "/a/,/b/" did NOT set vi's absolute mark, but "/a/,/b/d" did.
 	 */
 	if ((F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_NOPRDEF)) &&
 	    F_ISSET(ecp, E_USELASTCMD) && vi_address == 0) {
 		switch (ecp->addrcnt) {
 		case 2:
 			if (sp->lno !=
 			    (ecp->addr2.lno ? ecp->addr2.lno : 1)) {
 				sp->lno =
 				    ecp->addr2.lno ? ecp->addr2.lno : 1;
 				sp->cno = 0;
 				(void)nonblank(sp, sp->lno, &sp->cno);
 			}
 			break;
 		case 1:
 			if (sp->lno !=
 			    (ecp->addr1.lno ? ecp->addr1.lno : 1)) {
 				sp->lno =
 				    ecp->addr1.lno ? ecp->addr1.lno : 1;
 				sp->cno = 0;
 				(void)nonblank(sp, sp->lno, &sp->cno);
 			}
 			break;
 		}
 		ecp->cp = ecp->save_cmd;
 		ecp->clen = ecp->save_cmdlen;
 		goto loop;
 	}
 
 	/*
 	 * Set the absolute mark -- we have to set it for vi here, in case
 	 * it's a compound command, e.g. ":5p|6" should set the absolute
 	 * mark for vi.
 	 */
 	if (F_ISSET(ecp, E_ABSMARK)) {
 		cur.lno = sp->lno;
 		cur.cno = sp->cno;
 		F_CLR(ecp, E_ABSMARK);
 		if (mark_set(sp, ABSMARK1, &cur, 1))
 			goto err;
 	}
 
 #if defined(DEBUG) && defined(COMLOG)
 	ex_comlog(sp, ecp);
 #endif
 	/* Increment the command count if not called from vi. */
 	if (F_ISSET(sp, SC_EX))
 		++sp->ccnt;
 
 	/*
 	 * If file state available, and not doing a global command,
 	 * log the start of an action.
 	 */
 	if (sp->ep != NULL && !F_ISSET(sp, SC_EX_GLOBAL))
 		(void)log_cursor(sp);
 
 	/*
 	 * !!!
 	 * There are two special commands for the purposes of this code: the
 	 * default command (<carriage-return>) or the scrolling commands (^D
 	 * and <EOF>) as the first non-<blank> characters  in the line.
 	 *
 	 * If this is the first command in the command line, we received the
 	 * command from the ex command loop and we're talking to a tty, and
 	 * and there's nothing else on the command line, and it's one of the
 	 * special commands, we move back up to the previous line, and erase
 	 * the prompt character with the output.  Since ex runs in canonical
 	 * mode, we don't have to do anything else, a <newline> has already
 	 * been echoed by the tty driver.  It's OK if vi calls us -- we won't
 	 * be in ex mode so we'll do nothing.
 	 */
 	if (F_ISSET(ecp, E_NRSEP)) {
 		if (sp->ep != NULL &&
 		    F_ISSET(sp, SC_EX) && !F_ISSET(gp, G_SCRIPTED) &&
 		    (F_ISSET(ecp, E_USELASTCMD) || ecp->cmd == &cmds[C_SCROLL]))
 			gp->scr_ex_adjust(sp, EX_TERM_SCROLL);
 		F_CLR(ecp, E_NRSEP);
 	}
 
 	/*
 	 * Call the underlying function for the ex command.
 	 *
 	 * XXX
 	 * Interrupts behave like errors, for now.
 	 */
 	if (ecp->cmd->fn(sp, ecp) || INTERRUPTED(sp)) {
 		if (F_ISSET(gp, G_SCRIPTED))
 			F_SET(sp, SC_EXIT_FORCE);
 		goto err;
 	}
 
 #ifdef DEBUG
 	/* Make sure no function left global temporary space locked. */
 	if (F_ISSET(gp, G_TMP_INUSE)) {
 		F_CLR(gp, G_TMP_INUSE);
 		msgq_wstr(sp, M_ERR, ecp->cmd->name,
 		    "087|%s: temporary buffer not released");
 	}
 #endif
 	/*
 	 * Ex displayed the number of lines modified immediately after each
 	 * command, so the command "1,10d|1,10d" would display:
 	 *
 	 *	10 lines deleted
 	 *	10 lines deleted
 	 *	<autoprint line>
 	 *
 	 * Executing ex commands from vi only reported the final modified
 	 * lines message -- that's wrong enough that we don't match it.
 	 */
 	if (F_ISSET(sp, SC_EX))
 		mod_rpt(sp);
 
 	/*
 	 * Integrate any offset parsed by the underlying command, and make
 	 * sure the referenced line exists.
 	 *
 	 * XXX
 	 * May not match historic practice (which I've never been able to
 	 * completely figure out.)  For example, the '=' command from vi
 	 * mode often got the offset wrong, and complained it was too large,
 	 * but didn't seem to have a problem with the cursor.  If anyone
 	 * complains, ask them how it's supposed to work, they might know.
 	 */
 	if (sp->ep != NULL && ecp->flagoff) {
 		if (ecp->flagoff < 0) {
 			if (sp->lno <= -ecp->flagoff) {
 				msgq(sp, M_ERR,
 				    "088|Flag offset to before line 1");
 				goto err;
 			}
 		} else {
 			if (!NPFITS(MAX_REC_NUMBER, sp->lno, ecp->flagoff)) {
 				ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
 				goto err;
 			}
 			if (!db_exist(sp, sp->lno + ecp->flagoff)) {
 				msgq(sp, M_ERR,
 				    "089|Flag offset past end-of-file");
 				goto err;
 			}
 		}
 		sp->lno += ecp->flagoff;
 	}
 
 	/*
 	 * If the command executed successfully, we may want to display a line
 	 * based on the autoprint option or an explicit print flag.  (Make sure
 	 * that there's a line to display.)  Also, the autoprint edit option is
 	 * turned off for the duration of global commands.
 	 */
 	if (F_ISSET(sp, SC_EX) && sp->ep != NULL && sp->lno != 0) {
 		/*
 		 * The print commands have already handled the `print' flags.
 		 * If so, clear them.
 		 */
 		if (FL_ISSET(ecp->iflags, E_CLRFLAG))
 			FL_CLR(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT);
 
 		/* If hash set only because of the number option, discard it. */
 		if (F_ISSET(ecp, E_OPTNUM))
 			FL_CLR(ecp->iflags, E_C_HASH);
 
 		/*
 		 * If there was an explicit flag to display the new cursor line,
 		 * or autoprint is set and a change was made, display the line.
 		 * If any print flags were set use them, else default to print.
 		 */
 		LF_INIT(FL_ISSET(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT));
 		if (!LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT | E_NOAUTO) &&
 		    !F_ISSET(sp, SC_EX_GLOBAL) &&
 		    O_ISSET(sp, O_AUTOPRINT) && F_ISSET(ecp, E_AUTOPRINT))
 			LF_INIT(E_C_PRINT);
 
 		if (LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT)) {
 			cur.lno = sp->lno;
 			cur.cno = 0;
 			(void)ex_print(sp, ecp, &cur, &cur, flags);
 		}
 	}
 
 	/*
 	 * If the command had an associated "+cmd", it has to be executed
 	 * before we finish executing any more of this ex command.  For
 	 * example, consider a .exrc file that contains the following lines:
 	 *
 	 *	:set all
 	 *	:edit +25 file.c|s/abc/ABC/|1
 	 *	:3,5 print
 	 *
 	 * This can happen more than once -- the historic vi simply hung or
 	 * dropped core, of course.  Prepend the + command back into the
 	 * current command and continue.  We may have to add an additional
 	 * <literal next> character.  We know that it will fit because we
 	 * discarded at least one space and the + character.
 	 */
 	if (arg1_len != 0) {
 		/*
 		 * If the last character of the + command was a <literal next>
 		 * character, it would be treated differently because of the
 		 * append.  Quote it, if necessary.
 		 */
 		if (IS_ESCAPE(sp, ecp, arg1[arg1_len - 1])) {
 			*--ecp->save_cmd = CH_LITERAL;
 			++ecp->save_cmdlen;
 		}
 
 		ecp->save_cmd -= arg1_len;
 		ecp->save_cmdlen += arg1_len;
 		MEMMOVE(ecp->save_cmd, arg1, arg1_len);
 
 		/*
 		 * Any commands executed from a +cmd are executed starting at
 		 * the first column of the last line of the file -- NOT the
 		 * first nonblank.)  The main file startup code doesn't know
 		 * that a +cmd was set, however, so it may have put us at the
 		 * top of the file.  (Note, this is safe because we must have
 		 * switched files to get here.)
 		 */
 		F_SET(ecp, E_MOVETOEND);
 	}
 
 	/* Update the current command. */
 	ecp->cp = ecp->save_cmd;
 	ecp->clen = ecp->save_cmdlen;
 
 	/*
 	 * !!!
 	 * If we've changed screens or underlying files, any pending global or
 	 * v command, or @ buffer that has associated addresses, has to be
 	 * discarded.  This is historic practice for globals, and necessary for
 	 * @ buffers that had associated addresses.
 	 *
 	 * Otherwise, if we've changed underlying files, it's not a problem,
 	 * we continue with the rest of the ex command(s), operating on the
 	 * new file.  However, if we switch screens (either by exiting or by
 	 * an explicit command), we have no way of knowing where to put output
 	 * messages, and, since we don't control screens here, we could screw
 	 * up the upper layers, (e.g. we could exit/reenter a screen multiple
 	 * times).  So, return and continue after we've got a new screen.
 	 */
 	if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_FSWITCH | SC_SSWITCH)) {
 		at_found = gv_found = 0;
 		SLIST_FOREACH(ecp, sp->gp->ecq, q)
 			switch (ecp->agv_flags) {
 			case 0:
 			case AGV_AT_NORANGE:
 				break;
 			case AGV_AT:
 				if (!at_found) {
 					at_found = 1;
 					msgq(sp, M_ERR,
 		"090|@ with range running when the file/screen changed");
 				}
 				break;
 			case AGV_GLOBAL:
 			case AGV_V:
 				if (!gv_found) {
 					gv_found = 1;
 					msgq(sp, M_ERR,
 		"091|Global/v command running when the file/screen changed");
 				}
 				break;
 			default:
 				abort();
 			}
 		if (at_found || gv_found)
 			goto discard;
 		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_SSWITCH))
 			goto rsuccess;
 	}
 
 	goto loop;
 	/* NOTREACHED */
 
 err:	/*
 	 * On command failure, we discard keys and pending commands remaining,
 	 * as well as any keys that were mapped and waiting.  The save_cmdlen
 	 * test is not necessarily correct.  If we fail early enough we don't
 	 * know if the entire string was a single command or not.  Guess, as
 	 * it's useful to know if commands other than the current one are being
 	 * discarded.
 	 */
 	if (ecp->save_cmdlen == 0)
 		for (; ecp->clen; --ecp->clen) {
 			ch = *ecp->cp++;
 			if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
 				--ecp->clen;
 				++ecp->cp;
 			} else if (ch == '\n' || ch == '|') {
 				if (ecp->clen > 1)
 					ecp->save_cmdlen = 1;
 				break;
 			}
 		}
 	if (ecp->save_cmdlen != 0 || SLIST_FIRST(gp->ecq) != &gp->excmd) {
 discard:	msgq(sp, M_BERR,
 		    "092|Ex command failed: pending commands discarded");
 		ex_discard(sp);
 	}
 	if (v_event_flush(sp, CH_MAPPED))
 		msgq(sp, M_BERR,
 		    "093|Ex command failed: mapped keys discarded");
 
 rfail:	tmp = 1;
 	if (0)
 rsuccess:	tmp = 0;
 
 	/* Turn off any file name error information. */
 	gp->if_name = NULL;
 
 	/* Turn off the global bit. */
 	F_CLR(sp, SC_EX_GLOBAL);
 
 	return (tmp);
 }
 
 /*
  * ex_range --
  *	Get a line range for ex commands, or perform a vi ex address search.
  *
  * PUBLIC: int ex_range(SCR *, EXCMD *, int *);
  */
 int
 ex_range(SCR *sp, EXCMD *ecp, int *errp)
 {
 	enum { ADDR_FOUND, ADDR_NEED, ADDR_NONE } addr;
 	GS *gp;
 	EX_PRIVATE *exp;
 	MARK m;
 	int isaddr;
 
 	*errp = 0;
 
 	/*
 	 * Parse comma or semi-colon delimited line specs.
 	 *
 	 * Semi-colon delimiters update the current address to be the last
 	 * address.  For example, the command
 	 *
 	 *	:3;/pattern/ecp->cp
 	 *
 	 * will search for pattern from line 3.  In addition, if ecp->cp
 	 * is not a valid command, the current line will be left at 3, not
 	 * at the original address.
 	 *
 	 * Extra addresses are discarded, starting with the first.
 	 *
 	 * !!!
 	 * If any addresses are missing, they default to the current line.
 	 * This was historically true for both leading and trailing comma
 	 * delimited addresses as well as for trailing semicolon delimited
 	 * addresses.  For consistency, we make it true for leading semicolon
 	 * addresses as well.
 	 */
 	gp = sp->gp;
 	exp = EXP(sp);
 	for (addr = ADDR_NONE, ecp->addrcnt = 0; ecp->clen > 0;)
 		switch (*ecp->cp) {
 		case '%':		/* Entire file. */
 			/* Vi ex address searches didn't permit % signs. */
 			if (F_ISSET(ecp, E_VISEARCH))
 				goto ret;
 
 			/* It's an error if the file is empty. */
 			if (sp->ep == NULL) {
 				ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
 				*errp = 1;
 				return (0);
 			}
 			/*
 			 * !!!
 			 * A percent character addresses all of the lines in
 			 * the file.  Historically, it couldn't be followed by
 			 * any other address.  We do it as a text substitution
 			 * for simplicity.  POSIX 1003.2 is expected to follow
 			 * this practice.
 			 *
 			 * If it's an empty file, the first line is 0, not 1.
 			 */
 			if (addr == ADDR_FOUND) {
 				ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
 				*errp = 1;
 				return (0);
 			}
 			if (db_last(sp, &ecp->addr2.lno))
 				return (1);
 			ecp->addr1.lno = ecp->addr2.lno == 0 ? 0 : 1;
 			ecp->addr1.cno = ecp->addr2.cno = 0;
 			ecp->addrcnt = 2;
 			addr = ADDR_FOUND;
 			++ecp->cp;
 			--ecp->clen;
 			break;
 		case ',':	       /* Comma delimiter. */
 			/* Vi ex address searches didn't permit commas. */
 			if (F_ISSET(ecp, E_VISEARCH))
 				goto ret;
 			/* FALLTHROUGH */
 		case ';':	       /* Semi-colon delimiter. */
 			if (sp->ep == NULL) {
 				ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
 				*errp = 1;
 				return (0);
 			}
 			if (addr != ADDR_FOUND)
 				switch (ecp->addrcnt) {
 				case 0:
 					ecp->addr1.lno = sp->lno;
 					ecp->addr1.cno = sp->cno;
 					ecp->addrcnt = 1;
 					break;
 				case 2:
 					ecp->addr1 = ecp->addr2;
 					/* FALLTHROUGH */
 				case 1:
 					ecp->addr2.lno = sp->lno;
 					ecp->addr2.cno = sp->cno;
 					ecp->addrcnt = 2;
 					break;
 				}
 			if (*ecp->cp == ';')
 				switch (ecp->addrcnt) {
 				case 0:
 					abort();
 					/* NOTREACHED */
 				case 1:
 					sp->lno = ecp->addr1.lno;
 					sp->cno = ecp->addr1.cno;
 					break;
 				case 2:
 					sp->lno = ecp->addr2.lno;
 					sp->cno = ecp->addr2.cno;
 					break;
 				}
 			addr = ADDR_NEED;
 			/* FALLTHROUGH */
 		case ' ':		/* Whitespace. */
 		case '\t':		/* Whitespace. */
 			++ecp->cp;
 			--ecp->clen;
 			break;
 		default:
 			/* Get a line specification. */
 			if (ex_line(sp, ecp, &m, &isaddr, errp))
 				return (1);
 			if (*errp)
 				return (0);
 			if (!isaddr)
 				goto ret;
 			if (addr == ADDR_FOUND) {
 				ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
 				*errp = 1;
 				return (0);
 			}
 			switch (ecp->addrcnt) {
 			case 0:
 				ecp->addr1 = m;
 				ecp->addrcnt = 1;
 				break;
 			case 1:
 				ecp->addr2 = m;
 				ecp->addrcnt = 2;
 				break;
 			case 2:
 				ecp->addr1 = ecp->addr2;
 				ecp->addr2 = m;
 				break;
 			}
 			addr = ADDR_FOUND;
 			break;
 		}
 
 	/*
 	 * !!!
 	 * Vi ex address searches are indifferent to order or trailing
 	 * semi-colons.
 	 */
 ret:	if (F_ISSET(ecp, E_VISEARCH))
 		return (0);
 
 	if (addr == ADDR_NEED)
 		switch (ecp->addrcnt) {
 		case 0:
 			ecp->addr1.lno = sp->lno;
 			ecp->addr1.cno = sp->cno;
 			ecp->addrcnt = 1;
 			break;
 		case 2:
 			ecp->addr1 = ecp->addr2;
 			/* FALLTHROUGH */
 		case 1:
 			ecp->addr2.lno = sp->lno;
 			ecp->addr2.cno = sp->cno;
 			ecp->addrcnt = 2;
 			break;
 		}
 
 	if (ecp->addrcnt == 2 && ecp->addr2.lno < ecp->addr1.lno) {
 		msgq(sp, M_ERR,
 		    "094|The second address is smaller than the first");
 		*errp = 1;
 	}
 	return (0);
 }
 
 /*
  * ex_line --
  *	Get a single line address specifier.
  *
  * The way the "previous context" mark worked was that any "non-relative"
  * motion set it.  While ex/vi wasn't totally consistent about this, ANY
  * numeric address, search pattern, '$', or mark reference in an address
  * was considered non-relative, and set the value.  Which should explain
  * why we're hacking marks down here.  The problem was that the mark was
  * only set if the command was called, i.e. we have to set a flag and test
  * it later.
  *
  * XXX
  * This is probably still not exactly historic practice, although I think
  * it's fairly close.
  */
 static int
 ex_line(SCR *sp, EXCMD *ecp, MARK *mp, int *isaddrp, int *errp)
 {
 	enum nresult nret;
 	EX_PRIVATE *exp;
 	GS *gp;
 	long total, val;
 	int isneg;
 	int (*sf)(SCR *, MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int);
 	CHAR_T *endp;
 
 	gp = sp->gp;
 	exp = EXP(sp);
 
 	*isaddrp = *errp = 0;
 	F_CLR(ecp, E_DELTA);
 
 	/* No addresses permitted until a file has been read in. */
 	if (sp->ep == NULL && STRCHR(L("$0123456789'\\/?.+-^"), *ecp->cp)) {
 		ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
 		*errp = 1;
 		return (0);
 	}
 
 	switch (*ecp->cp) {
 	case '$':				/* Last line in the file. */
 		*isaddrp = 1;
 		F_SET(ecp, E_ABSMARK);
 
 		mp->cno = 0;
 		if (db_last(sp, &mp->lno))
 			return (1);
 		++ecp->cp;
 		--ecp->clen;
 		break;				/* Absolute line number. */
 	case '0': case '1': case '2': case '3': case '4':
 	case '5': case '6': case '7': case '8': case '9':
 		*isaddrp = 1;
 		F_SET(ecp, E_ABSMARK);
 
 		if ((nret = nget_slong(&val, ecp->cp, &endp, 10)) != NUM_OK) {
 			ex_badaddr(sp, NULL, A_NOTSET, nret);
 			*errp = 1;
 			return (0);
 		}
 		if (!NPFITS(MAX_REC_NUMBER, 0, val)) {
 			ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
 			*errp = 1;
 			return (0);
 		}
 		mp->lno = val;
 		mp->cno = 0;
 		ecp->clen -= (endp - ecp->cp);
 		ecp->cp = endp;
 		break;
 	case '\'':				/* Use a mark. */
 		*isaddrp = 1;
 		F_SET(ecp, E_ABSMARK);
 
 		if (ecp->clen == 1) {
 			msgq(sp, M_ERR, "095|No mark name supplied");
 			*errp = 1;
 			return (0);
 		}
 		if (mark_get(sp, ecp->cp[1], mp, M_ERR)) {
 			*errp = 1;
 			return (0);
 		}
 		ecp->cp += 2;
 		ecp->clen -= 2;
 		break;
 	case '\\':				/* Search: forward/backward. */
 		/*
 		 * !!!
 		 * I can't find any difference between // and \/ or between
 		 * ?? and \?.  Mark Horton doesn't remember there being any
 		 * difference.  C'est la vie.
 		 */
 		if (ecp->clen < 2 ||
 		    (ecp->cp[1] != '/' && ecp->cp[1] != '?')) {
 			msgq(sp, M_ERR, "096|\\ not followed by / or ?");
 			*errp = 1;
 			return (0);
 		}
 		++ecp->cp;
 		--ecp->clen;
 		sf = ecp->cp[0] == '/' ? f_search : b_search;
 		goto search;
 	case '/':				/* Search forward. */
 		sf = f_search;
 		goto search;
 	case '?':				/* Search backward. */
 		sf = b_search;
 
 search:		mp->lno = sp->lno;
 		mp->cno = sp->cno;
 		if (sf(sp, mp, mp, ecp->cp, ecp->clen, &endp,
 		    SEARCH_MSG | SEARCH_PARSE | SEARCH_SET |
 		    (F_ISSET(ecp, E_SEARCH_WMSG) ? SEARCH_WMSG : 0))) {
 			*errp = 1;
 			return (0);
 		}
 
 		/* Fix up the command pointers. */
 		ecp->clen -= (endp - ecp->cp);
 		ecp->cp = endp;
 
 		*isaddrp = 1;
 		F_SET(ecp, E_ABSMARK);
 		break;
 	case '.':				/* Current position. */
 		*isaddrp = 1;
 		mp->cno = sp->cno;
 
 		/* If an empty file, then '.' is 0, not 1. */
 		if (sp->lno == 1) {
 			if (db_last(sp, &mp->lno))
 				return (1);
 			if (mp->lno != 0)
 				mp->lno = 1;
 		} else
 			mp->lno = sp->lno;
 
 		/*
 		 * !!!
 		 * Historically, .<number> was the same as .+<number>, i.e.
 		 * the '+' could be omitted.  (This feature is found in ed
 		 * as well.)
 		 */
 		if (ecp->clen > 1 && ISDIGIT(ecp->cp[1]))
 			*ecp->cp = '+';
 		else {
 			++ecp->cp;
 			--ecp->clen;
 		}
 		break;
 	}
 
 	/* Skip trailing <blank>s. */
 	for (; ecp->clen > 0 &&
 	    cmdskip(ecp->cp[0]); ++ecp->cp, --ecp->clen);
 
 	/*
 	 * Evaluate any offset.  If no address yet found, the offset
 	 * is relative to ".".
 	 */
 	total = 0;
 	if (ecp->clen != 0 && (ISDIGIT(ecp->cp[0]) ||
 	    ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
 	    ecp->cp[0] == '^')) {
 		if (!*isaddrp) {
 			*isaddrp = 1;
 			mp->lno = sp->lno;
 			mp->cno = sp->cno;
 		}
 		/*
 		 * Evaluate an offset, defined as:
 		 *
 		 *		[+-^<blank>]*[<blank>]*[0-9]*
 		 *
 		 * The rough translation is any number of signs, optionally
 		 * followed by numbers, or a number by itself, all <blank>
 		 * separated.
 		 *
 		 * !!!
 		 * All address offsets were additive, e.g. "2 2 3p" was the
 		 * same as "7p", or, "/ZZZ/ 2" was the same as "/ZZZ/+2".
 		 * Note, however, "2 /ZZZ/" was an error.  It was also legal
 		 * to insert signs without numbers, so "3 - 2" was legal, and
 		 * equal to 4.
 		 *
 		 * !!!
 		 * Offsets were historically permitted for any line address,
 		 * e.g. the command "1,2 copy 2 2 2 2" copied lines 1,2 after
 		 * line 8.
 		 *
 		 * !!!
 		 * Offsets were historically permitted for search commands,
 		 * and handled as addresses: "/pattern/2 2 2" was legal, and
 		 * referenced the 6th line after pattern.
 		 */
 		F_SET(ecp, E_DELTA);
 		for (;;) {
 			for (; ecp->clen > 0 && cmdskip(ecp->cp[0]);
 			    ++ecp->cp, --ecp->clen);
 			if (ecp->clen == 0 || (!ISDIGIT(ecp->cp[0]) &&
 			    ecp->cp[0] != '+' && ecp->cp[0] != '-' &&
 			    ecp->cp[0] != '^'))
 				break;
 			if (!ISDIGIT(ecp->cp[0]) &&
 			    !ISDIGIT(ecp->cp[1])) {
 				total += ecp->cp[0] == '+' ? 1 : -1;
 				--ecp->clen;
 				++ecp->cp;
 			} else {
 				if (ecp->cp[0] == '-' ||
 				    ecp->cp[0] == '^') {
 					++ecp->cp;
 					--ecp->clen;
 					isneg = 1;
 				} else
 					isneg = 0;
 
 				/* Get a signed long, add it to the total. */
 				if ((nret = nget_slong(&val,
 				    ecp->cp, &endp, 10)) != NUM_OK ||
 				    (nret = NADD_SLONG(sp,
 				    total, val)) != NUM_OK) {
 					ex_badaddr(sp, NULL, A_NOTSET, nret);
 					*errp = 1;
 					return (0);
 				}
 				total += isneg ? -val : val;
 				ecp->clen -= (endp - ecp->cp);
 				ecp->cp = endp;
 			}
 		}
 	}
 
 	/*
 	 * Any value less than 0 is an error.  Make sure that the new value
 	 * will fit into a recno_t.
 	 */
 	if (*isaddrp && total != 0) {
 		if (total < 0) {
 			if (-total > mp->lno) {
 				msgq(sp, M_ERR,
 			    "097|Reference to a line number less than 0");
 				*errp = 1;
 				return (0);
 			}
 		} else
 			if (!NPFITS(MAX_REC_NUMBER, mp->lno, total)) {
 				ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
 				*errp = 1;
 				return (0);
 			}
 		mp->lno += total;
 	}
 	return (0);
 }
 
 
 /*
  * ex_load --
  *	Load up the next command, which may be an @ buffer or global command.
  */
 static int
 ex_load(SCR *sp)
 {
 	GS *gp;
 	EXCMD *ecp;
 	RANGE *rp;
 
 	F_CLR(sp, SC_EX_GLOBAL);
 
 	/*
 	 * Lose any exhausted commands.  We know that the first command
 	 * can't be an AGV command, which makes things a bit easier.
 	 */
 	for (gp = sp->gp;;) {
 		ecp = SLIST_FIRST(gp->ecq);
 
 		/* Discard the allocated source name as requested. */
 		if (F_ISSET(ecp, E_NAMEDISCARD))
 			free(ecp->if_name);
 
 		/*
 		 * If we're back to the original structure, leave it around,
 		 * since we've returned to the beginning of the command stack.
 		 */
 		if (ecp == &gp->excmd) {
 			ecp->if_name = NULL;
 			return (0);
 		}
 
 		/*
 		 * ecp->clen will be 0 for the first discarded command, but
 		 * may not be 0 for subsequent ones, e.g. if the original
 		 * command was ":g/xx/@a|s/b/c/", then when we discard the
 		 * command pushed on the stack by the @a, we have to resume
 		 * the global command which included the substitute command.
 		 */
 		if (ecp->clen != 0)
 			return (0);
 
 		/*
 		 * If it's an @, global or v command, we may need to continue
 		 * the command on a different line.
 		 */
 		if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
 			/* Discard any exhausted ranges. */
 			while ((rp = TAILQ_FIRST(ecp->rq)) != NULL)
 				if (rp->start > rp->stop) {
 					TAILQ_REMOVE(ecp->rq, rp, q);
 					free(rp);
 				} else
 					break;
 
 			/* If there's another range, continue with it. */
 			if (rp != NULL)
 				break;
 
 			/* If it's a global/v command, fix up the last line. */
 			if (FL_ISSET(ecp->agv_flags,
-			    AGV_GLOBAL | AGV_V) && ecp->range_lno != OOBLNO)
+			    AGV_GLOBAL | AGV_V) && ecp->range_lno != OOBLNO) {
 				if (db_exist(sp, ecp->range_lno))
 					sp->lno = ecp->range_lno;
 				else {
 					if (db_last(sp, &sp->lno))
 						return (1);
 					if (sp->lno == 0)
 						sp->lno = 1;
 				}
+			}
 			free(ecp->o_cp);
 		}
 
 		/* Discard the EXCMD. */
 		SLIST_REMOVE_HEAD(gp->ecq, q);
 		free(ecp);
 	}
 
 	/*
 	 * We only get here if it's an active @, global or v command.  Set
 	 * the current line number, and get a new copy of the command for
 	 * the parser.  Note, the original pointer almost certainly moved,
 	 * so we have play games.
 	 */
 	ecp->cp = ecp->o_cp;
 	MEMCPY(ecp->cp, ecp->cp + ecp->o_clen, ecp->o_clen);
 	ecp->clen = ecp->o_clen;
 	ecp->range_lno = sp->lno = rp->start++;
 
 	if (FL_ISSET(ecp->agv_flags, AGV_GLOBAL | AGV_V))
 		F_SET(sp, SC_EX_GLOBAL);
 	return (0);
 }
 
 /*
  * ex_discard --
  *	Discard any pending ex commands.
  */
 static int
 ex_discard(SCR *sp)
 {
 	GS *gp;
 	EXCMD *ecp;
 	RANGE *rp;
 
 	/*
 	 * We know the first command can't be an AGV command, so we don't
 	 * process it specially.  We do, however, nail the command itself.
 	 */
 	for (gp = sp->gp;;) {
 		ecp = SLIST_FIRST(gp->ecq);
 		if (F_ISSET(ecp, E_NAMEDISCARD))
 			free(ecp->if_name);
 		/* Reset the last command without dropping it. */
 		if (ecp == &gp->excmd)
 			break;
 		if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
 			while ((rp = TAILQ_FIRST(ecp->rq)) != NULL) {
 				TAILQ_REMOVE(ecp->rq, rp, q);
 				free(rp);
 			}
 			free(ecp->o_cp);
 		}
 		SLIST_REMOVE_HEAD(gp->ecq, q);
 		free(ecp);
 	}
 
 	ecp->if_name = NULL;
 	ecp->clen = 0;
 	return (0);
 }
 
 /*
  * ex_unknown --
  *	Display an unknown command name.
  */
 static void
 ex_unknown(SCR *sp, CHAR_T *cmd, size_t len)
 {
 	size_t blen;
 	CHAR_T *bp;
 
 	GET_SPACE_GOTOW(sp, bp, blen, len + 1);
 	bp[len] = '\0';
 	MEMCPY(bp, cmd, len);
 	msgq_wstr(sp, M_ERR, bp, "098|The %s command is unknown");
 	FREE_SPACEW(sp, bp, blen);
 
 alloc_err:
 	return;
 }
 
 /*
  * ex_is_abbrev -
  *	The vi text input routine needs to know if ex thinks this is an
  *	[un]abbreviate command, so it can turn off abbreviations.  See
  *	the usual ranting in the vi/v_txt_ev.c:txt_abbrev() routine.
  *
  * PUBLIC: int ex_is_abbrev(CHAR_T *, size_t);
  */
 int
 ex_is_abbrev(CHAR_T *name, size_t len)
 {
 	EXCMDLIST const *cp;
 
 	return ((cp = ex_comm_search(name, len)) != NULL &&
 	    (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
 }
 
 /*
  * ex_is_unmap -
  *	The vi text input routine needs to know if ex thinks this is an
  *	unmap command, so it can turn off input mapping.  See the usual
  *	ranting in the vi/v_txt_ev.c:txt_unmap() routine.
  *
  * PUBLIC: int ex_is_unmap(CHAR_T *, size_t);
  */
 int
 ex_is_unmap(CHAR_T *name, size_t len)
 {
 	EXCMDLIST const *cp;
 
 	/*
 	 * The command the vi input routines are really interested in
 	 * is "unmap!", not just unmap.
 	 */
 	if (name[len - 1] != '!')
 		return (0);
 	--len;
 	return ((cp = ex_comm_search(name, len)) != NULL &&
 	    cp == &cmds[C_UNMAP]);
 }
 
 /*
  * ex_comm_search --
  *	Search for a command name.
  */
 static EXCMDLIST const *
 ex_comm_search(CHAR_T *name, size_t len)
 {
 	EXCMDLIST const *cp;
 
 	for (cp = cmds; cp->name != NULL; ++cp) {
 		if (cp->name[0] > name[0])
 			return (NULL);
 		if (cp->name[0] != name[0])
 			continue;
 		if (!MEMCMP(name, cp->name, len))
 			return (cp);
 	}
 	return (NULL);
 }
 
 /*
  * ex_badaddr --
  *	Display a bad address message.
  *
  * PUBLIC: void ex_badaddr
  * PUBLIC:   (SCR *, EXCMDLIST const *, enum badaddr, enum nresult);
  */
 void
 ex_badaddr(SCR *sp, const EXCMDLIST *cp, enum badaddr ba, enum nresult nret)
 {
 	recno_t lno;
 
 	switch (nret) {
 	case NUM_OK:
 		break;
 	case NUM_ERR:
 		msgq(sp, M_SYSERR, NULL);
 		return;
 	case NUM_OVER:
 		msgq(sp, M_ERR, "099|Address value overflow");
 		return;
 	case NUM_UNDER:
 		msgq(sp, M_ERR, "100|Address value underflow");
 		return;
 	}
 
 	/*
 	 * When encountering an address error, tell the user if there's no
 	 * underlying file, that's the real problem.
 	 */
 	if (sp->ep == NULL) {
 		ex_wemsg(sp, cp ? cp->name : NULL, EXM_NOFILEYET);
 		return;
 	}
 
 	switch (ba) {
 	case A_COMBO:
 		msgq(sp, M_ERR, "101|Illegal address combination");
 		break;
 	case A_EOF:
 		if (db_last(sp, &lno))
 			return;
 		if (lno != 0) {
 			msgq(sp, M_ERR,
 			    "102|Illegal address: only %lu lines in the file",
 			    (u_long)lno);
 			break;
 		}
 		/* FALLTHROUGH */
 	case A_EMPTY:
 		msgq(sp, M_ERR, "103|Illegal address: the file is empty");
 		break;
 	case A_NOTSET:
 		abort();
 		/* NOTREACHED */
 	case A_ZERO:
 		msgq_wstr(sp, M_ERR, cp->name,
 		    "104|The %s command doesn't permit an address of 0");
 		break;
 	}
 	return;
 }
 
 #if defined(DEBUG) && defined(COMLOG)
 /*
  * ex_comlog --
  *	Log ex commands.
  */
 static void
 ex_comlog(sp, ecp)
 	SCR *sp;
 	EXCMD *ecp;
 {
 	TRACE(sp, "ecmd: "WS, ecp->cmd->name);
 	if (ecp->addrcnt > 0) {
 		TRACE(sp, " a1 %d", ecp->addr1.lno);
 		if (ecp->addrcnt > 1)
 			TRACE(sp, " a2: %d", ecp->addr2.lno);
 	}
 	if (ecp->lineno)
 		TRACE(sp, " line %d", ecp->lineno);
 	if (ecp->flags)
 		TRACE(sp, " flags 0x%x", ecp->flags);
 	if (FL_ISSET(ecp->iflags, E_C_BUFFER))
 		TRACE(sp, " buffer "WC, ecp->buffer);
 	if (ecp->argc) {
 		int cnt;
 		for (cnt = 0; cnt < ecp->argc; ++cnt)
 			TRACE(sp, " arg %d: {"WS"}", cnt, ecp->argv[cnt]->bp);
 	}
 	TRACE(sp, "\n");
 }
 #endif
Index: head/contrib/nvi/ex/ex.h
===================================================================
--- head/contrib/nvi/ex/ex.h	(revision 366308)
+++ head/contrib/nvi/ex/ex.h	(revision 366309)
@@ -1,231 +1,231 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #define	PROMPTCHAR	':'		/* Prompt using a colon. */
 
 typedef struct _excmdlist {		/* Ex command table structure. */
 	CHAR_T *name;			/* Command name, underlying function. */
 	int (*fn)(SCR *, EXCMD *);
 
 #define	E_ADDR1		0x00000001	/* One address. */
 #define	E_ADDR2		0x00000002	/* Two addresses. */
 #define	E_ADDR2_ALL	0x00000004	/* Zero/two addresses; zero == all. */
 #define	E_ADDR2_NONE	0x00000008	/* Zero/two addresses; zero == none. */
 #define	E_ADDR_ZERO	0x00000010	/* 0 is a legal addr1. */
 #define	E_ADDR_ZERODEF	0x00000020	/* 0 is default addr1 of empty files. */
 #define	E_AUTOPRINT	0x00000040	/* Command always sets autoprint. */
 #define	E_CLRFLAG	0x00000080	/* Clear the print (#, l, p) flags. */
 #define	E_NEWSCREEN	0x00000100	/* Create a new screen. */
 #define	E_SECURE	0x00000200	/* Permission denied if O_SECURE set. */
 #define	E_VIONLY	0x00000400	/* Meaningful only in vi. */
 #define	__INUSE1	0xfffff800	/* Same name space as EX_PRIVATE. */
 	u_int16_t flags;
 
 	char *syntax;			/* Syntax script. */
 	char *usage;			/* Usage line. */
 	char *help;			/* Help line. */
 } EXCMDLIST;
 
 #define	MAXCMDNAMELEN	12		/* Longest command name. */
 extern EXCMDLIST const cmds[];		/* Table of ex commands. */
 
 /*
  * !!!
  * QUOTING NOTE:
  *
  * Historically, .exrc files and EXINIT variables could only use ^V as an
  * escape character, neither ^Q or a user specified character worked.  We
  * enforce that here, just in case someone depends on it.
  */
 #define	IS_ESCAPE(sp, cmdp, ch)						\
 	(F_ISSET(cmdp, E_VLITONLY) ?					\
 	    (ch) == CH_LITERAL : KEY_VAL(sp, ch) == K_VLNEXT)
 
 #define	IS_SHELLMETA(sp, ch)						\
 	((ch) <= CHAR_MAX && strchr(O_STR(sp, O_SHELLMETA), ch) != NULL)
 
 /*
  * File state must be checked for each command -- any ex command may be entered
  * at any time, and most of them won't work well if a file hasn't yet been read
  * in.  Historic vi generally took the easy way out and dropped core.
  */
-#define	NEEDFILE(sp, cmdp) {						\
+#define	NEEDFILE(sp, cmdp) do {						\
 	if ((sp)->ep == NULL) {						\
 		ex_wemsg(sp, (cmdp)->cmd->name, EXM_NOFILEYET);		\
 		return (1);						\
 	}								\
-}
+} while (0)
 
 /* Range structures for global and @ commands. */
 typedef struct _range RANGE;
 struct _range {				/* Global command range. */
 	TAILQ_ENTRY(_range) q;		/* Linked list of ranges. */
 	recno_t start, stop;		/* Start/stop of the range. */
 };
 
 /* Ex command structure. */
 struct _excmd {
 	SLIST_ENTRY(_excmd) q;		/* Linked list of commands. */
 
 	char	 *if_name;		/* Associated file. */
 	recno_t	  if_lno;		/* Associated line number. */
 
 	/* Clear the structure for the ex parser. */
 #define	CLEAR_EX_PARSER(cmdp)						\
 	memset(&((cmdp)->cp), 0, ((char *)&(cmdp)->flags -		\
 	    (char *)&((cmdp)->cp)) + sizeof((cmdp)->flags))
 
 	CHAR_T	 *cp;			/* Current command text. */
 	size_t	  clen;			/* Current command length. */
 
 	CHAR_T	 *save_cmd;		/* Remaining command. */
 	size_t	  save_cmdlen;		/* Remaining command length. */
 
 	EXCMDLIST const *cmd;		/* Command: entry in command table. */
 	EXCMDLIST rcmd;			/* Command: table entry/replacement. */
 
 	TAILQ_HEAD(_rh, _range) rq[1];	/* @/global range: linked list. */
 	recno_t   range_lno;		/* @/global range: set line number. */
 	CHAR_T	 *o_cp;			/* Original @/global command. */
 	size_t	  o_clen;		/* Original @/global command length. */
 #define	AGV_AT		0x01		/* @ buffer execution. */
 #define	AGV_AT_NORANGE	0x02		/* @ buffer execution without range. */
 #define	AGV_GLOBAL	0x04		/* global command. */
 #define	AGV_V		0x08		/* v command. */
 #define	AGV_ALL		(AGV_AT | AGV_AT_NORANGE | AGV_GLOBAL | AGV_V)
 	u_int8_t  agv_flags;
 
 	/* Clear the structure before each ex command. */
-#define	CLEAR_EX_CMD(cmdp) {						\
+#define	CLEAR_EX_CMD(cmdp) do {						\
 	u_int32_t L__f = F_ISSET(cmdp, E_PRESERVE);			\
 	memset(&((cmdp)->buffer), 0, ((char *)&(cmdp)->flags -		\
 	    (char *)&((cmdp)->buffer)) + sizeof((cmdp)->flags));	\
 	F_SET(cmdp, L__f);						\
-}
+} while (0)
 
 	CHAR_T	  buffer;		/* Command: named buffer. */
 	recno_t	  lineno;		/* Command: line number. */
 	long	  count;		/* Command: signed count. */
 	long	  flagoff;		/* Command: signed flag offset. */
 	int	  addrcnt;		/* Command: addresses (0, 1 or 2). */
 	MARK	  addr1;		/* Command: 1st address. */
 	MARK	  addr2;		/* Command: 2nd address. */
 	ARGS	**argv;			/* Command: array of arguments. */
 	int	  argc;			/* Command: count of arguments. */
 
 #define	E_C_BUFFER	0x00001		/* Buffer name specified. */
 #define	E_C_CARAT	0x00002		/*  ^ flag. */
 #define	E_C_COUNT	0x00004		/* Count specified. */
 #define	E_C_COUNT_NEG	0x00008		/* Count was signed negative. */
 #define	E_C_COUNT_POS	0x00010		/* Count was signed positive. */
 #define	E_C_DASH	0x00020		/*  - flag. */
 #define	E_C_DOT		0x00040		/*  . flag. */
 #define	E_C_EQUAL	0x00080		/*  = flag. */
 #define	E_C_FORCE	0x00100		/*  ! flag. */
 #define	E_C_HASH	0x00200		/*  # flag. */
 #define	E_C_LIST	0x00400		/*  l flag. */
 #define	E_C_PLUS	0x00800		/*  + flag. */
 #define	E_C_PRINT	0x01000		/*  p flag. */
 	u_int16_t iflags;		/* User input information. */
 
 #define	__INUSE2	0x000007ff	/* Same name space as EXCMDLIST. */
 #define	E_BLIGNORE	0x00000800	/* Ignore blank lines. */
 #define	E_NAMEDISCARD	0x00001000	/* Free/discard the name. */
 #define	E_NOAUTO	0x00002000	/* Don't do autoprint output. */
 #define	E_NOPRDEF	0x00004000	/* Don't print as default. */
 #define	E_NRSEP		0x00008000	/* Need to line adjust ex output. */
 #define	E_OPTNUM	0x00010000	/* Number edit option affected. */
 #define	E_VLITONLY	0x00020000	/* Use ^V quoting only. */
 #define	E_PRESERVE	0x0003f800	/* Bits to preserve across commands. */
 
 #define	E_ABSMARK	0x00040000	/* Set the absolute mark. */
 #define	E_ADDR_DEF	0x00080000	/* Default addresses used. */
 #define	E_DELTA		0x00100000	/* Search address with delta. */
 #define	E_MODIFY	0x00200000	/* File name expansion modified arg. */
 #define	E_MOVETOEND	0x00400000	/* Move to the end of the file first. */
 #define	E_NEWLINE	0x00800000	/* Found ending <newline>. */
 #define	E_SEARCH_WMSG	0x01000000	/* Display search-wrapped message. */
 #define	E_USELASTCMD	0x02000000	/* Use the last command. */
 #define	E_VISEARCH	0x04000000	/* It's really a vi search command. */
 	u_int32_t flags;		/* Current flags. */
 };
 
 /* Ex private, per-screen memory. */
 typedef struct _ex_private {
 					/* Tag file list. */
 	TAILQ_HEAD(_tagfh, _tagf) tagfq[1];
 	TAILQ_HEAD(_tqh, _tagq) tq[1];	/* Tag queue. */
 	SLIST_HEAD(_csch, _csc) cscq[1];/* Cscope connection list. */
 	CHAR_T	*tag_last;		/* Saved last tag string. */
 
 	CHAR_T	*lastbcomm;		/* Last bang command. */
 
 	ARGS   **args;			/* Command: argument list. */
 	int	 argscnt;		/* Command: argument list count. */
 	int	 argsoff;		/* Command: offset into arguments. */
 
 	u_int32_t fdef;			/* Saved E_C_* default command flags. */
 
 	char	*ibp;			/* File line input buffer. */
 	size_t	 ibp_len;		/* File line input buffer length. */
 	CONVWIN	 ibcw;			/* File line input conversion buffer. */
 
 	/*
 	 * Buffers for the ex output.  The screen/vi support doesn't do any
 	 * character buffering of any kind.  We do it here so that we're not
 	 * calling the screen output routines on every character.
 	 *
 	 * XXX
 	 * Change to grow dynamically.
 	 */
 	char	 obp[1024];		/* Ex output buffer. */
 	size_t	 obp_len;		/* Ex output buffer length. */
 
 #define	EXP_CSCINIT	0x01		/* Cscope initialized. */
 	u_int8_t flags;
 } EX_PRIVATE;
 #define	EXP(sp)	((EX_PRIVATE *)((sp)->ex_private))
 
 /*
  * Filter actions:
  *
  *	FILTER_BANG	!:	filter text through the utility.
  *	FILTER_RBANG	!:	read from the utility (without stdin).
  *	FILTER_READ	read:	read from the utility (with stdin).
  *	FILTER_WRITE	write:	write to the utility, display its output.
  */
 enum filtertype { FILTER_BANG, FILTER_RBANG, FILTER_READ, FILTER_WRITE };
 
 /* Ex common error messages. */
 typedef enum {
 	EXM_EMPTYBUF,			/* Empty buffer. */
 	EXM_FILECOUNT,			/* Too many file names. */
 	EXM_NOCANON,			/* No terminal interface. */
 	EXM_NOCANON_F,			/* EXM_NOCANO: filter version. */
 	EXM_NOFILEYET,			/* Illegal until a file read in. */
 	EXM_NOPREVBUF,			/* No previous buffer specified. */
 	EXM_NOPREVRE,			/* No previous RE specified. */
 	EXM_NOSUSPEND,			/* No suspension. */
 	EXM_SECURE,			/* Illegal if secure edit option set. */
 	EXM_SECURE_F,			/* EXM_SECURE: filter version */
 	EXM_USAGE			/* Standard usage message. */
 } exm_t;
 
 /* Ex address error types. */
 enum badaddr { A_COMBO, A_EMPTY, A_EOF, A_NOTSET, A_ZERO };
 
 /* Ex common tag error messages. */
 typedef enum {
 	TAG_BADLNO,		/* Tag line doesn't exist. */
 	TAG_EMPTY,		/* Tags stack is empty. */
 	TAG_SEARCH		/* Tags search pattern wasn't found. */
 } tagmsg_t;
 
 #include "ex_def.h"
 #include "extern.h"
Index: head/contrib/nvi/ex/ex_argv.c
===================================================================
--- head/contrib/nvi/ex/ex_argv.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_argv.c	(revision 366309)
@@ -1,910 +1,918 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <limits.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 
 static int argv_alloc(SCR *, size_t);
 static int argv_comp(const void *, const void *);
 static int argv_fexp(SCR *, EXCMD *,
 	CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int);
 static int argv_sexp(SCR *, CHAR_T **, size_t *, size_t *);
 static int argv_flt_user(SCR *, EXCMD *, CHAR_T *, size_t);
 
 /*
  * argv_init --
  *	Build  a prototype arguments list.
  *
  * PUBLIC: int argv_init(SCR *, EXCMD *);
  */
 int
 argv_init(SCR *sp, EXCMD *excp)
 {
 	EX_PRIVATE *exp;
 
 	exp = EXP(sp);
 	exp->argsoff = 0;
 	argv_alloc(sp, 1);
 
 	excp->argv = exp->args;
 	excp->argc = exp->argsoff;
 	return (0);
 }
 
 /*
  * argv_exp0 --
  *	Append a string to the argument list.
  *
  * PUBLIC: int argv_exp0(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 int
 argv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
 {
 	EX_PRIVATE *exp;
 
 	exp = EXP(sp);
 	argv_alloc(sp, cmdlen);
 	MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen);
 	exp->args[exp->argsoff]->bp[cmdlen] = '\0';
 	exp->args[exp->argsoff]->len = cmdlen;
 	++exp->argsoff;
 	excp->argv = exp->args;
 	excp->argc = exp->argsoff;
 	return (0);
 }
 
 /*
  * argv_exp1 --
  *	Do file name expansion on a string, and append it to the
  *	argument list.
  *
  * PUBLIC: int argv_exp1(SCR *, EXCMD *, CHAR_T *, size_t, int);
  */
 int
 argv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang)
 {
 	EX_PRIVATE *exp;
 	size_t blen, len;
 	CHAR_T *p, *t, *bp;
 
 	GET_SPACE_RETW(sp, bp, blen, 512);
 
 	len = 0;
 	exp = EXP(sp);
 	if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
 		FREE_SPACEW(sp, bp, blen);
 		return (1);
 	}
 
 	/* If it's empty, we're done. */
 	if (len != 0) {
 		for (p = bp, t = bp + len; p < t; ++p)
 			if (!cmdskip(*p))
 				break;
 		if (p == t)
 			goto ret;
 	} else
 		goto ret;
 
 	(void)argv_exp0(sp, excp, bp, len);
 
 ret:	FREE_SPACEW(sp, bp, blen);
 	return (0);
 }
 
 /*
  * argv_exp2 --
  *	Do file name and shell expansion on a string, and append it to
  *	the argument list.
  *
  * PUBLIC: int argv_exp2(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 int
 argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
 {
 	size_t blen, len, n;
 	int rval;
 	CHAR_T *bp, *p;
 
 	GET_SPACE_RETW(sp, bp, blen, 512);
 
 #define	SHELLECHO	L("echo ")
 #define	SHELLOFFSET	(SIZE(SHELLECHO) - 1)
 	MEMCPY(bp, SHELLECHO, SHELLOFFSET);
 	p = bp + SHELLOFFSET;
 	len = SHELLOFFSET;
 
 #if defined(DEBUG) && 0
 	TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
 #endif
 
 	if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
 		rval = 1;
 		goto err;
 	}
 
 #if defined(DEBUG) && 0
 	TRACE(sp, "before shell: %d: {%s}\n", len, bp);
 #endif
 
 	/*
 	 * Do shell word expansion -- it's very, very hard to figure out what
 	 * magic characters the user's shell expects.  Historically, it was a
 	 * union of v7 shell and csh meta characters.  We match that practice
 	 * by default, so ":read \%" tries to read a file named '%'.  It would
 	 * make more sense to pass any special characters through the shell,
 	 * but then, if your shell was csh, the above example will behave
 	 * differently in nvi than in vi.  If you want to get other characters
 	 * passed through to your shell, change the "meta" option.
 	 */
 	if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
 		n = 0;
 	else {
 		p = bp + SHELLOFFSET;
 		n = len - SHELLOFFSET;
 		for (; n > 0; --n, ++p)
 			if (IS_SHELLMETA(sp, *p))
 				break;
 	}
 
 	/*
 	 * If we found a meta character in the string, fork a shell to expand
 	 * it.  Unfortunately, this is comparatively slow.  Historically, it
 	 * didn't matter much, since users don't enter meta characters as part
 	 * of pathnames that frequently.  The addition of filename completion
 	 * broke that assumption because it's easy to use.  To increase the
 	 * completion performance, nvi used to have an internal routine to
 	 * handle "filename*".  However, the shell special characters does not
 	 * limit to "shellmeta", so such a hack breaks historic practice.
 	 * After it all, we split the completion logic out from here.
 	 */
 	switch (n) {
 	case 0:
 		p = bp + SHELLOFFSET;
 		len -= SHELLOFFSET;
 		rval = argv_exp3(sp, excp, p, len);
 		break;
 	default:
 		if (argv_sexp(sp, &bp, &blen, &len)) {
 			rval = 1;
 			goto err;
 		}
 		p = bp;
 		rval = argv_exp3(sp, excp, p, len);
 		break;
 	}
 
 err:	FREE_SPACEW(sp, bp, blen);
 	return (rval);
 }
 
 /*
  * argv_exp3 --
  *	Take a string and break it up into an argv, which is appended
  *	to the argument list.
  *
  * PUBLIC: int argv_exp3(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 int
 argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
 {
 	EX_PRIVATE *exp;
 	size_t len;
 	int ch, off;
 	CHAR_T *ap, *p;
 
 	for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
 		/* Skip any leading whitespace. */
 		for (; cmdlen > 0; --cmdlen, ++cmd) {
 			ch = *cmd;
 			if (!cmdskip(ch))
 				break;
 		}
 		if (cmdlen == 0)
 			break;
 
 		/*
 		 * Determine the length of this whitespace delimited
 		 * argument.
 		 *
 		 * QUOTING NOTE:
 		 *
 		 * Skip any character preceded by the user's quoting
 		 * character.
 		 */
 		for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
 			ch = *cmd;
 			if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
 				++cmd;
 				--cmdlen;
 			} else if (cmdskip(ch))
 				break;
 		}
 
 		/*
 		 * Copy the argument into place.
 		 *
 		 * QUOTING NOTE:
 		 *
 		 * Lose quote chars.
 		 */
 		argv_alloc(sp, len);
 		off = exp->argsoff;
 		exp->args[off]->len = len;
 		for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
 			if (IS_ESCAPE(sp, excp, *ap))
 				++ap;
 		*p = '\0';
 	}
 	excp->argv = exp->args;
 	excp->argc = exp->argsoff;
 
 #if defined(DEBUG) && 0
 	for (cnt = 0; cnt < exp->argsoff; ++cnt)
 		TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
 #endif
 	return (0);
 }
 
 /*
  * argv_flt_ex --
  *	Filter the ex commands with a prefix, and append the results to
  *	the argument list.
  *
  * PUBLIC: int argv_flt_ex(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 int
 argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
 {
 	EX_PRIVATE *exp;
 	EXCMDLIST const *cp;
 	int off;
 	size_t len;
 
 	exp = EXP(sp);
 
 	for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) {
 		len = STRLEN(cp->name);
 		if (cmdlen > 0 &&
 		    (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen)))
 			continue;
 
 		/* Copy the matched ex command name. */
 		argv_alloc(sp, len + 1);
 		MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1);
 		exp->args[exp->argsoff]->len = len;
 		++exp->argsoff;
 		excp->argv = exp->args;
 		excp->argc = exp->argsoff;
 	}
 
 	return (0);
 }
 
 /*
  * argv_flt_user --
  *	Filter the ~user list on the system with a prefix, and append
  *	the results to the argument list.
  */
 static int
 argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen)
 {
 	EX_PRIVATE *exp;
 	struct passwd *pw;
 	int off;
 	char *np;
 	size_t len, nlen;
 
 	exp = EXP(sp);
 	off = exp->argsoff;
 
 	/* The input must come with a leading '~'. */
 	INT2CHAR(sp, uname + 1, ulen - 1, np, nlen);
 	if ((np = v_strdup(sp, np, nlen)) == NULL)
 		return (1);
 
 	setpwent();
 	while ((pw = getpwent()) != NULL) {
 		len = strlen(pw->pw_name);
 		if (nlen > 0 &&
 		    (nlen > len || memcmp(np, pw->pw_name, nlen)))
 			continue;
 
 		/* Copy '~' + the matched user name. */
 		CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen);
 		argv_alloc(sp, ulen + 1);
 		exp->args[exp->argsoff]->bp[0] = '~';
 		MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen);
 		exp->args[exp->argsoff]->len = ulen;
 		++exp->argsoff;
 		excp->argv = exp->args;
 		excp->argc = exp->argsoff;
 	}
 	endpwent();
 	free(np);
 
 	qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
 	return (0);
 }
 
 /*
  * argv_fexp --
  *	Do file name and bang command expansion.
  */
 static int
 argv_fexp(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang)
 {
 	EX_PRIVATE *exp;
 	char *t;
 	size_t blen, len, off, tlen;
 	CHAR_T *bp;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/* Replace file name characters. */
 	for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
 		switch (*cmd) {
 		case '!':
 			if (!is_bang)
 				goto ins_ch;
 			exp = EXP(sp);
 			if (exp->lastbcomm == NULL) {
 				msgq(sp, M_ERR,
 				    "115|No previous command to replace \"!\"");
 				return (1);
 			}
 			len += tlen = STRLEN(exp->lastbcomm);
 			off = p - bp;
 			ADD_SPACE_RETW(sp, bp, blen, len);
 			p = bp + off;
 			MEMCPY(p, exp->lastbcomm, tlen);
 			p += tlen;
 			F_SET(excp, E_MODIFY);
 			break;
 		case '%':
 			if ((t = sp->frp->name) == NULL) {
 				msgq(sp, M_ERR,
 				    "116|No filename to substitute for %%");
 				return (1);
 			}
 			tlen = strlen(t);
 			len += tlen;
 			off = p - bp;
 			ADD_SPACE_RETW(sp, bp, blen, len);
 			p = bp + off;
 			CHAR2INT(sp, t, tlen, wp, wlen);
 			MEMCPY(p, wp, wlen);
 			p += wlen;
 			F_SET(excp, E_MODIFY);
 			break;
 		case '#':
 			if ((t = sp->alt_name) == NULL) {
 				msgq(sp, M_ERR,
 				    "117|No filename to substitute for #");
 				return (1);
 			}
 			len += tlen = strlen(t);
 			off = p - bp;
 			ADD_SPACE_RETW(sp, bp, blen, len);
 			p = bp + off;
 			CHAR2INT(sp, t, tlen, wp, wlen);
 			MEMCPY(p, wp, wlen);
 			p += wlen;
 			F_SET(excp, E_MODIFY);
 			break;
 		case '\\':
 			/*
 			 * QUOTING NOTE:
 			 *
 			 * Strip any backslashes that protected the file
 			 * expansion characters.
 			 */
 			if (cmdlen > 1 &&
 			    (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
 				++cmd;
 				--cmdlen;
 			}
 			/* FALLTHROUGH */
 		default:
 ins_ch:			++len;
 			off = p - bp;
 			ADD_SPACE_RETW(sp, bp, blen, len);
 			p = bp + off;
 			*p++ = *cmd;
 		}
 
 	/* Nul termination. */
 	++len;
 	off = p - bp;
 	ADD_SPACE_RETW(sp, bp, blen, len);
 	p = bp + off;
 	*p = '\0';
 
 	/* Return the new string length, buffer, buffer length. */
 	*lenp = len - 1;
 	*bpp = bp;
 	*blenp = blen;
 	return (0);
 }
 
 /*
  * argv_alloc --
  *	Make more space for arguments.
  */
 static int
 argv_alloc(SCR *sp, size_t len)
 {
 	ARGS *ap;
 	EX_PRIVATE *exp;
 	int cnt, off;
 
 	/*
 	 * Allocate room for another argument, always leaving
 	 * enough room for an ARGS structure with a length of 0.
 	 */
 #define	INCREMENT	20
 	exp = EXP(sp);
 	off = exp->argsoff;
 	if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
 		cnt = exp->argscnt + INCREMENT;
 		REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
 		if (exp->args == NULL) {
 			(void)argv_free(sp);
 			goto mem;
 		}
 		memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
 		exp->argscnt = cnt;
 	}
 
 	/* First argument. */
 	if (exp->args[off] == NULL) {
 		CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
 		if (exp->args[off] == NULL)
 			goto mem;
 	}
 
 	/* First argument buffer. */
 	ap = exp->args[off];
 	ap->len = 0;
 	if (ap->blen < len + 1) {
 		ap->blen = len + 1;
 		REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
 		if (ap->bp == NULL) {
 			ap->bp = NULL;
 			ap->blen = 0;
 			F_CLR(ap, A_ALLOCATED);
 mem:			msgq(sp, M_SYSERR, NULL);
 			return (1);
 		}
 		F_SET(ap, A_ALLOCATED);
 	}
 
 	/* Second argument. */
 	if (exp->args[++off] == NULL) {
 		CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
 		if (exp->args[off] == NULL)
 			goto mem;
 	}
 	/* 0 length serves as end-of-argument marker. */
 	exp->args[off]->len = 0;
 	return (0);
 }
 
 /*
  * argv_free --
  *	Free up argument structures.
  *
  * PUBLIC: int argv_free(SCR *);
  */
 int
 argv_free(SCR *sp)
 {
 	EX_PRIVATE *exp;
 	int off;
 
 	exp = EXP(sp);
 	if (exp->args != NULL) {
 		for (off = 0; off < exp->argscnt; ++off) {
 			if (exp->args[off] == NULL)
 				continue;
 			if (F_ISSET(exp->args[off], A_ALLOCATED))
 				free(exp->args[off]->bp);
 			free(exp->args[off]);
 		}
 		free(exp->args);
 	}
 	exp->args = NULL;
 	exp->argscnt = 0;
 	exp->argsoff = 0;
 	return (0);
 }
 
 /*
  * argv_flt_path --
  *	Find all file names matching the prefix and append them to the
  *	argument list.
  *
  * PUBLIC: int argv_flt_path(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 int
 argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen)
 {
 	struct dirent *dp;
 	DIR *dirp;
 	EX_PRIVATE *exp;
 	int off;
 	size_t dlen, len, nlen;
 	CHAR_T *dname;
 	CHAR_T *p, *np, *n;
 	char *name, *tp, *epd = NULL;
 	CHAR_T *wp;
 	size_t wlen;
 
 	exp = EXP(sp);
 
 	/* Set up the name and length for comparison. */
 	if ((path = v_wstrdup(sp, path, plen)) == NULL)
 		return (1);
 	if ((p = STRRCHR(path, '/')) == NULL) {
 		if (*path == '~') {
 			int rc;
 			
 			/* Filter ~user list instead. */
 			rc = argv_flt_user(sp, excp, path, plen);
 			free(path);
 			return (rc);
 		}
 		dname = L(".");
 		dlen = 0;
 		np = path;
 	} else {
 		if (p == path) {
 			dname = L("/");
 			dlen = 1;
 		} else {
 			*p = '\0';
 			dname = path;
 			dlen = p - path;
 		}
 		np = p + 1;
 	}
 
 	INT2CHAR(sp, dname, dlen + 1, tp, nlen);
 	if ((epd = expanduser(tp)) != NULL)
 		tp = epd;
 	if ((dirp = opendir(tp)) == NULL) {
 		free(epd);
 		free(path);
 		return (1);
 	}
 	free(epd);
 
 	INT2CHAR(sp, np, STRLEN(np), tp, nlen);
 	if ((name = v_strdup(sp, tp, nlen)) == NULL) {
 		free(path);
 		return (1);
 	}
 
 	for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
 		if (nlen == 0) {
 			if (dp->d_name[0] == '.')
 				continue;
+#ifdef HAVE_DIRENT_D_NAMLEN
 			len = dp->d_namlen;
+#else
+			len = strlen(dp->d_name);
+#endif
 		} else {
+#ifdef HAVE_DIRENT_D_NAMLEN
 			len = dp->d_namlen;
+#else
+			len = strlen(dp->d_name);
+#endif
 			if (len < nlen || memcmp(dp->d_name, name, nlen))
 				continue;
 		}
 
 		/* Directory + name + slash + null. */
 		CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
 		argv_alloc(sp, dlen + wlen + 1);
 		n = exp->args[exp->argsoff]->bp;
 		if (dlen != 0) {
 			MEMCPY(n, dname, dlen);
 			n += dlen;
 			if (dlen > 1 || dname[0] != '/')
 				*n++ = '/';
 			exp->args[exp->argsoff]->len = dlen + 1;
 		}
 		MEMCPY(n, wp, wlen);
 		exp->args[exp->argsoff]->len += wlen - 1;
 		++exp->argsoff;
 		excp->argv = exp->args;
 		excp->argc = exp->argsoff;
 	}
 	closedir(dirp);
 	free(name);
 	free(path);
 
 	qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
 	return (0);
 }
 
 /*
  * argv_comp --
  *	Alphabetic comparison.
  */
 static int
 argv_comp(const void *a, const void *b)
 {
 	return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp));
 }
 
 /*
  * argv_sexp --
  *	Fork a shell, pipe a command through it, and read the output into
  *	a buffer.
  */
 static int
 argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
 {
 	enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
 	FILE *ifp;
 	pid_t pid;
 	size_t blen, len;
 	int ch, std_output[2];
 	CHAR_T *bp, *p;
 	char *sh, *sh_path;
 	char *np;
 	size_t nlen;
 
 	/* Secure means no shell access. */
 	if (O_ISSET(sp, O_SECURE)) {
 		msgq(sp, M_ERR,
 "289|Shell expansions not supported when the secure edit option is set");
 		return (1);
 	}
 
 	sh_path = O_STR(sp, O_SHELL);
 	if ((sh = strrchr(sh_path, '/')) == NULL)
 		sh = sh_path;
 	else
 		++sh;
 
 	/* Local copies of the buffer variables. */
 	bp = *bpp;
 	blen = *blenp;
 
 	/*
 	 * There are two different processes running through this code, named
 	 * the utility (the shell) and the parent. The utility reads standard
 	 * input and writes standard output and standard error output.  The
 	 * parent writes to the utility, reads its standard output and ignores
 	 * its standard error output.  Historically, the standard error output
 	 * was discarded by vi, as it produces a lot of noise when file patterns
 	 * don't match.
 	 *
 	 * The parent reads std_output[0], and the utility writes std_output[1].
 	 */
 	ifp = NULL;
 	std_output[0] = std_output[1] = -1;
 	if (pipe(std_output) < 0) {
 		msgq(sp, M_SYSERR, "pipe");
 		return (1);
 	}
 	if ((ifp = fdopen(std_output[0], "r")) == NULL) {
 		msgq(sp, M_SYSERR, "fdopen");
 		goto err;
 	}
 
 	/*
 	 * Do the minimal amount of work possible, the shell is going to run
 	 * briefly and then exit.  We sincerely hope.
 	 */
 	switch (pid = vfork()) {
 	case -1:			/* Error. */
 		msgq(sp, M_SYSERR, "vfork");
 err:		if (ifp != NULL)
 			(void)fclose(ifp);
 		else if (std_output[0] != -1)
 			close(std_output[0]);
 		if (std_output[1] != -1)
 			close(std_output[0]);
 		return (1);
 	case 0:				/* Utility. */
 		/* Redirect stdout to the write end of the pipe. */
 		(void)dup2(std_output[1], STDOUT_FILENO);
 
 		/* Close the utility's file descriptors. */
 		(void)close(std_output[0]);
 		(void)close(std_output[1]);
 		(void)close(STDERR_FILENO);
 
 		/*
 		 * XXX
 		 * Assume that all shells have -c.
 		 */
 		INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
 		execl(sh_path, sh, "-c", np, (char *)NULL);
 		msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
 		_exit(127);
 	default:			/* Parent. */
 		/* Close the pipe ends the parent won't use. */
 		(void)close(std_output[1]);
 		break;
 	}
 
 	/*
 	 * Copy process standard output into a buffer.
 	 *
 	 * !!!
 	 * Historic vi apparently discarded leading \n and \r's from
 	 * the shell output stream.  We don't on the grounds that any
 	 * shell that does that is broken.
 	 */
 	for (p = bp, len = 0, ch = EOF;
 	    (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
 		if (blen < 5) {
 			ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
 			p = bp + len;
 			blen = *blenp - len;
 		}
 
 	/* Delete the final newline, nul terminate the string. */
 	if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
 		--p;
 		--len;
 	}
 	*p = '\0';
 	*lenp = len;
 	*bpp = bp;		/* *blenp is already updated. */
 
 	if (ferror(ifp))
 		goto ioerr;
 	if (fclose(ifp)) {
 ioerr:		msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
 alloc_err:	rval = SEXP_ERR;
 	} else
 		rval = SEXP_OK;
 
 	/*
 	 * Wait for the process.  If the shell process fails (e.g., "echo $q"
 	 * where q wasn't a defined variable) or if the returned string has
 	 * no characters or only blank characters, (e.g., "echo $5"), complain
 	 * that the shell expansion failed.  We can't know for certain that's
 	 * the error, but it's a good guess, and it matches historic practice.
 	 * This won't catch "echo foo_$5", but that's not a common error and
 	 * historic vi didn't catch it either.
 	 */
 	if (proc_wait(sp, (long)pid, sh, 1, 0))
 		rval = SEXP_EXPANSION_ERR;
 
 	for (p = bp; len; ++p, --len)
 		if (!cmdskip(*p))
 			break;
 	if (len == 0)
 		rval = SEXP_EXPANSION_ERR;
 
 	if (rval == SEXP_EXPANSION_ERR)
 		msgq(sp, M_ERR, "304|Shell expansion failed");
 
 	return (rval == SEXP_OK ? 0 : 1);
 }
 
 /*
  * argv_esc --
  *	Escape a string into an ex and shell argument.
  *
  * PUBLIC: CHAR_T *argv_esc(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 CHAR_T *
 argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
 {
 	size_t blen, off;
 	CHAR_T *bp, *p;
 	int ch;
 
 	GET_SPACE_GOTOW(sp, bp, blen, len + 1);
 
 	/*
 	 * Leaving the first '~' unescaped causes the user to need a
 	 * "./" prefix to edit a file which really starts with a '~'.
 	 * However, the file completion happens to not work for these
 	 * files without the prefix.
 	 * 
 	 * All ex expansion characters, "!%#", are double escaped.
 	 */
 	for (p = bp; len > 0; ++str, --len) {
 		ch = *str;
 		off = p - bp;
 		if (blen / sizeof(CHAR_T) - off < 3) {
 			ADD_SPACE_GOTOW(sp, bp, blen, off + 3);
 			p = bp + off;
 		}
 		if (cmdskip(ch) || ch == '\n' ||
 		    IS_ESCAPE(sp, excp, ch))			/* Ex. */
 			*p++ = CH_LITERAL;
 		else switch (ch) {
 		case '~':					/* ~user. */
 			if (p != bp)
 				*p++ = '\\';
 			break;
 		case '+':					/* Ex +cmd. */
 			if (p == bp)
 				*p++ = '\\';
 			break;
 		case '!': case '%': case '#':			/* Ex exp. */
 			*p++ = '\\';
 			*p++ = '\\';
 			break;
 		case ',': case '-': case '.': case '/':		/* Safe. */
 		case ':': case '=': case '@': case '_':
 			break;
 		default:					/* Unsafe. */
 			if (isascii(ch) && !isalnum(ch))
 				*p++ = '\\';
 		}
 		*p++ = ch;
 	}
 	*p = '\0';
 
 	return bp;
 
 alloc_err:
 	return NULL;
 }
 
 /*
  * argv_uesc --
  *	Unescape an escaped ex and shell argument.
  *
  * PUBLIC: CHAR_T *argv_uesc(SCR *, EXCMD *, CHAR_T *, size_t);
  */
 CHAR_T *
 argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
 {
 	size_t blen;
 	CHAR_T *bp, *p;
 
 	GET_SPACE_GOTOW(sp, bp, blen, len + 1);
 
 	for (p = bp; len > 0; ++str, --len) {
 		if (IS_ESCAPE(sp, excp, *str)) {
 			if (--len < 1)
 				break;
 			++str;
 		} else if (*str == '\\') {
 			if (--len < 1)
 				break;
 			++str;
 
 			/* Check for double escaping. */
 			if (*str == '\\' && len > 1)
 				switch (str[1]) {
 				case '!': case '%': case '#':
 					++str;
 					--len;
 				}
 		}
 		*p++ = *str;
 	}
 	*p = '\0';
 
 	return bp;
 
 alloc_err:
 	return NULL;
 }
Index: head/contrib/nvi/ex/ex_bang.c
===================================================================
--- head/contrib/nvi/ex/ex_bang.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_bang.c	(revision 366309)
@@ -1,186 +1,187 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "../vi/vi.h"
 
 /*
  * ex_bang -- :[line [,line]] ! command
  *
  * Pass the rest of the line after the ! character to the program named by
  * the O_SHELL option.
  *
  * Historical vi did NOT do shell expansion on the arguments before passing
  * them, only file name expansion.  This means that the O_SHELL program got
  * "$t" as an argument if that is what the user entered.  Also, there's a
  * special expansion done for the bang command.  Any exclamation points in
  * the user's argument are replaced by the last, expanded ! command.
  *
  * There's some fairly amazing slop in this routine to make the different
  * ways of getting here display the right things.  It took a long time to
  * get it right (wrong?), so be careful.
  *
  * PUBLIC: int ex_bang(SCR *, EXCMD *);
  */
 int
 ex_bang(SCR *sp, EXCMD *cmdp)
 {
 	enum filtertype ftype;
 	ARGS *ap;
 	EX_PRIVATE *exp;
 	MARK rm;
 	recno_t lno;
 	int rval;
 	const char *msg;
 	char *np;
 	size_t nlen;
 
 	ap = cmdp->argv[0];
 	if (ap->len == 0) {
 		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
 		return (1);
 	}
 
 	/* Set the "last bang command" remembered value. */
 	exp = EXP(sp);
 	free(exp->lastbcomm);
 	if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 
 	/*
 	 * If the command was modified by the expansion, it was historically
 	 * redisplayed.
 	 */
 	if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
 		/*
 		 * Display the command if modified.  Historic ex/vi displayed
 		 * the command if it was modified due to file name and/or bang
 		 * expansion.  If piping lines in vi, it would be immediately
 		 * overwritten by any error or line change reporting.
 		 */
 		if (F_ISSET(sp, SC_VI))
 			vs_update(sp, "!", ap->bp);
 		else {
 			(void)ex_printf(sp, "!"WS"\n", ap->bp);
 			(void)ex_fflush(sp);
 		}
 	}
 
 	/*
 	 * If no addresses were specified, run the command.  If there's an
 	 * underlying file, it's been modified and autowrite is set, write
 	 * the file back.  If the file has been modified, autowrite is not
 	 * set and the warn option is set, tell the user about the file.
 	 */
 	if (cmdp->addrcnt == 0) {
 		msg = NULL;
-		if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED))
+		if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED)) {
 			if (O_ISSET(sp, O_AUTOWRITE)) {
 				if (file_aw(sp, FS_ALL))
 					return (0);
 			} else if (O_ISSET(sp, O_WARN) &&
 			    !F_ISSET(sp, SC_EX_SILENT))
 				msg = msg_cat(sp,
 				    "303|File modified since last write.",
 				    NULL);
+		}
 
 		/* If we're still in a vi screen, move out explicitly. */
 		INT2CHAR(sp, ap->bp, ap->len+1, np, nlen);
 		(void)ex_exec_proc(sp,
 		    cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
 	}
 
 	/*
 	 * If addresses were specified, pipe lines from the file through the
 	 * command.
 	 *
 	 * Historically, vi lines were replaced by both the stdout and stderr
 	 * lines of the command, but ex lines by only the stdout lines.  This
 	 * makes no sense to me, so nvi makes it consistent for both, and
 	 * matches vi's historic behavior.
 	 */
 	else {
 		NEEDFILE(sp, cmdp);
 
 		/* Autoprint is set historically, even if the command fails. */
 		F_SET(cmdp, E_AUTOPRINT);
 
 		/*
 		 * !!!
 		 * Historical vi permitted "!!" in an empty file.  When this
 		 * happens, we arrive here with two addresses of 1,1 and a
 		 * bad attitude.  The simple solution is to turn it into a
 		 * FILTER_READ operation, with the exception that stdin isn't
 		 * opened for the utility, and the cursor position isn't the
 		 * same.  The only historic glitch (I think) is that we don't
 		 * put an empty line into the default cut buffer, as historic
 		 * vi did.  Imagine, if you can, my disappointment.
 		 */
 		ftype = FILTER_BANG;
 		if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
 			if (db_last(sp, &lno))
 				return (1);
 			if (lno == 0) {
 				cmdp->addr1.lno = cmdp->addr2.lno = 0;
 				ftype = FILTER_RBANG;
 			}
 		}
 		rval = ex_filter(sp, cmdp,
 		    &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
 
 		/*
 		 * If in vi mode, move to the first nonblank.
 		 *
 		 * !!!
 		 * Historic vi wasn't consistent in this area -- if you used
 		 * a forward motion it moved to the first nonblank, but if you
 		 * did a backward motion it didn't.  And, if you followed a
 		 * backward motion with a forward motion, it wouldn't move to
 		 * the nonblank for either.  Going to the nonblank generally
 		 * seems more useful and consistent, so we do it.
 		 */
 		sp->lno = rm.lno;
 		if (F_ISSET(sp, SC_VI)) {
 			sp->cno = 0;
 			(void)nonblank(sp, sp->lno, &sp->cno);
 		} else
 			sp->cno = rm.cno;
 	}
 
 	/* Ex terminates with a bang, even if the command fails. */
 	if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
 		(void)ex_puts(sp, "!\n");
 
 	/* Apply expandtab to the new text */
 	if (O_ISSET(sp, O_EXPANDTAB))
 		ex_retab(sp, cmdp);
 
 	/*
 	 * XXX
 	 * The ! commands never return an error, so that autoprint always
 	 * happens in the ex parser.
 	 */
 	return (0);
 }
Index: head/contrib/nvi/ex/ex_cscope.c
===================================================================
--- head/contrib/nvi/ex/ex_cscope.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_cscope.c	(revision 366309)
@@ -1,1084 +1,1086 @@
 /*-
  * Copyright (c) 1994, 1996
  *	Rob Mayoff.  All rights reserved.
  * Copyright (c) 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <termios.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "pathnames.h"
 #include "tag.h"
 
 #define	CSCOPE_DBFILE		"cscope.out"
 #define	CSCOPE_PATHS		"cscope.tpath"
 
 /*
  * 0name	find all uses of name
  * 1name	find definition of name
  * 2name	find all function calls made from name
  * 3name	find callers of name
  * 4string	find text string (cscope 12.9)
  * 4name	find assignments to name (cscope 13.3)
  * 5pattern	change pattern -- NOT USED
  * 6pattern	find pattern
  * 7name	find files with name as substring
  * 8name	find files #including name
  */
 #define	FINDHELP "\
 find c|d|e|f|g|i|s|t buffer|pattern\n\
       c: find callers of name\n\
       d: find all function calls made from name\n\
       e: find pattern\n\
       f: find files with name as substring\n\
       g: find definition of name\n\
       i: find files #including name\n\
       s: find all uses of name\n\
       t: find assignments to name"
 
 static int cscope_add(SCR *, EXCMD *, CHAR_T *);
 static int cscope_find(SCR *, EXCMD*, CHAR_T *);
 static int cscope_help(SCR *, EXCMD *, CHAR_T *);
 static int cscope_kill(SCR *, EXCMD *, CHAR_T *);
 static int cscope_reset(SCR *, EXCMD *, CHAR_T *);
 
 typedef struct _cc {
 	char	 *name;
 	int	(*function)(SCR *, EXCMD *, CHAR_T *);
 	char	 *help_msg;
 	char	 *usage_msg;
 } CC;
 
 static CC const cscope_cmds[] = {
 	{ "add",   cscope_add,
 	  "Add a new cscope database", "add file | directory" },
 	{ "find",  cscope_find,
 	  "Query the databases for a pattern", FINDHELP },
 	{ "help",  cscope_help,
 	  "Show help for cscope commands", "help [command]" },
 	{ "kill",  cscope_kill,
 	  "Kill a cscope connection", "kill number" },
 	{ "reset", cscope_reset,
 	  "Discard all current cscope connections", "reset" },
 	{ NULL }
 };
 
 static TAGQ	*create_cs_cmd(SCR *, char *, size_t *);
 static int	 csc_help(SCR *, char *);
 static void	 csc_file(SCR *,
 		    CSC *, char *, char **, size_t *, int *);
 static int	 get_paths(SCR *, CSC *);
 static CC const	*lookup_ccmd(char *);
 static int	 parse(SCR *, CSC *, TAGQ *, int *);
 static int	 read_prompt(SCR *, CSC *);
 static int	 run_cscope(SCR *, CSC *, char *);
 static int	 start_cscopes(SCR *, EXCMD *);
 static int	 terminate(SCR *, CSC *, int);
 
 /*
  * ex_cscope --
  *	Perform an ex cscope.
  *
  * PUBLIC: int ex_cscope(SCR *, EXCMD *);
  */
 int
 ex_cscope(SCR *sp, EXCMD *cmdp)
 {
 	CC const *ccp;
 	EX_PRIVATE *exp;
 	int i;
 	CHAR_T *cmd;
 	CHAR_T *p;
 	char *np;
 	size_t nlen;
 
 	/* Initialize the default cscope directories. */
 	exp = EXP(sp);
 	if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp))
 		return (1);
 	F_SET(exp, EXP_CSCINIT);
 
 	/* Skip leading whitespace. */
 	for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p)
 		if (!isspace(*p))
 			break;
 	if (i == 0)
 		goto usage;
 
 	/* Skip the command to any arguments. */
 	for (cmd = p; i > 0; --i, ++p)
 		if (isspace(*p))
 			break;
 	if (*p != '\0') {
 		*p++ = '\0';
 		for (; *p && isspace(*p); ++p);
 	}
 
 	INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen);
 	if ((ccp = lookup_ccmd(np)) == NULL) {
 usage:		msgq(sp, M_ERR, "309|Use \"cscope help\" for help");
 		return (1);
 	}
 
 	/* Call the underlying function. */
 	return (ccp->function(sp, cmdp, p));
 }
 
 /*
  * start_cscopes --
  *	Initialize the cscope package.
  */
 static int
 start_cscopes(SCR *sp, EXCMD *cmdp)
 {
 	size_t blen, len;
 	char *bp, *cscopes, *p, *t;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/*
 	 * EXTENSION #1:
 	 *
 	 * If the CSCOPE_DIRS environment variable is set, we treat it as a
 	 * list of cscope directories that we're using, similar to the tags
 	 * edit option.
 	 *
 	 * XXX
 	 * This should probably be an edit option, although that implies that
 	 * we start/stop cscope processes periodically, instead of once when
 	 * the editor starts.
 	 */
 	if ((cscopes = getenv("CSCOPE_DIRS")) == NULL)
 		return (0);
 	len = strlen(cscopes);
 	GET_SPACE_RETC(sp, bp, blen, len);
 	memcpy(bp, cscopes, len + 1);
 
 	for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;)
 		if (*p != '\0') {
 			CHAR2INT(sp, p, strlen(p) + 1, wp, wlen);
 			(void)cscope_add(sp, cmdp, wp);
 		}
 
 	FREE_SPACE(sp, bp, blen);
 	return (0);
 }
 
 /*
  * cscope_add --
  *	The cscope add command.
  */
 static int
 cscope_add(SCR *sp, EXCMD *cmdp, CHAR_T *dname)
 {
 	struct stat sb;
 	EX_PRIVATE *exp;
 	CSC *csc;
 	size_t len;
 	int cur_argc;
 	char *dbname, *path;
 	char *np = NULL;
 	size_t nlen;
 
 	exp = EXP(sp);
 
 	/*
 	 *  0 additional args: usage.
 	 *  1 additional args: matched a file.
 	 * >1 additional args: object, too many args.
 	 */
 	cur_argc = cmdp->argc;
 	if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) {
 		return (1);
 	}
 	if (cmdp->argc == cur_argc) {
 		(void)csc_help(sp, "add");
 		return (1);
 	}
 	if (cmdp->argc == cur_argc + 1)
 		dname = cmdp->argv[cur_argc]->bp;
 	else {
 		ex_emsg(sp, np, EXM_FILECOUNT);
 		return (1);
 	}
 
 	INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen);
 
 	/*
 	 * The user can specify a specific file (so they can have multiple
 	 * Cscope databases in a single directory) or a directory.  If the
 	 * file doesn't exist, we're done.  If it's a directory, append the
 	 * standard database file name and try again.  Store the directory
 	 * name regardless so that we can use it as a base for searches.
 	 */
 	if (stat(np, &sb)) {
 		msgq(sp, M_SYSERR, "%s", np);
 		return (1);
 	}
 	if (S_ISDIR(sb.st_mode)) {
 		if ((path = join(np, CSCOPE_DBFILE)) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			return (1);
 		}
 		if (stat(path, &sb)) {
 			msgq(sp, M_SYSERR, "%s", path);
 			free(path);
 			return (1);
 		}
 		free(path);
 		dbname = CSCOPE_DBFILE;
 	} else if ((dbname = strrchr(np, '/')) != NULL)
 		*dbname++ = '\0';
 	else {
 		dbname = np;
 		np = ".";
 	}
 
 	/* Allocate a cscope connection structure and initialize its fields. */
 	len = strlen(np);
 	CALLOC_RET(sp, csc, 1, sizeof(CSC) + len);
 	csc->dname = csc->buf;
 	csc->dlen = len;
 	memcpy(csc->dname, np, len);
-	csc->mtim = sb.st_mtimespec;
+	csc->mtim = sb.st_mtim;
 
 	/* Get the search paths for the cscope. */
 	if (get_paths(sp, csc))
 		goto err;
 
 	/* Start the cscope process. */
 	if (run_cscope(sp, csc, dbname))
 		goto err;
 
 	/*
 	 * Add the cscope connection to the screen's list.  From now on, 
 	 * on error, we have to call terminate, which expects the csc to
 	 * be on the chain.
 	 */
 	SLIST_INSERT_HEAD(exp->cscq, csc, q);
 
 	/* Read the initial prompt from the cscope to make sure it's okay. */
 	return read_prompt(sp, csc);
 
 err:	free(csc);
 	return (1);
 }
 
 /*
  * get_paths --
  *	Get the directories to search for the files associated with this
  *	cscope database.
  */
 static int
 get_paths(SCR *sp, CSC *csc)
 {
 	struct stat sb;
 	int fd, nentries;
 	size_t len;
 	char *p, **pathp, *buf;
 
 	/*
 	 * EXTENSION #2:
 	 *
 	 * If there's a cscope directory with a file named CSCOPE_PATHS, it
 	 * contains a colon-separated list of paths in which to search for
 	 * files returned by cscope.
 	 *
 	 * XXX
 	 * These paths are absolute paths, and not relative to the cscope
 	 * directory.  To fix this, rewrite the each path using the cscope
 	 * directory as a prefix.
 	 */
 	if ((buf = join(csc->dname, CSCOPE_PATHS)) == NULL) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 	if (stat(buf, &sb) == 0) {
 		/* Read in the CSCOPE_PATHS file. */
 		len = sb.st_size;
 		MALLOC_RET(sp, csc->pbuf, len + 1);
 		if ((fd = open(buf, O_RDONLY, 0)) < 0 ||
 		    read(fd, csc->pbuf, len) != len) {
 			 msgq_str(sp, M_SYSERR, buf, "%s");
 			 if (fd >= 0)
 				(void)close(fd);
 			 free(buf);
 			 return (1);
 		}
 		(void)close(fd);
 		free(buf);
 		csc->pbuf[len] = '\0';
 
 		/* Count up the entries. */
 		for (nentries = 0, p = csc->pbuf; *p != '\0'; ++p)
 			if (p[0] == ':' && p[1] != '\0')
 				++nentries;
 
 		/* Build an array of pointers to the paths. */
 		CALLOC_GOTO(sp, csc->paths, nentries + 1, sizeof(char **));
 		for (pathp = csc->paths, p = strtok(csc->pbuf, ":");
 		    p != NULL; p = strtok(NULL, ":"))
 			*pathp++ = p;
 		return (0);
 	}
 	free(buf);
 
 	/*
 	 * If the CSCOPE_PATHS file doesn't exist, we look for files
 	 * relative to the cscope directory.
 	 */
 	if ((csc->pbuf = strdup(csc->dname)) == NULL) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 	CALLOC_GOTO(sp, csc->paths, 2, sizeof(char *));
 	csc->paths[0] = csc->pbuf;
 	return (0);
 
 alloc_err:
 	free(csc->pbuf);
 	csc->pbuf = NULL;
 	return (1);
 }
 
 /*
  * run_cscope --
  *	Fork off the cscope process.
  */
 static int
 run_cscope(SCR *sp, CSC *csc, char *dbname)
 {
 	int to_cs[2], from_cs[2];
 	char *cmd;
 
 	/*
 	 * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
 	 * from_cs[0] and writes to to_cs[1].
 	 */
 	to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1;
 	if (pipe(to_cs) < 0 || pipe(from_cs) < 0) {
 		msgq(sp, M_SYSERR, "pipe");
 		goto err;
 	}
 	switch (csc->pid = vfork()) {
 		char *dn, *dbn;
 	case -1:
 		msgq(sp, M_SYSERR, "vfork");
 err:		if (to_cs[0] != -1)
 			(void)close(to_cs[0]);
 		if (to_cs[1] != -1)
 			(void)close(to_cs[1]);
 		if (from_cs[0] != -1)
 			(void)close(from_cs[0]);
 		if (from_cs[1] != -1)
 			(void)close(from_cs[1]);
 		return (1);
 	case 0:				/* child: run cscope. */
 		(void)dup2(to_cs[0], STDIN_FILENO);
 		(void)dup2(from_cs[1], STDOUT_FILENO);
 		(void)dup2(from_cs[1], STDERR_FILENO);
 
 		/* Close unused file descriptors. */
 		(void)close(to_cs[1]);
 		(void)close(from_cs[0]);
 
 		/* Run the cscope command. */
 #define	CSCOPE_CMD_FMT		"cd %s && exec cscope -dl -f %s"
 		if ((dn = quote(csc->dname)) == NULL)
 			goto nomem;
 		if ((dbn = quote(dbname)) == NULL) {
 			free(dn);
 			goto nomem;
 		}
-		(void)asprintf(&cmd, CSCOPE_CMD_FMT, dn, dbn);
+		if (asprintf(&cmd, CSCOPE_CMD_FMT, dn, dbn) == -1)
+			cmd = NULL;
 		free(dbn);
 		free(dn);
 		if (cmd == NULL) {
 nomem:			msgq(sp, M_SYSERR, NULL);
 			_exit (1);
 		}
 		(void)execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
 		msgq_str(sp, M_SYSERR, cmd, "execl: %s");
 		free(cmd);
 		_exit (127);
 		/* NOTREACHED */
 	default:			/* parent. */
 		/* Close unused file descriptors. */
 		(void)close(to_cs[0]);
 		(void)close(from_cs[1]);
 
 		/*
 		 * Save the file descriptors for later duplication, and
 		 * reopen as streams.
 		 */
 		csc->to_fd = to_cs[1];
 		csc->to_fp = fdopen(to_cs[1], "w");
 		csc->from_fd = from_cs[0];
 		csc->from_fp = fdopen(from_cs[0], "r");
 		break;
 	}
 	return (0);
 }
 
 /*
  * cscope_find --
  *	The cscope find command.
  */
 static int
 cscope_find(SCR *sp, EXCMD *cmdp, CHAR_T *pattern)
 {
 	CSC *csc, *csc_next;
 	EX_PRIVATE *exp;
 	FREF *frp;
 	TAGQ *rtqp, *tqp;
 	TAG *rtp;
 	recno_t lno;
 	size_t cno, search;
 	int force, istmp, matches;
 	char *np = NULL;
 	size_t nlen;
 
 	exp = EXP(sp);
 
 	/* Check for connections. */
 	if (SLIST_EMPTY(exp->cscq)) {
 		msgq(sp, M_ERR, "310|No cscope connections running");
 		return (1);
 	}
 
 	/*
 	 * Allocate all necessary memory before doing anything hard.  If the
 	 * tags stack is empty, we'll need the `local context' TAGQ structure
 	 * later.
 	 */
 	rtp = NULL;
 	rtqp = NULL;
 	if (TAILQ_EMPTY(exp->tq)) {
 		/* Initialize the `local context' tag queue structure. */
 		CALLOC_GOTO(sp, rtqp, 1, sizeof(TAGQ));
 		TAILQ_INIT(rtqp->tagq);
 
 		/* Initialize and link in its tag structure. */
 		CALLOC_GOTO(sp, rtp, 1, sizeof(TAG));
 		TAILQ_INSERT_HEAD(rtqp->tagq, rtp, q);
 		rtqp->current = rtp;
 	}
 
 	/* Create the cscope command. */
 	INT2CHAR(sp, pattern, STRLEN(pattern) + 1, np, nlen);
 	np = strdup(np);
 	if ((tqp = create_cs_cmd(sp, np, &search)) == NULL)
 		goto err;
 	free(np);
 	np = NULL;
 
 	/*
 	 * Stick the current context in a convenient place, we'll lose it
 	 * when we switch files.
 	 */
 	frp = sp->frp;
 	lno = sp->lno;
 	cno = sp->cno;
 	istmp = F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN);
 
 	/* Search all open connections for a match. */
 	matches = 0;
 	/* Copy next connect here in case csc is killed. */
 	SLIST_FOREACH_SAFE(csc, exp->cscq, q, csc_next) {
 		/*
 		 * Send the command to the cscope program.  (We skip the
 		 * first two bytes of the command, because we stored the
 		 * search cscope command character and a leading space
 		 * there.)
 		 */
 		(void)fprintf(csc->to_fp, "%lu%s\n", search, tqp->tag + 2);
 		(void)fflush(csc->to_fp);
 
 		/* Read the output. */
 		if (parse(sp, csc, tqp, &matches))
 			goto nomatch;
 	}
 
 	if (matches == 0) {
 		msgq(sp, M_INFO, "278|No matches for query");
 nomatch:	free(rtp);
 		free(rtqp);
 		tagq_free(sp, tqp);
 		return (1);
 	}
 
 	/* Try to switch to the first tag. */
 	force = FL_ISSET(cmdp->iflags, E_C_FORCE);
 	if (F_ISSET(cmdp, E_NEWSCREEN)) {
 		if (ex_tag_Nswitch(sp, tqp->current, force))
 			goto err;
 
 		/* Everything else gets done in the new screen. */
 		sp = sp->nextdisp;
 		exp = EXP(sp);
 	} else
 		if (ex_tag_nswitch(sp, tqp->current, force))
 			goto err;
 
 	/*
 	 * If this is the first tag, put a `current location' queue entry
 	 * in place, so we can pop all the way back to the current mark.
 	 * Note, it doesn't point to much of anything, it's a placeholder.
 	 */
 	if (TAILQ_EMPTY(exp->tq)) {
 		TAILQ_INSERT_HEAD(exp->tq, rtqp, q);
 	} else
 		rtqp = TAILQ_FIRST(exp->tq);
 
 	/* Link the current TAGQ structure into place. */
 	TAILQ_INSERT_HEAD(exp->tq, tqp, q);
 
 	(void)cscope_search(sp, tqp, tqp->current);
 
 	/*
 	 * Move the current context from the temporary save area into the
 	 * right structure.
 	 *
 	 * If we were in a temporary file, we don't have a context to which
 	 * we can return, so just make it be the same as what we're moving
 	 * to.  It will be a little odd that ^T doesn't change anything, but
 	 * I don't think it's a big deal.
 	 */
 	if (istmp) {
 		rtqp->current->frp = sp->frp;
 		rtqp->current->lno = sp->lno;
 		rtqp->current->cno = sp->cno;
 	} else {
 		rtqp->current->frp = frp;
 		rtqp->current->lno = lno;
 		rtqp->current->cno = cno;
 	}
 
 	return (0);
 
 err:
 alloc_err:
 	free(rtqp);
 	free(rtp);
 	free(np);
 	return (1);
 }
 
 /*
  * create_cs_cmd --
  *	Build a cscope command, creating and initializing the base TAGQ.
  */
 static TAGQ *
 create_cs_cmd(SCR *sp, char *pattern, size_t *searchp)
 {
 	CB *cbp;
 	TAGQ *tqp;
 	size_t tlen;
 	char *p;
 
 	/*
 	 * Cscope supports a "change pattern" command which we never use,
 	 * cscope command 5.  Set CSCOPE_QUERIES[5] to " " since the user
 	 * can't pass " " as the first character of pattern.  That way the
 	 * user can't ask for pattern 5 so we don't need any special-case
 	 * code.
 	 */
 #define	CSCOPE_QUERIES		"sgdct efi"
 
 	if (pattern == NULL)
 		goto usage;
 
 	/* Skip leading blanks, check for command character. */
 	for (; cmdskip(pattern[0]); ++pattern);
 	if (pattern[0] == '\0' || !cmdskip(pattern[1]))
 		goto usage;
 	for (*searchp = 0, p = CSCOPE_QUERIES;
 	    *p != '\0' && *p != pattern[0]; ++*searchp, ++p);
 	if (*p == '\0') {
 		msgq(sp, M_ERR,
 		    "311|%s: unknown search type: use one of %s",
 		    KEY_NAME(sp, pattern[0]), CSCOPE_QUERIES);
 		return (NULL);
 	}
 
 	/* Skip <blank> characters to the pattern. */
 	for (p = pattern + 1; *p != '\0' && cmdskip(*p); ++p);
 	if (*p == '\0') {
 usage:		(void)csc_help(sp, "find");
 		return (NULL);
 	}
 
 	/* The user can specify the contents of a buffer as the pattern. */
 	cbp = NULL;
 	if (p[0] == '"' && p[1] != '\0' && p[2] == '\0')
 		CBNAME(sp, cbp, p[1]);
 	if (cbp != NULL) {
 		INT2CHAR(sp, TAILQ_FIRST(cbp->textq)->lb,
 			TAILQ_FIRST(cbp->textq)->len, p, tlen);
 	} else
 		tlen = strlen(p);
 
 	/* Allocate and initialize the TAGQ structure. */
 	CALLOC(sp, tqp, 1, sizeof(TAGQ) + tlen + 3);
 	if (tqp == NULL)
 		return (NULL);
 	TAILQ_INIT(tqp->tagq);
 	tqp->tag = tqp->buf;
 	tqp->tag[0] = pattern[0];
 	tqp->tag[1] = ' ';
 	tqp->tlen = tlen + 2;
 	memcpy(tqp->tag + 2, p, tlen);
 	tqp->tag[tlen + 2] = '\0';
 	F_SET(tqp, TAG_CSCOPE);
 
 	return (tqp);
 }
 
 /*
  * parse --
  *	Parse the cscope output.
  */
 static int
 parse(SCR *sp, CSC *csc, TAGQ *tqp, int *matchesp)
 {
 	TAG *tp;
 	recno_t slno = 0;
 	size_t dlen, nlen = 0, slen = 0;
 	int ch, i, isolder = 0, nlines;
 	char *dname = NULL, *name = NULL, *search, *p, *t, dummy[2], buf[2048];
 	CHAR_T *wp;
 	size_t wlen;
 
 	for (;;) {
 		if (!fgets(buf, sizeof(buf), csc->from_fp))
 			goto io_err;
 
 		/*
 		 * If the database is out of date, or there's some other
 		 * problem, cscope will output error messages before the
 		 * number-of-lines output.  Display/discard any output
 		 * that doesn't match what we want.
 		 */
 #define	CSCOPE_NLINES_FMT	"cscope: %d lines%1[\n]"
 		if (sscanf(buf, CSCOPE_NLINES_FMT, &nlines, dummy) == 2)
 			break;
 		if ((p = strchr(buf, '\n')) != NULL)
 			*p = '\0';
 		msgq(sp, M_ERR, "%s: \"%s\"", csc->dname, buf);
 	}
 
 	while (nlines--) {
 		if (fgets(buf, sizeof(buf), csc->from_fp) == NULL)
 			goto io_err;
 
 		/* If the line's too long for the buffer, discard it. */
 		if ((p = strchr(buf, '\n')) == NULL) {
 			while ((ch = getc(csc->from_fp)) != EOF && ch != '\n');
 			continue;
 		}
 		*p = '\0';
 
 		/*
 		 * The cscope output is in the following format:
 		 *
 		 *	<filename> <context> <line number> <pattern>
 		 *
 		 * Figure out how long everything is so we can allocate in one
 		 * swell foop, but discard anything that looks wrong.
 		 */
 		for (p = buf, i = 0;
 		    i < 3 && (t = strsep(&p, "\t ")) != NULL; ++i)
 			switch (i) {
 			case 0:			/* Filename. */
 				name = t;
 				nlen = strlen(name);
 				break;
 			case 1:			/* Context. */
 				break;
 			case 2:			/* Line number. */
 				slno = (recno_t)atol(t);
 				break;
 			}
 		if (i != 3 || p == NULL || t == NULL)
 			continue;
 
 		/* The rest of the string is the search pattern. */
 		search = p;
 		slen = strlen(p);
 
 		/* Resolve the file name. */
 		csc_file(sp, csc, name, &dname, &dlen, &isolder);
 
 		/*
 		 * If the file is older than the cscope database, that is,
 		 * the database was built since the file was last modified,
 		 * or there wasn't a search string, use the line number.
 		 */
 		if (isolder || strcmp(search, "<unknown>") == 0) {
 			search = NULL;
 			slen = 0;
 		}
 
 		/*
 		 * Allocate and initialize a tag structure plus the variable
 		 * length cscope information that follows it.
 		 */
 		CALLOC_RET(sp, tp, 1,
 			   sizeof(TAG) + dlen + 2 + nlen + 1 + (slen + 1) * sizeof(CHAR_T));
 		tp->fname = (char *)tp->buf;
 		if (dlen == 1 && *dname == '.')
 			--dlen;
 		else if (dlen != 0) {
 			memcpy(tp->fname, dname, dlen);
 			tp->fname[dlen] = '/';
 			++dlen;
 		}
 		memcpy(tp->fname + dlen, name, nlen + 1);
 		tp->fnlen = dlen + nlen;
 		tp->slno = slno;
 		if (slen != 0) {
 			tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1);
 			CHAR2INT(sp, search, slen + 1, wp, wlen);
 			MEMCPY(tp->search, wp, (tp->slen = slen) + 1);
 		}
 		TAILQ_INSERT_TAIL(tqp->tagq, tp, q);
 
 		/* Try to preset the tag within the current file. */
 		if (sp->frp != NULL && sp->frp->name != NULL &&
 		    tqp->current == NULL && !strcmp(tp->fname, sp->frp->name))
 			tqp->current = tp;
 
 		++*matchesp;
 	}
 
 	if (tqp->current == NULL)
 		tqp->current = TAILQ_FIRST(tqp->tagq);
 
 	return read_prompt(sp, csc);
 
 io_err:	if (feof(csc->from_fp))
 		errno = EIO;
 	msgq_str(sp, M_SYSERR, "%s", csc->dname);
 	terminate(sp, csc, 0);
 	return (1);
 }
 
 /*
  * csc_file --
  *	Search for the right path to this file.
  */
 static void
 csc_file(SCR *sp, CSC *csc, char *name, char **dirp, size_t *dlenp, int *isolderp)
 {
 	struct stat sb;
 	char **pp, *buf;
 
 	/*
 	 * Check for the file in all of the listed paths.  If we don't
 	 * find it, we simply return it unchanged.  We have to do this
 	 * now, even though it's expensive, because if the user changes
 	 * directories, we can't change our minds as to where the file
 	 * lives.
 	 */
 	for (pp = csc->paths; *pp != NULL; ++pp) {
 		if ((buf = join(*pp, name)) == NULL) {
 			msgq(sp, M_SYSERR, NULL);
 			*dlenp = 0;
 			return;
 		}
 		if (stat(buf, &sb) == 0) {
 			free(buf);
 			*dirp = *pp;
 			*dlenp = strlen(*pp);
 			*isolderp = timespeccmp(
-			    &sb.st_mtimespec, &csc->mtim, <);
+			    &sb.st_mtim, &csc->mtim, <);
 			return;
 		}
 		free(buf);
 	}
 	*dlenp = 0;
 }
 
 /*
  * cscope_help --
  *	The cscope help command.
  */
 static int
 cscope_help(SCR *sp, EXCMD *cmdp, CHAR_T *subcmd)
 {
 	char *np;
 	size_t nlen;
 
 	INT2CHAR(sp, subcmd, STRLEN(subcmd) + 1, np, nlen);
 	return (csc_help(sp, np));
 }
 
 /*
  * csc_help --
  *	Display help/usage messages.
  */
 static int
 csc_help(SCR *sp, char *cmd)
 {
 	CC const *ccp;
 
-	if (cmd != NULL && *cmd != '\0')
+	if (cmd != NULL && *cmd != '\0') {
 		if ((ccp = lookup_ccmd(cmd)) == NULL) {
 			ex_printf(sp,
 			    "%s doesn't match any cscope command\n", cmd);
 			return (1);
 		} else {
 			ex_printf(sp,
 			  "Command: %s (%s)\n", ccp->name, ccp->help_msg);
 			ex_printf(sp, "  Usage: %s\n", ccp->usage_msg);
 			return (0);
 		}
+	}
 
 	ex_printf(sp, "cscope commands:\n");
 	for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
 		ex_printf(sp, "  %*s: %s\n", 5, ccp->name, ccp->help_msg);
 	return (0);
 }
 
 /*
  * cscope_kill --
  *	The cscope kill command.
  */
 static int
 cscope_kill(SCR *sp, EXCMD *cmdp, CHAR_T *cn)
 {
 	char *np;
 	size_t nlen;
 	int n = 1;
 
 	if (*cn) {
 		INT2CHAR(sp, cn, STRLEN(cn) + 1, np, nlen);
 		n = atoi(np);
 	}
 	return (terminate(sp, NULL, n));
 }
 
 /*
  * terminate --
  *	Detach from a cscope process.
  */
 static int
 terminate(SCR *sp, CSC *csc, int n)
 {
 	EX_PRIVATE *exp;
 	int i = 0, pstat;
 	CSC *cp, *pre_cp = NULL;
 
 	exp = EXP(sp);
 
 	/*
 	 * We either get a csc structure or a number.  Locate and remove
 	 * the candidate which matches the structure or the number.
 	 */
 	if (csc == NULL && n < 1)
 		goto badno;
 	SLIST_FOREACH(cp, exp->cscq, q) {
 		++i;
 		if (csc == NULL ? i != n : cp != csc) {
 			pre_cp = cp;
 			continue;
 		}
 		if (cp == SLIST_FIRST(exp->cscq))
 			SLIST_REMOVE_HEAD(exp->cscq, q);
 		else
 			SLIST_REMOVE_AFTER(pre_cp, q);
 		csc = cp;
 		break;
 	}
 	if (csc == NULL) {
 badno:		msgq(sp, M_ERR, "312|%d: no such cscope session", n);
 		return (1);
 	}
 
 	/*
 	 * XXX
 	 * Theoretically, we have the only file descriptors to the process,
 	 * so closing them should let it exit gracefully, deleting temporary
 	 * files, etc.  However, the earlier created cscope processes seems
 	 * to refuse to quit unless we send a SIGTERM signal.
 	 */
 	if (csc->from_fp != NULL)
 		(void)fclose(csc->from_fp);
 	if (csc->to_fp != NULL)
 		(void)fclose(csc->to_fp);
 	if (i > 1)
 		(void)kill(csc->pid, SIGTERM);
 	(void)waitpid(csc->pid, &pstat, 0);
 
 	/* Discard cscope connection information. */
 	free(csc->pbuf);
 	free(csc->paths);
 	free(csc);
 	return (0);
 }
 
 /*
  * cscope_reset --
  *	The cscope reset command.
  */
 static int
 cscope_reset(SCR *sp, EXCMD *cmdp, CHAR_T *notusedp)
 {
 	return cscope_end(sp);
 }
 
 /*
  * cscope_end --
  *	End all cscope connections.
  *
  * PUBLIC: int cscope_end(SCR *);
  */
 int
 cscope_end(SCR *sp)
 {
 	EX_PRIVATE *exp;
 
 	for (exp = EXP(sp); !SLIST_EMPTY(exp->cscq);)
 		if (terminate(sp, NULL, 1))
 			return (1);
 	return (0);
 }
 
 /*
  * cscope_display --
  *	Display current connections.
  *
  * PUBLIC: int cscope_display(SCR *);
  */
 int
 cscope_display(SCR *sp)
 {
 	EX_PRIVATE *exp;
 	CSC *csc;
 	int i = 0;
 
 	exp = EXP(sp);
 	if (SLIST_EMPTY(exp->cscq)) {
 		ex_printf(sp, "No cscope connections.\n");
 		return (0);
 	}
 	SLIST_FOREACH(csc, exp->cscq, q)
 		ex_printf(sp, "%2d %s (process %lu)\n",
 		    ++i, csc->dname, (u_long)csc->pid);
 	return (0);
 }
 
 /*
  * cscope_search --
  *	Search a file for a cscope entry.
  *
  * PUBLIC: int cscope_search(SCR *, TAGQ *, TAG *);
  */
 int
 cscope_search(SCR *sp, TAGQ *tqp, TAG *tp)
 {
 	MARK m;
 
 	/* If we don't have a search pattern, use the line number. */
 	if (tp->search == NULL) {
 		if (!db_exist(sp, tp->slno)) {
 			tag_msg(sp, TAG_BADLNO, tqp->tag);
 			return (1);
 		}
 		m.lno = tp->slno;
 	} else {
 		/*
 		 * Search for the tag; cheap fallback for C functions
 		 * if the name is the same but the arguments have changed.
 		 */
 		m.lno = 1;
 		m.cno = 0;
 		if (f_search(sp, &m, &m,
 		    tp->search, tp->slen, NULL, SEARCH_CSCOPE | SEARCH_FILE)) {
 			tag_msg(sp, TAG_SEARCH, tqp->tag);
 			return (1);
 		}
 
 		/*
 		 * !!!
 		 * Historically, tags set the search direction if it wasn't
 		 * already set.
 		 */
 		if (sp->searchdir == NOTSET)
 			sp->searchdir = FORWARD;
 	}
 
 	/*
 	 * !!!
 	 * Tags move to the first non-blank, NOT the search pattern start.
 	 */
 	sp->lno = m.lno;
 	sp->cno = 0;
 	(void)nonblank(sp, sp->lno, &sp->cno);
 	return (0);
 }
 
 
 /*
  * lookup_ccmd --
  *	Return a pointer to the command structure.
  */
 static CC const *
 lookup_ccmd(char *name)
 {
 	CC const *ccp;
 	size_t len;
 
 	len = strlen(name);
 	for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
 		if (strncmp(name, ccp->name, len) == 0)
 			return (ccp);
 	return (NULL);
 }
 
 /*
  * read_prompt --
  *	Read a prompt from cscope.
  */
 static int
 read_prompt(SCR *sp, CSC *csc)
 {
 	int ch;
 
 #define	CSCOPE_PROMPT		">> "
 	for (;;) {
 		while ((ch =
 		    getc(csc->from_fp)) != EOF && ch != CSCOPE_PROMPT[0]);
 		if (ch == EOF) {
 			terminate(sp, csc, 0);
 			return (1);
 		}
 		if (getc(csc->from_fp) != CSCOPE_PROMPT[1])
 			continue;
 		if (getc(csc->from_fp) != CSCOPE_PROMPT[2])
 			continue;
 		break;
 	}
 	return (0);
 }
Index: head/contrib/nvi/ex/ex_filter.c
===================================================================
--- head/contrib/nvi/ex/ex_filter.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_filter.c	(revision 366309)
@@ -1,314 +1,315 @@
 /*-
  * Copyright (c) 1991, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1991, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 
 static int filter_ldisplay(SCR *, FILE *);
 
 /*
  * ex_filter --
  *	Run a range of lines through a filter utility and optionally
  *	replace the original text with the stdout/stderr output of
  *	the utility.
  *
  * PUBLIC: int ex_filter(SCR *, 
  * PUBLIC:    EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype);
  */
 int
 ex_filter(SCR *sp, EXCMD *cmdp, MARK *fm, MARK *tm, MARK *rp, CHAR_T *cmd, enum filtertype ftype)
 {
 	FILE *ifp, *ofp;
 	pid_t parent_writer_pid, utility_pid;
 	recno_t nread;
 	int input[2], output[2], rval;
 	char *name;
 	char *np;
 	size_t nlen;
 
 	rval = 0;
 
 	/* Set return cursor position, which is never less than line 1. */
 	*rp = *fm;
 	if (rp->lno == 0)
 		rp->lno = 1;
 
 	/* We're going to need a shell. */
 	if (opts_empty(sp, O_SHELL, 0))
 		return (1);
 
 	/*
 	 * There are three different processes running through this code.
 	 * They are the utility, the parent-writer and the parent-reader.
 	 * The parent-writer is the process that writes from the file to
 	 * the utility, the parent reader is the process that reads from
 	 * the utility.
 	 *
 	 * Input and output are named from the utility's point of view.
 	 * The utility reads from input[0] and the parent(s) write to
 	 * input[1].  The parent(s) read from output[0] and the utility
 	 * writes to output[1].
 	 *
 	 * !!!
 	 * Historically, in the FILTER_READ case, the utility reads from
 	 * the terminal (e.g. :r! cat works).  Otherwise open up utility
 	 * input pipe.
 	 */
 	ofp = NULL;
 	input[0] = input[1] = output[0] = output[1] = -1;
 	if (ftype != FILTER_READ && pipe(input) < 0) {
 		msgq(sp, M_SYSERR, "pipe");
 		goto err;
 	}
 
 	/* Open up utility output pipe. */
 	if (pipe(output) < 0) {
 		msgq(sp, M_SYSERR, "pipe");
 		goto err;
 	}
 	if ((ofp = fdopen(output[0], "r")) == NULL) {
 		msgq(sp, M_SYSERR, "fdopen");
 		goto err;
 	}
 
 	/* Fork off the utility process. */
 	switch (utility_pid = vfork()) {
 	case -1:			/* Error. */
 		msgq(sp, M_SYSERR, "vfork");
 err:		if (input[0] != -1)
 			(void)close(input[0]);
 		if (input[1] != -1)
 			(void)close(input[1]);
 		if (ofp != NULL)
 			(void)fclose(ofp);
 		else if (output[0] != -1)
 			(void)close(output[0]);
 		if (output[1] != -1)
 			(void)close(output[1]);
 		return (1);
 	case 0:				/* Utility. */
 		/*
 		 * Redirect stdin from the read end of the input pipe, and
 		 * redirect stdout/stderr to the write end of the output pipe.
 		 *
 		 * !!!
 		 * Historically, ex only directed stdout into the input pipe,
 		 * letting stderr come out on the terminal as usual.  Vi did
 		 * not, directing both stdout and stderr into the input pipe.
 		 * We match that practice in both ex and vi for consistency.
 		 */
 		if (input[0] != -1)
 			(void)dup2(input[0], STDIN_FILENO);
 		(void)dup2(output[1], STDOUT_FILENO);
 		(void)dup2(output[1], STDERR_FILENO);
 
 		/* Close the utility's file descriptors. */
 		if (input[0] != -1)
 			(void)close(input[0]);
 		if (input[1] != -1)
 			(void)close(input[1]);
 		(void)close(output[0]);
 		(void)close(output[1]);
 
 		if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
 			name = O_STR(sp, O_SHELL);
 		else
 			++name;
 
 		INT2CHAR(sp, cmd, STRLEN(cmd)+1, np, nlen);
 		execl(O_STR(sp, O_SHELL), name, "-c", np, (char *)NULL);
 		msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
 		_exit (127);
 		/* NOTREACHED */
 	default:			/* Parent-reader, parent-writer. */
 		/* Close the pipe ends neither parent will use. */
 		if (input[0] != -1)
 			(void)close(input[0]);
 		(void)close(output[1]);
 		break;
 	}
 
 	/*
 	 * FILTER_RBANG, FILTER_READ:
 	 *
 	 * Reading is the simple case -- we don't need a parent writer,
 	 * so the parent reads the output from the read end of the output
 	 * pipe until it finishes, then waits for the child.  Ex_readfp
 	 * appends to the MARK, and closes ofp.
 	 *
 	 * For FILTER_RBANG, there is nothing to write to the utility.
 	 * Make sure it doesn't wait forever by closing its standard
 	 * input.
 	 *
 	 * !!!
 	 * Set the return cursor to the last line read in for FILTER_READ.
 	 * Historically, this behaves differently from ":r file" command,
 	 * which leaves the cursor at the first line read in.  Check to
 	 * make sure that it's not past EOF because we were reading into an
 	 * empty file.
 	 */
 	if (ftype == FILTER_RBANG || ftype == FILTER_READ) {
 		if (ftype == FILTER_RBANG)
 			(void)close(input[1]);
 
 		if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
 			rval = 1;
 		sp->rptlines[L_ADDED] += nread;
-		if (ftype == FILTER_READ)
+		if (ftype == FILTER_READ) {
 			if (fm->lno == 0)
 				rp->lno = nread;
 			else
 				rp->lno += nread;
+		}
 		goto uwait;
 	}
 
 	/*
 	 * FILTER_BANG, FILTER_WRITE
 	 *
 	 * Here we need both a reader and a writer.  Temporary files are
 	 * expensive and we'd like to avoid disk I/O.  Using pipes has the
 	 * obvious starvation conditions.  It's done as follows:
 	 *
 	 *	fork
 	 *	child
 	 *		write lines out
 	 *		exit
 	 *	parent
 	 *		FILTER_BANG:
 	 *			read lines into the file
 	 *			delete old lines
 	 *		FILTER_WRITE
 	 *			read and display lines
 	 *		wait for child
 	 *
 	 * XXX
 	 * We get away without locking the underlying database because we know
 	 * that none of the records that we're reading will be modified until
 	 * after we've read them.  This depends on the fact that the current
 	 * B+tree implementation doesn't balance pages or similar things when
 	 * it inserts new records.  When the DB code has locking, we should
 	 * treat vi as if it were multiple applications sharing a database, and
 	 * do the required locking.  If necessary a work-around would be to do
 	 * explicit locking in the line.c:db_get() code, based on the flag set
 	 * here.
 	 */
 	F_SET(sp->ep, F_MULTILOCK);
 	switch (parent_writer_pid = fork()) {
 	case -1:			/* Error. */
 		msgq(sp, M_SYSERR, "fork");
 		(void)close(input[1]);
 		(void)close(output[0]);
 		rval = 1;
 		break;
 	case 0:				/* Parent-writer. */
 		/*
 		 * Write the selected lines to the write end of the input
 		 * pipe.  This instance of ifp is closed by ex_writefp.
 		 */
 		(void)close(output[0]);
 		if ((ifp = fdopen(input[1], "w")) == NULL)
 			_exit (1);
 		_exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
 
 		/* NOTREACHED */
 	default:			/* Parent-reader. */
 		(void)close(input[1]);
 		if (ftype == FILTER_WRITE) {
 			/*
 			 * Read the output from the read end of the output
 			 * pipe and display it.  Filter_ldisplay closes ofp.
 			 */
 			if (filter_ldisplay(sp, ofp))
 				rval = 1;
 		} else {
 			/*
 			 * Read the output from the read end of the output
 			 * pipe.  Ex_readfp appends to the MARK and closes
 			 * ofp.
 			 */
 			if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
 				rval = 1;
 			sp->rptlines[L_ADDED] += nread;
 		}
 
 		/* Wait for the parent-writer. */
 		if (proc_wait(sp,
 		    (long)parent_writer_pid, "parent-writer", 0, 1))
 			rval = 1;
 
 		/* Delete any lines written to the utility. */
 		if (rval == 0 && ftype == FILTER_BANG &&
 		    (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
 		    del(sp, fm, tm, 1))) {
 			rval = 1;
 			break;
 		}
 
 		/*
 		 * If the filter had no output, we may have just deleted
 		 * the cursor.  Don't do any real error correction, we'll
 		 * try and recover later.
 		 */
 		 if (rp->lno > 1 && !db_exist(sp, rp->lno))
 			--rp->lno;
 		break;
 	}
 	F_CLR(sp->ep, F_MULTILOCK);
 
 	/*
 	 * !!!
 	 * Ignore errors on vi file reads, to make reads prettier.  It's
 	 * completely inconsistent, and historic practice.
 	 */
 uwait:	INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen);
 	return (proc_wait(sp, (long)utility_pid, np,
 	    ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
 }
 
 /*
  * filter_ldisplay --
  *	Display output from a utility.
  *
  * !!!
  * Historically, the characters were passed unmodified to the terminal.
  * We use the ex print routines to make sure they're printable.
  */
 static int
 filter_ldisplay(SCR *sp, FILE *fp)
 {
 	size_t len;
 	size_t wlen;
 	CHAR_T *wp;
 
 	EX_PRIVATE *exp;
 
 	for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);) {
 		FILE2INT5(sp, exp->ibcw, exp->ibp, len, wp, wlen);
 		if (ex_ldisplay(sp, wp, wlen, 0, 0))
 			break;
 	}
 	if (ferror(fp))
 		msgq(sp, M_SYSERR, "filter read");
 	(void)fclose(fp);
 	return (0);
 }
Index: head/contrib/nvi/ex/ex_global.c
===================================================================
--- head/contrib/nvi/ex/ex_global.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_global.c	(revision 366309)
@@ -1,312 +1,313 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 
 enum which {GLOBAL, V};
 
 static int ex_g_setup(SCR *, EXCMD *, enum which);
 
 /*
  * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
  *	Exec on lines matching a pattern.
  *
  * PUBLIC: int ex_global(SCR *, EXCMD *);
  */
 int
 ex_global(SCR *sp, EXCMD *cmdp)
 {
 	return (ex_g_setup(sp,
 	    cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
 }
 
 /*
  * ex_v -- [line [,line]] v /pattern/ [commands]
  *	Exec on lines not matching a pattern.
  *
  * PUBLIC: int ex_v(SCR *, EXCMD *);
  */
 int
 ex_v(SCR *sp, EXCMD *cmdp)
 {
 	return (ex_g_setup(sp, cmdp, V));
 }
 
 /*
  * ex_g_setup --
  *	Ex global and v commands.
  */
 static int
 ex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd)
 {
 	CHAR_T *ptrn, *p, *t;
 	EXCMD *ecp;
 	MARK abs;
 	RANGE *rp;
 	busy_t btype;
 	recno_t start, end;
 	regex_t *re;
 	regmatch_t match[1];
 	size_t len;
 	int cnt, delim, eval;
 	CHAR_T *dbp;
 
 	NEEDFILE(sp, cmdp);
 
 	if (F_ISSET(sp, SC_EX_GLOBAL)) {
 		msgq_wstr(sp, M_ERR, cmdp->cmd->name,
 	"124|The %s command can't be used as part of a global or v command");
 		return (1);
 	}
 
 	/*
 	 * Skip leading white space.  Historic vi allowed any non-alphanumeric
 	 * to serve as the global command delimiter.
 	 */
 	if (cmdp->argc == 0)
 		goto usage;
 	for (p = cmdp->argv[0]->bp; cmdskip(*p); ++p);
 	if (*p == '\0' || is09azAZ(*p) ||
 	    *p == '\\' || *p == '|' || *p == '\n') {
 usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
 		return (1);
 	}
 	delim = *p++;
 
 	/*
 	 * Get the pattern string, toss escaped characters.
 	 *
 	 * QUOTING NOTE:
 	 * Only toss an escaped character if it escapes a delimiter.
 	 */
 	for (ptrn = t = p;;) {
 		if (p[0] == '\0' || p[0] == delim) {
 			if (p[0] == delim)
 				++p;
 			/*
 			 * !!!
 			 * Nul terminate the pattern string -- it's passed
 			 * to regcomp which doesn't understand anything else.
 			 */
 			*t = '\0';
 			break;
 		}
-		if (p[0] == '\\')
+		if (p[0] == '\\') {
 			if (p[1] == delim)
 				++p;
 			else if (p[1] == '\\')
 				*t++ = *p++;
+		}
 		*t++ = *p++;
 	}
 
 	/* If the pattern string is empty, use the last one. */
 	if (*ptrn == '\0') {
 		if (sp->re == NULL) {
 			ex_emsg(sp, NULL, EXM_NOPREVRE);
 			return (1);
 		}
 
 		/* Re-compile the RE if necessary. */
 		if (!F_ISSET(sp, SC_RE_SEARCH) &&
 		    re_compile(sp, sp->re, sp->re_len,
 		    NULL, NULL, &sp->re_c, RE_C_SEARCH))
 			return (1);
 	} else {
 		/* Compile the RE. */
 		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
 		    &sp->re_len, &sp->re_c, RE_C_SEARCH))
 			return (1);
 
 		/*
 		 * Set saved RE.  Historic practice is that globals set
 		 * direction as well as the RE.
 		 */
 		sp->searchdir = FORWARD;
 	}
 	re = &sp->re_c;
 
 	/* The global commands always set the previous context mark. */
 	abs.lno = sp->lno;
 	abs.cno = sp->cno;
 	if (mark_set(sp, ABSMARK1, &abs, 1))
 		return (1);
 
 	/* Get an EXCMD structure. */
 	CALLOC_RET(sp, ecp, 1, sizeof(EXCMD));
 	TAILQ_INIT(ecp->rq);
 
 	/*
 	 * Get a copy of the command string; the default command is print.
 	 * Don't worry about a set of <blank>s with no command, that will
 	 * default to print in the ex parser.  We need to have two copies
 	 * because the ex parser may step on the command string when it's
 	 * parsing it.
 	 */
 	if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
 		p = L("p");
 		len = 1;
 	}
 
 	MALLOC_RET(sp, ecp->cp, (len * 2) * sizeof(CHAR_T));
 	ecp->o_cp = ecp->cp;
 	ecp->o_clen = len;
 	MEMCPY(ecp->cp + len, p, len);
 	ecp->range_lno = OOBLNO;
 	FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
 	SLIST_INSERT_HEAD(sp->gp->ecq, ecp, q);
 
 	/*
 	 * For each line...  The semantics of global matching are that we first
 	 * have to decide which lines are going to get passed to the command,
 	 * and then pass them to the command, ignoring other changes.  There's
 	 * really no way to do this in a single pass, since arbitrary line
 	 * creation, deletion and movement can be done in the ex command.  For
 	 * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
 	 * What we do is create linked list of lines that are tracked through
 	 * each ex command.  There's a callback routine which the DB interface
 	 * routines call when a line is created or deleted.  This doesn't help
 	 * the layering much.
 	 */
 	btype = BUSY_ON;
 	cnt = INTERRUPT_CHECK;
 	for (start = cmdp->addr1.lno,
 	    end = cmdp->addr2.lno; start <= end; ++start) {
 		if (cnt-- == 0) {
 			if (INTERRUPTED(sp)) {
 				SLIST_REMOVE_HEAD(sp->gp->ecq, q);
 				free(ecp->cp);
 				free(ecp);
 				break;
 			}
 			search_busy(sp, btype);
 			btype = BUSY_UPDATE;
 			cnt = INTERRUPT_CHECK;
 		}
 		if (db_get(sp, start, DBG_FATAL, &dbp, &len))
 			return (1);
 		match[0].rm_so = 0;
 		match[0].rm_eo = len;
 		switch (eval =
 		    regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
 		case 0:
 			if (cmd == V)
 				continue;
 			break;
 		case REG_NOMATCH:
 			if (cmd == GLOBAL)
 				continue;
 			break;
 		default:
 			re_error(sp, eval, &sp->re_c);
 			break;
 		}
 
 		/* If follows the last entry, extend the last entry's range. */
 		if ((rp = TAILQ_LAST(ecp->rq, _rh)) != NULL &&
 		    rp->stop == start - 1) {
 			++rp->stop;
 			continue;
 		}
 
 		/* Allocate a new range, and append it to the list. */
 		CALLOC(sp, rp, 1, sizeof(RANGE));
 		if (rp == NULL)
 			return (1);
 		rp->start = rp->stop = start;
 		TAILQ_INSERT_TAIL(ecp->rq, rp, q);
 	}
 	search_busy(sp, BUSY_OFF);
 	return (0);
 }
 
 /*
  * ex_g_insdel --
  *	Update the ranges based on an insertion or deletion.
  *
  * PUBLIC: int ex_g_insdel(SCR *, lnop_t, recno_t);
  */
 int
 ex_g_insdel(SCR *sp, lnop_t op, recno_t lno)
 {
 	EXCMD *ecp;
 	RANGE *nrp, *rp;
 
 	/* All insert/append operations are done as inserts. */
 	if (op == LINE_APPEND)
 		abort();
 
 	if (op == LINE_RESET)
 		return (0);
 
 	SLIST_FOREACH(ecp, sp->gp->ecq, q) {
 		if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
 			continue;
 		TAILQ_FOREACH_SAFE(rp, ecp->rq, q, nrp) {
 			/* If range less than the line, ignore it. */
 			if (rp->stop < lno)
 				continue;
 			
 			/*
 			 * If range greater than the line, decrement or
 			 * increment the range.
 			 */
 			if (rp->start > lno) {
 				if (op == LINE_DELETE) {
 					--rp->start;
 					--rp->stop;
 				} else {
 					++rp->start;
 					++rp->stop;
 				}
 				continue;
 			}
 
 			/*
 			 * Lno is inside the range, decrement the end point
 			 * for deletion, and split the range for insertion.
 			 * In the latter case, since we're inserting a new
 			 * element, neither range can be exhausted.
 			 */
 			if (op == LINE_DELETE) {
 				if (rp->start > --rp->stop) {
 					TAILQ_REMOVE(ecp->rq, rp, q);
 					free(rp);
 				}
 			} else {
 				CALLOC_RET(sp, nrp, 1, sizeof(RANGE));
 				nrp->start = lno + 1;
 				nrp->stop = rp->stop + 1;
 				rp->stop = lno - 1;
 				TAILQ_INSERT_AFTER(ecp->rq, rp, nrp, q);
 			}
 		}
 
 		/*
 		 * If the command deleted/inserted lines, the cursor moves to
 		 * the line after the deleted/inserted line.
 		 */
 		ecp->range_lno = lno;
 	}
 	return (0);
 }
Index: head/contrib/nvi/ex/ex_script.c
===================================================================
--- head/contrib/nvi/ex/ex_script.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_script.c	(revision 366309)
@@ -1,622 +1,624 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Brian Hirt.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/queue.h>
 #include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <termios.h>
 #include <unistd.h>
 #ifdef HAVE_LIBUTIL_H
 #include <libutil.h>
+#elif defined HAVE_PTY_H
+#include <pty.h>
 #else
 #include <util.h>
 #endif
 
 #include "../common/common.h"
 #include "../vi/vi.h"
 #include "script.h"
 #include "pathnames.h"
 
 static void	sscr_check(SCR *);
 static int	sscr_getprompt(SCR *);
 static int	sscr_init(SCR *);
 static int	sscr_insert(SCR *);
 static int	sscr_matchprompt(SCR *, char *, size_t, size_t *);
 static int	sscr_setprompt(SCR *, char *, size_t);
 
 /*
  * ex_script -- : sc[ript][!] [file]
  *	Switch to script mode.
  *
  * PUBLIC: int ex_script(SCR *, EXCMD *);
  */
 int
 ex_script(SCR *sp, EXCMD *cmdp)
 {
 	/* Vi only command. */
 	if (!F_ISSET(sp, SC_VI)) {
 		msgq(sp, M_ERR,
 		    "150|The script command is only available in vi mode");
 		return (1);
 	}
 
 	/* Switch to the new file. */
 	if (cmdp->argc != 0 && ex_edit(sp, cmdp))
 		return (1);
 
 	/* Create the shell, figure out the prompt. */
 	if (sscr_init(sp))
 		return (1);
 
 	return (0);
 }
 
 /*
  * sscr_init --
  *	Create a pty setup for a shell.
  */
 static int
 sscr_init(SCR *sp)
 {
 	SCRIPT *sc;
 	char *sh, *sh_path;
 
 	/* We're going to need a shell. */
 	if (opts_empty(sp, O_SHELL, 0))
 		return (1);
 
 	MALLOC_RET(sp, sc, sizeof(SCRIPT));
 	sp->script = sc;
 	sc->sh_prompt = NULL;
 	sc->sh_prompt_len = 0;
 
 	/*
 	 * There are two different processes running through this code.
 	 * They are the shell and the parent.
 	 */
 	sc->sh_master = sc->sh_slave = -1;
 
 	if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
 		msgq(sp, M_SYSERR, "tcgetattr");
 		goto err;
 	}
 
 	/*
 	 * Turn off output postprocessing and echo.
 	 */
 	sc->sh_term.c_oflag &= ~OPOST;
 	sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);
 
 	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
 		msgq(sp, M_SYSERR, "tcgetattr");
 		goto err;
 	}
 
 	if (openpty(&sc->sh_master,
 	    &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
 		msgq(sp, M_SYSERR, "openpty");
 		goto err;
 	}
 
 	/*
 	 * __TK__ huh?
 	 * Don't use vfork() here, because the signal semantics differ from
 	 * implementation to implementation.
 	 */
 	switch (sc->sh_pid = fork()) {
 	case -1:			/* Error. */
 		msgq(sp, M_SYSERR, "fork");
 err:		if (sc->sh_master != -1)
 			(void)close(sc->sh_master);
 		if (sc->sh_slave != -1)
 			(void)close(sc->sh_slave);
 		return (1);
 	case 0:				/* Utility. */
 		/*
 		 * XXX
 		 * So that shells that do command line editing turn it off.
 		 */
 		(void)setenv("TERM", "emacs", 1);
 		(void)setenv("TERMCAP", "emacs:", 1);
 		(void)setenv("EMACS", "t", 1);
 
 		(void)setsid();
 #ifdef TIOCSCTTY
 		/*
 		 * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
 		 * ioctl, not by opening a terminal device file.  POSIX 1003.1
 		 * doesn't define a portable way to do this.  If TIOCSCTTY is
 		 * not available, hope that the open does it.
 		 */
 		(void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
 #endif
 		(void)close(sc->sh_master);
 		(void)dup2(sc->sh_slave, STDIN_FILENO);
 		(void)dup2(sc->sh_slave, STDOUT_FILENO);
 		(void)dup2(sc->sh_slave, STDERR_FILENO);
 		(void)close(sc->sh_slave);
 
 		/* Assumes that all shells have -i. */
 		sh_path = O_STR(sp, O_SHELL);
 		if ((sh = strrchr(sh_path, '/')) == NULL)
 			sh = sh_path;
 		else
 			++sh;
 		execl(sh_path, sh, "-i", NULL);
 		msgq_str(sp, M_SYSERR, sh_path, "execl: %s");
 		_exit(127);
 	default:			/* Parent. */
 		break;
 	}
 
 	if (sscr_getprompt(sp))
 		return (1);
 
 	F_SET(sp, SC_SCRIPT);
 	F_SET(sp->gp, G_SCRWIN);
 	return (0);
 }
 
 /*
  * sscr_getprompt --
  *	Eat lines printed by the shell until a line with no trailing
  *	carriage return comes; set the prompt from that line.
  */
 static int
 sscr_getprompt(SCR *sp)
 {
 	EX_PRIVATE *exp;
 	struct timeval tv;
 	char *endp, *p, *t, buf[1024];
 	SCRIPT *sc;
 	fd_set fdset;
 	recno_t lline;
 	size_t llen, len;
 	int nr;
 	CHAR_T *wp;
 	size_t wlen;
 
 	exp = EXP(sp);
 
 	FD_ZERO(&fdset);
 	endp = buf;
 	len = sizeof(buf);
 
 	/* Wait up to a second for characters to read. */
 	tv.tv_sec = 5;
 	tv.tv_usec = 0;
 	sc = sp->script;
 	FD_SET(sc->sh_master, &fdset);
 	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
 	case -1:		/* Error or interrupt. */
 		msgq(sp, M_SYSERR, "select");
 		goto prompterr;
 	case  0:		/* Timeout */
 		msgq(sp, M_ERR, "Error: timed out");
 		goto prompterr;
 	case  1:		/* Characters to read. */
 		break;
 	}
 
 	/* Read the characters. */
 more:	len = sizeof(buf) - (endp - buf);
 	switch (nr = read(sc->sh_master, endp, len)) {
 	case  0:			/* EOF. */
 		msgq(sp, M_ERR, "Error: shell: EOF");
 		goto prompterr;
 	case -1:			/* Error or interrupt. */
 		msgq(sp, M_SYSERR, "shell");
 		goto prompterr;
 	default:
 		endp += nr;
 		break;
 	}
 
 	/* If any complete lines, push them into the file. */
 	for (p = t = buf; p < endp; ++p) {
 		if (*p == '\r' || *p == '\n') {
 			if (CHAR2INT5(sp, exp->ibcw, t, p - t, wp, wlen))
 				goto conv_err;
 			if (db_last(sp, &lline) ||
 			    db_append(sp, 0, lline, wp, wlen))
 				goto prompterr;
 			t = p + 1;
 		}
 	}
 	if (p > buf) {
 		memmove(buf, t, endp - t);
 		endp = buf + (endp - t);
 	}
 	if (endp == buf)
 		goto more;
 
 	/* Wait up 1/10 of a second to make sure that we got it all. */
 	tv.tv_sec = 0;
 	tv.tv_usec = 100000;
 	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
 	case -1:		/* Error or interrupt. */
 		msgq(sp, M_SYSERR, "select");
 		goto prompterr;
 	case  0:		/* Timeout */
 		break;
 	case  1:		/* Characters to read. */
 		goto more;
 	}
 
 	/* Timed out, so theoretically we have a prompt. */
 	llen = endp - buf;
 	endp = buf;
 
 	/* Append the line into the file. */
 	if (CHAR2INT5(sp, exp->ibcw, buf, llen, wp, wlen))
 		goto conv_err;
 	if (db_last(sp, &lline) || db_append(sp, 0, lline, wp, wlen)) {
 		if (0)
 conv_err:		msgq(sp, M_ERR, "323|Invalid input. Truncated.");
 prompterr:	sscr_end(sp);
 		return (1);
 	}
 
 	return (sscr_setprompt(sp, buf, llen));
 }
 
 /*
  * sscr_exec --
  *	Take a line and hand it off to the shell.
  *
  * PUBLIC: int sscr_exec(SCR *, recno_t);
  */
 int
 sscr_exec(SCR *sp, recno_t lno)
 {
 	SCRIPT *sc;
 	recno_t last_lno;
 	size_t blen, len, last_len, tlen;
 	int isempty, matchprompt, nw, rval;
 	char *bp = NULL, *p;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/* If there's a prompt on the last line, append the command. */
 	if (db_last(sp, &last_lno))
 		return (1);
 	if (db_get(sp, last_lno, DBG_FATAL, &wp, &wlen))
 		return (1);
 	INT2CHAR(sp, wp, wlen, p, last_len);
 	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
 		matchprompt = 1;
 		GET_SPACE_RETC(sp, bp, blen, last_len + 128);
 		memmove(bp, p, last_len);
 	} else
 		matchprompt = 0;
 
 	/* Get something to execute. */
 	if (db_eget(sp, lno, &wp, &wlen, &isempty)) {
 		if (isempty)
 			goto empty;
 		goto err1;
 	}
 
 	/* Empty lines aren't interesting. */
 	if (wlen == 0)
 		goto empty;
 	INT2CHAR(sp, wp, wlen, p, len);
 
 	/* Delete any prompt. */
 	if (sscr_matchprompt(sp, p, len, &tlen)) {
 		if (tlen == len) {
 empty:			msgq(sp, M_BERR, "151|No command to execute");
 			goto err1;
 		}
 		p += (len - tlen);
 		len = tlen;
 	}
 
 	/* Push the line to the shell. */
 	sc = sp->script;
 	if ((nw = write(sc->sh_master, p, len)) != len)
 		goto err2;
 	rval = 0;
 	if (write(sc->sh_master, "\n", 1) != 1) {
 err2:		if (nw == 0)
 			errno = EIO;
 		msgq(sp, M_SYSERR, "shell");
 		goto err1;
 	}
 
 	if (matchprompt) {
 		ADD_SPACE_RETC(sp, bp, blen, last_len + len);
 		memmove(bp + last_len, p, len);
 		CHAR2INT(sp, bp, last_len + len, wp, wlen);
 		if (db_set(sp, last_lno, wp, wlen))
 err1:			rval = 1;
 	}
 	if (matchprompt)
 		FREE_SPACE(sp, bp, blen);
 	return (rval);
 }
 
 /*
  * sscr_input --
  *	Read any waiting shell input.
  *
  * PUBLIC: int sscr_input(SCR *);
  */
 int
 sscr_input(SCR *sp)
 {
 	GS *gp;
 	struct timeval poll;
 	fd_set rdfd;
 	int maxfd;
 
 	gp = sp->gp;
 
 loop:	maxfd = 0;
 	FD_ZERO(&rdfd);
 	poll.tv_sec = 0;
 	poll.tv_usec = 0;
 
 	/* Set up the input mask. */
 	TAILQ_FOREACH(sp, gp->dq, q)
 		if (F_ISSET(sp, SC_SCRIPT)) {
 			FD_SET(sp->script->sh_master, &rdfd);
 			if (sp->script->sh_master > maxfd)
 				maxfd = sp->script->sh_master;
 		}
 
 	/* Check for input. */
 	switch (select(maxfd + 1, &rdfd, NULL, NULL, &poll)) {
 	case -1:
 		msgq(sp, M_SYSERR, "select");
 		return (1);
 	case 0:
 		return (0);
 	default:
 		break;
 	}
 
 	/* Read the input. */
 	TAILQ_FOREACH(sp, gp->dq, q)
 		if (F_ISSET(sp, SC_SCRIPT) &&
 		    FD_ISSET(sp->script->sh_master, &rdfd) &&
 		    sscr_insert(sp))
 			return (1);
 	goto loop;
 }
 
 /*
  * sscr_insert --
  *	Take a line from the shell and insert it into the file.
  */
 static int
 sscr_insert(SCR *sp)
 {
 	EX_PRIVATE *exp;
 	struct timeval tv;
 	char *endp, *p, *t;
 	SCRIPT *sc;
 	fd_set rdfd;
 	recno_t lno;
 	size_t blen, len, tlen;
 	int nr, rval;
 	char *bp;
 	CHAR_T *wp;
 	size_t wlen = 0;
 
 	exp = EXP(sp);
 
 
 	/* Find out where the end of the file is. */
 	if (db_last(sp, &lno))
 		return (1);
 
 #define	MINREAD	1024
 	GET_SPACE_RETC(sp, bp, blen, MINREAD);
 	endp = bp;
 
 	/* Read the characters. */
 	rval = 1;
 	sc = sp->script;
 more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
 	case  0:			/* EOF; shell just exited. */
 		sscr_end(sp);
 		rval = 0;
 		goto ret;
 	case -1:			/* Error or interrupt. */
 		msgq(sp, M_SYSERR, "shell");
 		goto ret;
 	default:
 		endp += nr;
 		break;
 	}
 
 	/* Append the lines into the file. */
 	for (p = t = bp; p < endp; ++p) {
 		if (*p == '\r' || *p == '\n') {
 			len = p - t;
 			if (CHAR2INT5(sp, exp->ibcw, t, len, wp, wlen))
 				goto conv_err;
 			if (db_append(sp, 1, lno++, wp, wlen))
 				goto ret;
 			t = p + 1;
 		}
 	}
 	if (p > t) {
 		len = p - t;
 		/*
 		 * If the last thing from the shell isn't another prompt, wait
 		 * up to 1/10 of a second for more stuff to show up, so that
 		 * we don't break the output into two separate lines.  Don't
 		 * want to hang indefinitely because some program is hanging,
 		 * confused the shell, or whatever.
 		 */
 		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
 			tv.tv_sec = 0;
 			tv.tv_usec = 100000;
 			FD_ZERO(&rdfd);
 			FD_SET(sc->sh_master, &rdfd);
 			if (select(sc->sh_master + 1,
 			    &rdfd, NULL, NULL, &tv) == 1) {
 				memmove(bp, t, len);
 				endp = bp + len;
 				goto more;
 			}
 		}
 		if (sscr_setprompt(sp, t, len))
 			return (1);
 		if (CHAR2INT5(sp, exp->ibcw, t, len, wp, wlen))
 			goto conv_err;
 		if (db_append(sp, 1, lno++, wp, wlen))
 			goto ret;
 	}
 
 	/* The cursor moves to EOF. */
 	sp->lno = lno;
 	sp->cno = wlen ? wlen - 1 : 0;
 	rval = vs_refresh(sp, 1);
 
 	if (0)
 conv_err:	msgq(sp, M_ERR, "323|Invalid input. Truncated.");
 
 ret:	FREE_SPACE(sp, bp, blen);
 	return (rval);
 }
 
 /*
  * sscr_setprompt --
  *
  * Set the prompt to the last line we got from the shell.
  *
  */
 static int
 sscr_setprompt(SCR *sp, char *buf, size_t len)
 {
 	SCRIPT *sc;
 
 	sc = sp->script;
 	free(sc->sh_prompt);
 	MALLOC(sp, sc->sh_prompt, len + 1);
 	if (sc->sh_prompt == NULL) {
 		sscr_end(sp);
 		return (1);
 	}
 	memmove(sc->sh_prompt, buf, len);
 	sc->sh_prompt_len = len;
 	sc->sh_prompt[len] = '\0';
 	return (0);
 }
 
 /*
  * sscr_matchprompt --
  *	Check to see if a line matches the prompt.  Nul's indicate
  *	parts that can change, in both content and size.
  */
 static int
 sscr_matchprompt(SCR *sp, char *lp, size_t line_len, size_t *lenp)
 {
 	SCRIPT *sc;
 	size_t prompt_len;
 	char *pp;
 
 	sc = sp->script;
 	if (line_len < (prompt_len = sc->sh_prompt_len))
 		return (0);
 
 	for (pp = sc->sh_prompt;
 	    prompt_len && line_len; --prompt_len, --line_len) {
 		if (*pp == '\0') {
 			for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
 			if (!prompt_len)
 				return (0);
 			for (; line_len && *lp != *pp; --line_len, ++lp);
 			if (!line_len)
 				return (0);
 		}
 		if (*pp++ != *lp++)
 			break;
 	}
 
 	if (prompt_len)
 		return (0);
 	if (lenp != NULL)
 		*lenp = line_len;
 	return (1);
 }
 
 /*
  * sscr_end --
  *	End the pipe to a shell.
  *
  * PUBLIC: int sscr_end(SCR *);
  */
 int
 sscr_end(SCR *sp)
 {
 	SCRIPT *sc;
 
 	if ((sc = sp->script) == NULL)
 		return (0);
 
 	/* Turn off the script flags. */
 	F_CLR(sp, SC_SCRIPT);
 	sscr_check(sp);
 
 	/* Close down the parent's file descriptors. */
 	if (sc->sh_master != -1)
 	    (void)close(sc->sh_master);
 	if (sc->sh_slave != -1)
 	    (void)close(sc->sh_slave);
 
 	/* This should have killed the child. */
 	(void)proc_wait(sp, (long)sc->sh_pid, "script-shell", 0, 0);
 
 	/* Free memory. */
 	free(sc->sh_prompt);
 	free(sc);
 	sp->script = NULL;
 
 	return (0);
 }
 
 /*
  * sscr_check --
  *	Set/clear the global scripting bit.
  */
 static void
 sscr_check(SCR *sp)
 {
 	GS *gp;
 
 	gp = sp->gp;
 	TAILQ_FOREACH(sp, gp->dq, q)
 		if (F_ISSET(sp, SC_SCRIPT)) {
 			F_SET(gp, G_SCRWIN);
 			return;
 		}
 	F_CLR(gp, G_SCRWIN);
 }
Index: head/contrib/nvi/ex/ex_shell.c
===================================================================
--- head/contrib/nvi/ex/ex_shell.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_shell.c	(revision 366309)
@@ -1,223 +1,222 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/queue.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 
 static const char *sigmsg(int);
 
 /*
  * ex_shell -- :sh[ell]
  *	Invoke the program named in the SHELL environment variable
  *	with the argument -i.
  *
  * PUBLIC: int ex_shell(SCR *, EXCMD *);
  */
 int
 ex_shell(SCR *sp, EXCMD *cmdp)
 {
 	int rval;
 	char *buf;
 
 	/* We'll need a shell. */
 	if (opts_empty(sp, O_SHELL, 0))
 		return (1);
 
 	/*
 	 * XXX
 	 * Assumes all shells use -i.
 	 */
-	(void)asprintf(&buf, "%s -i", O_STR(sp, O_SHELL));
-	if (buf == NULL) {
+	if (asprintf(&buf, "%s -i", O_STR(sp, O_SHELL)) == -1) {
 		msgq(sp, M_SYSERR, NULL);
 		return (1);
 	}
 
 	/* Restore the window name. */
 	(void)sp->gp->scr_rename(sp, NULL, 0);
 
 	/* If we're still in a vi screen, move out explicitly. */
 	rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
 	free(buf);
 
 	/* Set the window name. */
 	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
 
 	/*
 	 * !!!
 	 * Historically, vi didn't require a continue message after the
 	 * return of the shell.  Match it.
 	 */
 	F_SET(sp, SC_EX_WAIT_NO);
 
 	return (rval);
 }
 
 /*
  * ex_exec_proc --
  *	Run a separate process.
  *
  * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int);
  */
 int
 ex_exec_proc(SCR *sp, EXCMD *cmdp, char *cmd, const char *msg, int need_newline)
 {
 	GS *gp;
 	const char *name;
 	pid_t pid;
 
 	gp = sp->gp;
 
 	/* We'll need a shell. */
 	if (opts_empty(sp, O_SHELL, 0))
 		return (1);
 
 	/* Enter ex mode. */
 	if (F_ISSET(sp, SC_VI)) {
 		if (gp->scr_screen(sp, SC_EX)) {
 			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
 			return (1);
 		}
 		(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
 		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
 	}
 
 	/* Put out additional newline, message. */
 	if (need_newline)
 		(void)ex_puts(sp, "\n");
 	if (msg != NULL) {
 		(void)ex_puts(sp, msg);
 		(void)ex_puts(sp, "\n");
 	}
 	(void)ex_fflush(sp);
 
 	switch (pid = vfork()) {
 	case -1:			/* Error. */
 		msgq(sp, M_SYSERR, "vfork");
 		return (1);
 	case 0:				/* Utility. */
 		if (gp->scr_child)
 			gp->scr_child(sp);
 		if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
 			name = O_STR(sp, O_SHELL);
 		else
 			++name;
 		execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL);
 		msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
 		_exit(127);
 		/* NOTREACHED */
 	default:			/* Parent. */
 		return (proc_wait(sp, (long)pid, cmd, 0, 0));
 	}
 	/* NOTREACHED */
 }
 
 /*
  * proc_wait --
  *	Wait for one of the processes.
  *
  * !!!
  * The pid_t type varies in size from a short to a long depending on the
  * system.  It has to be cast into something or the standard promotion
  * rules get you.  I'm using a long based on the belief that nobody is
  * going to make it unsigned and it's unlikely to be a quad.
  *
  * PUBLIC: int proc_wait(SCR *, long, const char *, int, int);
  */
 int
 proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe)
 {
 	size_t len;
 	int nf, pstat;
 	char *p;
 
 	/* Wait for the utility, ignoring interruptions. */
 	for (;;) {
 		errno = 0;
 		if (waitpid((pid_t)pid, &pstat, 0) != -1)
 			break;
 		if (errno != EINTR) {
 			msgq(sp, M_SYSERR, "waitpid");
 			return (1);
 		}
 	}
 
 	/*
 	 * Display the utility's exit status.  Ignore SIGPIPE from the
 	 * parent-writer, as that only means that the utility chose to
 	 * exit before reading all of its input.
 	 */
 	if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
 		for (; cmdskip(*cmd); ++cmd);
 		p = msg_print(sp, cmd, &nf);
 		len = strlen(p);
 		msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
 		    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
 		    sigmsg(WTERMSIG(pstat)),
 		    WCOREDUMP(pstat) ? "; core dumped" : "");
 		if (nf)
 			FREE_SPACE(sp, p, 0);
 		return (1);
 	}
 
 	if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
 		/*
 		 * Remain silent for "normal" errors when doing shell file
 		 * name expansions, they almost certainly indicate nothing
 		 * more than a failure to match.
 		 *
 		 * Remain silent for vi read filter errors.  It's historic
 		 * practice.
 		 */
 		if (!silent) {
 			for (; cmdskip(*cmd); ++cmd);
 			p = msg_print(sp, cmd, &nf);
 			len = strlen(p);
 			msgq(sp, M_ERR, "%.*s%s: exited with status %d",
 			    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
 			    WEXITSTATUS(pstat));
 			if (nf)
 				FREE_SPACE(sp, p, 0);
 		}
 		return (1);
 	}
 	return (0);
 }
 
 /*
  * sigmsg --
  * 	Return a pointer to a message describing a signal.
  */
 static const char *
 sigmsg(int signo)
 {
 	static char buf[40];
 	char *message;
 
 	/* POSIX.1-2008 leaves strsignal(3)'s return value unspecified. */
 	if ((message = strsignal(signo)) != NULL)
 		return message;
 	(void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
 	return (buf);
 }
Index: head/contrib/nvi/ex/ex_subst.c
===================================================================
--- head/contrib/nvi/ex/ex_subst.c	(revision 366308)
+++ head/contrib/nvi/ex/ex_subst.c	(revision 366309)
@@ -1,1433 +1,1434 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "../vi/vi.h"
 
 #define	SUB_FIRST	0x01		/* The 'r' flag isn't reasonable. */
 #define	SUB_MUSTSETR	0x02		/* The 'r' flag is required. */
 
 static int re_conv(SCR *, CHAR_T **, size_t *, int *);
 static int re_cscope_conv(SCR *, CHAR_T **, size_t *, int *);
 static int re_sub(SCR *,
 		CHAR_T *, CHAR_T **, size_t *, size_t *, regmatch_t [10]);
 static int re_tag_conv(SCR *, CHAR_T **, size_t *, int *);
 static int s(SCR *, EXCMD *, CHAR_T *, regex_t *, u_int);
 
 /*
  * ex_s --
  *	[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
  *
  *	Substitute on lines matching a pattern.
  *
  * PUBLIC: int ex_s(SCR *, EXCMD *);
  */
 int
 ex_s(SCR *sp, EXCMD *cmdp)
 {
 	regex_t *re;
 	size_t blen, len;
 	u_int flags;
 	int delim;
 	CHAR_T *bp, *p, *ptrn, *rep, *t;
 
 	/*
 	 * Skip leading white space.
 	 *
 	 * !!!
 	 * Historic vi allowed any non-alphanumeric to serve as the
 	 * substitution command delimiter.
 	 *
 	 * !!!
 	 * If the arguments are empty, it's the same as &, i.e. we
 	 * repeat the last substitution.
 	 */
 	if (cmdp->argc == 0)
 		goto subagain;
 	for (p = cmdp->argv[0]->bp,
 	    len = cmdp->argv[0]->len; len > 0; --len, ++p) {
 		if (!cmdskip(*p))
 			break;
 	}
 	if (len == 0)
 subagain:	return (ex_subagain(sp, cmdp));
 
 	delim = *p++;
 	if (is09azAZ(delim) || delim == '\\')
 		return (s(sp, cmdp, p, &sp->subre_c, SUB_MUSTSETR));
 
 	/*
 	 * !!!
 	 * The full-blown substitute command reset the remembered
 	 * state of the 'c' and 'g' suffices.
 	 */
 	sp->c_suffix = sp->g_suffix = 0;
 
 	/*
 	 * Get the pattern string, toss escaping characters.
 	 *
 	 * !!!
 	 * Historic vi accepted any of the following forms:
 	 *
 	 *	:s/abc/def/		change "abc" to "def"
 	 *	:s/abc/def		change "abc" to "def"
 	 *	:s/abc/			delete "abc"
 	 *	:s/abc			delete "abc"
 	 *
 	 * QUOTING NOTE:
 	 *
 	 * Only toss an escaping character if it escapes a delimiter.
 	 * This means that "s/A/\\\\f" replaces "A" with "\\f".  It
 	 * would be nice to be more regular, i.e. for each layer of
 	 * escaping a single escaping character is removed, but that's
 	 * not how the historic vi worked.
 	 */
 	for (ptrn = t = p;;) {
 		if (p[0] == '\0' || p[0] == delim) {
 			if (p[0] == delim)
 				++p;
 			/*
 			 * !!!
 			 * Nul terminate the pattern string -- it's passed
 			 * to regcomp which doesn't understand anything else.
 			 */
 			*t = '\0';
 			break;
 		}
-		if (p[0] == '\\')
+		if (p[0] == '\\') {
 			if (p[1] == delim)
 				++p;
 			else if (p[1] == '\\')
 				*t++ = *p++;
+		}
 		*t++ = *p++;
 	}
 
 	/*
 	 * If the pattern string is empty, use the last RE (not just the
 	 * last substitution RE).
 	 */
 	if (*ptrn == '\0') {
 		if (sp->re == NULL) {
 			ex_emsg(sp, NULL, EXM_NOPREVRE);
 			return (1);
 		}
 
 		/* Re-compile the RE if necessary. */
 		if (!F_ISSET(sp, SC_RE_SEARCH) &&
 		    re_compile(sp, sp->re, sp->re_len,
 		    NULL, NULL, &sp->re_c, RE_C_SEARCH))
 			return (1);
 		flags = 0;
 	} else {
 		/*
 		 * !!!
 		 * Compile the RE.  Historic practice is that substitutes set
 		 * the search direction as well as both substitute and search
 		 * RE's.  We compile the RE twice, as we don't want to bother
 		 * ref counting the pattern string and (opaque) structure.
 		 */
 		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
 		    &sp->re_len, &sp->re_c, RE_C_SEARCH))
 			return (1);
 		if (re_compile(sp, ptrn, t - ptrn, &sp->subre,
 		    &sp->subre_len, &sp->subre_c, RE_C_SUBST))
 			return (1);
 		
 		flags = SUB_FIRST;
 		sp->searchdir = FORWARD;
 	}
 	re = &sp->re_c;
 
 	/*
 	 * Get the replacement string.
 	 *
 	 * The special character & (\& if O_MAGIC not set) matches the
 	 * entire RE.  No handling of & is required here, it's done by
 	 * re_sub().
 	 *
 	 * The special character ~ (\~ if O_MAGIC not set) inserts the
 	 * previous replacement string into this replacement string.
 	 * Count ~'s to figure out how much space we need.  We could
 	 * special case nonexistent last patterns or whether or not
 	 * O_MAGIC is set, but it's probably not worth the effort.
 	 *
 	 * QUOTING NOTE:
 	 *
 	 * Only toss an escaping character if it escapes a delimiter or
 	 * if O_MAGIC is set and it escapes a tilde.
 	 *
 	 * !!!
 	 * If the entire replacement pattern is "%", then use the last
 	 * replacement pattern.  This semantic was added to vi in System
 	 * V and then percolated elsewhere, presumably around the time
 	 * that it was added to their version of ed(1).
 	 */
 	if (p[0] == '\0' || p[0] == delim) {
 		if (p[0] == delim)
 			++p;
 		free(sp->repl);
 		sp->repl = NULL;
 		sp->repl_len = 0;
 	} else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim))
 		p += p[1] == delim ? 2 : 1;
 	else {
 		for (rep = p, len = 0;
 		    p[0] != '\0' && p[0] != delim; ++p, ++len)
 			if (p[0] == '~')
 				len += sp->repl_len;
 		GET_SPACE_RETW(sp, bp, blen, len);
 		for (t = bp, len = 0, p = rep;;) {
 			if (p[0] == '\0' || p[0] == delim) {
 				if (p[0] == delim)
 					++p;
 				break;
 			}
 			if (p[0] == '\\') {
 				if (p[1] == delim)
 					++p;
 				else if (p[1] == '\\') {
 					*t++ = *p++;
 					++len;
 				} else if (p[1] == '~') {
 					++p;
 					if (!O_ISSET(sp, O_MAGIC))
 						goto tilde;
 				}
 			} else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
 tilde:				++p;
 				MEMCPY(t, sp->repl, sp->repl_len);
 				t += sp->repl_len;
 				len += sp->repl_len;
 				continue;
 			}
 			*t++ = *p++;
 			++len;
 		}
 		if ((sp->repl_len = len) != 0) {
 			free(sp->repl);
 			MALLOC(sp, sp->repl, len * sizeof(CHAR_T));
 			if (sp->repl == NULL) {
 				FREE_SPACEW(sp, bp, blen);
 				return (1);
 			}
 			MEMCPY(sp->repl, bp, len);
 		}
 		FREE_SPACEW(sp, bp, blen);
 	}
 	return (s(sp, cmdp, p, re, flags));
 }
 
 /*
  * ex_subagain --
  *	[line [,line]] & [cgr] [count] [#lp]]
  *
  *	Substitute using the last substitute RE and replacement pattern.
  *
  * PUBLIC: int ex_subagain(SCR *, EXCMD *);
  */
 int
 ex_subagain(SCR *sp, EXCMD *cmdp)
 {
 	if (sp->subre == NULL) {
 		ex_emsg(sp, NULL, EXM_NOPREVRE);
 		return (1);
 	}
 	if (!F_ISSET(sp, SC_RE_SUBST) &&
 	    re_compile(sp, sp->subre, sp->subre_len,
 	    NULL, NULL, &sp->subre_c, RE_C_SUBST))
 		return (1);
 	return (s(sp,
 	    cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->subre_c, 0));
 }
 
 /*
  * ex_subtilde --
  *	[line [,line]] ~ [cgr] [count] [#lp]]
  *
  *	Substitute using the last RE and last substitute replacement pattern.
  *
  * PUBLIC: int ex_subtilde(SCR *, EXCMD *);
  */
 int
 ex_subtilde(SCR *sp, EXCMD *cmdp)
 {
 	if (sp->re == NULL) {
 		ex_emsg(sp, NULL, EXM_NOPREVRE);
 		return (1);
 	}
 	if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp, sp->re,
 	    sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
 		return (1);
 	return (s(sp,
 	    cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->re_c, 0));
 }
 
 /*
  * s --
  * Do the substitution.  This stuff is *really* tricky.  There are lots of
  * special cases, and general nastiness.  Don't mess with it unless you're
  * pretty confident.
  * 
  * The nasty part of the substitution is what happens when the replacement
  * string contains newlines.  It's a bit tricky -- consider the information
  * that has to be retained for "s/f\(o\)o/^M\1^M\1/".  The solution here is
  * to build a set of newline offsets which we use to break the line up later,
  * when the replacement is done.  Don't change it unless you're *damned*
  * confident.
  */
-#define	NEEDNEWLINE(sp) {						\
+#define	NEEDNEWLINE(sp) do {						\
 	if (sp->newl_len == sp->newl_cnt) {				\
 		sp->newl_len += 25;					\
 		REALLOC(sp, sp->newl, size_t *,				\
 		    sp->newl_len * sizeof(size_t));			\
 		if (sp->newl == NULL) {					\
 			sp->newl_len = 0;				\
 			return (1);					\
 		}							\
 	}								\
-}
+} while (0)
 
-#define	BUILD(sp, l, len) {						\
+#define	BUILD(sp, l, len) do {						\
 	if (lbclen + (len) > lblen) {					\
 		lblen = p2roundup(MAX(lbclen + (len), 256));		\
 		REALLOC(sp, lb, CHAR_T *, lblen * sizeof(CHAR_T));	\
 		if (lb == NULL) {					\
 			lbclen = 0;					\
 			return (1);					\
 		}							\
 	}								\
 	MEMCPY(lb + lbclen, l, len);					\
 	lbclen += len;							\
-}
+} while (0)
 
-#define	NEEDSP(sp, len, pnt) {						\
+#define	NEEDSP(sp, len, pnt) do {					\
 	if (lbclen + (len) > lblen) {					\
 		lblen = p2roundup(MAX(lbclen + (len), 256));		\
 		REALLOC(sp, lb, CHAR_T *, lblen * sizeof(CHAR_T));	\
 		if (lb == NULL) {					\
 			lbclen = 0;					\
 			return (1);					\
 		}							\
 		pnt = lb + lbclen;					\
 	}								\
-}
+} while (0)
 
 static int
 s(SCR *sp, EXCMD *cmdp, CHAR_T *s, regex_t *re, u_int flags)
 {
 	EVENT ev;
 	MARK from, to;
 	TEXTH tiq[] = {{ 0 }};
 	recno_t elno, lno, slno;
 	u_long ul;
 	regmatch_t match[10];
 	size_t blen, cnt, last, lbclen, lblen, len, llen;
 	size_t offset, saved_offset, scno;
 	int cflag, lflag, nflag, pflag, rflag;
 	int didsub, do_eol_match, eflags, empty_ok, eval;
 	int linechanged, matched, quit, rval;
 	CHAR_T *bp, *lb;
 	enum nresult nret;
 
 	NEEDFILE(sp, cmdp);
 
 	slno = sp->lno;
 	scno = sp->cno;
 
 	/*
 	 * !!!
 	 * Historically, the 'g' and 'c' suffices were always toggled as flags,
 	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was
 	 * not set, they were initialized to 0 for all substitute commands.  If
 	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
 	 * specified substitute/replacement patterns (see ex_s()).
 	 */
 	if (!O_ISSET(sp, O_EDCOMPATIBLE))
 		sp->c_suffix = sp->g_suffix = 0;
 
 	/*
 	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
 	 * it only displayed the last change.  I'd disallow them, but they are
 	 * useful in combination with the [v]global commands.  In the current
 	 * model the problem is combining them with the 'c' flag -- the screen
 	 * would have to flip back and forth between the confirm screen and the
 	 * ex print screen, which would be pretty awful.  We do display all
 	 * changes, though, for what that's worth.
 	 *
 	 * !!!
 	 * Historic vi was fairly strict about the order of "options", the
 	 * count, and "flags".  I'm somewhat fuzzy on the difference between
 	 * options and flags, anyway, so this is a simpler approach, and we
 	 * just take it them in whatever order the user gives them.  (The ex
 	 * usage statement doesn't reflect this.)
 	 */
 	cflag = lflag = nflag = pflag = rflag = 0;
 	if (s == NULL)
 		goto noargs;
 	for (lno = OOBLNO; *s != '\0'; ++s)
 		switch (*s) {
 		case ' ':
 		case '\t':
 			continue;
 		case '+':
 			++cmdp->flagoff;
 			break;
 		case '-':
 			--cmdp->flagoff;
 			break;
 		case '0': case '1': case '2': case '3': case '4':
 		case '5': case '6': case '7': case '8': case '9':
 			if (lno != OOBLNO)
 				goto usage;
 			errno = 0;
 			nret = nget_uslong(&ul, s, &s, 10);
 			lno = ul;
 			if (*s == '\0')		/* Loop increment correction. */
 				--s;
 			if (nret != NUM_OK) {
 				if (nret == NUM_OVER)
 					msgq(sp, M_ERR, "153|Count overflow");
 				else if (nret == NUM_UNDER)
 					msgq(sp, M_ERR, "154|Count underflow");
 				else
 					msgq(sp, M_SYSERR, NULL);
 				return (1);
 			}
 			/*
 			 * In historic vi, the count was inclusive from the
 			 * second address.
 			 */
 			cmdp->addr1.lno = cmdp->addr2.lno;
 			cmdp->addr2.lno += lno - 1;
 			if (!db_exist(sp, cmdp->addr2.lno) &&
 			    db_last(sp, &cmdp->addr2.lno))
 				return (1);
 			break;
 		case '#':
 			nflag = 1;
 			break;
 		case 'c':
 			sp->c_suffix = !sp->c_suffix;
 
 			/* Ex text structure initialization. */
 			if (F_ISSET(sp, SC_EX))
 				TAILQ_INIT(tiq);
 			break;
 		case 'g':
 			sp->g_suffix = !sp->g_suffix;
 			break;
 		case 'l':
 			lflag = 1;
 			break;
 		case 'p':
 			pflag = 1;
 			break;
 		case 'r':
 			if (LF_ISSET(SUB_FIRST)) {
 				msgq(sp, M_ERR,
 		    "155|Regular expression specified; r flag meaningless");
 				return (1);
 			}
 			if (!F_ISSET(sp, SC_RE_SEARCH)) {
 				ex_emsg(sp, NULL, EXM_NOPREVRE);
 				return (1);
 			}
 			rflag = 1;
 			re = &sp->re_c;
 			break;
 		default:
 			goto usage;
 		}
 
 	if (*s != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) {
 usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
 		return (1);
 	}
 
 noargs:	if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
 		msgq(sp, M_ERR,
 "156|The #, l and p flags may not be combined with the c flag in vi mode");
 		return (1);
 	}
 
 	/*
 	 * bp:		if interactive, line cache
 	 * blen:	if interactive, line cache length
 	 * lb:		build buffer pointer.
 	 * lbclen:	current length of built buffer.
 	 * lblen;	length of build buffer.
 	 */
 	bp = lb = NULL;
 	blen = lbclen = lblen = 0;
 
 	/* For each line... */
 	lno = cmdp->addr1.lno == 0 ? 1 : cmdp->addr1.lno;
 	for (matched = quit = 0,
 	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {
 
 		/* Someone's unhappy, time to stop. */
 		if (INTERRUPTED(sp))
 			break;
 
 		/* Get the line. */
 		if (db_get(sp, lno, DBG_FATAL, &s, &llen))
 			goto err;
 
 		/*
 		 * Make a local copy if doing confirmation -- when calling
 		 * the confirm routine we're likely to lose the cached copy.
 		 */
 		if (sp->c_suffix) {
 			if (bp == NULL) {
 				GET_SPACE_RETW(sp, bp, blen, llen);
 			} else
 				ADD_SPACE_RETW(sp, bp, blen, llen);
 			MEMCPY(bp, s, llen);
 			s = bp;
 		}
 
 		/* Start searching from the beginning. */
 		offset = 0;
 		len = llen;
 
 		/* Reset the build buffer offset. */
 		lbclen = 0;
 
 		/* Reset empty match flag. */
 		empty_ok = 1;
 
 		/*
 		 * We don't want to have to do a setline if the line didn't
 		 * change -- keep track of whether or not this line changed.
 		 * If doing confirmations, don't want to keep setting the
 		 * line if change is refused -- keep track of substitutions.
 		 */
 		didsub = linechanged = 0;
 
 		/* New line, do an EOL match. */
 		do_eol_match = 1;
 
 		/* It's not nul terminated, but we pretend it is. */
 		eflags = REG_STARTEND;
 
 		/*
 		 * The search area is from s + offset to the EOL.
 		 *
 		 * Generally, match[0].rm_so is the offset of the start
 		 * of the match from the start of the search, and offset
 		 * is the offset of the start of the last search.
 		 */
 nextmatch:	match[0].rm_so = 0;
 		match[0].rm_eo = len;
 
 		/* Get the next match. */
 		eval = regexec(re, s + offset, 10, match, eflags);
 
 		/*
 		 * There wasn't a match or if there was an error, deal with
 		 * it.  If there was a previous match in this line, resolve
 		 * the changes into the database.  Otherwise, just move on.
 		 */
 		if (eval == REG_NOMATCH)
 			goto endmatch;
 		if (eval != 0) {
 			re_error(sp, eval, re);
 			goto err;
 		}
 		matched = 1;
 
 		/* Only the first search can match an anchored expression. */
 		eflags |= REG_NOTBOL;
 
 		/*
 		 * !!!
 		 * It's possible to match 0-length strings -- for example, the
 		 * command s;a*;X;, when matched against the string "aabb" will
 		 * result in "XbXbX", i.e. the matches are "aa", the space
 		 * between the b's and the space between the b's and the end of
 		 * the string.  There is a similar space between the beginning
 		 * of the string and the a's.  The rule that we use (because vi
 		 * historically used it) is that any 0-length match, occurring
 		 * immediately after a match, is ignored.  Otherwise, the above
 		 * example would have resulted in "XXbXbX".  Another example is
 		 * incorrectly using " *" to replace groups of spaces with one
 		 * space.
 		 *
 		 * The way we do this is that if we just had a successful match,
 		 * the starting offset does not skip characters, and the match
 		 * is empty, ignore the match and move forward.  If there's no
 		 * more characters in the string, we were attempting to match
 		 * after the last character, so quit.
 		 */
 		if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
 			empty_ok = 1;
 			if (len == 0)
 				goto endmatch;
-			BUILD(sp, s + offset, 1)
+			BUILD(sp, s + offset, 1);
 			++offset;
 			--len;
 			goto nextmatch;
 		}
 
 		/* Confirm change. */
 		if (sp->c_suffix) {
 			/*
 			 * Set the cursor position for confirmation.  Note,
 			 * if we matched on a '$', the cursor may be past
 			 * the end of line.
 			 */
 			from.lno = to.lno = lno;
 			from.cno = match[0].rm_so + offset;
 			to.cno = match[0].rm_eo + offset;
 			/*
 			 * Both ex and vi have to correct for a change before
 			 * the first character in the line.
 			 */
 			if (llen == 0)
 				from.cno = to.cno = 0;
 			if (F_ISSET(sp, SC_VI)) {
 				/*
 				 * Only vi has to correct for a change after
 				 * the last character in the line.
 				 *
 				 * XXX
 				 * It would be nice to change the vi code so
 				 * that we could display a cursor past EOL.
 				 */
 				if (to.cno >= llen)
 					to.cno = llen - 1;
 				if (from.cno >= llen)
 					from.cno = llen - 1;
 
 				sp->lno = from.lno;
 				sp->cno = from.cno;
 				if (vs_refresh(sp, 1))
 					goto err;
 
 				vs_update(sp, msg_cat(sp,
 				    "169|Confirm change? [n]", NULL), NULL);
 
 				if (v_event_get(sp, &ev, 0, 0))
 					goto err;
 				switch (ev.e_event) {
 				case E_CHARACTER:
 					break;
 				case E_EOF:
 				case E_ERR:
 				case E_INTERRUPT:
 					goto lquit;
 				default:
 					v_event_err(sp, &ev);
 					goto lquit;
 				}
 			} else {
 				if (ex_print(sp, cmdp, &from, &to, 0) ||
 				    ex_scprint(sp, &from, &to))
 					goto lquit;
 				if (ex_txt(sp, tiq, 0, TXT_CR))
 					goto err;
 				ev.e_c = TAILQ_FIRST(tiq)->lb[0];
 			}
 
 			switch (ev.e_c) {
 			case CH_YES:
 				break;
 			default:
 			case CH_NO:
 				didsub = 0;
 				BUILD(sp, s +offset, match[0].rm_eo);
 				goto skip;
 			case CH_QUIT:
 				/* Set the quit/interrupted flags. */
 lquit:				quit = 1;
 				F_SET(sp->gp, G_INTERRUPTED);
 
 				/*
 				 * Resolve any changes, then return to (and
 				 * exit from) the main loop.
 				 */
 				goto endmatch;
 			}
 		}
 
 		/*
 		 * Set the cursor to the last position changed, converting
 		 * from 1-based to 0-based.
 		 */
 		sp->lno = lno;
 		sp->cno = match[0].rm_so;
 
 		/* Copy the bytes before the match into the build buffer. */
 		BUILD(sp, s + offset, match[0].rm_so);
 
 		/* Substitute the matching bytes. */
 		didsub = 1;
 		if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match))
 			goto err;
 
 		/* Set the change flag so we know this line was modified. */
 		linechanged = 1;
 
 		/* Move past the matched bytes. */
 skip:		offset += match[0].rm_eo;
 		len -= match[0].rm_eo;
 
 		/* A match cannot be followed by an empty pattern. */
 		empty_ok = 0;
 
 		/*
 		 * If doing a global change with confirmation, we have to
 		 * update the screen.  The basic idea is to store the line
 		 * so the screen update routines can find it, and restart.
 		 */
 		if (didsub && sp->c_suffix && sp->g_suffix) {
 			/*
 			 * The new search offset will be the end of the
 			 * modified line.
 			 */
 			saved_offset = lbclen;
 
 			/* Copy the rest of the line. */
 			if (len)
-				BUILD(sp, s + offset, len)
+				BUILD(sp, s + offset, len);
 
 			/* Set the new offset. */
 			offset = saved_offset;
 
 			/* Store inserted lines, adjusting the build buffer. */
 			last = 0;
 			if (sp->newl_cnt) {
 				for (cnt = 0;
 				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
 					if (db_insert(sp, lno,
 					    lb + last, sp->newl[cnt] - last))
 						goto err;
 					last = sp->newl[cnt] + 1;
 					++sp->rptlines[L_ADDED];
 				}
 				lbclen -= last;
 				offset -= last;
 				sp->newl_cnt = 0;
 			}
 
 			/* Store and retrieve the line. */
 			if (db_set(sp, lno, lb + last, lbclen))
 				goto err;
 			if (db_get(sp, lno, DBG_FATAL, &s, &llen))
 				goto err;
-			ADD_SPACE_RETW(sp, bp, blen, llen)
+			ADD_SPACE_RETW(sp, bp, blen, llen);
 			MEMCPY(bp, s, llen);
 			s = bp;
 			len = llen - offset;
 
 			/* Restart the build. */
 			lbclen = 0;
 			BUILD(sp, s, offset);
 
 			/*
 			 * If we haven't already done the after-the-string
 			 * match, do one.  Set REG_NOTEOL so the '$' pattern
 			 * only matches once.
 			 */
 			if (!do_eol_match)
 				goto endmatch;
 			if (offset == len) {
 				do_eol_match = 0;
 				eflags |= REG_NOTEOL;
 			}
 			goto nextmatch;
 		}
 
 		/*
 		 * If it's a global:
 		 *
 		 * If at the end of the string, do a test for the after
 		 * the string match.  Set REG_NOTEOL so the '$' pattern
 		 * only matches once.
 		 */
 		if (sp->g_suffix && do_eol_match) {
 			if (len == 0) {
 				do_eol_match = 0;
 				eflags |= REG_NOTEOL;
 			}
 			goto nextmatch;
 		}
 
 endmatch:	if (!linechanged)
 			continue;
 
 		/* Copy any remaining bytes into the build buffer. */
 		if (len)
-			BUILD(sp, s + offset, len)
+			BUILD(sp, s + offset, len);
 
 		/* Store inserted lines, adjusting the build buffer. */
 		last = 0;
 		if (sp->newl_cnt) {
 			for (cnt = 0;
 			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
 				if (db_insert(sp,
 				    lno, lb + last, sp->newl[cnt] - last))
 					goto err;
 				last = sp->newl[cnt] + 1;
 				++sp->rptlines[L_ADDED];
 			}
 			lbclen -= last;
 			sp->newl_cnt = 0;
 		}
 
 		/* Store the changed line. */
 		if (db_set(sp, lno, lb + last, lbclen))
 			goto err;
 
 		/* Update changed line counter. */
 		if (sp->rptlchange != lno) {
 			sp->rptlchange = lno;
 			++sp->rptlines[L_CHANGED];
 		}
 
 		/*
 		 * !!!
 		 * Display as necessary.  Historic practice is to only
 		 * display the last line of a line split into multiple
 		 * lines.
 		 */
 		if (lflag || nflag || pflag) {
 			from.lno = to.lno = lno;
 			from.cno = to.cno = 0;
 			if (lflag)
 				(void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
 			if (nflag)
 				(void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
 			if (pflag)
 				(void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
 		}
 	}
 
 	/*
 	 * !!!
 	 * Historically, vi attempted to leave the cursor at the same place if
 	 * the substitution was done at the current cursor position.  Otherwise
 	 * it moved it to the first non-blank of the last line changed.  There
 	 * were some problems: for example, :s/$/foo/ with the cursor on the
 	 * last character of the line left the cursor on the last character, or
 	 * the & command with multiple occurrences of the matching string in the
 	 * line usually left the cursor in a fairly random position.
 	 *
 	 * We try to do the same thing, with the exception that if the user is
 	 * doing substitution with confirmation, we move to the last line about
 	 * which the user was consulted, as opposed to the last line that they
 	 * actually changed.  This prevents a screen flash if the user doesn't
 	 * change many of the possible lines.
 	 */
 	if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
 		sp->cno = 0;
 		(void)nonblank(sp, sp->lno, &sp->cno);
 	}
 
 	/*
 	 * If not in a global command, and nothing matched, say so.
 	 * Else, if none of the lines displayed, put something up.
 	 */
 	rval = 0;
 	if (!matched) {
 		if (!F_ISSET(sp, SC_EX_GLOBAL)) {
 			msgq(sp, M_ERR, "157|No match found");
 			goto err;
 		}
 	} else if (!lflag && !nflag && !pflag)
 		F_SET(cmdp, E_AUTOPRINT);
 
 	if (0) {
 err:		rval = 1;
 	}
 
 	if (bp != NULL)
 		FREE_SPACEW(sp, bp, blen);
 	free(lb);
 	return (rval);
 }
 
 /*
  * re_compile --
  *	Compile the RE.
  *
  * PUBLIC: int re_compile(SCR *,
  * PUBLIC:     CHAR_T *, size_t, CHAR_T **, size_t *, regex_t *, u_int);
  */
 int
 re_compile(SCR *sp, CHAR_T *ptrn, size_t plen, CHAR_T **ptrnp, size_t *lenp, regex_t *rep, u_int flags)
 {
 	size_t len;
 	int reflags, replaced, rval;
 	CHAR_T *p;
 
 	/* Set RE flags. */
 	reflags = 0;
 	if (!LF_ISSET(RE_C_CSCOPE | RE_C_TAG)) {
 		if (O_ISSET(sp, O_EXTENDED))
 			reflags |= REG_EXTENDED;
 		if (O_ISSET(sp, O_IGNORECASE))
 			reflags |= REG_ICASE;
 		if (O_ISSET(sp, O_ICLOWER)) {
 			for (p = ptrn, len = plen; len > 0; ++p, --len)
 				if (ISUPPER(*p))
 					break;
 			if (len == 0)
 				reflags |= REG_ICASE;
 		}
 	}
 
 	/* If we're replacing a saved value, clear the old one. */
 	if (LF_ISSET(RE_C_SEARCH) && F_ISSET(sp, SC_RE_SEARCH)) {
 		regfree(&sp->re_c);
 		F_CLR(sp, SC_RE_SEARCH);
 	}
 	if (LF_ISSET(RE_C_SUBST) && F_ISSET(sp, SC_RE_SUBST)) {
 		regfree(&sp->subre_c);
 		F_CLR(sp, SC_RE_SUBST);
 	}
 
 	/*
 	 * If we're saving the string, it's a pattern we haven't seen before,
 	 * so convert the vi-style RE's to POSIX 1003.2 RE's.  Save a copy for
 	 * later recompilation.   Free any previously saved value.
 	 */
 	if (ptrnp != NULL) {
 		replaced = 0;
 		if (LF_ISSET(RE_C_CSCOPE)) {
 			if (re_cscope_conv(sp, &ptrn, &plen, &replaced))
 				return (1);
 			/*
 			 * XXX
 			 * Currently, the match-any-<blank> expression used in
 			 * re_cscope_conv() requires extended RE's.  This may
 			 * not be right or safe.
 			 */
 			reflags |= REG_EXTENDED;
 		} else if (LF_ISSET(RE_C_TAG)) {
 			if (re_tag_conv(sp, &ptrn, &plen, &replaced))
 				return (1);
 		} else
 			if (re_conv(sp, &ptrn, &plen, &replaced))
 				return (1);
 
 		/* Discard previous pattern. */
 		free(*ptrnp);
 		*ptrnp = NULL;
 
 		if (lenp != NULL)
 			*lenp = plen;
 
 		/*
 		 * Copy the string into allocated memory.
 		 *
 		 * XXX
 		 * Regcomp isn't 8-bit clean, so the pattern is nul-terminated
 		 * for now.  There's just no other solution.  
 		 */
 		MALLOC(sp, *ptrnp, (plen + 1) * sizeof(CHAR_T));
 		if (*ptrnp != NULL) {
 			MEMCPY(*ptrnp, ptrn, plen);
 			(*ptrnp)[plen] = '\0';
 		}
 
 		/* Free up conversion-routine-allocated memory. */
 		if (replaced)
 			FREE_SPACEW(sp, ptrn, 0);
 
 		if (*ptrnp == NULL)
 			return (1);
 
 		ptrn = *ptrnp;
 	}
 
 	/*
 	 * XXX
 	 * Regcomp isn't 8-bit clean, so we just lost if the pattern
 	 * contained a nul.  Bummer!
 	 */
 	if ((rval = regcomp(rep, ptrn, /* plen, */ reflags)) != 0) {
 		if (!LF_ISSET(RE_C_SILENT))
 			re_error(sp, rval, rep); 
 		return (1);
 	}
 
 	if (LF_ISSET(RE_C_SEARCH))
 		F_SET(sp, SC_RE_SEARCH);
 	if (LF_ISSET(RE_C_SUBST))
 		F_SET(sp, SC_RE_SUBST);
 
 	return (0);
 }
 
 /*
  * re_conv --
  *	Convert vi's regular expressions into something that the
  *	the POSIX 1003.2 RE functions can handle.
  *
  * There are three conversions we make to make vi's RE's (specifically
  * the global, search, and substitute patterns) work with POSIX RE's.
  *
  * 1: If O_MAGIC is not set, strip backslashes from the magic character
  *    set (.[*~) that have them, and add them to the ones that don't.
  * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
  *    from the last substitute command's replacement string.  If O_MAGIC
  *    is set, it's the string "~".
  * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
  *    new RE escapes.
  *
  * !!!/XXX
  * This doesn't exactly match the historic behavior of vi because we do
  * the ~ substitution before calling the RE engine, so magic characters
  * in the replacement string will be expanded by the RE engine, and they
  * weren't historically.  It's a bug.
  */
 static int
 re_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
 {
 	size_t blen, len, needlen;
 	int magic;
 	CHAR_T *bp, *p, *t;
 
 	/*
 	 * First pass through, we figure out how much space we'll need.
 	 * We do it in two passes, on the grounds that most of the time
 	 * the user is doing a search and won't have magic characters.
 	 * That way we can skip most of the memory allocation and copies.
 	 */
 	magic = 0;
 	for (p = *ptrnp, len = *plenp, needlen = 0; len > 0; ++p, --len)
 		switch (*p) {
 		case '\\':
 			if (len > 1) {
 				--len;
 				switch (*++p) {
 				case '<':
 					magic = 1;
 					needlen += RE_WSTART_LEN + 1;
 					break;
 				case '>':
 					magic = 1;
 					needlen += RE_WSTOP_LEN + 1;
 					break;
 				case '~':
 					if (!O_ISSET(sp, O_MAGIC)) {
 						magic = 1;
 						needlen += sp->repl_len;
 					}
 					break;
 				case '.':
 				case '[':
 				case '*':
 					if (!O_ISSET(sp, O_MAGIC)) {
 						magic = 1;
 						needlen += 1;
 					}
 					break;
 				default:
 					needlen += 2;
 				}
 			} else
 				needlen += 1;
 			break;
 		case '~':
 			if (O_ISSET(sp, O_MAGIC)) {
 				magic = 1;
 				needlen += sp->repl_len;
 			}
 			break;
 		case '.':
 		case '[':
 		case '*':
 			if (!O_ISSET(sp, O_MAGIC)) {
 				magic = 1;
 				needlen += 2;
 			}
 			break;
 		default:
 			needlen += 1;
 			break;
 		}
 
 	if (!magic) {
 		*replacedp = 0;
 		return (0);
 	}
 
 	/* Get enough memory to hold the final pattern. */
 	*replacedp = 1;
 	GET_SPACE_RETW(sp, bp, blen, needlen);
 
 	for (p = *ptrnp, len = *plenp, t = bp; len > 0; ++p, --len)
 		switch (*p) {
 		case '\\':
 			if (len > 1) {
 				--len;
 				switch (*++p) {
 				case '<':
 					MEMCPY(t,
 					    RE_WSTART, RE_WSTART_LEN);
 					t += RE_WSTART_LEN;
 					break;
 				case '>':
 					MEMCPY(t,
 					    RE_WSTOP, RE_WSTOP_LEN);
 					t += RE_WSTOP_LEN;
 					break;
 				case '~':
 					if (O_ISSET(sp, O_MAGIC))
 						*t++ = '~';
 					else {
 						MEMCPY(t,
 						    sp->repl, sp->repl_len);
 						t += sp->repl_len;
 					}
 					break;
 				case '.':
 				case '[':
 				case '*':
 					if (O_ISSET(sp, O_MAGIC))
 						*t++ = '\\';
 					*t++ = *p;
 					break;
 				default:
 					*t++ = '\\';
 					*t++ = *p;
 				}
 			} else
 				*t++ = '\\';
 			break;
 		case '~':
 			if (O_ISSET(sp, O_MAGIC)) {
 				MEMCPY(t, sp->repl, sp->repl_len);
 				t += sp->repl_len;
 			} else
 				*t++ = '~';
 			break;
 		case '.':
 		case '[':
 		case '*':
 			if (!O_ISSET(sp, O_MAGIC))
 				*t++ = '\\';
 			*t++ = *p;
 			break;
 		default:
 			*t++ = *p;
 			break;
 		}
 
 	*ptrnp = bp;
 	*plenp = t - bp;
 	return (0);
 }
 
 /*
  * re_tag_conv --
  *	Convert a tags search path into something that the POSIX
  *	1003.2 RE functions can handle.
  */
 static int
 re_tag_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
 {
 	size_t blen, len;
 	int lastdollar;
 	CHAR_T *bp, *p, *t;
 
 	len = *plenp;
 
 	/* Max memory usage is 2 times the length of the string. */
 	*replacedp = 1;
 	GET_SPACE_RETW(sp, bp, blen, len * 2);
 
 	p = *ptrnp;
 	t = bp;
 
 	/* If the last character is a '/' or '?', we just strip it. */
 	if (len > 0 && (p[len - 1] == '/' || p[len - 1] == '?'))
 		--len;
 
 	/* If the next-to-last or last character is a '$', it's magic. */
 	if (len > 0 && p[len - 1] == '$') {
 		--len;
 		lastdollar = 1;
 	} else
 		lastdollar = 0;
 
 	/* If the first character is a '/' or '?', we just strip it. */
 	if (len > 0 && (p[0] == '/' || p[0] == '?')) {
 		++p;
 		--len;
 	}
 
 	/* If the first or second character is a '^', it's magic. */
 	if (p[0] == '^') {
 		*t++ = *p++;
 		--len;
 	}
 
 	/*
 	 * Escape every other magic character we can find, meanwhile stripping
 	 * the backslashes ctags inserts when escaping the search delimiter
 	 * characters.
 	 */
 	for (; len > 0; --len) {
 		if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) {
 			++p;
 			--len;
 		} else if (STRCHR(L("^.[]$*"), p[0]))
 			*t++ = '\\';
 		*t++ = *p++;
 	}
 	if (lastdollar)
 		*t++ = '$';
 
 	*ptrnp = bp;
 	*plenp = t - bp;
 	return (0);
 }
 
 /*
  * re_cscope_conv --
  *	 Convert a cscope search path into something that the POSIX
  *      1003.2 RE functions can handle.
  */
 static int
 re_cscope_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
 {
 	size_t blen, len, nspaces;
 	CHAR_T *bp, *t;
 	CHAR_T *p;
 	CHAR_T *wp;
 	size_t wlen;
 
 	/*
 	 * Each space in the source line printed by cscope represents an
 	 * arbitrary sequence of spaces, tabs, and comments.
 	 */
 #define	CSCOPE_RE_SPACE		"([ \t]|/\\*([^*]|\\*/)*\\*/)*"
 #define CSCOPE_LEN	sizeof(CSCOPE_RE_SPACE) - 1
 	CHAR2INT(sp, CSCOPE_RE_SPACE, CSCOPE_LEN, wp, wlen);
 	for (nspaces = 0, p = *ptrnp, len = *plenp; len > 0; ++p, --len)
 		if (*p == ' ')
 			++nspaces;
 
 	/*
 	 * Allocate plenty of space:
 	 *	the string, plus potential escaping characters;
 	 *	nspaces + 2 copies of CSCOPE_RE_SPACE;
 	 *	^, $, nul terminator characters.
 	 */
 	*replacedp = 1;
 	len = (p - *ptrnp) * 2 + (nspaces + 2) * sizeof(CSCOPE_RE_SPACE) + 3;
 	GET_SPACE_RETW(sp, bp, blen, len);
 
 	p = *ptrnp;
 	t = bp;
 
 	*t++ = '^';
 	MEMCPY(t, wp, wlen);
 	t += wlen;
 
 	for (len = *plenp; len > 0; ++p, --len)
 		if (*p == ' ') {
 			MEMCPY(t, wp, wlen);
 			t += wlen;
 		} else {
 			if (STRCHR(L("\\^.[]$*+?()|{}"), *p))
 				*t++ = '\\';
 			*t++ = *p;
 		}
 
 	MEMCPY(t, wp, wlen);
 	t += wlen;
 	*t++ = '$';
 
 	*ptrnp = bp;
 	*plenp = t - bp;
 	return (0);
 }
 
 /*
  * re_error --
  *	Report a regular expression error.
  *
  * PUBLIC: void re_error(SCR *, int, regex_t *);
  */
 void
 re_error(SCR *sp, int errcode, regex_t *preg)
 {
 	size_t s;
 	char *oe;
 
 	s = regerror(errcode, preg, "", 0);
 	MALLOC(sp, oe, s);
 	if (oe != NULL) {
 		(void)regerror(errcode, preg, oe, s);
 		msgq(sp, M_ERR, "RE error: %s", oe);
 		free(oe);
 	}
 }
 
 /*
  * re_sub --
  * 	Do the substitution for a regular expression.
  */
 static int
 re_sub(
 	SCR *sp,
 	CHAR_T *ip,			/* Input line. */
 	CHAR_T **lbp,
 	size_t *lbclenp,
 	size_t *lblenp,
 	regmatch_t match[10])
 {
 	enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;
 	size_t lbclen, lblen;		/* Local copies. */
 	size_t mlen;			/* Match length. */
 	size_t rpl;			/* Remaining replacement length. */
 	CHAR_T *rp;			/* Replacement pointer. */
 	int ch;
 	int no;				/* Match replacement offset. */
 	CHAR_T *p, *t;			/* Buffer pointers. */
 	CHAR_T *lb;			/* Local copies. */
 
 	lb = *lbp;			/* Get local copies. */
 	lbclen = *lbclenp;
 	lblen = *lblenp;
 
 	/*
 	 * QUOTING NOTE:
 	 *
 	 * There are some special sequences that vi provides in the
 	 * replacement patterns.
 	 *	 & string the RE matched (\& if nomagic set)
 	 *	\# n-th regular subexpression
 	 *	\E end \U, \L conversion
 	 *	\e end \U, \L conversion
 	 *	\l convert the next character to lower-case
 	 *	\L convert to lower-case, until \E, \e, or end of replacement
 	 *	\u convert the next character to upper-case
 	 *	\U convert to upper-case, until \E, \e, or end of replacement
 	 *
 	 * Otherwise, since this is the lowest level of replacement, discard
 	 * all escaping characters.  This (hopefully) matches historic practice.
 	 */
-#define	OUTCH(ch, nltrans) {						\
+#define	OUTCH(ch, nltrans) do {						\
 	ARG_CHAR_T __ch = (ch);						\
 	e_key_t __value = KEY_VAL(sp, __ch);				\
 	if (nltrans && (__value == K_CR || __value == K_NL)) {		\
 		NEEDNEWLINE(sp);					\
 		sp->newl[sp->newl_cnt++] = lbclen;			\
 	} else if (conv != C_NOTSET) {					\
 		switch (conv) {						\
 		case C_ONELOWER:					\
 			conv = C_NOTSET;				\
 			/* FALLTHROUGH */				\
 		case C_LOWER:						\
 			if (ISUPPER(__ch))				\
 				__ch = TOLOWER(__ch);			\
 			break;						\
 		case C_ONEUPPER:					\
 			conv = C_NOTSET;				\
 			/* FALLTHROUGH */				\
 		case C_UPPER:						\
 			if (ISLOWER(__ch))				\
 				__ch = TOUPPER(__ch);			\
 			break;						\
 		default:						\
 			abort();					\
 		}							\
 	}								\
 	NEEDSP(sp, 1, p);						\
 	*p++ = __ch;							\
 	++lbclen;							\
-}
+} while (0)
 	conv = C_NOTSET;
 	for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {
 		switch (ch = *rp++) {
 		case '&':
 			if (O_ISSET(sp, O_MAGIC)) {
 				no = 0;
 				goto subzero;
 			}
 			break;
 		case '\\':
 			if (rpl == 0)
 				break;
 			--rpl;
 			switch (ch = *rp) {
 			case '&':
 				++rp;
 				if (!O_ISSET(sp, O_MAGIC)) {
 					no = 0;
 					goto subzero;
 				}
 				break;
 			case '0': case '1': case '2': case '3': case '4':
 			case '5': case '6': case '7': case '8': case '9':
 				no = *rp++ - '0';
 subzero:			if (match[no].rm_so == -1 ||
 				    match[no].rm_eo == -1)
 					break;
 				mlen = match[no].rm_eo - match[no].rm_so;
 				for (t = ip + match[no].rm_so; mlen--; ++t)
 					OUTCH(*t, 0);
 				continue;
 			case 'e':
 			case 'E':
 				++rp;
 				conv = C_NOTSET;
 				continue;
 			case 'l':
 				++rp;
 				conv = C_ONELOWER;
 				continue;
 			case 'L':
 				++rp;
 				conv = C_LOWER;
 				continue;
 			case 'u':
 				++rp;
 				conv = C_ONEUPPER;
 				continue;
 			case 'U':
 				++rp;
 				conv = C_UPPER;
 				continue;
 			case '\r':
 				OUTCH(ch, 0);
 				continue;
 			default:
 				++rp;
 				break;
 			}
 		}
 		OUTCH(ch, 1);
 	}
 
 	*lbp = lb;			/* Update caller's information. */
 	*lbclenp = lbclen;
 	*lblenp = lblen;
 	return (0);
 }
Index: head/contrib/nvi/files/config.h.in
===================================================================
--- head/contrib/nvi/files/config.h.in	(revision 366308)
+++ head/contrib/nvi/files/config.h.in	(revision 366309)
@@ -1,17 +1,26 @@
 /* Define when using wide characters */
 #cmakedefine USE_WIDECHAR
 
 /* Define when iconv can be used */
 #cmakedefine USE_ICONV
 
 /* Define when the 2nd argument of iconv(3) is not const */
 #cmakedefine ICONV_TRADITIONAL
 
 /* Define if you have <libutil.h> */
 #cmakedefine HAVE_LIBUTIL_H
 
 /* Define if you have <ncurses.h> */
 #cmakedefine HAVE_NCURSES_H
 
+/* Define if you have <ncursesw/ncurses.h> */
+#cmakedefine HAVE_NCURSESW_NCURSES_H
+
+/* Define if you have <pty.h> */
+#cmakedefine HAVE_PTY_H
+
 /* Define if you have <term.h> */
 #cmakedefine HAVE_TERM_H
+
+/* Define if struct dirent has field d_namlen */
+#cmakedefine HAVE_DIRENT_D_NAMLEN
Index: head/contrib/nvi/files/pathnames.h.in
===================================================================
--- head/contrib/nvi/files/pathnames.h.in	(revision 366308)
+++ head/contrib/nvi/files/pathnames.h.in	(revision 366309)
@@ -1,26 +1,25 @@
 /* Read standard system paths first. */
 #include <paths.h>
 
 #ifndef	_PATH_EXRC
 #define	_PATH_EXRC	".exrc"
 #endif
 
 #ifndef	_PATH_MSGCAT
 #define	_PATH_MSGCAT	"@vi_cv_path_msgcat@"
 #endif
 
 #ifndef	_PATH_NEXRC
 #define	_PATH_NEXRC	".nexrc"
 #endif
 
-#ifndef	_PATH_PRESERVE
-#define	_PATH_PRESERVE	"@vi_cv_path_preserve@"
-#endif
+/* On linux _PATH_PRESERVE is only writable by root */
+#define	NVI_PATH_PRESERVE	"@vi_cv_path_preserve@"
 
 #ifndef	_PATH_SYSEXRC
 #define	_PATH_SYSEXRC	"/etc/vi.exrc"
 #endif
 
 #ifndef	_PATH_TAGS
 #define	_PATH_TAGS	"tags"
 #endif
Index: head/contrib/nvi/regex/engine.c
===================================================================
--- head/contrib/nvi/regex/engine.c	(revision 366308)
+++ head/contrib/nvi/regex/engine.c	(revision 366309)
@@ -1,1036 +1,1036 @@
 /*	$NetBSD: engine.c,v 1.7 2011/11/19 17:45:11 tnozaki Exp $ */
 
 /*-
  * Copyright (c) 1992, 1993, 1994 Henry Spencer.
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Henry Spencer of the University of Toronto.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)engine.c	8.4 (Berkeley) 3/19/94
  */
 
 /*
  * The matching engine and friends.  This file is #included by regexec.c
  * after suitable #defines of a variety of macros used herein, so that
  * different state representations can be used without duplicating masses
  * of code.
  */
 
 #ifdef SNAMES
 #define	matcher	smatcher
 #define	fast	sfast
 #define	slow	sslow
 #define	dissect	sdissect
 #define	backref	sbackref
 #define	step	sstep
 #define	print	sprint
 #define	at	sat
 #define	match	smat
 #endif
 #ifdef LNAMES
 #define	matcher	lmatcher
 #define	fast	lfast
 #define	slow	lslow
 #define	dissect	ldissect
 #define	backref	lbackref
 #define	step	lstep
 #define	print	lprint
 #define	at	lat
 #define	match	lmat
 #endif
 
 /* another structure passed up and down to avoid zillions of parameters */
 struct match {
 	struct re_guts *g;
 	int eflags;
 	regmatch_t *pmatch;	/* [nsub+1] (0 element unused) */
 	const RCHAR_T *offp;		/* offsets work from here */
 	const RCHAR_T *beginp;		/* start of string -- virtual NUL precedes */
 	const RCHAR_T *endp;		/* end of string -- virtual NUL here */
 	const RCHAR_T *coldp;		/* can be no match starting before here */
 	const RCHAR_T **lastpos;	/* [nplus+1] */
 	STATEVARS;
 	states st;		/* current states */
 	states fresh;		/* states for a fresh start */
 	states tmp;		/* temporary */
 	states empty;		/* empty set of states */
 };
 
 /* ========= begin header generated by ./mkh ========= */
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* === engine.c === */
 static int matcher(struct re_guts *g, const RCHAR_T *string, size_t nmatch, regmatch_t pmatch[], int eflags);
 static const RCHAR_T *dissect(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst, sopno stopst);
 static const RCHAR_T *backref(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst, sopno stopst, sopno lev);
 static const RCHAR_T *fast(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst, sopno stopst);
 static const RCHAR_T *slow(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst, sopno stopst);
 static states step(struct re_guts *g, sopno start, sopno stop, states bef, int flag, RCHAR_T ch, states aft);
 #define	BOL	(1)
 #define	EOL	(BOL+1)
 #define	BOLEOL	(BOL+2)
 #define	NOTHING	(BOL+3)
 #define	BOW	(BOL+4)
 #define	EOW	(BOL+5)
 #ifdef REDEBUG
 static void print(struct match *m, char *caption, states st, int ch, FILE *d);
 #endif
 #ifdef REDEBUG
 static void at(struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst);
 #endif
 #ifdef REDEBUG
 static char *pchar(int ch);
 #endif
 
 #ifdef __cplusplus
 }
 #endif
 /* ========= end header generated by ./mkh ========= */
 
 #ifdef REDEBUG
 #define	SP(t, s, c)	print(m, t, s, c, stdout)
 #define	AT(t, p1, p2, s1, s2)	at(m, t, p1, p2, s1, s2)
-#define	NOTE(str)	{ if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
+#define	NOTE(str)	do { if (m->eflags&REG_TRACE) printf("=%s\n", (str)); } while(0);
 #else
 #define	SP(t, s, c)	/* nothing */
 #define	AT(t, p1, p2, s1, s2)	/* nothing */
 #define	NOTE(s)	/* nothing */
 #endif
 
 /*
  - matcher - the actual matching engine
  */
 static int			/* 0 success, REG_NOMATCH failure */
 matcher(struct re_guts *g, const RCHAR_T *string, size_t nmatch,
     regmatch_t pmatch[], int eflags)
 {
 	const RCHAR_T *endp;
 	size_t i;
 	struct match mv;
 	struct match *m = &mv;
 	const RCHAR_T *dp;
 	const sopno gf = g->firststate+1;	/* +1 for OEND */
 	const sopno gl = g->laststate;
 	const RCHAR_T *start;
 	const RCHAR_T *stop;
 
 	/* simplify the situation where possible */
 	if (g->cflags&REG_NOSUB)
 		nmatch = 0;
 	if (eflags&REG_STARTEND) {
 		start = string + pmatch[0].rm_so;
 		stop = string + pmatch[0].rm_eo;
 	} else {
 		start = string;
 		stop = start + STRLEN(start);
 	}
 	if (stop < start)
 		return(REG_INVARG);
 
 	/* prescreening; this does wonders for this rather slow code */
 	if (g->must != NULL) {
 		for (dp = start; dp < stop; dp++)
 			if (*dp == g->must[0] && (size_t)(stop - dp) >= g->mlen &&
 				MEMCMP(dp, g->must, g->mlen) == 0)
 				break;
 		if (dp == stop)		/* we didn't find g->must */
 			return(REG_NOMATCH);
 	}
 
 	/* match struct setup */
 	m->g = g;
 	m->eflags = eflags;
 	m->pmatch = NULL;
 	m->lastpos = NULL;
 	m->offp = string;
 	m->beginp = start;
 	m->endp = stop;
 	STATESETUP(m, 4);
 	SETUP(m->st);
 	SETUP(m->fresh);
 	SETUP(m->tmp);
 	SETUP(m->empty);
 	CLEAR(m->empty);
 
 	/* this loop does only one repetition except for backrefs */
 	for (;;) {
 		endp = fast(m, start, stop, gf, gl);
 		if (endp == NULL) {		/* a miss */
 			STATETEARDOWN(m);
 			return(REG_NOMATCH);
 		}
 		if (nmatch == 0 && !g->backrefs)
 			break;		/* no further info needed */
 
 		/* where? */
 		assert(m->coldp != NULL);
 		for (;;) {
 			NOTE("finding start");
 			endp = slow(m, m->coldp, stop, gf, gl);
 			if (endp != NULL)
 				break;
 			assert(m->coldp < m->endp);
 			m->coldp++;
 		}
 		if (nmatch == 1 && !g->backrefs)
 			break;		/* no further info needed */
 
 		/* oh my, he wants the subexpressions... */
 		if (m->pmatch == NULL)
 			m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
 							sizeof(regmatch_t));
 		if (m->pmatch == NULL) {
 			STATETEARDOWN(m);
 			return(REG_ESPACE);
 		}
 		for (i = 1; i <= m->g->nsub; i++)
 			m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
 		if (!g->backrefs && !(m->eflags&REG_BACKR)) {
 			NOTE("dissecting");
 			dp = dissect(m, m->coldp, endp, gf, gl);
 		} else {
 			if (g->nplus > 0 && m->lastpos == NULL)
 				m->lastpos = (const RCHAR_T **)malloc((g->nplus+1) *
 							sizeof(const RCHAR_T *));
 			if (g->nplus > 0 && m->lastpos == NULL) {
 				free(m->pmatch);
 				STATETEARDOWN(m);
 				return(REG_ESPACE);
 			}
 			NOTE("backref dissect");
 			dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
 		}
 		if (dp != NULL)
 			break;
 
 		/* uh-oh... we couldn't find a subexpression-level match */
 		assert(g->backrefs);	/* must be back references doing it */
 		assert(g->nplus == 0 || m->lastpos != NULL);
 		for (;;) {
 			if (dp != NULL || endp <= m->coldp)
 				break;		/* defeat */
 			NOTE("backoff");
 			endp = slow(m, m->coldp, endp-1, gf, gl);
 			if (endp == NULL)
 				break;		/* defeat */
 			/* try it on a shorter possibility */
 #ifndef NDEBUG
 			for (i = 1; i <= m->g->nsub; i++) {
 				assert(m->pmatch[i].rm_so == -1);
 				assert(m->pmatch[i].rm_eo == -1);
 			}
 #endif
 			NOTE("backoff dissect");
 			dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
 		}
 		assert(dp == NULL || dp == endp);
 		if (dp != NULL)		/* found a shorter one */
 			break;
 
 		/* despite initial appearances, there is no match here */
 		NOTE("false alarm");
 		start = m->coldp + 1;	/* recycle starting later */
 		assert(start <= stop);
 	}
 
 	/* fill in the details if requested */
 	if (nmatch > 0) {
 		pmatch[0].rm_so = m->coldp - m->offp;
 		pmatch[0].rm_eo = endp - m->offp;
 	}
 	if (nmatch > 1) {
 		assert(m->pmatch != NULL);
 		for (i = 1; i < nmatch; i++)
 			if (i <= m->g->nsub)
 				pmatch[i] = m->pmatch[i];
 			else {
 				pmatch[i].rm_so = -1;
 				pmatch[i].rm_eo = -1;
 			}
 	}
 
 	if (m->pmatch != NULL)
 		free((char *)m->pmatch);
 	if (m->lastpos != NULL)
 		free((char *)m->lastpos);
 	STATETEARDOWN(m);
 	return(0);
 }
 
 /*
  - dissect - figure out what matched what, no back references
  */
 static const RCHAR_T *			/* == stop (success) always */
 dissect(struct match *m, const RCHAR_T *start, const RCHAR_T *stop,
     sopno startst, sopno stopst)
 {
 	int i;
 	sopno ss;	/* start sop of current subRE */
 	sopno es;	/* end sop of current subRE */
 	const RCHAR_T *sp;	/* start of string matched by it */
 	const RCHAR_T *stp;	/* string matched by it cannot pass here */
 	const RCHAR_T *rest;	/* start of rest of string */
 	const RCHAR_T *tail;	/* string unmatched by rest of RE */
 	sopno ssub;	/* start sop of subsubRE */
 	sopno esub;	/* end sop of subsubRE */
 	const RCHAR_T *ssp;	/* start of string matched by subsubRE */
 	const RCHAR_T *sep;	/* end of string matched by subsubRE */
 	const RCHAR_T *oldssp;	/* previous ssp */
 	const RCHAR_T *dp;
 
 	AT("diss", start, stop, startst, stopst);
 	sp = start;
 	for (ss = startst; ss < stopst; ss = es) {
 		/* identify end of subRE */
 		es = ss;
 		switch (m->g->strip[es]) {
 		case OPLUS_:
 		case OQUEST_:
 			es += m->g->stripdata[es];
 			break;
 		case OCH_:
 			while (m->g->strip[es] != O_CH)
 				es += m->g->stripdata[es];
 			break;
 		}
 		es++;
 
 		/* figure out what it matched */
 		switch (m->g->strip[ss]) {
 		case OEND:
 			assert(nope);
 			break;
 		case OCHAR:
 			sp++;
 			break;
 		case OBOL:
 		case OEOL:
 		case OBOW:
 		case OEOW:
 			break;
 		case OANY:
 		case OANYOF:
 			sp++;
 			break;
 		case OBACK_:
 		case O_BACK:
 			assert(nope);
 			break;
 		/* cases where length of match is hard to find */
 		case OQUEST_:
 			stp = stop;
 			for (;;) {
 				/* how long could this one be? */
 				rest = slow(m, sp, stp, ss, es);
 				assert(rest != NULL);	/* it did match */
 				/* could the rest match the rest? */
 				tail = slow(m, rest, stop, es, stopst);
 				if (tail == stop)
 					break;		/* yes! */
 				/* no -- try a shorter match for this one */
 				stp = rest - 1;
 				assert(stp >= sp);	/* it did work */
 			}
 			ssub = ss + 1;
 			esub = es - 1;
 			/* did innards match? */
 			if (slow(m, sp, rest, ssub, esub) != NULL) {
 				dp = dissect(m, sp, rest, ssub, esub);
 				assert(dp == rest);
 			} else		/* no */
 				assert(sp == rest);
 			sp = rest;
 			break;
 		case OPLUS_:
 			stp = stop;
 			for (;;) {
 				/* how long could this one be? */
 				rest = slow(m, sp, stp, ss, es);
 				assert(rest != NULL);	/* it did match */
 				/* could the rest match the rest? */
 				tail = slow(m, rest, stop, es, stopst);
 				if (tail == stop)
 					break;		/* yes! */
 				/* no -- try a shorter match for this one */
 				stp = rest - 1;
 				assert(stp >= sp);	/* it did work */
 			}
 			ssub = ss + 1;
 			esub = es - 1;
 			ssp = sp;
 			oldssp = ssp;
 			for (;;) {	/* find last match of innards */
 				sep = slow(m, ssp, rest, ssub, esub);
 				if (sep == NULL || sep == ssp)
 					break;	/* failed or matched null */
 				oldssp = ssp;	/* on to next try */
 				ssp = sep;
 			}
 			if (sep == NULL) {
 				/* last successful match */
 				sep = ssp;
 				ssp = oldssp;
 			}
 			assert(sep == rest);	/* must exhaust substring */
 			assert(slow(m, ssp, sep, ssub, esub) == rest);
 			dp = dissect(m, ssp, sep, ssub, esub);
 			assert(dp == sep);
 			sp = rest;
 			break;
 		case OCH_:
 			stp = stop;
 			for (;;) {
 				/* how long could this one be? */
 				rest = slow(m, sp, stp, ss, es);
 				assert(rest != NULL);	/* it did match */
 				/* could the rest match the rest? */
 				tail = slow(m, rest, stop, es, stopst);
 				if (tail == stop)
 					break;		/* yes! */
 				/* no -- try a shorter match for this one */
 				stp = rest - 1;
 				assert(stp >= sp);	/* it did work */
 			}
 			ssub = ss + 1;
 			esub = ss + m->g->stripdata[ss] - 1;
 			assert(m->g->strip[esub] == OOR1);
 			for (;;) {	/* find first matching branch */
 				if (slow(m, sp, rest, ssub, esub) == rest)
 					break;	/* it matched all of it */
 				/* that one missed, try next one */
 				assert(m->g->strip[esub] == OOR1);
 				esub++;
 				assert(m->g->strip[esub] == OOR2);
 				ssub = esub + 1;
 				esub += m->g->stripdata[esub];
 				if (m->g->strip[esub] == OOR2)
 					esub--;
 				else
 					assert(m->g->strip[esub] == O_CH);
 			}
 			dp = dissect(m, sp, rest, ssub, esub);
 			assert(dp == rest);
 			sp = rest;
 			break;
 		case O_PLUS:
 		case O_QUEST:
 		case OOR1:
 		case OOR2:
 		case O_CH:
 			assert(nope);
 			break;
 		case OLPAREN:
 			i = m->g->stripdata[ss];
 			assert(0 < i && i <= m->g->nsub);
 			m->pmatch[i].rm_so = sp - m->offp;
 			break;
 		case ORPAREN:
 			i = m->g->stripdata[ss];
 			assert(0 < i && i <= m->g->nsub);
 			m->pmatch[i].rm_eo = sp - m->offp;
 			break;
 		default:		/* uh oh */
 			assert(nope);
 			break;
 		}
 	}
 
 	assert(sp == stop);
 	return(sp);
 }
 
 /*
  - backref - figure out what matched what, figuring in back references
  */
 static const RCHAR_T *			/* == stop (success) or NULL (failure) */
 backref(struct match *m, const RCHAR_T *start, const RCHAR_T *stop,
     sopno startst, sopno stopst, sopno lev) /* PLUS nesting level */
 {
 	int i;
 	sopno ss;	/* start sop of current subRE */
 	const RCHAR_T *sp;	/* start of string matched by it */
 	sopno ssub;	/* start sop of subsubRE */
 	sopno esub;	/* end sop of subsubRE */
 	const RCHAR_T *ssp;	/* start of string matched by subsubRE */
 	const RCHAR_T *dp;
 	size_t len;
 	int hard;
 	sop s;
 	RCHAR_T d;
 	regoff_t offsave;
 	cset *cs;
 
 	AT("back", start, stop, startst, stopst);
 	sp = start;
 
 	/* get as far as we can with easy stuff */
 	hard = 0;
 	for (ss = startst; !hard && ss < stopst; ss++) {
 		s = m->g->strip[ss];
 		d = m->g->stripdata[ss];
 		switch (s) {
 		case OCHAR:
 			if (sp == stop || *sp++ != d)
 				return(NULL);
 			break;
 		case OANY:
 			if (sp == stop)
 				return(NULL);
 			sp++;
 			break;
 		case OANYOF:
 			cs = &m->g->sets[d];
 			if (sp == stop || !CHIN(cs, *sp++))
 				return(NULL);
 			break;
 		case OBOL:
 			if ( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
 					(sp < m->endp && *(sp-1) == '\n' &&
 						(m->g->cflags&REG_NEWLINE)) )
 				{ /* yes */ }
 			else
 				return(NULL);
 			break;
 		case OEOL:
 			if ( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
 					(sp < m->endp && *sp == '\n' &&
 						(m->g->cflags&REG_NEWLINE)) )
 				{ /* yes */ }
 			else
 				return(NULL);
 			break;
 		case OBOW:
 			if (( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
 					(sp < m->endp && *(sp-1) == '\n' &&
 						(m->g->cflags&REG_NEWLINE)) ||
 					(sp > m->beginp &&
 							!ISWORD(*(sp-1))) ) &&
 					(sp < m->endp && ISWORD(*sp)) )
 				{ /* yes */ }
 			else
 				return(NULL);
 			break;
 		case OEOW:
 			if (( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
 					(sp < m->endp && *sp == '\n' &&
 						(m->g->cflags&REG_NEWLINE)) ||
 					(sp < m->endp && !ISWORD(*sp)) ) &&
 					(sp > m->beginp && ISWORD(*(sp-1))) )
 				{ /* yes */ }
 			else
 				return(NULL);
 			break;
 		case O_QUEST:
 			break;
 		case OOR1:	/* matches null but needs to skip */
 			ss++;
 			s = m->g->strip[ss];
 			d = m->g->stripdata[ss];
 			do {
 				assert(s == OOR2);
 				ss += d;
 				s = m->g->strip[ss];
 				d = m->g->stripdata[ss];
 			} while (s != O_CH);
 			/* note that the ss++ gets us past the O_CH */
 			break;
 		default:	/* have to make a choice */
 			hard = 1;
 			break;
 		}
 	}
 	if (!hard) {		/* that was it! */
 		if (sp != stop)
 			return(NULL);
 		return(sp);
 	}
 	ss--;			/* adjust for the for's final increment */
 
 	/* the hard stuff */
 	AT("hard", sp, stop, ss, stopst);
 	s = m->g->strip[ss];
 	d = m->g->stripdata[ss];
 	switch (s) {
 	case OBACK_:		/* the vilest depths */
 		i = d;
 		assert(0 < i && i <= m->g->nsub);
 		if (m->pmatch[i].rm_eo == -1)
 			return(NULL);
 		assert(m->pmatch[i].rm_so != -1);
 		len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
 		assert(stop - m->beginp >= len);
 		if (sp > stop - len)
 			return(NULL);	/* not enough left to match */
 		ssp = m->offp + m->pmatch[i].rm_so;
 		if (memcmp(sp, ssp, len) != 0)
 			return(NULL);
 		while (m->g->strip[ss] != O_BACK || m->g->stripdata[ss] != i)
 			ss++;
 		return(backref(m, sp+len, stop, ss+1, stopst, lev));
 		break;
 	case OQUEST_:		/* to null or not */
 		dp = backref(m, sp, stop, ss+1, stopst, lev);
 		if (dp != NULL)
 			return(dp);	/* not */
 		return(backref(m, sp, stop, ss+d+1, stopst, lev));
 		break;
 	case OPLUS_:
 		assert(m->lastpos != NULL);
 		assert(lev+1 <= m->g->nplus);
 		m->lastpos[lev+1] = sp;
 		return(backref(m, sp, stop, ss+1, stopst, lev+1));
 		break;
 	case O_PLUS:
 		if (sp == m->lastpos[lev])	/* last pass matched null */
 			return(backref(m, sp, stop, ss+1, stopst, lev-1));
 		/* try another pass */
 		m->lastpos[lev] = sp;
 		dp = backref(m, sp, stop, ss-d+1, stopst, lev);
 		if (dp == NULL)
 			return(backref(m, sp, stop, ss+1, stopst, lev-1));
 		else
 			return(dp);
 		break;
 	case OCH_:		/* find the right one, if any */
 		ssub = ss + 1;
 		esub = ss + d - 1;
 		assert(m->g->strip[esub] == OOR1);
 		for (;;) {	/* find first matching branch */
 			dp = backref(m, sp, stop, ssub, esub, lev);
 			if (dp != NULL)
 				return(dp);
 			/* that one missed, try next one */
 			if (m->g->strip[esub] == O_CH)
 				return(NULL);	/* there is none */
 			esub++;
 			assert(m->g->strip[esub] == OOR2);
 			ssub = esub + 1;
 			esub += m->g->stripdata[esub];
 			if (m->g->strip[esub] == OOR2)
 				esub--;
 			else
 				assert(m->g->strip[esub] == O_CH);
 		}
 		break;
 	case OLPAREN:		/* must undo assignment if rest fails */
 		i = d;
 		assert(0 < i && i <= m->g->nsub);
 		offsave = m->pmatch[i].rm_so;
 		m->pmatch[i].rm_so = sp - m->offp;
 		dp = backref(m, sp, stop, ss+1, stopst, lev);
 		if (dp != NULL)
 			return(dp);
 		m->pmatch[i].rm_so = offsave;
 		return(NULL);
 		break;
 	case ORPAREN:		/* must undo assignment if rest fails */
 		i = d;
 		assert(0 < i && i <= m->g->nsub);
 		offsave = m->pmatch[i].rm_eo;
 		m->pmatch[i].rm_eo = sp - m->offp;
 		dp = backref(m, sp, stop, ss+1, stopst, lev);
 		if (dp != NULL)
 			return(dp);
 		m->pmatch[i].rm_eo = offsave;
 		return(NULL);
 		break;
 	default:		/* uh oh */
 		assert(nope);
 		break;
 	}
 
 	/* "can't happen" */
 	assert(nope);
 	/* NOTREACHED */
 	return NULL;
 }
 
 /*
  - fast - step through the string at top speed
  */
 static const RCHAR_T *			/* where tentative match ended, or NULL */
 fast(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst,
     sopno stopst)
 {
 	states st = m->st;
 	states fresh = m->fresh;
 	states tmp = m->tmp;
 	const RCHAR_T *p = start;
 	RCHAR_T c = (start == m->beginp) ? OUT : *(start-1);
 	RCHAR_T lastc;	/* previous c */
 	int flag;
 	int i;
 	const RCHAR_T *coldp;	/* last p after which no match was underway */
 
 	CLEAR(st);
 	SET1(st, startst);
 	st = step(m->g, startst, stopst, st, NOTHING, OUT, st);
 	ASSIGN(fresh, st);
 	SP("start", st, *p);
 	coldp = NULL;
 	for (;;) {
 		/* next character */
 		lastc = c;
 		c = (p == m->endp) ? OUT : *p;
 		if (EQ(st, fresh))
 			coldp = p;
 
 		/* is there an EOL and/or BOL between lastc and c? */
 		flag = 0;
 		i = 0;
 		if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
 				(lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
 			flag = BOL;
 			i = m->g->nbol;
 		}
 		if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
 				(c == OUT && !(m->eflags&REG_NOTEOL)) ) {
 			flag = (flag == BOL) ? BOLEOL : EOL;
 			i += m->g->neol;
 		}
 		if (i != 0) {
 			for (; i > 0; i--)
 				st = step(m->g, startst, stopst, st, flag, OUT, st);
 			SP("boleol", st, c);
 		}
 
 		/* how about a word boundary? */
 		if ( (flag == BOL || (lastc != OUT && !ISWORD(lastc))) &&
 					(c != OUT && ISWORD(c)) ) {
 			flag = BOW;
 		}
 		if ( (lastc != OUT && ISWORD(lastc)) &&
 				(flag == EOL || (c != OUT && !ISWORD(c))) ) {
 			flag = EOW;
 		}
 		if (flag == BOW || flag == EOW) {
 			st = step(m->g, startst, stopst, st, flag, OUT, st);
 			SP("boweow", st, c);
 		}
 
 		/* are we done? */
 		if (ISSET(st, stopst) || p == stop)
 			break;		/* NOTE BREAK OUT */
 
 		/* no, we must deal with this character */
 		ASSIGN(tmp, st);
 		ASSIGN(st, fresh);
 		assert(c != OUT);
 		st = step(m->g, startst, stopst, tmp, 0, c, st);
 		SP("aft", st, c);
 		assert(EQ(step(m->g, startst, stopst, st, NOTHING, OUT, st), st));
 		p++;
 	}
 
 	assert(coldp != NULL);
 	m->coldp = coldp;
 	if (ISSET(st, stopst))
 		return(p+1);
 	else
 		return(NULL);
 }
 
 /*
  - slow - step through the string more deliberately
  */
 static const RCHAR_T *			/* where it ended */
 slow(struct match *m, const RCHAR_T *start, const RCHAR_T *stop, sopno startst,
     sopno stopst)
 {
 	states st = m->st;
 	states empty = m->empty;
 	states tmp = m->tmp;
 	const RCHAR_T *p = start;
 	RCHAR_T c = (start == m->beginp) ? OUT : *(start-1);
 	RCHAR_T lastc;	/* previous c */
 	int flag;
 	int i;
 	const RCHAR_T *matchp;	/* last p at which a match ended */
 
 	AT("slow", start, stop, startst, stopst);
 	CLEAR(st);
 	SET1(st, startst);
 	SP("sstart", st, *p);
 	st = step(m->g, startst, stopst, st, NOTHING, OUT, st);
 	matchp = NULL;
 	for (;;) {
 		/* next character */
 		lastc = c;
 		c = (p == m->endp) ? OUT : *p;
 
 		/* is there an EOL and/or BOL between lastc and c? */
 		flag = 0;
 		i = 0;
 		if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
 				(lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
 			flag = BOL;
 			i = m->g->nbol;
 		}
 		if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
 				(c == OUT && !(m->eflags&REG_NOTEOL)) ) {
 			flag = (flag == BOL) ? BOLEOL : EOL;
 			i += m->g->neol;
 		}
 		if (i != 0) {
 			for (; i > 0; i--)
 				st = step(m->g, startst, stopst, st, flag, OUT, st);
 			SP("sboleol", st, c);
 		}
 
 		/* how about a word boundary? */
 		if ( (flag == BOL || (lastc != OUT && !ISWORD(lastc))) &&
 					(c != OUT && ISWORD(c)) ) {
 			flag = BOW;
 		}
 		if ( (lastc != OUT && ISWORD(lastc)) &&
 				(flag == EOL || (c != OUT && !ISWORD(c))) ) {
 			flag = EOW;
 		}
 		if (flag == BOW || flag == EOW) {
 			st = step(m->g, startst, stopst, st, flag, OUT, st);
 			SP("sboweow", st, c);
 		}
 
 		/* are we done? */
 		if (ISSET(st, stopst))
 			matchp = p;
 		if (EQ(st, empty) || p == stop)
 			break;		/* NOTE BREAK OUT */
 
 		/* no, we must deal with this character */
 		ASSIGN(tmp, st);
 		ASSIGN(st, empty);
 		assert(c != OUT);
 		st = step(m->g, startst, stopst, tmp, 0, c, st);
 		SP("saft", st, c);
 		assert(EQ(step(m->g, startst, stopst, st, NOTHING, OUT, st), st));
 		p++;
 	}
 
 	return(matchp);
 }
 
 
 /*
  - step - map set of states reachable before char to set reachable after
  */
 static states
 step(struct re_guts *g,
     sopno start,			/* start state within strip */
     sopno stop,			/* state after stop state within strip */
     states bef,		/* states reachable before */
     int flag,			/* NONCHAR flag */
     RCHAR_T ch,			/* character code */
     states aft)		/* states already known reachable after */
 {
 	cset *cs;
 	sop s;
 	RCHAR_T d;
 	sopno pc;
 	onestate here;		/* note, macros know this name */
 	sopno look;
 	int i;
 
 	for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
 		s = g->strip[pc];
 		d = g->stripdata[pc];
 		switch (s) {
 		case OEND:
 			assert(pc == stop-1);
 			break;
 		case OCHAR:
 			/* only characters can match */
 			assert(!flag || ch != d);
 			if (ch == d)
 				FWD(aft, bef, 1);
 			break;
 		case OBOL:
 			if (flag == BOL || flag == BOLEOL)
 				FWD(aft, bef, 1);
 			break;
 		case OEOL:
 			if (flag == EOL || flag == BOLEOL)
 				FWD(aft, bef, 1);
 			break;
 		case OBOW:
 			if (flag == BOW)
 				FWD(aft, bef, 1);
 			break;
 		case OEOW:
 			if (flag == EOW)
 				FWD(aft, bef, 1);
 			break;
 		case OANY:
 			if (!flag)
 				FWD(aft, bef, 1);
 			break;
 		case OANYOF:
 			cs = &g->sets[d];
 			if (!flag && CHIN(cs, ch))
 				FWD(aft, bef, 1);
 			break;
 		case OBACK_:		/* ignored here */
 		case O_BACK:
 			FWD(aft, aft, 1);
 			break;
 		case OPLUS_:		/* forward, this is just an empty */
 			FWD(aft, aft, 1);
 			break;
 		case O_PLUS:		/* both forward and back */
 			FWD(aft, aft, 1);
 			i = ISSETBACK(aft, d);
 			BACK(aft, aft, d);
 			if (!i && ISSETBACK(aft, d)) {
 				/* oho, must reconsider loop body */
 				pc -= d + 1;
 				INIT(here, pc);
 			}
 			break;
 		case OQUEST_:		/* two branches, both forward */
 			FWD(aft, aft, 1);
 			FWD(aft, aft, d);
 			break;
 		case O_QUEST:		/* just an empty */
 			FWD(aft, aft, 1);
 			break;
 		case OLPAREN:		/* not significant here */
 		case ORPAREN:
 			FWD(aft, aft, 1);
 			break;
 		case OCH_:		/* mark the first two branches */
 			FWD(aft, aft, 1);
 			assert(OP(g->strip[pc+d]) == OOR2);
 			FWD(aft, aft, d);
 			break;
 		case OOR1:		/* done a branch, find the O_CH */
 			if (ISSTATEIN(aft, here)) {
 				for (look = 1; /**/; look += d) {
 					s = g->strip[pc+look];
 					d = g->stripdata[pc+look];
 					if (s == O_CH)
 						break;
 					assert(s == OOR2);
 				}
 				FWD(aft, aft, look);
 			}
 			break;
 		case OOR2:		/* propagate OCH_'s marking */
 			FWD(aft, aft, 1);
 			if (g->strip[pc+d] != O_CH) {
 				assert(g->strip[pc+d] == OOR2);
 				FWD(aft, aft, d);
 			}
 			break;
 		case O_CH:		/* just empty */
 			FWD(aft, aft, 1);
 			break;
 		default:		/* ooooops... */
 			assert(nope);
 			break;
 		}
 	}
 
 	return(aft);
 }
 
 #ifdef REDEBUG
 /*
  - print - print a set of states
  */
 static void
 print(struct match *m, char *caption, states st, int ch, FILE *d)
 {
 	struct re_guts *g = m->g;
 	int i;
 	int first = 1;
 
 	if (!(m->eflags&REG_TRACE))
 		return;
 
 	fprintf(d, "%s", caption);
 	if (ch != '\0')
 		fprintf(d, " %s", pchar(ch));
 	for (i = 0; i < g->nstates; i++)
 		if (ISSET(st, i)) {
 			fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
 			first = 0;
 		}
 	fprintf(d, "\n");
 }
 
 /* 
  - at - print current situation
  */
 static void
 at(struct match *m, char *title, char *start, char *stop, sopno startst,
     sopno stopst)
 {
 	if (!(m->eflags&REG_TRACE))
 		return;
 
 	printf("%s %s-", title, pchar(*start));
 	printf("%s ", pchar(*stop));
 	printf("%ld-%ld\n", (long)startst, (long)stopst);
 }
 
 #ifndef PCHARDONE
 #define	PCHARDONE	/* never again */
 /*
  - pchar - make a character printable
  *
  * Is this identical to regchar() over in debug.c?  Well, yes.  But a
  * duplicate here avoids having a debugging-capable regexec.o tied to
  * a matching debug.o, and this is convenient.  It all disappears in
  * the non-debug compilation anyway, so it doesn't matter much.
  */
 static char *			/* -> representation */
 pchar(int ch)
 {
 	static char pbuf[10];
 
 	if (isprint(ch) || ch == ' ')
 		snprintf(pbuf, sizeof(pbuf), "%c", ch);
 	else
 		snprintf(pbuf, sizeof(pbuf), "\\%o", ch);
 	return(pbuf);
 }
 #endif
 #endif
 
 #undef	matcher
 #undef	fast
 #undef	slow
 #undef	dissect
 #undef	backref
 #undef	step
 #undef	print
 #undef	at
 #undef	match
Index: head/contrib/nvi/regex/regexec.c
===================================================================
--- head/contrib/nvi/regex/regexec.c	(revision 366308)
+++ head/contrib/nvi/regex/regexec.c	(revision 366309)
@@ -1,173 +1,173 @@
 /*	$NetBSD: regexec.c,v 1.4 2009/10/31 20:11:53 dsl Exp $ */
 
 /*-
  * Copyright (c) 1992, 1993, 1994 Henry Spencer.
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Henry Spencer of the University of Toronto.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)regexec.c	8.2 (Berkeley) 3/16/94
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)regexec.c	8.2 (Berkeley) 3/16/94";
 #endif /* LIBC_SCCS and not lint */
 
 /*
  * the outer shell of regexec()
  *
  * This file includes engine.c *twice*, after muchos fiddling with the
  * macros that code uses.  This lets the same code operate on two different
  * representations for state sets.
  */
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <ctype.h>
 #include <regex.h>
 
 #include "utils.h"
 #include "regex2.h"
 
 /* macros for manipulating states, small version */
 #define	states	int
 #define	states1	int		/* for later use in regexec() decision */
 #define	CLEAR(v)	((v) = 0)
 #define	SET0(v, n)	((v) &= ~(1 << (n)))
 #define	SET1(v, n)	((v) |= 1 << (n))
 #define	ISSET(v, n)	((v) & (1 << (n)))
 #define	ASSIGN(d, s)	((d) = (s))
 #define	EQ(a, b)	((a) == (b))
 #define	STATEVARS	int dummy	/* dummy version */
 #define	STATESETUP(m, n)	/* nothing */
 #define	STATETEARDOWN(m)	/* nothing */
 #define	SETUP(v)	((v) = 0)
 #define	onestate	int
 #define	INIT(o, n)	((o) = (unsigned)1 << (n))
 #define	INC(o)	((o) <<= 1)
 #define	ISSTATEIN(v, o)	((v) & (o))
 /* some abbreviations; note that some of these know variable names! */
 /* do "if I'm here, I can also be there" etc without branches */
 #define	FWD(dst, src, n)	((dst) |= ((unsigned)(src)&(here)) << (n))
 #define	BACK(dst, src, n)	((dst) |= ((unsigned)(src)&(here)) >> (n))
 #define	ISSETBACK(v, n)	((v) & ((unsigned)here >> (n)))
 /* function names */
 #define SNAMES			/* engine.c looks after details */
 
 #include "engine.c"
 
 /* now undo things */
 #undef	states
 #undef	CLEAR
 #undef	SET0
 #undef	SET1
 #undef	ISSET
 #undef	ASSIGN
 #undef	EQ
 #undef	STATEVARS
 #undef	STATESETUP
 #undef	STATETEARDOWN
 #undef	SETUP
 #undef	onestate
 #undef	INIT
 #undef	INC
 #undef	ISSTATEIN
 #undef	FWD
 #undef	BACK
 #undef	ISSETBACK
 #undef	SNAMES
 
 /* macros for manipulating states, large version */
 #define	states	char *
 #define	CLEAR(v)	memset(v, 0, m->g->nstates)
 #define	SET0(v, n)	((v)[n] = 0)
 #define	SET1(v, n)	((v)[n] = 1)
 #define	ISSET(v, n)	((v)[n])
 #define	ASSIGN(d, s)	memcpy(d, s, m->g->nstates)
 #define	EQ(a, b)	(memcmp(a, b, m->g->nstates) == 0)
 #define	STATEVARS	int vn; char *space
-#define	STATESETUP(m, nv)	{ (m)->space = malloc((nv)*(m)->g->nstates); \
+#define	STATESETUP(m, nv)	do { (m)->space = malloc((nv)*(m)->g->nstates); \
 				if ((m)->space == NULL) return(REG_ESPACE); \
-				(m)->vn = 0; }
-#define	STATETEARDOWN(m)	{ free((m)->space); }
+				(m)->vn = 0; } while (0)
+#define	STATETEARDOWN(m)	free((m)->space)
 #define	SETUP(v)	((v) = &m->space[m->vn++ * m->g->nstates])
 #define	onestate	int
 #define	INIT(o, n)	((o) = (n))
 #define	INC(o)	((o)++)
 #define	ISSTATEIN(v, o)	((v)[o])
 /* some abbreviations; note that some of these know variable names! */
 /* do "if I'm here, I can also be there" etc without branches */
 #define	FWD(dst, src, n)	((dst)[here+(n)] |= (src)[here])
 #define	BACK(dst, src, n)	((dst)[here-(n)] |= (src)[here])
 #define	ISSETBACK(v, n)	((v)[here - (n)])
 /* function names */
 #define	LNAMES			/* flag */
 
 #include "engine.c"
 
 /*
  - regexec - interface for matching
  = extern int regexec(const regex_t *, const char *, size_t, \
  =					regmatch_t [], int);
  = #define	REG_NOTBOL	00001
  = #define	REG_NOTEOL	00002
  = #define	REG_STARTEND	00004
  = #define	REG_TRACE	00400	// tracing of execution
  = #define	REG_LARGE	01000	// force large representation
  = #define	REG_BACKR	02000	// force use of backref code
  *
  * We put this here so we can exploit knowledge of the state representation
  * when choosing which matcher to call.  Also, by this point the matchers
  * have been prototyped.
  */
 int				/* 0 success, REG_NOMATCH failure */
 regexec(const regex_t *preg, const RCHAR_T *string, size_t nmatch,
     regmatch_t *pmatch, int eflags)
 {
 	struct re_guts *g = preg->re_g;
 #ifdef REDEBUG
 #	define	GOODFLAGS(f)	(f)
 #else
 #	define	GOODFLAGS(f)	((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
 #endif
 
 	if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
 		return(REG_BADPAT);
 	assert(!(g->iflags&BAD));
 	if (g->iflags&BAD)		/* backstop for no-debug case */
 		return(REG_BADPAT);
 	eflags = GOODFLAGS(eflags);
 
 	if (g->nstates <= (int)(CHAR_BIT*sizeof(states1)) && !(eflags&REG_LARGE))
 		return(smatcher(g, string, nmatch, pmatch, eflags));
 	else
 		return(lmatcher(g, string, nmatch, pmatch, eflags));
 }
Index: head/contrib/nvi/vi/v_itxt.c
===================================================================
--- head/contrib/nvi/vi/v_itxt.c	(revision 366308)
+++ head/contrib/nvi/vi/v_itxt.c	(revision 366309)
@@ -1,510 +1,510 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 /*
  * !!!
  * Repeated input in the historic vi is mostly wrong and this isn't very
  * backward compatible.  For example, if the user entered "3Aab\ncd" in
  * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
  * appended to the result.  There was also a hack which I don't remember
  * right now, where "3o" would open 3 lines and then let the user fill them
  * in, to make screen movements on 300 baud modems more tolerable.  I don't
  * think it's going to be missed.
  *
  * !!!
  * There's a problem with the way that we do logging for change commands with
  * implied motions (e.g. A, I, O, cc, etc.).  Since the main vi loop logs the
  * starting cursor position before the change command "moves" the cursor, the
  * cursor position to which we return on undo will be where the user entered
  * the change command, not the start of the change.  Several of the following
  * routines re-log the cursor to make this work correctly.  Historic vi tried
  * to do the same thing, and mostly got it right.  (The only spectacular way
  * it fails is if the user entered 'o' from anywhere but the last character of
  * the line, the undo returned the cursor to the start of the line.  If the
  * user was on the last character of the line, the cursor returned to that
  * position.)  We also check for mapped keys waiting, i.e. if we're in the
  * middle of a map, don't bother logging the cursor.
  */
-#define	LOG_CORRECT {							\
+#define	LOG_CORRECT do {						\
 	if (!MAPPED_KEYS_WAITING(sp))					\
 		(void)log_cursor(sp);					\
-}
+} while (0)
 
 static u_int32_t set_txt_std(SCR *, VICMD *, u_int32_t);
 
 /*
  * v_iA -- [count]A
  *	Append text to the end of the line.
  *
  * PUBLIC: int v_iA(SCR *, VICMD *);
  */
 int
 v_iA(SCR *sp, VICMD *vp)
 {
 	size_t len;
 
 	if (!db_get(sp, vp->m_start.lno, 0, NULL, &len))
 		sp->cno = len == 0 ? 0 : len - 1;
 
 	LOG_CORRECT;
 
 	return (v_ia(sp, vp));
 }
 
 /*
  * v_ia -- [count]a
  *	   [count]A
  *	Append text to the cursor position.
  *
  * PUBLIC: int v_ia(SCR *, VICMD *);
  */
 int
 v_ia(SCR *sp, VICMD *vp)
 {
 	size_t len;
 	u_int32_t flags;
 	int isempty;
 	CHAR_T *p;
 
 	flags = set_txt_std(sp, vp, 0);
 	sp->showmode = SM_APPEND;
 	sp->lno = vp->m_start.lno;
 
 	/* Move the cursor one column to the right and repaint the screen. */
 	if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		len = 0;
 		LF_SET(TXT_APPENDEOL);
 	} else if (len) {
 		if (len == sp->cno + 1) {
 			sp->cno = len;
 			LF_SET(TXT_APPENDEOL);
 		} else
 			++sp->cno;
 	} else
 		LF_SET(TXT_APPENDEOL);
 
 	return (v_txt(sp, vp, NULL, p, len,
 	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
 }
 
 /*
  * v_iI -- [count]I
  *	Insert text at the first nonblank.
  *
  * PUBLIC: int v_iI(SCR *, VICMD *);
  */
 int
 v_iI(SCR *sp, VICMD *vp)
 {
 	sp->cno = 0;
 	if (nonblank(sp, vp->m_start.lno, &sp->cno))
 		return (1);
 
 	LOG_CORRECT;
 
 	return (v_ii(sp, vp));
 }
 
 /*
  * v_ii -- [count]i
  *	   [count]I
  *	Insert text at the cursor position.
  *
  * PUBLIC: int v_ii(SCR *, VICMD *);
  */
 int
 v_ii(SCR *sp, VICMD *vp)
 {
 	size_t len;
 	u_int32_t flags;
 	int isempty;
 	CHAR_T *p;
 
 	flags = set_txt_std(sp, vp, 0);
 	sp->showmode = SM_INSERT;
 	sp->lno = vp->m_start.lno;
 
 	if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		len = 0;
 	}
 
 	if (len == 0)
 		LF_SET(TXT_APPENDEOL);
 	return (v_txt(sp, vp, NULL, p, len,
 	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
 }
 
 enum which { o_cmd, O_cmd };
 static int io(SCR *, VICMD *, enum which);
 
 /*
  * v_iO -- [count]O
  *	Insert text above this line.
  *
  * PUBLIC: int v_iO(SCR *, VICMD *);
  */
 int
 v_iO(SCR *sp, VICMD *vp)
 {
 	return (io(sp, vp, O_cmd));
 }
 
 /*
  * v_io -- [count]o
  *	Insert text after this line.
  *
  * PUBLIC: int v_io(SCR *, VICMD *);
  */
 int
 v_io(SCR *sp, VICMD *vp)
 {
 	return (io(sp, vp, o_cmd));
 }
 
 static int
 io(SCR *sp, VICMD *vp, enum which cmd)
 {
 	recno_t ai_line, lno;
 	size_t len;
 	u_int32_t flags;
 	CHAR_T *p;
 
 	flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL);
 	sp->showmode = SM_INSERT;
 
 	if (sp->lno == 1) {
 		if (db_last(sp, &lno))
 			return (1);
 		if (lno != 0)
 			goto insert;
 		p = NULL;
 		len = 0;
 		ai_line = OOBLNO;
 	} else {
 insert:		p = L("");
 		sp->cno = 0;
 		LOG_CORRECT;
 
 		if (cmd == O_cmd) {
 			if (db_insert(sp, sp->lno, p, 0))
 				return (1);
 			if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
 				return (1);
 			ai_line = sp->lno + 1;
 		} else {
 			if (db_append(sp, 1, sp->lno, p, 0))
 				return (1);
 			if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len))
 				return (1);
 			ai_line = sp->lno - 1;
 		}
 	}
 	return (v_txt(sp, vp, NULL, p, len,
 	    0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
 }
 
 /*
  * v_change -- [buffer][count]c[count]motion
  *	       [buffer][count]C
  *	       [buffer][count]S
  *	Change command.
  *
  * PUBLIC: int v_change(SCR *, VICMD *);
  */
 int
 v_change(SCR *sp, VICMD *vp)
 {
 	size_t blen, len;
 	u_int32_t flags;
 	int isempty, lmode, rval;
 	CHAR_T *bp;
 	CHAR_T *p;
 
 	/*
 	 * 'c' can be combined with motion commands that set the resulting
 	 * cursor position, i.e. "cG".  Clear the VM_RCM flags and make the
 	 * resulting cursor position stick, inserting text has its own rules
 	 * for cursor positioning.
 	 */
 	F_CLR(vp, VM_RCM_MASK);
 	F_SET(vp, VM_RCM_SET);
 
 	/*
 	 * Find out if the file is empty, it's easier to handle it as a
 	 * special case.
 	 */
 	if (vp->m_start.lno == vp->m_stop.lno &&
 	    db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		return (v_ia(sp, vp));
 	}
 
 	flags = set_txt_std(sp, vp, 0);
 	sp->showmode = SM_CHANGE;
 
 	/*
 	 * Move the cursor to the start of the change.  Note, if autoindent
 	 * is turned on, the cc command in line mode changes from the first
 	 * *non-blank* character of the line, not the first character.  And,
 	 * to make it just a bit more exciting, the initial space is handled
 	 * as auto-indent characters.
 	 */
 	lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
 	if (lmode) {
 		vp->m_start.cno = 0;
 		if (O_ISSET(sp, O_AUTOINDENT)) {
 			if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno))
 				return (1);
 			LF_SET(TXT_AICHARS);
 		}
 	}
 	sp->lno = vp->m_start.lno;
 	sp->cno = vp->m_start.cno;
 
 	LOG_CORRECT;
 
 	/*
 	 * If not in line mode and changing within a single line, copy the
 	 * text and overwrite it.
 	 */
 	if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
 		/*
 		 * !!!
 		 * Historic practice, c did not cut into the numeric buffers,
 		 * only the unnamed one.
 		 */
 		if (cut(sp,
 		    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
 		    &vp->m_start, &vp->m_stop, lmode))
 			return (1);
 		if (len == 0)
 			LF_SET(TXT_APPENDEOL);
 		LF_SET(TXT_EMARK | TXT_OVERWRITE);
 		return (v_txt(sp, vp, &vp->m_stop, p, len,
 		    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
 	}
 
 	/*
 	 * It's trickier if in line mode or changing over multiple lines.  If
 	 * we're in line mode delete all of the lines and insert a replacement
 	 * line which the user edits.  If there was leading whitespace in the
 	 * first line being changed, we copy it and use it as the replacement.
 	 * If we're not in line mode, we delete the text and start inserting.
 	 *
 	 * !!!
 	 * Copy the text.  Historic practice, c did not cut into the numeric
 	 * buffers, only the unnamed one.
 	 */
 	if (cut(sp,
 	    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
 	    &vp->m_start, &vp->m_stop, lmode))
 		return (1);
 
 	/* If replacing entire lines and there's leading text. */
 	if (lmode && vp->m_start.cno) {
 		/*
 		 * Get a copy of the first line changed, and copy out the
 		 * leading text.
 		 */
 		if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
 			return (1);
 		GET_SPACE_RETW(sp, bp, blen, vp->m_start.cno);
 		MEMMOVE(bp, p, vp->m_start.cno);
 	} else
 		bp = NULL;
 
 	/* Delete the text. */
 	if (del(sp, &vp->m_start, &vp->m_stop, lmode))
 		return (1);
 
 	/* If replacing entire lines, insert a replacement line. */
 	if (lmode) {
 		if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno))
 			return (1);
 		sp->lno = vp->m_start.lno;
 		len = sp->cno = vp->m_start.cno;
 	}
 
 	/* Get the line we're editing. */
 	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		len = 0;
 	}
 
 	/* Check to see if we're appending to the line. */
 	if (vp->m_start.cno >= len)
 		LF_SET(TXT_APPENDEOL);
 
 	rval = v_txt(sp, vp, NULL, p, len,
 	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags);
 
 	if (bp != NULL)
 		FREE_SPACEW(sp, bp, blen);
 	return (rval);
 }
 
 /*
  * v_Replace -- [count]R
  *	Overwrite multiple characters.
  *
  * PUBLIC: int v_Replace(SCR *, VICMD *);
  */
 int
 v_Replace(SCR *sp, VICMD *vp)
 {
 	size_t len;
 	u_int32_t flags;
 	int isempty;
 	CHAR_T *p;
 
 	flags = set_txt_std(sp, vp, 0);
 	sp->showmode = SM_REPLACE;
 
 	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		len = 0;
 		LF_SET(TXT_APPENDEOL);
 	} else {
 		if (len == 0)
 			LF_SET(TXT_APPENDEOL);
 		LF_SET(TXT_OVERWRITE | TXT_REPLACE);
 	}
 	vp->m_stop.lno = vp->m_start.lno;
 	vp->m_stop.cno = len ? len - 1 : 0;
 
 	return (v_txt(sp, vp, &vp->m_stop, p, len,
 	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
 }
 
 /*
  * v_subst -- [buffer][count]s
  *	Substitute characters.
  *
  * PUBLIC: int v_subst(SCR *, VICMD *);
  */
 int
 v_subst(SCR *sp, VICMD *vp)
 {
 	size_t len;
 	u_int32_t flags;
 	int isempty;
 	CHAR_T *p;
 
 	flags = set_txt_std(sp, vp, 0);
 	sp->showmode = SM_CHANGE;
 
 	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
 		if (!isempty)
 			return (1);
 		len = 0;
 		LF_SET(TXT_APPENDEOL);
 	} else {
 		if (len == 0)
 			LF_SET(TXT_APPENDEOL);
 		LF_SET(TXT_EMARK | TXT_OVERWRITE);
 	}
 
 	vp->m_stop.lno = vp->m_start.lno;
 	vp->m_stop.cno =
 	    vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
 	if (vp->m_stop.cno > len - 1)
 		vp->m_stop.cno = len - 1;
 
 	if (p != NULL && cut(sp,
 	    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
 	    &vp->m_start, &vp->m_stop, 0))
 		return (1);
 
 	return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags));
 }
 
 /*
  * set_txt_std --
  *	Initialize text processing flags.
  */
 static u_int32_t
 set_txt_std(SCR *sp, VICMD *vp, u_int32_t flags)
 {
 	LF_SET(TXT_CNTRLT |
 	    TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE);
 
 	if (F_ISSET(vp, VC_ISDOT))
 		LF_SET(TXT_REPLAY);
 
 	if (O_ISSET(sp, O_ALTWERASE))
 		LF_SET(TXT_ALTWERASE);
 	if (O_ISSET(sp, O_AUTOINDENT))
 		LF_SET(TXT_AUTOINDENT);
 	if (O_ISSET(sp, O_BEAUTIFY))
 		LF_SET(TXT_BEAUTIFY);
 	if (O_ISSET(sp, O_SHOWMATCH))
 		LF_SET(TXT_SHOWMATCH);
 	if (F_ISSET(sp, SC_SCRIPT))
 		LF_SET(TXT_CR);
 	if (O_ISSET(sp, O_TTYWERASE))
 		LF_SET(TXT_TTYWERASE);
 
 	/*
 	 * !!!
 	 * Mapped keys were sometimes unaffected by the wrapmargin option
 	 * in the historic 4BSD vi.  Consider the following commands, where
 	 * each is executed on an empty line, in an 80 column screen, with
 	 * the wrapmargin value set to 60.
 	 *
 	 *	aABC DEF <ESC>....
 	 *	:map K aABC DEF ^V<ESC><CR>KKKKK
 	 *	:map K 5aABC DEF ^V<ESC><CR>K
 	 *
 	 * The first and second commands are affected by wrapmargin.  The
 	 * third is not.  (If the inserted text is itself longer than the
 	 * wrapmargin value, i.e. if the "ABC DEF " string is replaced by
 	 * something that's longer than 60 columns from the beginning of
 	 * the line, the first two commands behave as before, but the third
 	 * command gets fairly strange.)  The problem is that people wrote
 	 * macros that depended on the third command NOT being affected by
 	 * wrapmargin, as in this gem which centers lines:
 	 *
 	 *	map #c $mq81a ^V^[81^V^V|D`qld0:s/  / /g^V^M$p
 	 *
 	 * For compatibility reasons, we try and make it all work here.  I
 	 * offer no hope that this is right, but it's probably pretty close.
 	 *
 	 * XXX
 	 * Once I work my courage up, this is all gonna go away.  It's too
 	 * evil to survive.
 	 */
 	if ((O_ISSET(sp, O_WRAPLEN) || O_ISSET(sp, O_WRAPMARGIN)) &&
 	    (!MAPPED_KEYS_WAITING(sp) || !F_ISSET(vp, VC_C1SET)))
 		LF_SET(TXT_WRAPMARGIN);
 	return (flags);
 }
Index: head/contrib/nvi/vi/v_paragraph.c
===================================================================
--- head/contrib/nvi/vi/v_paragraph.c	(revision 366308)
+++ head/contrib/nvi/vi/v_paragraph.c	(revision 366309)
@@ -1,335 +1,337 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
-#define	INTEXT_CHECK {							\
+#define	INTEXT_CHECK do {						\
 	if (len == 0 || v_isempty(p, len)) {				\
 		if (!--cnt)						\
 			goto found;					\
 		pstate = P_INBLANK;					\
 	}								\
 	/*								\
 	 * !!!								\
 	 * Historic documentation (USD:15-11, 4.2) said that formfeed	\
 	 * characters (^L) in the first column delimited paragraphs.	\
 	 * The historic vi code mentions formfeed characters, but never	\
 	 * implements them.  It seems reasonable, do it.		\
 	 */								\
 	if (p[0] == '\014') {						\
 		if (!--cnt)						\
 			goto found;					\
 		continue;						\
 	}								\
 	if (p[0] != '.' || len < 2)					\
 		continue;						\
 	for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2)			\
 		if (lp[0] == p[1] &&					\
 		    (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&	\
 		    !--cnt)						\
 			goto found;					\
-}
+} while (0)
 
 /*
  * v_paragraphf -- [count]}
  *	Move forward count paragraphs.
  *
  * Paragraphs are empty lines after text, formfeed characters, or values
  * from the paragraph or section options.
  *
  * PUBLIC: int v_paragraphf(SCR *, VICMD *);
  */
 int
 v_paragraphf(SCR *sp, VICMD *vp)
 {
 	enum { P_INTEXT, P_INBLANK } pstate;
 	size_t lastlen, len;
 	recno_t cnt, lastlno, lno;
 	int isempty;
 	CHAR_T *p;
 	char *lp;
 
 	/*
 	 * !!!
 	 * If the starting cursor position is at or before any non-blank
 	 * characters in the line, i.e. the movement is cutting all of the
 	 * line's text, the buffer is in line mode.  It's a lot easier to
 	 * check here, because we know that the end is going to be the start
 	 * or end of a line.
 	 *
 	 * This was historical practice in vi, with a single exception.  If
 	 * the paragraph movement was from the start of the last line to EOF,
 	 * then all the characters were deleted from the last line, but the
 	 * line itself remained.  If somebody complains, don't pause, don't
 	 * hesitate, just hit them.
 	 */
-	if (ISMOTION(vp))
+	if (ISMOTION(vp)) {
 		if (vp->m_start.cno == 0)
 			F_SET(vp, VM_LMODE);
 		else {
 			vp->m_stop = vp->m_start;
 			vp->m_stop.cno = 0;
 			if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
 				return (1);
 			if (vp->m_start.cno <= vp->m_stop.cno)
 				F_SET(vp, VM_LMODE);
 		}
+	}
 
 	/* Figure out what state we're currently in. */
 	lno = vp->m_start.lno;
 	if (db_get(sp, lno, 0, &p, &len))
 		goto eof;
 
 	/*
 	 * If we start in text, we want to switch states
 	 * (2 * N - 1) times, in non-text, (2 * N) times.
 	 */
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 	cnt *= 2;
 	if (len == 0 || v_isempty(p, len))
 		pstate = P_INBLANK;
 	else {
 		--cnt;
 		pstate = P_INTEXT;
 	}
 
 	for (;;) {
 		lastlno = lno;
 		lastlen = len;
 		if (db_get(sp, ++lno, 0, &p, &len))
 			goto eof;
 		switch (pstate) {
 		case P_INTEXT:
 			INTEXT_CHECK;
 			break;
 		case P_INBLANK:
 			if (len == 0 || v_isempty(p, len))
 				break;
 			if (--cnt) {
 				pstate = P_INTEXT;
 				break;
 			}
 			/*
 			 * !!!
 			 * Non-motion commands move to the end of the range,
 			 * delete and yank stay at the start.  Ignore others.
 			 * Adjust the end of the range for motion commands;
 			 * historically, a motion component was to the end of
 			 * the previous line, whereas the movement command was
 			 * to the start of the new "paragraph".
 			 */
 found:			if (ISMOTION(vp)) {
 				vp->m_stop.lno = lastlno;
 				vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
 				vp->m_final = vp->m_start;
 			} else {
 				vp->m_stop.lno = lno;
 				vp->m_stop.cno = 0;
 				vp->m_final = vp->m_stop;
 			}
 			return (0);
 		default:
 			abort();
 		}
 	}
 
 	/*
 	 * !!!
 	 * Adjust end of the range for motion commands; EOF is a movement
 	 * sink.  The } command historically moved to the end of the last
 	 * line, not the beginning, from any position before the end of the
 	 * last line.  It also historically worked on empty files, so we
 	 * have to make it okay.
 	 */
 eof:	if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) {
 		if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
 			if (!isempty)
 				return (1);
 			vp->m_start.cno = 0;
 			return (0);
 		}
 		if (vp->m_start.cno == (len ? len - 1 : 0)) {
 			v_eof(sp, NULL);
 			return (1);
 		}
 	}
 	/*
 	 * !!!
 	 * Non-motion commands move to the end of the range, delete
 	 * and yank stay at the start.  Ignore others.
 	 *
 	 * If deleting the line (which happens if deleting to EOF), then
 	 * cursor movement is to the first nonblank.
 	 */
 	if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) {
 		F_CLR(vp, VM_RCM_MASK);
 		F_SET(vp, VM_RCM_SETFNB);
 	}
 	vp->m_stop.lno = lno - 1;
 	vp->m_stop.cno = len ? len - 1 : 0;
 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
 	return (0);
 }
 
 /*
  * v_paragraphb -- [count]{
  *	Move backward count paragraphs.
  *
  * PUBLIC: int v_paragraphb(SCR *, VICMD *);
  */
 int
 v_paragraphb(SCR *sp, VICMD *vp)
 {
 	enum { P_INTEXT, P_INBLANK } pstate;
 	size_t len;
 	recno_t cnt, lno;
 	CHAR_T *p;
 	char *lp;
 
 	/*
 	 * !!!
 	 * Check for SOF.  The historic vi didn't complain if users hit SOF
 	 * repeatedly, unless it was part of a motion command.  There is no
 	 * question but that Emerson's editor of choice was vi.
 	 *
 	 * The { command historically moved to the beginning of the first
 	 * line if invoked on the first line.
 	 *
 	 * !!!
 	 * If the starting cursor position is in the first column (backward
 	 * paragraph movements did NOT historically pay attention to non-blank
 	 * characters) i.e. the movement is cutting the entire line, the buffer
 	 * is in line mode.  Cuts from the beginning of the line also did not
 	 * cut the current line, but started at the previous EOL.
 	 *
 	 * Correct for a left motion component while we're thinking about it.
 	 */
 	lno = vp->m_start.lno;
 
-	if (ISMOTION(vp))
+	if (ISMOTION(vp)) {
 		if (vp->m_start.cno == 0) {
 			if (vp->m_start.lno == 1) {
 				v_sof(sp, &vp->m_start);
 				return (1);
 			} else
 				--vp->m_start.lno;
 			F_SET(vp, VM_LMODE);
 		} else
 			--vp->m_start.cno;
+	}
 
 	if (vp->m_start.lno <= 1)
 		goto sof;
 
 	/* Figure out what state we're currently in. */
 	if (db_get(sp, lno, 0, &p, &len))
 		goto sof;
 
 	/*
 	 * If we start in text, we want to switch states
 	 * (2 * N - 1) times, in non-text, (2 * N) times.
 	 */
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 	cnt *= 2;
 	if (len == 0 || v_isempty(p, len))
 		pstate = P_INBLANK;
 	else {
 		--cnt;
 		pstate = P_INTEXT;
 
 		/*
 		 * !!!
 		 * If the starting cursor is past the first column,
 		 * the current line is checked for a paragraph.
 		 */
 		if (vp->m_start.cno > 0)
 			++lno;
 	}
 
 	for (;;) {
 		if (db_get(sp, --lno, 0, &p, &len))
 			goto sof;
 		switch (pstate) {
 		case P_INTEXT:
 			INTEXT_CHECK;
 			break;
 		case P_INBLANK:
 			if (len != 0 && !v_isempty(p, len)) {
 				if (!--cnt)
 					goto found;
 				pstate = P_INTEXT;
 			}
 			break;
 		default:
 			abort();
 		}
 	}
 
 	/* SOF is a movement sink. */
 sof:	lno = 1;
 
 found:	vp->m_stop.lno = lno;
 	vp->m_stop.cno = 0;
 
 	/*
 	 * All commands move to the end of the range.  (We already
 	 * adjusted the start of the range for motion commands).
 	 */
 	vp->m_final = vp->m_stop;
 	return (0);
 }
 
 /*
  * v_buildps --
  *	Build the paragraph command search pattern.
  *
  * PUBLIC: int v_buildps(SCR *, char *, char *);
  */
 int
 v_buildps(SCR *sp, char *p_p, char *s_p)
 {
 	VI_PRIVATE *vip;
 	size_t p_len, s_len;
 	char *p;
 
 	/*
 	 * The vi paragraph command searches for either a paragraph or
 	 * section option macro.
 	 */
 	p_len = p_p == NULL ? 0 : strlen(p_p);
 	s_len = s_p == NULL ? 0 : strlen(s_p);
 
 	if (p_len == 0 && s_len == 0)
 		return (0);
 
 	MALLOC_RET(sp, p, p_len + s_len + 1);
 
 	vip = VIP(sp);
 	free(vip->ps);
 
 	if (p_p != NULL)
 		memmove(p, p_p, p_len + 1);
 	if (s_p != NULL)
 		memmove(p + p_len, s_p, s_len + 1);
 	vip->ps = p;
 	return (0);
 }
Index: head/contrib/nvi/vi/v_section.c
===================================================================
--- head/contrib/nvi/vi/v_section.c	(revision 366308)
+++ head/contrib/nvi/vi/v_section.c	(revision 366309)
@@ -1,246 +1,247 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 /*
  * !!!
  * In historic vi, the section commands ignored empty lines, unlike the
  * paragraph commands, which was probably okay.  However, they also moved
  * to the start of the last line when there where no more sections instead
  * of the end of the last line like the paragraph commands.  I've changed
  * the latter behavior to match the paragraph commands.
  *
  * In historic vi, a section was defined as the first character(s) of the
  * line matching, which could be followed by anything.  This implementation
  * follows that historic practice.
  *
  * !!!
  * The historic vi documentation (USD:15-10) claimed:
  *	The section commands interpret a preceding count as a different
  *	window size in which to redraw the screen at the new location,
  *	and this window size is the base size for newly drawn windows
  *	until another size is specified.  This is very useful if you are
  *	on a slow terminal ...
  *
  * I can't get the 4BSD vi to do this, it just beeps at me.  For now, a
  * count to the section commands simply repeats the command.
  */
 
 /*
  * v_sectionf -- [count]]]
  *	Move forward count sections/functions.
  *
  * !!!
  * Using ]] as a motion command was a bit special, historically.  It could
  * match } as well as the usual { and section values.  If it matched a { or
  * a section, it did NOT include the matched line.  If it matched a }, it
  * did include the line.  No clue why.
  *
  * PUBLIC: int v_sectionf(SCR *, VICMD *);
  */
 int
 v_sectionf(SCR *sp, VICMD *vp)
 {
 	recno_t cnt, lno;
 	size_t len;
 	CHAR_T *p;
 	char *list, *lp;
 
 	/* Get the macro list. */
 	if ((list = O_STR(sp, O_SECTIONS)) == NULL)
 		return (1);
 
 	/*
 	 * !!!
 	 * If the starting cursor position is at or before any non-blank
 	 * characters in the line, i.e. the movement is cutting all of the
 	 * line's text, the buffer is in line mode.  It's a lot easier to
 	 * check here, because we know that the end is going to be the start
 	 * or end of a line.
 	 */
-	if (ISMOTION(vp))
+	if (ISMOTION(vp)) {
 		if (vp->m_start.cno == 0)
 			F_SET(vp, VM_LMODE);
 		else {
 			vp->m_stop = vp->m_start;
 			vp->m_stop.cno = 0;
 			if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
 				return (1);
 			if (vp->m_start.cno <= vp->m_stop.cno)
 				F_SET(vp, VM_LMODE);
 		}
+	}
 
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 	for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) {
 		if (len == 0)
 			continue;
 		if (p[0] == '{' || (ISMOTION(vp) && p[0] == '}')) {
 			if (!--cnt) {
 				if (p[0] == '{')
 					goto adjust1;
 				goto adjust2;
 			}
 			continue;
 		}
 		/*
 		 * !!!
 		 * Historic documentation (USD:15-11, 4.2) said that formfeed
 		 * characters (^L) in the first column delimited sections.
 		 * The historic code mentions formfeed characters, but never
 		 * implements them.  Seems reasonable, do it.
 		 */
 		if (p[0] == '\014') {
 			if (!--cnt)
 				goto adjust1;
 			continue;
 		}
 		if (p[0] != '.' || len < 2)
 			continue;
 		for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
 			if (lp[0] == p[1] &&
 			    ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&
 			    !--cnt) {
 				/*
 				 * !!!
 				 * If not cutting this line, adjust to the end
 				 * of the previous one.  Otherwise, position to
 				 * column 0.
 				 */
 adjust1:			if (ISMOTION(vp))
 					goto ret1;
 
 adjust2:			vp->m_stop.lno = lno;
 				vp->m_stop.cno = 0;
 				goto ret2;
 			}
 	}
 
 	/* If moving forward, reached EOF, check to see if we started there. */
 	if (vp->m_start.lno == lno - 1) {
 		v_eof(sp, NULL);
 		return (1);
 	}
 
 ret1:	if (db_get(sp, --lno, DBG_FATAL, NULL, &len))
 		return (1);
 	vp->m_stop.lno = lno;
 	vp->m_stop.cno = len ? len - 1 : 0;
 
 	/*
 	 * Non-motion commands go to the end of the range.  Delete and
 	 * yank stay at the start of the range.  Ignore others.
 	 */
 ret2:	if (ISMOTION(vp)) {
 		vp->m_final = vp->m_start;
 		if (F_ISSET(vp, VM_LMODE))
 			vp->m_final.cno = 0;
 	} else
 		vp->m_final = vp->m_stop;
 	return (0);
 }
 
 /*
  * v_sectionb -- [count][[
  *	Move backward count sections/functions.
  *
  * PUBLIC: int v_sectionb(SCR *, VICMD *);
  */
 int
 v_sectionb(SCR *sp, VICMD *vp)
 {
 	size_t len;
 	recno_t cnt, lno;
 	CHAR_T *p;
 	char *list, *lp;
 
 	/* An empty file or starting from line 1 is always illegal. */
 	if (vp->m_start.lno <= 1) {
 		v_sof(sp, NULL);
 		return (1);
 	}
 
 	/* Get the macro list. */
 	if ((list = O_STR(sp, O_SECTIONS)) == NULL)
 		return (1);
 
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 	for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) {
 		if (len == 0)
 			continue;
 		if (p[0] == '{') {
 			if (!--cnt)
 				goto adjust1;
 			continue;
 		}
 		/*
 		 * !!!
 		 * Historic documentation (USD:15-11, 4.2) said that formfeed
 		 * characters (^L) in the first column delimited sections.
 		 * The historic code mentions formfeed characters, but never
 		 * implements them.  Seems reasonable, do it.
 		 */
 		if (p[0] == '\014') {
 			if (!--cnt)
 				goto adjust1;
 			continue;
 		}
 		if (p[0] != '.' || len < 2)
 			continue;
 		for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
 			if (lp[0] == p[1] &&
 			    ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&
 			    !--cnt) {
 adjust1:			vp->m_stop.lno = lno;
 				vp->m_stop.cno = 0;
 				goto ret1;
 			}
 	}
 
 	/*
 	 * If moving backward, reached SOF, which is a movement sink.
 	 * We already checked for starting there.
 	 */
 	vp->m_stop.lno = 1;
 	vp->m_stop.cno = 0;
 
 	/*
 	 * All commands move to the end of the range.
 	 *
 	 * !!!
 	 * Historic practice is the section cut was in line mode if it started
 	 * from column 0 and was in the backward direction.  Otherwise, left
 	 * motion commands adjust the starting point to the character before
 	 * the current one.  What makes this worse is that if it cut to line
 	 * mode it also went to the first non-<blank>.
 	 */
 ret1:	if (vp->m_start.cno == 0) {
 		F_CLR(vp, VM_RCM_MASK);
 		F_SET(vp, VM_RCM_SETFNB);
 
 		--vp->m_start.lno;
 		F_SET(vp, VM_LMODE);
 	} else
 		--vp->m_start.cno;
 
 	vp->m_final = vp->m_stop;
 	return (0);
 }
Index: head/contrib/nvi/vi/v_sentence.c
===================================================================
--- head/contrib/nvi/vi/v_sentence.c	(revision 366308)
+++ head/contrib/nvi/vi/v_sentence.c	(revision 366309)
@@ -1,351 +1,352 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <limits.h>
 #include <stdio.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 /*
  * !!!
  * In historic vi, a sentence was delimited by a '.', '?' or '!' character
  * followed by TWO spaces or a newline.  One or more empty lines was also
  * treated as a separate sentence.  The Berkeley documentation for historical
  * vi states that any number of ')', ']', '"' and '\'' characters can be
  * between the delimiter character and the spaces or end of line, however,
  * the historical implementation did not handle additional '"' characters.
  * We follow the documentation here, not the implementation.
  *
  * Once again, historical vi didn't do sentence movements associated with
  * counts consistently, mostly in the presence of lines containing only
  * white-space characters.
  *
  * This implementation also permits a single tab to delimit sentences, and
  * treats lines containing only white-space characters as empty lines.
  * Finally, tabs are eaten (along with spaces) when skipping to the start
  * of the text following a "sentence".
  */
 
 /*
  * v_sentencef -- [count])
  *	Move forward count sentences.
  *
  * PUBLIC: int v_sentencef(SCR *, VICMD *);
  */
 int
 v_sentencef(SCR *sp, VICMD *vp)
 {
 	enum { BLANK, NONE, PERIOD } state;
 	VCS cs;
 	size_t len;
 	u_long cnt;
 
 	cs.cs_lno = vp->m_start.lno;
 	cs.cs_cno = vp->m_start.cno;
 	if (cs_init(sp, &cs))
 		return (1);
 
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 
 	/*
 	 * !!!
 	 * If in white-space, the next start of sentence counts as one.
 	 * This may not handle "  .  " correctly, but it's real unclear
 	 * what correctly means in that case.
 	 */
 	if (cs.cs_flags == CS_EMP || (cs.cs_flags == 0 && isblank(cs.cs_ch))) {
 		if (cs_fblank(sp, &cs))
 			return (1);
 		if (--cnt == 0) {
 			if (vp->m_start.lno != cs.cs_lno ||
 			    vp->m_start.cno != cs.cs_cno)
 				goto okret;
 			return (1);
 		}
 	}
 
 	for (state = NONE;;) {
 		if (cs_next(sp, &cs))
 			return (1);
 		if (cs.cs_flags == CS_EOF)
 			break;
 		if (cs.cs_flags == CS_EOL) {
 			if ((state == PERIOD || state == BLANK) && --cnt == 0) {
 				if (cs_next(sp, &cs))
 					return (1);
 				if (cs.cs_flags == 0 &&
 				    isblank(cs.cs_ch) && cs_fblank(sp, &cs))
 					return (1);
 				goto okret;
 			}
 			state = NONE;
 			continue;
 		}
 		if (cs.cs_flags == CS_EMP) {	/* An EMP is two sentences. */
 			if (--cnt == 0)
 				goto okret;
 			if (cs_fblank(sp, &cs))
 				return (1);
 			if (--cnt == 0)
 				goto okret;
 			state = NONE;
 			continue;
 		}
 		switch (cs.cs_ch) {
 		case '.':
 		case '?':
 		case '!':
 			state = PERIOD;
 			break;
 		case ')':
 		case ']':
 		case '"':
 		case '\'':
 			if (state != PERIOD)
 				state = NONE;
 			break;
 		case '\t':
 			if (state == PERIOD)
 				state = BLANK;
 			/* FALLTHROUGH */
 		case ' ':
 			if (state == PERIOD) {
 				state = BLANK;
 				break;
 			}
 			if (state == BLANK && --cnt == 0) {
 				if (cs_fblank(sp, &cs))
 					return (1);
 				goto okret;
 			}
 			/* FALLTHROUGH */
 		default:
 			state = NONE;
 			break;
 		}
 	}
 
 	/* EOF is a movement sink, but it's an error not to have moved. */
 	if (vp->m_start.lno == cs.cs_lno && vp->m_start.cno == cs.cs_cno) {
 		v_eof(sp, NULL);
 		return (1);
 	}
 
 okret:	vp->m_stop.lno = cs.cs_lno;
 	vp->m_stop.cno = cs.cs_cno;
 
 	/*
 	 * !!!
 	 * Historic, uh, features, yeah, that's right, call 'em features.
 	 * If the starting and ending cursor positions are at the first
 	 * column in their lines, i.e. the movement is cutting entire lines,
 	 * the buffer is in line mode, and the ending position is the last
 	 * character of the previous line.  Note check to make sure that
 	 * it's not within a single line.
 	 *
 	 * Non-motion commands move to the end of the range.  Delete and
 	 * yank stay at the start.  Ignore others.  Adjust the end of the
 	 * range for motion commands.
 	 */
 	if (ISMOTION(vp)) {
 		if (vp->m_start.cno == 0 &&
 		    (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
 			if (vp->m_start.lno < vp->m_stop.lno) {
 				if (db_get(sp,
 				    --vp->m_stop.lno, DBG_FATAL, NULL, &len))
 					return (1);
 				vp->m_stop.cno = len ? len - 1 : 0;
 			}
 			F_SET(vp, VM_LMODE);
 		} else
 			--vp->m_stop.cno;
 		vp->m_final = vp->m_start;
 	} else
 		vp->m_final = vp->m_stop;
 	return (0);
 }
 
 /*
  * v_sentenceb -- [count](
  *	Move backward count sentences.
  *
  * PUBLIC: int v_sentenceb(SCR *, VICMD *);
  */
 int
 v_sentenceb(SCR *sp, VICMD *vp)
 {
 	VCS cs;
 	recno_t slno;
 	size_t len, scno;
 	u_long cnt;
 	int last;
 
 	/*
 	 * !!!
 	 * Historic vi permitted the user to hit SOF repeatedly.
 	 */
 	if (vp->m_start.lno == 1 && vp->m_start.cno == 0)
 		return (0);
 
 	cs.cs_lno = vp->m_start.lno;
 	cs.cs_cno = vp->m_start.cno;
 	if (cs_init(sp, &cs))
 		return (1);
 
 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
 
 	/*
 	 * !!!
 	 * In empty lines, skip to the previous non-white-space character.
 	 * If in text, skip to the prevous white-space character.  Believe
 	 * it or not, in the paragraph:
 	 *	ab cd.
 	 *	AB CD.
 	 * if the cursor is on the 'A' or 'B', ( moves to the 'a'.  If it
 	 * is on the ' ', 'C' or 'D', it moves to the 'A'.  Yes, Virginia,
 	 * Berkeley was once a major center of drug activity.
 	 */
 	if (cs.cs_flags == CS_EMP) {
 		if (cs_bblank(sp, &cs))
 			return (1);
 		for (;;) {
 			if (cs_prev(sp, &cs))
 				return (1);
 			if (cs.cs_flags != CS_EOL)
 				break;
 		}
 	} else if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
 		for (;;) {
 			if (cs_prev(sp, &cs))
 				return (1);
 			if (cs.cs_flags != 0 || isblank(cs.cs_ch))
 				break;
 		}
 
 	for (last = 0;;) {
 		if (cs_prev(sp, &cs))
 			return (1);
 		if (cs.cs_flags == CS_SOF)	/* SOF is a movement sink. */
 			break;
 		if (cs.cs_flags == CS_EOL) {
 			last = 1;
 			continue;
 		}
 		if (cs.cs_flags == CS_EMP) {
 			if (--cnt == 0)
 				goto ret;
 			if (cs_bblank(sp, &cs))
 				return (1);
 			last = 0;
 			continue;
 		}
 		switch (cs.cs_ch) {
 		case '.':
 		case '?':
 		case '!':
 			if (!last || --cnt != 0) {
 				last = 0;
 				continue;
 			}
 
 ret:			slno = cs.cs_lno;
 			scno = cs.cs_cno;
 
 			/*
 			 * Move to the start of the sentence, skipping blanks
 			 * and special characters.
 			 */
 			do {
 				if (cs_next(sp, &cs))
 					return (1);
 			} while (!cs.cs_flags &&
 			    (cs.cs_ch == ')' || cs.cs_ch == ']' ||
 			    cs.cs_ch == '"' || cs.cs_ch == '\''));
 			if ((cs.cs_flags || isblank(cs.cs_ch)) &&
 			    cs_fblank(sp, &cs))
 				return (1);
 
 			/*
 			 * If it was ".  xyz", with the cursor on the 'x', or
 			 * "end.  ", with the cursor in the spaces, or the
 			 * beginning of a sentence preceded by an empty line,
 			 * we can end up where we started.  Fix it.
 			 */
 			if (vp->m_start.lno != cs.cs_lno ||
 			    vp->m_start.cno > cs.cs_cno)
 				goto okret;
 
 			/*
 			 * Well, if an empty line preceded possible blanks
 			 * and the sentence, it could be a real sentence.
 			 */
 			for (;;) {
 				if (cs_prev(sp, &cs))
 					return (1);
 				if (cs.cs_flags == CS_EOL)
 					continue;
 				if (cs.cs_flags == 0 && isblank(cs.cs_ch))
 					continue;
 				break;
 			}
 			if (cs.cs_flags == CS_EMP)
 				goto okret;
 
 			/* But it wasn't; try again. */
 			++cnt;
 			cs.cs_lno = slno;
 			cs.cs_cno = scno;
 			last = 0;
 			break;
 		case '\t':
 			last = 1;
 			break;
 		default:
 			last =
 			    cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
 			    cs.cs_ch == ')' || cs.cs_ch == ']' ||
 			    cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
 		}
 	}
 
 okret:	vp->m_stop.lno = cs.cs_lno;
 	vp->m_stop.cno = cs.cs_cno;
 
 	/*
 	 * !!!
 	 * If the starting and stopping cursor positions are at the first
 	 * columns in the line, i.e. the movement is cutting an entire line,
 	 * the buffer is in line mode, and the starting position is the last
 	 * character of the previous line.
 	 *
 	 * All commands move to the end of the range.  Adjust the start of
 	 * the range for motion commands.
 	 */
-	if (ISMOTION(vp))
+	if (ISMOTION(vp)) {
 		if (vp->m_start.cno == 0 &&
 		    (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
 			if (db_get(sp,
 			    --vp->m_start.lno, DBG_FATAL, NULL, &len))
 				return (1);
 			vp->m_start.cno = len ? len - 1 : 0;
 			F_SET(vp, VM_LMODE);
 		} else
 			--vp->m_start.cno;
+	}
 	vp->m_final = vp->m_stop;
 	return (0);
 }
Index: head/contrib/nvi/vi/v_txt.c
===================================================================
--- head/contrib/nvi/vi/v_txt.c	(revision 366308)
+++ head/contrib/nvi/vi/v_txt.c	(revision 366309)
@@ -1,2897 +1,2898 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 static int	 txt_abbrev(SCR *, TEXT *, CHAR_T *, int, int *, int *);
 static void	 txt_ai_resolve(SCR *, TEXT *, int *);
 static TEXT	*txt_backup(SCR *, TEXTH *, TEXT *, u_int32_t *);
 static int	 txt_dent(SCR *, TEXT *, int, int);
 static int	 txt_emark(SCR *, TEXT *, size_t);
 static void	 txt_err(SCR *, TEXTH *);
 static int	 txt_fc(SCR *, TEXT *, int *);
 static int	 txt_fc_col(SCR *, int, ARGS **);
 static int	 txt_hex(SCR *, TEXT *);
 static int	 txt_insch(SCR *, TEXT *, CHAR_T *, u_int);
 static int	 txt_isrch(SCR *, VICMD *, TEXT *, u_int8_t *);
 static int	 txt_map_end(SCR *);
 static int	 txt_map_init(SCR *);
 static int	 txt_margin(SCR *, TEXT *, TEXT *, int *, u_int32_t);
 static void	 txt_nomorech(SCR *);
 static void	 txt_Rresolve(SCR *, TEXTH *, TEXT *, const size_t);
 static int	 txt_resolve(SCR *, TEXTH *, u_int32_t);
 static int	 txt_showmatch(SCR *, TEXT *);
 static void	 txt_unmap(SCR *, TEXT *, u_int32_t *);
 
 /* Cursor character (space is hard to track on the screen). */
 #if defined(DEBUG) && 0
 #undef	CH_CURSOR
 #define	CH_CURSOR	'+'
 #endif
 
 /*
  * v_tcmd --
  *	Fill a buffer from the terminal for vi.
  *
  * PUBLIC: int v_tcmd(SCR *, VICMD *, ARG_CHAR_T, u_int);
  */
 int
 v_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags)
 {
 	/* Normally, we end up where we started. */
 	vp->m_final.lno = sp->lno;
 	vp->m_final.cno = sp->cno;
 
 	/* Initialize the map. */
 	if (txt_map_init(sp))
 		return (1);
 
 	/* Move to the last line. */
 	sp->lno = TMAP[0].lno;
 	sp->cno = 0;
 
 	/* Don't update the modeline for now. */
 	F_SET(sp, SC_TINPUT_INFO);
 
 	/* Set the input flags. */
 	LF_SET(TXT_APPENDEOL |
 	    TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
 	if (O_ISSET(sp, O_ALTWERASE))
 		LF_SET(TXT_ALTWERASE);
 	if (O_ISSET(sp, O_TTYWERASE))
 		LF_SET(TXT_TTYWERASE);
 
 	/* Do the input thing. */
 	if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags))
 		return (1);
 
 	/* Reenable the modeline updates. */
 	F_CLR(sp, SC_TINPUT_INFO);
 
 	/* Clean up the map. */
 	if (txt_map_end(sp))
 		return (1);
 
 	if (IS_ONELINE(sp))
 		F_SET(sp, SC_SCR_REDRAW);	/* XXX */
 
 	/* Set the cursor to the resulting position. */
 	sp->lno = vp->m_final.lno;
 	sp->cno = vp->m_final.cno;
 
 	return (0);
 }
 
 /*
  * txt_map_init
  *	Initialize the screen map for colon command-line input.
  */
 static int
 txt_map_init(SCR *sp)
 {
 	SMAP *esmp;
 	VI_PRIVATE *vip;
 
 	vip = VIP(sp);
 	if (!IS_ONELINE(sp)) {
 		/*
 		 * Fake like the user is doing input on the last line of the
 		 * screen.  This makes all of the scrolling work correctly,
 		 * and allows us the use of the vi text editing routines, not
 		 * to mention practically infinite length ex commands.
 		 *
 		 * Save the current location.
 		 */
 		vip->sv_tm_lno = TMAP->lno;
 		vip->sv_tm_soff = TMAP->soff;
 		vip->sv_tm_coff = TMAP->coff;
 		vip->sv_t_maxrows = sp->t_maxrows;
 		vip->sv_t_minrows = sp->t_minrows;
 		vip->sv_t_rows = sp->t_rows;
 
 		/*
 		 * If it's a small screen, TMAP may be small for the screen.
 		 * Fix it, filling in fake lines as we go.
 		 */
 		if (IS_SMALL(sp))
 			for (esmp =
 			    HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
 				TMAP[1].lno = TMAP[0].lno + 1;
 				TMAP[1].coff = HMAP->coff;
 				TMAP[1].soff = 1;
 			}
 
 		/* Build the fake entry. */
 		TMAP[1].lno = TMAP[0].lno + 1;
 		TMAP[1].soff = 1;
 		TMAP[1].coff = 0;
 		SMAP_FLUSH(&TMAP[1]);
 		++TMAP;
 
 		/* Reset the screen information. */
 		sp->t_rows = sp->t_minrows = ++sp->t_maxrows;
 	}
 	return (0);
 }
 
 /*
  * txt_map_end
  *	Reset the screen map for colon command-line input.
  */
 static int
 txt_map_end(SCR *sp)
 {
 	VI_PRIVATE *vip;
 	size_t cnt;
 
 	vip = VIP(sp);
 	if (!IS_ONELINE(sp)) {
 		/* Restore the screen information. */
 		sp->t_rows = vip->sv_t_rows;
 		sp->t_minrows = vip->sv_t_minrows;
 		sp->t_maxrows = vip->sv_t_maxrows;
 
 		/*
 		 * If it's a small screen, TMAP may be wrong.  Clear any
 		 * lines that might have been overwritten.
 		 */
 		if (IS_SMALL(sp)) {
 			for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
 				(void)sp->gp->scr_move(sp, cnt, 0);
 				(void)sp->gp->scr_clrtoeol(sp);
 			}
 			TMAP = HMAP + (sp->t_rows - 1);
 		} else
 			--TMAP;
 
 		/*
 		 * The map may be wrong if the user entered more than one
 		 * (logical) line.  Fix it.  If the user entered a whole
 		 * screen, this will be slow, but we probably don't care.
 		 */
 		if (!O_ISSET(sp, O_LEFTRIGHT))
 			while (vip->sv_tm_lno != TMAP->lno ||
 			    vip->sv_tm_soff != TMAP->soff)
 				if (vs_sm_1down(sp))
 					return (1);
 	}
 
 	/*
 	 * Invalidate the cursor and the line size cache, the line never
 	 * really existed.  This fixes bugs where the user searches for
 	 * the last line on the screen + 1 and the refresh routine thinks
 	 * that's where we just were.
 	 */
 	VI_SCR_CFLUSH(vip);
 	F_SET(vip, VIP_CUR_INVALID);
 
 	return (0);
 }
 
 /*
  * If doing input mapping on the colon command line, may need to unmap
  * based on the command.
  */
 #define	UNMAP_TST							\
 	FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE)
 
 /* 
  * Internally, we maintain tp->lno and tp->cno, externally, everyone uses
  * sp->lno and sp->cno.  Make them consistent as necessary.
  */
-#define	UPDATE_POSITION(sp, tp) {					\
+#define	UPDATE_POSITION(sp, tp) do {					\
 	(sp)->lno = (tp)->lno;						\
 	(sp)->cno = (tp)->cno;						\
-}
+} while (0)
 
 /*
  * v_txt --
  *	Vi text input.
  *
  * PUBLIC: int v_txt(SCR *, VICMD *, MARK *,
  * PUBLIC:    const CHAR_T *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t);
  */
 int
 v_txt(
 	SCR *sp,
 	VICMD *vp,
 	MARK *tm,		/* To MARK. */
 	const CHAR_T *lp,	/* Input line. */
 	size_t len,		/* Input line length. */
 	ARG_CHAR_T prompt,	/* Prompt to display. */
 	recno_t ai_line,	/* Line number to use for autoindent count. */
 	u_long rcount,		/* Replay count. */
 	u_int32_t flags)	/* TXT_* flags. */
 {
 	EVENT ev, *evp = NULL;	/* Current event. */
 	EVENT fc;		/* File name completion event. */
 	GS *gp;
 	TEXT *ntp, *tp;		/* Input text structures. */
 	TEXT ait;		/* Autoindent text structure. */
 	TEXT wmt = {{ 0 }};	/* Wrapmargin text structure. */
 	TEXTH *tiqh;
 	VI_PRIVATE *vip;
 	abb_t abb;		/* State of abbreviation checks. */
 	carat_t carat;		/* State of the "[^0]^D" sequences. */
 	quote_t quote;		/* State of quotation. */
 	size_t owrite, insert;	/* Temporary copies of TEXT fields. */
 	size_t margin;		/* Wrapmargin value. */
 	size_t rcol;		/* 0-N: insert offset in the replay buffer. */
 	size_t tcol;		/* Temporary column. */
 	u_int32_t ec_flags;	/* Input mapping flags. */
 #define	IS_RESTART	0x01	/* Reset the incremental search. */
 #define	IS_RUNNING	0x02	/* Incremental search turned on. */
 	u_int8_t is_flags;
 	int abcnt, ab_turnoff;	/* Abbreviation character count, switch. */
 	int filec_redraw;	/* Redraw after the file completion routine. */
 	int hexcnt;		/* Hex character count. */
 	int showmatch;		/* Showmatch set on this character. */
 	int wm_set, wm_skip;	/* Wrapmargin happened, blank skip flags. */
 	int max, tmp;
 	int nochange;
 	CHAR_T *p;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	/*
 	 * Set the input flag, so tabs get displayed correctly
 	 * and everyone knows that the text buffer is in use.
 	 */
 	F_SET(sp, SC_TINPUT);
 
 	/*
 	 * Get one TEXT structure with some initial buffer space, reusing
 	 * the last one if it's big enough.  (All TEXT bookkeeping fields
 	 * default to 0 -- text_init() handles this.)  If changing a line,
 	 * copy it into the TEXT buffer.
 	 */
 	tiqh = sp->tiq;
 	if (!TAILQ_EMPTY(tiqh)) {
 		tp = TAILQ_FIRST(tiqh);
 		if (TAILQ_NEXT(tp, q) != NULL ||
 		    tp->lb_len < (len + 32) * sizeof(CHAR_T)) {
 			text_lfree(tiqh);
 			goto newtp;
 		}
 		tp->ai = tp->insert = tp->offset = tp->owrite = 0;
 		if (lp != NULL) {
 			tp->len = len;
 			BINC_RETW(sp, tp->lb, tp->lb_len, len);
 			MEMMOVE(tp->lb, lp, len);
 		} else
 			tp->len = 0;
 	} else {
 newtp:		if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
 			return (1);
 		TAILQ_INSERT_HEAD(tiqh, tp, q);
 	}
 
 	/* Set default termination condition. */
 	tp->term = TERM_OK;
 
 	/* Set the starting line, column. */
 	tp->lno = sp->lno;
 	tp->cno = sp->cno;
 
 	/*
 	 * Set the insert and overwrite counts.  If overwriting characters,
 	 * do insertion afterward.  If not overwriting characters, assume
 	 * doing insertion.  If change is to a mark, emphasize it with an
 	 * CH_ENDMARK character.
 	 */
 	if (len) {
 		if (LF_ISSET(TXT_OVERWRITE)) {
 			tp->owrite = (tm->cno - tp->cno) + 1;
 			tp->insert = (len - tm->cno) - 1;
 		} else
 			tp->insert = len - tp->cno;
 
 		if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))
 			return (1);
 	}
 
 	/*
 	 * Many of the special cases in text input are to handle autoindent
 	 * support.  Somebody decided that it would be a good idea if "^^D"
 	 * and "0^D" deleted all of the autoindented characters.  In an editor
 	 * that takes single character input from the user, this beggars the
 	 * imagination.  Note also, "^^D" resets the next lines' autoindent,
 	 * but "0^D" doesn't.
 	 *
 	 * We assume that autoindent only happens on empty lines, so insert
 	 * and overwrite will be zero.  If doing autoindent, figure out how
 	 * much indentation we need and fill it in.  Update input column and
 	 * screen cursor as necessary.
 	 */
 	if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
 		if (v_txt_auto(sp, ai_line, NULL, 0, tp))
 			return (1);
 		tp->cno = tp->ai;
 	} else {
 		/*
 		 * The cc and S commands have a special feature -- leading
 		 * <blank> characters are handled as autoindent characters.
 		 * Beauty!
 		 */
 		if (LF_ISSET(TXT_AICHARS)) {
 			tp->offset = 0;
 			tp->ai = tp->cno;
 		} else
 			tp->offset = tp->cno;
 	}
 
 	/* If getting a command buffer from the user, there may be a prompt. */
 	if (LF_ISSET(TXT_PROMPT)) {
 		tp->lb[tp->cno++] = prompt;
 		++tp->len;
 		++tp->offset;
 	}
 
 	/*
 	 * If appending after the end-of-line, add a space into the buffer
 	 * and move the cursor right.  This space is inserted, i.e. pushed
 	 * along, and then deleted when the line is resolved.  Assumes that
 	 * the cursor is already positioned at the end of the line.  This
 	 * avoids the nastiness of having the cursor reside on a magical
 	 * column, i.e. a column that doesn't really exist.  The only down
 	 * side is that we may wrap lines or scroll the screen before it's
 	 * strictly necessary.  Not a big deal.
 	 */
 	if (LF_ISSET(TXT_APPENDEOL)) {
 		tp->lb[tp->cno] = CH_CURSOR;
 		++tp->len;
 		++tp->insert;
 		(void)vs_change(sp, tp->lno, LINE_RESET);
 	}
 
 	/*
 	 * Historic practice is that the wrapmargin value was a distance
 	 * from the RIGHT-HAND margin, not the left.  It's more useful to
 	 * us as a distance from the left-hand margin, i.e. the same as
 	 * the wraplen value.  The wrapmargin option is historic practice.
 	 * Nvi added the wraplen option so that it would be possible to
 	 * edit files with consistent margins without knowing the number of
 	 * columns in the window.
 	 *
 	 * XXX
 	 * Setting margin causes a significant performance hit.  Normally
 	 * we don't update the screen if there are keys waiting, but we
 	 * have to if margin is set, otherwise the screen routines don't
 	 * know where the cursor is.
 	 *
 	 * !!!
 	 * Abbreviated keys were affected by the wrapmargin option in the
 	 * historic 4BSD vi.  Mapped keys were usually, but sometimes not.
 	 * See the comment in vi/v_text():set_txt_std for more information.
 	 *
 	 * !!!
 	 * One more special case.  If an inserted <blank> character causes
 	 * wrapmargin to split the line, the next user entered character is
 	 * discarded if it's a <space> character.
 	 */
 	wm_set = wm_skip = 0;
 	if (LF_ISSET(TXT_WRAPMARGIN))
 		if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
 			margin = sp->cols - margin;
 		else
 			margin = O_VAL(sp, O_WRAPLEN);
 	else
 		margin = 0;
 
 	/* Initialize abbreviation checks. */
 	abcnt = ab_turnoff = 0;
 	abb = F_ISSET(gp, G_ABBREV) &&
 	    LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;
 
 	/*
 	 * Set up the dot command.  Dot commands are done by saving the actual
 	 * characters and then reevaluating them so that things like wrapmargin
 	 * can change between the insert and the replay.
 	 *
 	 * !!!
 	 * Historically, vi did not remap or reabbreviate replayed input.  (It
 	 * did beep at you if you changed an abbreviation and then replayed the
 	 * input.  We're not that compatible.)  We don't have to do anything to
 	 * avoid remapping, as we're not getting characters from the terminal
 	 * routines.  Turn the abbreviation check off.
 	 *
 	 * XXX
 	 * It would be nice if we could swallow backspaces and such, but it's
 	 * not all that easy to do.  What we can do is turn off the common
 	 * error messages during the replay.  Otherwise, when the user enters
 	 * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",
 	 * and then does a '.', they get a list of error messages after command
 	 * completion.
 	 */
 	rcol = 0;
 	if (LF_ISSET(TXT_REPLAY)) {
 		abb = AB_NOTSET;
 		LF_CLR(TXT_RECORD);
 	}
 
 	/* Other text input mode setup. */
 	quote = Q_NOTSET;
 	carat = C_NOTSET;
 	nochange = 0;
 	FL_INIT(is_flags,
 	    LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);
 	filec_redraw = hexcnt = showmatch = 0;
 
 	/* Initialize input flags. */
 	ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;
 
 	/* Refresh the screen. */
 	UPDATE_POSITION(sp, tp);
 	if (vs_refresh(sp, 1))
 		return (1);
 
 	/* If it's dot, just do it now. */
 	if (F_ISSET(vp, VC_ISDOT))
 		goto replay;
 
 	/* Get an event. */
 	evp = &ev;
 next:	if (v_event_get(sp, evp, 0, ec_flags))
 		return (1);
 
 	/*
 	 * If file completion overwrote part of the screen and nothing else has
 	 * been displayed, clean up.  We don't do this as part of the normal
 	 * message resolution because we know the user is on the colon command
 	 * line and there's no reason to enter explicit characters to continue.
 	 */
 	if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {
 		filec_redraw = 0;
 
 		fc.e_event = E_REPAINT;
 		fc.e_flno = vip->totalcount >=
 		    sp->rows ? 1 : sp->rows - vip->totalcount;
 		fc.e_tlno = sp->rows;
 		vip->linecount = vip->lcontinue = vip->totalcount = 0;
 		(void)vs_repaint(sp, &fc);
 		(void)vs_refresh(sp, 1);
 	}
 
 	/* Deal with all non-character events. */
 	switch (evp->e_event) {
 	case E_CHARACTER:
 		break;
 	case E_ERR:
 	case E_EOF:
 		F_SET(sp, SC_EXIT_FORCE);
 		return (1);
 	case E_INTERRUPT:
 		/*
 		 * !!!
 		 * Historically, <interrupt> exited the user from text input
 		 * mode or cancelled a colon command, and returned to command
 		 * mode.  It also beeped the terminal, but that seems a bit
 		 * excessive.
 		 */
 		goto k_escape;
 	case E_REPAINT:
 		if (vs_repaint(sp, &ev))
 			return (1);
 		goto next;
 	case E_WRESIZE:
 		/* <resize> interrupts the input mode. */
 		v_emsg(sp, NULL, VIM_WRESIZE);
 		goto k_escape;
 	default:
 		v_event_err(sp, evp);
 		goto k_escape;
 	}
 
 	/*
 	 * !!!
 	 * If the first character of the input is a nul, replay the previous
 	 * input.  (Historically, it's okay to replay non-existent input.)
 	 * This was not documented as far as I know, and is a great test of vi
 	 * clones.
 	 */
 	if (LF_ISSET(TXT_RECORD) && rcol == 0 && evp->e_c == '\0') {
 		if (vip->rep == NULL)
 			goto done;
 
 		abb = AB_NOTSET;
 		LF_CLR(TXT_RECORD);
 		LF_SET(TXT_REPLAY);
 		goto replay;
 	}
 
 	/*
 	 * File name completion and colon command-line editing.   We don't
 	 * have enough meta characters, so we expect people to overload
 	 * them.  If the two characters are the same, then we do file name
 	 * completion if the cursor is past the first column, and do colon
 	 * command-line editing if it's not.
 	 */
 	if (quote == Q_NOTSET) {
 		int L__cedit, L__filec;
 
 		L__cedit = L__filec = 0;
 		if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&
 		    O_STR(sp, O_CEDIT)[0] == evp->e_c)
 			L__cedit = 1;
 		if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&
 		    O_STR(sp, O_FILEC)[0] == evp->e_c)
 			L__filec = 1;
 		if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {
 			tp->term = TERM_CEDIT;
 			goto k_escape;
 		}
 		if (L__filec == 1) {
 			if (txt_fc(sp, tp, &filec_redraw))
 				goto err;
 			goto resolve;
 		}
 	}
 
 	/* Abbreviation overflow check.  See comment in txt_abbrev(). */
 #define	MAX_ABBREVIATION_EXPANSION	256
 	if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) {
 		if (++abcnt > MAX_ABBREVIATION_EXPANSION) {
 			if (v_event_flush(sp, CH_ABBREVIATED))
 				msgq(sp, M_ERR,
 "191|Abbreviation exceeded expansion limit: characters discarded");
 			abcnt = 0;
 			if (LF_ISSET(TXT_REPLAY))
 				goto done;
 			goto resolve;
 		}
 	} else
 		abcnt = 0;
 
 	/* Check to see if the character fits into the replay buffers. */
 	if (LF_ISSET(TXT_RECORD)) {
 		BINC_GOTO(sp, EVENT, vip->rep,
 		    vip->rep_len, (rcol + 1) * sizeof(EVENT));
 		vip->rep[rcol++] = *evp;
 	}
 
 replay:	if (LF_ISSET(TXT_REPLAY)) {
 		if (rcol == vip->rep_cnt)
 			goto k_escape;
 		evp = vip->rep + rcol++;
 	}
 
 	/* Wrapmargin check for leading space. */
 	if (wm_skip) {
 		wm_skip = 0;
 		if (evp->e_c == ' ')
 			goto resolve;
 	}
 
 	/* If quoted by someone else, simply insert the character. */
 	if (F_ISSET(&evp->e_ch, CH_QUOTED))
 		goto insq_ch;
 
 	/*
 	 * !!!
 	 * If this character was quoted by a K_VLNEXT, replace the placeholder
 	 * (a carat) with the new character.  We've already adjusted the cursor
 	 * because it has to appear on top of the placeholder character.
 	 * Historic practice.
 	 *
 	 * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"
 	 * doesn't perform an abbreviation.  Special case, ^V^J (not ^V^M) is
 	 * the same as ^J, historically.
 	 */
 	if (quote == Q_VTHIS) {
 		FL_CLR(ec_flags, EC_QUOTED);
 		if (LF_ISSET(TXT_MAPINPUT))
 			FL_SET(ec_flags, EC_MAPINPUT);
 
 		if (evp->e_value != K_NL) {
 			quote = Q_NOTSET;
 			goto insl_ch;
 		}
 		quote = Q_NOTSET;
 	}
 
 	/*
 	 * !!!
 	 * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:
 	 * this test delimits the value by any non-hex character.  Offset by
 	 * one, we use 0 to mean that we've found <CH_HEX>.
 	 */
 	if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) {
 		hexcnt = 0;
 		if (txt_hex(sp, tp))
 			goto err;
 	}
 
 	switch (evp->e_value) {
 	case K_CR:				/* Carriage return. */
 	case K_NL:				/* New line. */
 		/* Return in script windows and the command line. */
 k_cr:		if (LF_ISSET(TXT_CR)) {
 			/*
 			 * If this was a map, we may have not displayed
 			 * the line.  Display it, just in case.
 			 *
 			 * If a script window and not the colon line,
 			 * push a <cr> so it gets executed.
 			 */
 			if (LF_ISSET(TXT_INFOLINE)) {
 				if (vs_change(sp, tp->lno, LINE_RESET))
 					goto err;
 			} else if (F_ISSET(sp, SC_SCRIPT))
 				(void)v_event_push(sp, NULL, L("\r"), 1, CH_NOMAP);
 
 			/* Set term condition: if empty. */
 			if (tp->cno <= tp->offset)
 				tp->term = TERM_CR;
 			/*
 			 * Set term condition: if searching incrementally and
 			 * the user entered a pattern, return a completed
 			 * search, regardless if the entire pattern was found.
 			 */
 			if (FL_ISSET(is_flags, IS_RUNNING) &&
 			    tp->cno >= tp->offset + 1)
 				tp->term = TERM_SEARCH;
 
 			goto k_escape;
 		}
 
-#define	LINE_RESOLVE {							\
+#define	LINE_RESOLVE do {						\
 		/*							\
 		 * Handle abbreviations.  If there was one, discard the	\
 		 * replay characters.					\
 		 */							\
 		if (abb == AB_INWORD &&					\
 		    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {	\
 			if (txt_abbrev(sp, tp, &evp->e_c,		\
 			    LF_ISSET(TXT_INFOLINE), &tmp,		\
 			    &ab_turnoff))				\
 				goto err;				\
 			if (tmp) {					\
 				if (LF_ISSET(TXT_RECORD))		\
 					rcol -= tmp + 1;		\
 				goto resolve;				\
 			}						\
 		}							\
 		if (abb != AB_NOTSET)					\
 			abb = AB_NOTWORD;				\
 		if (UNMAP_TST)						\
 			txt_unmap(sp, tp, &ec_flags);			\
 		/*							\
 		 * Delete any appended cursor.  It's possible to get in	\
 		 * situations where TXT_APPENDEOL is set but tp->insert	\
 		 * is 0 when using the R command and all the characters	\
 		 * are tp->owrite characters.				\
 		 */							\
 		if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) {	\
 			--tp->len;					\
 			--tp->insert;					\
 		}							\
-}
+} while (0)
 		LINE_RESOLVE;
 
 		/*
 		 * Save the current line information for restoration in
 		 * txt_backup(), and set the line final length.
 		 */
 		tp->sv_len = tp->len;
 		tp->sv_cno = tp->cno;
 		tp->len = tp->cno;
 
 		/* Update the old line. */
 		if (vs_change(sp, tp->lno, LINE_RESET))
 			goto err;
 
 		/*
 		 * Historic practice, when the autoindent edit option was set,
 		 * was to delete <blank> characters following the inserted
 		 * newline.  This affected the 'R', 'c', and 's' commands; 'c'
 		 * and 's' retained the insert characters only, 'R' moved the
 		 * overwrite and insert characters into the next TEXT structure.
 		 * We keep track of the number of characters erased for the 'R'
 		 * command so that the final resolution of the line is correct.
 		 */
 		tp->R_erase = 0;
 		owrite = tp->owrite;
 		insert = tp->insert;
 		if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
 			for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p);
 			    ++p, --owrite, ++tp->R_erase);
 			if (owrite == 0)
 				for (; insert > 0 && isblank(*p);
 				    ++p, ++tp->R_erase, --insert);
 		} else {
 			p = tp->lb + tp->cno + owrite;
 			if (O_ISSET(sp, O_AUTOINDENT))
 				for (; insert > 0 &&
 				    isblank(*p); ++p, --insert);
 			owrite = 0;
 		}
 
 		/*
 		 * !!!
 		 * Create a new line and insert the new TEXT into the queue.
 		 * DON'T insert until the old line has been updated, or the
 		 * inserted line count in line.c:db_get() will be wrong.
 		 */
 		if ((ntp = text_init(sp, p,
 		    insert + owrite, insert + owrite + 32)) == NULL)
 			goto err;
 		TAILQ_INSERT_TAIL(sp->tiq, ntp, q);
 
 		/* Set up bookkeeping for the new line. */
 		ntp->insert = insert;
 		ntp->owrite = owrite;
 		ntp->lno = tp->lno + 1;
 
 		/*
 		 * Reset the autoindent line value.  0^D keeps the autoindent
 		 * line from changing, ^D changes the level, even if there were
 		 * no characters in the old line.  Note, if using the current
 		 * tp structure, use the cursor as the length, the autoindent
 		 * characters may have been erased.
 		 */
 		if (LF_ISSET(TXT_AUTOINDENT)) {
 			if (nochange) {
 				nochange = 0;
 				if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))
 					goto err;
 				FREE_SPACEW(sp, ait.lb, ait.lb_len);
 			} else
 				if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))
 					goto err;
 			carat = C_NOTSET;
 		}
 
 		/* Reset the cursor. */
 		ntp->cno = ntp->ai;
 
 		/*
 		 * If we're here because wrapmargin was set and we've broken a
 		 * line, there may be additional information (i.e. the start of
 		 * a line) in the wmt structure.
 		 */
 		if (wm_set) {
 			if (wmt.offset != 0 ||
 			    wmt.owrite != 0 || wmt.insert != 0) {
 #define	WMTSPACE	wmt.offset + wmt.owrite + wmt.insert
 				BINC_GOTOW(sp, ntp->lb,
 				    ntp->lb_len, ntp->len + WMTSPACE + 32);
 				MEMMOVE(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);
 				ntp->len += WMTSPACE;
 				ntp->cno += wmt.offset;
 				ntp->owrite = wmt.owrite;
 				ntp->insert = wmt.insert;
 			}
 			wm_set = 0;
 		}
 
 		/* New lines are TXT_APPENDEOL. */
 		if (ntp->owrite == 0 && ntp->insert == 0) {
 			BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
 			LF_SET(TXT_APPENDEOL);
 			ntp->lb[ntp->cno] = CH_CURSOR;
 			++ntp->insert;
 			++ntp->len;
 		}
 
 		/* Swap old and new TEXT's, and update the new line. */
 		tp = ntp;
 		if (vs_change(sp, tp->lno, LINE_INSERT))
 			goto err;
 
 		goto resolve;
 	case K_ESCAPE:				/* Escape. */
 		if (!LF_ISSET(TXT_ESCAPE))
 			goto ins_ch;
 
 		/* If we have a count, start replaying the input. */
 		if (rcount > 1) {
 			--rcount;
 
 			vip->rep_cnt = rcol;
 			rcol = 0;
 			abb = AB_NOTSET;
 			LF_CLR(TXT_RECORD);
 			LF_SET(TXT_REPLAY);
 
 			/*
 			 * Some commands (e.g. 'o') need a <newline> for each
 			 * repetition.
 			 */
 			if (LF_ISSET(TXT_ADDNEWLINE))
 				goto k_cr;
 
 			/*
 			 * The R command turns into the 'a' command after the
 			 * first repetition.
 			 */
 			if (LF_ISSET(TXT_REPLACE)) {
 				tp->insert = tp->owrite;
 				tp->owrite = 0;
 				LF_CLR(TXT_REPLACE);
 			}
 			goto replay;
 		}
 
 		/* Set term condition: if empty. */
 		if (tp->cno <= tp->offset)
 			tp->term = TERM_ESC;
 		/*
 		 * Set term condition: if searching incrementally and the user
 		 * entered a pattern, return a completed search, regardless if
 		 * the entire pattern was found.
 		 */
 		if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)
 			tp->term = TERM_SEARCH;
 
 k_escape:	LINE_RESOLVE;
 
 		/*
 		 * Clean up for the 'R' command, restoring overwrite
 		 * characters, and making them into insert characters.
 		 */
 		if (LF_ISSET(TXT_REPLACE))
 			txt_Rresolve(sp, sp->tiq, tp, len);
 
 		/*
 		 * If there are any overwrite characters, copy down
 		 * any insert characters, and decrement the length.
 		 */
 		if (tp->owrite) {
 			if (tp->insert)
 				MEMMOVE(tp->lb + tp->cno,
 				    tp->lb + tp->cno + tp->owrite, tp->insert);
 			tp->len -= tp->owrite;
 		}
 
 		/*
 		 * Optionally resolve the lines into the file.  If not
 		 * resolving the lines into the file, end the line with
 		 * a nul.  If the line is empty, then set the length to
 		 * 0, the termination condition has already been set.
 		 *
 		 * XXX
 		 * This is wrong, should pass back a length.
 		 */
 		if (LF_ISSET(TXT_RESOLVE)) {
 			if (txt_resolve(sp, sp->tiq, flags))
 				goto err;
 		} else {
 			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
 			tp->lb[tp->len] = '\0';
 		}
 
 		/*
 		 * Set the return cursor position to rest on the last
 		 * inserted character.
 		 */
 		if (tp->cno != 0)
 			--tp->cno;
 
 		/* Update the last line. */
 		if (vs_change(sp, tp->lno, LINE_RESET))
 			return (1);
 		goto done;
 	case K_CARAT:			/* Delete autoindent chars. */
 		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
 			carat = C_CARATSET;
 		goto ins_ch;
 	case K_ZERO:			/* Delete autoindent chars. */
 		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
 			carat = C_ZEROSET;
 		goto ins_ch;
 	case K_CNTRLD:			/* Delete autoindent char. */
 		/*
 		 * If in the first column or no characters to erase, ignore
 		 * the ^D (this matches historic practice).  If not doing
 		 * autoindent or already inserted non-ai characters, it's a
 		 * literal.  The latter test is done in the switch, as the
 		 * CARAT forms are N + 1, not N.
 		 */
 		if (!LF_ISSET(TXT_AUTOINDENT))
 			goto ins_ch;
 		if (tp->cno == 0)
 			goto resolve;
 
 		switch (carat) {
 		case C_CARATSET:	/* ^^D */
 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
 				goto ins_ch;
 
 			/* Save the ai string for later. */
 			ait.lb = NULL;
 			ait.lb_len = 0;
 			BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai);
 			MEMMOVE(ait.lb, tp->lb, tp->ai);
 			ait.ai = ait.len = tp->ai;
 
 			carat = C_NOTSET;
 			nochange = 1;
 			goto leftmargin;
 		case C_ZEROSET:		/* 0^D */
 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
 				goto ins_ch;
 
 			carat = C_NOTSET;
 leftmargin:		tp->lb[tp->cno - 1] = ' ';
 			tp->owrite += tp->cno - tp->offset;
 			tp->ai = 0;
 			tp->cno = tp->offset;
 			break;
 		case C_NOTSET:		/* ^D */
 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)
 				goto ins_ch;
 
 			(void)txt_dent(sp, tp, O_SHIFTWIDTH, 0);
 			break;
 		default:
 			abort();
 		}
 		break;
 	case K_VERASE:			/* Erase the last character. */
 		/* If can erase over the prompt, return. */
 		if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {
 			tp->term = TERM_BS;
 			goto done;
 		}
 
 		/*
 		 * If at the beginning of the line, try and drop back to a
 		 * previously inserted line.
 		 */
 		if (tp->cno == 0) {
 			if ((ntp =
 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
 				goto err;
 			tp = ntp;
 			break;
 		}
 
 		/* If nothing to erase, bell the user. */
 		if (tp->cno <= tp->offset) {
 			if (!LF_ISSET(TXT_REPLAY))
 				txt_nomorech(sp);
 			break;
 		}
 
 		/* Drop back one character. */
 		--tp->cno;
 
 		/*
 		 * Historically, vi didn't replace the erased characters with
 		 * <blank>s, presumably because it's easier to fix a minor
 		 * typing mistake and continue on if the previous letters are
 		 * already there.  This is a problem for incremental searching,
 		 * because the user can no longer tell where they are in the
 		 * colon command line because the cursor is at the last search
 		 * point in the screen.  So, if incrementally searching, erase
 		 * the erased characters from the screen.
 		 */
 		if (FL_ISSET(is_flags, IS_RUNNING))
 			tp->lb[tp->cno] = ' ';
 
 		/*
 		 * Increment overwrite, decrement ai if deleted.
 		 *
 		 * !!!
 		 * Historic vi did not permit users to use erase characters
 		 * to delete autoindent characters.  We do.  Eat hot death,
 		 * POSIX.
 		 */
 		++tp->owrite;
 		if (tp->cno < tp->ai)
 			--tp->ai;
 
 		/* Reset if we deleted an incremental search character. */
 		if (FL_ISSET(is_flags, IS_RUNNING))
 			FL_SET(is_flags, IS_RESTART);
 		break;
 	case K_VWERASE:			/* Skip back one word. */
 		/*
 		 * If at the beginning of the line, try and drop back to a
 		 * previously inserted line.
 		 */
 		if (tp->cno == 0) {
 			if ((ntp =
 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
 				goto err;
 			tp = ntp;
 		}
 
 		/*
 		 * If at offset, nothing to erase so bell the user.
 		 */
 		if (tp->cno <= tp->offset) {
 			if (!LF_ISSET(TXT_REPLAY))
 				txt_nomorech(sp);
 			break;
 		}
 
 		/*
 		 * The first werase goes back to any autoindent column and the
 		 * second werase goes back to the offset.
 		 *
 		 * !!!
 		 * Historic vi did not permit users to use erase characters to
 		 * delete autoindent characters.
 		 */
 		if (tp->ai && tp->cno > tp->ai)
 			max = tp->ai;
 		else {
 			tp->ai = 0;
 			max = tp->offset;
 		}
 
 		/* Skip over trailing space characters. */
 		while (tp->cno > max && ISBLANK(tp->lb[tp->cno - 1])) {
 			--tp->cno;
 			++tp->owrite;
 		}
 		if (tp->cno == max)
 			break;
 		/*
 		 * There are three types of word erase found on UNIX systems.
 		 * They can be identified by how the string /a/b/c is treated
 		 * -- as 1, 3, or 6 words.  Historic vi had two classes of
 		 * characters, and strings were delimited by them and
 		 * <blank>'s, so, 6 words.  The historic tty interface used
 		 * <blank>'s to delimit strings, so, 1 word.  The algorithm
 		 * offered in the 4.4BSD tty interface (as stty altwerase)
 		 * treats it as 3 words -- there are two classes of
 		 * characters, and strings are delimited by them and
 		 * <blank>'s.  The difference is that the type of the first
 		 * erased character erased is ignored, which is exactly right
 		 * when erasing pathname components.  The edit options
 		 * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty
 		 * interface and the historic tty driver behavior,
 		 * respectively, and the default is the same as the historic
 		 * vi behavior.
 		 *
 		 * Overwrite erased characters if doing incremental search;
 		 * see comment above.
 		 */
 		if (LF_ISSET(TXT_TTYWERASE))
 			while (tp->cno > max) {
 				if (ISBLANK(tp->lb[tp->cno - 1]))
 					break;
 				--tp->cno;
 				++tp->owrite;
 				if (FL_ISSET(is_flags, IS_RUNNING))
 					tp->lb[tp->cno] = ' ';
 			}
 		else {
 			if (LF_ISSET(TXT_ALTWERASE)) {
 				--tp->cno;
 				++tp->owrite;
 				if (FL_ISSET(is_flags, IS_RUNNING))
 					tp->lb[tp->cno] = ' ';
 			}
 			if (tp->cno > max)
 				tmp = inword(tp->lb[tp->cno - 1]);
 			while (tp->cno > max) {
 				if (tmp != inword(tp->lb[tp->cno - 1])
 				    || ISBLANK(tp->lb[tp->cno - 1]))
 					break;
 				--tp->cno;
 				++tp->owrite;
 				if (FL_ISSET(is_flags, IS_RUNNING))
 					tp->lb[tp->cno] = ' ';
 			}
 		}
 
 		/* Reset if we deleted an incremental search character. */
 		if (FL_ISSET(is_flags, IS_RUNNING))
 			FL_SET(is_flags, IS_RESTART);
 		break;
 	case K_VKILL:			/* Restart this line. */
 		/*
 		 * !!!
 		 * If at the beginning of the line, try and drop back to a
 		 * previously inserted line.  Historic vi did not permit
 		 * users to go back to previous lines.
 		 */
 		if (tp->cno == 0) {
 			if ((ntp =
 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
 				goto err;
 			tp = ntp;
 		}
 
 		/* If at offset, nothing to erase so bell the user. */
 		if (tp->cno <= tp->offset) {
 			if (!LF_ISSET(TXT_REPLAY))
 				txt_nomorech(sp);
 			break;
 		}
 
 		/*
 		 * First kill goes back to any autoindent and second kill goes
 		 * back to the offset.
 		 *
 		 * !!!
 		 * Historic vi did not permit users to use erase characters to
 		 * delete autoindent characters.
 		 */
 		if (tp->ai && tp->cno > tp->ai)
 			max = tp->ai;
 		else {
 			tp->ai = 0;
 			max = tp->offset;
 		}
 		tp->owrite += tp->cno - max;
 
 		/*
 		 * Overwrite erased characters if doing incremental search;
 		 * see comment above.
 		 */
 		if (FL_ISSET(is_flags, IS_RUNNING))
 			do {
 				tp->lb[--tp->cno] = ' ';
 			} while (tp->cno > max);
 		else
 			tp->cno = max;
 
 		/* Reset if we deleted an incremental search character. */
 		if (FL_ISSET(is_flags, IS_RUNNING))
 			FL_SET(is_flags, IS_RESTART);
 		break;
 	case K_CNTRLT:			/* Add autoindent characters. */
 		if (!LF_ISSET(TXT_CNTRLT))
 			goto ins_ch;
 		if (txt_dent(sp, tp, O_SHIFTWIDTH, 1))
 			goto err;
 		goto ebuf_chk;
 	case K_VLNEXT:			/* Quote next character. */
 		evp->e_c = '^';
 		quote = Q_VNEXT;
 		/*
 		 * Turn on the quote flag so that the underlying routines
 		 * quote the next character where it's possible. Turn off
 		 * the input mapbiting flag so that we don't remap the next
 		 * character.
 		 */
 		FL_SET(ec_flags, EC_QUOTED);
 		FL_CLR(ec_flags, EC_MAPINPUT);
 
 		/*
 		 * !!!
 		 * Skip the tests for abbreviations, so ":ab xa XA",
 		 * "ixa^V<space>" doesn't perform the abbreviation.
 		 */
 		goto insl_ch;
 	case K_HEXCHAR:
 		hexcnt = 1;
 		goto insq_ch;
 	case K_TAB:
 		if (sp->showmode != SM_COMMAND && quote != Q_VTHIS &&
 		    O_ISSET(sp, O_EXPANDTAB)) {
 			if (txt_dent(sp, tp, O_TABSTOP, 1))
 				goto err;
 			goto ebuf_chk;
 		}
 		goto insq_ch;
 	default:			/* Insert the character. */
 		if (LF_ISSET(TXT_SHOWMATCH)) {
 			CHAR_T *match_chars, *cp;
 
 			match_chars = VIP(sp)->mcs;
 			cp = STRCHR(match_chars, evp->e_c);
 			if (cp != NULL && (cp - match_chars) & 1)
 				showmatch = 1;
 		}
 ins_ch:		/*
 		 * Historically, vi eliminated nul's out of hand.  If the
 		 * beautify option was set, it also deleted any unknown
 		 * ASCII value less than space (040) and the del character
 		 * (0177), except for tabs.  Unknown is a key word here.
 		 * Most vi documentation claims that it deleted everything
 		 * but <tab>, <nl> and <ff>, as that's what the original
 		 * 4BSD documentation said.  This is obviously wrong,
 		 * however, as <esc> would be included in that list.  What
 		 * we do is eliminate any unquoted, iscntrl() character that
 		 * wasn't a replay and wasn't handled specially, except
 		 * <tab> or <ff>.
 		 */
 		if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) &&
 		    evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {
 			msgq(sp, M_BERR,
 			    "192|Illegal character; quote to enter");
 			if (LF_ISSET(TXT_REPLAY))
 				goto done;
 			break;
 		}
 
 insq_ch:	/*
 		 * If entering a non-word character after a word, check for
 		 * abbreviations.  If there was one, discard replay characters.
 		 * If entering a blank character, check for unmap commands,
 		 * as well.
 		 */
 		if (!inword(evp->e_c)) {
 			if (abb == AB_INWORD &&
 			    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {
 				if (txt_abbrev(sp, tp, &evp->e_c,
 				    LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
 					goto err;
 				if (tmp) {
 					if (LF_ISSET(TXT_RECORD))
 						rcol -= tmp + 1;
 					goto resolve;
 				}
 			}
 			if (isblank(evp->e_c) && UNMAP_TST)
 				txt_unmap(sp, tp, &ec_flags);
 		}
 		if (abb != AB_NOTSET)
 			abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD;
 
 insl_ch:	if (txt_insch(sp, tp, &evp->e_c, flags))
 			goto err;
 
 		/*
 		 * If we're using K_VLNEXT to quote the next character, then
 		 * we want the cursor to position itself on the ^ placeholder
 		 * we're displaying, to match historic practice.
 		 */
 		if (quote == Q_VNEXT) {
 			--tp->cno;
 			++tp->owrite;
 		}
 
 		/*
 		 * !!!
 		 * Translate "<CH_HEX>[isxdigit()]*" to a character with
 		 * a hex value: this test delimits the value by the max
 		 * number of hex bytes.  Offset by one, we use 0 to mean
 		 * that we've found <CH_HEX>.
 		 */
 		if (hexcnt != 0 && hexcnt++ == 3) {
 			hexcnt = 0;
 			if (txt_hex(sp, tp))
 				goto err;
 		}
 
 		/*
 		 * Check to see if we've crossed the margin.
 		 *
 		 * !!!
 		 * In the historic vi, the wrapmargin value was figured out
 		 * using the display widths of the characters, i.e. <tab>
 		 * characters were counted as two characters if the list edit
 		 * option is set, but as the tabstop edit option number of
 		 * characters otherwise.  That's what the vs_column() function
 		 * gives us, so we use it.
 		 */
 		if (margin != 0) {
 			if (vs_column(sp, &tcol))
 				goto err;
 			if (tcol >= margin) {
 				if (txt_margin(sp, tp, &wmt, &tmp, flags))
 					goto err;
 				if (tmp) {
 					if (isblank(evp->e_c))
 						wm_skip = 1;
 					wm_set = 1;
 					goto k_cr;
 				}
 			}
 		}
 
 		/*
 		 * If we've reached the end of the buffer, then we need to
 		 * switch into insert mode.  This happens when there's a
 		 * change to a mark and the user puts in more characters than
 		 * the length of the motion.
 		 */
 ebuf_chk:	if (tp->cno >= tp->len) {
 			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
 			LF_SET(TXT_APPENDEOL);
 
 			tp->lb[tp->cno] = CH_CURSOR;
 			++tp->insert;
 			++tp->len;
 		}
 
 		/* Step the quote state forward. */
 		if (quote == Q_VNEXT)
 			quote = Q_VTHIS;
 		break;
 	}
 
 #ifdef DEBUG
 	if (tp->cno + tp->insert + tp->owrite != tp->len) {
 		msgq(sp, M_ERR,
 		    "len %zu != cno: %zu ai: %zu insert %zu overwrite %zu",
 		    tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);
 		if (LF_ISSET(TXT_REPLAY))
 			goto done;
 		tp->len = tp->cno + tp->insert + tp->owrite;
 	}
 #endif
 
 resolve:/*
 	 * 1: If we don't need to know where the cursor really is and we're
 	 *    replaying text, keep going.
 	 */
 	if (margin == 0 && LF_ISSET(TXT_REPLAY))
 		goto replay;
 
 	/*
 	 * 2: Reset the line.  Don't bother unless we're about to wait on
 	 *    a character or we need to know where the cursor really is.
 	 *    We have to do this before showing matching characters so the
 	 *    user can see what they're matching.
 	 */
 	if ((margin != 0 || !KEYS_WAITING(sp)) &&
 	    vs_change(sp, tp->lno, LINE_RESET))
 		return (1);
 
 	/*
 	 * 3: If there aren't keys waiting, display the matching character.
 	 *    We have to do this before resolving any messages, otherwise
 	 *    the error message from a missing match won't appear correctly.
 	 */
 	if (showmatch) {
 		if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))
 			return (1);
 		showmatch = 0;
 	}
 
 	/*
 	 * 4: If there have been messages and we're not editing on the colon
 	 *    command line or doing file name completion, resolve them.
 	 */
 	if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&
 	    !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw &&
 	    vs_resolve(sp, NULL, 0))
 		return (1);
 
 	/*
 	 * 5: Refresh the screen if we're about to wait on a character or we
 	 *    need to know where the cursor really is.
 	 */
 	if (margin != 0 || !KEYS_WAITING(sp)) {
 		UPDATE_POSITION(sp, tp);
 		if (vs_refresh(sp, margin != 0))
 			return (1);
 	}
 
 	/* 6: Proceed with the incremental search. */
 	if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))
 		return (1);
 
 	/* 7: Next character... */
 	if (LF_ISSET(TXT_REPLAY))
 		goto replay;
 	goto next;
 
 done:	/* Leave input mode. */
 	F_CLR(sp, SC_TINPUT);
 
 	/* If recording for playback, save it. */
 	if (LF_ISSET(TXT_RECORD))
 		vip->rep_cnt = rcol;
 
 	/*
 	 * If not working on the colon command line, set the final cursor
 	 * position.
 	 */
 	if (!F_ISSET(sp, SC_TINPUT_INFO)) {
 		vp->m_final.lno = tp->lno;
 		vp->m_final.cno = tp->cno;
 	}
 	return (0);
 
 err:
 alloc_err:
 	F_CLR(sp, SC_TINPUT);
 	txt_err(sp, sp->tiq);
 	return (1);
 }
 
 /*
  * txt_abbrev --
  *	Handle abbreviations.
  */
 static int
 txt_abbrev(SCR *sp, TEXT *tp, CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp)
 {
 	VI_PRIVATE *vip;
 	CHAR_T ch, *p;
 	SEQ *qp;
 	size_t len, off;
 
 	/* Check to make sure we're not at the start of an append. */
 	*didsubp = 0;
 	if (tp->cno == tp->offset)
 		return (0);
 
 	vip = VIP(sp);
 
 	/*
 	 * Find the start of the "word".
 	 *
 	 * !!!
 	 * We match historic practice, which, as far as I can tell, had an
 	 * off-by-one error.  The way this worked was that when the inserted
 	 * text switched from a "word" character to a non-word character,
 	 * vi would check for possible abbreviations.  It would then take the
 	 * type (i.e. word/non-word) of the character entered TWO characters
 	 * ago, and move backward in the text until reaching a character that
 	 * was not that type, or the beginning of the insert, the line, or
 	 * the file.  For example, in the string "abc<space>", when the <space>
 	 * character triggered the abbreviation check, the type of the 'b'
 	 * character was used for moving through the string.  Maybe there's a
 	 * reason for not using the first (i.e. 'c') character, but I can't
 	 * think of one.
 	 *
 	 * Terminate at the beginning of the insert or the character after the
 	 * offset character -- both can be tested for using tp->offset.
 	 */
 	off = tp->cno - 1;			/* Previous character. */
 	p = tp->lb + off;
 	len = 1;				/* One character test. */
 	if (off == tp->offset || isblank(p[-1]))
 		goto search;
 	if (inword(p[-1]))			/* Move backward to change. */
 		for (;;) {
 			--off; --p; ++len;
 			if (off == tp->offset || !inword(p[-1]))
 				break;
 		}
 	else
 		for (;;) {
 			--off; --p; ++len;
 			if (off == tp->offset ||
 			    inword(p[-1]) || isblank(p[-1]))
 				break;
 		}
 
 	/*
 	 * !!!
 	 * Historic vi exploded abbreviations on the command line.  This has
 	 * obvious problems in that unabbreviating the string can be extremely
 	 * tricky, particularly if the string has, say, an embedded escape
 	 * character.  Personally, I think it's a stunningly bad idea.  Other
 	 * examples of problems this caused in historic vi are:
 	 *	:ab foo bar
 	 *	:ab foo baz
 	 * results in "bar" being abbreviated to "baz", which wasn't what the
 	 * user had in mind at all.  Also, the commands:
 	 *	:ab foo bar
 	 *	:unab foo<space>
 	 * resulted in an error message that "bar" wasn't mapped.  Finally,
 	 * since the string was already exploded by the time the unabbreviate
 	 * command got it, all it knew was that an abbreviation had occurred.
 	 * Cleverly, it checked the replacement string for its unabbreviation
 	 * match, which meant that the commands:
 	 *	:ab foo1 bar
 	 *	:ab foo2 bar
 	 *	:unab foo2
 	 * unabbreviate "foo1", and the commands:
 	 *	:ab foo bar
 	 *	:ab bar baz
 	 * unabbreviate "foo"!
 	 *
 	 * Anyway, people neglected to first ask my opinion before they wrote
 	 * macros that depend on this stuff, so, we make this work as follows.
 	 * When checking for an abbreviation on the command line, if we get a
 	 * string which is <blank> terminated and which starts at the beginning
 	 * of the line, we check to see it is the abbreviate or unabbreviate
 	 * commands.  If it is, turn abbreviations off and return as if no
 	 * abbreviation was found.  Note also, minor trickiness, so that if
 	 * the user erases the line and starts another command, we turn the
 	 * abbreviations back on.
 	 *
 	 * This makes the layering look like a Nachos Supreme.
 	 */
-search:	if (isinfoline)
+search:	if (isinfoline) {
 		if (off == tp->ai || off == tp->offset)
 			if (ex_is_abbrev(p, len)) {
 				*turnoffp = 1;
 				return (0);
 			} else
 				*turnoffp = 0;
 		else
 			if (*turnoffp)
 				return (0);
+	}
 
 	/* Check for any abbreviations. */
 	if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
 		return (0);
 
 	/*
 	 * Push the abbreviation onto the tty stack.  Historically, characters
 	 * resulting from an abbreviation expansion were themselves subject to
 	 * map expansions, O_SHOWMATCH matching etc.  This means the expanded
 	 * characters will be re-tested for abbreviations.  It's difficult to
 	 * know what historic practice in this case was, since abbreviations
 	 * were applied to :colon command lines, so entering abbreviations that
 	 * looped was tricky, although possible.  In addition, obvious loops
 	 * didn't work as expected.  (The command ':ab a b|ab b c|ab c a' will
 	 * silently only implement and/or display the last abbreviation.)
 	 *
 	 * This implementation doesn't recover well from such abbreviations.
 	 * The main input loop counts abbreviated characters, and, when it
 	 * reaches a limit, discards any abbreviated characters on the queue.
 	 * It's difficult to back up to the original position, as the replay
 	 * queue would have to be adjusted, and the line state when an initial
 	 * abbreviated character was received would have to be saved.
 	 */
 	ch = *pushcp;
 	if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED))
 		return (1);
 	if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED))
 		return (1);
 
 	/*
 	 * If the size of the abbreviation is larger than or equal to the size
 	 * of the original text, move to the start of the replaced characters,
 	 * and add their length to the overwrite count.
 	 *
 	 * If the abbreviation is smaller than the original text, we have to
 	 * delete the additional overwrite characters and copy down any insert
 	 * characters.
 	 */
 	tp->cno -= len;
 	if (qp->olen >= len)
 		tp->owrite += len;
 	else {
 		if (tp->insert)
 			MEMMOVE(tp->lb + tp->cno + qp->olen,
 			    tp->lb + tp->cno + tp->owrite + len, tp->insert);
 		tp->owrite += qp->olen;
 		tp->len -= len - qp->olen;
 	}
 
 	/*
 	 * We return the length of the abbreviated characters.  This is so
 	 * the calling routine can replace the replay characters with the
 	 * abbreviation.  This means that subsequent '.' commands will produce
 	 * the same text, regardless of intervening :[un]abbreviate commands.
 	 * This is historic practice.
 	 */
 	*didsubp = len;
 	return (0);
 }
 
 /*
  * txt_unmap --
  *	Handle the unmap command.
  */
 static void
 txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp)
 {
 	size_t len, off;
 	CHAR_T *p;
 
 	/* Find the beginning of this "word". */
 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
 		if (isblank(*p)) {
 			++p;
 			break;
 		}
 		++len;
 		if (off == tp->ai || off == tp->offset)
 			break;
 	}
 
 	/*
 	 * !!!
 	 * Historic vi exploded input mappings on the command line.  See the
 	 * txt_abbrev() routine for an explanation of the problems inherent
 	 * in this.
 	 *
 	 * We make this work as follows.  If we get a string which is <blank>
 	 * terminated and which starts at the beginning of the line, we check
 	 * to see it is the unmap command.  If it is, we return that the input
 	 * mapping should be turned off.  Note also, minor trickiness, so that
 	 * if the user erases the line and starts another command, we go ahead
 	 * an turn mapping back on.
 	 */
 	if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
 		FL_CLR(*ec_flagsp, EC_MAPINPUT);
 	else
 		FL_SET(*ec_flagsp, EC_MAPINPUT);
 }
 
 /*
  * txt_ai_resolve --
  *	When a line is resolved by <esc>, review autoindent characters.
  */
 static void
 txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp)
 {
 	u_long ts;
 	int del;
 	size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
 	CHAR_T *p;
 
 	*changedp = 0;
 
 	/*
 	 * If the line is empty, has an offset, or no autoindent
 	 * characters, we're done.
 	 */
 	if (!tp->len || tp->offset || !tp->ai)
 		return;
 
 	/*
 	 * If the length is less than or equal to the autoindent
 	 * characters, delete them.
 	 */
 	if (tp->len <= tp->ai) {
 		tp->ai = tp->cno = tp->len = 0;
 		return;
 	}
 
 	/*
 	 * The autoindent characters plus any leading <blank> characters
 	 * in the line are resolved into the minimum number of characters.
 	 * Historic practice.
 	 */
 	ts = O_VAL(sp, O_TABSTOP);
 
 	/* Figure out the last <blank> screen column. */
 	for (p = tp->lb, scno = 0, len = tp->len,
 	    spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
 		if (*p == '\t') {
 			if (spaces)
 				tab_after_sp = 1;
 			scno += COL_OFF(scno, ts);
 		} else {
 			++spaces;
 			++scno;
 		}
 
 	/*
 	 * If there are no spaces, or no tabs after spaces and less than
 	 * ts spaces, it's already minimal.
 	 * Keep analysing if expandtab is set.
 	 */
 	if ((!spaces || (!tab_after_sp && spaces < ts)) &&
 	    !O_ISSET(sp, O_EXPANDTAB))
 		return;
 
 	/* Count up spaces/tabs needed to get to the target. */
 	cno = 0;
 	tabs = 0;
 	if (!O_ISSET(sp, O_EXPANDTAB)) {
 		for (; cno + COL_OFF(cno, ts) <= scno; ++tabs)
 			cno += COL_OFF(cno, ts);
 	}
 	spaces = scno - cno;
 
 	/*
 	 * Figure out how many characters we're dropping -- if we're not
 	 * dropping any, it's already minimal, we're done.
 	 */
 	old = p - tp->lb;
 	new = spaces + tabs;
 	if (old == new)
 		return;
 
 	/* Shift the rest of the characters down, adjust the counts. */
 	del = old - new;
 	MEMMOVE(p - del, p, tp->len - old);
 	tp->len -= del;
 	tp->cno -= del;
 
 	/* Fill in space/tab characters. */
 	for (p = tp->lb; tabs--;)
 		*p++ = '\t';
 	while (spaces--)
 		*p++ = ' ';
 	*changedp = 1;
 }
 
 /*
  * v_txt_auto --
  *	Handle autoindent.  If aitp isn't NULL, use it, otherwise,
  *	retrieve the line.
  *
  * PUBLIC: int v_txt_auto(SCR *, recno_t, TEXT *, size_t, TEXT *);
  */
 int
 v_txt_auto(SCR *sp, recno_t lno, TEXT *aitp, size_t len, TEXT *tp)
 {
 	size_t nlen;
 	CHAR_T *p, *t;
 
 	if (aitp == NULL) {
 		/*
 		 * If the ex append command is executed with an address of 0,
 		 * it's possible to get here with a line number of 0.  Return
 		 * an indent of 0.
 		 */
 		if (lno == 0) {
 			tp->ai = 0;
 			return (0);
 		}
 		if (db_get(sp, lno, DBG_FATAL, &t, &len))
 			return (1);
 	} else
 		t = aitp->lb;
 
 	/* Count whitespace characters. */
 	for (p = t; len > 0; ++p, --len)
 		if (!isblank(*p))
 			break;
 
 	/* Set count, check for no indentation. */
 	if ((nlen = (p - t)) == 0)
 		return (0);
 
 	/* Make sure the buffer's big enough. */
 	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
 
 	/* Copy the buffer's current contents up. */
 	if (tp->len != 0)
 		MEMMOVE(tp->lb + nlen, tp->lb, tp->len);
 	tp->len += nlen;
 
 	/* Copy the indentation into the new buffer. */
 	MEMMOVE(tp->lb, t, nlen);
 
 	/* Set the autoindent count. */
 	tp->ai = nlen;
 	return (0);
 }
 
 /*
  * txt_backup --
  *	Back up to the previously edited line.
  */
 static TEXT *
 txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp)
 {
 	VI_PRIVATE *vip;
 	TEXT *ntp;
 
 	/* Get a handle on the previous TEXT structure. */
 	if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) {
 		if (!FL_ISSET(*flagsp, TXT_REPLAY))
 			msgq(sp, M_BERR,
 			    "193|Already at the beginning of the insert");
 		return (tp);
 	}
 
 	/* Bookkeeping. */
 	ntp->len = ntp->sv_len;
 
 	/* Handle appending to the line. */
 	vip = VIP(sp);
 	if (ntp->owrite == 0 && ntp->insert == 0) {
 		ntp->lb[ntp->len] = CH_CURSOR;
 		++ntp->insert;
 		++ntp->len;
 		FL_SET(*flagsp, TXT_APPENDEOL);
 	} else
 		FL_CLR(*flagsp, TXT_APPENDEOL);
 
 	/* Release the current TEXT. */
 	TAILQ_REMOVE(tiqh, tp, q);
 	text_free(tp);
 
 	/* Update the old line on the screen. */
 	if (vs_change(sp, ntp->lno + 1, LINE_DELETE))
 		return (NULL);
 
 	/* Return the new/current TEXT. */
 	return (ntp);
 }
 
 /*
  * Text indentation is truly strange.  ^T and ^D do movements to the next or
  * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3,
  * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D
  * moves it back.
  *
  * !!!
  * The ^T and ^D characters in historical vi had special meaning only when they
  * were the first characters entered after entering text input mode.  As normal
  * erase characters couldn't erase autoindent characters (^T in this case), it
  * meant that inserting text into previously existing text was strange -- ^T
  * only worked if it was the first keystroke(s), and then could only be erased
  * using ^D.  This implementation treats ^T specially anywhere it occurs in the
  * input, and permits the standard erase characters to erase the characters it
  * inserts.
  *
  * !!!
  * A fun test is to try:
  *	:se sw=4 ai list
  *	i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc>
  * Historic vi loses some of the '$' marks on the line ends, but otherwise gets
  * it right.
  *
  * XXX
  * Technically, txt_dent should be part of the screen interface, as it requires
  * knowledge of character sizes, including <space>s, on the screen.  It's here
  * because it's a complicated little beast, and I didn't want to shove it down
  * into the screen.  It's probable that KEY_COL will call into the screen once
  * there are screens with different character representations.
  *
  * txt_dent --
  *	Handle ^T indents, ^D outdents.
  *
  * If anything changes here, check the ex version to see if it needs similar
  * changes.
  */
 static int
 txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
 {
 	CHAR_T ch;
 	u_long sw, ts;
 	size_t cno, current, spaces, target, tabs;
 	int ai_reset;
 
 	ts = O_VAL(sp, O_TABSTOP);
 	sw = O_VAL(sp, swopt);
 
 	/*
 	 * Since we don't know what precedes the character(s) being inserted
 	 * (or deleted), the preceding whitespace characters must be resolved.
 	 * An example is a <tab>, which doesn't need a full shiftwidth number
 	 * of columns because it's preceded by <space>s.  This is easy to get
 	 * if the user sets shiftwidth to a value less than tabstop (or worse,
 	 * something for which tabstop isn't a multiple) and then uses ^T to
 	 * indent, and ^D to outdent.
 	 *
 	 * Figure out the current and target screen columns.  In the historic
 	 * vi, the autoindent column was NOT determined using display widths
 	 * of characters as was the wrapmargin column.  For that reason, we
 	 * can't use the vs_column() function, but have to calculate it here.
 	 * This is slow, but it's normally only on the first few characters of
 	 * a line.
 	 */
 	for (current = cno = 0; cno < tp->cno; ++cno)
 		current += tp->lb[cno] == '\t' ?
 		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
 
 	target = current;
 	if (isindent)
 		target += COL_OFF(target, sw);
 	else {
 		--target;
 		target -= target % sw;
 	}
 
 	/*
 	 * The AI characters will be turned into overwrite characters if the
 	 * cursor immediately follows them.  We test both the cursor position
 	 * and the indent flag because there's no single test.  (^T can only
 	 * be detected by the cursor position, and while we know that the test
 	 * is always true for ^D, the cursor can be in more than one place, as
 	 * "0^D" and "^D" are different.)
 	 */
 	ai_reset = !isindent || tp->cno == tp->ai + tp->offset;
 
 	/*
 	 * Back up over any previous <blank> characters, changing them into
 	 * overwrite characters (including any ai characters).  Then figure
 	 * out the current screen column.
 	 */
 	for (; tp->cno > tp->offset &&
 	    (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t');
 	    --tp->cno, ++tp->owrite);
 	for (current = cno = 0; cno < tp->cno; ++cno)
 		current += tp->lb[cno] == '\t' ?
 		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
 
 	/*
 	 * If we didn't move up to or past the target, it's because there
 	 * weren't enough characters to delete, e.g. the first character
 	 * of the line was a tp->offset character, and the user entered
 	 * ^D to move to the beginning of a line.  An example of this is:
 	 *
 	 *	:set ai sw=4<cr>i<space>a<esc>i^T^D
 	 *
 	 * Otherwise, count up the total spaces/tabs needed to get from the
 	 * beginning of the line (or the last non-<blank> character) to the
 	 * target.
 	 */
 	if (current >= target)
 		spaces = tabs = 0;
 	else {
 		cno = current;
 		tabs = 0;
 		if (!O_ISSET(sp, O_EXPANDTAB)) {
 			for (; cno + COL_OFF(cno, ts) <= target; ++tabs)
 				cno += COL_OFF(cno, ts);
 		}
 		spaces = target - cno;
 	}
 
 	/* If we overwrote ai characters, reset the ai count. */
 	if (ai_reset)
 		tp->ai = tabs + spaces;
 
 	/*
 	 * Call txt_insch() to insert each character, so that we get the
 	 * correct effect when we add a <tab> to replace N <spaces>.
 	 */
 	for (ch = '\t'; tabs > 0; --tabs)
 		(void)txt_insch(sp, tp, &ch, 0);
 	for (ch = ' '; spaces > 0; --spaces)
 		(void)txt_insch(sp, tp, &ch, 0);
 	return (0);
 }
 
 /*
  * txt_fc --
  *	File name and ex command completion.
  */
 static int
 txt_fc(SCR *sp, TEXT *tp, int *redrawp)
 {
 	struct stat sb;
 	ARGS **argv;
 	EXCMD cmd;
 	size_t indx, len, nlen, off;
 	int argc;
 	CHAR_T *p, *t, *bp;
 	char *np, *epd = NULL;
 	size_t nplen;
 	int fstwd = 1;
 
 	*redrawp = 0;
 	ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0);
 
 	/*
 	 * Find the beginning of this "word" -- if we're at the beginning
 	 * of the line, it's a special case.
 	 */
 	if (tp->cno == 1) {
 		len = 0;
 		p = tp->lb;
 	} else {
 		CHAR_T *ap;
 
 		for (len = 0,
 		    off = MAX(tp->ai, tp->offset), ap = tp->lb + off, p = ap;
 		    off < tp->cno; ++off, ++ap) {
 			if (IS_ESCAPE(sp, &cmd, *ap)) {
 				if (++off == tp->cno)
 					break;
 				++ap;
 				len += 2;
 			} else if (cmdskip(*ap)) {
 				p = ap + 1;
 				if (len > 0)
 					fstwd = 0;
 				len = 0;
 			} else
 				++len;
 		}
 	}
 
 	/*
 	 * If we are at the first word, do ex command completion instead of
 	 * file name completion.
 	 */
 	if (fstwd)
 		(void)argv_flt_ex(sp, &cmd, p, len);
 	else {
 		if ((bp = argv_uesc(sp, &cmd, p, len)) == NULL)
 			return (1);
 		if (argv_flt_path(sp, &cmd, bp, STRLEN(bp))) {
 			FREE_SPACEW(sp, bp, 0);
 			return (0);
 		}
 		FREE_SPACEW(sp, bp, 0);
 	}
 	argc = cmd.argc;
 	argv = cmd.argv;
 
 	switch (argc) {
 	case 0:				/* No matches. */
 		(void)sp->gp->scr_bell(sp);
 		return (0);
 	case 1:				/* One match. */
 		/* Always overwrite the old text. */
 		nlen = STRLEN(cmd.argv[0]->bp);
 		break;
 	default:			/* Multiple matches. */
 		*redrawp = 1;
 		if (txt_fc_col(sp, argc, argv))
 			return (1);
 
 		/* Find the length of the shortest match. */
 		for (nlen = cmd.argv[0]->len; --argc > 0;) {
 			if (cmd.argv[argc]->len < nlen)
 				nlen = cmd.argv[argc]->len;
 			for (indx = 0; indx < nlen &&
 			    cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx];
 			    ++indx);
 			nlen = indx;
 		}
 		break;
 	}
 
 	/* Escape the matched part of the path. */
 	if (fstwd)
 		bp = cmd.argv[0]->bp;
 	else {
 		if ((bp = argv_esc(sp, &cmd, cmd.argv[0]->bp, nlen)) == NULL)
 			return (1);
 		nlen = STRLEN(bp);
 	}
 
 	/* Overwrite the expanded text first. */
 	for (t = bp; len > 0 && nlen > 0; --len, --nlen)
 		*p++ = *t++;
 
 	/* If lost text, make the remaining old text overwrite characters. */
 	if (len) {
 		tp->cno -= len;
 		tp->owrite += len;
 	}
 
 	/* Overwrite any overwrite characters next. */
 	for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno)
 		*p++ = *t++;
 
 	/* Shift remaining text up, and move the cursor to the end. */
 	if (nlen) {
 		off = p - tp->lb;
 		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
 		p = tp->lb + off;
 
 		tp->cno += nlen;
 		tp->len += nlen;
 
 		if (tp->insert != 0)
 			(void)MEMMOVE(p + nlen, p, tp->insert);
 		while (nlen--)
 			*p++ = *t++;
 	}
 
 	if (!fstwd)
 		FREE_SPACEW(sp, bp, 0);
 
 	/* If not a single match of path, we've done. */
 	if (argc != 1 || fstwd)
 		return (0);
 
 	/* If a single match and it's a directory, append a '/'. */
 	INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen);
 	if ((epd = expanduser(np)) != NULL)
 		np = epd;
 	if (!stat(np, &sb) && S_ISDIR(sb.st_mode)) {
 		if (tp->owrite == 0) {
 			off = p - tp->lb;
 			BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
 			p = tp->lb + off;
 			if (tp->insert != 0)
 				(void)MEMMOVE(p + 1, p, tp->insert);
 			++tp->len;
 		} else
 			--tp->owrite;
 
 		++tp->cno;
 		*p++ = '/';
 	}
 	free(epd);
 	return (0);
 }
 
 /*
  * txt_fc_col --
  *	Display file names for file name completion.
  */
 static int
 txt_fc_col(SCR *sp, int argc, ARGS **argv)
 {
 	ARGS **av;
 	CHAR_T *p;
 	GS *gp;
 	size_t base, cnt, col, colwidth, numrows, numcols, prefix, row;
 	int ac, nf, reset;
 	char *np, *pp;
 	size_t nlen;
 
 	gp = sp->gp;
 
 	/* Trim any directory prefix common to all of the files. */
 	INT2CHAR(sp, argv[0]->bp, argv[0]->len + 1, np, nlen);
 	if ((pp = strrchr(np, '/')) == NULL)
 		prefix = 0;
 	else {
 		prefix = (pp - np) + 1;
 		for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av)
 			if (av[0]->len < prefix ||
 			    MEMCMP(av[0]->bp, argv[0]->bp, 
 				   prefix)) {
 				prefix = 0;
 				break;
 			}
 	}
 
 	/*
 	 * Figure out the column width for the longest name.  Output is done on
 	 * 6 character "tab" boundaries for no particular reason.  (Since we
 	 * don't output tab characters, we ignore the terminal's tab settings.)
 	 * Ignore the user's tab setting because we have no idea how reasonable
 	 * it is.
 	 */
 	for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) {
 		for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p)
 			col += KEY_COL(sp, *p);
 		if (col > colwidth)
 			colwidth = col;
 	}
 	colwidth += COL_OFF(colwidth, 6);
 
 	/*
 	 * Writing to the bottom line of the screen is always turned off when
 	 * SC_TINPUT_INFO is set.  Turn it back on, we know what we're doing.
 	 */
 	if (F_ISSET(sp, SC_TINPUT_INFO)) {
 		reset = 1;
 		F_CLR(sp, SC_TINPUT_INFO);
 	} else
 		reset = 0;
 
 #define	CHK_INTR							\
 	if (F_ISSET(gp, G_INTERRUPTED))					\
 		goto intr;
 
 	/* If the largest file name is too large, just print them. */
 	if (colwidth >= sp->cols) {
 		for (ac = argc, av = argv; ac > 0; --ac, ++av) {
 			INT2CHAR(sp, av[0]->bp+prefix, av[0]->len+1-prefix,
 				 np, nlen);
 			pp = msg_print(sp, np, &nf);
 			(void)ex_printf(sp, "%s\n", pp);
 			if (nf)
 				FREE_SPACE(sp, pp, 0);
 			if (F_ISSET(gp, G_INTERRUPTED))
 				break;
 		}
 		CHK_INTR;
 	} else {
 		/* Figure out the number of columns. */
 		numcols = (sp->cols - 1) / colwidth;
 		if (argc > numcols) {
 			numrows = argc / numcols;
 			if (argc % numcols)
 				++numrows;
 		} else
 			numrows = 1;
 
 		/* Display the files in sorted order. */
 		for (row = 0; row < numrows; ++row) {
 			for (base = row, col = 0; col < numcols; ++col) {
 				INT2CHAR(sp, argv[base]->bp+prefix, 
 					argv[base]->len+1-prefix, np, nlen);
 				pp = msg_print(sp, np, &nf);
 				cnt = ex_printf(sp, "%s", pp);
 				if (nf)
 					FREE_SPACE(sp, pp, 0);
 				CHK_INTR;
 				if ((base += numrows) >= argc)
 					break;
 				(void)ex_printf(sp,
 				    "%*s", (int)(colwidth - cnt), "");
 				CHK_INTR;
 			}
 			(void)ex_puts(sp, "\n");
 			CHK_INTR;
 		}
 		(void)ex_puts(sp, "\n");
 		CHK_INTR;
 	}
 	(void)ex_fflush(sp);
 
 	if (0) {
 intr:		F_CLR(gp, G_INTERRUPTED);
 	}
 	if (reset)
 		F_SET(sp, SC_TINPUT_INFO);
 
 	return (0);
 }
 
 /*
  * txt_emark --
  *	Set the end mark on the line.
  */
 static int
 txt_emark(SCR *sp, TEXT *tp, size_t cno)
 {
 	CHAR_T ch;
 	u_char *kp;
 	size_t chlen, nlen, olen;
 	CHAR_T *p;
 
 	ch = CH_ENDMARK;
 
 	/*
 	 * The end mark may not be the same size as the current character.
 	 * Don't let the line shift.
 	 */
 	nlen = KEY_COL(sp, ch);
 	if (tp->lb[cno] == '\t')
 		(void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen);
 	else
 		olen = KEY_COL(sp, tp->lb[cno]);
 
 	/*
 	 * If the line got longer, well, it's weird, but it's easy.  If
 	 * it's the same length, it's easy.  If it got shorter, we have
 	 * to fix it up.
 	 */
 	if (olen > nlen) {
 		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + olen);
 		chlen = olen - nlen;
 		if (tp->insert != 0)
 			MEMMOVE(tp->lb + cno + 1 + chlen,
 			    tp->lb + cno + 1, tp->insert);
 
 		tp->len += chlen;
 		tp->owrite += chlen;
 		p = tp->lb + cno;
 		if (tp->lb[cno] == '\t' ||
 		    KEY_NEEDSWIDE(sp, tp->lb[cno]))
 			for (cno += chlen; chlen--;)
 				*p++ = ' ';
 		else
 			for (kp = (u_char *)
 			    KEY_NAME(sp, tp->lb[cno]),
 			    cno += chlen; chlen--;)
 				*p++ = *kp++;
 	}
 	tp->lb[cno] = ch;
 	return (vs_change(sp, tp->lno, LINE_RESET));
 }
 
 /*
  * txt_err --
  *	Handle an error during input processing.
  */
 static void
 txt_err(SCR *sp, TEXTH *tiqh)
 {
 	recno_t lno;
 
 	/*
 	 * The problem with input processing is that the cursor is at an
 	 * indeterminate position since some input may have been lost due
 	 * to a malloc error.  So, try to go back to the place from which
 	 * the cursor started, knowing that it may no longer be available.
 	 *
 	 * We depend on at least one line number being set in the text
 	 * chain.
 	 */
 	for (lno = TAILQ_FIRST(tiqh)->lno;
 	    !db_exist(sp, lno) && lno > 0; --lno);
 
 	sp->lno = lno == 0 ? 1 : lno;
 	sp->cno = 0;
 
 	/* Redraw the screen, just in case. */
 	F_SET(sp, SC_SCR_REDRAW);
 }
 
 /*
  * txt_hex --
  *	Let the user insert any character value they want.
  *
  * !!!
  * This is an extension.  The pattern "^X[0-9a-fA-F]*" is a way
  * for the user to specify a character value which their keyboard
  * may not be able to enter.
  */
 static int
 txt_hex(SCR *sp, TEXT *tp)
 {
 	CHAR_T savec;
 	size_t len, off;
 	u_long value;
 	CHAR_T *p, *wp;
 
 	/*
 	 * Null-terminate the string.  Since nul isn't a legal hex value,
 	 * this should be okay, and lets us use a local routine, which
 	 * presumably understands the character set, to convert the value.
 	 */
 	savec = tp->lb[tp->cno];
 	tp->lb[tp->cno] = 0;
 
 	/* Find the previous CH_HEX character. */
 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
 		if (*p == CH_HEX) {
 			wp = p + 1;
 			break;
 		}
 		/* Not on this line?  Shouldn't happen. */
 		if (off == tp->ai || off == tp->offset)
 			goto nothex;
 	}
 
 	/* If length of 0, then it wasn't a hex value. */
 	if (len == 0)
 		goto nothex;
 
 	/* Get the value. */
 	errno = 0;
 	value = STRTOL(wp, NULL, 16);
 	if (errno || value > UCHAR_MAX) {
 nothex:		tp->lb[tp->cno] = savec;
 		return (0);
 	}
 
 	/* Restore the original character. */
 	tp->lb[tp->cno] = savec;
 
 	/* Adjust the bookkeeping. */
 	tp->cno -= len;
 	tp->len -= len;
 	tp->lb[tp->cno - 1] = value;
 
 	/* Copy down any overwrite characters. */
 	if (tp->owrite)
 		MEMMOVE(tp->lb + tp->cno, tp->lb + tp->cno + len, 
 		    tp->owrite);
 
 	/* Copy down any insert characters. */
 	if (tp->insert)
 		MEMMOVE(tp->lb + tp->cno + tp->owrite,
 		    tp->lb + tp->cno + tp->owrite + len, 
 		    tp->insert);
 
 	return (0);
 }
 
 /*
  * txt_insch --
  *
  * !!!
  * Historic vi did a special screen optimization for tab characters.  As an
  * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the
  * rest of the string when it was displayed.
  *
  * Because early versions of this implementation redisplayed the entire line
  * on each keystroke, the "bcd" was pushed to the right as it ignored that
  * the user had "promised" to change the rest of the characters.  However,
  * the historic vi implementation had an even worse bug: given the keystrokes
  * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
  * on the second <esc> key.
  *
  * POSIX 1003.2 requires (will require) that this be fixed, specifying that
  * vi overwrite characters the user has committed to changing, on the basis
  * of the screen space they require, but that it not overwrite other characters.
  */
 static int
 txt_insch(SCR *sp, TEXT *tp, CHAR_T *chp, u_int flags)
 {
 	u_char *kp;
 	CHAR_T savech;
 	size_t chlen, cno, copydown, olen, nlen;
 	CHAR_T *p;
 
 	/*
 	 * The 'R' command does one-for-one replacement, because there's
 	 * no way to know how many characters the user intends to replace.
 	 */
 	if (LF_ISSET(TXT_REPLACE)) {
 		if (tp->owrite) {
 			--tp->owrite;
 			tp->lb[tp->cno++] = *chp;
 			return (0);
 		}
 	} else if (tp->owrite) {		/* Overwrite a character. */
 		cno = tp->cno;
 
 		/*
 		 * If the old or new characters are tabs, then the length of the
 		 * display depends on the character position in the display.  We
 		 * don't even try to handle this here, just ask the screen.
 		 */
 		if (*chp == '\t') {
 			savech = tp->lb[cno];
 			tp->lb[cno] = '\t';
 			(void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen);
 			tp->lb[cno] = savech;
 		} else
 			nlen = KEY_COL(sp, *chp);
 
 		/*
 		 * Eat overwrite characters until we run out of them or we've
 		 * handled the length of the new character.  If we only eat
 		 * part of an overwrite character, break it into its component
 		 * elements and display the remaining components.
 		 */
 		for (copydown = 0; nlen != 0 && tp->owrite != 0;) {
 			--tp->owrite;
 
 			if (tp->lb[cno] == '\t')
 				(void)vs_columns(sp,
 				    tp->lb, tp->lno, &cno, &olen);
 			else
 				olen = KEY_COL(sp, tp->lb[cno]);
 
 			if (olen == nlen) {
 				nlen = 0;
 				break;
 			}
 			if (olen < nlen) {
 				++copydown;
 				nlen -= olen;
 			} else {
 				BINC_RETW(sp,
 				    tp->lb, tp->lb_len, tp->len + olen);
 				chlen = olen - nlen;
 				MEMMOVE(tp->lb + cno + 1 + chlen,
 				    tp->lb + cno + 1, 
 				    tp->owrite + tp->insert);
 
 				tp->len += chlen;
 				tp->owrite += chlen;
 				if (tp->lb[cno] == '\t' ||
 				   KEY_NEEDSWIDE(sp, tp->lb[cno]))
 					for (p = tp->lb + cno + 1; chlen--;)
 						*p++ = ' ';
 				else
 					for (kp = (u_char *)
 					    KEY_NAME(sp, tp->lb[cno]) + nlen,
 					    p = tp->lb + cno + 1; chlen--;)
 						*p++ = *kp++;
 				nlen = 0;
 				break;
 			}
 		}
 
 		/*
 		 * If had to erase several characters, we adjust the total
 		 * count, and if there are any characters left, shift them
 		 * into position.
 		 */
 		if (copydown != 0 && (tp->len -= copydown) != 0)
 			MEMMOVE(tp->lb + cno, tp->lb + cno + copydown,
 			    tp->owrite + tp->insert + copydown);
 
 		/* If we had enough overwrite characters, we're done. */
 		if (nlen == 0) {
 			tp->lb[tp->cno++] = *chp;
 			return (0);
 		}
 	}
 
 	/* Check to see if the character fits into the input buffer. */
 	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
 
 	++tp->len;
 	if (tp->insert) {			/* Insert a character. */
 		if (tp->insert == 1)
 			tp->lb[tp->cno + 1] = tp->lb[tp->cno];
 		else
 			MEMMOVE(tp->lb + tp->cno + 1,
 			    tp->lb + tp->cno, tp->owrite + tp->insert);
 	}
 	tp->lb[tp->cno++] = *chp;
 	return (0);
 }
 
 /*
  * txt_isrch --
  *	Do an incremental search.
  */
 static int
 txt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp)
 {
 	MARK start;
 	recno_t lno;
 	u_int sf;
 
 	/* If it's a one-line screen, we don't do incrementals. */
 	if (IS_ONELINE(sp)) {
 		FL_CLR(*is_flagsp, IS_RUNNING);
 		return (0);
 	}
 
 	/*
 	 * If the user erases back to the beginning of the buffer, there's
 	 * nothing to search for.  Reset the cursor to the starting point.
 	 */
 	if (tp->cno <= 1) {
 		vp->m_final = vp->m_start;
 		return (0);
 	}
 
 	/*
 	 * If it's an RE quote character, and not quoted, ignore it until
 	 * we get another character.
 	 */
 	if (tp->lb[tp->cno - 1] == '\\' &&
 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
 		return (0);
 
 	/*
 	 * If it's a magic shell character, and not quoted, reset the cursor
 	 * to the starting point.
 	 */
 	if (IS_SHELLMETA(sp, tp->lb[tp->cno - 1]) &&
 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
 		vp->m_final = vp->m_start;
 
 	/*
 	 * If we see the search pattern termination character, then quit doing
 	 * an incremental search.  There may be more, e.g., ":/foo/;/bar/",
 	 * and we can't handle that incrementally.  Also, reset the cursor to
 	 * the original location, the ex search routines don't know anything
 	 * about incremental searches.
 	 */
 	if (tp->lb[0] == tp->lb[tp->cno - 1] &&
 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) {
 		vp->m_final = vp->m_start;
 		FL_CLR(*is_flagsp, IS_RUNNING);
 		return (0);
 	}
 		
 	/*
 	 * Remember the input line and discard the special input map,
 	 * but don't overwrite the input line on the screen.
 	 */
 	lno = tp->lno;
 	F_SET(VIP(sp), VIP_S_MODELINE);
 	F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO);
 	if (txt_map_end(sp))
 		return (1);
 
 	/*
 	 * Specify a starting point and search.  If we find a match, move to
 	 * it and refresh the screen.  If we didn't find the match, then we
 	 * beep the screen.  When searching from the original cursor position, 
 	 * we have to move the cursor, otherwise, we don't want to move the
 	 * cursor in case the text at the current position continues to match.
 	 */
 	if (FL_ISSET(*is_flagsp, IS_RESTART)) {
 		start = vp->m_start;
 		sf = SEARCH_SET;
 	} else {
 		start = vp->m_final;
 		sf = SEARCH_INCR | SEARCH_SET;
 	}
 
 	if (tp->lb[0] == '/' ?
 	    !f_search(sp,
 	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) :
 	    !b_search(sp,
 	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) {
 		sp->lno = vp->m_final.lno;
 		sp->cno = vp->m_final.cno;
 		FL_CLR(*is_flagsp, IS_RESTART);
 
 		if (!KEYS_WAITING(sp) && vs_refresh(sp, 0))
 			return (1);
 	} else
 		FL_SET(*is_flagsp, IS_RESTART);
 
 	/* Reinstantiate the special input map. */
 	if (txt_map_init(sp))
 		return (1);
 	F_CLR(VIP(sp), VIP_S_MODELINE);
 	F_SET(sp, SC_TINPUT | SC_TINPUT_INFO);
 
 	/* Reset the line number of the input line. */
 	tp->lno = TMAP[0].lno; 
 
 	/*
 	 * If the colon command-line moved, i.e. the screen scrolled,
 	 * refresh the input line.
 	 *
 	 * XXX
 	 * We shouldn't be calling vs_line, here -- we need dirty bits
 	 * on entries in the SMAP array.
 	 */
 	if (lno != TMAP[0].lno) {
 		if (vs_line(sp, &TMAP[0], NULL, NULL))
 			return (1);
 		(void)sp->gp->scr_refresh(sp, 0);
 	}
 	return (0);
 }
 
 /*
  * txt_resolve --
  *	Resolve the input text chain into the file.
  */
 static int
 txt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags)
 {
 	VI_PRIVATE *vip;
 	TEXT *tp;
 	recno_t lno;
 	int changed;
 
 	/*
 	 * The first line replaces a current line, and all subsequent lines
 	 * are appended into the file.  Resolve autoindented characters for
 	 * each line before committing it.  If the latter causes the line to
 	 * change, we have to redisplay it, otherwise the information cached
 	 * about the line will be wrong.
 	 */
 	vip = VIP(sp);
 	tp = TAILQ_FIRST(tiqh);
 
 	if (LF_ISSET(TXT_AUTOINDENT))
 		txt_ai_resolve(sp, tp, &changed);
 	else
 		changed = 0;
 	if (db_set(sp, tp->lno, tp->lb, tp->len) ||
 	    (changed && vs_change(sp, tp->lno, LINE_RESET)))
 		return (1);
 
 	for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)) != NULL; ++lno) {
 		if (LF_ISSET(TXT_AUTOINDENT))
 			txt_ai_resolve(sp, tp, &changed);
 		else
 			changed = 0;
 		if (db_append(sp, 0, lno, tp->lb, tp->len) ||
 		    (changed && vs_change(sp, tp->lno, LINE_RESET)))
 			return (1);
 	}
 
 	/*
 	 * Clear the input flag, the look-aside buffer is no longer valid.
 	 * Has to be done as part of text resolution, or upon return we'll
 	 * be looking at incorrect data.
 	 */
 	F_CLR(sp, SC_TINPUT);
 
 	return (0);
 }
 
 /*
  * txt_showmatch --
  *	Show a character match.
  *
  * !!!
  * Historic vi tried to display matches even in the :colon command line.
  * I think not.
  */
 static int
 txt_showmatch(SCR *sp, TEXT *tp)
 {
 	GS *gp;
 	VCS cs;
 	MARK m;
 	int cnt, endc, startc;
 
 	gp = sp->gp;
 
 	/*
 	 * Do a refresh first, in case we haven't done one in awhile,
 	 * so the user can see what we're complaining about.
 	 */
 	UPDATE_POSITION(sp, tp);
 	if (vs_refresh(sp, 1))
 		return (1);
 
 	/*
 	 * We don't display the match if it's not on the screen.  Find
 	 * out what the first character on the screen is.
 	 */
 	if (vs_sm_position(sp, &m, 0, P_TOP))
 		return (1);
 
 	/* Initialize the getc() interface. */
 	cs.cs_lno = tp->lno;
 	cs.cs_cno = tp->cno - 1;
 	if (cs_init(sp, &cs))
 		return (1);
 	startc = STRCHR(VIP(sp)->mcs, endc = cs.cs_ch)[-1];
 
 	/* Search for the match. */
 	for (cnt = 1;;) {
 		if (cs_prev(sp, &cs))
 			return (1);
 		if (cs.cs_flags != 0) {
 			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
 				msgq(sp, M_BERR,
 				    "Unmatched %s", KEY_NAME(sp, endc));
 				return (0);
 			}
 			continue;
 		}
 		if (cs.cs_ch == endc)
 			++cnt;
 		else if (cs.cs_ch == startc && --cnt == 0)
 			break;
 	}
 
 	/* If the match is on the screen, move to it. */
 	if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno))
 		return (0);
 	sp->lno = cs.cs_lno;
 	sp->cno = cs.cs_cno;
 	if (vs_refresh(sp, 1))
 		return (1);
 
 	/* Wait for timeout or character arrival. */
 	return (v_event_get(sp,
 	    NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT));
 }
 
 /*
  * txt_margin --
  *	Handle margin wrap.
  */
 static int
 txt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags)
 {
 	VI_PRIVATE *vip;
 	size_t len, off;
 	CHAR_T *p, *wp;
 
 	/* Find the nearest previous blank. */
 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
 		if (isblank(*p)) {
 			wp = p + 1;
 			break;
 		}
 
 		/*
 		 * If reach the start of the line, there's nowhere to break.
 		 *
 		 * !!!
 		 * Historic vi belled each time a character was entered after
 		 * crossing the margin until a space was entered which could
 		 * be used to break the line.  I don't as it tends to wake the
 		 * cats.
 		 */
 		if (off == tp->ai || off == tp->offset) {
 			*didbreak = 0;
 			return (0);
 		}
 	}
 
 	/*
 	 * Store saved information about the rest of the line in the
 	 * wrapmargin TEXT structure.
 	 *
 	 * !!!
 	 * The offset field holds the length of the current characters
 	 * that the user entered, but which are getting split to the new
 	 * line -- it's going to be used to set the cursor value when we
 	 * move to the new line.
 	 */
 	vip = VIP(sp);
 	wmtp->lb = p + 1;
 	wmtp->offset = len;
 	wmtp->insert = LF_ISSET(TXT_APPENDEOL) ?  tp->insert - 1 : tp->insert;
 	wmtp->owrite = tp->owrite;
 
 	/* Correct current bookkeeping information. */
 	tp->cno -= len;
 	if (LF_ISSET(TXT_APPENDEOL)) {
 		tp->len -= len + tp->owrite + (tp->insert - 1);
 		tp->insert = 1;
 	} else {
 		tp->len -= len + tp->owrite + tp->insert;
 		tp->insert = 0;
 	}
 	tp->owrite = 0;
 
 	/*
 	 * !!!
 	 * Delete any trailing whitespace from the current line.
 	 */
 	for (;; --p, --off) {
 		if (!isblank(*p))
 			break;
 		--tp->cno;
 		--tp->len;
 		if (off == tp->ai || off == tp->offset)
 			break;
 	}
 	*didbreak = 1;
 	return (0);
 }
 
 /*
  * txt_Rresolve --
  *	Resolve the input line for the 'R' command.
  */
 static void
 txt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len)
 {
 	TEXT *ttp;
 	size_t input_len, retain;
 	CHAR_T *p;
 
 	/*
 	 * Check to make sure that the cursor hasn't moved beyond
 	 * the end of the line.
 	 */
 	if (tp->owrite == 0)
 		return;
 
 	/*
 	 * Calculate how many characters the user has entered,
 	 * plus the blanks erased by <carriage-return>/<newline>s.
 	 */
 	for (ttp = TAILQ_FIRST(tiqh), input_len = 0;;) {
 		input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase;
 		if ((ttp = TAILQ_NEXT(ttp, q)) == NULL)
 			break;
 	}
 
 	/*
 	 * If the user has entered less characters than the original line
 	 * was long, restore any overwriteable characters to the original
 	 * characters.  These characters are entered as "insert characters",
 	 * because they're after the cursor and we don't want to lose them.
 	 * (This is okay because the R command has no insert characters.)
 	 * We set owrite to 0 so that the insert characters don't get copied
 	 * to somewhere else, which means that the line and the length have
 	 * to be adjusted here as well.
 	 *
 	 * We have to retrieve the original line because the original pinned
 	 * page has long since been discarded.  If it doesn't exist, that's
 	 * okay, the user just extended the file.
 	 */
 	if (input_len < orig_len) {
 		retain = MIN(tp->owrite, orig_len - input_len);
 		if (db_get(sp,
 		    TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL))
 			return;
 		MEMCPY(tp->lb + tp->cno, p + input_len, retain);
 		tp->len -= tp->owrite - retain;
 		tp->owrite = 0;
 		tp->insert += retain;
 	}
 }
 
 /*
  * txt_nomorech --
  *	No more characters message.
  */
 static void
 txt_nomorech(SCR *sp)
 {
 	msgq(sp, M_BERR, "194|No more characters to erase");
 }
Index: head/contrib/nvi/vi/vi.c
===================================================================
--- head/contrib/nvi/vi/vi.c	(revision 366308)
+++ head/contrib/nvi/vi/vi.c	(revision 366309)
@@ -1,1240 +1,1240 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 typedef enum {
 	GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK
 } gcret_t;
 
 static VIKEYS const
 	       *v_alias(SCR *, VICMD *, VIKEYS const *);
 static gcret_t	v_cmd(SCR *, VICMD *, VICMD *, VICMD *, int *, int *);
 static int	v_count(SCR *, ARG_CHAR_T, u_long *);
 static void	v_dtoh(SCR *);
 static int	v_init(SCR *);
 static gcret_t	v_key(SCR *, int, EVENT *, u_int32_t);
 static int	v_motion(SCR *, VICMD *, VICMD *, int *);
 
 #if defined(DEBUG) && defined(COMLOG)
 static void	v_comlog(SCR *, VICMD *);
 #endif
 
 /*
  * Side-effect:
  *	The dot structure can be set by the underlying vi functions,
  *	see v_Put() and v_put().
  */
 #define	DOT		(&VIP(sp)->sdot)
 #define	DOTMOTION	(&VIP(sp)->sdotmotion)
 
 /*
  * vi --
  * 	Main vi command loop.
  *
  * PUBLIC: int vi(SCR **);
  */
 int
 vi(SCR **spp)
 {
 	GS *gp;
 	MARK abs;
 	SCR *next, *sp;
 	VICMD cmd = { 0 }, *vp;
 	VI_PRIVATE *vip;
 	int comcount, mapped, rval;
 
 	/* Get the first screen. */
 	sp = *spp;
 	gp = sp->gp;
 
 	/* Point to the command structure. */
 	vp = &cmd;
 
 	/* Reset strange attraction. */
 	F_SET(vp, VM_RCM_SET);
 
 	/* Initialize the vi screen. */
 	if (v_init(sp))
 		return (1);
 
 	/* Set the focus. */
 	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
 
 	for (vip = VIP(sp), rval = 0;;) {
 		/* Resolve messages. */
 		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
 			goto ret;
 
 		/*
 		 * If not skipping a refresh, return to command mode and
 		 * refresh the screen.
 		 */
 		if (F_ISSET(vip, VIP_S_REFRESH))
 			F_CLR(vip, VIP_S_REFRESH);
 		else {
 			sp->showmode = SM_COMMAND;
 			if (vs_refresh(sp, 0))
 				goto ret;
 		}
 
 		/* Set the new favorite position. */
 		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
 			F_CLR(vip, VIP_RCM_LAST);
 			(void)vs_column(sp, &sp->rcm);
 		}
 
 		/*
 		 * If not currently in a map, log the cursor position,
 		 * and set a flag so that this command can become the
 		 * DOT command.
 		 */
 		if (MAPPED_KEYS_WAITING(sp))
 			mapped = 1;
 		else {
 			if (log_cursor(sp))
 				goto err;
 			mapped = 0;
 		}
 
 		/*
 		 * There may be an ex command waiting, and we returned here
 		 * only because we exited a screen or file.  In this case,
 		 * we simply go back into the ex parser.
 		 */
 		if (EXCMD_RUNNING(gp)) {
 			vp->kp = &vikeys[':'];
 			goto ex_continue;
 		}
 
 		/* Refresh the command structure. */
 		memset(vp, 0, sizeof(VICMD));
 
 		/*
 		 * We get a command, which may or may not have an associated
 		 * motion.  If it does, we get it too, calling its underlying
 		 * function to get the resulting mark.  We then call the
 		 * command setting the cursor to the resulting mark.
 		 *
 		 * !!!
 		 * Vi historically flushed mapped characters on error, but
 		 * entering extra <escape> characters at the beginning of
 		 * a map wasn't considered an error -- in fact, users would
 		 * put leading <escape> characters in maps to clean up vi
 		 * state before the map was interpreted.  Beauty!
 		 */
 		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
 		case GC_ERR:
 			goto err;
 		case GC_ERR_NOFLUSH:
 			goto gc_err_noflush;
 		case GC_EVENT:
 			goto gc_event;
 		case GC_FATAL:
 			goto ret;
 		case GC_INTERRUPT:
 			goto intr;
 		case GC_OK:
 			break;
 		}
 
 		/* Check for security setting. */
 		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
 			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
 			goto err;
 		}
 
 		/*
 		 * Historical practice: if a dot command gets a new count,
 		 * any motion component goes away, i.e. "d3w2." deletes a
 		 * total of 5 words.
 		 */
 		if (F_ISSET(vp, VC_ISDOT) && comcount)
 			DOTMOTION->count = 1;
 
 		/* Copy the key flags into the local structure. */
 		F_SET(vp, vp->kp->flags);
 
 		/* Prepare to set the previous context. */
 		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
 			abs.lno = sp->lno;
 			abs.cno = sp->cno;
 		}
 
 		/*
 		 * Set the three cursor locations to the current cursor.  The
 		 * underlying routines don't bother if the cursor doesn't move.
 		 * This also handles line commands (e.g. Y) defaulting to the
 		 * current line.
 		 */
 		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
 		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
 
 		/*
 		 * Do any required motion; v_motion sets the from MARK and the
 		 * line mode flag, as well as the VM_RCM flags.
 		 */
 		if (F_ISSET(vp, V_MOTION) &&
 		    v_motion(sp, DOTMOTION, vp, &mapped)) {
 			if (INTERRUPTED(sp))
 				goto intr;
 			goto err;
 		}
 
 		/*
 		 * If a count is set and the command is line oriented, set the
 		 * to MARK here relative to the cursor/from MARK.  This is for
 		 * commands that take both counts and motions, i.e. "4yy" and
 		 * "y%".  As there's no way the command can know which the user
 		 * did, we have to do it here.  (There are commands that are
 		 * line oriented and that take counts ("#G", "#H"), for which
 		 * this calculation is either completely meaningless or wrong.
 		 * Each command must validate the value for itself.
 		 */
 		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
 			vp->m_stop.lno += vp->count - 1;
 
 		/* Increment the command count. */
 		++sp->ccnt;
 
 #if defined(DEBUG) && defined(COMLOG)
 		v_comlog(sp, vp);
 #endif
 		/* Call the function. */
 ex_continue:	if (vp->kp->func(sp, vp))
 			goto err;
 gc_event:
 #ifdef DEBUG
 		/* Make sure no function left the temporary space locked. */
 		if (F_ISSET(gp, G_TMP_INUSE)) {
 			F_CLR(gp, G_TMP_INUSE);
 			msgq(sp, M_ERR,
 			    "232|vi: temporary buffer not released");
 		}
 #endif
 		/*
 		 * If we're exiting this screen, move to the next one, or, if
 		 * there aren't any more, return to the main editor loop.  The
 		 * ordering is careful, don't discard the contents of sp until
 		 * the end.
 		 */
 		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
 			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
 				goto ret;
 			if (vs_discard(sp, &next))
 				goto ret;
 			if (next == NULL && vs_swap(sp, &next, NULL))
 				goto ret;
 			*spp = next;
 			if (screen_end(sp))
 				goto ret;
 			if (next == NULL)
 				break;
 
 			/* Switch screens, change focus. */
 			sp = next;
 			vip = VIP(sp);
 			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
 
 			/* Don't trust the cursor. */
 			F_SET(vip, VIP_CUR_INVALID);
 
 			continue;
 		}
 
 		/*
 		 * Set the dot command structure.
 		 *
 		 * !!!
 		 * Historically, commands which used mapped keys did not
 		 * set the dot command, with the exception of the text
 		 * input commands.
 		 */
 		if (F_ISSET(vp, V_DOT) && !mapped) {
 			*DOT = cmd;
 			F_SET(DOT, VC_ISDOT);
 
 			/*
 			 * If a count was supplied for both the command and
 			 * its motion, the count was used only for the motion.
 			 * Turn the count back on for the dot structure.
 			 */
 			if (F_ISSET(vp, VC_C1RESET))
 				F_SET(DOT, VC_C1SET);
 
 			/* VM flags aren't retained. */
 			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
 		}
 
 		/*
 		 * Some vi row movements are "attracted" to the last position
 		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
 		 * commands' candle.  If the movement is to the EOL the vi
 		 * command handles it.  If it's to the beginning, we handle it
 		 * here.
 		 *
 		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
 		 * flag, but do the work themselves.  The reason is that they
 		 * have to modify the column in case they're being used as a
 		 * motion component.  Other similar commands (e.g. +, -) don't
 		 * have to modify the column because they are always line mode
 		 * operations when used as motions, so the column number isn't
 		 * of any interest.
 		 *
 		 * Does this totally violate the screen and editor layering?
 		 * You betcha.  As they say, if you think you understand it,
 		 * you don't.
 		 */
 		switch (F_ISSET(vp, VM_RCM_MASK)) {
 		case 0:
 		case VM_RCM_SET:
 			break;
 		case VM_RCM:
 			vp->m_final.cno = vs_rcm(sp,
 			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
 			break;
 		case VM_RCM_SETLAST:
 			F_SET(vip, VIP_RCM_LAST);
 			break;
 		case VM_RCM_SETFNB:
 			vp->m_final.cno = 0;
 			/* FALLTHROUGH */
 		case VM_RCM_SETNNB:
 			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
 				goto err;
 			break;
 		default:
 			abort();
 		}
 
 		/* Update the cursor. */
 		sp->lno = vp->m_final.lno;
 		sp->cno = vp->m_final.cno;
 
 		/*
 		 * Set the absolute mark -- set even if a tags or similar
 		 * command, since the tag may be moving to the same file.
 		 */
 		if ((F_ISSET(vp, V_ABS) ||
 		    (F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno) ||
 		    (F_ISSET(vp, V_ABS_C) &&
 		    (sp->lno != abs.lno || sp->cno != abs.cno))) &&
 		    mark_set(sp, ABSMARK1, &abs, 1))
 			goto err;
 
 		if (0) {
 err:			if (v_event_flush(sp, CH_MAPPED))
 				msgq(sp, M_BERR,
 			    "110|Vi command failed: mapped keys discarded");
 		}
 
 		/*
 		 * Check and clear interrupts.  There's an obvious race, but
 		 * it's not worth fixing.
 		 */
 gc_err_noflush:	if (INTERRUPTED(sp)) {
 intr:			CLR_INTERRUPT(sp);
 			if (v_event_flush(sp, CH_MAPPED))
 				msgq(sp, M_ERR,
 				    "231|Interrupted: mapped keys discarded");
 			else
 				msgq(sp, M_ERR, "236|Interrupted");
 		}
 
 		/* If the last command switched screens, update. */
 		if (F_ISSET(sp, SC_SSWITCH)) {
 			F_CLR(sp, SC_SSWITCH);
 
 			/*
 			 * If the current screen is still displayed, it will
 			 * need a new status line.
 			 */
 			F_SET(sp, SC_STATUS);
 
 			/* Switch screens, change focus. */
 			sp = sp->nextdisp;
 			vip = VIP(sp);
 			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
 
 			/* Don't trust the cursor. */
 			F_SET(vip, VIP_CUR_INVALID);
 
 			/* Refresh so we can display messages. */
 			if (vs_refresh(sp, 1))
 				return (1);
 		}
 
 		/* If the last command switched files, change focus. */
 		if (F_ISSET(sp, SC_FSWITCH)) {
 			F_CLR(sp, SC_FSWITCH);
 			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
 		}
 
 		/* If leaving vi, return to the main editor loop. */
 		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
 			*spp = sp;
 			v_dtoh(sp);
 			gp->scr_discard(sp, NULL);
 			break;
 		}
 	}
 	if (0)
 ret:		rval = 1;
 	return (rval);
 }
 
-#define	KEY(key, ec_flags) {						\
+#define	KEY(key, ec_flags) do {						\
 	if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK)		\
 		return (gcret);						\
 	if (ev.e_value == K_ESCAPE)					\
 		goto esc;						\
 	if (F_ISSET(&ev.e_ch, CH_MAPPED))				\
 		*mappedp = 1;						\
 	key = ev.e_c;							\
-}
+} while (0)
 
 /*
  * The O_TILDEOP option makes the ~ command take a motion instead
  * of a straight count.  This is the replacement structure we use
  * instead of the one currently in the VIKEYS table.
  *
  * XXX
  * This should probably be deleted -- it's not all that useful, and
  * we get help messages wrong.
  */
 VIKEYS const tmotion = {
 	v_mulcase,	V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
 	"[count]~[count]motion",
 	" ~ change case to motion"
 };
 
 /*
  * v_cmd --
  *
  * The command structure for vi is less complex than ex (and don't think
  * I'm not grateful!)  The command syntax is:
  *
  *	[count] [buffer] [count] key [[motion] | [buffer] [character]]
  *
  * and there are several special cases.  The motion value is itself a vi
  * command, with the syntax:
  *
  *	[count] key [character]
  */
 static gcret_t
 v_cmd(
 	SCR *sp,
 	VICMD *dp,
 	VICMD *vp,
 	VICMD *ismotion,	/* Previous key if getting motion component. */
 	int *comcountp,
 	int *mappedp)
 {
 	enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
 	EVENT ev;
 	VIKEYS const *kp;
 	gcret_t gcret;
 	u_int flags;
 	CHAR_T key;
 	char *s;
 
 	/*
 	 * Get a key.
 	 *
 	 * <escape> cancels partial commands, i.e. a command where at least
 	 * one non-numeric character has been entered.  Otherwise, it beeps
 	 * the terminal.
 	 *
 	 * !!!
 	 * POSIX 1003.2-1992 explicitly disallows cancelling commands where
 	 * all that's been entered is a number, requiring that the terminal
 	 * be alerted.
 	 */
 	cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
 	if ((gcret =
 	    v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
 		if (gcret == GC_EVENT)
 			vp->ev = ev;
 		return (gcret);
 	}
 	if (ev.e_value == K_ESCAPE)
 		goto esc;
 	if (F_ISSET(&ev.e_ch, CH_MAPPED))
 		*mappedp = 1;
 	key = ev.e_c;
 
 	if (ismotion == NULL)
 		cpart = NOTPARTIAL;
 
 	/* Pick up an optional buffer. */
 	if (key == '"') {
 		cpart = ISPARTIAL;
 		if (ismotion != NULL) {
 			v_emsg(sp, NULL, VIM_COMBUF);
 			return (GC_ERR);
 		}
 		KEY(vp->buffer, 0);
 		F_SET(vp, VC_BUFFER);
 
 		KEY(key, EC_MAPCOMMAND);
 	}
 
 	/*
 	 * Pick up an optional count, where a leading 0 is not a count,
 	 * it's a command.
 	 */
 	if (ISDIGIT(key) && key != '0') {
 		if (v_count(sp, key, &vp->count))
 			return (GC_ERR);
 		F_SET(vp, VC_C1SET);
 		*comcountp = 1;
 
 		KEY(key, EC_MAPCOMMAND);
 	} else
 		*comcountp = 0;
 
 	/* Pick up optional buffer. */
 	if (key == '"') {
 		cpart = ISPARTIAL;
 		if (F_ISSET(vp, VC_BUFFER)) {
 			msgq(sp, M_ERR, "234|Only one buffer may be specified");
 			return (GC_ERR);
 		}
 		if (ismotion != NULL) {
 			v_emsg(sp, NULL, VIM_COMBUF);
 			return (GC_ERR);
 		}
 		KEY(vp->buffer, 0);
 		F_SET(vp, VC_BUFFER);
 
 		KEY(key, EC_MAPCOMMAND);
 	}
 
 	/* Check for an OOB command key. */
 	cpart = ISPARTIAL;
 	if (key > MAXVIKEY) {
 		v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
 		return (GC_ERR);
 	}
 	kp = &vikeys[vp->key = key];
 
 	/*
 	 * !!!
 	 * Historically, D accepted and then ignored a count.  Match it.
 	 */
 	if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
 		*comcountp = 0;
 		vp->count = 0;
 		F_CLR(vp, VC_C1SET);
 	}
 
 	/* Check for command aliases. */
 	if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
 		return (GC_ERR);
 
 	/* The tildeop option makes the ~ command take a motion. */
 	if (key == '~' && O_ISSET(sp, O_TILDEOP))
 		kp = &tmotion;
 
 	vp->kp = kp;
 
 	/*
 	 * Find the command.  The only legal command with no underlying
 	 * function is dot.  It's historic practice that <escape> doesn't
 	 * just erase the preceding number, it beeps the terminal as well.
 	 * It's a common problem, so just beep the terminal unless verbose
 	 * was set.
 	 */
 	if (kp->func == NULL) {
 		if (key != '.') {
 			v_emsg(sp, KEY_NAME(sp, key),
 			    ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
 			return (GC_ERR);
 		}
 
 		/* If called for a motion command, stop now. */
 		if (dp == NULL)
 			goto usage;
 
 		/*
 		 * !!!
 		 * If a '.' is immediately entered after an undo command, we
 		 * replay the log instead of redoing the last command.  This
 		 * is necessary because 'u' can't set the dot command -- see
 		 * vi/v_undo.c:v_undo for details.
 		 */
 		if (VIP(sp)->u_ccnt == sp->ccnt) {
 			vp->kp = &vikeys['u'];
 			F_SET(vp, VC_ISDOT);
 			return (GC_OK);
 		}
 
 		/* Otherwise, a repeatable command must have been executed. */
 		if (!F_ISSET(dp, VC_ISDOT)) {
 			msgq(sp, M_ERR, "208|No command to repeat");
 			return (GC_ERR);
 		}
 
 		/* Set new count/buffer, if any, and return. */
 		if (F_ISSET(vp, VC_C1SET)) {
 			F_SET(dp, VC_C1SET);
 			dp->count = vp->count;
 		}
 		if (F_ISSET(vp, VC_BUFFER))
 			dp->buffer = vp->buffer;
 
 		*vp = *dp;
 		return (GC_OK);
 	}
 
 	/* Set the flags based on the command flags. */
 	flags = kp->flags;
 
 	/* Check for illegal count. */
 	if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
 		goto usage;
 
 	/* Illegal motion command. */
 	if (ismotion == NULL) {
 		/* Illegal buffer. */
 		if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
 			goto usage;
 
 		/* Required buffer. */
 		if (LF_ISSET(V_RBUF)) {
 			KEY(vp->buffer, 0);
 			F_SET(vp, VC_BUFFER);
 		}
 	}
 
 	/*
 	 * Special case: '[', ']' and 'Z' commands.  Doesn't the fact that
 	 * the *single* characters don't mean anything but the *doubled*
 	 * characters do, just frost your shorts?
 	 */
 	if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
 		/*
 		 * Historically, half entered [[, ]] or Z commands weren't
 		 * cancelled by <escape>, the terminal was beeped instead.
 		 * POSIX.2-1992 probably didn't notice, and requires that
 		 * they be cancelled instead of beeping.  Seems fine to me.
 		 *
 		 * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
 		 * vi meta-character, and we don't want the user to wait while
 		 * we time out a possible mapping.  This *appears* to match
 		 * historic vi practice, but with mapping characters, You Just
 		 * Never Know.
 		 */
 		KEY(key, 0);
 
 		if (vp->key != key) {
 usage:			if (ismotion == NULL)
 				s = kp->usage;
 			else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
 				s = tmotion.usage;
 			else
 				s = vikeys[ismotion->key].usage;
 			v_emsg(sp, s, VIM_USAGE);
 			return (GC_ERR);
 		}
 	}
 	/* Special case: 'z' command. */
 	if (vp->key == 'z') {
 		KEY(vp->character, 0);
 		if (ISDIGIT(vp->character)) {
 			if (v_count(sp, vp->character, &vp->count2))
 				return (GC_ERR);
 			F_SET(vp, VC_C2SET);
 			KEY(vp->character, 0);
 		}
 	}
 
 	/*
 	 * Commands that have motion components can be doubled to imply the
 	 * current line.
 	 */
 	if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
 		msgq(sp, M_ERR, "210|%s may not be used as a motion command",
 		    KEY_NAME(sp, key));
 		return (GC_ERR);
 	}
 
 	/* Pick up required trailing character. */
 	if (LF_ISSET(V_CHAR))
 		KEY(vp->character, 0);
 
 	/* Get any associated cursor word. */
 	if (F_ISSET(kp, V_KEYW) && v_curword(sp))
 		return (GC_ERR);
 
 	return (GC_OK);
 
 esc:	switch (cpart) {
 	case COMMANDMODE:
 		msgq(sp, M_BERR, "211|Already in command mode");
 		return (GC_ERR_NOFLUSH);
 	case ISPARTIAL:
 		break;
 	case NOTPARTIAL:
 		(void)sp->gp->scr_bell(sp);
 		break;
 	}
 	return (GC_ERR);
 }
 
 /*
  * v_motion --
  *
  * Get resulting motion mark.
  */
 static int
 v_motion(
 	SCR *sp,
 	VICMD *dm,
 	VICMD *vp,
 	int *mappedp)
 {
 	VICMD motion;
 	size_t len;
 	u_long cnt;
 	u_int flags;
 	int tilde_reset, notused;
 
 	/*
 	 * If '.' command, use the dot motion, else get the motion command.
 	 * Clear any line motion flags, the subsequent motion isn't always
 	 * the same, i.e. "/aaa" may or may not be a line motion.
 	 */
 	if (F_ISSET(vp, VC_ISDOT)) {
 		motion = *dm;
 		F_SET(&motion, VC_ISDOT);
 		F_CLR(&motion, VM_COMMASK);
 	} else {
 		memset(&motion, 0, sizeof(VICMD));
 		if (v_cmd(sp, NULL, &motion, vp, &notused, mappedp) != GC_OK)
 			return (1);
 	}
 
 	/*
 	 * A count may be provided both to the command and to the motion, in
 	 * which case the count is multiplicative.  For example, "3y4y" is the
 	 * same as "12yy".  This count is provided to the motion command and
 	 * not to the regular function.
 	 */
 	cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
 	if (F_ISSET(vp, VC_C1SET)) {
 		motion.count *= vp->count;
 		F_SET(&motion, VC_C1SET);
 
 		/*
 		 * Set flags to restore the original values of the command
 		 * structure so dot commands can change the count values,
 		 * e.g. "2dw" "3." deletes a total of five words.
 		 */
 		F_CLR(vp, VC_C1SET);
 		F_SET(vp, VC_C1RESET);
 	}
 
 	/*
 	 * Some commands can be repeated to indicate the current line.  In
 	 * this case, or if the command is a "line command", set the flags
 	 * appropriately.  If not a doubled command, run the function to get
 	 * the resulting mark.
  	 */
 	if (vp->key == motion.key) {
 		F_SET(vp, VM_LDOUBLE | VM_LMODE);
 
 		/* Set the origin of the command. */
 		vp->m_start.lno = sp->lno;
 		vp->m_start.cno = 0;
 
 		/*
 		 * Set the end of the command.
 		 *
 		 * If the current line is missing, i.e. the file is empty,
 		 * historic vi permitted a "cc" or "!!" command to insert
 		 * text.
 		 */
 		vp->m_stop.lno = sp->lno + motion.count - 1;
 		if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
 			if (vp->m_stop.lno != 1 ||
 			   (vp->key != 'c' && vp->key != '!')) {
 				v_emsg(sp, NULL, VIM_EMPTY);
 				return (1);
 			}
 			vp->m_stop.cno = 0;
 		} else
 			vp->m_stop.cno = len ? len - 1 : 0;
 	} else {
 		/*
 		 * Motion commands change the underlying movement (*snarl*).
 		 * For example, "l" is illegal at the end of a line, but "dl"
 		 * is not.  Set flags so the function knows the situation.
 		 */
 		motion.rkp = vp->kp;
 
 		/*
 		 * XXX
 		 * Use yank instead of creating a new motion command, it's a
 		 * lot easier for now.
 		 */
 		if (vp->kp == &tmotion) {
 			tilde_reset = 1;
 			vp->kp = &vikeys['y'];
 		} else
 			tilde_reset = 0;
 
 		/*
 		 * Copy the key flags into the local structure, except for the
 		 * RCM flags -- the motion command will set the RCM flags in
 		 * the vp structure if necessary.  This means that the motion
 		 * command is expected to determine where the cursor ends up!
 		 * However, we save off the current RCM mask and restore it if
 		 * it no RCM flags are set by the motion command, with a small
 		 * modification.
 		 *
 		 * We replace the VM_RCM_SET flag with the VM_RCM flag.  This
 		 * is so that cursor movement doesn't set the relative position
 		 * unless the motion command explicitly specified it.  This
 		 * appears to match historic practice, but I've never been able
 		 * to develop a hard-and-fast rule.
 		 */
 		flags = F_ISSET(vp, VM_RCM_MASK);
 		if (LF_ISSET(VM_RCM_SET)) {
 			LF_SET(VM_RCM);
 			LF_CLR(VM_RCM_SET);
 		}
 		F_CLR(vp, VM_RCM_MASK);
 		F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
 
 		/*
 		 * Set the three cursor locations to the current cursor.  This
 		 * permits commands like 'j' and 'k', that are line oriented
 		 * motions and have special cursor suck semantics when they are
 		 * used as standalone commands, to ignore column positioning.
 		 */
 		motion.m_final.lno =
 		    motion.m_stop.lno = motion.m_start.lno = sp->lno;
 		motion.m_final.cno =
 		    motion.m_stop.cno = motion.m_start.cno = sp->cno;
 
 		/* Run the function. */
 		if ((motion.kp->func)(sp, &motion))
 			return (1);
 
 		/*
 		 * If the current line is missing, i.e. the file is empty,
 		 * historic vi allowed "c<motion>" or "!<motion>" to insert
 		 * text.  Otherwise fail -- most motion commands will have
 		 * already failed, but some, e.g. G, succeed in empty files.
 		 */
 		if (!db_exist(sp, vp->m_stop.lno)) {
 			if (vp->m_stop.lno != 1 ||
 			   (vp->key != 'c' && vp->key != '!')) {
 				v_emsg(sp, NULL, VIM_EMPTY);
 				return (1);
 			}
 			vp->m_stop.cno = 0;
 		}
 
 		/*
 		 * XXX
 		 * See above.
 		 */
 		if (tilde_reset)
 			vp->kp = &tmotion;
 
 		/*
 		 * Copy cut buffer, line mode and cursor position information
 		 * from the motion command structure, i.e. anything that the
 		 * motion command can set for us.  The commands can flag the
 		 * movement as a line motion (see v_sentence) as well as set
 		 * the VM_RCM_* flags explicitly.
 		 */
 		F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
 
 		/*
 		 * If the motion command set no relative motion flags, use
 		 * the (slightly) modified previous values.
 		 */
 		if (!F_ISSET(vp, VM_RCM_MASK))
 			F_SET(vp, flags);
 
 		/*
 		 * Commands can change behaviors based on the motion command
 		 * used, for example, the ! command repeated the last bang
 		 * command if N or n was used as the motion.
 		 */
 		vp->rkp = motion.kp;
 
 		/*
 		 * Motion commands can reset all of the cursor information.
 		 * If the motion is in the reverse direction, switch the
 		 * from and to MARK's so that it's in a forward direction.
 		 * Motions are from the from MARK to the to MARK (inclusive).
 		 */
 		if (motion.m_start.lno > motion.m_stop.lno ||
 		    (motion.m_start.lno == motion.m_stop.lno &&
 		    motion.m_start.cno > motion.m_stop.cno)) {
 			vp->m_start = motion.m_stop;
 			vp->m_stop = motion.m_start;
 		} else {
 			vp->m_start = motion.m_start;
 			vp->m_stop = motion.m_stop;
 		}
 		vp->m_final = motion.m_final;
 	}
 
 	/*
 	 * If the command sets dot, save the motion structure.  The motion
 	 * count was changed above and needs to be reset, that's why this
 	 * is done here, and not in the calling routine.
 	 */
 	if (F_ISSET(vp->kp, V_DOT)) {
 		*dm = motion;
 		dm->count = cnt;
 	}
 	return (0);
 }
 
 /*
  * v_init --
  *	Initialize the vi screen.
  */
 static int
 v_init(SCR *sp)
 {
 	GS *gp;
 	VI_PRIVATE *vip;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	/* Switch into vi. */
 	if (gp->scr_screen(sp, SC_VI))
 		return (1);
 	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
 
 	F_CLR(sp, SC_EX | SC_SCR_EX);
 	F_SET(sp, SC_VI);
 
 	/*
 	 * Initialize screen values.
 	 *
 	 * Small windows: see vs_refresh(), section 6a.
 	 *
 	 * Setup:
 	 *	t_minrows is the minimum rows to display
 	 *	t_maxrows is the maximum rows to display (rows - 1)
 	 *	t_rows is the rows currently being displayed
 	 */
 	sp->rows = vip->srows = O_VAL(sp, O_LINES);
 	sp->cols = O_VAL(sp, O_COLUMNS);
 	sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
 	if (sp->rows != 1) {
 		if (sp->t_rows > sp->rows - 1) {
 			sp->t_minrows = sp->t_rows = sp->rows - 1;
 			msgq(sp, M_INFO,
 			    "214|Windows option value is too large, max is %u",
 			    (u_int)sp->t_rows);
 		}
 		sp->t_maxrows = sp->rows - 1;
 	} else
 		sp->t_maxrows = 1;
 	sp->roff = sp->coff = 0;
 
 	/* Create a screen map. */
 	CALLOC_RET(sp, HMAP, SIZE_HMAP(sp), sizeof(SMAP));
 	TMAP = HMAP + (sp->t_rows - 1);
 	HMAP->lno = sp->lno;
 	HMAP->coff = 0;
 	HMAP->soff = 1;
 
 	/*
 	 * Fill the screen map from scratch -- try and center the line.  That
 	 * way if we're starting with a file we've seen before, we'll put the
 	 * line in the middle, otherwise, it won't work and we'll end up with
 	 * the line at the top.
 	 */
 	F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
 
 	/* Invalidate the cursor. */
 	F_SET(vip, VIP_CUR_INVALID);
 
 	/* Paint the screen image from scratch. */
 	F_SET(vip, VIP_N_EX_PAINT);
 
 	return (0);
 }
 
 /*
  * v_dtoh --
  *	Move all but the current screen to the hidden queue.
  */
 static void
 v_dtoh(SCR *sp)
 {
 	GS *gp;
 	SCR *tsp;
 	int hidden;
 
 	/* Move all screens to the hidden queue, tossing screen maps. */
 	for (hidden = 0, gp = sp->gp;
 	    (tsp = TAILQ_FIRST(gp->dq)) != NULL; ++hidden) {
 		free(_HMAP(tsp));
 		_HMAP(tsp) = NULL;
 		TAILQ_REMOVE(gp->dq, tsp, q);
 		TAILQ_INSERT_TAIL(gp->hq, tsp, q);
 		/* XXXX Change if hidden screens per window */
 		gp->scr_discard(tsp, NULL);
 	}
 
 	/* Move current screen back to the display queue. */
 	TAILQ_REMOVE(gp->hq, sp, q);
 	TAILQ_INSERT_TAIL(gp->dq, sp, q);
 
 	if (hidden > 1)
 		msgq(sp, M_INFO,
 		    "319|%d screens backgrounded; use :display to list them",
 		    hidden - 1);
 }
 
 /*
  * v_curword --
  *	Get the word (tagstring, actually) the cursor is on.
  *
  * PUBLIC: int v_curword(SCR *);
  */
 int
 v_curword(SCR *sp)
 {
 	VI_PRIVATE *vip;
 	size_t beg, end, len;
 	int moved;
 	CHAR_T *p;
 
 	if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
 		return (1);
 
 	/*
 	 * !!!
 	 * Historically, tag commands skipped over any leading whitespace
 	 * characters.  Make this true in general when using cursor words.
 	 * If movement, getting a cursor word implies moving the cursor to
 	 * its beginning.  Refresh now.
 	 *
 	 * !!!
 	 * Find the beginning/end of the keyword.  Keywords are currently
 	 * used for cursor-word searching and for tags.  Historical vi
 	 * only used the word in a tag search from the cursor to the end
 	 * of the word, i.e. if the cursor was on the 'b' in " abc ", the
 	 * tag was "bc".  For consistency, we make cursor word searches
 	 * follow the same rule.
 	 */
 	for (moved = 0,
 	    beg = sp->cno; beg < len && ISSPACE(p[beg]); moved = 1, ++beg);
 	if (beg >= len) {
 		msgq(sp, M_BERR, "212|Cursor not in a word");
 		return (1);
 	}
 	if (moved) {
 		sp->cno = beg;
 		(void)vs_refresh(sp, 0);
 	}
 
 	/*
 	 * Find the end of the word.
 	 *
 	 * !!!
 	 * Historically, vi accepted any non-blank as initial character
 	 * when building up a tagstring.  Required by IEEE 1003.1-2001.
 	 */
 	for (end = beg; ++end < len && inword(p[end]););
 
 	vip = VIP(sp);
 	vip->klen = len = (end - beg);
 	BINC_RETW(sp, vip->keyw, vip->keywlen, len+1);
 	MEMMOVE(vip->keyw, p + beg, len);
 	vip->keyw[len] = '\0';				/* XXX */
 	return (0);
 }
 
 /*
  * v_alias --
  *	Check for a command alias.
  */
 static VIKEYS const *
 v_alias(
 	SCR *sp,
 	VICMD *vp,
 	VIKEYS const *kp)
 {
 	CHAR_T push;
 
 	switch (vp->key) {
 	case 'C':			/* C -> c$ */
 		push = '$';
 		vp->key = 'c';
 		break;
 	case 'D':			/* D -> d$ */
 		push = '$';
 		vp->key = 'd';
 		break;
 	case 'S':			/* S -> c_ */
 		push = '_';
 		vp->key = 'c';
 		break;
 	case 'Y':			/* Y -> y_ */
 		push = '_';
 		vp->key = 'y';
 		break;
 	default:
 		return (kp);
 	}
 	return (v_event_push(sp,
 	    NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]);
 }
 
 /*
  * v_count --
  *	Return the next count.
  */
 static int
 v_count(
 	SCR *sp,
 	ARG_CHAR_T fkey,
 	u_long *countp)
 {
 	EVENT ev;
 	u_long count, tc;
 
 	ev.e_c = fkey;
 	count = tc = 0;
 	do {
 		/*
 		 * XXX
 		 * Assume that overflow results in a smaller number.
 		 */
 		tc = count * 10 + ev.e_c - '0';
 		if (count > tc) {
 			/* Toss to the next non-digit. */
 			do {
 				if (v_key(sp, 0, &ev,
 				    EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
 					return (1);
 			} while (ISDIGIT(ev.e_c));
 			msgq(sp, M_ERR,
 			    "235|Number larger than %lu", ULONG_MAX);
 			return (1);
 		}
 		count = tc;
 		if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
 			return (1);
 	} while (ISDIGIT(ev.e_c));
 	*countp = count;
 	return (0);
 }
 
 /*
  * v_key --
  *	Return the next event.
  */
 static gcret_t
 v_key(
 	SCR *sp,
 	int command_events,
 	EVENT *evp,
 	u_int32_t ec_flags)
 {
 	u_int32_t quote;
 
 	for (quote = 0;;) {
 		if (v_event_get(sp, evp, 0, ec_flags | quote))
 			return (GC_FATAL);
 		quote = 0;
 
 		switch (evp->e_event) {
 		case E_CHARACTER:
 			/*
 			 * !!!
 			 * Historically, ^V was ignored in the command stream,
 			 * although it had a useful side-effect of interrupting
 			 * mappings.  Adding a quoting bit to the call probably
 			 * extends historic practice, but it feels right.
 			 */
 			if (evp->e_value == K_VLNEXT) {
 				quote = EC_QUOTED;
 				break;
 			}
 			return (GC_OK);
 		case E_ERR:
 		case E_EOF:
 			return (GC_FATAL);
 		case E_INTERRUPT:
 			/*
 			 * !!!
 			 * Historically, vi beeped on command level interrupts.
 			 *
 			 * Historically, vi exited to ex mode if no file was
 			 * named on the command line, and two interrupts were
 			 * generated in a row.  (Just figured you might want
 			 * to know that.)
 			 */
 			(void)sp->gp->scr_bell(sp);
 			return (GC_INTERRUPT);
 		case E_REPAINT:
 			if (vs_repaint(sp, evp))
 				return (GC_FATAL);
 			break;
 		case E_WRESIZE:
 			return (GC_ERR);
 		default:
 			v_event_err(sp, evp);
 			return (GC_ERR);
 		}
 	}
 	/* NOTREACHED */
 }
 
 #if defined(DEBUG) && defined(COMLOG)
 /*
  * v_comlog --
  *	Log the contents of the command structure.
  */
 static void
 v_comlog(
 	SCR *sp,
 	VICMD *vp)
 {
 	TRACE(sp, "vcmd: "WC, vp->key);
 	if (F_ISSET(vp, VC_BUFFER))
 		TRACE(sp, " buffer: "WC, vp->buffer);
 	if (F_ISSET(vp, VC_C1SET))
 		TRACE(sp, " c1: %lu", vp->count);
 	if (F_ISSET(vp, VC_C2SET))
 		TRACE(sp, " c2: %lu", vp->count2);
 	TRACE(sp, " flags: 0x%x\n", vp->flags);
 }
 #endif
Index: head/contrib/nvi/vi/vs_line.c
===================================================================
--- head/contrib/nvi/vi/vs_line.c	(revision 366308)
+++ head/contrib/nvi/vi/vs_line.c	(revision 366309)
@@ -1,534 +1,536 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 #ifdef VISIBLE_TAB_CHARS
 #define	TABCH	'-'
 #else
 #define	TABCH	' '
 #endif
 
 /*
  * vs_line --
  *	Update one line on the screen.
  *
  * PUBLIC: int vs_line(SCR *, SMAP *, size_t *, size_t *);
  */
 int
 vs_line(SCR *sp, SMAP *smp, size_t *yp, size_t *xp)
 {
 	u_char *kp;
 	GS *gp;
 	SMAP *tsmp;
 	size_t chlen = 0, cno_cnt, cols_per_screen, len, nlen;
 	size_t offset_in_char, offset_in_line, oldx, oldy;
 	size_t scno, skip_cols, skip_screens;
 	int dne, is_cached, is_partial, is_tab, no_draw;
 	int list_tab, list_dollar;
 	CHAR_T *p;
 	CHAR_T *cbp, *ecbp, cbuf[128];
 	ARG_CHAR_T ch = '\0';
 
 #if defined(DEBUG) && 0
 	TRACE(sp, "vs_line: row %u: line: %u off: %u\n",
 	    smp - HMAP, smp->lno, smp->off);
 #endif
 	/*
 	 * If ex modifies the screen after ex output is already on the screen,
 	 * don't touch it -- we'll get scrolling wrong, at best.
 	 */
 	no_draw = 0;
 	if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1)
 		no_draw = 1;
 	if (F_ISSET(sp, SC_SCR_EXWROTE) && smp - HMAP != LASTLINE(sp))
 		no_draw = 1;
 
 	/*
 	 * Assume that, if the cache entry for the line is filled in, the
 	 * line is already on the screen, and all we need to do is return
 	 * the cursor position.  If the calling routine doesn't need the
 	 * cursor position, we can just return.
 	 */
 	is_cached = SMAP_CACHE(smp);
 	if (yp == NULL && (is_cached || no_draw))
 		return (0);
 
 	/*
 	 * A nasty side effect of this routine is that it returns the screen
 	 * position for the "current" character.  Not pretty, but this is the
 	 * only routine that really knows what's out there.
 	 *
 	 * Move to the line.  This routine can be called by vs_sm_position(),
 	 * which uses it to fill in the cache entry so it can figure out what
 	 * the real contents of the screen are.  Because of this, we have to
 	 * return to whereever we started from.
 	 */
 	gp = sp->gp;
 	(void)gp->scr_cursor(sp, &oldy, &oldx);
 	(void)gp->scr_move(sp, smp - HMAP, 0);
 
 	/* Get the line. */
 	dne = db_get(sp, smp->lno, 0, &p, &len);
 
 	/*
 	 * Special case if we're printing the info/mode line.  Skip printing
 	 * the leading number, as well as other minor setup.  The only time
 	 * this code paints the mode line is when the user is entering text
 	 * for a ":" command, so we can put the code here instead of dealing
 	 * with the empty line logic below.  This is a kludge, but it's pretty
 	 * much confined to this module.
 	 *
 	 * Set the number of columns for this screen.
 	 * Set the number of chars or screens to skip until a character is to
 	 * be displayed.
 	 */
 	cols_per_screen = sp->cols;
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		skip_screens = 0;
 		skip_cols = smp->coff;
 	} else {
 		skip_screens = smp->soff - 1;
 		skip_cols = skip_screens * cols_per_screen;
 	}
 
 	list_tab = O_ISSET(sp, O_LIST);
 	if (F_ISSET(sp, SC_TINPUT_INFO))
 		list_dollar = 0;
 	else {
 		list_dollar = list_tab;
 
 		/*
 		 * If O_NUMBER is set, the line doesn't exist and it's line
 		 * number 1, i.e., an empty file, display the line number.
 		 *
 		 * If O_NUMBER is set, the line exists and the first character
 		 * on the screen is the first character in the line, display
 		 * the line number.
 		 *
 		 * !!!
 		 * If O_NUMBER set, decrement the number of columns in the
 		 * first screen.  DO NOT CHANGE THIS -- IT'S RIGHT!  The
 		 * rest of the code expects this to reflect the number of
 		 * columns in the first screen, regardless of the number of
 		 * columns we're going to skip.
 		 */
 		if (O_ISSET(sp, O_NUMBER)) {
 			cols_per_screen -= O_NUMBER_LENGTH;
 			if ((!dne || smp->lno == 1) && skip_cols == 0) {
 				nlen = snprintf((char*)cbuf,
 				    sizeof(cbuf), O_NUMBER_FMT, (u_long)smp->lno);
 				(void)gp->scr_addstr(sp, (char*)cbuf, nlen);
 			}
 		}
 	}
 
 	/*
 	 * Special case non-existent lines and the first line of an empty
 	 * file.  In both cases, the cursor position is 0, but corrected
 	 * as necessary for the O_NUMBER field, if it was displayed.
 	 */
 	if (dne || len == 0) {
 		/* Fill in the cursor. */
 		if (yp != NULL && smp->lno == sp->lno) {
 			*yp = smp - HMAP;
 			*xp = sp->cols - cols_per_screen;
 		}
 
 		/* If the line is on the screen, quit. */
 		if (is_cached || no_draw)
 			goto ret1;
 
 		/* Set line cache information. */
 		smp->c_sboff = smp->c_eboff = 0;
 		smp->c_scoff = smp->c_eclen = 0;
 
 		/*
 		 * Lots of special cases for empty lines, but they only apply
 		 * if we're displaying the first screen of the line.
 		 */
-		if (skip_cols == 0)
+		if (skip_cols == 0) {
 			if (dne) {
 				if (smp->lno == 1) {
 					if (list_dollar) {
 						ch = '$';
 						goto empty;
 					}
 				} else {
 					ch = '~';
 					goto empty;
 				}
-			} else
+			} else {
 				if (list_dollar) {
 					ch = '$';
 empty:					(void)gp->scr_addstr(sp,
 					    KEY_NAME(sp, ch), KEY_LEN(sp, ch));
 				}
+			}
+		}
 
 		(void)gp->scr_clrtoeol(sp);
 		(void)gp->scr_move(sp, oldy, oldx);
 		return (0);
 	}
 
 	/* If we shortened this line in another screen, the cursor
 	 * position may have fallen off.
 	 */
 	if (sp->lno == smp->lno && sp->cno >= len)
 	    sp->cno = len - 1;
 
 	/*
 	 * If we just wrote this or a previous line, we cached the starting
 	 * and ending positions of that line.  The way it works is we keep
 	 * information about the lines displayed in the SMAP.  If we're
 	 * painting the screen in the forward direction, this saves us from
 	 * reformatting the physical line for every line on the screen.  This
 	 * wins big on binary files with 10K lines.
 	 *
 	 * Test for the first screen of the line, then the current screen line,
 	 * then the line behind us, then do the hard work.  Note, it doesn't
 	 * do us any good to have a line in front of us -- it would be really
 	 * hard to try and figure out tabs in the reverse direction, i.e. how
 	 * many spaces a tab takes up in the reverse direction depends on
 	 * what characters preceded it.
 	 *
 	 * Test for the first screen of the line.
 	 */
 	if (skip_cols == 0) {
 		smp->c_sboff = offset_in_line = 0;
 		smp->c_scoff = offset_in_char = 0;
 		p = &p[offset_in_line];
 		goto display;
 	}
 
 	/* Test to see if we've seen this exact line before. */
 	if (is_cached) {
 		offset_in_line = smp->c_sboff;
 		offset_in_char = smp->c_scoff;
 		p = &p[offset_in_line];
 
 		/* Set cols_per_screen to 2nd and later line length. */
 		if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
 			cols_per_screen = sp->cols;
 		goto display;
 	}
 
 	/* Test to see if we saw an earlier part of this line before. */
 	if (smp != HMAP &&
 	    SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) {
 		if (tsmp->c_eclen != tsmp->c_ecsize) {
 			offset_in_line = tsmp->c_eboff;
 			offset_in_char = tsmp->c_eclen;
 		} else {
 			offset_in_line = tsmp->c_eboff + 1;
 			offset_in_char = 0;
 		}
 
 		/* Put starting info for this line in the cache. */
 		smp->c_sboff = offset_in_line;
 		smp->c_scoff = offset_in_char;
 		p = &p[offset_in_line];
 
 		/* Set cols_per_screen to 2nd and later line length. */
 		if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
 			cols_per_screen = sp->cols;
 		goto display;
 	}
 
 	scno = 0;
 	offset_in_line = 0;
 	offset_in_char = 0;
 
 	/* Do it the hard way, for leftright scrolling screens. */
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		for (; offset_in_line < len; ++offset_in_line) {
 			chlen = (ch = *p++) == '\t' && !list_tab ?
 			    TAB_OFF(scno) : KEY_COL(sp, ch);
 			if ((scno += chlen) >= skip_cols)
 				break;
 		}
 
 		/* Set cols_per_screen to 2nd and later line length. */
 		cols_per_screen = sp->cols;
 
 		/* Put starting info for this line in the cache. */
 		if (offset_in_line >= len) {
 			smp->c_sboff = offset_in_line;
 			smp->c_scoff = 255;
 		} else if (scno != skip_cols) {
 			smp->c_sboff = offset_in_line;
 			smp->c_scoff =
 			    offset_in_char = chlen - (scno - skip_cols);
 			--p;
 		} else {
 			smp->c_sboff = ++offset_in_line;
 			smp->c_scoff = 0;
 		}
 	}
 
 	/* Do it the hard way, for historic line-folding screens. */
 	else {
 		for (; offset_in_line < len; ++offset_in_line) {
 			chlen = (ch = *p++) == '\t' && !list_tab ?
 			    TAB_OFF(scno) : KEY_COL(sp, ch);
 			if ((scno += chlen) < cols_per_screen)
 				continue;
 			scno -= cols_per_screen;
 
 			/* Set cols_per_screen to 2nd and later line length. */
 			cols_per_screen = sp->cols;
 
 			/*
 			 * If crossed the last skipped screen boundary, start
 			 * displaying the characters.
 			 */
 			if (--skip_screens == 0)
 				break;
 		}
 
 		/* Put starting info for this line in the cache. */
 		if (scno != 0) {
 			smp->c_sboff = offset_in_line;
 			smp->c_scoff = offset_in_char = chlen - scno;
 			--p;
 		} else {
 			smp->c_sboff = ++offset_in_line;
 			smp->c_scoff = 0;
 		}
 	}
 
 display:
 	/*
 	 * Set the number of characters to skip before reaching the cursor
 	 * character.  Offset by 1 and use 0 as a flag value.  Vs_line is
 	 * called repeatedly with a valid pointer to a cursor position.
 	 * Don't fill anything in unless it's the right line and the right
 	 * character, and the right part of the character...
 	 */
 	if (yp == NULL ||
 	    smp->lno != sp->lno || sp->cno < offset_in_line ||
 	    offset_in_line + cols_per_screen < sp->cno) {
 		cno_cnt = 0;
 		/* If the line is on the screen, quit. */
 		if (is_cached || no_draw)
 			goto ret1;
 	} else
 		cno_cnt = (sp->cno - offset_in_line) + 1;
 
 	/* This is the loop that actually displays characters. */
 	ecbp = (cbp = cbuf) + SIZE(cbuf) - 1;
 	for (is_partial = 0, scno = 0;
 	    offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
 		if ((ch = *p++) == '\t' && !list_tab) {
 			scno += chlen = TAB_OFF(scno) - offset_in_char;
 			is_tab = 1;
 		} else {
 			scno += chlen = KEY_COL(sp, ch) - offset_in_char;
 			is_tab = 0;
 		}
 
 		/*
 		 * Only display up to the right-hand column.  Set a flag if
 		 * the entire character wasn't displayed for use in setting
 		 * the cursor.  If reached the end of the line, set the cache
 		 * info for the screen.  Don't worry about there not being
 		 * characters to display on the next screen, its lno/off won't
 		 * match up in that case.
 		 */
 		if (scno >= cols_per_screen) {
 			if (is_tab == 1) {
 				chlen -= scno - cols_per_screen;
 				smp->c_ecsize = smp->c_eclen = chlen;
 				scno = cols_per_screen;
 			} else {
 				smp->c_ecsize = chlen;
 				chlen -= scno - cols_per_screen;
 				smp->c_eclen = chlen;
 
 				if (scno > cols_per_screen)
 					is_partial = 1;
 			}
 			smp->c_eboff = offset_in_line;
 
 			/* Terminate the loop. */
 			offset_in_line = len;
 		}
 
 		/*
 		 * If the caller wants the cursor value, and this was the
 		 * cursor character, set the value.  There are two ways to
 		 * put the cursor on a character -- if it's normal display
 		 * mode, it goes on the last column of the character.  If
 		 * it's input mode, it goes on the first.  In normal mode,
 		 * set the cursor only if the entire character was displayed.
 		 */
 		if (cno_cnt &&
 		    --cno_cnt == 0 && (F_ISSET(sp, SC_TINPUT) || !is_partial)) {
 			*yp = smp - HMAP;
 			if (F_ISSET(sp, SC_TINPUT))
 				if (is_partial)
 					*xp = scno - smp->c_ecsize;
 				else
 					*xp = scno - chlen;
 			else
 				*xp = scno - 1;
 			if (O_ISSET(sp, O_NUMBER) &&
 			    !F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0)
 				*xp += O_NUMBER_LENGTH;
 
 			/* If the line is on the screen, quit. */
 			if (is_cached || no_draw)
 				goto ret1;
 		}
 
 		/* If the line is on the screen, don't display anything. */
 		if (is_cached || no_draw)
 			continue;
 
-#define	FLUSH {								\
+#define	FLUSH do {							\
 	*cbp = '\0';							\
 	(void)gp->scr_waddstr(sp, cbuf, cbp - cbuf);			\
 	cbp = cbuf;							\
-}
+} while (0)
 		/*
 		 * Display the character.  We do tab expansion here because
 		 * the screen interface doesn't have any way to set the tab
 		 * length.  Note, it's theoretically possible for chlen to
 		 * be larger than cbuf, if the user set a impossibly large
 		 * tabstop.
 		 */
 		if (is_tab)
 			while (chlen--) {
 				if (cbp >= ecbp)
 					FLUSH;
 				*cbp++ = TABCH;
 			}
 		else {
 			if (cbp + chlen >= ecbp)
 				FLUSH;
 
 			/* don't display half a wide character */
 			if (is_partial && CHAR_WIDTH(sp, ch) > 1) {
 				*cbp++ = ' ';
 				break;
 			}
 
 			if (KEY_NEEDSWIDE(sp, ch))
 				*cbp++ = ch;
 			else
 				for (kp = (u_char *)
 				    KEY_NAME(sp, ch) + offset_in_char;
 				    chlen--;)
 					*cbp++ = *kp++;
 		}
 	}
 
 	if (scno < cols_per_screen) {
 		/* If didn't paint the whole line, update the cache. */
 		smp->c_ecsize = smp->c_eclen = KEY_COL(sp, ch);
 		smp->c_eboff = len - 1;
 
 		/*
 		 * If not the info/mode line, and O_LIST set, and at the
 		 * end of the line, and the line ended on this screen,
 		 * add a trailing $.
 		 */
 		if (list_dollar) {
 			++scno;
 
 			chlen = KEY_LEN(sp, '$');
 			if (cbp + chlen >= ecbp)
 				FLUSH;
 			for (kp = (u_char *)
 			    KEY_NAME(sp, '$'); chlen--;)
 				*cbp++ = *kp++;
 		}
 
 		/* If still didn't paint the whole line, clear the rest. */
 		if (scno < cols_per_screen)
 			(void)gp->scr_clrtoeol(sp);
 	}
 
 	/* Flush any buffered characters. */
 	if (cbp > cbuf)
 		FLUSH;
 
 ret1:	(void)gp->scr_move(sp, oldy, oldx);
 	return (0);
 }
 
 /*
  * vs_number --
  *	Repaint the numbers on all the lines.
  *
  * PUBLIC: int vs_number(SCR *);
  */
 int
 vs_number(SCR *sp)
 {
 	GS *gp;
 	SMAP *smp;
 	VI_PRIVATE *vip;
 	size_t len, oldy, oldx;
 	int exist;
 	char nbuf[10];
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	/* No reason to do anything if we're in input mode on the info line. */
 	if (F_ISSET(sp, SC_TINPUT_INFO))
 		return (0);
 
 	/*
 	 * Try and avoid getting the last line in the file, by getting the
 	 * line after the last line in the screen -- if it exists, we know
 	 * we have to to number all the lines in the screen.  Get the one
 	 * after the last instead of the last, so that the info line doesn't
 	 * fool us.  (The problem is that file_lline will lie, and tell us
 	 * that the info line is the last line in the file.) If that test
 	 * fails, we have to check each line for existence.
 	 */
 	exist = db_exist(sp, TMAP->lno + 1);
 
 	(void)gp->scr_cursor(sp, &oldy, &oldx);
 	for (smp = HMAP; smp <= TMAP; ++smp) {
 		/* Numbers are only displayed for the first screen line. */
 		if (O_ISSET(sp, O_LEFTRIGHT)) {
 			if (smp->coff != 0)
 				continue;
 		} else
 			if (smp->soff != 1)
 				continue;
 
 		/*
 		 * The first line of an empty file gets numbered, otherwise
 		 * number any existing line.
 		 */
 		if (smp->lno != 1 && !exist && !db_exist(sp, smp->lno))
 			break;
 
 		(void)gp->scr_move(sp, smp - HMAP, 0);
 		len = snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, (u_long)smp->lno);
 		(void)gp->scr_addstr(sp, nbuf, len);
 	}
 	(void)gp->scr_move(sp, oldy, oldx);
 	return (0);
 }
Index: head/contrib/nvi/vi/vs_msg.c
===================================================================
--- head/contrib/nvi/vi/vs_msg.c	(revision 366308)
+++ head/contrib/nvi/vi/vs_msg.c	(revision 366309)
@@ -1,895 +1,898 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 typedef enum {
 	SCROLL_W,			/* User wait. */
 	SCROLL_W_EX,			/* User wait, or enter : to continue. */
 	SCROLL_W_QUIT			/* User wait, or enter q to quit. */
 					/*
 					 * SCROLL_W_QUIT has another semantic
 					 * -- only wait if the screen is full
 					 */
 } sw_t;
 
 static void	vs_divider(SCR *);
 static void	vs_msgsave(SCR *, mtype_t, char *, size_t);
 static void	vs_output(SCR *, mtype_t, const char *, int);
 static void	vs_scroll(SCR *, int *, sw_t);
 static void	vs_wait(SCR *, int *, sw_t);
 
 /*
  * vs_busy --
  *	Display, update or clear a busy message.
  *
  * This routine is the default editor interface for vi busy messages.  It
  * implements a standard strategy of stealing lines from the bottom of the
  * vi text screen.  Screens using an alternate method of displaying busy
  * messages, e.g. X11 clock icons, should set their scr_busy function to the
  * correct function before calling the main editor routine.
  *
  * PUBLIC: void vs_busy(SCR *, const char *, busy_t);
  */
 void
 vs_busy(SCR *sp, const char *msg, busy_t btype)
 {
 	GS *gp;
 	VI_PRIVATE *vip;
 	static const char flagc[] = "|/-\\";
 	struct timespec ts, ts_diff;
 	const struct timespec ts_min = { 0, 125000000 };
 	size_t len, notused;
 	const char *p;
 
 	/* Ex doesn't display busy messages. */
 	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
 		return;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	/*
 	 * Most of this routine is to deal with the screen sharing real estate
 	 * between the normal edit messages and the busy messages.  Logically,
 	 * all that's needed is something that puts up a message, periodically
 	 * updates it, and then goes away.
 	 */
 	switch (btype) {
 	case BUSY_ON:
 		++vip->busy_ref;
 		if (vip->totalcount != 0 || vip->busy_ref != 1)
 			break;
 
 		/* Initialize state for updates. */
 		vip->busy_ch = 0;
 		timepoint_steady(&vip->busy_ts);
 
 		/* Save the current cursor. */
 		(void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
 
 		/* Display the busy message. */
 		p = msg_cat(sp, msg, &len);
 		(void)gp->scr_move(sp, LASTLINE(sp), 0);
 		(void)gp->scr_addstr(sp, p, len);
 		(void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
 		(void)gp->scr_clrtoeol(sp);
 		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
 		break;
 	case BUSY_OFF:
 		if (vip->busy_ref == 0)
 			break;
 		--vip->busy_ref;
 
 		/*
 		 * If the line isn't in use for another purpose, clear it.
 		 * Always return to the original position.
 		 */
 		if (vip->totalcount == 0 && vip->busy_ref == 0) {
 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
 			(void)gp->scr_clrtoeol(sp);
 		}
 		(void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
 		break;
 	case BUSY_UPDATE:
 		if (vip->totalcount != 0 || vip->busy_ref == 0)
 			break;
 
 		/* Update no more than every 1/8 of a second. */
 		timepoint_steady(&ts);
 		ts_diff = ts;
 		timespecsub(&ts_diff, &vip->busy_ts);
 		if (timespeccmp(&ts_diff, &ts_min, <))
 			return;
 		vip->busy_ts = ts;
 
 		/* Display the update. */
 		if (vip->busy_ch == sizeof(flagc) - 1)
 			vip->busy_ch = 0;
 		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
 		(void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
 		(void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
 		break;
 	}
 	(void)gp->scr_refresh(sp, 0);
 }
 
 /* 
  * vs_home --
  *	Home the cursor to the bottom row, left-most column.
  *
  * PUBLIC: void vs_home(SCR *);
  */
 void
 vs_home(SCR *sp)
 {
 	(void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
 	(void)sp->gp->scr_refresh(sp, 0);
 }
 
 /*
  * vs_update --
  *	Update a command.
  *
  * PUBLIC: void vs_update(SCR *, const char *, const CHAR_T *);
  */
 void
 vs_update(SCR *sp, const char *m1, const CHAR_T *m2)
 {
 	GS *gp;
 	size_t len, mlen, oldx, oldy;
 	CONST char *np;
 	size_t nlen;
 
 	gp = sp->gp;
 
 	/*
 	 * This routine displays a message on the bottom line of the screen,
 	 * without updating any of the command structures that would keep it
 	 * there for any period of time, i.e. it is overwritten immediately.
 	 *
 	 * It's used by the ex read and ! commands when the user's command is
 	 * expanded, and by the ex substitution confirmation prompt.
 	 */
 	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
 		if (m2 != NULL)
 			INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen);
 		(void)ex_printf(sp,
 		    "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np);
 		(void)ex_fflush(sp);
 	}
 
 	/*
 	 * Save the cursor position, the substitute-with-confirmation code
 	 * will have already set it correctly.
 	 */
 	(void)gp->scr_cursor(sp, &oldy, &oldx);
 
 	/* Clear the bottom line. */
 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
 	(void)gp->scr_clrtoeol(sp);
 
 	/*
 	 * XXX
 	 * Don't let long file names screw up the screen.
 	 */
 	if (m1 != NULL) {
 		mlen = len = strlen(m1);
 		if (len > sp->cols - 2)
 			mlen = len = sp->cols - 2;
 		(void)gp->scr_addstr(sp, m1, mlen);
 	} else
 		len = 0;
 	if (m2 != NULL) {
 		mlen = STRLEN(m2);
 		if (len + mlen > sp->cols - 2)
 			mlen = (sp->cols - 2) - len;
 		(void)gp->scr_waddstr(sp, m2, mlen);
 	}
 
 	(void)gp->scr_move(sp, oldy, oldx);
 	(void)gp->scr_refresh(sp, 0);
 }
 
 /*
  * vs_msg --
  *	Display ex output or error messages for the screen.
  *
  * This routine is the default editor interface for all ex output, and all ex
  * and vi error/informational messages.  It implements the standard strategy
  * of stealing lines from the bottom of the vi text screen.  Screens using an
  * alternate method of displaying messages, e.g. dialog boxes, should set their
  * scr_msg function to the correct function before calling the editor.
  *
  * PUBLIC: void vs_msg(SCR *, mtype_t, char *, size_t);
  */
 void
 vs_msg(SCR *sp, mtype_t mtype, char *line, size_t len)
 {
 	GS *gp;
 	VI_PRIVATE *vip;
 	size_t maxcols, oldx, oldy, padding;
 	const char *e, *s, *t;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	/*
 	 * Ring the bell if it's scheduled.
 	 *
 	 * XXX
 	 * Shouldn't we save this, too?
 	 */
-	if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
+	if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) {
 		if (F_ISSET(sp, SC_SCR_VI)) {
 			F_CLR(gp, G_BELLSCHED);
 			(void)gp->scr_bell(sp);
 		} else
 			F_SET(gp, G_BELLSCHED);
+	}
 
 	/*
 	 * If vi is using the error line for text input, there's no screen
 	 * real-estate for the error message.  Nothing to do without some
 	 * information as to how important the error message is.
 	 */
 	if (F_ISSET(sp, SC_TINPUT_INFO))
 		return;
 
 	/*
 	 * Ex or ex controlled screen output.
 	 *
 	 * If output happens during startup, e.g., a .exrc file, we may be
 	 * in ex mode but haven't initialized the screen.  Initialize here,
 	 * and in this case, stay in ex mode.
 	 *
 	 * If the SC_SCR_EXWROTE bit is set, then we're switching back and
 	 * forth between ex and vi, but the screen is trashed and we have
 	 * to respect that.  Switch to ex mode long enough to put out the
 	 * message.
 	 *
 	 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
 	 * the screen, so previous opinions are ignored.
 	 */
 	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
-		if (!F_ISSET(sp, SC_SCR_EX))
+		if (!F_ISSET(sp, SC_SCR_EX)) {
 			if (F_ISSET(sp, SC_SCR_EXWROTE)) {
 				if (sp->gp->scr_screen(sp, SC_EX))
 					return;
 			} else
 				if (ex_init(sp))
 					return;
+		}
 
 		if (mtype == M_ERR)
 			(void)gp->scr_attr(sp, SA_INVERSE, 1);
 		(void)printf("%.*s", (int)len, line);
 		if (mtype == M_ERR)
 			(void)gp->scr_attr(sp, SA_INVERSE, 0);
 		(void)fflush(stdout);
 
 		F_CLR(sp, SC_EX_WAIT_NO);
 
 		if (!F_ISSET(sp, SC_SCR_EX))
 			(void)sp->gp->scr_screen(sp, SC_VI);
 		return;
 	}
 
 	/* If the vi screen isn't ready, save the message. */
 	if (!F_ISSET(sp, SC_SCR_VI)) {
 		(void)vs_msgsave(sp, mtype, line, len);
 		return;
 	}
 
 	/* Save the cursor position. */
 	(void)gp->scr_cursor(sp, &oldy, &oldx);
 
 	/* If it's an ex output message, just write it out. */
 	if (mtype == M_NONE) {
 		vs_output(sp, mtype, line, len);
 		goto ret;
 	}
 
 	/*
 	 * If it's a vi message, strip the trailing <newline> so we can
 	 * try and paste messages together.
 	 */
 	if (line[len - 1] == '\n')
 		--len;
 
 	/*
 	 * If a message won't fit on a single line, try to split on a <blank>.
 	 * If a subsequent message fits on the same line, write a separator
 	 * and output it.  Otherwise, put out a newline.
 	 *
 	 * Need up to two padding characters normally; a semi-colon and a
 	 * separating space.  If only a single line on the screen, add some
 	 * more for the trailing continuation message.
 	 *
 	 * XXX
 	 * Assume that periods and semi-colons take up a single column on the
 	 * screen.
 	 *
 	 * XXX
 	 * There are almost certainly pathological cases that will break this
 	 * code.
 	 */
 	if (IS_ONELINE(sp))
 		(void)msg_cmsg(sp, CMSG_CONT_S, &padding);
 	else
 		padding = 0;
 	padding += 2;
 
 	maxcols = sp->cols - 1;
-	if (vip->lcontinue != 0)
+	if (vip->lcontinue != 0) {
 		if (len + vip->lcontinue + padding > maxcols)
 			vs_output(sp, vip->mtype, ".\n", 2);
 		else  {
 			vs_output(sp, vip->mtype, ";", 1);
 			vs_output(sp, M_NONE, " ", 1);
 		}
+	}
 	vip->mtype = mtype;
 	for (s = line;; s = t) {
 		for (; len > 0 && isblank((u_char)*s); --len, ++s);
 		if (len == 0)
 			break;
 		if (len + vip->lcontinue > maxcols) {
 			for (e = s + (maxcols - vip->lcontinue);
 			    e > s && !isblank((u_char)*e); --e);
 			if (e == s)
 				 e = t = s + (maxcols - vip->lcontinue);
 			else
 				for (t = e; isblank((u_char)e[-1]); --e);
 		} else
 			e = t = s + len;
 
 		/*
 		 * If the message ends in a period, discard it, we want to
 		 * gang messages where possible.
 		 */
 		len -= t - s;
 		if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
 			--e;
 		vs_output(sp, mtype, s, e - s);
 
 		if (len != 0)
 			vs_output(sp, M_NONE, "\n", 1);
 
 		if (INTERRUPTED(sp))
 			break;
 	}
 
 ret:	(void)gp->scr_move(sp, oldy, oldx);
 	(void)gp->scr_refresh(sp, 0);
 }
 
 /*
  * vs_output --
  *	Output the text to the screen.
  */
 static void
 vs_output(SCR *sp, mtype_t mtype, const char *line, int llen)
 {
 	GS *gp;
 	VI_PRIVATE *vip;
 	size_t notused;
 	int len, rlen, tlen;
 	const char *p, *t;
 	char *cbp, *ecbp, cbuf[128];
 
 	gp = sp->gp;
 	vip = VIP(sp);
 	for (p = line, rlen = llen; llen > 0;) {
 		/* Get the next physical line. */
 		if ((p = memchr(line, '\n', llen)) == NULL)
 			len = llen;
 		else
 			len = p - line;
 
 		/*
 		 * The max is sp->cols characters, and we may have already
 		 * written part of the line.
 		 */
 		if (len + vip->lcontinue > sp->cols)
 			len = sp->cols - vip->lcontinue;
 
 		/*
 		 * If the first line output, do nothing.  If the second line
 		 * output, draw the divider line.  If drew a full screen, we
 		 * remove the divider line.  If it's a continuation line, move
 		 * to the continuation point, else, move the screen up.
 		 */
 		if (vip->lcontinue == 0) {
 			if (!IS_ONELINE(sp)) {
 				if (vip->totalcount == 1) {
 					(void)gp->scr_move(sp,
 					    LASTLINE(sp) - 1, 0);
 					(void)gp->scr_clrtoeol(sp);
 					(void)vs_divider(sp);
 					F_SET(vip, VIP_DIVIDER);
 					++vip->totalcount;
 					++vip->linecount;
 				}
 				if (vip->totalcount == sp->t_maxrows &&
 				    F_ISSET(vip, VIP_DIVIDER)) {
 					--vip->totalcount;
 					--vip->linecount;
 					F_CLR(vip, VIP_DIVIDER);
 				}
 			}
 			if (vip->totalcount != 0)
 				vs_scroll(sp, NULL, SCROLL_W_QUIT);
 
 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
 			++vip->totalcount;
 			++vip->linecount;
 
 			if (INTERRUPTED(sp))
 				break;
 		} else
 			(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
 
 		/* Error messages are in inverse video. */
 		if (mtype == M_ERR)
 			(void)gp->scr_attr(sp, SA_INVERSE, 1);
 
 		/* Display the line, doing character translation. */
-#define	FLUSH {								\
+#define	FLUSH do {							\
 	*cbp = '\0';							\
 	(void)gp->scr_addstr(sp, cbuf, cbp - cbuf);			\
 	cbp = cbuf;							\
-}
+} while (0)
 		ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
 		for (t = line, tlen = len; tlen--; ++t) {
 			/*
 			 * Replace tabs with spaces, there are places in
 			 * ex that do column calculations without looking
 			 * at <tabs> -- and all routines that care about
 			 * <tabs> do their own expansions.  This catches
 			 * <tabs> in things like tag search strings.
 			 */
 			if (cbp + 1 >= ecbp)
 				FLUSH;
 			*cbp++ = *t == '\t' ? ' ' : *t;
 		}
 		if (cbp > cbuf)
 			FLUSH;
 		if (mtype == M_ERR)
 			(void)gp->scr_attr(sp, SA_INVERSE, 0);
 
 		/* Clear the rest of the line. */
 		(void)gp->scr_clrtoeol(sp);
 
 		/* If we loop, it's a new line. */
 		vip->lcontinue = 0;
 
 		/* Reset for the next line. */
 		line += len;
 		llen -= len;
 		if (p != NULL) {
 			++line;
 			--llen;
 		}
 	}
 
 	/* Set up next continuation line. */
 	if (p == NULL)
 		gp->scr_cursor(sp, &notused, &vip->lcontinue);
 }
 
 /*
  * vs_ex_resolve --
  *	Deal with ex message output.
  *
  * This routine is called when exiting a colon command to resolve any ex
  * output that may have occurred.
  *
  * PUBLIC: int vs_ex_resolve(SCR *, int *);
  */
 int
 vs_ex_resolve(SCR *sp, int *continuep)
 {
 	EVENT ev;
 	GS *gp;
 	VI_PRIVATE *vip;
 	sw_t wtype;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 	*continuep = 0;
 
 	/* If we ran any ex command, we can't trust the cursor position. */
 	F_SET(vip, VIP_CUR_INVALID);
 
 	/* Terminate any partially written message. */
 	if (vip->lcontinue != 0) {
 		vs_output(sp, vip->mtype, ".", 1);
 		vip->lcontinue = 0;
 
 		vip->mtype = M_NONE;
 	}
 
 	/*
 	 * If we switched out of the vi screen into ex, switch back while we
 	 * figure out what to do with the screen and potentially get another
 	 * command to execute.
 	 *
 	 * If we didn't switch into ex, we're not required to wait, and less
 	 * than 2 lines of output, we can continue without waiting for the
 	 * wait.
 	 *
 	 * Note, all other code paths require waiting, so we leave the report
 	 * of modified lines until later, so that we won't wait for no other
 	 * reason than a threshold number of lines were modified.  This means
 	 * we display cumulative line modification reports for groups of ex
 	 * commands.  That seems right to me (well, at least not wrong).
 	 */
 	if (F_ISSET(sp, SC_SCR_EXWROTE)) {
 		if (sp->gp->scr_screen(sp, SC_VI))
 			return (1);
 	} else
 		if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
 			F_CLR(sp, SC_EX_WAIT_NO);
 			return (0);
 		}
 
 	/* Clear the required wait flag, it's no longer needed. */
 	F_CLR(sp, SC_EX_WAIT_YES);
 
 	/*
 	 * Wait, unless explicitly told not to wait or the user interrupted
 	 * the command.  If the user is leaving the screen, for any reason,
 	 * they can't continue with further ex commands.
 	 */
 	if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
 		wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
 		    SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
 		if (F_ISSET(sp, SC_SCR_EXWROTE))
 			vs_wait(sp, continuep, wtype);
 		else
 			vs_scroll(sp, continuep, wtype);
 		if (*continuep)
 			return (0);
 	}
 
 	/* If ex wrote on the screen, refresh the screen image. */
 	if (F_ISSET(sp, SC_SCR_EXWROTE))
 		F_SET(vip, VIP_N_EX_PAINT);
 
 	/*
 	 * If we're not the bottom of the split screen stack, the screen
 	 * image itself is wrong, so redraw everything.
 	 */
 	if (TAILQ_NEXT(sp, q) != NULL)
 		F_SET(sp, SC_SCR_REDRAW);
 
 	/* If ex changed the underlying file, the map itself is wrong. */
 	if (F_ISSET(vip, VIP_N_EX_REDRAW))
 		F_SET(sp, SC_SCR_REFORMAT);
 
 	/* Ex may have switched out of the alternate screen, return. */
 	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
 
 	/*
 	 * Whew.  We're finally back home, after what feels like years.
 	 * Kiss the ground.
 	 */
 	F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
 
 	/*
 	 * We may need to repaint some of the screen, e.g.:
 	 *
 	 *	:set
 	 *	:!ls
 	 *
 	 * gives us a combination of some lines that are "wrong", and a need
 	 * for a full refresh.
 	 */
 	if (vip->totalcount > 1) {
 		/* Set up the redraw of the overwritten lines. */
 		ev.e_event = E_REPAINT;
 		ev.e_flno = vip->totalcount >=
 		    sp->rows ? 1 : sp->rows - vip->totalcount;
 		ev.e_tlno = sp->rows;
 
 		/* Reset the count of overwriting lines. */
 		vip->linecount = vip->lcontinue = vip->totalcount = 0;
 
 		/* Redraw. */
 		(void)vs_repaint(sp, &ev);
 	} else
 		/* Reset the count of overwriting lines. */
 		vip->linecount = vip->lcontinue = vip->totalcount = 0;
 
 	return (0);
 }
 
 /*
  * vs_resolve --
  *	Deal with message output.
  *
  * PUBLIC: int vs_resolve(SCR *, SCR *, int);
  */
 int
 vs_resolve(SCR *sp, SCR *csp, int forcewait)
 {
 	EVENT ev;
 	GS *gp;
 	MSGS *mp;
 	VI_PRIVATE *vip;
 	size_t oldy, oldx;
 	int redraw;
 
 	/*
 	 * Vs_resolve is called from the main vi loop and the refresh function
 	 * to periodically ensure that the user has seen any messages that have
 	 * been displayed and that any status lines are correct.  The sp screen
 	 * is the screen we're checking, usually the current screen.  When it's
 	 * not, csp is the current screen, used for final cursor positioning.
 	 */
 	gp = sp->gp;
 	vip = VIP(sp);
 	if (csp == NULL)
 		csp = sp;
 
 	/* Save the cursor position. */
 	(void)gp->scr_cursor(csp, &oldy, &oldx);
 
 	/* Ring the bell if it's scheduled. */
 	if (F_ISSET(gp, G_BELLSCHED)) {
 		F_CLR(gp, G_BELLSCHED);
 		(void)gp->scr_bell(sp);
 	}
 
 	/* Display new file status line. */
 	if (F_ISSET(sp, SC_STATUS)) {
 		F_CLR(sp, SC_STATUS);
 		msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
 	}
 
 	/* Report on line modifications. */
 	mod_rpt(sp);
 
 	/*
 	 * Flush any saved messages.  If the screen isn't ready, refresh
 	 * it.  (A side-effect of screen refresh is that we can display
 	 * messages.)  Once this is done, don't trust the cursor.  That
 	 * extra refresh screwed the pooch.
 	 */
 	if (!SLIST_EMPTY(gp->msgq)) {
 		if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
 			return (1);
 		while ((mp = SLIST_FIRST(gp->msgq)) != NULL) {
 			gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
 			SLIST_REMOVE_HEAD(gp->msgq, q);
 			free(mp->buf);
 			free(mp);
 		}
 		F_SET(vip, VIP_CUR_INVALID);
 	}
 
 	switch (vip->totalcount) {
 	case 0:
 		redraw = 0;
 		break;
 	case 1:
 		/*
 		 * If we're switching screens, we have to wait for messages,
 		 * regardless.  If we don't wait, skip updating the modeline.
 		 */
 		if (forcewait)
 			vs_scroll(sp, NULL, SCROLL_W);
 		else
 			F_SET(vip, VIP_S_MODELINE);
 
 		redraw = 0;
 		break;
 	default:
 		/*
 		 * If >1 message line in use, prompt the user to continue and
 		 * repaint overwritten lines.
 		 */
 		vs_scroll(sp, NULL, SCROLL_W);
 
 		ev.e_event = E_REPAINT;
 		ev.e_flno = vip->totalcount >=
 		    sp->rows ? 1 : sp->rows - vip->totalcount;
 		ev.e_tlno = sp->rows;
 
 		redraw = 1;
 		break;
 	}
 
 	/* Reset the count of overwriting lines. */
 	vip->linecount = vip->lcontinue = vip->totalcount = 0;
 
 	/* Redraw. */
 	if (redraw)
 		(void)vs_repaint(sp, &ev);
 
 	/* Restore the cursor position. */
 	(void)gp->scr_move(csp, oldy, oldx);
 
 	return (0);
 }
 
 /*
  * vs_scroll --
  *	Scroll the screen for output.
  */
 static void
 vs_scroll(SCR *sp, int *continuep, sw_t wtype)
 {
 	GS *gp;
 	VI_PRIVATE *vip;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 	if (!IS_ONELINE(sp)) {
 		/*
 		 * Scroll the screen.  Instead of scrolling the entire screen,
 		 * delete the line above the first line output so preserve the
 		 * maximum amount of the screen.
 		 */
 		(void)gp->scr_move(sp, vip->totalcount <
 		    sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
 		(void)gp->scr_deleteln(sp);
 
 		/* If there are screens below us, push them back into place. */
 		if (TAILQ_NEXT(sp, q) != NULL) {
 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
 			(void)gp->scr_insertln(sp);
 		}
 	}
 	if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
 		return;
 	vs_wait(sp, continuep, wtype);
 }
 
 /*
  * vs_wait --
  *	Prompt the user to continue.
  */
 static void
 vs_wait(SCR *sp, int *continuep, sw_t wtype)
 {
 	EVENT ev;
 	VI_PRIVATE *vip;
 	const char *p;
 	GS *gp;
 	size_t len;
 
 	gp = sp->gp;
 	vip = VIP(sp);
 
 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
 	if (IS_ONELINE(sp))
 		p = msg_cmsg(sp, CMSG_CONT_S, &len);
 	else
 		switch (wtype) {
 		case SCROLL_W_QUIT:
 			p = msg_cmsg(sp, CMSG_CONT_Q, &len);
 			break;
 		case SCROLL_W_EX:
 			p = msg_cmsg(sp, CMSG_CONT_EX, &len);
 			break;
 		case SCROLL_W:
 			p = msg_cmsg(sp, CMSG_CONT, &len);
 			break;
 		default:
 			abort();
 			/* NOTREACHED */
 		}
 	(void)gp->scr_addstr(sp, p, len);
 
 	++vip->totalcount;
 	vip->linecount = 0;
 
 	(void)gp->scr_clrtoeol(sp);
 	(void)gp->scr_refresh(sp, 0);
 
 	/* Get a single character from the terminal. */
 	if (continuep != NULL)
 		*continuep = 0;
 	for (;;) {
 		if (v_event_get(sp, &ev, 0, 0))
 			return;
 		if (ev.e_event == E_CHARACTER)
 			break;
 		if (ev.e_event == E_INTERRUPT) {
 			ev.e_c = CH_QUIT;
 			F_SET(gp, G_INTERRUPTED);
 			break;
 		}
 		(void)gp->scr_bell(sp);
 	}
 	switch (wtype) {
 	case SCROLL_W_QUIT:
 		if (ev.e_c == CH_QUIT)
 			F_SET(gp, G_INTERRUPTED);
 		break;
 	case SCROLL_W_EX:
 		if (ev.e_c == ':' && continuep != NULL)
 			*continuep = 1;
 		break;
 	case SCROLL_W:
 		break;
 	}
 }
 
 /*
  * vs_divider --
  *	Draw a dividing line between the screen and the output.
  */
 static void
 vs_divider(SCR *sp)
 {
 	GS *gp;
 	size_t len;
 
 #define	DIVIDESTR	"+=+=+=+=+=+=+=+"
 	len =
 	    sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
 	gp = sp->gp;
 	(void)gp->scr_attr(sp, SA_INVERSE, 1);
 	(void)gp->scr_addstr(sp, DIVIDESTR, len);
 	(void)gp->scr_attr(sp, SA_INVERSE, 0);
 }
 
 /*
  * vs_msgsave --
  *	Save a message for later display.
  */
 static void
 vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len)
 {
 	GS *gp;
 	MSGS *mp_c, *mp_n;
 
 	/*
 	 * We have to handle messages before we have any place to put them.
 	 * If there's no screen support yet, allocate a msg structure, copy
 	 * in the message, and queue it on the global structure.  If we can't
 	 * allocate memory here, we're genuinely screwed, dump the message
 	 * to stderr in the (probably) vain hope that someone will see it.
 	 */
 	CALLOC_GOTO(sp, mp_n, 1, sizeof(MSGS));
 	MALLOC_GOTO(sp, mp_n->buf, len);
 
 	memmove(mp_n->buf, p, len);
 	mp_n->len = len;
 	mp_n->mtype = mt;
 
 	gp = sp->gp;
 	if (SLIST_EMPTY(gp->msgq)) {
 		SLIST_INSERT_HEAD(gp->msgq, mp_n, q);
 	} else {
 		SLIST_FOREACH(mp_c, gp->msgq, q)
 			if (SLIST_NEXT(mp_c, q) == NULL)
 				break;
 		SLIST_INSERT_AFTER(mp_c, mp_n, q);
 	}
 	return;
 
 alloc_err:
 	free(mp_n);
 	(void)fprintf(stderr, "%.*s\n", (int)len, p);
 }
Index: head/contrib/nvi/vi/vs_refresh.c
===================================================================
--- head/contrib/nvi/vi/vs_refresh.c	(revision 366308)
+++ head/contrib/nvi/vi/vs_refresh.c	(revision 366309)
@@ -1,883 +1,886 @@
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <ctype.h>
 #include <libgen.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 #define	UPDATE_CURSOR	0x01			/* Update the cursor. */
 #define	UPDATE_SCREEN	0x02			/* Flush to screen. */
 
 static void	vs_modeline(SCR *);
 static int	vs_paint(SCR *, u_int);
 
 /*
  * v_repaint --
  *	Repaint selected lines from the screen.
  *
  * PUBLIC: int vs_repaint(SCR *, EVENT *);
  */
 int
 vs_repaint(
 	SCR *sp,
 	EVENT *evp)
 {
 	SMAP *smp;
 
 	for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) {
 		smp = HMAP + evp->e_flno - 1;
 		SMAP_FLUSH(smp);
 		if (vs_line(sp, smp, NULL, NULL))
 			return (1);
 	}
 	return (0);
 }
 
 /*
  * vs_refresh --
  *	Refresh all screens.
  *
  * PUBLIC: int vs_refresh(SCR *, int);
  */
 int
 vs_refresh(
 	SCR *sp,
 	int forcepaint)
 {
 	GS *gp;
 	SCR *tsp;
 	int need_refresh = 0;
 	u_int priv_paint, pub_paint;
 
 	gp = sp->gp;
 
 	/*
 	 * 1: Refresh the screen.
 	 *
 	 * If SC_SCR_REDRAW is set in the current screen, repaint everything
 	 * that we can find, including status lines.
 	 */
 	if (F_ISSET(sp, SC_SCR_REDRAW))
 		TAILQ_FOREACH(tsp, gp->dq, q)
 			if (tsp != sp)
 				F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
 
 	/*
 	 * 2: Related or dirtied screens, or screens with messages.
 	 *
 	 * If related screens share a view into a file, they may have been
 	 * modified as well.  Refresh any screens that aren't exiting that
 	 * have paint or dirty bits set.  Always update their screens, we
 	 * are not likely to get another chance.  Finally, if we refresh any
 	 * screens other than the current one, the cursor will be trashed.
 	 */
 	pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
 	priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
 	if (O_ISSET(sp, O_NUMBER))
 		priv_paint |= VIP_N_RENUMBER;
 	TAILQ_FOREACH(tsp, gp->dq, q)
 		if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
 		    (F_ISSET(tsp, pub_paint) ||
 		    F_ISSET(VIP(tsp), priv_paint))) {
 			(void)vs_paint(tsp,
 			    (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
 			    UPDATE_CURSOR : 0) | UPDATE_SCREEN);
 			F_SET(VIP(sp), VIP_CUR_INVALID);
 		}
 
 	/*
 	 * 3: Refresh the current screen.
 	 *
 	 * Always refresh the current screen, it may be a cursor movement.
 	 * Also, always do it last -- that way, SC_SCR_REDRAW can be set
 	 * in the current screen only, and the screen won't flash.
 	 */
 	if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
 	    F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
 		return (1);
 
 	/*
 	 * 4: Paint any missing status lines.
 	 *
 	 * XXX
 	 * This is fairly evil.  Status lines are written using the vi message
 	 * mechanism, since we have no idea how long they are.  Since we may be
 	 * painting screens other than the current one, we don't want to make
 	 * the user wait.  We depend heavily on there not being any other lines
 	 * currently waiting to be displayed and the message truncation code in
 	 * the msgq_status routine working.
 	 *
 	 * And, finally, if we updated any status lines, make sure the cursor
 	 * gets back to where it belongs.
 	 */
 	TAILQ_FOREACH(tsp, gp->dq, q)
 		if (F_ISSET(tsp, SC_STATUS)) {
 			need_refresh = 1;
 			vs_resolve(tsp, sp, 0);
 		}
 	if (need_refresh)
 		(void)gp->scr_refresh(sp, 0);
 
 	/*
 	 * A side-effect of refreshing the screen is that it's now ready
 	 * for everything else, i.e. messages.
 	 */
 	F_SET(sp, SC_SCR_VI);
 	return (0);
 }
 
 /*
  * vs_paint --
  *	This is the guts of the vi curses screen code.  The idea is that
  *	the SCR structure passed in contains the new coordinates of the
  *	screen.  What makes this hard is that we don't know how big
  *	characters are, doing input can put the cursor in illegal places,
  *	and we're frantically trying to avoid repainting unless it's
  *	absolutely necessary.  If you change this code, you'd better know
  *	what you're doing.  It's subtle and quick to anger.
  */
 static int
 vs_paint(
 	SCR *sp,
 	u_int flags)
 {
 	GS *gp;
 	SMAP *smp, tmp;
 	VI_PRIVATE *vip;
 	recno_t lastline, lcnt;
 	size_t cwtotal, cnt, len, notused, off, y;
 	int ch = 0, didpaint, isempty, leftright_warp;
 	CHAR_T *p;
 
 #define	 LNO	sp->lno			/* Current file line. */
 #define	OLNO	vip->olno		/* Remembered file line. */
 #define	 CNO	sp->cno			/* Current file column. */
 #define	OCNO	vip->ocno		/* Remembered file column. */
 #define	SCNO	vip->sc_col		/* Current screen column. */
 
 	gp = sp->gp;
 	vip = VIP(sp);
 	didpaint = leftright_warp = 0;
 
 	/*
 	 * 5: Reformat the lines.
 	 *
 	 * If the lines themselves have changed (:set list, for example),
 	 * fill in the map from scratch.  Adjust the screen that's being
 	 * displayed if the leftright flag is set.
 	 */
 	if (F_ISSET(sp, SC_SCR_REFORMAT)) {
 		/* Invalidate the line size cache. */
 		VI_SCR_CFLUSH(vip);
 
 		/* Toss vs_line() cached information. */
 		if (F_ISSET(sp, SC_SCR_TOP)) {
 			if (vs_sm_fill(sp, LNO, P_TOP))
 				return (1);
 		}
 		else if (F_ISSET(sp, SC_SCR_CENTER)) {
 			if (vs_sm_fill(sp, LNO, P_MIDDLE))
 				return (1);
 		} else
 			if (vs_sm_fill(sp, OOBLNO, P_TOP))
 				return (1);
 		F_SET(sp, SC_SCR_REDRAW);
 	}
 
 	/*
 	 * 6: Line movement.
 	 *
 	 * Line changes can cause the top line to change as well.  As
 	 * before, if the movement is large, the screen is repainted.
 	 *
 	 * 6a: Small screens.
 	 *
 	 * Users can use the window, w300, w1200 and w9600 options to make
 	 * the screen artificially small.  The behavior of these options
 	 * in the historic vi wasn't all that consistent, and, in fact, it
 	 * was never documented how various screen movements affected the
 	 * screen size.  Generally, one of three things would happen:
 	 *	1: The screen would expand in size, showing the line
 	 *	2: The screen would scroll, showing the line
 	 *	3: The screen would compress to its smallest size and
 	 *		repaint.
 	 * In general, scrolling didn't cause compression (200^D was handled
 	 * the same as ^D), movement to a specific line would (:N where N
 	 * was 1 line below the screen caused a screen compress), and cursor
 	 * movement would scroll if it was 11 lines or less, and compress if
 	 * it was more than 11 lines.  (And, no, I have no idea where the 11
 	 * comes from.)
 	 *
 	 * What we do is try and figure out if the line is less than half of
 	 * a full screen away.  If it is, we expand the screen if there's
 	 * room, and then scroll as necessary.  The alternative is to compress
 	 * and repaint.
 	 *
 	 * !!!
 	 * This code is a special case from beginning to end.  Unfortunately,
 	 * home modems are still slow enough that it's worth having.
 	 *
 	 * XXX
 	 * If the line a really long one, i.e. part of the line is on the
 	 * screen but the column offset is not, we'll end up in the adjust
 	 * code, when we should probably have compressed the screen.
 	 */
-	if (IS_SMALL(sp))
+	if (IS_SMALL(sp)) {
 		if (LNO < HMAP->lno) {
 			lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
 			if (lcnt <= HALFSCREEN(sp))
 				for (; lcnt && sp->t_rows != sp->t_maxrows;
 				     --lcnt, ++sp->t_rows) {
 					++TMAP;
 					if (vs_sm_1down(sp))
 						return (1);
 				}
 			else
 				goto small_fill;
 		} else if (LNO > TMAP->lno) {
 			lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
 			if (lcnt <= HALFSCREEN(sp))
 				for (; lcnt && sp->t_rows != sp->t_maxrows;
 				     --lcnt, ++sp->t_rows) {
 					if (vs_sm_next(sp, TMAP, TMAP + 1))
 						return (1);
 					++TMAP;
 					if (vs_line(sp, TMAP, NULL, NULL))
 						return (1);
 				}
 			else {
 small_fill:			(void)gp->scr_move(sp, LASTLINE(sp), 0);
 				(void)gp->scr_clrtoeol(sp);
 				for (; sp->t_rows > sp->t_minrows;
 				    --sp->t_rows, --TMAP) {
 					(void)gp->scr_move(sp, TMAP - HMAP, 0);
 					(void)gp->scr_clrtoeol(sp);
 				}
 				if (vs_sm_fill(sp, LNO, P_FILL))
 					return (1);
 				F_SET(sp, SC_SCR_REDRAW);
 				goto adjust;
 			}
 		}
+	}
 
 	/*
 	 * 6b: Line down, or current screen.
 	 */
 	if (LNO >= HMAP->lno) {
 		/* Current screen. */
 		if (LNO <= TMAP->lno)
 			goto adjust;
 		if (F_ISSET(sp, SC_SCR_TOP))
 			goto top;
 		if (F_ISSET(sp, SC_SCR_CENTER))
 			goto middle;
 
 		/*
 		 * If less than half a screen above the line, scroll down
 		 * until the line is on the screen.
 		 */
 		lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
 		if (lcnt < HALFTEXT(sp)) {
 			while (lcnt--)
 				if (vs_sm_1up(sp))
 					return (1);
 			goto adjust;
 		}
 		goto bottom;
 	}
 
 	/*
 	 * 6c: If not on the current screen, may request center or top.
 	 */
 	if (F_ISSET(sp, SC_SCR_TOP))
 		goto top;
 	if (F_ISSET(sp, SC_SCR_CENTER))
 		goto middle;
 
 	/*
 	 * 6d: Line up.
 	 */
 	lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
 	if (lcnt < HALFTEXT(sp)) {
 		/*
 		 * If less than half a screen below the line, scroll up until
 		 * the line is the first line on the screen.  Special check so
 		 * that if the screen has been emptied, we refill it.
 		 */
 		if (db_exist(sp, HMAP->lno)) {
 			while (lcnt--)
 				if (vs_sm_1down(sp))
 					return (1);
 			goto adjust;
 		} else
 			goto top;	/* XXX No such line. */
 
 		/*
 		 * If less than a half screen from the bottom of the file,
 		 * put the last line of the file on the bottom of the screen.
 		 */
 bottom:		if (db_last(sp, &lastline))
 			return (1);
 		tmp.lno = LNO;
 		tmp.coff = HMAP->coff;
 		tmp.soff = 1;
 		lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows);
 		if (lcnt < HALFTEXT(sp)) {
 			if (vs_sm_fill(sp, lastline, P_BOTTOM))
 				return (1);
 			F_SET(sp, SC_SCR_REDRAW);
 			goto adjust;
 		}
 		/* It's not close, just put the line in the middle. */
 		goto middle;
 	}
 
 	/*
 	 * If less than half a screen from the top of the file, put the first
 	 * line of the file at the top of the screen.  Otherwise, put the line
 	 * in the middle of the screen.
 	 */
 	tmp.lno = 1;
 	tmp.coff = HMAP->coff;
 	tmp.soff = 1;
 	lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
 	if (lcnt < HALFTEXT(sp)) {
 		if (vs_sm_fill(sp, 1, P_TOP))
 			return (1);
 	} else
 middle:		if (vs_sm_fill(sp, LNO, P_MIDDLE))
 			return (1);
 	if (0) {
 top:		if (vs_sm_fill(sp, LNO, P_TOP))
 			return (1);
 	}
 	F_SET(sp, SC_SCR_REDRAW);
 
 	/*
 	 * At this point we know part of the line is on the screen.  Since
 	 * scrolling is done using logical lines, not physical, all of the
 	 * line may not be on the screen.  While that's not necessarily bad,
 	 * if the part the cursor is on isn't there, we're going to lose.
 	 * This can be tricky; if the line covers the entire screen, lno
 	 * may be the same as both ends of the map, that's why we test BOTH
 	 * the top and the bottom of the map.  This isn't a problem for
 	 * left-right scrolling, the cursor movement code handles the problem.
 	 *
 	 * There's a performance issue here if editing *really* long lines.
 	 * This gets to the right spot by scrolling, and, in a binary, by
 	 * scrolling hundreds of lines.  If the adjustment looks like it's
 	 * going to be a serious problem, refill the screen and repaint.
 	 */
 adjust:	if (!O_ISSET(sp, O_LEFTRIGHT) &&
 	    (LNO == HMAP->lno || LNO == TMAP->lno)) {
 		cnt = vs_screens(sp, LNO, &CNO);
-		if (LNO == HMAP->lno && cnt < HMAP->soff)
+		if (LNO == HMAP->lno && cnt < HMAP->soff) {
 			if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
 				HMAP->soff = cnt;
 				vs_sm_fill(sp, OOBLNO, P_TOP);
 				F_SET(sp, SC_SCR_REDRAW);
 			} else
 				while (cnt < HMAP->soff)
 					if (vs_sm_1down(sp))
 						return (1);
-		if (LNO == TMAP->lno && cnt > TMAP->soff)
+		}
+		if (LNO == TMAP->lno && cnt > TMAP->soff) {
 			if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
 				TMAP->soff = cnt;
 				vs_sm_fill(sp, OOBLNO, P_BOTTOM);
 				F_SET(sp, SC_SCR_REDRAW);
 			} else
 				while (cnt > TMAP->soff)
 					if (vs_sm_1up(sp))
 						return (1);
+		}
 	}
 
 	/*
 	 * If the screen needs to be repainted, skip cursor optimization.
 	 * However, in the code above we skipped leftright scrolling on
 	 * the grounds that the cursor code would handle it.  Make sure
 	 * the right screen is up.
 	 */
 	if (F_ISSET(sp, SC_SCR_REDRAW)) {
 		if (O_ISSET(sp, O_LEFTRIGHT))
 			goto slow;
 		goto paint;
 	}
 
 	/*
 	 * 7: Cursor movements (current screen only).
 	 */
 	if (!LF_ISSET(UPDATE_CURSOR))
 		goto number;
 
 	/*
 	 * Decide cursor position.  If the line has changed, the cursor has
 	 * moved over a tab, or don't know where the cursor was, reparse the
 	 * line.  Otherwise, we've just moved over fixed-width characters,
 	 * and can calculate the left/right scrolling and cursor movement
 	 * without reparsing the line.  Note that we don't know which (if any)
 	 * of the characters between the old and new cursor positions changed.
 	 *
 	 * XXX
 	 * With some work, it should be possible to handle tabs quickly, at
 	 * least in obvious situations, like moving right and encountering
 	 * a tab, without reparsing the whole line.
 	 *
 	 * If the line we're working with has changed, reread it..
 	 */
 	if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
 		goto slow;
 
 	/* Otherwise, if nothing's changed, ignore the cursor. */
 	if (CNO == OCNO)
 		goto fast;
 
 	/*
 	 * Get the current line.  If this fails, we either have an empty
 	 * file and can just repaint, or there's a real problem.  This
 	 * isn't a performance issue because there aren't any ways to get
 	 * here repeatedly.
 	 */
 	if (db_eget(sp, LNO, &p, &len, &isempty)) {
 		if (isempty)
 			goto slow;
 		return (1);
 	}
 
 #ifdef DEBUG
 	/* Sanity checking. */
 	if (CNO >= len && len != 0) {
 		msgq(sp, M_ERR, "Error: %s/%d: cno (%zu) >= len (%zu)",
 		     basename(__FILE__), __LINE__, CNO, len);
 		return (1);
 	}
 #endif
 	/*
 	 * The basic scheme here is to look at the characters in between
 	 * the old and new positions and decide how big they are on the
 	 * screen, and therefore, how many screen positions to move.
 	 */
 	if (CNO < OCNO) {
 		/*
 		 * 7a: Cursor moved left.
 		 *
 		 * Point to the old character.  The old cursor position can
 		 * be past EOL if, for example, we just deleted the rest of
 		 * the line.  In this case, since we don't know the width of
 		 * the characters we traversed, we have to do it slowly.
 		 */
 		p += OCNO;
 		cnt = (OCNO - CNO) + 1;
 		if (OCNO >= len)
 			goto slow;
 
 		/*
 		 * Quick sanity check -- it's hard to figure out exactly when
 		 * we cross a screen boundary as we do in the cursor right
 		 * movement.  If cnt is so large that we're going to cross the
 		 * boundary no matter what, stop now.
 		 */
 		if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
 			goto slow;
 
 		/*
 		 * Count up the widths of the characters.  If it's a tab
 		 * character, go do it the slow way.
 		 */
 		for (cwtotal = 0; cnt--; cwtotal += KEY_COL(sp, ch))
 			if ((ch = *(UCHAR_T *)p--) == '\t')
 				goto slow;
 
 		/*
 		 * Decrement the screen cursor by the total width of the
 		 * characters minus 1.
 		 */
 		cwtotal -= 1;
 
 		/*
 		 * If we're moving left, and there's a wide character in the
 		 * current position, go to the end of the character.
 		 */
 		if (KEY_COL(sp, ch) > 1)
 			cwtotal -= KEY_COL(sp, ch) - 1;
 
 		/*
 		 * If the new column moved us off of the current logical line,
 		 * calculate a new one.  If doing leftright scrolling, we've
 		 * moved off of the current screen, as well.
 		 */
 		if (SCNO < cwtotal)
 			goto slow;
 		SCNO -= cwtotal;
 	} else {
 		/*
 		 * 7b: Cursor moved right.
 		 *
 		 * Point to the first character to the right.
 		 */
 		p += OCNO + 1;
 		cnt = CNO - OCNO;
 
 		/*
 		 * Count up the widths of the characters.  If it's a tab
 		 * character, go do it the slow way.  If we cross a
 		 * screen boundary, we can quit.
 		 */
 		for (cwtotal = SCNO; cnt--;) {
 			if ((ch = *(UCHAR_T *)p++) == '\t')
 				goto slow;
 			if ((cwtotal += KEY_COL(sp, ch)) >= SCREEN_COLS(sp))
 				break;
 		}
 
 		/*
 		 * Increment the screen cursor by the total width of the
 		 * characters.
 		 */
 		SCNO = cwtotal;
 
 		/* See screen change comment in section 6a. */
 		if (SCNO >= SCREEN_COLS(sp))
 			goto slow;
 	}
 
 	/*
 	 * 7c: Fast cursor update.
 	 *
 	 * We have the current column, retrieve the current row.
 	 */
 fast:	(void)gp->scr_cursor(sp, &y, &notused);
 	goto done_cursor;
 
 	/*
 	 * 7d: Slow cursor update.
 	 *
 	 * Walk through the map and find the current line.
 	 */
 slow:	for (smp = HMAP; smp->lno != LNO; ++smp);
 
 	/*
 	 * 7e: Leftright scrolling adjustment.
 	 *
 	 * If doing left-right scrolling and the cursor movement has changed
 	 * the displayed screen, scroll the screen left or right, unless we're
 	 * updating the info line in which case we just scroll that one line.
 	 * We adjust the offset up or down until we have a window that covers
 	 * the current column, making sure that we adjust differently for the
 	 * first screen as compared to subsequent ones.
 	 */
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		/*
 		 * Get the screen column for this character, and correct
 		 * for the number option offset.
 		 */
 		cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
 		if (O_ISSET(sp, O_NUMBER))
 			cnt -= O_NUMBER_LENGTH;
 
 		/* Adjust the window towards the beginning of the line. */
 		off = smp->coff;
 		if (off >= cnt) {
 			do {
 				if (off >= O_VAL(sp, O_SIDESCROLL))
 					off -= O_VAL(sp, O_SIDESCROLL);
 				else {
 					off = 0;
 					break;
 				}
 			} while (off >= cnt);
 			goto shifted;
 		}
 
 		/* Adjust the window towards the end of the line. */
 		if ((off == 0 && off + SCREEN_COLS(sp) < cnt) ||
 		    (off != 0 && off + sp->cols < cnt)) {
 			do {
 				off += O_VAL(sp, O_SIDESCROLL);
 			} while (off + sp->cols < cnt);
 
 shifted:		/* Fill in screen map with the new offset. */
 			if (F_ISSET(sp, SC_TINPUT_INFO))
 				smp->coff = off;
 			else {
 				for (smp = HMAP; smp <= TMAP; ++smp)
 					smp->coff = off;
 				leftright_warp = 1;
 			}
 			goto paint;
 		}
 
 		/*
 		 * We may have jumped here to adjust a leftright screen because
 		 * redraw was set.  If so, we have to paint the entire screen.
 		 */
 		if (F_ISSET(sp, SC_SCR_REDRAW))
 			goto paint;
 	}
 
 	/*
 	 * Update the screen lines for this particular file line until we
 	 * have a new screen cursor position.
 	 */
 	for (y = -1,
 	    vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
 		if (vs_line(sp, smp, &y, &SCNO))
 			return (1);
 		if (y != -1) {
 			vip->sc_smap = smp;
 			break;
 		}
 	}
 	goto done_cursor;
 
 	/*
 	 * 8: Repaint the entire screen.
 	 *
 	 * Lost big, do what you have to do.  We flush the cache, since
 	 * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and
 	 * it's simpler to repaint.  So, don't trust anything that we
 	 * think we know about it.
 	 */
 paint:	for (smp = HMAP; smp <= TMAP; ++smp)
 		SMAP_FLUSH(smp);
 	for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
 		if (vs_line(sp, smp, &y, &SCNO))
 			return (1);
 		if (y != -1 && vip->sc_smap == NULL)
 			vip->sc_smap = smp;
 	}
 	/*
 	 * If it's a small screen and we're redrawing, clear the unused lines,
 	 * ex may have overwritten them.
 	 */
 	if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
 		for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
 			(void)gp->scr_move(sp, cnt, 0);
 			(void)gp->scr_clrtoeol(sp);
 		}
 
 	didpaint = 1;
 
 done_cursor:
 	/*
 	 * Sanity checking.  When the repainting code messes up, the usual
 	 * result is we don't repaint the cursor and so sc_smap will be
 	 * NULL.  If we're debugging, die, otherwise restart from scratch.
 	 */
 #ifdef DEBUG
 	if (vip->sc_smap == NULL)
 		abort();
 #else
 	if (vip->sc_smap == NULL) {
 		F_SET(sp, SC_SCR_REFORMAT);
 		return (vs_paint(sp, flags));
 	}
 #endif
 
 	/*
 	 * 9: Set the remembered cursor values.
 	 */
 	OCNO = CNO;
 	OLNO = LNO;
 
 	/*
 	 * 10: Repaint the line numbers.
 	 *
 	 * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we
 	 * didn't repaint the screen, repaint all of the line numbers,
 	 * they've changed.
 	 */
 number:	if (O_ISSET(sp, O_NUMBER) &&
 	    F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
 		return (1);
 
 	/*
 	 * 11: Update the mode line, position the cursor, and flush changes.
 	 *
 	 * If we warped the screen, we have to refresh everything.
 	 */
 	if (leftright_warp)
 		LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
 
 	if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
 	    !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
 		vs_modeline(sp);
 
 	if (LF_ISSET(UPDATE_CURSOR)) {
 		(void)gp->scr_move(sp, y, SCNO);
 
 		/*
 		 * XXX
 		 * If the screen shifted, we recalculate the "most favorite"
 		 * cursor position.  Vi won't know that we've warped the
 		 * screen, so it's going to have a wrong idea about where the
 		 * cursor should be.  This is vi's problem, and fixing it here
 		 * is a gross layering violation.
 		 */
 		if (leftright_warp)
 			(void)vs_column(sp, &sp->rcm);
 	}
 
 	if (LF_ISSET(UPDATE_SCREEN))
 		(void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
 
 	/* 12: Clear the flags that are handled by this routine. */
 	F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
 	F_CLR(vip, VIP_CUR_INVALID |
 	    VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
 
 	return (0);
 
 #undef	 LNO
 #undef	OLNO
 #undef	 CNO
 #undef	OCNO
 #undef	SCNO
 }
 
 /*
  * vs_modeline --
  *	Update the mode line.
  */
 static void
 vs_modeline(SCR *sp)
 {
 	static char * const modes[] = {
 		"215|Append",			/* SM_APPEND */
 		"216|Change",			/* SM_CHANGE */
 		"217|Command",			/* SM_COMMAND */
 		"218|Insert",			/* SM_INSERT */
 		"219|Replace",			/* SM_REPLACE */
 	};
 	GS *gp;
 	size_t cols, curcol, curlen, endpoint, len, midpoint;
 	const char *t = NULL;
 	int ellipsis;
 	char buf[20];
 
 	gp = sp->gp;
 
 	/*
 	 * We put down the file name, the ruler, the mode and the dirty flag.
 	 * If there's not enough room, there's not enough room, we don't play
 	 * any special games.  We try to put the ruler in the middle and the
 	 * mode and dirty flag at the end.
 	 *
 	 * !!!
 	 * Leave the last character blank, in case it's a really dumb terminal
 	 * with hardware scroll.  Second, don't paint the last character in the
 	 * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
 	 *
 	 * Move to the last line on the screen.
 	 */
 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
 
 	/* If more than one screen in the display, show the file name. */
 	curlen = 0;
 	if (IS_SPLIT(sp)) {
 		CHAR_T *wp, *p;
 		size_t l;
 
 		CHAR2INT(sp, sp->frp->name, strlen(sp->frp->name) + 1, wp, l);
 		p = wp + l;
 		for (ellipsis = 0, cols = sp->cols / 2; --p > wp;) {
 			if (*p == '/') {
 				++p;
 				break;
 			}
 			if ((curlen += KEY_COL(sp, *p)) > cols) {
 				ellipsis = 3;
 				curlen +=
 				    KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
 				while (curlen > cols) {
 					++p;
 					curlen -= KEY_COL(sp, *p);
 				}
 				break;
 			}
 		}
 		if (ellipsis) {
 			while (ellipsis--)
 				(void)gp->scr_addstr(sp,
 				    KEY_NAME(sp, '.'), KEY_LEN(sp, '.'));
 			(void)gp->scr_addstr(sp,
 			    KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
 		}
 		for (; *p != '\0'; ++p)
 			(void)gp->scr_addstr(sp,
 			    KEY_NAME(sp, *p), KEY_COL(sp, *p));
 	}
 
 	/* Clear the rest of the line. */
 	(void)gp->scr_clrtoeol(sp);
 
 	/*
 	 * Display the ruler.  If we're not at the midpoint yet, move there.
 	 * Otherwise, add in two extra spaces.
 	 *
 	 * Adjust the current column for the fact that the editor uses it as
 	 * a zero-based number.
 	 *
 	 * XXX
 	 * Assume that numbers, commas, and spaces only take up a single
 	 * column on the screen.
 	 */
 	cols = sp->cols - 1;
 	if (O_ISSET(sp, O_RULER)) {
 		vs_column(sp, &curcol);
 		len = snprintf(buf, sizeof(buf), "%lu,%lu",
 		    (u_long)sp->lno, (u_long)(curcol + 1));
 
 		midpoint = (cols - ((len + 1) / 2)) / 2;
 		if (curlen < midpoint) {
 			(void)gp->scr_move(sp, LASTLINE(sp), midpoint);
 			curlen += len;
 		} else if (curlen + 2 + len < cols) {
 			(void)gp->scr_addstr(sp, "  ", 2);
 			curlen += 2 + len;
 		}
 		(void)gp->scr_addstr(sp, buf, len);
 	}
 
 	/*
 	 * Display the mode and the modified flag, as close to the end of the
 	 * line as possible, but guaranteeing at least two spaces between the
 	 * ruler and the modified flag.
 	 */
 #define	MODESIZE	9
 	endpoint = cols;
 	if (O_ISSET(sp, O_SHOWMODE)) {
 		if (F_ISSET(sp->ep, F_MODIFIED))
 			--endpoint;
 		t = msg_cat(sp, modes[sp->showmode], &len);
 		endpoint -= len;
 	}
 
 	if (endpoint > curlen + 2) {
 		(void)gp->scr_move(sp, LASTLINE(sp), endpoint);
 		if (O_ISSET(sp, O_SHOWMODE)) {
 			if (F_ISSET(sp->ep, F_MODIFIED))
 				(void)gp->scr_addstr(sp,
 				    KEY_NAME(sp, '*'), KEY_LEN(sp, '*'));
 			(void)gp->scr_addstr(sp, t, len);
 		}
 	}
 }
Index: head/contrib/nvi/vi/vs_relative.c
===================================================================
--- head/contrib/nvi/vi/vs_relative.c	(revision 366308)
+++ head/contrib/nvi/vi/vs_relative.c	(revision 366309)
@@ -1,290 +1,291 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 /*
  * vs_column --
  *	Return the logical column of the cursor in the line.
  *
  * PUBLIC: int vs_column(SCR *, size_t *);
  */
 int
 vs_column(SCR *sp, size_t *colp)
 {
 	VI_PRIVATE *vip;
 
 	vip = VIP(sp);
 
 	*colp = (O_ISSET(sp, O_LEFTRIGHT) ?
 	    vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) +
 	    vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0);
 	return (0);
 }
 
 /*
  * vs_screens --
  *	Return the screens necessary to display the line, or if specified,
  *	the physical character column within the line, including space
  *	required for the O_NUMBER and O_LIST options.
  *
  * PUBLIC: size_t vs_screens(SCR *, recno_t, size_t *);
  */
 size_t
 vs_screens(SCR *sp, recno_t lno, size_t *cnop)
 {
 	size_t cols, screens;
 
 	/* Left-right screens are simple, it's always 1. */
 	if (O_ISSET(sp, O_LEFTRIGHT))
 		return (1);
 
 	/*
 	 * Check for a cached value.  We maintain a cache because, if the
 	 * line is large, this routine gets called repeatedly.  One other
 	 * hack, lots of time the cursor is on column one, which is an easy
 	 * one.
 	 */
 	if (cnop == NULL) {
 		if (VIP(sp)->ss_lno == lno)
 			return (VIP(sp)->ss_screens);
 	} else if (*cnop == 0)
 		return (1);
 
 	/* Figure out how many columns the line/column needs. */
 	cols = vs_columns(sp, NULL, lno, cnop, NULL);
 
 	screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
 	if (screens == 0)
 		screens = 1;
 
 	/* Cache the value. */
 	if (cnop == NULL) {
 		VIP(sp)->ss_lno = lno;
 		VIP(sp)->ss_screens = screens;
 	}
 	return (screens);
 }
 
 /*
  * vs_columns --
  *	Return the screen columns necessary to display the line, or,
  *	if specified, the physical character column within the line.
  *
  * PUBLIC: size_t vs_columns(SCR *, CHAR_T *, recno_t, size_t *, size_t *);
  */
 size_t
 vs_columns(SCR *sp, CHAR_T *lp, recno_t lno, size_t *cnop, size_t *diffp)
 {
 	size_t chlen, cno, curoff, last = 0, len, scno;
 	int ch, leftright, listset;
 	CHAR_T *p;
 
 	/*
 	 * Initialize the screen offset.
 	 */
 	scno = 0;
 
 	/* Leading number if O_NUMBER option set. */
 	if (O_ISSET(sp, O_NUMBER))
 		scno += O_NUMBER_LENGTH;
 
 	/* Need the line to go any further. */
 	if (lp == NULL) {
 		(void)db_get(sp, lno, 0, &lp, &len);
 		if (len == 0)
 			goto done;
 	}
 
 	/* Missing or empty lines are easy. */
 	if (lp == NULL) {
 done:		if (diffp != NULL)		/* XXX */
 			*diffp = 0;
 		return scno;
 	}
 
 	/* Store away the values of the list and leftright edit options. */
 	listset = O_ISSET(sp, O_LIST);
 	leftright = O_ISSET(sp, O_LEFTRIGHT);
 
 	/*
 	 * Initialize the pointer into the buffer and current offset.
 	 */
 	p = lp;
 	curoff = scno;
 
 	/* Macro to return the display length of any signal character. */
 #define	CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' &&			\
 	    !listset ? TAB_OFF(val) : KEY_COL(sp, ch);
 
 	/*
 	 * If folding screens (the historic vi screen format), past the end
 	 * of the current screen, and the character was a tab, reset the
 	 * current screen column to 0, and the total screen columns to the
 	 * last column of the screen.  Otherwise, display the rest of the
 	 * character in the next screen.
 	 */
-#define	TAB_RESET {							\
+#define	TAB_RESET do {							\
 	curoff += chlen;						\
-	if (!leftright && curoff >= sp->cols)				\
+	if (!leftright && curoff >= sp->cols) {				\
 		if (ch == '\t') {					\
 			curoff = 0;					\
 			scno -= scno % sp->cols;			\
 		} else							\
 			curoff -= sp->cols;				\
-}
+	}								\
+} while (0)
 	if (cnop == NULL)
 		while (len--) {
 			chlen = CHLEN(curoff);
 			last = scno;
 			scno += chlen;
 			TAB_RESET;
 		}
 	else
 		for (cno = *cnop;; --cno) {
 			chlen = CHLEN(curoff);
 			last = scno;
 			scno += chlen;
 			TAB_RESET;
 			if (cno == 0)
 				break;
 		}
 
 	/* Add the trailing '$' if the O_LIST option set. */
 	if (listset && cnop == NULL)
 		scno += KEY_LEN(sp, '$');
 
 	/*
 	 * The text input screen code needs to know how much additional
 	 * room the last two characters required, so that it can handle
 	 * tab character displays correctly.
 	 */
 	if (diffp != NULL)
 		*diffp = scno - last;
 	return (scno);
 }
 
 /*
  * vs_rcm --
  *	Return the physical column from the line that will display a
  *	character closest to the currently most attractive character
  *	position (which is stored as a screen column).
  *
  * PUBLIC: size_t vs_rcm(SCR *, recno_t, int);
  */
 size_t
 vs_rcm(SCR *sp, recno_t lno, int islast)
 {
 	size_t len;
 
 	/* Last character is easy, and common. */
 	if (islast) {
 		if (db_get(sp, lno, 0, NULL, &len) || len == 0)
 			return (0);
 		return (len - 1);
 	}
 
 	/* First character is easy, and common. */
 	if (sp->rcm == 0)
 		return (0);
 
 	return (vs_colpos(sp, lno, sp->rcm));
 }
 
 /*
  * vs_colpos --
  *	Return the physical column from the line that will display a
  *	character closest to the specified screen column.
  *
  * PUBLIC: size_t vs_colpos(SCR *, recno_t, size_t);
  */
 size_t
 vs_colpos(SCR *sp, recno_t lno, size_t cno)
 {
 	size_t chlen, curoff, len, llen, off, scno;
 	int ch = 0, leftright, listset;
 	CHAR_T *lp, *p;
 
 	/* Need the line to go any further. */
 	(void)db_get(sp, lno, 0, &lp, &llen);
 
 	/* Missing or empty lines are easy. */
 	if (lp == NULL || llen == 0)
 		return (0);
 
 	/* Store away the values of the list and leftright edit options. */
 	listset = O_ISSET(sp, O_LIST);
 	leftright = O_ISSET(sp, O_LEFTRIGHT);
 
 	/* Discard screen (logical) lines. */
 	off = cno / sp->cols;
 	cno %= sp->cols;
 	for (scno = 0, p = lp, len = llen; off--;) {
 		for (; len && scno < sp->cols; --len)
 			scno += CHLEN(scno);
 
 		/*
 		 * If reached the end of the physical line, return the last
 		 * physical character in the line.
 		 */
 		if (len == 0)
 			return (llen - 1);
 
 		/*
 		 * If folding screens (the historic vi screen format), past
 		 * the end of the current screen, and the character was a tab,
 		 * reset the current screen column to 0.  Otherwise, the rest
 		 * of the character is displayed in the next screen.
 		 */
 		if (leftright && ch == '\t')
 			scno = 0;
 		else
 			scno -= sp->cols;
 	}
 
 	/* Step through the line until reach the right character or EOL. */
 	for (curoff = scno; len--;) {
 		chlen = CHLEN(curoff);
 
 		/*
 		 * If we've reached the specific character, there are three
 		 * cases.
 		 *
 		 * 1: scno == cno, i.e. the current character ends at the
 		 *    screen character we care about.
 		 *	a: off < llen - 1, i.e. not the last character in
 		 *	   the line, return the offset of the next character.
 		 *	b: else return the offset of the last character.
 		 * 2: scno != cno, i.e. this character overruns the character
 		 *    we care about, return the offset of this character.
 		 */
 		if ((scno += chlen) >= cno) {
 			off = p - lp;
 			return (scno == cno ?
 			    (off < llen - 1 ? off : llen - 1) : off - 1);
 		}
 
 		TAB_RESET;
 	}
 
 	/* No such character; return the start of the last character. */
 	return (llen - 1);
 }
Index: head/contrib/nvi/vi/vs_smap.c
===================================================================
--- head/contrib/nvi/vi/vs_smap.c	(revision 366308)
+++ head/contrib/nvi/vi/vs_smap.c	(revision 366309)
@@ -1,1238 +1,1238 @@
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1993, 1994, 1995, 1996
  *	Keith Bostic.  All rights reserved.
  *
  * See the LICENSE file for redistribution information.
  */
 
 #include "config.h"
 
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/time.h>
 
 #include <bitstring.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "../common/common.h"
 #include "vi.h"
 
 static int	vs_deleteln(SCR *, int);
 static int	vs_insertln(SCR *, int);
 static int	vs_sm_delete(SCR *, recno_t);
 static int	vs_sm_down(SCR *, MARK *, recno_t, scroll_t, SMAP *);
 static int	vs_sm_erase(SCR *);
 static int	vs_sm_insert(SCR *, recno_t);
 static int	vs_sm_reset(SCR *, recno_t);
 static int	vs_sm_up(SCR *, MARK *, recno_t, scroll_t, SMAP *);
 
 /*
  * vs_change --
  *	Make a change to the screen.
  *
  * PUBLIC: int vs_change(SCR *, recno_t, lnop_t);
  */
 int
 vs_change(SCR *sp, recno_t lno, lnop_t op)
 {
 	VI_PRIVATE *vip;
 	SMAP *p;
 	size_t cnt, oldy, oldx;
 
 	vip = VIP(sp);
 
 	/*
 	 * XXX
 	 * Very nasty special case.  The historic vi code displays a single
 	 * space (or a '$' if the list option is set) for the first line in
 	 * an "empty" file.  If we "insert" a line, that line gets scrolled
 	 * down, not repainted, so it's incorrect when we refresh the screen.
 	 * The vi text input functions detect it explicitly and don't insert
 	 * a new line.
 	 *
 	 * Check for line #2 before going to the end of the file.
 	 */
 	if (((op == LINE_APPEND && lno == 0) || 
 	    (op == LINE_INSERT && lno == 1)) &&
 	    !db_exist(sp, 2)) {
 		lno = 1;
 		op = LINE_RESET;
 	}
 
 	/* Appending is the same as inserting, if the line is incremented. */
 	if (op == LINE_APPEND) {
 		++lno;
 		op = LINE_INSERT;
 	}
 
 	/* Ignore the change if the line is after the map. */
 	if (lno > TMAP->lno)
 		return (0);
 
 	/*
 	 * If the line is before the map, and it's a decrement, decrement
 	 * the map.  If it's an increment, increment the map.  Otherwise,
 	 * ignore it.
 	 */
 	if (lno < HMAP->lno) {
 		switch (op) {
 		case LINE_APPEND:
 			abort();
 			/* NOTREACHED */
 		case LINE_DELETE:
 			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
 				--p->lno;
 			if (sp->lno >= lno)
 				--sp->lno;
 			F_SET(vip, VIP_N_RENUMBER);
 			break;
 		case LINE_INSERT:
 			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
 				++p->lno;
 			if (sp->lno >= lno)
 				++sp->lno;
 			F_SET(vip, VIP_N_RENUMBER);
 			break;
 		case LINE_RESET:
 			break;
 		}
 		return (0);
 	}
 
 	F_SET(vip, VIP_N_REFRESH);
 
 	/*
 	 * Invalidate the line size cache, and invalidate the cursor if it's
 	 * on this line,
 	 */
 	VI_SCR_CFLUSH(vip);
 	if (sp->lno == lno)
 		F_SET(vip, VIP_CUR_INVALID);
 
 	/*
 	 * If ex modifies the screen after ex output is already on the screen
 	 * or if we've switched into ex canonical mode, don't touch it -- we'll
 	 * get scrolling wrong, at best.
 	 */
 	if (!F_ISSET(sp, SC_TINPUT_INFO) &&
 	    (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
 		F_SET(vip, VIP_N_EX_REDRAW);
 		return (0);
 	}
 
 	/* Save and restore the cursor for these routines. */
 	(void)sp->gp->scr_cursor(sp, &oldy, &oldx);
 
 	switch (op) {
 	case LINE_DELETE:
 		if (vs_sm_delete(sp, lno))
 			return (1);
 		if (sp->lno > lno)
 			--sp->lno;
 		F_SET(vip, VIP_N_RENUMBER);
 		break;
 	case LINE_INSERT:
 		if (vs_sm_insert(sp, lno))
 			return (1);
 		if (sp->lno > lno)
 			++sp->lno;
 		F_SET(vip, VIP_N_RENUMBER);
 		break;
 	case LINE_RESET:
 		if (vs_sm_reset(sp, lno))
 			return (1);
 		break;
 	default:
 		abort();
 	}
 
 	(void)sp->gp->scr_move(sp, oldy, oldx);
 	return (0);
 }
 
 /*
  * vs_sm_fill --
  *	Fill in the screen map, placing the specified line at the
  *	right position.  There isn't any way to tell if an SMAP
  *	entry has been filled in, so this routine had better be
  *	called with P_FILL set before anything else is done.
  *
  * !!!
  * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
  * slot is already filled in, P_BOTTOM means that the TMAP slot is
  * already filled in, and we just finish up the job.
  *
  * PUBLIC: int vs_sm_fill(SCR *, recno_t, pos_t);
  */
 int
 vs_sm_fill(SCR *sp, recno_t lno, pos_t pos)
 {
 	SMAP *p, tmp;
 	size_t cnt;
 
 	/* Flush all cached information from the SMAP. */
 	for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
 		SMAP_FLUSH(p);
 
 	/*
 	 * If the map is filled, the screen must be redrawn.
 	 *
 	 * XXX
 	 * This is a bug.  We should try and figure out if the desired line
 	 * is already in the map or close by -- scrolling the screen would
 	 * be a lot better than redrawing.
 	 */
 	F_SET(sp, SC_SCR_REDRAW);
 
 	switch (pos) {
 	case P_FILL:
 		tmp.lno = 1;
 		tmp.coff = 0;
 		tmp.soff = 1;
 
 		/* See if less than half a screen from the top. */
 		if (vs_sm_nlines(sp,
 		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
 			lno = 1;
 			goto top;
 		}
 
 		/* See if less than half a screen from the bottom. */
 		if (db_last(sp, &tmp.lno))
 			return (1);
 		tmp.coff = 0;
 		tmp.soff = vs_screens(sp, tmp.lno, NULL);
 		if (vs_sm_nlines(sp,
 		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
 			TMAP->lno = tmp.lno;
 			TMAP->coff = tmp.coff;
 			TMAP->soff = tmp.soff;
 			goto bottom;
 		}
 		goto middle;
 	case P_TOP:
 		if (lno != OOBLNO) {
 top:			HMAP->lno = lno;
 			HMAP->coff = 0;
 			HMAP->soff = 1;
 		} else {
 			/*
 			 * If number of lines HMAP->lno (top line) spans
 			 * changed due to, say reformatting, and now is
 			 * fewer than HMAP->soff, reset so the line is
 			 * redrawn at the top of the screen.
 			 */
 			cnt = vs_screens(sp, HMAP->lno, NULL);
 			if (cnt < HMAP->soff)
 				HMAP->soff = 1;
 		}
 		/* If we fail, just punt. */
 		for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
 			if (vs_sm_next(sp, p, p + 1))
 				goto err;
 		break;
 	case P_MIDDLE:
 		/* If we fail, guess that the file is too small. */
 middle:		p = HMAP + sp->t_rows / 2;
 		p->lno = lno;
 		p->coff = 0;
 		p->soff = 1;
 		for (; p > HMAP; --p)
 			if (vs_sm_prev(sp, p, p - 1)) {
 				lno = 1;
 				goto top;
 			}
 
 		/* If we fail, just punt. */
 		p = HMAP + sp->t_rows / 2;
 		for (; p < TMAP; ++p)
 			if (vs_sm_next(sp, p, p + 1))
 				goto err;
 		break;
 	case P_BOTTOM:
 		if (lno != OOBLNO) {
 			TMAP->lno = lno;
 			TMAP->coff = 0;
 			TMAP->soff = vs_screens(sp, lno, NULL);
 		}
 		/* If we fail, guess that the file is too small. */
 bottom:		for (p = TMAP; p > HMAP; --p)
 			if (vs_sm_prev(sp, p, p - 1)) {
 				lno = 1;
 				goto top;
 			}
 		break;
 	default:
 		abort();
 	}
 	return (0);
 
 	/*
 	 * Try and put *something* on the screen.  If this fails, we have a
 	 * serious hard error.
 	 */
 err:	HMAP->lno = 1;
 	HMAP->coff = 0;
 	HMAP->soff = 1;
 	for (p = HMAP; p < TMAP; ++p)
 		if (vs_sm_next(sp, p, p + 1))
 			return (1);
 	return (0);
 }
 
 /*
  * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
  * screen contains only a single line (whether because the screen is small
  * or the line large), it gets fairly exciting.  Skip the fun, set a flag
  * so the screen map is refilled and the screen redrawn, and return.  This
  * is amazingly slow, but it's not clear that anyone will care.
  */
-#define	HANDLE_WEIRDNESS(cnt) {						\
+#define	HANDLE_WEIRDNESS(cnt) do {					\
 	if (cnt >= sp->t_rows) {					\
 		F_SET(sp, SC_SCR_REFORMAT);				\
 		return (0);						\
 	}								\
-}
+} while (0)
 
 /*
  * vs_sm_delete --
  *	Delete a line out of the SMAP.
  */
 static int
 vs_sm_delete(SCR *sp, recno_t lno)
 {
 	SMAP *p, *t;
 	size_t cnt_orig;
 
 	/*
 	 * Find the line in the map, and count the number of screen lines
 	 * which display any part of the deleted line.
 	 */
 	for (p = HMAP; p->lno != lno; ++p);
 	if (O_ISSET(sp, O_LEFTRIGHT))
 		cnt_orig = 1;
 	else
 		for (cnt_orig = 1, t = p + 1;
 		    t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
 
 	HANDLE_WEIRDNESS(cnt_orig);
 
 	/* Delete that many lines from the screen. */
 	(void)sp->gp->scr_move(sp, p - HMAP, 0);
 	if (vs_deleteln(sp, cnt_orig))
 		return (1);
 
 	/* Shift the screen map up. */
 	memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
 
 	/* Decrement the line numbers for the rest of the map. */
 	for (t = TMAP - cnt_orig; p <= t; ++p)
 		--p->lno;
 
 	/* Display the new lines. */
 	for (p = TMAP - cnt_orig;;) {
 		if (p < TMAP && vs_sm_next(sp, p, p + 1))
 			return (1);
 		/* vs_sm_next() flushed the cache. */
 		if (vs_line(sp, ++p, NULL, NULL))
 			return (1);
 		if (p == TMAP)
 			break;
 	}
 	return (0);
 }
 
 /*
  * vs_sm_insert --
  *	Insert a line into the SMAP.
  */
 static int
 vs_sm_insert(SCR *sp, recno_t lno)
 {
 	SMAP *p, *t;
 	size_t cnt_orig, cnt, coff;
 
 	/* Save the offset. */
 	coff = HMAP->coff;
 
 	/*
 	 * Find the line in the map, find out how many screen lines
 	 * needed to display the line.
 	 */
 	for (p = HMAP; p->lno != lno; ++p);
 
 	cnt_orig = vs_screens(sp, lno, NULL);
 	HANDLE_WEIRDNESS(cnt_orig);
 
 	/*
 	 * The lines left in the screen override the number of screen
 	 * lines in the inserted line.
 	 */
 	cnt = (TMAP - p) + 1;
 	if (cnt_orig > cnt)
 		cnt_orig = cnt;
 
 	/* Push down that many lines. */
 	(void)sp->gp->scr_move(sp, p - HMAP, 0);
 	if (vs_insertln(sp, cnt_orig))
 		return (1);
 
 	/* Shift the screen map down. */
 	memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
 
 	/* Increment the line numbers for the rest of the map. */
 	for (t = p + cnt_orig; t <= TMAP; ++t)
 		++t->lno;
 
 	/* Fill in the SMAP for the new lines, and display. */
 	for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
 		t->lno = lno;
 		t->coff = coff;
 		t->soff = cnt;
 		SMAP_FLUSH(t);
 		if (vs_line(sp, t, NULL, NULL))
 			return (1);
 	}
 	return (0);
 }
 
 /*
  * vs_sm_reset --
  *	Reset a line in the SMAP.
  */
 static int
 vs_sm_reset(SCR *sp, recno_t lno)
 {
 	SMAP *p, *t;
 	size_t cnt_orig, cnt_new, cnt, diff;
 
 	/*
 	 * See if the number of on-screen rows taken up by the old display
 	 * for the line is the same as the number needed for the new one.
 	 * If so, repaint, otherwise do it the hard way.
 	 */
 	for (p = HMAP; p->lno != lno; ++p);
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		t = p;
 		cnt_orig = cnt_new = 1;
 	} else {
 		for (cnt_orig = 0,
 		    t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
 		cnt_new = vs_screens(sp, lno, NULL);
 	}
 
 	HANDLE_WEIRDNESS(cnt_orig);
 
 	if (cnt_orig == cnt_new) {
 		do {
 			SMAP_FLUSH(p);
 			if (vs_line(sp, p, NULL, NULL))
 				return (1);
 		} while (++p < t);
 		return (0);
 	}
 
 	if (cnt_orig < cnt_new) {
 		/* Get the difference. */
 		diff = cnt_new - cnt_orig;
 
 		/*
 		 * The lines left in the screen override the number of screen
 		 * lines in the inserted line.
 		 */
 		cnt = (TMAP - p) + 1;
 		if (diff > cnt)
 			diff = cnt;
 
 		/* If there are any following lines, push them down. */
 		if (cnt > 1) {
 			(void)sp->gp->scr_move(sp, p - HMAP, 0);
 			if (vs_insertln(sp, diff))
 				return (1);
 
 			/* Shift the screen map down. */
 			memmove(p + diff, p,
 			    (((TMAP - p) - diff) + 1) * sizeof(SMAP));
 		}
 
 		/* Fill in the SMAP for the replaced line, and display. */
 		for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
 			t->lno = lno;
 			t->soff = cnt;
 			SMAP_FLUSH(t);
 			if (vs_line(sp, t, NULL, NULL))
 				return (1);
 		}
 	} else {
 		/* Get the difference. */
 		diff = cnt_orig - cnt_new;
 
 		/* Delete that many lines from the screen. */
 		(void)sp->gp->scr_move(sp, p - HMAP, 0);
 		if (vs_deleteln(sp, diff))
 			return (1);
 
 		/* Shift the screen map up. */
 		memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
 
 		/* Fill in the SMAP for the replaced line, and display. */
 		for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
 			t->lno = lno;
 			t->soff = cnt;
 			SMAP_FLUSH(t);
 			if (vs_line(sp, t, NULL, NULL))
 				return (1);
 		}
 
 		/* Display the new lines at the bottom of the screen. */
 		for (t = TMAP - diff;;) {
 			if (t < TMAP && vs_sm_next(sp, t, t + 1))
 				return (1);
 			/* vs_sm_next() flushed the cache. */
 			if (vs_line(sp, ++t, NULL, NULL))
 				return (1);
 			if (t == TMAP)
 				break;
 		}
 	}
 	return (0);
 }
 
 /*
  * vs_sm_scroll
  *	Scroll the SMAP up/down count logical lines.  Different
  *	semantics based on the vi command, *sigh*.
  *
  * PUBLIC: int vs_sm_scroll(SCR *, MARK *, recno_t, scroll_t);
  */
 int
 vs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd)
 {
 	SMAP *smp;
 
 	/*
 	 * Invalidate the cursor.  The line is probably going to change,
 	 * (although for ^E and ^Y it may not).  In any case, the scroll
 	 * routines move the cursor to draw things.
 	 */
 	F_SET(VIP(sp), VIP_CUR_INVALID);
 
 	/* Find the cursor in the screen. */
 	if (vs_sm_cursor(sp, &smp))
 		return (1);
 
 	switch (scmd) {
 	case CNTRL_B:
 	case CNTRL_U:
 	case CNTRL_Y:
 	case Z_CARAT:
 		if (vs_sm_down(sp, rp, count, scmd, smp))
 			return (1);
 		break;
 	case CNTRL_D:
 	case CNTRL_E:
 	case CNTRL_F:
 	case Z_PLUS:
 		if (vs_sm_up(sp, rp, count, scmd, smp))
 			return (1);
 		break;
 	default:
 		abort();
 	}
 
 	/*
 	 * !!!
 	 * If we're at the start of a line, go for the first non-blank.
 	 * This makes it look like the old vi, even though we're moving
 	 * around by logical lines, not physical ones.
 	 *
 	 * XXX
 	 * In the presence of a long line, which has more than a screen
 	 * width of leading spaces, this code can cause a cursor warp.
 	 * Live with it.
 	 */
 	if (scmd != CNTRL_E && scmd != CNTRL_Y &&
 	    rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
 		return (1);
 
 	return (0);
 }
 
 /*
  * vs_sm_up --
  *	Scroll the SMAP up count logical lines.
  */
 static int
 vs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
 {
 	int cursor_set, echanged, zset;
 	SMAP *ssmp, s1, s2;
 
 	/*
 	 * Check to see if movement is possible.
 	 *
 	 * Get the line after the map.  If that line is a new one (and if
 	 * O_LEFTRIGHT option is set, this has to be true), and the next
 	 * line doesn't exist, and the cursor doesn't move, or the cursor
 	 * isn't even on the screen, or the cursor is already at the last
 	 * line in the map, it's an error.  If that test succeeded because
 	 * the cursor wasn't at the end of the map, test to see if the map
 	 * is mostly empty.
 	 */
 	if (vs_sm_next(sp, TMAP, &s1))
 		return (1);
 	if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
 		if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
 			v_eof(sp, NULL);
 			return (1);
 		}
 		if (vs_sm_next(sp, smp, &s1))
 			return (1);
 		if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
 			v_eof(sp, NULL);
 			return (1);
 		}
 	}
 
 	/*
 	 * Small screens: see vs_refresh.c section 6a.
 	 *
 	 * If it's a small screen, and the movement isn't larger than a
 	 * screen, i.e some context will remain, open up the screen and
 	 * display by scrolling.  In this case, the cursor moves down one
 	 * line for each line displayed.  Otherwise, erase/compress and
 	 * repaint, and move the cursor to the first line in the screen.
 	 * Note, the ^F command is always in the latter case, for historical
 	 * reasons.
 	 */
 	cursor_set = 0;
 	if (IS_SMALL(sp)) {
 		if (count >= sp->t_maxrows || scmd == CNTRL_F) {
 			s1 = TMAP[0];
 			if (vs_sm_erase(sp))
 				return (1);
 			for (; count--; s1 = s2) {
 				if (vs_sm_next(sp, &s1, &s2))
 					return (1);
 				if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
 					break;
 			}
 			TMAP[0] = s2;
 			if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
 				return (1);
 			return (vs_sm_position(sp, rp, 0, P_TOP));
 		}
 		cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
 		for (; count &&
 		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
 			if (vs_sm_next(sp, TMAP, &s1))
 				return (1);
 			if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
 				break;
 			*++TMAP = s1;
 			/* vs_sm_next() flushed the cache. */
 			if (vs_line(sp, TMAP, NULL, NULL))
 				return (1);
 
 			if (!cursor_set)
 				++ssmp;
 		}
 		if (!cursor_set) {
 			rp->lno = ssmp->lno;
 			rp->cno = ssmp->c_sboff;
 		}
 		if (count == 0)
 			return (0);
 	}
 
 	for (echanged = zset = 0; count; --count) {
 		/* Decide what would show up on the screen. */
 		if (vs_sm_next(sp, TMAP, &s1))
 			return (1);
 
 		/* If the line doesn't exist, we're done. */
 		if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
 			break;
 
 		/* Scroll the screen cursor up one logical line. */
 		if (vs_sm_1up(sp))
 			return (1);
 		switch (scmd) {
 		case CNTRL_E:
 			if (smp > HMAP)
 				--smp;
 			else
 				echanged = 1;
 			break;
 		case Z_PLUS:
 			if (zset) {
 				if (smp > HMAP)
 					--smp;
 			} else {
 				smp = TMAP;
 				zset = 1;
 			}
 			/* FALLTHROUGH */
 		default:
 			break;
 		}
 	}
 
 	if (cursor_set)
 		return(0);
 
 	switch (scmd) {
 	case CNTRL_E:
 		/*
 		 * On a ^E that was forced to change lines, try and keep the
 		 * cursor as close as possible to the last position, but also
 		 * set it up so that the next "real" movement will return the
 		 * cursor to the closest position to the last real movement.
 		 */
 		if (echanged) {
 			rp->lno = smp->lno;
 			rp->cno = vs_colpos(sp, smp->lno,
 			    (O_ISSET(sp, O_LEFTRIGHT) ? 
 			    smp->coff : (smp->soff - 1) * sp->cols) +
 			    sp->rcm % sp->cols);
 		}
 		return (0);
 	case CNTRL_F:
 		/*
 		 * If there are more lines, the ^F command is positioned at
 		 * the first line of the screen.
 		 */
 		if (!count) {
 			smp = HMAP;
 			break;
 		}
 		/* FALLTHROUGH */
 	case CNTRL_D:
 		/*
 		 * The ^D and ^F commands move the cursor towards EOF
 		 * if there are more lines to move.  Check to be sure
 		 * the lines actually exist.  (They may not if the
 		 * file is smaller than the screen.)
 		 */
 		for (; count; --count, ++smp)
 			if (smp == TMAP || !db_exist(sp, smp[1].lno))
 				break;
 		break;
 	case Z_PLUS:
 		 /* The z+ command moves the cursor to the first new line. */
 		break;
 	default:
 		abort();
 	}
 
 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
 		return (1);
 	rp->lno = smp->lno;
 	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
 	return (0);
 }
 
 /*
  * vs_sm_1up --
  *	Scroll the SMAP up one.
  *
  * PUBLIC: int vs_sm_1up(SCR *);
  */
 int
 vs_sm_1up(SCR *sp)
 {
 	/*
 	 * Delete the top line of the screen.  Shift the screen map
 	 * up and display a new line at the bottom of the screen.
 	 */
 	(void)sp->gp->scr_move(sp, 0, 0);
 	if (vs_deleteln(sp, 1))
 		return (1);
 
 	/* One-line screens can fail. */
 	if (IS_ONELINE(sp)) {
 		if (vs_sm_next(sp, TMAP, TMAP))
 			return (1);
 	} else {
 		memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
 		if (vs_sm_next(sp, TMAP - 1, TMAP))
 			return (1);
 	}
 	/* vs_sm_next() flushed the cache. */
 	return (vs_line(sp, TMAP, NULL, NULL));
 }
 
 /*
  * vs_deleteln --
  *	Delete a line a la curses, make sure to put the information
  *	line and other screens back.
  */
 static int
 vs_deleteln(SCR *sp, int cnt)
 {
 	GS *gp;
 	size_t oldy, oldx;
 
 	gp = sp->gp;
 
 	/* If the screen is vertically split, we can't scroll it. */
 	if (IS_VSPLIT(sp)) {
 		F_SET(sp, SC_SCR_REDRAW);
 		return (0);
 	}
 		
 	if (IS_ONELINE(sp))
 		(void)gp->scr_clrtoeol(sp);
 	else {
 		(void)gp->scr_cursor(sp, &oldy, &oldx);
 		while (cnt--) {
 			(void)gp->scr_deleteln(sp);
 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
 			(void)gp->scr_insertln(sp);
 			(void)gp->scr_move(sp, oldy, oldx);
 		}
 	}
 	return (0);
 }
 
 /*
  * vs_sm_down --
  *	Scroll the SMAP down count logical lines.
  */
 static int
 vs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
 {
 	SMAP *ssmp, s1, s2;
 	int cursor_set, ychanged, zset;
 
 	/* Check to see if movement is possible. */
 	if (HMAP->lno == 1 &&
 	    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
 	    (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
 		v_sof(sp, NULL);
 		return (1);
 	}
 
 	/*
 	 * Small screens: see vs_refresh.c section 6a.
 	 *
 	 * If it's a small screen, and the movement isn't larger than a
 	 * screen, i.e some context will remain, open up the screen and
 	 * display by scrolling.  In this case, the cursor moves up one
 	 * line for each line displayed.  Otherwise, erase/compress and
 	 * repaint, and move the cursor to the first line in the screen.
 	 * Note, the ^B command is always in the latter case, for historical
 	 * reasons.
 	 */
 	cursor_set = scmd == CNTRL_Y;
 	if (IS_SMALL(sp)) {
 		if (count >= sp->t_maxrows || scmd == CNTRL_B) {
 			s1 = HMAP[0];
 			if (vs_sm_erase(sp))
 				return (1);
 			for (; count--; s1 = s2) {
 				if (vs_sm_prev(sp, &s1, &s2))
 					return (1);
 				if (s2.lno == 1 &&
 				    (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
 					break;
 			}
 			HMAP[0] = s2;
 			if (vs_sm_fill(sp, OOBLNO, P_TOP))
 				return (1);
 			return (vs_sm_position(sp, rp, 0, P_BOTTOM));
 		}
 		cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
 		for (; count &&
 		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
 			if (HMAP->lno == 1 &&
 			    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
 				break;
 			++TMAP;
 			if (vs_sm_1down(sp))
 				return (1);
 		}
 		if (!cursor_set) {
 			rp->lno = ssmp->lno;
 			rp->cno = ssmp->c_sboff;
 		}
 		if (count == 0)
 			return (0);
 	}
 
 	for (ychanged = zset = 0; count; --count) {
 		/* If the line doesn't exist, we're done. */
 		if (HMAP->lno == 1 &&
 		    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
 			break;
 
 		/* Scroll the screen and cursor down one logical line. */
 		if (vs_sm_1down(sp))
 			return (1);
 		switch (scmd) {
 		case CNTRL_Y:
 			if (smp < TMAP)
 				++smp;
 			else
 				ychanged = 1;
 			break;
 		case Z_CARAT:
 			if (zset) {
 				if (smp < TMAP)
 					++smp;
 			} else {
 				smp = HMAP;
 				zset = 1;
 			}
 			/* FALLTHROUGH */
 		default:
 			break;
 		}
 	}
 
 	if (scmd != CNTRL_Y && cursor_set)
 		return(0);
 
 	switch (scmd) {
 	case CNTRL_B:
 		/*
 		 * If there are more lines, the ^B command is positioned at
 		 * the last line of the screen.  However, the line may not
 		 * exist.
 		 */
 		if (!count) {
 			for (smp = TMAP; smp > HMAP; --smp)
 				if (db_exist(sp, smp->lno))
 					break;
 			break;
 		}
 		/* FALLTHROUGH */
 	case CNTRL_U:
 		/*
 		 * The ^B and ^U commands move the cursor towards SOF
 		 * if there are more lines to move.
 		 */
 		if (count < smp - HMAP)
 			smp -= count;
 		else
 			smp = HMAP;
 		break;
 	case CNTRL_Y:
 		/*
 		 * On a ^Y that was forced to change lines, try and keep the
 		 * cursor as close as possible to the last position, but also
 		 * set it up so that the next "real" movement will return the
 		 * cursor to the closest position to the last real movement.
 		 */
 		if (ychanged) {
 			rp->lno = smp->lno;
 			rp->cno = vs_colpos(sp, smp->lno,
 			    (O_ISSET(sp, O_LEFTRIGHT) ? 
 			    smp->coff : (smp->soff - 1) * sp->cols) +
 			    sp->rcm % sp->cols);
 		}
 		return (0);
 	case Z_CARAT:
 		 /* The z^ command moves the cursor to the first new line. */
 		break;
 	default:
 		abort();
 	}
 
 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
 		return (1);
 	rp->lno = smp->lno;
 	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
 	return (0);
 }
 
 /*
  * vs_sm_erase --
  *	Erase the small screen area for the scrolling functions.
  */
 static int
 vs_sm_erase(SCR *sp)
 {
 	GS *gp;
 
 	gp = sp->gp;
 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
 	(void)gp->scr_clrtoeol(sp);
 	for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
 		(void)gp->scr_move(sp, TMAP - HMAP, 0);
 		(void)gp->scr_clrtoeol(sp);
 	}
 	return (0);
 }
 
 /*
  * vs_sm_1down --
  *	Scroll the SMAP down one.
  *
  * PUBLIC: int vs_sm_1down(SCR *);
  */
 int
 vs_sm_1down(SCR *sp)
 {
 	/*
 	 * Insert a line at the top of the screen.  Shift the screen map
 	 * down and display a new line at the top of the screen.
 	 */
 	(void)sp->gp->scr_move(sp, 0, 0);
 	if (vs_insertln(sp, 1))
 		return (1);
 
 	/* One-line screens can fail. */
 	if (IS_ONELINE(sp)) {
 		if (vs_sm_prev(sp, HMAP, HMAP))
 			return (1);
 	} else {
 		memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
 		if (vs_sm_prev(sp, HMAP + 1, HMAP))
 			return (1);
 	}
 	/* vs_sm_prev() flushed the cache. */
 	return (vs_line(sp, HMAP, NULL, NULL));
 }
 
 /*
  * vs_insertln --
  *	Insert a line a la curses, make sure to put the information
  *	line and other screens back.
  */
 static int
 vs_insertln(SCR *sp, int cnt)
 {
 	GS *gp;
 	size_t oldy, oldx;
 
 	gp = sp->gp;
 
 	/* If the screen is vertically split, we can't scroll it. */
 	if (IS_VSPLIT(sp)) {
 		F_SET(sp, SC_SCR_REDRAW);
 		return (0);
 	}
 
 	if (IS_ONELINE(sp)) {
 		(void)gp->scr_move(sp, LASTLINE(sp), 0);
 		(void)gp->scr_clrtoeol(sp);
 	} else {
 		(void)gp->scr_cursor(sp, &oldy, &oldx);
 		while (cnt--) {
 			(void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
 			(void)gp->scr_deleteln(sp);
 			(void)gp->scr_move(sp, oldy, oldx);
 			(void)gp->scr_insertln(sp);
 		}
 	}
 	return (0);
 }
 
 /*
  * vs_sm_next --
  *	Fill in the next entry in the SMAP.
  *
  * PUBLIC: int vs_sm_next(SCR *, SMAP *, SMAP *);
  */
 int
 vs_sm_next(SCR *sp, SMAP *p, SMAP *t)
 {
 	size_t lcnt;
 
 	SMAP_FLUSH(t);
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		t->lno = p->lno + 1;
 		t->coff = p->coff;
 	} else {
 		lcnt = vs_screens(sp, p->lno, NULL);
 		if (lcnt == p->soff) {
 			t->lno = p->lno + 1;
 			t->soff = 1;
 		} else {
 			t->lno = p->lno;
 			t->soff = p->soff + 1;
 		}
 	}
 	return (0);
 }
 
 /*
  * vs_sm_prev --
  *	Fill in the previous entry in the SMAP.
  *
  * PUBLIC: int vs_sm_prev(SCR *, SMAP *, SMAP *);
  */
 int
 vs_sm_prev(SCR *sp, SMAP *p, SMAP *t)
 {
 	SMAP_FLUSH(t);
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
 		t->lno = p->lno - 1;
 		t->coff = p->coff;
 	} else {
 		if (p->soff != 1) {
 			t->lno = p->lno;
 			t->soff = p->soff - 1;
 		} else {
 			t->lno = p->lno - 1;
 			t->soff = vs_screens(sp, t->lno, NULL);
 		}
 	}
 	return (t->lno == 0);
 }
 
 /*
  * vs_sm_cursor --
  *	Return the SMAP entry referenced by the cursor.
  *
  * PUBLIC: int vs_sm_cursor(SCR *, SMAP **);
  */
 int
 vs_sm_cursor(SCR *sp, SMAP **smpp)
 {
 	SMAP *p;
 
 	/* See if the cursor is not in the map. */
 	if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
 		return (1);
 
 	/* Find the first occurence of the line. */
 	for (p = HMAP; p->lno != sp->lno; ++p);
 
 	/* Fill in the map information until we find the right line. */
 	for (; p <= TMAP; ++p) {
 		/* Short lines are common and easy to detect. */
 		if (p != TMAP && (p + 1)->lno != p->lno) {
 			*smpp = p;
 			return (0);
 		}
 		if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
 			return (1);
 		if (p->c_eboff >= sp->cno) {
 			*smpp = p;
 			return (0);
 		}
 	}
 
 	/* It was past the end of the map after all. */
 	return (1);
 }
 
 /*
  * vs_sm_position --
  *	Return the line/column of the top, middle or last line on the screen.
  *	(The vi H, M and L commands.)  Here because only the screen routines
  *	know what's really out there.
  *
  * PUBLIC: int vs_sm_position(SCR *, MARK *, u_long, pos_t);
  */
 int
 vs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos)
 {
 	SMAP *smp;
 	recno_t last;
 
 	switch (pos) {
 	case P_TOP:
 		/*
 		 * !!!
 		 * Historically, an invalid count to the H command failed.
 		 * We do nothing special here, just making sure that H in
 		 * an empty screen works.
 		 */
 		if (cnt > TMAP - HMAP)
 			goto sof;
 		smp = HMAP + cnt;
 		if (cnt && !db_exist(sp, smp->lno)) {
 sof:			msgq(sp, M_BERR, "220|Movement past the end-of-screen");
 			return (1);
 		}
 		break;
 	case P_MIDDLE:
 		/*
 		 * !!!
 		 * Historically, a count to the M command was ignored.
 		 * If the screen isn't filled, find the middle of what's
 		 * real and move there.
 		 */
 		if (!db_exist(sp, TMAP->lno)) {
 			if (db_last(sp, &last))
 				return (1);
 			for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
 			if (smp > HMAP)
 				smp -= (smp - HMAP) / 2;
 		} else
 			smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
 		break;
 	case P_BOTTOM:
 		/*
 		 * !!!
 		 * Historically, an invalid count to the L command failed.
 		 * If the screen isn't filled, find the bottom of what's
 		 * real and try to offset from there.
 		 */
 		if (cnt > TMAP - HMAP)
 			goto eof;
 		smp = TMAP - cnt;
 		if (!db_exist(sp, smp->lno)) {
 			if (db_last(sp, &last))
 				return (1);
 			for (; smp->lno > last && smp > HMAP; --smp);
 			if (cnt > smp - HMAP) {
 eof:				msgq(sp, M_BERR,
 			    "221|Movement past the beginning-of-screen");
 				return (1);
 			}
 			smp -= cnt;
 		}
 		break;
 	default:
 		abort();
 	}
 
 	/* Make sure that the cached information is valid. */
 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
 		return (1);
 	rp->lno = smp->lno;
 	rp->cno = smp->c_sboff;
 
 	return (0);
 }
 
 /*
  * vs_sm_nlines --
  *	Return the number of screen lines from an SMAP entry to the
  *	start of some file line, less than a maximum value.
  *
  * PUBLIC: recno_t vs_sm_nlines(SCR *, SMAP *, recno_t, size_t);
  */
 recno_t
 vs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max)
 {
 	recno_t lno, lcnt;
 
 	if (O_ISSET(sp, O_LEFTRIGHT))
 		return (from_sp->lno > to_lno ?
 		    from_sp->lno - to_lno : to_lno - from_sp->lno);
 
 	if (from_sp->lno == to_lno)
 		return (from_sp->soff - 1);
 
 	if (from_sp->lno > to_lno) {
 		lcnt = from_sp->soff - 1;	/* Correct for off-by-one. */
 		for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
 			lcnt += vs_screens(sp, lno, NULL);
 	} else {
 		lno = from_sp->lno;
 		lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
 		for (; ++lno < to_lno && lcnt <= max;)
 			lcnt += vs_screens(sp, lno, NULL);
 	}
 	return (lcnt);
 }
Index: head/contrib/nvi
===================================================================
--- head/contrib/nvi	(revision 366308)
+++ head/contrib/nvi	(revision 366309)

Property changes on: head/contrib/nvi
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /vendor/nvi/dist:r366307
Index: head/usr.bin/vi/catalog/Makefile
===================================================================
--- head/usr.bin/vi/catalog/Makefile	(revision 366308)
+++ head/usr.bin/vi/catalog/Makefile	(revision 366309)
@@ -1,166 +1,167 @@
 #	$Id: Makefile,v 9.0 2012/10/19 15:13:11 zy Exp $
 # $FreeBSD$
 
 .include <bsd.own.mk>
 
 PACKAGE=vi
 V=	${.CURDIR}/../../../contrib/nvi
 
 FILESGROUPS+=VICAT
 CAT=	dutch english french german polish ru_RU.KOI8-R spanish swedish \
 	uk_UA.KOI8-U zh_CN.GB2312 tr_TR.ISO8859-9 tr_TR.UTF-8
 VICAT=		${CAT}
 VICATDIR=	${FILESDIR}
 VICATPACKAGE=	${PACKAGE}
 
 SCAN=	${V}/cl/*.c ${V}/common/*.c ${V}/ex/*.c ${V}/vi/*.c
 
 .PATH: ${V}/catalog
 
 all:		dump
 
 build-tools:	dump
+WARNS=		3
 
 # Helper since iconv is non trivial to make a build tool
 utf8convert:
 .for c in dutch french german spanish swedish
 	iconv -f ISO8859-1 -t UTF-8 $V/catalog/$c.base > $c.UTF-8.base
 .endfor
 	iconv -f ISO8859-2 -t UTF-8 $V/catalog/polish.base > polish.UTF-8.base
 	iconv -f GB2312 -t UTF-8 $V/catalog/zh_CN.GB2312.base > zh_CN.UTF-8.base
 	iconv -f KOI8-R -t UTF-8 $V/catalog/ru_RU.KOI8-R.base > ru_RU.UTF-8.base
 	iconv -f KOI8-U -t UTF-8 $V/catalog/uk_UA.KOI8-U.base > uk_UA.UTF-8.base
 	
 .for c in dutch french german polish spanish swedish zh_CN ru_RU uk_UA
 CAT+=	$c.UTF-8
 .endfor
 
 .for c in ${CAT}
 ${c}:	${c}.base 
 	echo "... $c";							\
 	rm -f $c;							\
 	env LC_ALL=C sort -u ${.ALLSRC} |				\
 	awk '{								\
 		if ($$1 == 1) {						\
 			print "\nMESSAGE NUMBER 1 IS NOT LEGAL";	\
 			exit 1;						\
 		}							\
 		if (++nline > $$1) {					\
 			print "DUPLICATE MESSAGE NUMBER " $$1;		\
 			exit 1;						\
 		}							\
 		print $$0;						\
 	}' |								\
 	sed -e '1s/^/$$set 1~$$quote "~/; 1y/~/\n/' |			\
 	gencat $c /dev/stdin;						\
 	chmod 444 $c;							\
 	if grep DUPLICATE $c > /dev/null; then				\
 		grep DUPLICATE $@;					\
 	fi;								\
 	if grep 'NOT LEGAL' $c > /dev/null; then			\
 		grep 'NOT LEGAL' $@;					\
 	fi
 .endfor
 
 CHK=	dutch.check english.check french.check german.check \
 	polish.check ru_RU.KOI8-R.check spanish.check swedish.check \
 	uk_UA.KOI8-U.check zh_CN.GB2312.check
 check: ${CHK}
 .for c in ${CAT}
 ${c}.check: ${c}.base
 	@echo "... $c";							\
 	f=${.ALLSRC:S;.base$;;};					\
 	(echo "Unused message id's (this is okay):";			\
 	awk '{								\
 		while (++nline < $$1)					\
 			printf "%03d\n", nline;				\
 	}' < $$f.base;							\
 	echo =========================;					\
 	echo "MISSING ERROR MESSAGES (Please add!):";			\
 	awk '{print $$1}' < $$f.base | sort -u > __ck1;			\
 	awk '{print $$1}' < english.base | sort -u > __ck2;		\
 	comm -13 __ck1 __ck2;						\
 	echo =========================;					\
 	echo "Extra error messages (just delete them):";		\
 	comm -23 __ck1 __ck2;						\
 	echo =========================;					\
 	echo "MESSAGES WITH THE SAME MESSAGE ID's (FIX!):";		\
 	for j in							\
 	    `sed '/^$$/d' < $$f.base | LC_ALL=C sort -u |		\
 	    awk '{print $$1}' | uniq -d`; do				\
 		egrep $$j $$f.base;					\
 	done;								\
 	echo =========================;					\
 	echo "Duplicate messages, both id and message (this is okay):";	\
 	sed '/^$$/d' < $$f.base | LC_ALL=C sort | uniq -c |		\
 	awk '$$1 != 1 { print $$0 }' | sort -n;				\
 	echo =========================) > $c
 .endfor
 
 english.base: dump ${SCAN} #Makefile
 	${BTOOLSPATH:U.}/dump ${SCAN} |\
 	sed -e '/|/!d' \
 	    -e 's/|/ "/' \
 	    -e 's/^"//' |\
-	sort -nu > $@
+	LC_ALL=C sort -nu > $@
 
 
 DEPENDOBJS+=	dump
 dump: ${BUILD_TOOLS_META}
 
 CLEANFILES+= dump ${CAT} english.base *.check __ck1 __ck2
 
 CATALOGS= ${CAT}
 NLLINKS= nl_NL
 ENLINKS= en_AU en_CA en_GB en_NZ en_US
 FRLINKS= fr_BE fr_CA fr_CH fr_FR
 DELINKS= de_AT de_CH de_DE
 ESLINKS= es_ES
 SVLINKS= sv_SE
 PLLINKS= pl_PL
 
 FILES=	${CATALOGS}
 FILESDIR= ${SHAREDIR}/vi/catalog
 SYMLINKS=
 .for l in ${NLLINKS}
 SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= dutch ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= dutch.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 .for l in ${ENLINKS}
 SYMLINKS+= english ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= english ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= english ${FILESDIR}/$l.US-ASCII
 SYMLINKS+= english ${FILESDIR}/$l.UTF-8
 .endfor
 SYMLINKS+= english ${FILESDIR}/POSIX
 SYMLINKS+= english ${FILESDIR}/C
 .for l in ${FRLINKS}
 SYMLINKS+= french ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= french ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= french.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 .for l in ${DELINKS}
 SYMLINKS+= german ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= german ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= german.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 .for l in ${ESLINKS}
 SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= spanish ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= spanish.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 .for l in ${SVLINKS}
 SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-1
 SYMLINKS+= swedish ${FILESDIR}/$l.ISO8859-15
 SYMLINKS+= swedish.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 .for l in ${PLLINKS}
 SYMLINKS+= polish ${FILESDIR}/$l.ISO8859-2
 SYMLINKS+= polish.UTF-8 ${FILESDIR}/$l.UTF-8
 .endfor
 SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.GB18030
 SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.GBK
 SYMLINKS+= zh_CN.GB2312 ${FILESDIR}/zh_CN.eucCN
 
 .include <bsd.prog.mk>
Index: head/usr.bin/vi/pathnames.h
===================================================================
--- head/usr.bin/vi/pathnames.h	(revision 366308)
+++ head/usr.bin/vi/pathnames.h	(revision 366309)
@@ -1,28 +1,27 @@
 /* $FreeBSD$ */
 
 /* Read standard system paths first. */
 #include <paths.h>
 
 #ifndef	_PATH_EXRC
 #define	_PATH_EXRC	".exrc"
 #endif
 
 #ifndef	_PATH_MSGCAT
 #define	_PATH_MSGCAT	"/usr/share/vi/catalog/"
 #endif
 
 #ifndef	_PATH_NEXRC
 #define	_PATH_NEXRC	".nexrc"
 #endif
 
-#ifndef	_PATH_PRESERVE
-#define	_PATH_PRESERVE	"/var/tmp/vi.recover/"
-#endif
+/* On linux _PATH_PRESERVE is only writable by root */
+#define	NVI_PATH_PRESERVE	"/var/tmp/vi.recover/"
 
 #ifndef	_PATH_SYSEXRC
 #define	_PATH_SYSEXRC	"/etc/vi.exrc"
 #endif
 
 #ifndef	_PATH_TAGS
 #define	_PATH_TAGS	"tags"
 #endif