This is `nvi' (tweaked to install as vi), version 1.01.
authorJordan K. Hubbard <jkh@FreeBSD.org>
Mon, 24 Jan 1994 01:14:16 +0000 (01:14 +0000)
committerJordan K. Hubbard <jkh@FreeBSD.org>
Mon, 24 Jan 1994 01:14:16 +0000 (01:14 +0000)
82 files changed:
usr.bin/vi/Makefile [new file with mode: 0644]
usr.bin/vi/args.h [new file with mode: 0644]
usr.bin/vi/ascii.c [new file with mode: 0644]
usr.bin/vi/clib/Xaddnstr.c [new file with mode: 0644]
usr.bin/vi/clib/addidlok.c [new file with mode: 0644]
usr.bin/vi/clib/err.c [new file with mode: 0644]
usr.bin/vi/clib/fchmod.c [new file with mode: 0644]
usr.bin/vi/clib/flock.c [new file with mode: 0644]
usr.bin/vi/clib/fwopen.c [new file with mode: 0644]
usr.bin/vi/clib/lockf.c [new file with mode: 0644]
usr.bin/vi/clib/memmove.c [new file with mode: 0644]
usr.bin/vi/clib/memset.c [new file with mode: 0644]
usr.bin/vi/clib/mktemp.c [new file with mode: 0644]
usr.bin/vi/clib/pty.c [new file with mode: 0644]
usr.bin/vi/clib/pty_s5r4.c [new file with mode: 0644]
usr.bin/vi/clib/realloc.c [new file with mode: 0644]
usr.bin/vi/clib/siglist.c [new file with mode: 0644]
usr.bin/vi/clib/snprintf.c [new file with mode: 0644]
usr.bin/vi/clib/strdup.c [new file with mode: 0644]
usr.bin/vi/clib/strerror.c [new file with mode: 0644]
usr.bin/vi/clib/strsep.c [new file with mode: 0644]
usr.bin/vi/clib/strtoul.c [new file with mode: 0644]
usr.bin/vi/cut.c [new file with mode: 0644]
usr.bin/vi/cut.h [new file with mode: 0644]
usr.bin/vi/delete.c [new file with mode: 0644]
usr.bin/vi/exf.c [new file with mode: 0644]
usr.bin/vi/exf.h [new file with mode: 0644]
usr.bin/vi/gs.h [new file with mode: 0644]
usr.bin/vi/include/bitstring.h [new file with mode: 0644]
usr.bin/vi/include/cdefs.h [new file with mode: 0644]
usr.bin/vi/include/compat.h [new file with mode: 0644]
usr.bin/vi/include/err.h [new file with mode: 0644]
usr.bin/vi/include/file.h [new file with mode: 0644]
usr.bin/vi/include/glob.h [new file with mode: 0644]
usr.bin/vi/include/mpool.h [new file with mode: 0644]
usr.bin/vi/include/ndbm.h [new file with mode: 0644]
usr.bin/vi/include/queue.h [new file with mode: 0644]
usr.bin/vi/interrupt.h [new file with mode: 0644]
usr.bin/vi/line.c [new file with mode: 0644]
usr.bin/vi/log.c [new file with mode: 0644]
usr.bin/vi/log.h [new file with mode: 0644]
usr.bin/vi/main.c [new file with mode: 0644]
usr.bin/vi/mark.c [new file with mode: 0644]
usr.bin/vi/mark.h [new file with mode: 0644]
usr.bin/vi/mem.h [new file with mode: 0644]
usr.bin/vi/msg.h [new file with mode: 0644]
usr.bin/vi/options.c [new file with mode: 0644]
usr.bin/vi/options.h.stub [new file with mode: 0644]
usr.bin/vi/options_f.c [new file with mode: 0644]
usr.bin/vi/pathnames.h [new file with mode: 0644]
usr.bin/vi/recover.c [new file with mode: 0644]
usr.bin/vi/screen.c [new file with mode: 0644]
usr.bin/vi/screen.h [new file with mode: 0644]
usr.bin/vi/search.c [new file with mode: 0644]
usr.bin/vi/search.h [new file with mode: 0644]
usr.bin/vi/seq.c [new file with mode: 0644]
usr.bin/vi/seq.h [new file with mode: 0644]
usr.bin/vi/sex/sex_confirm.c [new file with mode: 0644]
usr.bin/vi/sex/sex_get.c [new file with mode: 0644]
usr.bin/vi/sex/sex_refresh.c [new file with mode: 0644]
usr.bin/vi/sex/sex_screen.c [new file with mode: 0644]
usr.bin/vi/sex/sex_screen.h [new file with mode: 0644]
usr.bin/vi/sex/sex_term.c [new file with mode: 0644]
usr.bin/vi/sex/sex_util.c [new file with mode: 0644]
usr.bin/vi/svi/svi_confirm.c [new file with mode: 0644]
usr.bin/vi/svi/svi_ex.c [new file with mode: 0644]
usr.bin/vi/svi/svi_get.c [new file with mode: 0644]
usr.bin/vi/svi/svi_line.c [new file with mode: 0644]
usr.bin/vi/svi/svi_refresh.c [new file with mode: 0644]
usr.bin/vi/svi/svi_relative.c [new file with mode: 0644]
usr.bin/vi/svi/svi_screen.c [new file with mode: 0644]
usr.bin/vi/svi/svi_screen.h [new file with mode: 0644]
usr.bin/vi/svi/svi_smap.c [new file with mode: 0644]
usr.bin/vi/svi/svi_split.c [new file with mode: 0644]
usr.bin/vi/svi/svi_util.c [new file with mode: 0644]
usr.bin/vi/term.c [new file with mode: 0644]
usr.bin/vi/term.h [new file with mode: 0644]
usr.bin/vi/timer.c [new file with mode: 0644]
usr.bin/vi/trace.c [new file with mode: 0644]
usr.bin/vi/util.c [new file with mode: 0644]
usr.bin/vi/vi.h [new file with mode: 0644]
usr.bin/vi/xaw/xaw_screen.c [new file with mode: 0644]

diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile
new file mode 100644 (file)
index 0000000..57671e6
--- /dev/null
@@ -0,0 +1,105 @@
+#      @(#)Makefile    8.26 (Berkeley) 1/12/94
+
+PROG=  vi
+MAN1=  vi.1
+BINDIR?=       /usr/bin
+
+#CFLAGS=-g -DDEBUG
+#CFLAGS+=-pg
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/../include -I${.CURDIR}/../curses -I${.CURDIR}/ex -I${.CURDIR}/vi -I${.CURDIR}/../regex
+#STRIP=
+.PATH: ${.CURDIR}/ex ${.CURDIR}/sex ${.CURDIR}/vi ${.CURDIR}/svi \
+       ${.CURDIR}/xaw
+CLEANFILES+=ex
+
+# General sources.
+SRCS=  ascii.c cut.c delete.c exf.c line.c log.c main.c mark.c \
+       options.c options_f.c screen.c search.c seq.c recover.c \
+       term.c timer.c trace.c util.c
+
+# Ex source.
+SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c \
+       ex_bang.c ex_cd.c ex_delete.c ex_digraph.c ex_display.c \
+       ex_edit.c ex_equal.c ex_exit.c ex_file.c ex_global.c ex_init.c \
+       ex_join.c ex_map.c ex_mark.c ex_mkexrc.c ex_move.c ex_open.c \
+       ex_preserve.c ex_print.c ex_put.c ex_read.c ex_screen.c \
+       ex_script.c ex_set.c ex_shell.c ex_shift.c ex_source.c ex_stop.c \
+       ex_subst.c ex_tag.c ex_undo.c ex_usage.c ex_util.c ex_version.c \
+       ex_visual.c ex_write.c ex_yank.c ex_z.c excmd.c filter.c
+
+# Ex screen source.
+SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \
+       sex_util.c
+
+# Vi source.
+SRCS+= getc.c v_again.c v_at.c v_ch.c v_delete.c v_ex.c v_exit.c \
+       v_exmode.c v_filter.c v_increment.c v_init.c v_join.c v_left.c \
+       v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \
+       v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \
+       v_sentence.c v_shift.c v_status.c v_stop.c v_switch.c v_tag.c \
+       v_text.c v_ulcase.c v_undo.c v_util.c v_word.c v_xchar.c v_yank.c \
+       v_z.c vcmd.c vi.c
+
+# Vi curses screen source.
+SRCS+= svi_confirm.c svi_ex.c svi_get.c svi_line.c svi_refresh.c \
+       svi_relative.c svi_screen.c svi_smap.c svi_split.c svi_util.c
+
+# Athena widget set screen source.
+SRCS+= xaw_screen.c
+
+#LDADD+=-pg
+DPADD+= ${LIBCURSES} ${LIBTERM}
+LDADD+=        -L${.CURDIR}/../curses/obj -L${.CURDIR}/../curses \
+       -L${.CURDIR}/../regex/obj -L${.CURDIR}/../regex \
+       -lregex -lcurses -ltermlib -lutil
+SPECHDR=excmd.h options.h
+CLEANFILES+=${SPECHDR}
+LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view
+
+all: .curses-stamp vi vi.1
+
+.curses-stamp:
+       @(cd ${.CURDIR}/curses; make obj)
+       @(cd ${.CURDIR}/curses; make)
+       touch -f .curses-stamp
+
+warn:: ${SRCS}
+       -(cd ${.CURDIR} && \
+           gcc -Wall -O -DDEBUG -Iobj -Ivi -Iex -I. ${.ALLSRC} \
+           -lcurses -ltermlib 2>&1 | \
+           sed -e "/warning: .*sccsid.*defined but not used/d" \
+               -e "/warning: suggest parentheses around/d" \
+               -e "/In function /d" \
+               -e "/At top level:/d" \
+               -e "/warning: .*inline call to/d" \
+               -e "/warning: comparison is always 1 due /d") > \
+               ${.CURDIR}/WARN.OUT
+
+options.h: options.h.stub options.c # Makefile
+       rm -f options.h
+       cp ${.CURDIR}/options.h.stub options.h
+       chmod 664 options.h
+       (echo '/^\/\* O_[0-9A-Z_]*/ {'; \
+        echo 'printf("#define %s %d\n", $$2, cnt++)'; \
+        echo 'next'; \
+        echo '}'; \
+        echo 'END {'; \
+        echo 'printf("#define O_OPTIONCOUNT %d\n", cnt)'; \
+        echo '}') > /tmp/__vi.options.h
+        awk -f /tmp/__vi.options.h ${.CURDIR}/options.c >> options.h
+        rm -f /tmp/__vi.options.h
+
+excmd.h: excmd.h.stub excmd.c # Makefile
+       rm -f excmd.h
+       cp ${.CURDIR}/ex/excmd.h.stub excmd.h
+       chmod 664 excmd.h
+       (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \
+        echo 'printf("#define %s %d\n", $$2, cnt++)'; \
+        echo 'next'; \
+        echo '}') > /tmp/__vi.excmd.h
+        awk -f /tmp/__vi.excmd.h ${.CURDIR}/ex/excmd.c >> excmd.h
+        rm -f /tmp/__vi.excmd.h
+
+.include <bsd.prog.mk>
+
+.depend: ${SPECHDR}
diff --git a/usr.bin/vi/args.h b/usr.bin/vi/args.h
new file mode 100644 (file)
index 0000000..4d43744
--- /dev/null
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)args.h      8.3 (Berkeley) 12/19/93
+ */
+
+/*
+ * Structure for building "argc/argv" vector of arguments.
+ *
+ * !!!
+ * All arguments are nul terminated as well as having an associated length.
+ * The argument vector is NOT necessarily NULL terminated.  The proper way
+ * to check the number of arguments is to use the argc value in the EXCMDARG
+ * structure or to walk the array until an ARGS structure with a length of 0
+ * is found.
+ */
+typedef struct _args {
+       CHAR_T  *bp;            /* Argument. */
+       size_t   blen;          /* Buffer length. */
+       size_t   len;           /* Argument length. */
+
+#define        A_ALLOCATED     0x01    /* If allocated space. */
+       u_char   flags;
+} ARGS;
diff --git a/usr.bin/vi/ascii.c b/usr.bin/vi/ascii.c
new file mode 100644 (file)
index 0000000..61c7948
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ascii.c    8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+
+CHNAME const asciiname[UCHAR_MAX + 1] = {
+         {"^@", 2},   {"^A", 2},   {"^B", 2},   {"^C", 2},
+         {"^D", 2},   {"^E", 2},   {"^F", 2},   {"^G", 2}, 
+         {"^H", 2},   {"^I", 2},   {"^J", 2},   {"^K", 2},
+         {"^L", 2},   {"^M", 2},   {"^N", 2},   {"^O", 2}, 
+         {"^P", 2},   {"^Q", 2},   {"^R", 2},   {"^S", 2},
+         {"^T", 2},   {"^U", 2},   {"^V", 2},   {"^W", 2}, 
+         {"^X", 2},   {"^Y", 2},   {"^Z", 2},   {"^[", 2},
+        {"^\\", 2},   {"^]", 2},   {"^^", 2},   {"^_", 2}, 
+          {" ", 1},    {"!", 1},   {"\"", 1},    {"#", 1},
+          {"$", 1},    {"%", 1},    {"&", 1},    {"'", 1}, 
+          {"(", 1},    {")", 1},    {"*", 1},    {"+", 1},
+          {",", 1},    {"-", 1},    {".", 1},    {"/", 1}, 
+          {"0", 1},    {"1", 1},    {"2", 1},    {"3", 1},
+          {"4", 1},    {"5", 1},    {"6", 1},    {"7", 1}, 
+          {"8", 1},    {"9", 1},    {":", 1},    {";", 1},
+          {"<", 1},    {"=", 1},    {">", 1},    {"?", 1}, 
+          {"@", 1},    {"A", 1},    {"B", 1},    {"C", 1},
+          {"D", 1},    {"E", 1},    {"F", 1},    {"G", 1}, 
+          {"H", 1},    {"I", 1},    {"J", 1},    {"K", 1},
+          {"L", 1},    {"M", 1},    {"N", 1},    {"O", 1}, 
+          {"P", 1},    {"Q", 1},    {"R", 1},    {"S", 1},
+          {"T", 1},    {"U", 1},    {"V", 1},    {"W", 1}, 
+          {"X", 1},    {"Y", 1},    {"Z", 1},    {"[", 1},
+         {"\\", 1},    {"]", 1},    {"^", 1},    {"_", 1}, 
+          {"`", 1},    {"a", 1},    {"b", 1},    {"c", 1},
+          {"d", 1},    {"e", 1},    {"f", 1},    {"g", 1}, 
+          {"h", 1},    {"i", 1},    {"j", 1},    {"k", 1},
+          {"l", 1},    {"m", 1},    {"n", 1},    {"o", 1}, 
+          {"p", 1},    {"q", 1},    {"r", 1},    {"s", 1},
+          {"t", 1},    {"u", 1},    {"v", 1},    {"w", 1}, 
+          {"x", 1},    {"y", 1},    {"z", 1},    {"{", 1},
+          {"|", 1},    {"}", 1},    {"~", 1},   {"^?", 2},
+       {"0x80", 4}, {"0x81", 4}, {"0x82", 4}, {"0x83", 4},
+       {"0x84", 4}, {"0x85", 4}, {"0x86", 4}, {"0x87", 4},
+       {"0x88", 4}, {"0x89", 4}, {"0x8a", 4}, {"0x8b", 4},
+       {"0x8c", 4}, {"0x8d", 4}, {"0x8e", 4}, {"0x8f", 4},
+       {"0x90", 4}, {"0x91", 4}, {"0x92", 4}, {"0x93", 4},
+       {"0x94", 4}, {"0x95", 4}, {"0x96", 4}, {"0x97", 4},
+       {"0x98", 4}, {"0x99", 4}, {"0x9a", 4}, {"0x9b", 4},
+       {"0x9c", 4}, {"0x9d", 4}, {"0x9e", 4}, {"0x9f", 4},
+       {"0xa0", 4}, {"0xa1", 4}, {"0xa2", 4}, {"0xa3", 4},
+       {"0xa4", 4}, {"0xa5", 4}, {"0xa6", 4}, {"0xa7", 4},
+       {"0xa8", 4}, {"0xa9", 4}, {"0xaa", 4}, {"0xab", 4},
+       {"0xac", 4}, {"0xad", 4}, {"0xae", 4}, {"0xaf", 4},
+       {"0xb0", 4}, {"0xb1", 4}, {"0xb2", 4}, {"0xb3", 4},
+       {"0xb4", 4}, {"0xb5", 4}, {"0xb6", 4}, {"0xb7", 4},
+       {"0xb8", 4}, {"0xb9", 4}, {"0xba", 4}, {"0xbb", 4},
+       {"0xbc", 4}, {"0xbd", 4}, {"0xbe", 4}, {"0xbf", 4},
+       {"0xc0", 4}, {"0xc1", 4}, {"0xc2", 4}, {"0xc3", 4},
+       {"0xc4", 4}, {"0xc5", 4}, {"0xc6", 4}, {"0xc7", 4},
+       {"0xc8", 4}, {"0xc9", 4}, {"0xca", 4}, {"0xcb", 4},
+       {"0xcc", 4}, {"0xcd", 4}, {"0xce", 4}, {"0xcf", 4},
+       {"0xd0", 4}, {"0xd1", 4}, {"0xd2", 4}, {"0xd3", 4},
+       {"0xd4", 4}, {"0xd5", 4}, {"0xd6", 4}, {"0xd7", 4},
+       {"0xd8", 4}, {"0xd9", 4}, {"0xda", 4}, {"0xdb", 4},
+       {"0xdc", 4}, {"0xdd", 4}, {"0xde", 4}, {"0xdf", 4},
+       {"0xe0", 4}, {"0xe1", 4}, {"0xe2", 4}, {"0xe3", 4},
+       {"0xe4", 4}, {"0xe5", 4}, {"0xe6", 4}, {"0xe7", 4},
+       {"0xe8", 4}, {"0xe9", 4}, {"0xea", 4}, {"0xeb", 4},
+       {"0xec", 4}, {"0xed", 4}, {"0xee", 4}, {"0xef", 4},
+       {"0xf0", 4}, {"0xf1", 4}, {"0xf2", 4}, {"0xf3", 4},
+       {"0xf4", 4}, {"0xf5", 4}, {"0xf6", 4}, {"0xf7", 4},
+       {"0xf8", 4}, {"0xf9", 4}, {"0xfa", 4}, {"0xfb", 4},
+       {"0xfc", 4}, {"0xfd", 4}, {"0xfe", 4}, {"0xff", 4},
+};
+
+char *
+charname(sp, ch)
+       SCR *sp;
+       ARG_CHAR_T ch;
+{
+       return (sp->gp->cname[ch & UCHAR_MAX].name);
+}
diff --git a/usr.bin/vi/clib/Xaddnstr.c b/usr.bin/vi/clib/Xaddnstr.c
new file mode 100644 (file)
index 0000000..9d27028
--- /dev/null
@@ -0,0 +1,15 @@
+#include <sys/cdefs.h>
+
+#include <curses.h>
+
+int
+addnstr(s, n)
+       const char *s;
+       int n;
+{
+       int ch;
+
+       while (n-- && (ch = *s++))
+               addch(ch);
+       return (OK);
+}
diff --git a/usr.bin/vi/clib/addidlok.c b/usr.bin/vi/clib/addidlok.c
new file mode 100644 (file)
index 0000000..906992d
--- /dev/null
@@ -0,0 +1,13 @@
+#include <curses.h>
+
+/*
+ * idlok --
+ *      Fake idlok.
+ */
+void
+idlok(win, bf)
+        WINDOW *win;
+        int bf;
+{
+       return;
+}
diff --git a/usr.bin/vi/clib/err.c b/usr.bin/vi/clib/err.c
new file mode 100644 (file)
index 0000000..7b3ca9b
--- /dev/null
@@ -0,0 +1,151 @@
+#include <sys/cdefs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+char *__progname = "nvi";              /* Program name, from crt0. */
+
+void
+#ifdef __STDC__
+err(int eval, const char *fmt, ...)
+#else
+err(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verr(eval, fmt, ap);
+       va_end(ap);
+}
+
+void
+verr(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+       exit(eval);
+}
+
+void
+#ifdef __STDC__
+errx(int eval, const char *fmt, ...)
+#else
+errx(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verrx(eval, fmt, ap);
+       va_end(ap);
+}
+
+void
+verrx(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+       exit(eval);
+}
+
+void
+#ifdef __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
+}
+
+void
+vwarn(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+}
+
+void
+#ifdef __STDC__
+warnx(const char *fmt, ...)
+#else
+warnx(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarnx(fmt, ap);
+       va_end(ap);
+}
+
+void
+vwarnx(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+}
diff --git a/usr.bin/vi/clib/fchmod.c b/usr.bin/vi/clib/fchmod.c
new file mode 100644 (file)
index 0000000..887786c
--- /dev/null
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <compat.h>
+
+int
+fchmod(fd, mode)
+       int fd;
+       u_int mode;
+{
+       char *tty;
+
+       /* Find the tty. */
+       if ((tty = ttyname(fd)) == NULL)
+               return (-1);
+
+       return (chmod(tty, mode));
+}
diff --git a/usr.bin/vi/clib/flock.c b/usr.bin/vi/clib/flock.c
new file mode 100644 (file)
index 0000000..0027373
--- /dev/null
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+/*
+ * Include <sys/file.h>, not <fcntl.h>, the flock(2)
+ * #defines were found there on historical systems.
+ */
+#include <sys/file.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <compat.h>
+
+/*
+ * Use fcntl(2) locking to fake flock(2) locking.
+ *
+ * DO NOT USE fcntl(2) UNLESS YOU HAVE TO, THERE ARE SOME SYSTEMS
+ * (I.E. ULTRIX) WHERE LOCKS AREN'T RELEASED WHEN PROCESSES DIE.
+ */
+int
+flock(fd, operation)
+       int fd, operation;
+{
+       struct flock arg;
+
+       switch (operation & ~LOCK_NB) {
+       case LOCK_EX:
+               arg.l_type = F_WRLCK;
+               break;
+       case LOCK_SH:
+               arg.l_type = F_RDLCK;
+               break;
+       case LOCK_UN:
+               arg.l_type = F_UNLCK;
+               break;
+       default:
+               abort();
+       }
+
+       arg.l_start = arg.l_len = 0;
+       arg.l_pid = 0;
+       arg.l_whence = 0;               /* SEEK_SET */
+       
+       if (!fcntl(fd, operation & LOCK_NB ? F_SETLK : F_SETLKW, &arg))
+               return (0);
+       if (errno == EACCES || errno == EAGAIN)
+               errno = EWOULDBLOCK;
+       return (-1);
+}
diff --git a/usr.bin/vi/clib/fwopen.c b/usr.bin/vi/clib/fwopen.c
new file mode 100644 (file)
index 0000000..c6d4c67
--- /dev/null
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1993 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)fwopen.c   8.5 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <stdio.h>
+#undef fwopen                                  /* For testing. */
+#include <unistd.h>
+
+#include "vi.h"
+#include "pathnames.h"
+
+/*
+ * The major portability problem in nvi is that it uses new functionality
+ * from 4.4BSD to handle the interaction between vi and ex.
+ *
+ * Ex was written to use the stdio output routines because it's a lot easier
+ * that way.  Since vi wants to take that output and put it up on the screen
+ * using curses, it needs to replace the underlying read/write routines in a
+ * stdio stream with its own.  The way this works is that when vi wants to
+ * use the ex routines, it sets it up so that the output of ex goes to an nvi
+ * function which knows how to display the output on the screen.  This way vi
+ * never has to leave curses (resulting in much nicer screen displays) and ex
+ * can use printf(3) without concern for what else is going on.  4.4BSD has a
+ * stdio function (fwopen(3)) which provides this functionality.  Most other
+ * systems don't.
+ *
+ * Vi/ex uses the following key strings:
+ *
+ *     ex_printf       -- ex printf routine
+ *     EXCOOKIE        -- the cookie passed to the ex printf routine
+ *     ex_fflush       -- ex flush routine
+ *
+ * and there are #defines (based on FWOPEN_NOT_AVAILABLE) in vi.h to set them
+ * to the corresponding stdio(3) routines if fwopen(3) is available.  If it's
+ * not available, this file contains the routines that fake it for you.
+ */
+
+/*
+ * fwopen --
+ *     Return an open file descriptor.
+ */
+FILE *
+fwopen(sp, func)
+       SCR *sp;
+       void *func;
+{
+       return (fopen(_PATH_DEVNULL, "w"));
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static size_t off;
+static char buf[1024];
+
+/*
+ * ex_printf --
+ *     Ex's version of printf.
+ *
+ * XXX
+ * Hard limits -- if we get more than 1024 of formatted
+ * input in a write, it will be discarded.
+ */
+int
+#ifdef __STDC__
+ex_printf(SCR *sp, const char *fmt, ...)
+#else
+ex_printf(sp, fmt, va_alist)
+       SCR *sp;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+       int n;
+       char b1[1024];
+
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       if (sp->stdfp == stdout)
+           return (vfprintf(stdout, fmt, ap));
+
+       n = vsnprintf(b1, sizeof(b1), fmt, ap);
+       va_end(ap);
+
+       if (n > 512 || n + off > sizeof(buf)) {
+               (void)sp->s_ex_write(sp, buf, off);
+               off = 0;
+               if (n > 512) {
+                       (void)sp->s_ex_write(sp, b1, n);
+                       return (n);
+               }
+       }
+       memmove(buf + off, b1, n);
+       off += n;
+       return (n);
+}
+
+/*
+ * ex_fflush --
+ *     Ex's version of fflush.
+ */
+int
+ex_fflush(sp)
+       SCR *sp;
+{
+       if (sp->stdfp == stdout)
+               return (fflush(stdout));
+
+       if (off) {
+               (void)sp->s_ex_write(sp, buf, off);
+               off = 0;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/clib/lockf.c b/usr.bin/vi/clib/lockf.c
new file mode 100644 (file)
index 0000000..a7f6a55
--- /dev/null
@@ -0,0 +1,27 @@
+#include <sys/types.h>
+/*
+ * Include <sys/file.h>, not <fcntl.h>, the flock(2)
+ * #defines were found there on historical systems.
+ */
+#include <sys/file.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <compat.h>
+
+/*
+ * Use lockf(2) locking to fake flock(2) locking.
+ */
+int
+flock(fd, operation)
+       int fd, operation;
+{
+       if (!lockf(fd,
+           operation & LOCK_UN ? F_ULOCK :
+           operation & LOCK_NB ? F_TLOCK : F_LOCK, 0))
+               return (0);
+       if (errno == EACCES || errno == EAGAIN)
+               errno = EWOULDBLOCK;
+       return (-1);
+}
diff --git a/usr.bin/vi/clib/memmove.c b/usr.bin/vi/clib/memmove.c
new file mode 100644 (file)
index 0000000..f90b09c
--- /dev/null
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bcopy.c    8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <string.h>
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef        int word;               /* "word" used for optimal copy speed */
+
+#define        wsize   sizeof(word)
+#define        wmask   (wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ */
+#ifdef MEMCOPY
+void *
+memcpy(dst0, src0, length)
+#else
+#ifdef MEMMOVE
+void *
+memmove(dst0, src0, length)
+#else
+void
+bcopy(src0, dst0, length)
+#endif
+#endif
+       void *dst0;
+       const void *src0;
+       register size_t length;
+{
+       register char *dst = dst0;
+       register const char *src = src0;
+       register size_t t;
+
+       if (length == 0 || dst == src)          /* nothing to do */
+               goto done;
+
+       /*
+        * Macros: loop-t-times; and loop-t-times, t>0
+        */
+#define        TLOOP(s) if (t) TLOOP1(s)
+#define        TLOOP1(s) do { s; } while (--t)
+
+       if ((unsigned long)dst < (unsigned long)src) {
+               /*
+                * Copy forward.
+                */
+               t = (int)src;   /* only need low bits */
+               if ((t | (int)dst) & wmask) {
+                       /*
+                        * Try to align operands.  This cannot be done
+                        * unless the low bits match.
+                        */
+                       if ((t ^ (int)dst) & wmask || length < wsize)
+                               t = length;
+                       else
+                               t = wsize - (t & wmask);
+                       length -= t;
+                       TLOOP1(*dst++ = *src++);
+               }
+               /*
+                * Copy whole words, then mop up any trailing bytes.
+                */
+               t = length / wsize;
+               TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
+               t = length & wmask;
+               TLOOP(*dst++ = *src++);
+       } else {
+               /*
+                * Copy backwards.  Otherwise essentially the same.
+                * Alignment works as before, except that it takes
+                * (t&wmask) bytes to align, not wsize-(t&wmask).
+                */
+               src += length;
+               dst += length;
+               t = (int)src;
+               if ((t | (int)dst) & wmask) {
+                       if ((t ^ (int)dst) & wmask || length <= wsize)
+                               t = length;
+                       else
+                               t &= wmask;
+                       length -= t;
+                       TLOOP1(*--dst = *--src);
+               }
+               t = length / wsize;
+               TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
+               t = length & wmask;
+               TLOOP(*--dst = *--src);
+       }
+done:
+#if defined(MEMCOPY) || defined(MEMMOVE)
+       return (dst0);
+#else
+       return;
+#endif
+}
diff --git a/usr.bin/vi/clib/memset.c b/usr.bin/vi/clib/memset.c
new file mode 100644 (file)
index 0000000..9c28b7b
--- /dev/null
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Hibler and Chris Torek.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)memset.c   8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <string.h>
+
+#define        wsize   sizeof(u_int)
+#define        wmask   (wsize - 1)
+
+#ifdef BZERO
+#define        RETURN  return
+#define        VAL     0
+#define        WIDEVAL 0
+
+void
+bzero(dst0, length)
+       void *dst0;
+       register size_t length;
+#else
+#define        RETURN  return (dst0)
+#define        VAL     c0
+#define        WIDEVAL c
+
+void *
+memset(dst0, c0, length)
+       void *dst0;
+       register int c0;
+       register size_t length;
+#endif
+{
+       register size_t t;
+       register u_int c;
+       register u_char *dst;
+
+       dst = dst0;
+       /*
+        * If not enough words, just fill bytes.  A length >= 2 words
+        * guarantees that at least one of them is `complete' after
+        * any necessary alignment.  For instance:
+        *
+        *      |-----------|-----------|-----------|
+        *      |00|01|02|03|04|05|06|07|08|09|0A|00|
+        *                ^---------------------^
+        *               dst             dst+length-1
+        *
+        * but we use a minimum of 3 here since the overhead of the code
+        * to do word writes is substantial.
+        */ 
+       if (length < 3 * wsize) {
+               while (length != 0) {
+                       *dst++ = VAL;
+                       --length;
+               }
+               RETURN;
+       }
+
+#ifndef BZERO
+       if ((c = (u_char)c0) != 0) {    /* Fill the word. */
+               c = (c << 8) | c;       /* u_int is 16 bits. */
+#if UINT_MAX > 0xffff
+               c = (c << 16) | c;      /* u_int is 32 bits. */
+#endif
+#if UINT_MAX > 0xffffffff
+               c = (c << 32) | c;      /* u_int is 64 bits. */
+#endif
+       }
+#endif
+       /* Align destination by filling in bytes. */
+       if ((t = (int)dst & wmask) != 0) {
+               t = wsize - t;
+               length -= t;
+               do {
+                       *dst++ = VAL;
+               } while (--t != 0);
+       }
+
+       /* Fill words.  Length was >= 2*words so we know t >= 1 here. */
+       t = length / wsize;
+       do {
+               *(u_int *)dst = WIDEVAL;
+               dst += wsize;
+       } while (--t != 0);
+
+       /* Mop up trailing bytes, if any. */
+       t = length & wmask;
+       if (t != 0)
+               do {
+                       *dst++ = VAL;
+               } while (--t != 0);
+       RETURN;
+}
diff --git a/usr.bin/vi/clib/mktemp.c b/usr.bin/vi/clib/mktemp.c
new file mode 100644 (file)
index 0000000..157c36f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1987, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c   8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <compat.h>
+
+static int _gettemp();
+
+mkstemp(path)
+       char *path;
+{
+       int fd;
+
+       return (_gettemp(path, &fd) ? fd : -1);
+}
+
+char *
+mktemp(path)
+       char *path;
+{
+       return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
+}
+
+static
+_gettemp(path, doopen)
+       char *path;
+       register int *doopen;
+{
+       extern int errno;
+       register char *start, *trv;
+       struct stat sbuf;
+       u_int pid;
+
+       pid = getpid();
+       for (trv = path; *trv; ++trv);          /* extra X's get set to 0's */
+       while (*--trv == 'X') {
+               *trv = (pid % 10) + '0';
+               pid /= 10;
+       }
+
+       /*
+        * check the target directory; if you have six X's and it
+        * doesn't exist this runs for a *very* long time.
+        */
+       for (start = trv + 1;; --trv) {
+               if (trv <= path)
+                       break;
+               if (*trv == '/') {
+                       *trv = '\0';
+                       if (stat(path, &sbuf))
+                               return(0);
+                       if (!S_ISDIR(sbuf.st_mode)) {
+                               errno = ENOTDIR;
+                               return(0);
+                       }
+                       *trv = '/';
+                       break;
+               }
+       }
+
+       for (;;) {
+               if (doopen) {
+                       if ((*doopen =
+                           open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+                               return(1);
+                       if (errno != EEXIST)
+                               return(0);
+               }
+               else if (stat(path, &sbuf))
+                       return(errno == ENOENT ? 1 : 0);
+
+               /* tricky little algorithm for backward compatibility */
+               for (trv = start;;) {
+                       if (!*trv)
+                               return(0);
+                       if (*trv == 'z')
+                               *trv++ = 'a';
+                       else {
+                               if (isdigit(*trv))
+                                       *trv = 'a';
+                               else
+                                       ++*trv;
+                               break;
+                       }
+               }
+       }
+       /*NOTREACHED*/
+}
diff --git a/usr.bin/vi/clib/pty.c b/usr.bin/vi/clib/pty.c
new file mode 100644 (file)
index 0000000..98001b1
--- /dev/null
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)pty.c      8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+
+openpty(amaster, aslave, name, termp, winp)
+       int *amaster, *aslave;
+       char *name;
+       struct termios *termp;
+       struct winsize *winp;
+{
+       static char line[] = "/dev/ptyXX";
+       register const char *cp1, *cp2;
+       register int master, slave, ttygid;
+       struct group *gr;
+
+       if ((gr = getgrnam("tty")) != NULL)
+               ttygid = gr->gr_gid;
+       else
+               ttygid = -1;
+
+       for (cp1 = "pqrs"; *cp1; cp1++) {
+               line[8] = *cp1;
+               for (cp2 = "0123456789abcdef"; *cp2; cp2++) {
+                       line[9] = *cp2;
+                       if ((master = open(line, O_RDWR, 0)) == -1) {
+                               if (errno == ENOENT)
+                                       return (-1);    /* out of ptys */
+                       } else {
+                               line[5] = 't';
+                               (void) chown(line, getuid(), ttygid);
+                               (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+#ifdef ONLY_44BSD_HAS_REVOKE
+                               (void) revoke(line);
+#endif
+                               if ((slave = open(line, O_RDWR, 0)) != -1) {
+                                       *amaster = master;
+                                       *aslave = slave;
+                                       if (name)
+                                               strcpy(name, line);
+                                       if (termp)
+                                               (void) tcsetattr(slave, 
+                                                       TCSAFLUSH, termp);
+                                       if (winp)
+                                               (void) ioctl(slave, TIOCSWINSZ, 
+                                                       (char *)winp);
+                                       return (0);
+                               }
+                               (void) close(master);
+                               line[5] = 'p';
+                       }
+               }
+       }
+       errno = ENOENT; /* out of ptys */
+       return (-1);
+}
diff --git a/usr.bin/vi/clib/pty_s5r4.c b/usr.bin/vi/clib/pty_s5r4.c
new file mode 100644 (file)
index 0000000..7fc83fd
--- /dev/null
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Brian Hirt.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pty_s5r4.c 8.1 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/stropts.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+
+/*
+ * ptym_open --
+ *     This function opens a master pty and returns the file descriptor
+ *     to it.  pts_name is also returned which is the name of the slave.
+ */
+static int
+ptym_open(pts_name)
+       char *pts_name;
+{
+       int fdm;
+       char *ptr;
+
+       strcpy(pts_name,"/dev/ptmx");
+       if ( (fdm = open(pts_name,O_RDWR)) < 0 )
+               return(-1);
+
+       if (grantpt(fdm) < 0) 
+       {
+               close(fdm);
+               return(-2);
+       }
+
+       if (unlockpt(fdm) < 0) 
+       {
+               close(fdm);
+               return(-3);
+       }
+
+       if (unlockpt(fdm) < 0) 
+       {
+               close(fdm);
+               return(-3);
+       }
+
+       /* get slave's name */
+       if ( (ptr = ptsname(fdm)) == NULL) 
+       {
+               close(fdm);
+               return(-3);
+       }
+       strcpy(pts_name,ptr);
+       return(fdm);
+}
+
+/*
+ * ptys_open --
+ *     This function opens the slave pty.
+ */
+static int
+ptys_open(fdm, pts_name)
+       int fdm;
+       char *pts_name;
+{
+       int fds;
+
+       if ( (fds = open(pts_name, O_RDWR)) < 0) 
+       {
+               close(fdm);
+               return(-5);
+       }
+
+       if (ioctl(fds, I_PUSH, "ptem") < 0) 
+       {
+               close(fds);
+               close(fdm);
+               return(-6);
+       }
+
+       if (ioctl(fds, I_PUSH, "ldterm") < 0) 
+       {
+               close(fds);
+               close(fdm);
+               return(-7);
+       }
+
+       if (ioctl(fds, I_PUSH, "ttcompat") < 0) 
+       {
+               close(fds);
+               close(fdm);
+               return(-8);
+       }
+
+       return(fds);
+}
+
+int
+openpty(amaster, aslave, name, termp, winp)
+       int *amaster, *aslave;
+       char *name;
+       struct termios *termp;
+{
+       register int master, slave, ttygid;
+
+       /* open master terminal */
+       if ((master = ptym_open(name)) < 0)  
+       {
+               errno = ENOENT; /* out of ptys */
+               return(-1);
+       }
+
+       /* open slave terminal */
+       if ((slave = ptys_open(master, name)) >= 0) 
+       {
+               *amaster = master;
+               *aslave = slave;
+       } 
+       else 
+       {
+               errno = ENOENT; /* out of ptys */
+               return(-1);
+       }
+
+       if (termp)
+               (void) tcsetattr(slave, TCSAFLUSH, termp);
+       if (winp)
+               (void) ioctl(slave, TIOCSWINSZ, (char *)winp);
+
+       return (0);
+}
diff --git a/usr.bin/vi/clib/realloc.c b/usr.bin/vi/clib/realloc.c
new file mode 100644 (file)
index 0000000..11e5cd2
--- /dev/null
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+void *
+__fix_realloc(p, n)
+       void *p;
+       size_t n;
+{
+       return (p == 0 ? malloc(n) : realloc(p, n));
+}
diff --git a/usr.bin/vi/clib/siglist.c b/usr.bin/vi/clib/siglist.c
new file mode 100644 (file)
index 0000000..be6334d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1983, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)siglist.c  8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+#include <signal.h>
+
+const char *const sys_signame[NSIG] = {
+       "Signal 0",
+       "hup",                          /* SIGHUP */
+       "int",                          /* SIGINT */
+       "quit",                         /* SIGQUIT */
+       "ill",                          /* SIGILL */
+       "trap",                         /* SIGTRAP */
+       "abrt",                         /* SIGABRT */
+       "emt",                          /* SIGEMT */
+       "fpe",                          /* SIGFPE */
+       "kill",                         /* SIGKILL */
+       "bus",                          /* SIGBUS */
+       "segv",                         /* SIGSEGV */
+       "sys",                          /* SIGSYS */
+       "pipe",                         /* SIGPIPE */
+       "alrm",                         /* SIGALRM */
+       "term",                         /* SIGTERM */
+       "urg",                          /* SIGURG */
+       "stop",                         /* SIGSTOP */
+       "tstp",                         /* SIGTSTP */
+       "cont",                         /* SIGCONT */
+       "chld",                         /* SIGCHLD */
+       "ttin",                         /* SIGTTIN */
+       "ttou",                         /* SIGTTOU */
+       "io",                           /* SIGIO */
+       "xcpu",                         /* SIGXCPU */
+       "xfsz",                         /* SIGXFSZ */
+       "vtalrm",                       /* SIGVTALRM */
+       "prof",                         /* SIGPROF */
+       "winch",                        /* SIGWINCH */
+       "info",                         /* SIGINFO */
+       "usr1",                         /* SIGUSR1 */
+       "usr2",                         /* SIGUSR2 */
+};
+
+const char *const sys_siglist[NSIG] = {
+       "Signal 0",
+       "Hangup",                       /* SIGHUP */
+       "Interrupt",                    /* SIGINT */
+       "Quit",                         /* SIGQUIT */
+       "Illegal instruction",          /* SIGILL */
+       "Trace/BPT trap",               /* SIGTRAP */
+       "Abort trap",                   /* SIGABRT */
+       "EMT trap",                     /* SIGEMT */
+       "Floating point exception",     /* SIGFPE */
+       "Killed",                       /* SIGKILL */
+       "Bus error",                    /* SIGBUS */
+       "Segmentation fault",           /* SIGSEGV */
+       "Bad system call",              /* SIGSYS */
+       "Broken pipe",                  /* SIGPIPE */
+       "Alarm clock",                  /* SIGALRM */
+       "Terminated",                   /* SIGTERM */
+       "Urgent I/O condition",         /* SIGURG */
+       "Suspended (signal)",           /* SIGSTOP */
+       "Suspended",                    /* SIGTSTP */
+       "Continued",                    /* SIGCONT */
+       "Child exited",                 /* SIGCHLD */
+       "Stopped (tty input)",          /* SIGTTIN */
+       "Stopped (tty output)",         /* SIGTTOU */
+       "I/O possible",                 /* SIGIO */
+       "Cputime limit exceeded",       /* SIGXCPU */
+       "Filesize limit exceeded",      /* SIGXFSZ */
+       "Virtual timer expired",        /* SIGVTALRM */
+       "Profiling timer expired",      /* SIGPROF */
+       "Window size changes",          /* SIGWINCH */
+       "Information request",          /* SIGINFO */
+       "User defined signal 1",        /* SIGUSR1 */
+       "User defined signal 2"         /* SIGUSR2 */
+};
diff --git a/usr.bin/vi/clib/snprintf.c b/usr.bin/vi/clib/snprintf.c
new file mode 100644 (file)
index 0000000..2863fa2
--- /dev/null
@@ -0,0 +1,54 @@
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <compat.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+int
+#ifdef __STDC__
+snprintf(char *str, size_t n, const char *fmt, ...)
+#else
+snprintf(str, n, fmt, va_alist)
+       char *str;
+       size_t n;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+       char *rp;
+       int rval;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+#ifdef VSPRINTF_CHARSTAR
+       rp = vsprintf(str, fmt, ap);
+       va_end(ap);
+       return (strlen(rp));
+#else
+       rval = vsprintf(str, fmt, ap);
+       va_end(ap);
+       return (rval);
+#endif
+}
+
+int
+vsnprintf(str, n, fmt, ap)
+       char *str;
+       size_t n;
+       const char *fmt;
+       va_list ap;
+{
+#ifdef VSPRINTF_CHARSTAR
+       return (strlen(vsprintf(str, fmt, ap)));
+#else
+       return (vsprintf(str, fmt, ap));
+#endif
+}
diff --git a/usr.bin/vi/clib/strdup.c b/usr.bin/vi/clib/strdup.c
new file mode 100644 (file)
index 0000000..6fa50ce
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1988, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strdup.c   8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+strdup(str)
+       const char *str;
+{
+       size_t len;
+       char *copy;
+
+       len = strlen(str) + 1;
+       if (!(copy = malloc((u_int)len)))
+               return (NULL);
+       bcopy(str, copy, len);
+       return (copy);
+}
diff --git a/usr.bin/vi/clib/strerror.c b/usr.bin/vi/clib/strerror.c
new file mode 100644 (file)
index 0000000..53f374b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1988, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+char *
+strerror(num)
+       int num;
+{
+       extern int sys_nerr;
+       extern char *sys_errlist[];
+#define        UPREFIX "Unknown error: "
+       static char ebuf[40] = UPREFIX;         /* 64-bit number + slop */
+       register unsigned int errnum;
+       register char *p, *t;
+       char tmp[40];
+
+       errnum = num;                           /* convert to unsigned */
+       if (errnum < sys_nerr)
+               return(sys_errlist[errnum]);
+
+       /* Do this by hand, so we don't include stdio(3). */
+       t = tmp;
+       do {
+               *t++ = "0123456789"[errnum % 10];
+       } while (errnum /= 10);
+       for (p = ebuf + sizeof(UPREFIX) - 1;;) {
+               *p++ = *--t;
+               if (t <= tmp)
+                       break;
+       }
+       return(ebuf);
+}
diff --git a/usr.bin/vi/clib/strsep.c b/usr.bin/vi/clib/strsep.c
new file mode 100644 (file)
index 0000000..18dc398
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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 <sys/cdefs.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strsep.c   8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.  
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(stringp, delim)
+       register char **stringp;
+       register const char *delim;
+{
+       register char *s;
+       register const char *spanp;
+       register int c, sc;
+       char *tok;
+
+       if ((s = *stringp) == NULL)
+               return (NULL);
+       for (tok = s;;) {
+               c = *s++;
+               spanp = delim;
+               do {
+                       if ((sc = *spanp++) == c) {
+                               if (c == 0)
+                                       s = NULL;
+                               else
+                                       s[-1] = 0;
+                               *stringp = s;
+                               return (tok);
+                       }
+               } while (sc != 0);
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/clib/strtoul.c b/usr.bin/vi/clib/strtoul.c
new file mode 100644 (file)
index 0000000..eed7cdc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1990, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtoul.c  8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <sys/cdefs.h>
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+unsigned long
+strtoul(nptr, endptr, base)
+       const char *nptr;
+       char **endptr;
+       register int base;
+{
+       register const char *s = nptr;
+       register unsigned long acc;
+       register int c;
+       register unsigned long cutoff;
+       register int neg = 0, any, cutlim;
+
+       /*
+        * See strtol for comments as to the logic used.
+        */
+       do {
+               c = *s++;
+       } while (isspace(c));
+       if (c == '-') {
+               neg = 1;
+               c = *s++;
+       } else if (c == '+')
+               c = *s++;
+       if ((base == 0 || base == 16) &&
+           c == '0' && (*s == 'x' || *s == 'X')) {
+               c = s[1];
+               s += 2;
+               base = 16;
+       }
+       if (base == 0)
+               base = c == '0' ? 8 : 10;
+       cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+       cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
+       for (acc = 0, any = 0;; c = *s++) {
+               if (isdigit(c))
+                       c -= '0';
+               else if (isalpha(c))
+                       c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+               else
+                       break;
+               if (c >= base)
+                       break;
+               if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+                       any = -1;
+               else {
+                       any = 1;
+                       acc *= base;
+                       acc += c;
+               }
+       }
+       if (any < 0) {
+               acc = ULONG_MAX;
+               errno = ERANGE;
+       } else if (neg)
+               acc = -acc;
+       if (endptr != 0)
+               *endptr = (char *)(any ? s - 1 : nptr);
+       return (acc);
+}
diff --git a/usr.bin/vi/cut.c b/usr.bin/vi/cut.c
new file mode 100644 (file)
index 0000000..1638296
--- /dev/null
@@ -0,0 +1,525 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cut.c      8.19 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+static int     cb_line __P((SCR *, EXF *, recno_t, size_t, size_t, TEXT **));
+static int     cb_rotate __P((SCR *));
+
+/* 
+ * cut --
+ *     Put a range of lines/columns into a buffer.
+ *
+ * There are two buffer areas, both found in the global structure.  The first
+ * is the linked list of all the buffers the user has named, the second is the
+ * default buffer storage.  There is a pointer, too, which is the current
+ * default buffer, i.e. it may point to the default buffer or a named buffer
+ * depending on into what buffer the last text was cut.  In both delete and
+ * yank operations, text is cut into either the buffer named by the user, or
+ * the default buffer.  If it's a delete of information on more than a single
+ * line, the contents of the numbered buffers are rotated up one, the contents
+ * of the buffer named '9' are discarded, and the text is also cut into the
+ * buffer named '1'.
+ *
+ * In all cases, upper-case buffer names are the same as lower-case names,
+ * with the exception that they cause the buffer to be appended to instead
+ * of replaced.
+ *
+ * !!!
+ * The contents of the default buffer would disappear after most operations in
+ * historic vi.  It's unclear that this is useful, so we don't bother.
+ *
+ * When users explicitly cut text into the numeric buffers, historic vi became
+ * genuinely strange.  I've never been able to figure out what was supposed to
+ * happen.  It behaved differently if you deleted text than if you yanked text,
+ * and, in the latter case, the text was appended to the buffer instead of
+ * replacing the contents.  Hopefully it's not worth getting right.
+ */
+int
+cut(sp, ep, cbp, namep, fm, tm, flags)
+       SCR *sp;
+       EXF *ep;
+       CB *cbp;
+       CHAR_T *namep;
+       int flags;
+       MARK *fm, *tm;
+{
+       CHAR_T name;
+       TEXT *tp;
+       recno_t lno;
+       size_t len;
+       int append, namedbuffer, setdefcb;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "cut: from {%lu, %d}, to {%lu, %d}%s\n",
+           fm->lno, fm->cno, tm->lno, tm->cno,
+           LF_ISSET(CUT_LINEMODE) ? " LINE MODE" : "");
+#endif
+       if (cbp == NULL) {
+               if (LF_ISSET(CUT_DELETE) && fm->lno != tm->lno) {
+                       (void)cb_rotate(sp);
+                       name = '1';
+                       goto defcb;
+               }
+               if (namep == NULL) {
+                       cbp = sp->gp->dcb_store;
+                       append = namedbuffer = 0;
+                       setdefcb = 1;
+               } else {
+                       name = *namep;
+defcb:                 CBNAME(sp, cbp, name);
+                       append = isupper(name);
+                       namedbuffer = setdefcb = 1;
+               }
+       } else
+               append = namedbuffer = setdefcb = 0;
+
+       /*
+        * If this is a new buffer, create it and add it into the list.
+        * Otherwise, if it's not an append, free its current contents.
+        */
+       if (cbp == NULL) {
+               CALLOC(sp, cbp, CB *, 1, sizeof(CB));
+               cbp->name = name;
+               CIRCLEQ_INIT(&cbp->textq);
+               if (namedbuffer) {
+                       LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
+               } else
+                       sp->gp->dcb_store = cbp;
+       } else if (!append) {
+               text_lfree(&cbp->textq);
+               cbp->len = 0;
+               cbp->flags = 0;
+       }
+
+       /* In line mode, it's pretty easy, just cut the lines. */
+       if (LF_ISSET(CUT_LINEMODE)) {
+               for (lno = fm->lno; lno <= tm->lno; ++lno) {
+                       if (cb_line(sp, ep, lno, 0, 0, &tp))
+                               goto mem;
+                       CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+                       cbp->len += tp->len;
+               }
+               cbp->flags |= CB_LMODE;
+       } else {
+               /* Get the first line. */
+               len = fm->lno < tm->lno ? 0 : tm->cno - fm->cno;
+               if (cb_line(sp, ep, fm->lno, fm->cno, len, &tp))
+                       goto mem;
+               CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+               cbp->len += tp->len;
+
+               /* Get the intermediate lines. */
+               for (lno = fm->lno; ++lno < tm->lno;) {
+                       if (cb_line(sp, ep, lno, 0, 0, &tp))
+                               goto mem;
+                       CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+                       cbp->len += tp->len;
+               }
+
+               /* Get the last line. */
+               if (tm->lno > fm->lno && tm->cno > 0) {
+                       if (cb_line(sp, ep, lno, 0, tm->cno, &tp)) {
+mem:                           if (append)
+                                       msgq(sp, M_ERR,
+                                           "Contents of %s buffer lost.",
+                                           charname(sp, name));
+                               text_lfree(&cbp->textq);
+                               cbp->len = 0;
+                               cbp->flags = 0;
+                               return (1);
+                       }
+                       CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+                       cbp->len += tp->len;
+               }
+       }
+       if (setdefcb)
+               sp->gp->dcbp = cbp;     /* Repoint default buffer. */
+       return (0);
+}
+
+/*
+ * cb_rotate --
+ *     Rotate the numbered buffers up one.
+ */
+static int
+cb_rotate(sp)
+       SCR *sp;
+{
+       CB *cbp, *del_cbp;
+
+       del_cbp = NULL;
+       for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next)
+               switch(cbp->name) {
+               case '1':
+                       cbp->name = '2';
+                       break;
+               case '2':
+                       cbp->name = '3';
+                       break;
+               case '3':
+                       cbp->name = '4';
+                       break;
+               case '4':
+                       cbp->name = '5';
+                       break;
+               case '5':
+                       cbp->name = '6';
+                       break;
+               case '6':
+                       cbp->name = '7';
+                       break;
+               case '7':
+                       cbp->name = '8';
+                       break;
+               case '8':
+                       cbp->name = '9';
+                       break;
+               case '9':
+                       del_cbp = cbp;
+                       break;
+               }
+       if (del_cbp != NULL) {
+               LIST_REMOVE(del_cbp, q);
+               text_lfree(&del_cbp->textq);
+               FREE(del_cbp, sizeof(CB));
+       }
+       return (0);
+}
+
+/*
+ * cb_line --
+ *     Cut a portion of a single line.
+ */
+static int
+cb_line(sp, ep, lno, fcno, clen, newp)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       size_t fcno, clen;
+       TEXT **newp;
+{
+       TEXT *tp;
+       size_t len;
+       char *p;
+
+       if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+               GETLINE_ERR(sp, lno);
+               return (1);
+       }
+
+       if ((*newp = tp = text_init(sp, NULL, 0, len)) == NULL)
+               return (1);
+
+       /*
+        * A length of zero means to cut from the MARK to the end
+        * of the line.
+        */
+       if (len != 0) {
+               if (clen == 0)
+                       clen = len - fcno;
+               memmove(tp->lb, p + fcno, clen);
+               tp->len = clen;
+       }
+       return (0);
+}
+
+/*
+ * text_init --
+ *     Allocate a new TEXT structure.
+ */
+TEXT *
+text_init(sp, p, len, total_len)
+       SCR *sp;
+       const char *p;
+       size_t len, total_len;
+{
+       TEXT *tp;
+
+       MALLOC(sp, tp, TEXT *, sizeof(TEXT));
+       if (tp == NULL)
+               return (NULL);
+       /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */
+       if (tp->lb_len = total_len) {
+               MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
+               if (tp->lb == NULL) {
+                       free(tp);
+                       return (NULL);
+               }
+               if (p != NULL && len != 0)
+                       memmove(tp->lb, p, len);
+       } else
+               tp->lb = NULL;
+       tp->len = len;
+       tp->ai = tp->insert = tp->offset = tp->owrite = 0;
+       tp->wd = NULL;
+       tp->wd_len = 0;
+       return (tp);
+}
+
+/*
+ * text_lfree --
+ *     Free a chain of text structures.
+ */
+void
+text_lfree(headp)
+       TEXTH *headp;
+{
+       TEXT *tp;
+
+       while ((tp = headp->cqh_first) != (void *)headp) {
+               CIRCLEQ_REMOVE(headp, tp, q);
+               text_free(tp);
+       }
+}
+
+/*
+ * text_free --
+ *     Free a text structure.
+ */
+void
+text_free(tp)
+       TEXT *tp;
+{
+       if (tp->lb != NULL)
+               FREE(tp->lb, tp->lb_len);
+       if (tp->wd != NULL)
+               FREE(tp->wd, tp->wd_len);
+       FREE(tp, sizeof(TEXT));
+}
+
+/*
+ * put --
+ *     Put text buffer contents into the file.
+ *
+ * !!!
+ * Historically, pasting into a file with no lines in vi would preserve
+ * the single blank line.  This is almost certainly a result of the fact
+ * that 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.
+ */    
+int
+put(sp, ep, cbp, namep, cp, rp, append)
+       SCR *sp;
+       EXF *ep;
+       CB *cbp;
+       CHAR_T *namep;
+       MARK *cp, *rp;
+       int append;
+{
+       CHAR_T name;
+       TEXT *ltp, *tp;
+       recno_t lno;
+       size_t blen, clen, len;
+       char *bp, *p, *t;
+
+       if (cbp == NULL)
+               if (namep == NULL) {
+                       cbp = sp->gp->dcbp;
+                       if (cbp == NULL) {
+                               msgq(sp, M_ERR, "The default buffer is empty.");
+                               return (1);
+                       }
+               } else {
+                       name = *namep;
+                       CBNAME(sp, cbp, name);
+                       if (cbp == NULL) {
+                               msgq(sp, M_ERR,
+                                   "Buffer %s is empty.", charname(sp, name));
+                               return (1);
+                       }
+               }
+       tp = cbp->textq.cqh_first;
+
+       /*
+        * 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.
+        *
+        * Historical practice is that the cursor ends up on the first
+        * non-blank character of the first line inserted.
+        */
+       if (cp->lno == 1) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0) {
+                       for (; tp != (void *)&cbp->textq;
+                            ++lno, tp = tp->q.cqe_next)
+                               if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+                                       return (1);
+                       rp->lno = 1;
+                       rp->cno = 0;
+                       (void)nonblank(sp, ep, rp->lno, &rp->cno);
+                       goto ret;
+               }
+       }
+                       
+       /* 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 != (void *)&cbp->textq; ++lno, tp = tp->q.cqe_next)
+                       if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+                               return (1);
+               rp->cno = 0;
+               (void)nonblank(sp, ep, rp->lno, &rp->cno);
+               goto ret;
+       }
+
+       /*
+        * 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 ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+               GETLINE_ERR(sp, lno);
+               return (1);
+       }
+
+       GET_SPACE_RET(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) {
+               memmove(bp, p, clen);
+               p += clen;
+               t += clen;
+       }
+
+       /* First line from the CB. */
+       memmove(t, tp->lb, tp->len);
+       t += tp->len;
+
+       /* Calculate length left in original line. */
+       clen = len ? len - cp->cno - (append ? 1 : 0) : 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 (tp->q.cqe_next == (void *)&cbp->textq) {
+               /*
+                * Historical practice is that if a non-line mode put
+                * is inside a single line, the cursor ends up on the
+                * last character inserted.
+                */
+               rp->lno = lno;
+               rp->cno = (t - bp) - 1;
+
+               if (clen > 0) {
+                       memmove(t, p, clen);
+                       t += clen;
+               }
+               if (file_sline(sp, ep, lno, bp, t - bp))
+                       goto mem;
+       } 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 = cbp->textq.cqh_last;
+               len = t - bp;
+               ADD_SPACE_RET(sp, bp, blen, ltp->len + clen);
+               t = bp + len;
+
+               /* Add in last part of the CB. */
+               memmove(t, ltp->lb, ltp->len);
+               if (clen)
+                       memmove(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 (file_sline(sp, ep, lno, bp, t - bp))
+                       goto mem;
+
+               /*
+                * Historical practice is that if a non-line mode put
+                * covers multiple lines, the cursor ends up on the
+                * first character inserted.  (Of course.)
+                */
+               rp->lno = lno;
+               rp->cno = (t - bp) - 1;
+
+               /* Output any intermediate lines in the CB. */
+               for (tp = tp->q.cqe_next;
+                   tp->q.cqe_next != (void *)&cbp->textq;
+                   ++lno, tp = tp->q.cqe_next)
+                       if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+                               goto mem;
+
+               if (file_aline(sp, ep, 1, lno, t, clen)) {
+mem:                   FREE_SPACE(sp, bp, blen);
+                       return (1);
+               }
+       }
+       FREE_SPACE(sp, bp, blen);
+
+       /* Reporting... */
+ret:   sp->rptlines[L_PUT] += lno - cp->lno;
+
+       return (0);
+}
diff --git a/usr.bin/vi/cut.h b/usr.bin/vi/cut.h
new file mode 100644 (file)
index 0000000..8ade88f
--- /dev/null
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)cut.h       8.11 (Berkeley) 1/11/94
+ */
+
+typedef struct _texth TEXTH;           /* TEXT list head structure. */
+CIRCLEQ_HEAD(_texth, _text);
+
+/* Cut buffers. */
+struct _cb {
+       LIST_ENTRY(_cb) q;              /* Linked list of cut buffers. */
+       TEXTH    textq;                 /* Linked list of TEXT structures. */
+       CHAR_T   name;                  /* Cut buffer name. */
+       size_t   len;                   /* Total length of cut text. */
+
+#define        CB_LMODE        0x01            /* Cut was in line mode. */
+       u_char   flags;
+};
+               
+/* Lines/blocks of text. */
+struct _text {                         /* Text: a linked list of lines. */
+       CIRCLEQ_ENTRY(_text) q;         /* Linked list of text structures. */
+       char    *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: line number. */
+       size_t   ai;                    /* 0-N: autoindent bytes. */
+       size_t   insert;                /* 0-N: bytes to insert (push). */
+       size_t   offset;                /* 0-N: initial, unerasable bytes. */
+       size_t   owrite;                /* 0-N: bytes to overwrite. */
+
+       /* These fields are used by the ex text input routine. */
+       u_char  *wd;                    /* Width buffer. */
+       size_t   wd_len;                /* Width buffer length. */
+};
+
+/*
+ * Get named buffer 'name'.
+ * Translate upper-case buffer names to lower-case buffer names.
+ */
+#define        CBNAME(sp, cbp, name) {                                         \
+       CHAR_T __name;                                                  \
+       __name = isupper(name) ? tolower(name) : (name);                \
+       for (cbp = sp->gp->cutq.lh_first;                               \
+           cbp != NULL; cbp = cbp->q.le_next)                          \
+               if (cbp->name == __name)                                \
+                       break;                                          \
+}
+
+#define        CUT_DELETE      0x01            /* Delete (rotate numeric buffers). */
+#define        CUT_LINEMODE    0x02            /* Cut in line mode. */
+int     cut __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
+int     delete __P((SCR *, EXF *, MARK *, MARK *, int));
+int     put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
+
+void    text_free __P((TEXT *));
+TEXT   *text_init __P((SCR *, const char *, size_t, size_t));
+void    text_lfree __P((TEXTH *));
diff --git a/usr.bin/vi/delete.c b/usr.bin/vi/delete.c
new file mode 100644 (file)
index 0000000..e8c0a5d
--- /dev/null
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)delete.c   8.7 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+/*
+ * delete --
+ *     Delete a range of text.
+ */
+int
+delete(sp, ep, fm, tm, lmode)
+       SCR *sp;
+       EXF *ep;
+       MARK *fm, *tm;
+       int lmode;
+{
+       recno_t lno;
+       size_t blen, len, tlen;
+       char *bp, *p;
+       int eof;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "delete: from %lu/%d to %lu/%d%s\n",
+           fm->lno, fm->cno, tm->lno, tm->cno, lmode ? " (LINE MODE)" : "");
+#endif
+       bp = NULL;
+
+       /* Case 1 -- delete in line mode. */
+       if (lmode) {
+               for (lno = tm->lno; lno >= fm->lno; --lno)
+                       if (file_dline(sp, ep, lno))
+                               return (1);
+               goto vdone;
+       }
+
+       /*
+        * Case 2 -- delete to EOF.  This is a special case because it's
+        * easier to pick it off than try and find it in the other cases.
+        */
+       if (file_lline(sp, ep, &lno))
+               return (1);
+       if (tm->lno >= lno) {
+               if (tm->lno == lno) {
+                       if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+                               GETLINE_ERR(sp, lno);
+                               return (1);
+                       }
+                       eof = tm->cno >= len ? 1 : 0;
+               } else
+                       eof = 1;
+               if (eof) {
+                       for (lno = tm->lno; lno > fm->lno; --lno) {
+                               if (file_dline(sp, ep, lno))
+                                       return (1);
+                               ++sp->rptlines[L_DELETED];
+                       }
+                       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                               GETLINE_ERR(sp, fm->lno);
+                               return (1);
+                       }
+                       GET_SPACE_RET(sp, bp, blen, fm->cno);
+                       memmove(bp, p, fm->cno);
+                       if (file_sline(sp, ep, fm->lno, bp, fm->cno))
+                               return (1);
+                       goto done;
+               }
+       }
+
+       /* Case 3 -- delete within a single line. */
+       if (tm->lno == fm->lno) {
+               if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               GET_SPACE_RET(sp, bp, blen, len);
+               memmove(bp, p, fm->cno);
+               memmove(bp + fm->cno, p + tm->cno, len - tm->cno);
+               if (file_sline(sp, ep, fm->lno, bp, len - (tm->cno - fm->cno)))
+                       goto err;
+               goto done;
+       }
+
+       /*
+        * Case 4 -- delete over multiple lines.
+        *
+        * Figure out how big a buffer we need.
+        */
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+       tlen = len;
+       if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
+               GETLINE_ERR(sp, tm->lno);
+               return (1);
+       }
+
+       /*
+        * XXX
+        * We can overflow memory here, if (len + tlen) > SIZE_T_MAX.  The
+        * only portable way I've found to test is to depend on the overflow
+        * being less than the value.
+        */
+       tlen += len;
+       if (len > tlen) {
+               msgq(sp, M_ERR, "Error: line length overflow");
+               return (1);
+       }
+
+       GET_SPACE_RET(sp, bp, blen, tlen);
+
+       /* Copy the start partial line into place. */
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               GETLINE_ERR(sp, fm->lno);
+               goto err;
+       }
+       memmove(bp, p, fm->cno);
+       tlen = fm->cno;
+
+       /* Copy the end partial line into place. */
+       if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
+               GETLINE_ERR(sp, tm->lno);
+               goto err;
+       }
+       memmove(bp + tlen, p + tm->cno, len - tm->cno);
+       tlen += len - tm->cno;
+
+       /* Set the current line. */
+       if (file_sline(sp, ep, fm->lno, bp, tlen))
+               goto err;
+
+       /* Delete the last and intermediate lines. */
+       for (lno = tm->lno; lno > fm->lno; --lno)
+               if (file_dline(sp, ep, lno))
+                       return (1);
+
+       /* Reporting. */
+vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1;
+
+done:  if (bp != NULL)
+               FREE_SPACE(sp, bp, blen);
+
+       return (0);
+
+       /* Free memory. */
+err:   FREE_SPACE(sp, bp, blen);
+
+       return (1);
+}
diff --git a/usr.bin/vi/exf.c b/usr.bin/vi/exf.c
new file mode 100644 (file)
index 0000000..0f9ac4b
--- /dev/null
@@ -0,0 +1,690 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)exf.c      8.65 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+/*
+ * We include <sys/file.h>, because the flock(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 <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+/*
+ * 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.
+ */
+FREF *
+file_add(sp, frp_append, name, ignore)
+       SCR *sp;
+       FREF *frp_append;
+       CHAR_T *name;
+       int ignore;
+{
+       FREF *frp;
+       char *p;
+
+       /*
+        * Return it if it already exists.  Note that we test against the
+        * user's current name, whatever that happens to be, including if
+        * it's a temporary file.  If the user is trying to set an argument
+        * list, the ignore argument will be on -- if we're ignoring the
+        * file turn off the ignore bit, so it's back in the argument list.
+        */
+       if (name != NULL)
+               for (frp = sp->frefq.cqh_first;
+                   frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+                       if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) {
+                               if (!ignore)
+                                       F_CLR(frp, FR_IGNORE);
+                               return (frp);
+                       }
+
+       /* Allocate and initialize the FREF structure. */
+       CALLOC(sp, frp, FREF *, 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.
+        */
+#define        TEMPORARY_FILE_STRING   "/tmp"
+       if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
+           (frp->name = strdup(name)) == NULL) {
+               FREE(frp, sizeof(FREF));
+               msgq(sp, M_SYSERR, NULL);
+               return (NULL);
+       }
+
+       /* Only the initial argument list is "remembered". */
+       if (ignore)
+               F_SET(frp, FR_IGNORE);
+
+       /* Append into the chain of file names. */
+       if (frp_append != NULL) {
+               CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q);
+       } else
+               CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q);
+
+       return (frp);
+}
+
+/*
+ * file_first --
+ *     Return the first file name for editing, if any.
+ */
+FREF *
+file_first(sp)
+       SCR *sp;
+{
+       FREF *frp;
+
+       /* Return the first file name. */
+       for (frp = sp->frefq.cqh_first;
+           frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+               if (!F_ISSET(frp, FR_IGNORE))
+                       return (frp);
+       return (NULL);
+}
+
+/*
+ * file_next --
+ *     Return the next file name, if any.
+ */
+FREF *
+file_next(sp, frp)
+       SCR *sp;
+       FREF *frp;
+{
+       while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq)
+               if (!F_ISSET(frp, FR_IGNORE))
+                       return (frp);
+       return (NULL);
+}
+
+/*
+ * file_prev --
+ *     Return the previous file name, if any.
+ */
+FREF *
+file_prev(sp, frp)
+       SCR *sp;
+       FREF *frp;
+{
+       while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq)
+               if (!F_ISSET(frp, FR_IGNORE))
+                       return (frp);
+       return (NULL);
+}
+
+/*
+ * file_unedited --
+ *     Return if there are files that aren't ignored and are unedited.
+ */
+FREF *
+file_unedited(sp)
+       SCR *sp;
+{
+       FREF *frp;
+
+       /* Return the next file name. */
+       for (frp = sp->frefq.cqh_first;
+           frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+               if (!F_ISSET(frp, FR_EDITED | FR_IGNORE))
+                       return (frp);
+       return (NULL);
+}
+
+/*
+ * 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.
+ */
+int
+file_init(sp, frp, rcv_name, force)
+       SCR *sp;
+       FREF *frp;
+       char *rcv_name;
+       int force;
+{
+       EXF *ep;
+       RECNOINFO oinfo;
+       struct stat sb;
+       size_t psize;
+       int fd;
+       char *p, *oname, tname[MAXPATHLEN];
+
+       /*
+        * Required ep initialization:
+        *      Flush the line caches.
+        *      Default recover mail file fd to -1.
+        *      Set initial EXF flag bits.
+        */
+       CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
+       ep->c_lno = ep->c_nlines = OOBLNO;
+       ep->rcv_fd = -1;
+       LIST_INIT(&ep->marks);
+       F_SET(ep, F_FIRSTMODIFY);
+
+       /*
+        * If no name or backing file, create a backing temporary file, saving
+        * the temp file name so can later unlink it.  Repoint the name to the
+        * temporary name (we display it to the user until they rename it).
+        * There are some games we play with the FR_FREE_TNAME and FR_NONAME
+        * flags (see ex/ex_file.c) to make sure that the temporary memory gets
+        * free'd up.
+        */
+       if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) {
+               (void)snprintf(tname, sizeof(tname),
+                   "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
+               if ((fd = mkstemp(tname)) == -1) {
+                       msgq(sp, M_SYSERR, "Temporary file");
+                       goto err;
+               }
+               (void)close(fd);
+               if ((frp->tname = strdup(tname)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       (void)unlink(tname);
+                       goto err;
+               }
+               oname = frp->tname;
+               psize = 4 * 1024;
+               F_SET(frp, FR_NEWFILE);
+       } else {
+               /* Try to keep it at 10 pages or less per file. */
+               if (sb.st_size < 40 * 1024)
+                       psize = 4 * 1024;
+               else if (sb.st_size < 320 * 1024)
+                       psize = 32 * 1024;
+               else
+                       psize = 64 * 1024;
+
+               frp->mtime = sb.st_mtime;
+
+               if (!S_ISREG(sb.st_mode))
+                       msgq(sp, M_ERR,
+                           "Warning: %s is not a regular file.", oname);
+       }
+       
+       /* Set up recovery. */
+       memset(&oinfo, 0, sizeof(RECNOINFO));
+       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, FILENAME(frp)))
+                       msgq(sp, M_ERR,
+                   "Modifications not recoverable if the system crashes.");
+               else
+                       oinfo.bfname = ep->rcv_path;
+       } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               goto err;
+       } else {
+               oinfo.bfname = ep->rcv_path;
+               F_SET(ep, F_MODIFIED | F_RCV_ON);
+       }
+
+       /* Open a db structure. */
+       if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
+           O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
+               msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
+               goto err;
+       }
+
+       /* Init file marks. */
+       if (mark_init(sp, ep))
+               goto err;
+
+       /* Start logging. */
+       if (log_init(sp, ep))
+               goto err;
+
+       /*
+        * The -R flag, or doing a "set readonly" during a session causes
+        * all files edited during the session (using an edit command, or
+        * even using tags) to be marked read-only.  Changing the file name
+        * (see ex/ex_file.c), clears this flag.
+        *
+        * 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 (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
+           (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
+           access(FILENAME(frp), W_OK)))
+               F_SET(frp, FR_RDONLY);
+       else
+               F_CLR(frp, FR_RDONLY);
+
+       /*
+        * Close the previous file; if that fails, close the new one
+        * and run for the border.
+        */
+       if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
+               (void)file_end(sp, ep, 1);
+               goto err;
+       }
+
+       /*
+        * 4.4BSD supports locking in the open call, other systems don't.
+        * Since the user can't interrupt us between the open and here,
+        * it's a don't care.
+        *
+        * !!!
+        * We need to distinguish a lock not being available for the file
+        * from the file system not supporting locking.  Assume that EAGAIN
+        * or EWOULDBLOCK is the former.  There isn't a portable way to do
+        * this.
+        *
+        * XXX
+        * The locking is flock(2) style, not fcntl(2).  The latter is known
+        * to fail badly on some systems, and its only advantage is that it
+        * occasionally works over NFS.
+        */
+       if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB))
+               if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                       msgq(sp, M_INFO,
+                           "%s already locked, session is read-only", oname);
+                       F_SET(frp, FR_RDONLY);
+               } else
+                       msgq(sp, M_VINFO, "%s cannot be locked", oname);
+
+       /*
+        * Set the previous file pointer and the alternate file name to be
+        * the file we're about to discard.
+        *
+        * !!!
+        * If the current file was a temporary file, the call to file_end()
+        * unlinked it and free'd the name.  So, there is no previous file,
+        * and there is no alternate file name.  This matches historical
+        * practice, although in historical vi it could only happen as the
+        * result of the initial command, i.e. if vi was execute without a
+        * file name.
+        */
+       if (sp->frp != NULL) {
+               p = FILENAME(sp->frp);
+               if (p == NULL)
+                       sp->p_frp = NULL;
+               else
+                       sp->p_frp = sp->frp;
+               set_alt_name(sp, p);
+       }
+
+       /* The new file has now been officially edited. */
+       F_SET(frp, FR_EDITED);
+
+       /* Switch... */
+       ++ep->refcnt;
+       sp->ep = ep;
+       sp->frp = frp;
+       return (0);
+
+err:   if (frp->tname != NULL) {
+               (void)unlink(frp->tname);
+               free(frp->tname);
+               frp->tname = NULL;
+       }
+       if (ep->rcv_path != NULL) {
+               free(ep->rcv_path);
+               ep->rcv_path = NULL;
+       }
+       FREE(ep, sizeof(EXF));
+       return (1);
+}
+
+/*
+ * file_end --
+ *     Stop editing a file.
+ */
+int
+file_end(sp, ep, force)
+       SCR *sp;
+       EXF *ep;
+       int force;
+{
+       FREF *frp;
+
+       /*
+        *
+        * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
+        *
+        * 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);
+
+       /* If multiply referenced, just decrement the count and return. */
+       if (--ep->refcnt != 0)
+               return (0);
+
+       /* Close the db structure. */
+       if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
+               msgq(sp, M_ERR,
+                   "%s: close: %s", FILENAME(frp), strerror(errno));
+               ++ep->refcnt;
+               return (1);
+       }
+
+       /* COMMITTED TO THE CLOSE.  THERE'S NO GOING BACK... */
+
+       /* Stop logging. */
+       (void)log_end(sp, ep);
+
+       /* Free up any marks. */
+       mark_end(sp, ep);
+
+       /*
+        * Delete the recovery files, close the open descriptor,
+        * free recovery memory.
+        */
+       if (!F_ISSET(ep, F_RCV_NORM)) {
+               if (ep->rcv_path != NULL && unlink(ep->rcv_path))
+                       msgq(sp, M_ERR,
+                           "%s: remove: %s", ep->rcv_path, strerror(errno));
+               if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
+                       msgq(sp, M_ERR,
+                           "%s: remove: %s", ep->rcv_mpath, strerror(errno));
+       }
+       if (ep->rcv_fd != -1)
+               (void)close(ep->rcv_fd);
+       if (ep->rcv_path != NULL)
+               free(ep->rcv_path);
+       if (ep->rcv_mpath != NULL)
+               free(ep->rcv_mpath);
+
+       /*
+        * Unlink any temporary file, file name.  We also turn on the
+        * ignore bit at this point, because it was a "created" file,
+        * not an argument file.
+        */
+       if (frp->tname != NULL) {
+               if (unlink(frp->tname))
+                       msgq(sp, M_ERR,
+                           "%s: remove: %s", frp->tname, strerror(errno));
+               free(frp->tname);
+               frp->tname = NULL;
+
+               if (frp->name == NULL && frp->cname == NULL)
+                       F_SET(frp, FR_IGNORE);
+       }
+       /* Free the EXF structure. */
+       FREE(ep, sizeof(EXF));
+       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.
+ */
+int
+file_write(sp, ep, fm, tm, name, flags)
+       SCR *sp;
+       EXF *ep;
+       MARK *fm, *tm;
+       char *name;
+       int flags;
+{
+       struct stat sb;
+       FILE *fp;
+       FREF *frp;
+       MARK from, to;
+       u_long nlno, nch;
+       int fd, oflags, rval;
+       char *msg;
+
+       /*
+        * Don't permit writing to temporary files.  The problem is that
+        * if it's a temp file, and the user does ":wq", we write and quit,
+        * unlinking the temporary file.  Not what the user had in mind
+        * at all.  This test cannot be forced.
+        */
+       frp = sp->frp;
+       if (name == NULL && frp->cname == NULL && frp->name == NULL) {
+               msgq(sp, M_ERR, "No filename to which to write.");
+               return (1);
+       }
+
+       /* Can't write files marked read-only, unless forced. */
+       if (!LF_ISSET(FS_FORCE) &&
+           name == NULL && F_ISSET(frp, FR_RDONLY)) {
+               if (LF_ISSET(FS_POSSIBLE))
+                       msgq(sp, M_ERR,
+                           "Read-only file, not written; use ! to override.");
+               else
+                       msgq(sp, M_ERR,
+                           "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 (name != NULL) {
+                       if (!stat(name, &sb))
+                               goto exists;
+               } else if (frp->cname != NULL &&
+                   !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) {
+                       name = frp->cname;
+exists:                        if (LF_ISSET(FS_POSSIBLE))
+                               msgq(sp, M_ERR,
+               "%s exists, not written; use ! to override.", name);
+                       else
+                               msgq(sp, M_ERR,
+                                   "%s exists, not written.", name);
+                       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) && name == NULL &&
+                   frp->cname == NULL && !stat(frp->name, &sb)) {
+                       if (LF_ISSET(FS_POSSIBLE))
+                               msgq(sp, M_ERR,
+                                   "Use ! to write a partial file.");
+                       else
+                               msgq(sp, M_ERR, "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.
+        *
+        * If the user is overwriting a file other than the original file, and
+        * O_WRITEANY was what got us here (neither force nor append was set),
+        * display the "existing file" messsage.  Since the FR_CHANGEWRITE flag
+        * is set on a successful write, the message only appears once when the
+        * user changes a file name.  This is historic practice.
+        *
+        * One final test.  If we're not forcing or appending, and we have a
+        * saved modification time, stop the user if it's been written since
+        * we last edited or wrote it, and make them force it.
+        */
+       if (stat(name == NULL ? FILENAME(frp) : name, &sb))
+               msg = ": new file";
+       else {
+               msg = "";
+               if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
+                       if (frp->mtime && sb.st_mtime > frp->mtime) {
+                               msgq(sp, M_ERR,
+                       "%s: file modified more recently than this copy%s.",
+                                   name == NULL ? frp->name : name,
+                                   LF_ISSET(FS_POSSIBLE) ?
+                                   "; use ! to override" : "");
+                               return (1);
+                       }
+                       if (name != NULL ||
+                           !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL)
+                               msg = ": existing file";
+               }
+       }
+
+       /* We no longer care where the name came from. */
+       if (name == NULL)
+               name = FILENAME(frp);
+
+       /* Set flags to either append or truncate. */
+       oflags = O_CREAT | O_WRONLY;
+       if (LF_ISSET(FS_APPEND))
+               oflags |= O_APPEND;
+       else
+               oflags |= O_TRUNC;
+
+       /* Open the file. */
+       if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
+               msgq(sp, M_SYSERR, name);
+               return (1);
+       }
+
+       /* Use stdio for buffering. */
+       if ((fp = fdopen(fd, "w")) == NULL) {
+               (void)close(fd);
+               msgq(sp, M_SYSERR, name);
+               return (1);
+       }
+
+       /* Build fake addresses, if necessary. */
+       if (fm == NULL) {
+               from.lno = 1;
+               from.cno = 0;
+               fm = &from;
+               if (file_lline(sp, ep, &to.lno))
+                       return (1);
+               to.cno = 0;
+               tm = &to;
+       }
+
+       /* Write the file. */
+       rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch);
+
+       /*
+        * Save the new last modification time -- even if the write fails
+        * we re-init the time if we wrote anything.  That way the user can
+        * clean up the disk and rewrite without having to force it.
+        */
+       if (nlno || nch)
+               frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
+       
+       /* If the write failed, complain loudly. */
+       if (rval) {
+               if (!LF_ISSET(FS_APPEND))
+                       msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
+               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_SET(frp, FR_CHANGEWRITE);
+
+       /* If wrote the entire file, clear the modified bit. */
+       if (LF_ISSET(FS_ALL))
+               F_CLR(ep, F_MODIFIED);
+
+       msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.",
+           name, msg, nlno, nlno == 1 ? "" : "s", nch);
+
+       return (0);
+}
diff --git a/usr.bin/vi/exf.h b/usr.bin/vi/exf.h
new file mode 100644 (file)
index 0000000..5049f3e
--- /dev/null
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)exf.h       8.20 (Berkeley) 12/28/93
+ */
+                                       /* Undo direction. */
+/*
+ * exf --
+ *     The file structure.
+ */
+struct _exf {
+       int      refcnt;                /* Reference count. */
+
+                                       /* Underlying database state. */
+       DB      *db;                    /* File db structure. */
+       char    *c_lp;                  /* Cached line. */
+       size_t   c_len;                 /* Cached line length. */
+       recno_t  c_lno;                 /* Cached line number. */
+       recno_t  c_nlines;              /* Cached lines in the file. */
+
+       DB      *log;                   /* Log db structure. */
+       char    *l_lp;                  /* Log buffer. */
+       size_t   l_len;                 /* Log buffer length. */
+       recno_t  l_high;                /* Log last + 1 record number. */
+       recno_t  l_cur;                 /* Log current record number. */
+       MARK     l_cursor;              /* Log cursor position. */
+       enum direction lundo;           /* Last undo direction. */
+
+       LIST_HEAD(_markh, _mark) marks; /* Linked list of file MARK's. */
+
+       /*
+        * Paths for the recovery mail file and the vi recovery file and
+        * a file descriptor for the former.  We keep a file descriptor
+        * to the recovery file open and locked, while the file is in use.
+        * This allows the recovery option to distinguish between files
+        * that are live, and those that should be recovered.
+        *
+        * F_RCV_ON is set as long as we believe that the file is recoverable.
+        * This doesn't mean that any initialization has been done, however.
+        * If F_RCV_NORM is not set and rcv_path and rcv_mpath are not NULL,
+        * they are unlinked on file exit.  If not NULL they are free'd on file
+        * exit.  On file exit, if rcv_fd is not -1, it is closed.
+        */
+#define        RCV_PERIOD      120             /* Sync every two minutes. */
+       char    *rcv_path;              /* Recover file name. */
+       char    *rcv_mpath;             /* Recover mail file name. */
+       int      rcv_fd;                /* Locked mail file descriptor. */
+
+#define        F_FIRSTMODIFY   0x001           /* File not yet modified. */
+#define        F_MODIFIED      0x002           /* File is currently dirty. */
+#define        F_MULTILOCK     0x004           /* Multiple processes running, lock. */
+#define        F_NOLOG         0x008           /* Logging turned off. */
+#define        F_RCV_ALRM      0x010           /* File needs to be synced. */
+#define        F_RCV_NORM      0x020           /* Don't delete recovery files. */
+#define        F_RCV_ON        0x040           /* Recovery is possible. */
+#define        F_UNDO          0x080           /* No change since last undo. */
+       u_int    flags;
+};
+
+/* Flags to file_write(). */
+#define        FS_ALL          0x01            /* Write the entire file. */
+#define        FS_APPEND       0x02            /* Append to the file. */
+#define        FS_FORCE        0x04            /* Force is set. */
+#define        FS_POSSIBLE     0x08            /* Force could be set. */
+
+#define        GETLINE_ERR(sp, lno) {                                          \
+       msgq((sp), M_ERR,                                               \
+           "Error: %s/%d: unable to retrieve line %u.",                \
+           tail(__FILE__), __LINE__, (lno));                           \
+}
+
+/* FREF routines. */
+FREF   *file_add __P((SCR *, FREF *, CHAR_T *, int));
+FREF   *file_first __P((SCR *));
+FREF   *file_next __P((SCR *, FREF *));
+FREF   *file_prev __P((SCR *, FREF *));
+FREF   *file_unedited __P((SCR *));
+
+/* EXF routines. */
+int     file_end __P((SCR *, EXF *, int));
+int     file_init __P((SCR *, FREF *, char *, int));
+int     file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int));
+
+/* Recovery routines. */
+void    rcv_hup __P((void));
+int     rcv_init __P((SCR *, EXF *));
+int     rcv_list __P((SCR *));
+int     rcv_read __P((SCR *, char *));
+int     rcv_sync __P((SCR *, EXF *));
+void    rcv_term __P((void));
+int     rcv_tmp __P((SCR *, EXF *, char *));
+
+/* DB interface routines */
+int     file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t));
+int     file_dline __P((SCR *, EXF *, recno_t));
+char   *file_gline __P((SCR *, EXF *, recno_t, size_t *));
+int     file_iline __P((SCR *, EXF *, recno_t, char *, size_t));
+int     file_lline __P((SCR *, EXF *, recno_t *));
+char   *file_rline __P((SCR *, EXF *, recno_t, size_t *));
+int     file_sline __P((SCR *, EXF *, recno_t, char *, size_t));
diff --git a/usr.bin/vi/gs.h b/usr.bin/vi/gs.h
new file mode 100644 (file)
index 0000000..86af729
--- /dev/null
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)gs.h        8.26 (Berkeley) 1/9/94
+ */
+
+struct _gs {
+       CIRCLEQ_HEAD(_dqh, _scr) dq;    /* Displayed screens. */
+       CIRCLEQ_HEAD(_hqh, _scr) hq;    /* Hidden screens. */
+       
+       mode_t   origmode;              /* Original terminal mode. */
+       struct termios
+                original_termios;      /* Original terminal values. */
+       struct termios
+                s5_curses_botch;       /* System V curses workaround. */
+
+       MSGH     msgq;                  /* User message list. */
+
+       char    *tmp_bp;                /* Temporary buffer. */
+       size_t   tmp_blen;              /* Size of temporary buffer. */
+
+#ifdef DEBUG
+       FILE    *tracefp;               /* Trace file pointer. */
+#endif
+
+/* INFORMATION SHARED BY ALL SCREENS. */
+       IBUF    *tty;                   /* Key input buffer. */
+
+       CB      *dcbp;                  /* Default cut buffer pointer. */
+       CB      *dcb_store;             /* Default cut buffer storage. */
+       LIST_HEAD(_cuth, _cb) cutq;     /* Linked list of cut buffers. */
+
+#define        MAX_BIT_SEQ     128             /* Max + 1 fast check character. */
+       LIST_HEAD(_seqh, _seq) seqq;    /* Linked list of maps, abbrevs. */
+       bitstr_t bit_decl(seqb, MAX_BIT_SEQ);
+
+#define        term_key_val(sp, ch)                                            \
+       ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] :               \
+           (ch) > sp->gp->max_special ? 0 : __term_key_val(sp, ch))
+#define        MAX_FAST_KEY    255             /* Max + 1 fast check character.*/
+       CHAR_T   max_special;           /* Max special character. */
+       u_char  *special_key;           /* Fast lookup table. */
+       CHNAME  const *cname;           /* Display names of ASCII characters. */
+
+#define        G_ABBREV        0x00001         /* If have abbreviations. */
+#define        G_BELLSCHED     0x00002         /* Bell scheduled. */
+#define        G_CURSES_INIT   0x00004         /* Curses: initialized. */
+#define        G_CURSES_S5CB   0x00008         /* Curses: s5_curses_botch set. */
+#define        G_ISFROMTTY     0x00010         /* Reading from a tty. */
+#define        G_RECOVER_SET   0x00020         /* Recover system initialized. */
+#define        G_SETMODE       0x00040         /* Tty mode changed. */
+#define        G_SIGALRM       0x00080         /* SIGALRM arrived. */
+#define        G_SIGHUP        0x00100         /* SIGHUP arrived. */
+#define        G_SIGTERM       0x00200         /* SIGTERM arrived. */
+#define        G_SIGWINCH      0x00400         /* SIGWINCH arrived. */
+#define        G_SLEEPING      0x00800         /* Asleep (die on signal). */
+#define        G_SNAPSHOT      0x01000         /* Always snapshot files. */
+#define        G_TMP_INUSE     0x02000         /* Temporary buffer in use. */
+       u_int    flags;
+};
+
+extern GS *__global_list;              /* List of screens. */
diff --git a/usr.bin/vi/include/bitstring.h b/usr.bin/vi/include/bitstring.h
new file mode 100644 (file)
index 0000000..88437e7
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Vixie.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)bitstring.h 8.1 (Berkeley) 7/19/93
+ */
+
+#ifndef _BITSTRING_H_
+#define        _BITSTRING_H_
+
+typedef        unsigned char bitstr_t;
+
+/* internal macros */
+                               /* byte of the bitstring bit is in */
+#define        _bit_byte(bit) \
+       ((bit) >> 3)
+
+                               /* mask for the bit within its byte */
+#define        _bit_mask(bit) \
+       (1 << ((bit)&0x7))
+
+/* external macros */
+                               /* bytes in a bitstring of nbits bits */
+#define        bitstr_size(nbits) \
+       ((((nbits) - 1) >> 3) + 1)
+
+                               /* allocate a bitstring */
+#define        bit_alloc(nbits) \
+       (bitstr_t *)calloc(1, \
+           (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
+
+                               /* allocate a bitstring on the stack */
+#define        bit_decl(name, nbits) \
+       (name)[bitstr_size(nbits)]
+
+                               /* is bit N of bitstring name set? */
+#define        bit_test(name, bit) \
+       ((name)[_bit_byte(bit)] & _bit_mask(bit))
+
+                               /* set bit N of bitstring name */
+#define        bit_set(name, bit) \
+       (name)[_bit_byte(bit)] |= _bit_mask(bit)
+
+                               /* clear bit N of bitstring name */
+#define        bit_clear(name, bit) \
+       (name)[_bit_byte(bit)] &= ~_bit_mask(bit)
+
+                               /* clear bits start ... stop in bitstring */
+#define        bit_nclear(name, start, stop) { \
+       register bitstr_t *_name = name; \
+       register int _start = start, _stop = stop; \
+       register int _startbyte = _bit_byte(_start); \
+       register int _stopbyte = _bit_byte(_stop); \
+       if (_startbyte == _stopbyte) { \
+               _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
+                                     (0xff << ((_stop&0x7) + 1))); \
+       } else { \
+               _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
+               while (++_startbyte < _stopbyte) \
+                       _name[_startbyte] = 0; \
+               _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
+       } \
+}
+
+                               /* set bits start ... stop in bitstring */
+#define        bit_nset(name, start, stop) { \
+       register bitstr_t *_name = name; \
+       register int _start = start, _stop = stop; \
+       register int _startbyte = _bit_byte(_start); \
+       register int _stopbyte = _bit_byte(_stop); \
+       if (_startbyte == _stopbyte) { \
+               _name[_startbyte] |= ((0xff << (_start&0x7)) & \
+                                   (0xff >> (7 - (_stop&0x7)))); \
+       } else { \
+               _name[_startbyte] |= 0xff << ((_start)&0x7); \
+               while (++_startbyte < _stopbyte) \
+                       _name[_startbyte] = 0xff; \
+               _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
+       } \
+}
+
+                               /* find first bit clear in name */
+#define        bit_ffc(name, nbits, value) { \
+       register bitstr_t *_name = name; \
+       register int _byte, _nbits = nbits; \
+       register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+       for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+               if (_name[_byte] != 0xff) { \
+                       _value = _byte << 3; \
+                       for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
+                           ++_value, _stopbyte >>= 1); \
+                       break; \
+               } \
+       *(value) = _value; \
+}
+
+                               /* find first bit set in name */
+#define        bit_ffs(name, nbits, value) { \
+       register bitstr_t *_name = name; \
+       register int _byte, _nbits = nbits; \
+       register int _stopbyte = _bit_byte(_nbits), _value = -1; \
+       for (_byte = 0; _byte <= _stopbyte; ++_byte) \
+               if (_name[_byte]) { \
+                       _value = _byte << 3; \
+                       for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
+                           ++_value, _stopbyte >>= 1); \
+                       break; \
+               } \
+       *(value) = _value; \
+}
+
+#endif /* !_BITSTRING_H_ */
diff --git a/usr.bin/vi/include/cdefs.h b/usr.bin/vi/include/cdefs.h
new file mode 100644 (file)
index 0000000..c4157bc
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)cdefs.h     8.2 (Berkeley) 10/4/93
+ */
+
+#ifndef        _CDEFS_H_
+#define        _CDEFS_H_
+
+#if defined(__cplusplus)
+#define        __BEGIN_DECLS   extern "C" {
+#define        __END_DECLS     };
+#else
+#define        __BEGIN_DECLS
+#define        __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments.  __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define        __P(protos)     protos          /* full-blown ANSI C */
+#define        __CONCAT(x,y)   x ## y
+#define        __STRING(x)     #x
+
+#if !defined(__GNUC__) && !defined(__cplusplus)
+#define        inline
+#endif
+
+#else  /* !(__STDC__ || __cplusplus) */
+#define        __P(protos)     ()              /* traditional C preprocessor */
+#define        __CONCAT(x,y)   x/**/y
+#define        __STRING(x)     "x"
+
+#ifdef __GNUC__
+#define        const           __const         /* GCC: ANSI C with -traditional */
+#define        inline          __inline
+#define        signed          __signed
+#define        volatile        __volatile
+
+#else  /* !__GNUC__ */
+#define        const                           /* delete ANSI C keywords */
+#define        inline
+#define        signed
+#define        volatile
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC has extensions for declaring functions as `pure' (always returns
+ * the same value given the same inputs, i.e., has no external state and
+ * no side effects) and `dead' (nonreturning).  These mainly affect
+ * optimization and warnings.  Unfortunately, GCC complains if these are
+ * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to
+ * define them only if compiling without this.
+ */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#else
+#define __dead
+#define __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/usr.bin/vi/include/compat.h b/usr.bin/vi/include/compat.h
new file mode 100644 (file)
index 0000000..1040699
--- /dev/null
@@ -0,0 +1,241 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)compat.h    8.10 (Berkeley) 1/11/94
+ */
+
+#ifndef        _COMPAT_H_
+#define        _COMPAT_H_
+
+#include <sys/types.h>
+#include <machine/limits.h>
+#include <termios.h>
+#include <errno.h>
+
+/*
+ * If your system doesn't typedef u_long, u_short, or u_char, change
+ * the 0 to a 1.
+ */
+#if 0
+typedef unsigned char  u_char;         /* 4.[34]BSD names. */
+typedef unsigned int   u_int;
+typedef unsigned long  u_long;
+typedef unsigned short u_short;
+#endif
+
+/* If your system doesn't typedef size_t, change the 0 to a 1. */
+#if 0
+typedef unsigned int   size_t;         /* 4.[34]BSD names. */
+#endif
+
+/*
+ * If your system doesn't have the POSIX type for a signal mask,
+ * change the 0 to a 1.
+ */
+#if 0                                  /* POSIX 1003.1 signal mask type. */
+typedef unsigned int   sigset_t;
+#endif
+
+/*
+ * If your system's vsprintf returns a char *, not an int,
+ * change the 0 to a 1.
+ */
+#if 0
+#define        VSPRINTF_CHARSTAR
+#endif
+
+/*
+ * If you don't have POSIX 1003.1 signals, the signal code surrounding the 
+ * temporary file creation is intended to block all of the possible signals
+ * long enough to create the file and unlink it.  All of this stuff is
+ * intended to use old-style BSD calls to fake POSIX 1003.1 calls.
+ */
+#ifdef NO_POSIX_SIGNALS
+#define        sigemptyset(set)        (*(set) = 0)
+#define        sigfillset(set)         (*(set) = ~(sigset_t)0, 0)
+#define        sigaddset(set,signo)    (*(set) |= sigmask(signo), 0)
+#define        sigdelset(set,signo)    (*(set) &= ~sigmask(signo), 0)
+#define        sigismember(set,signo)  ((*(set) & sigmask(signo)) != 0)
+
+#define        SIG_BLOCK       1
+#define        SIG_UNBLOCK     2
+#define        SIG_SETMASK     3
+
+static int __sigtemp;          /* For the use of sigprocmask */
+
+/* Repeated test of oset != NULL is to avoid "*0". */
+#define        sigprocmask(how, set, oset)                                     \
+       ((__sigtemp =                                                   \
+       (((how) == SIG_BLOCK) ?                                         \
+               sigblock(0) | *(set) :                                  \
+       (((how) == SIG_UNBLOCK) ?                                       \
+               sigblock(0) & ~(*(set)) :                               \
+       ((how) == SIG_SETMASK ?                                         \
+               *(set) : sigblock(0))))),                               \
+       ((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) :      \
+               sigsetmask(__sigtemp)), 0)
+#endif
+
+/*
+ * If realloc(3) of a NULL pointer on your system isn't the same as
+ * a malloc(3) call, change the 0 to a 1, and add realloc.o to the
+ * MISC line in your Makefile.
+ */
+#if 0
+#define        realloc __fix_realloc
+#endif
+
+/*
+ * If your system doesn't have an include file with the appropriate
+ * byte order set, make sure you specify the correct one.
+ */
+#ifndef BYTE_ORDER
+#define        LITTLE_ENDIAN   1234            /* LSB first: i386, vax */
+#define        BIG_ENDIAN      4321            /* MSB first: 68000, ibm, net */
+#define        BYTE_ORDER      LITTLE_ENDIAN   /* Set for your system. */
+#endif
+
+#if defined(SYSV) || defined(SYSTEM5)
+#define        index(a, b)             strchr(a, b)
+#define        rindex(a, b)            strrchr(a, b)
+#define        bzero(a, b)             memset(a, 0, b)
+#define        bcmp(a, b, n)           memcmp(a, b, n)
+#define        bcopy(a, b, n)          memmove(b, a, n)
+#endif
+
+#if defined(BSD) || defined(BSD4_3)
+#define        strchr(a, b)            index(a, b)
+#define        strrchr(a, b)           rindex(a, b)
+#define        memcmp(a, b, n)         bcmp(a, b, n)
+#define        memmove(a, b, n)        bcopy(b, a, n)
+#endif
+
+/*
+ * 32-bit machine.  The db routines are theoretically independent of
+ * the size of u_shorts and u_longs, but I don't know that anyone has
+ * ever actually tried it.  At a minimum, change the following #define's
+ * if you are trying to compile on a different type of system.
+ */
+#ifndef USHRT_MAX
+#define        USHRT_MAX               0xFFFF
+#define        ULONG_MAX               0xFFFFFFFF
+#endif
+
+#ifndef O_ACCMODE                      /* POSIX 1003.1 access mode mask. */
+#define        O_ACCMODE       (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+#ifndef        _POSIX2_RE_DUP_MAX              /* POSIX 1003.2 RE limit. */
+#define        _POSIX2_RE_DUP_MAX      255
+#endif
+
+/*
+ * If you can't provide lock values in the open(2) call.  Note, this
+ * allows races to happen.
+ */
+#ifndef O_EXLOCK                       /* 4.4BSD extension. */
+#define        O_EXLOCK        0
+#endif
+
+#ifndef O_SHLOCK                       /* 4.4BSD extension. */
+#define        O_SHLOCK        0
+#endif
+
+#ifndef EFTYPE
+#define        EFTYPE          EINVAL          /* POSIX 1003.1 format errno. */
+#endif
+
+#ifndef        WCOREDUMP                       /* 4.4BSD extension */
+#define        WCOREDUMP(a)    0
+#endif
+
+#ifndef        STDERR_FILENO
+#define        STDIN_FILENO    0               /* ANSI C #defines */
+#define        STDOUT_FILENO   1
+#define        STDERR_FILENO   2
+#endif
+
+#ifndef SEEK_END
+#define        SEEK_SET        0               /* POSIX 1003.1 seek values */
+#define        SEEK_CUR        1
+#define        SEEK_END        2
+#endif
+
+#ifndef _POSIX_VDISABLE                        /* POSIX 1003.1 disabling char. */
+#define        _POSIX_VDISABLE 0               /* Some systems used 0. */
+#endif
+
+#ifndef S_ISLNK                                /* BSD POSIX 1003.1 extensions */
+#define        S_ISLNK(m)      ((m & 0170000) == 0120000)
+#define        S_ISSOCK(m)     ((m & 0170000) == 0140000)
+#endif
+
+#ifndef        TCSASOFT                        /* 4.4BSD extension. */
+#define        TCSASOFT        0
+#endif
+
+#ifndef _POSIX2_RE_DUP_MAX             /* POSIX 1003.2 values. */
+#define        _POSIX2_RE_DUP_MAX      255
+#endif
+
+#ifndef NULL                           /* ANSI C #defines NULL everywhere. */
+#define        NULL            0
+#endif
+
+#ifndef        MAX                             /* Usually found in <sys/param.h>. */
+#define        MAX(_a,_b)      ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef        MIN                             /* Usually found in <sys/param.h>. */
+#define        MIN(_a,_b)      ((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Default file permissions. */
+#ifndef DEFFILEMODE                    /* 4.4BSD extension. */
+#define        DEFFILEMODE     (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#endif
+
+#ifndef S_ISDIR                                /* POSIX 1003.1 file type tests. */
+#define        S_ISDIR(m)      ((m & 0170000) == 0040000)      /* directory */
+#define        S_ISCHR(m)      ((m & 0170000) == 0020000)      /* char special */
+#define        S_ISBLK(m)      ((m & 0170000) == 0060000)      /* block special */
+#define        S_ISREG(m)      ((m & 0170000) == 0100000)      /* regular file */
+#define        S_ISFIFO(m)     ((m & 0170000) == 0010000)      /* fifo */
+#define        S_ISLNK(m)      ((m & 0170000) == 0120000)      /* symbolic link */
+#define        S_ISSOCK(m)     ((m & 0170000) == 0140000)      /* socket */
+#endif
+
+/* The type of a va_list. */
+#ifndef _BSD_VA_LIST_                  /* 4.4BSD #define. */
+#define        _BSD_VA_LIST_   char *
+#endif
+
+#endif /* !_COMPAT_H_ */
diff --git a/usr.bin/vi/include/err.h b/usr.bin/vi/include/err.h
new file mode 100644 (file)
index 0000000..b6d7e5f
--- /dev/null
@@ -0,0 +1,16 @@
+#include <sys/cdefs.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void   err __P((int, const char *, ...));
+void   verr __P((int, const char *, va_list));
+void   errx __P((int, const char *, ...));
+void   verrx __P((int, const char *, va_list));
+void   warn __P((const char *, ...));
+void   vwarn __P((const char *, va_list));
+void   warnx __P((const char *, ...));
+void   vwarnx __P((const char *, va_list));
diff --git a/usr.bin/vi/include/file.h b/usr.bin/vi/include/file.h
new file mode 100644 (file)
index 0000000..680797f
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * If we're doing flock(2) emulation, we need to get the LOCK_* #defines.
+ * This stub <sys/file.h> includes the real one, and, if they're not in
+ * it, we #define them here.
+ */
+
+#include </usr/include/sys/file.h>
+
+#ifndef LOCK_SH
+/* lock operations for flock(2) */
+#define        LOCK_SH         0x01            /* shared file lock */
+#define        LOCK_EX         0x02            /* exclusive file lock */
+#define        LOCK_NB         0x04            /* don't block when locking */
+#define        LOCK_UN         0x08            /* unlock file */
+#endif
diff --git a/usr.bin/vi/include/glob.h b/usr.bin/vi/include/glob.h
new file mode 100644 (file)
index 0000000..b679f6a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)glob.h      8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _GLOB_H_
+#define        _GLOB_H_
+
+#include <sys/cdefs.h>
+
+#include "compat.h"
+
+struct stat;
+typedef struct {
+       int gl_pathc;           /* Count of total paths so far. */
+       int gl_matchc;          /* Count of paths matching pattern. */
+       int gl_offs;            /* Reserved at beginning of gl_pathv. */
+       int gl_flags;           /* Copy of flags parameter to glob. */
+       char **gl_pathv;        /* List of paths matching pattern. */
+                               /* Copy of errfunc parameter to glob. */
+       int (*gl_errfunc) __P((const char *, int));
+
+       /*
+        * Alternate filesystem access methods for glob; replacement
+        * versions of closedir(3), readdir(3), opendir(3), stat(2)
+        * and lstat(2).
+        */
+       void (*gl_closedir) __P((void *));
+       struct dirent *(*gl_readdir) __P((void *));     
+       void *(*gl_opendir) __P((const char *));
+       int (*gl_lstat) __P((const char *, struct stat *));
+       int (*gl_stat) __P((const char *, struct stat *));
+} glob_t;
+
+#define        GLOB_APPEND     0x0001  /* Append to output from previous call. */
+#define        GLOB_DOOFFS     0x0002  /* Use gl_offs. */
+#define        GLOB_ERR        0x0004  /* Return on error. */
+#define        GLOB_MARK       0x0008  /* Append / to matching directories. */
+#define        GLOB_NOCHECK    0x0010  /* Return pattern itself if nothing matches. */
+#define        GLOB_NOSORT     0x0020  /* Don't sort. */
+
+#ifndef _POSIX_SOURCE
+#define        GLOB_ALTDIRFUNC 0x0040  /* Use alternately specified directory funcs. */
+#define        GLOB_BRACE      0x0080  /* Expand braces ala csh. */
+#define        GLOB_MAGCHAR    0x0100  /* Pattern had globbing characters. */
+#define        GLOB_NOMAGIC    0x0200  /* GLOB_NOCHECK without magic chars (csh). */
+#define        GLOB_QUOTE      0x0400  /* Quote special chars with \. */
+#define        GLOB_TILDE      0x0800  /* Expand tilde names from the passwd file. */
+#endif
+
+#define        GLOB_NOSPACE    (-1)    /* Malloc call failed. */
+#define        GLOB_ABEND      (-2)    /* Unignored error. */
+
+__BEGIN_DECLS
+int    glob __P((const char *, int, int (*)(const char *, int), glob_t *));
+void   globfree __P((glob_t *));
+__END_DECLS
+
+#endif /* !_GLOB_H_ */
diff --git a/usr.bin/vi/include/mpool.h b/usr.bin/vi/include/mpool.h
new file mode 100644 (file)
index 0000000..910e078
--- /dev/null
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)mpool.h     8.1 (Berkeley) 6/2/93
+ */
+
+/*
+ * The memory pool scheme is a simple one.  Each in memory page is referenced
+ * by a bucket which is threaded in three ways.  All active pages are threaded
+ * on a hash chain (hashed by the page number) and an lru chain.  Inactive
+ * pages are threaded on a free chain.  Each reference to a memory pool is
+ * handed an MPOOL which is the opaque cookie passed to all of the memory
+ * routines.
+ */
+#define        HASHSIZE        128
+#define        HASHKEY(pgno)   ((pgno - 1) % HASHSIZE)
+
+/* The BKT structures are the elements of the lists. */
+typedef struct BKT {
+       struct BKT      *hnext;         /* next hash bucket */
+       struct BKT      *hprev;         /* previous hash bucket */
+       struct BKT      *cnext;         /* next free/lru bucket */
+       struct BKT      *cprev;         /* previous free/lru bucket */
+       void            *page;          /* page */
+       pgno_t          pgno;           /* page number */
+
+#define        MPOOL_DIRTY     0x01            /* page needs to be written */
+#define        MPOOL_PINNED    0x02            /* page is pinned into memory */
+       unsigned long   flags;          /* flags */
+} BKT;
+
+/* The BKTHDR structures are the heads of the lists. */
+typedef struct BKTHDR {
+       struct BKT      *hnext;         /* next hash bucket */
+       struct BKT      *hprev;         /* previous hash bucket */
+       struct BKT      *cnext;         /* next free/lru bucket */
+       struct BKT      *cprev;         /* previous free/lru bucket */
+} BKTHDR;
+
+typedef struct MPOOL {
+       BKTHDR  free;                   /* The free list. */
+       BKTHDR  lru;                    /* The LRU list. */
+       BKTHDR  hashtable[HASHSIZE];    /* Hashed list by page number. */
+       pgno_t  curcache;               /* Current number of cached pages. */
+       pgno_t  maxcache;               /* Max number of cached pages. */
+       pgno_t  npages;                 /* Number of pages in the file. */
+       u_long  pagesize;               /* File page size. */
+       int     fd;                     /* File descriptor. */
+                                       /* Page in conversion routine. */
+       void    (*pgin) __P((void *, pgno_t, void *));
+                                       /* Page out conversion routine. */
+       void    (*pgout) __P((void *, pgno_t, void *));
+       void    *pgcookie;              /* Cookie for page in/out routines. */
+#ifdef STATISTICS
+       unsigned long   cachehit;
+       unsigned long   cachemiss;
+       unsigned long   pagealloc;
+       unsigned long   pageflush;
+       unsigned long   pageget;
+       unsigned long   pagenew;
+       unsigned long   pageput;
+       unsigned long   pageread;
+       unsigned long   pagewrite;
+#endif
+} MPOOL;
+
+#ifdef __MPOOLINTERFACE_PRIVATE
+/* Macros to insert/delete into/from hash chain. */
+#define rmhash(bp) { \
+        (bp)->hprev->hnext = (bp)->hnext; \
+        (bp)->hnext->hprev = (bp)->hprev; \
+}
+#define inshash(bp, pg) { \
+       hp = &mp->hashtable[HASHKEY(pg)]; \
+        (bp)->hnext = hp->hnext; \
+        (bp)->hprev = (struct BKT *)hp; \
+        hp->hnext->hprev = (bp); \
+        hp->hnext = (bp); \
+}
+
+/* Macros to insert/delete into/from lru and free chains. */
+#define        rmchain(bp) { \
+        (bp)->cprev->cnext = (bp)->cnext; \
+        (bp)->cnext->cprev = (bp)->cprev; \
+}
+#define inschain(bp, dp) { \
+        (bp)->cnext = (dp)->cnext; \
+        (bp)->cprev = (struct BKT *)(dp); \
+        (dp)->cnext->cprev = (bp); \
+        (dp)->cnext = (bp); \
+}
+#endif
+
+__BEGIN_DECLS
+MPOOL  *mpool_open __P((DBT *, int, pgno_t, pgno_t));
+void    mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+           void (*)(void *, pgno_t, void *), void *));
+void   *mpool_new __P((MPOOL *, pgno_t *));
+void   *mpool_get __P((MPOOL *, pgno_t, u_int));
+int     mpool_put __P((MPOOL *, void *, u_int));
+int     mpool_sync __P((MPOOL *));
+int     mpool_close __P((MPOOL *));
+#ifdef STATISTICS
+void    mpool_stat __P((MPOOL *));
+#endif
+__END_DECLS
diff --git a/usr.bin/vi/include/ndbm.h b/usr.bin/vi/include/ndbm.h
new file mode 100644 (file)
index 0000000..a545bca
--- /dev/null
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)ndbm.h      8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NDBM_H_
+#define        _NDBM_H_
+
+#include <db.h>
+
+/* Map dbm interface onto db(3). */
+#define DBM_RDONLY     O_RDONLY
+
+/* Flags to dbm_store(). */
+#define DBM_INSERT      0
+#define DBM_REPLACE     1
+
+/*
+ * The db(3) support for ndbm(3) always appends this suffix to the
+ * file name to avoid overwriting the user's original database.
+ */
+#define        DBM_SUFFIX      ".db"
+
+typedef struct {
+       char *dptr;
+       int dsize;
+} datum;
+
+typedef DB DBM;
+#define        dbm_pagfno(a)   DBM_PAGFNO_NOT_AVAILABLE
+
+__BEGIN_DECLS
+void    dbm_close __P((DBM *));
+int     dbm_delete __P((DBM *, datum));
+datum   dbm_fetch __P((DBM *, datum));
+datum   dbm_firstkey __P((DBM *));
+long    dbm_forder __P((DBM *, datum));
+datum   dbm_nextkey __P((DBM *));
+DBM    *dbm_open __P((const char *, int, int));
+int     dbm_store __P((DBM *, datum, datum, int));
+int     dbm_dirfno __P((DBM *));
+__END_DECLS
+
+#endif /* !_NDBM_H_ */
diff --git a/usr.bin/vi/include/queue.h b/usr.bin/vi/include/queue.h
new file mode 100644 (file)
index 0000000..40d32cc
--- /dev/null
@@ -0,0 +1,245 @@
+/* 
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)queue.h     8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef        _QUEUE_H_
+#define        _QUEUE_H_
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define LIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+#define        LIST_INIT(head) {                                               \
+       (head)->lh_first = NULL;                                        \
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) {                       \
+       if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
+               (listelm)->field.le_next->field.le_prev =               \
+                   &(elm)->field.le_next;                              \
+       (listelm)->field.le_next = (elm);                               \
+       (elm)->field.le_prev = &(listelm)->field.le_next;               \
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) {                           \
+       if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+               (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+       (head)->lh_first = (elm);                                       \
+       (elm)->field.le_prev = &(head)->lh_first;                       \
+}
+
+#define LIST_REMOVE(elm, field) {                                      \
+       if ((elm)->field.le_next != NULL)                               \
+               (elm)->field.le_next->field.le_prev =                   \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = (elm)->field.le_next;                   \
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define TAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define        TAILQ_INIT(head) {                                              \
+       (head)->tqh_first = NULL;                                       \
+       (head)->tqh_last = &(head)->tqh_first;                          \
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) {                          \
+       if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (head)->tqh_first = (elm);                                      \
+       (elm)->field.tqe_prev = &(head)->tqh_first;                     \
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) {                          \
+       (elm)->field.tqe_next = NULL;                                   \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &(elm)->field.tqe_next;                      \
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) {                        \
+       if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   &(elm)->field.tqe_next;                             \
+       else                                                            \
+               (head)->tqh_last = &(elm)->field.tqe_next;              \
+       (listelm)->field.tqe_next = (elm);                              \
+       (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
+}
+
+#define TAILQ_REMOVE(head, elm, field) {                               \
+       if (((elm)->field.tqe_next) != NULL)                            \
+               (elm)->field.tqe_next->field.tqe_prev =                 \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)                                       \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define CIRCLEQ_ENTRY(type)                                            \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_INIT(head) {                                            \
+       (head)->cqh_first = (void *)(head);                             \
+       (head)->cqh_last = (void *)(head);                              \
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) {              \
+       (elm)->field.cqe_next = (listelm)->field.cqe_next;              \
+       (elm)->field.cqe_prev = (listelm);                              \
+       if ((listelm)->field.cqe_next == (void *)(head))                \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (listelm)->field.cqe_next->field.cqe_prev = (elm);      \
+       (listelm)->field.cqe_next = (elm);                              \
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) {             \
+       (elm)->field.cqe_next = (listelm);                              \
+       (elm)->field.cqe_prev = (listelm)->field.cqe_prev;              \
+       if ((listelm)->field.cqe_prev == (void *)(head))                \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (listelm)->field.cqe_prev->field.cqe_next = (elm);      \
+       (listelm)->field.cqe_prev = (elm);                              \
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) {                                \
+       (elm)->field.cqe_next = (head)->cqh_first;                      \
+       (elm)->field.cqe_prev = (void *)(head);                         \
+       if ((head)->cqh_last == (void *)(head))                         \
+               (head)->cqh_last = (elm);                               \
+       else                                                            \
+               (head)->cqh_first->field.cqe_prev = (elm);              \
+       (head)->cqh_first = (elm);                                      \
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) {                                \
+       (elm)->field.cqe_next = (void *)(head);                         \
+       (elm)->field.cqe_prev = (head)->cqh_last;                       \
+       if ((head)->cqh_first == (void *)(head))                        \
+               (head)->cqh_first = (elm);                              \
+       else                                                            \
+               (head)->cqh_last->field.cqe_next = (elm);               \
+       (head)->cqh_last = (elm);                                       \
+}
+
+#define        CIRCLEQ_REMOVE(head, elm, field) {                              \
+       if ((elm)->field.cqe_next == (void *)(head))                    \
+               (head)->cqh_last = (elm)->field.cqe_prev;               \
+       else                                                            \
+               (elm)->field.cqe_next->field.cqe_prev =                 \
+                   (elm)->field.cqe_prev;                              \
+       if ((elm)->field.cqe_prev == (void *)(head))                    \
+               (head)->cqh_first = (elm)->field.cqe_next;              \
+       else                                                            \
+               (elm)->field.cqe_prev->field.cqe_next =                 \
+                   (elm)->field.cqe_next;                              \
+}
+#endif /* !_QUEUE_H_ */
diff --git a/usr.bin/vi/interrupt.h b/usr.bin/vi/interrupt.h
new file mode 100644 (file)
index 0000000..baa75fe
--- /dev/null
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)interrupt.h 8.1 (Berkeley) 1/9/94
+ */
+
+/*
+ * Macros to declare the variables and then turn on and off interrupts.
+ */
+#define        DECLARE_INTERRUPTS                                              \
+       struct sigaction __act, __oact;                                 \
+       struct termios __nterm, __term;                                 \
+       u_int __istate;                                                 \
+       int __isig, __termreset
+
+/*
+ * Search, global, and substitute interruptions.
+ *
+ * ISIG turns on VINTR, VQUIT and VSUSP.  We want VINTR to interrupt the
+ * search, so we install a handler.  VQUIT is ignored by main() because
+ * nvi never wants to catch it.  A handler for VSUSP should have been
+ * installed by the screen code.
+ */
+#define        SET_UP_INTERRUPTS(handler) {                                    \
+       if (F_ISSET(sp->gp, G_ISFROMTTY)) {                             \
+               __act.sa_handler = handler;                             \
+               sigemptyset(&__act.sa_mask);                            \
+               __act.sa_flags = 0;                                     \
+               if (__isig = !sigaction(SIGINT, &__act, &__oact)) {     \
+                       __termreset = 0;                                \
+                       __istate = F_ISSET(sp, S_INTERRUPTIBLE);        \
+                       F_CLR(sp, S_INTERRUPTED);                       \
+                       F_SET(sp, S_INTERRUPTIBLE);                     \
+                       if (tcgetattr(STDIN_FILENO, &__term)) {         \
+                               rval = 1;                               \
+                               msgq(sp, M_SYSERR, "tcgetattr");        \
+                               goto interrupt_err;                     \
+                       }                                               \
+                       __nterm = __term;                               \
+                       __nterm.c_lflag |= ISIG;                        \
+                       if (tcsetattr(STDIN_FILENO,                     \
+                           TCSANOW | TCSASOFT, &__nterm)) {            \
+                               rval = 1;                               \
+                               msgq(sp, M_SYSERR, "tcsetattr");        \
+                               goto interrupt_err;                     \
+                       }                                               \
+                       __termreset = 1;                                \
+               }                                                       \
+       }                                                               \
+}
+
+#define        TEAR_DOWN_INTERRUPTS {                                          \
+       if (F_ISSET(sp->gp, G_ISFROMTTY) && __isig) {                   \
+               if (sigaction(SIGINT, &__oact, NULL)) {                 \
+                       rval = 1;                                       \
+                       msgq(sp, M_SYSERR, "signal");                   \
+               }                                                       \
+               if (__termreset && tcsetattr(STDIN_FILENO,              \
+                   TCSANOW | TCSASOFT, &__term)) {                     \
+                       rval = 1;                                       \
+                       msgq(sp, M_SYSERR, "tcsetattr");                \
+               }                                                       \
+               F_CLR(sp, S_INTERRUPTED);                               \
+               if (!__istate)                                          \
+                       F_CLR(sp, S_INTERRUPTIBLE);                     \
+       }                                                               \
+}
diff --git a/usr.bin/vi/line.c b/usr.bin/vi/line.c
new file mode 100644 (file)
index 0000000..a747349
--- /dev/null
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)line.c     8.20 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int));
+
+/*
+ * file_gline --
+ *     Look in the text buffers for a line; if it's not there
+ *     call file_rline to retrieve it from the database.
+ */
+char *
+file_gline(sp, ep, lno, lenp)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;                            /* Line number. */
+       size_t *lenp;                           /* Length store. */
+{
+       TEXT *tp;
+       recno_t l1, l2;
+
+       /*
+        * The underlying recno stuff handles zero by returning NULL, but
+        * have to have an oob condition for the look-aside into the input
+        * buffer anyway.
+        */
+       if (lno == 0)
+               return (NULL);
+
+       /*
+        * Look-aside into the TEXT buffers and see if the line we want
+        * is there.
+        */
+       if (F_ISSET(sp, S_INPUT)) {
+               l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
+               l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
+               if (l1 <= lno && l2 >= lno) {
+                       for (tp = sp->tiq.cqh_first;
+                           tp->lno != lno; tp = tp->q.cqe_next);
+                       if (lenp)
+                               *lenp = tp->len;
+                       return (tp->lb);
+               }
+               /*
+                * Adjust the line number for the number of lines used
+                * by the text input buffers.
+                */
+               if (lno > l2)
+                       lno -= l2 - l1;
+       }
+       return (file_rline(sp, ep, lno, lenp));
+}
+
+/*
+ * file_rline --
+ *     Look in the cache for a line; if it's not there retrieve
+ *     it from the file.
+ */
+char *
+file_rline(sp, ep, lno, lenp)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;                            /* Line number. */
+       size_t *lenp;                           /* Length store. */
+{
+       DBT data, key;
+
+       /* Check the cache. */
+       if (lno == ep->c_lno) {
+               if (lenp)
+                       *lenp = ep->c_len;
+               return (ep->c_lp);
+       }
+       ep->c_lno = OOBLNO;
+
+       /* Get the line from the underlying database. */
+       key.data = &lno;
+       key.size = sizeof(lno);
+       switch (ep->db->get(ep->db, &key, &data, 0)) {
+        case -1:
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to get line %u: %s.",
+                   tail(__FILE__), __LINE__, lno, strerror(errno));
+               /* FALLTHROUGH */
+        case 1:
+               return (NULL);
+               /* NOTREACHED */
+       }
+       if (lenp)
+               *lenp = data.size;
+
+       /* Fill the cache. */
+       ep->c_lno = lno;
+       ep->c_len = data.size;
+       ep->c_lp = data.data;
+
+       return (data.data);
+}
+
+/*
+ * file_dline --
+ *     Delete a line from the file.
+ */
+int
+file_dline(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+{
+       DBT key;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "delete line %lu\n", lno);
+#endif
+       /*
+        * XXX
+        *
+        * Marks and global commands have to know when lines are
+        * inserted or deleted.
+        */
+       mark_insdel(sp, ep, LINE_DELETE, lno);
+       global_insdel(sp, ep, LINE_DELETE, lno);
+
+       /* Log change. */
+       log_line(sp, ep, lno, LOG_LINE_DELETE);
+
+       /* Update file. */
+       key.data = &lno;
+       key.size = sizeof(lno);
+       if (ep->db->del(ep->db, &key, 0) == 1) {
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to delete line %u: %s.",
+                   tail(__FILE__), __LINE__, lno, strerror(errno));
+               return (1);
+       }
+
+       /* Flush the cache, update line count, before screen update. */
+       if (lno <= ep->c_lno)
+               ep->c_lno = OOBLNO;
+       if (ep->c_nlines != OOBLNO)
+               --ep->c_nlines;
+
+       /* File now dirty. */
+       if (F_ISSET(ep, F_FIRSTMODIFY))
+               (void)rcv_init(sp, ep);
+       F_SET(ep, F_MODIFIED);
+
+       /* Update screen. */
+       return (scr_update(sp, ep, lno, LINE_DELETE, 1));
+}
+
+/*
+ * file_aline --
+ *     Append a line into the file.
+ */
+int
+file_aline(sp, ep, update, lno, p, len)
+       SCR *sp;
+       EXF *ep;
+       int update;
+       recno_t lno;
+       char *p;
+       size_t len;
+{
+       DBT data, key;
+       recno_t lline;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+       /*
+        * 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 the
+        * screen.  This is really hard to find and fix in the vi code -- the
+        * text input functions detect it explicitly and don't insert a new
+        * line.  The hack here is to repaint the screen if we're appending
+        * to an empty file.
+        */
+       if (lno == 0) {
+               if (file_lline(sp, ep, &lline))
+                       return (1);
+               if (lline == 0)
+                       F_SET(sp, S_REDRAW);
+       }
+
+       /* Update file. */
+       key.data = &lno;
+       key.size = sizeof(lno);
+       data.data = p;
+       data.size = len;
+       if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to append to line %u: %s.",
+                   tail(__FILE__), __LINE__, lno, strerror(errno));
+               return (1);
+       }
+
+       /* Flush the cache, update line count, before screen update. */
+       if (lno < ep->c_lno)
+               ep->c_lno = OOBLNO;
+       if (ep->c_nlines != OOBLNO)
+               ++ep->c_nlines;
+
+       /* File now dirty. */
+       if (F_ISSET(ep, F_FIRSTMODIFY))
+               (void)rcv_init(sp, ep);
+       F_SET(ep, F_MODIFIED);
+
+       /* Log change. */
+       log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
+
+       /*
+        * XXX
+        *
+        * Marks and global commands have to know when lines are
+        * inserted or deleted.
+        */
+       mark_insdel(sp, ep, LINE_INSERT, lno + 1);
+       global_insdel(sp, ep, LINE_INSERT, lno + 1);
+
+       /*
+        * Update screen.
+        *
+        * XXX
+        * Nasty hack.  If multiple lines are input by the user, they aren't
+        * committed until an <ESC> is entered.  The problem is the screen was
+        * updated/scrolled as each line was entered.  So, when this routine
+        * is called to copy the new lines from the cut buffer into the file,
+        * it has to know not to update the screen again.
+        */ 
+       return (scr_update(sp, ep, lno, LINE_APPEND, update));
+}
+
+/*
+ * file_iline --
+ *     Insert a line into the file.
+ */
+int
+file_iline(sp, ep, lno, p, len)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       char *p;
+       size_t len;
+{
+       DBT data, key;
+       recno_t lline;
+
+#if defined(DEBUG) && 0
+       TRACE(sp,
+           "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+
+       /* Very nasty special case.  See comment in file_aline(). */
+       if (lno == 1) {
+               if (file_lline(sp, ep, &lline))
+                       return (1);
+               if (lline == 0)
+                       F_SET(sp, S_REDRAW);
+       }
+
+       /* Update file. */
+       key.data = &lno;
+       key.size = sizeof(lno);
+       data.data = p;
+       data.size = len;
+       if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to insert at line %u: %s.",
+                   tail(__FILE__), __LINE__, lno, strerror(errno));
+               return (1);
+       }
+
+       /* Flush the cache, update line count, before screen update. */
+       if (lno >= ep->c_lno)
+               ep->c_lno = OOBLNO;
+       if (ep->c_nlines != OOBLNO)
+               ++ep->c_nlines;
+
+       /* File now dirty. */
+       if (F_ISSET(ep, F_FIRSTMODIFY))
+               (void)rcv_init(sp, ep);
+       F_SET(ep, F_MODIFIED);
+
+       /* Log change. */
+       log_line(sp, ep, lno, LOG_LINE_INSERT);
+
+       /*
+        * XXX
+        *
+        * Marks and global commands have to know when lines are
+        * inserted or deleted.
+        */
+       mark_insdel(sp, ep, LINE_INSERT, lno);
+       global_insdel(sp, ep, LINE_INSERT, lno);
+
+       /* Update screen. */
+       return (scr_update(sp, ep, lno, LINE_INSERT, 1));
+}
+
+/*
+ * file_sline --
+ *     Store a line in the file.
+ */
+int
+file_sline(sp, ep, lno, p, len)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       char *p;
+       size_t len;
+{
+       DBT data, key;
+
+#if defined(DEBUG) && 0
+       TRACE(sp,
+           "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+       /* Log before change. */
+       log_line(sp, ep, lno, LOG_LINE_RESET_B);
+
+       /* Update file. */
+       key.data = &lno;
+       key.size = sizeof(lno);
+       data.data = p;
+       data.size = len;
+       if (ep->db->put(ep->db, &key, &data, 0) == -1) {
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to store line %u: %s.",
+                   tail(__FILE__), __LINE__, lno, strerror(errno));
+               return (1);
+       }
+
+       /* Flush the cache, before logging or screen update. */
+       if (lno == ep->c_lno)
+               ep->c_lno = OOBLNO;
+
+       /* File now dirty. */
+       if (F_ISSET(ep, F_FIRSTMODIFY))
+               (void)rcv_init(sp, ep);
+       F_SET(ep, F_MODIFIED);
+
+       /* Log after change. */
+       log_line(sp, ep, lno, LOG_LINE_RESET_F);
+       
+       /* Update screen. */
+       return (scr_update(sp, ep, lno, LINE_RESET, 1));
+}
+
+/*
+ * file_lline --
+ *     Return the number of lines in the file.
+ */
+int
+file_lline(sp, ep, lnop)
+       SCR *sp;
+       EXF *ep;
+       recno_t *lnop;
+{
+       DBT data, key;
+       recno_t lno;
+
+       /* Check the cache. */
+       if (ep->c_nlines != OOBLNO) {
+               *lnop = (F_ISSET(sp, S_INPUT) &&
+                   ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ?
+                   ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines);
+               return (0);
+       }
+
+       key.data = &lno;
+       key.size = sizeof(lno);
+
+       switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
+        case -1:
+               msgq(sp, M_ERR,
+                   "Error: %s/%d: unable to get last line: %s.",
+                   tail(__FILE__), __LINE__, strerror(errno));
+               *lnop = 0;
+               return (1);
+        case 1:
+               lno = 0;
+               break;
+       default:
+               memmove(&lno, key.data, sizeof(lno));
+               break;
+       }
+
+       /* Fill the cache. */
+       ep->c_nlines = ep->c_lno = lno;
+       ep->c_len = data.size;
+       ep->c_lp = data.data;
+       *lnop = (F_ISSET(sp, S_INPUT) &&
+           ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
+           ((TEXT *)sp->tiq.cqh_last)->lno : lno);
+       return (0);
+}
+
+/*
+ * scr_update --
+ *     Update all of the screens that are backed by the file that
+ *     just changed.
+ */
+static inline int
+scr_update(sp, ep, lno, op, current)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       enum operation op;
+       int current;
+{
+       SCR *tsp;
+
+       if (ep->refcnt != 1)
+               for (tsp = sp->gp->dq.cqh_first;
+                   tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+                       if (sp != tsp && tsp->ep == ep)
+                               (void)sp->s_change(tsp, ep, lno, op);
+       return (current && sp->s_change(sp, ep, lno, op));
+}
diff --git a/usr.bin/vi/log.c b/usr.bin/vi/log.c
new file mode 100644 (file)
index 0000000..fdf959f
--- /dev/null
@@ -0,0 +1,663 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)log.c      8.9 (Berkeley) 12/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.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                MARK
+ *
+ * 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 __P((SCR *, EXF *, int));
+#if defined(DEBUG) && 0
+static void    log_trace __P((SCR *, char *, recno_t, u_char *));
+#endif
+
+/* Try and restart the log on failure, i.e. if we run out of memory. */
+#define        LOG_ERR {                                                       \
+       msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.",             \
+           tail(__FILE__), __LINE__, strerror(errno));                 \
+       (void)ep->log->close(ep->log);                                  \
+       if (!log_init(sp, ep))                                          \
+               msgq(sp, M_ERR, "Log restarted.");                      \
+       return (1);                                                     \
+}
+
+/*
+ * log_init --
+ *     Initialize the logging subsystem.
+ */
+int
+log_init(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       /*
+        * 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_ERR, "log db: %s", strerror(errno));
+               F_SET(ep, F_NOLOG);
+               return (1);
+       }
+
+       return (0);
+}
+
+/*
+ * log_end --
+ *     Close the logging subsystem.
+ */
+int
+log_end(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       if (ep->log != NULL) {
+               (void)(ep->log->close)(ep->log);
+               ep->log = NULL;
+       }
+       if (ep->l_lp != 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.
+ */
+int
+log_cursor(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       /*
+        * 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, ep, 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(sp, ep, type)
+       SCR *sp;
+       EXF *ep;
+       int type;
+{
+       DBT data, key;
+
+       BINC_RET(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.
+ */
+int
+log_line(sp, ep, lno, action)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       u_int action;
+{
+       DBT data, key;
+       size_t len;
+       char *lp;
+
+       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, ep, 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 ((lp = file_rline(sp, ep, lno, &len)) == NULL) {
+                       if (lno != 1) {
+                               GETLINE_ERR(sp, lno);
+                               return (1);
+                       }
+                       len = 0;
+                       lp = "";
+               }
+       } else
+               if ((lp = file_gline(sp, ep, lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, lno);
+                       return (1);
+               }
+       BINC_RET(sp,
+           ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
+       ep->l_lp[0] = action;
+       memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
+       memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
+
+       key.data = &ep->l_cur;
+       key.size = sizeof(recno_t);
+       data.data = ep->l_lp;
+       data.size = len + sizeof(u_char) + sizeof(recno_t);
+       if (ep->log->put(ep->log, &key, &data, 0) == -1)
+               LOG_ERR;
+
+#if defined(DEBUG) && 0
+       switch (action) {
+       case LOG_LINE_APPEND:
+               TRACE(sp, "%u: 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.
+ */
+int
+log_mark(sp, ep, mp)
+       SCR *sp;
+       EXF *ep;
+       MARK *mp;
+{
+       DBT data, key;
+
+       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, ep, LOG_CURSOR_INIT))
+                       return (1);
+               ep->l_cursor.lno = OOBLNO;
+       }
+
+       BINC_RET(sp, ep->l_lp,
+           ep->l_len, sizeof(u_char) + sizeof(MARK));
+       ep->l_lp[0] = LOG_MARK;
+       memmove(ep->l_lp + sizeof(u_char), mp, 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;
+
+       /* Reset high water mark. */
+       ep->l_high = ++ep->l_cur;
+       return (0);
+}
+
+/*
+ * Log_backward --
+ *     Roll the log backward one operation.
+ */
+int
+log_backward(sp, ep, rp)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+{
+       DBT key, data;
+       MARK m;
+       recno_t lno;
+       int didop;
+       u_char *p;
+
+       if (F_ISSET(ep, F_NOLOG)) {
+               msgq(sp, M_ERR,
+                   "Logging not being performed, undo not possible.");
+               return (1);
+       }
+
+       if (ep->l_cur == 1) {
+               msgq(sp, M_BERR, "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 (file_dline(sp, ep, lno))
+                               goto err;
+                       ++sp->rptlines[L_DELETED];
+                       break;
+               case LOG_LINE_DELETE:
+                       didop = 1;
+                       memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+                       if (file_iline(sp, ep, lno, p + sizeof(u_char) +
+                           sizeof(recno_t), data.size - sizeof(u_char) -
+                           sizeof(recno_t)))
+                               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 (file_sline(sp, ep, lno, p + sizeof(u_char) +
+                           sizeof(recno_t), data.size - sizeof(u_char) -
+                           sizeof(recno_t)))
+                               goto err;
+                       ++sp->rptlines[L_CHANGED];
+                       break;
+               case LOG_MARK:
+                       didop = 1;
+                       memmove(&m, p + sizeof(u_char), sizeof(MARK));
+                       if (mark_set(sp, ep, m.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.
+ */
+int
+log_setline(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       DBT key, data;
+       MARK m;
+       recno_t lno;
+       u_char *p;
+
+       if (F_ISSET(ep, F_NOLOG)) {
+               msgq(sp, M_ERR,
+                   "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 &&
+                           file_sline(sp, ep, lno, p + sizeof(u_char) +
+                           sizeof(recno_t), data.size - sizeof(u_char) -
+                           sizeof(recno_t)))
+                               goto err;
+                       ++sp->rptlines[L_CHANGED];
+               case LOG_MARK:
+                       memmove(&m, p + sizeof(u_char), sizeof(MARK));
+                       if (mark_set(sp, ep, m.name, &m, 0))
+                               goto err;
+                       break;
+               default:
+                       abort();
+               }
+       }
+
+err:   F_CLR(ep, F_NOLOG);
+       return (1);
+}
+
+/*
+ * Log_forward --
+ *     Roll the log forward one operation.
+ */
+int
+log_forward(sp, ep, rp)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+{
+       DBT key, data;
+       MARK m;
+       recno_t lno;
+       int didop;
+       u_char *p;
+
+       if (F_ISSET(ep, F_NOLOG)) {
+               msgq(sp, M_ERR,
+                   "Logging not being performed, roll-forward not possible.");
+               return (1);
+       }
+
+       if (ep->l_cur == ep->l_high) {
+               msgq(sp, M_BERR, "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 (file_iline(sp, ep, lno, p + sizeof(u_char) +
+                           sizeof(recno_t), data.size - sizeof(u_char) -
+                           sizeof(recno_t)))
+                               goto err;
+                       ++sp->rptlines[L_ADDED];
+                       break;
+               case LOG_LINE_DELETE:
+                       didop = 1;
+                       memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+                       if (file_dline(sp, ep, 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 (file_sline(sp, ep, lno, p + sizeof(u_char) +
+                           sizeof(recno_t), data.size - sizeof(u_char) -
+                           sizeof(recno_t)))
+                               goto err;
+                       ++sp->rptlines[L_CHANGED];
+                       break;
+               case LOG_MARK:
+                       didop = 1;
+                       memmove(&m, p + sizeof(u_char), sizeof(MARK));
+                       if (mark_set(sp, ep, m.name, &m, 0))
+                               goto err;
+                       break;
+               default:
+                       abort();
+               }
+       }
+
+err:   F_CLR(ep, F_NOLOG);
+       return (1);
+}
+
+#if defined(DEBUG) && 0
+static void
+log_trace(sp, msg, rno, p)
+       SCR *sp;
+       char *msg;
+       recno_t rno;
+       u_char *p;
+{
+       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(&m, p + sizeof(u_char), sizeof(MARK));
+               TRACE(sp, "%lu: %s:    MARK: %u/%u\n", rno, msg, m.lno, m.cno);
+               break;
+       default:
+               abort();
+       }
+}
+#endif
diff --git a/usr.bin/vi/log.h b/usr.bin/vi/log.h
new file mode 100644 (file)
index 0000000..840cf85
--- /dev/null
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)log.h       8.3 (Berkeley) 12/28/93
+ */
+
+#define        LOG_NOTYPE              0
+#define        LOG_CURSOR_INIT         1
+#define        LOG_CURSOR_END          2
+#define        LOG_LINE_APPEND         3
+#define        LOG_LINE_DELETE         4
+#define        LOG_LINE_INSERT         5
+#define        LOG_LINE_RESET_F        6
+#define        LOG_LINE_RESET_B        7
+#define        LOG_MARK                8
+
+int    log_backward __P((SCR *, EXF *, MARK *));
+int    log_cursor __P((SCR *, EXF *));
+int    log_end __P((SCR *, EXF *));
+int    log_forward __P((SCR *, EXF *, MARK *));
+int    log_init __P((SCR *, EXF *));
+int    log_line __P((SCR *, EXF *, recno_t, u_int));
+int    log_mark __P((SCR *, EXF *, MARK *));
+int    log_setline __P((SCR *, EXF *));
diff --git a/usr.bin/vi/main.c b/usr.bin/vi/main.c
new file mode 100644 (file)
index 0000000..26d9983
--- /dev/null
@@ -0,0 +1,745 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     8.63 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+#include "tag.h"
+
+static int      exrc_isok __P((SCR *, char *, int));
+static void     gs_end __P((GS *));
+static GS      *gs_init __P((void));
+static void     h_hup __P((int));
+static void     h_term __P((int));
+static void     h_winch __P((int));
+static void     obsolete __P((char *[]));
+static void     usage __P((int));
+
+GS *__global_list;                     /* GLOBAL: List of screens. */
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       extern int optind;
+       extern char *optarg;
+       static int reenter;             /* STATIC: Re-entrancy check. */
+       struct sigaction act;
+       GS *gp;
+       FREF *frp;
+       SCR *sp;
+       u_int flags, saved_vi_mode;
+       int ch, eval, flagchk, readonly, silent, snapshot;
+       char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg;
+       char path[MAXPATHLEN];
+
+       /* Stop if indirecting through a NULL pointer. */
+       if (reenter++)
+               abort();
+
+       /* Set screen type and mode based on the program name. */
+       readonly = 0;
+       if ((myname = strrchr(*argv, '/')) == NULL)
+               myname = *argv;
+       else
+               ++myname;
+       if (!strcmp(myname, "ex") || !strcmp(myname, "nex"))
+               LF_INIT(S_EX);
+       else {
+               /* View is readonly. */
+               if (!strcmp(myname, "view"))
+                       readonly = 1;
+               LF_INIT(S_VI_CURSES);
+       }
+       saved_vi_mode = S_VI_CURSES;
+
+       /* Convert old-style arguments into new-style ones. */
+       obsolete(argv);
+
+       /* Parse the arguments. */
+       flagchk = '\0';
+       excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL;
+       silent = 0;
+       snapshot = 1;
+       while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF)
+               switch (ch) {
+               case 'c':               /* Run the command. */
+                       excmdarg = optarg;
+                       break;
+               case 'e':               /* Ex mode. */
+                       LF_CLR(S_SCREENS);
+                       LF_SET(S_EX);
+                       break;
+               case 'F':               /* No snapshot. */
+                       snapshot = 0;
+                       break;
+               case 'l':
+                       if (flagchk != '\0' && flagchk != 'l')
+                               errx(1,
+                                   "only one of -%c and -l may be specified.",
+                                   flagchk);
+                       flagchk = 'l';
+                       break;
+               case 'R':               /* Readonly. */
+                       readonly = 1;
+                       break;
+               case 'r':               /* Recover. */
+                       if (flagchk == 'r')
+                               errx(1,
+                                   "only one recovery file may be specified.");
+                       if (flagchk != '\0')
+                               errx(1,
+                                   "only one of -%c and -r may be specified.",
+                                   flagchk);
+                       flagchk = 'r';
+                       rec_f = optarg;
+                       break;
+               case 's':
+                       if (!LF_ISSET(S_EX))
+                               errx(1, "-s only applicable to ex.");
+                       silent = 1;
+                       break;
+               case 'T':               /* Trace. */
+                       trace_f = optarg;
+                       break;
+               case 't':               /* Tag. */
+                       if (flagchk == 't')
+                               errx(1,
+                                   "only one tag file may be specified.");
+                       if (flagchk != '\0')
+                               errx(1,
+                                   "only one of -%c and -t may be specified.",
+                                   flagchk);
+                       flagchk = 't';
+                       tag_f = optarg;
+                       break;
+               case 'v':               /* Vi mode. */
+                       LF_CLR(S_SCREENS);
+                       LF_SET(S_VI_CURSES);
+                       break;
+               case 'w':
+                       wsizearg = optarg;
+                       break;
+               case 'x':
+                       if (!strcmp(optarg, "aw")) {
+                               LF_CLR(S_SCREENS);
+                               LF_SET(S_VI_XAW);
+                               saved_vi_mode = S_VI_XAW;
+                               break;
+                       }
+                       /* FALLTHROUGH */
+               case '?':
+               default:
+                       usage(LF_ISSET(S_EX));
+               }
+       argc -= optind;
+       argv += optind;
+
+       /* Build and initialize the GS structure. */
+       __global_list = gp = gs_init();
+               
+       if (snapshot)
+               F_SET(gp, G_SNAPSHOT);
+
+       /* Build and initialize the first/current screen. */
+       if (screen_init(NULL, &sp, flags)) {
+               if (sp == NULL)
+                       goto err2;
+               goto err1;
+       }
+       sp->saved_vi_mode = saved_vi_mode;
+       CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
+
+       if (trace_f != NULL) {
+#ifdef DEBUG
+               if ((gp->tracefp = fopen(optarg, "w")) == NULL)
+                       err(1, "%s", optarg);
+               (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg);
+#else
+               msgq(sp, M_ERR, "-T support not compiled into this version.");
+#endif
+       }
+
+       if (set_window_size(sp, 0, 0))  /* Set the window size. */
+               goto err1;
+
+       if (opts_init(sp))              /* Options initialization. */
+               goto err1;
+       if (readonly)
+               O_SET(sp, O_READONLY);
+       if (silent) {
+               O_CLR(sp, O_AUTOPRINT);
+               O_CLR(sp, O_PROMPT);
+               O_CLR(sp, O_VERBOSE);
+               O_CLR(sp, O_WARN);
+               F_SET(sp, S_EXSILENT);
+       }
+       if (wsizearg != NULL) {
+               ARGS *av[2], a, b;
+               if (strtol(optarg, &p, 10) < 0 || *p)
+                       errx(1, "illegal window size -- %s", optarg);
+               (void)snprintf(path, sizeof(path), "window=%s", optarg);
+               a.bp = path;
+               a.len = strlen(path);
+               b.bp = NULL;
+               b.len = 0;
+               av[0] = &a;
+               av[1] = &b;
+               if (opts_set(sp, av))
+                        msgq(sp, M_ERR,
+                            "Unable to set command line window option");
+       }
+
+       /* Keymaps, special keys, must follow option initializations. */
+       if (term_init(sp))
+               goto err1;
+
+#ifdef DIGRAPHS
+       if (digraph_init(sp))           /* Digraph initialization. */
+               goto err1;
+#endif
+
+       /*
+        * Source the system, environment, ~user and local .exrc values.
+        * Vi historically didn't check ~user/.exrc if the environment
+        * variable EXINIT was set.  This is all done before the file is
+        * read in because things in the .exrc information can set, for
+        * example, the recovery directory.
+        *
+        * !!!
+        * While nvi can handle any of the options settings of historic vi,
+        * the converse is not true.  Since users are going to have to have
+        * files and environmental variables that work with both, we use nvi
+        * versions if they exist, otherwise the historic ones.
+        */
+       if (!silent) {
+               if (exrc_isok(sp, _PATH_SYSEXRC, 1))
+                       (void)ex_cfile(sp, NULL, _PATH_SYSEXRC);
+
+               /* Source the {N,}EXINIT environment variable. */
+               if ((p = getenv("NEXINIT")) != NULL ||
+                   (p = getenv("EXINIT")) != NULL)
+                       if ((p = strdup(p)) == NULL) {
+                               msgq(sp, M_SYSERR, NULL);
+                               goto err1;
+                       } else {
+                               (void)ex_icmd(sp, NULL, p, strlen(p));
+                               free(p);
+                       }
+               else if ((p = getenv("HOME")) != NULL && *p) {
+                       (void)snprintf(path,
+                           sizeof(path), "%s/%s", p, _PATH_NEXRC);
+                       if (exrc_isok(sp, path, 0))
+                               (void)ex_cfile(sp, NULL, path);
+                       else {
+                               (void)snprintf(path,
+                                   sizeof(path), "%s/%s", p, _PATH_EXRC);
+                               if (exrc_isok(sp, path, 0))
+                                       (void)ex_cfile(sp, NULL, path);
+                       }
+               }
+               /*
+                * !!!
+                * According to O'Reilly ("Learning the VI Editor", Fifth Ed.,
+                * May 1992, page 106), System V release 3.2 and later, has an
+                * option "[no]exrc", causing vi to not "read .exrc files in
+                * the current directory unless you first set the exrc option
+                * in your home directory's .exrc file".  Yeah, right.  Did
+                * someone actually believe that users would change their home
+                * .exrc file based on whether or not they wanted to source the
+                * current local .exrc?  Or that users would want ALL the local
+                * .exrc files on some systems, and none of them on others?
+                * I think not.
+                *
+                * Apply the same tests to local .exrc files that are applied
+                * to any other .exrc file.
+                */
+               if (exrc_isok(sp, _PATH_EXRC, 0))
+                       (void)ex_cfile(sp, NULL, _PATH_EXRC);
+       }
+
+       /* List recovery files if -l specified. */
+       if (flagchk == 'l')
+               exit(rcv_list(sp));
+
+       /* Use a tag file or recovery file if specified. */
+       if (tag_f != NULL && ex_tagfirst(sp, tag_f))
+               goto err1;
+       else if (rec_f != NULL && rcv_read(sp, rec_f))
+               goto err1;
+
+       /* Append any remaining arguments as file names. */
+       if (*argv != NULL)
+               for (; *argv != NULL; ++argv)
+                       if (file_add(sp, NULL, *argv, 0) == NULL)
+                               goto err1;
+
+       /*
+        * If no recovery or tag file, get an EXF structure.
+        * If no argv file, use a temporary file.
+        */
+       if (tag_f == NULL && rec_f == NULL) {
+               if ((frp = file_first(sp)) == NULL &&
+                   (frp = file_add(sp, NULL, NULL, 1)) == NULL)
+                       goto err1;
+               if (file_init(sp, frp, NULL, 0))
+                       goto err1;
+       }
+
+       /* Set up the argument pointer. */
+       sp->a_frp = sp->frp;
+
+       /*
+        * Initialize the signals.  Use sigaction(2), not signal(3), because
+        * we don't want to always restart system calls on 4BSD systems.  It
+        * would be nice in some cases to restart system calls, but SA_RESTART
+        * is a 4BSD extension so we can't use it.
+        *
+        * SIGWINCH, SIGHUP, SIGTERM:
+        *      Catch and set a global bit.
+        */
+       act.sa_handler = h_hup;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       (void)sigaction(SIGHUP, &act, NULL);
+       act.sa_handler = h_term;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       (void)sigaction(SIGTERM, &act, NULL);
+       act.sa_handler = h_winch;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       (void)sigaction(SIGWINCH, &act, NULL);
+
+       /*
+        * SIGQUIT:
+        *      Always ignore.
+        */
+       act.sa_handler = SIG_IGN;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       (void)sigaction(SIGQUIT, &act, NULL);
+
+       /*
+        * If there's an initial command, push it on the command stack.
+        * Historically, it was always an ex command, not vi in vi mode
+        * or ex in ex mode.  So, make it look like an ex command to vi.
+        */
+       if (excmdarg != NULL)
+               if (IN_EX_MODE(sp)) {
+                       if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0))
+                               goto err1;
+               } else if (IN_VI_MODE(sp)) {
+                       if (term_push(sp, "\n", 1, 0, 0))
+                               goto err1;
+                       if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0))
+                               goto err1;
+                       if (term_push(sp, ":", 1, 0, 0))
+                               goto err1;
+               }
+               
+       /* Vi reads from the terminal. */
+       if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) {
+               msgq(sp, M_ERR, "Vi's standard input must be a terminal.");
+               goto err1;
+       }
+
+       for (;;) {
+               if (sp->s_edit(sp, sp->ep))
+                       goto err2;
+
+               /*
+                * Edit the next screen on the display queue, or, move
+                * a screen from the hidden queue to the display queue.
+                */
+               if ((sp = __global_list->dq.cqh_first) ==
+                   (void *)&__global_list->dq)
+                       if ((sp = __global_list->hq.cqh_first) !=
+                           (void *)&__global_list->hq) {
+                               CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+                               CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+                       } else
+                               break;
+
+               /*
+                * The screen type may have changed -- reinitialize the
+                * functions in case it has.
+                */
+               switch (F_ISSET(sp, S_SCREENS)) {
+               case S_EX:
+                       if (sex_screen_init(sp))
+                               goto err2;
+                       break;
+               case S_VI_CURSES:
+                       if (svi_screen_init(sp))
+                               goto err2;
+                       break;
+               case S_VI_XAW:
+                       if (xaw_screen_init(sp))
+                               goto err2;
+                       break;
+               default:
+                       abort();
+               }
+       }
+
+       /*
+        * Two error paths.  The first means that something failed before
+        * we called a screen routine.  Swap the message pointers between
+        * the SCR and the GS, so messages get displayed.  The second is
+        * something failed in a screen.  NOTE: sp may be GONE when the
+        * screen returns, so only the gp can be trusted.
+        */
+       eval = 0;
+       if (0) {
+err1:          gp->msgq.lh_first = sp->msgq.lh_first;
+err2:          eval = 1;
+       }
+
+       gs_end(gp);
+
+       /*
+        * XXX
+        * Make absolutely sure that the modes are restored correctly.
+        *
+        * This should no longer be needed, and it's here to handle what I
+        * believe are SunOS/Solaris curses problems.  The problem is that
+        * for some unknown reason, when endwin() is called in the svi
+        * routines, it isn't resetting the terminal correctly.  I have not
+        * been able to figure it out, so this resets the terminal to the
+        * right modes regardless.  The problem is that, in most tty driver
+        * implementations, you can only reset the terminal modes once
+        * (changing from !ICANON to ICANON) without losing the re-parsing
+        * effect on the pending input.  This means that this "fix" will make
+        * other systems mess up characters typed after the quit command to
+        * vi but before vi actually exits.
+        */
+       if (F_ISSET(gp, G_ISFROMTTY) &&
+           tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios))
+               err(1, "tcsetattr");
+       exit(eval);
+}
+
+/*
+ * gs_init --
+ *     Build and initialize the GS structure.
+ */
+static GS *
+gs_init()
+{
+       GS *gp;
+       int fd;
+
+       CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
+       if (gp == NULL)
+               err(1, NULL);
+
+       CIRCLEQ_INIT(&gp->dq);
+       CIRCLEQ_INIT(&gp->hq);
+       LIST_INIT(&gp->msgq);
+
+       /* Structures shared by screens so stored in the GS structure. */
+       CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF));
+       if (gp->tty == NULL)
+               err(1, NULL);
+
+       LIST_INIT(&gp->cutq);
+       LIST_INIT(&gp->seqq);
+
+       /* Set a flag if we're reading from the tty. */
+       if (isatty(STDIN_FILENO))
+               F_SET(gp, G_ISFROMTTY);
+
+       /*
+        * XXX
+        * Set a flag and don't do terminal sets/resets if the input isn't
+        * from a tty.  Under all circumstances put reasonable things into
+        * the original_termios field, as some routines (seq.c:seq_save()
+        * and term.c:term_init()) want values for special characters.
+        */
+       if (F_ISSET(gp, G_ISFROMTTY)) {
+               if (tcgetattr(STDIN_FILENO, &gp->original_termios))
+                       err(1, "tcgetattr");
+       } else {
+               if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1)
+                       err(1, "%s", _PATH_TTY);
+               if (tcgetattr(fd, &gp->original_termios))
+                       err(1, "tcgetattr");
+               (void)close(fd);
+       }
+
+       return (gp);
+}
+
+
+/*
+ * gs_end --
+ *     End the GS structure.
+ */
+static void
+gs_end(gp)
+       GS *gp;
+{
+       MSG *mp;
+       SCR *sp;
+       char *tty;
+
+       /* Reset anything that needs resetting. */
+       if (gp->flags & G_SETMODE)                      /* O_MESG */
+               if ((tty = ttyname(STDERR_FILENO)) == NULL)
+                       warn("ttyname");
+               else if (chmod(tty, gp->origmode) < 0)
+                       warn("%s", tty);
+
+       /* Ring the bell if scheduled. */
+       if (F_ISSET(gp, G_BELLSCHED))
+               (void)fprintf(stderr, "\07");           /* \a */
+
+       /* If there are any remaining screens, flush their messages. */
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               for (mp = sp->msgq.lh_first;
+                   mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) 
+                       (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+       for (sp = __global_list->hq.cqh_first;
+           sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+               for (mp = sp->msgq.lh_first;
+                   mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) 
+                       (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+       /* Flush messages on the global queue. */
+       for (mp = gp->msgq.lh_first;
+           mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) 
+               (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf);
+
+       if (gp->special_key != NULL)
+               FREE(gp->special_key, MAX_FAST_KEY);
+
+       /*
+        * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
+        * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
+        */
+}
+
+/*
+ * h_hup --
+ *     Handle SIGHUP.
+ */
+static void
+h_hup(signo)
+       int signo;
+{
+       F_SET(__global_list, G_SIGHUP);
+
+       /*
+        * If we're asleep, just die.
+        *
+        * XXX
+        * This isn't right if the windows are independent.
+        */
+       if (F_ISSET(__global_list, G_SLEEPING))
+               rcv_hup();
+}
+
+/*
+ * h_term --
+ *     Handle SIGTERM.
+ */
+static void
+h_term(signo)
+       int signo;
+{
+       F_SET(__global_list, G_SIGTERM);
+
+       /*
+        * If we're asleep, just die.
+        *
+        * XXX
+        * This isn't right if the windows are independent.
+        */
+       if (F_ISSET(__global_list, G_SLEEPING))
+               rcv_term();
+}
+
+/*
+ * h_winch --
+ *     Handle SIGWINCH.
+ */
+static void
+h_winch(signo)
+       int signo;
+{
+       F_SET(__global_list, G_SIGWINCH);
+}
+
+/*
+ * exrc_isok --
+ *     Check a .exrc for source-ability.
+ */
+static int
+exrc_isok(sp, path, rootok)
+       SCR *sp;
+       char *path;
+       int rootok;
+{
+       struct stat sb;
+       uid_t uid;
+       char *emsg, buf[MAXPATHLEN];
+
+       /* Check for the file's existence. */
+       if (stat(path, &sb))
+               return (0);
+
+       /*
+        * !!!
+        * Historically, vi did not read the .exrc files if they were owned
+        * by someone other than the user, unless the undocumented option
+        * sourceany was set.  We don't support the sourceany option.  We
+        * check that the user (or root, for system files) owns the file and
+        * require that it not be writeable by anyone other than the owner.
+        */
+
+       /* Owned by the user or root. */
+       uid = getuid();
+       if (rootok) {
+               if (sb.st_uid != uid && sb.st_uid != 0) {
+                       emsg = "not owned by you or root";
+                       goto err;
+               }
+       } else
+               if (sb.st_uid != uid) {
+                       emsg = "not owned by you";
+                       goto err;
+               }
+
+       /* Not writeable by anyone but the owner. */
+       if (sb.st_mode & (S_IWGRP | S_IWOTH)) {
+               emsg = "writeable by a user other than the owner";
+err:           if (strchr(path, '/') == NULL &&
+                   getcwd(buf, sizeof(buf)) != NULL)
+                       msgq(sp, M_ERR,
+                           "%s/%s: not sourced: %s.", buf, path, emsg);
+               else
+                       msgq(sp, M_ERR,
+                           "%s: not sourced: %s.", path, emsg);
+               return (0);
+       }
+       return (1);
+}
+
+static void
+obsolete(argv)
+       char *argv[];
+{
+       size_t len;
+       char *p, *myname;
+
+       /*
+        * Translate old style arguments into something getopt will like.
+        * Make sure it's not text space memory, because ex changes the
+        * strings.
+        *      Change "+" into "-c$".
+        *      Change "+<anything else>" into "-c<anything else>".
+        *      Change "-" into "-s"
+        *      Change "-r" into "-l"
+        */
+       for (myname = argv[0]; *++argv;)
+               if (argv[0][0] == '+') {
+                       if (argv[0][1] == '\0') {
+                               MALLOC_NOMSG(NULL, argv[0], char *, 4);
+                               if (argv[0] == NULL)
+                                       err(1, NULL);
+                               (void)strcpy(argv[0], "-c$");
+                       } else  {
+                               p = argv[0];
+                               len = strlen(argv[0]);
+                               MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
+                               if (argv[0] == NULL)
+                                       err(1, NULL);
+                               argv[0][0] = '-';
+                               argv[0][1] = 'c';
+                               (void)strcpy(argv[0] + 2, p + 1);
+                       }
+               } else if (argv[0][0] == '-') {
+                       if (argv[0][1] == 'r') {
+                               if (argv[0][2] == '\0' && argv[1] == NULL)
+                                       argv[0][1] = 'l';
+                       } else if (argv[0][1] == '\0') {
+                               MALLOC_NOMSG(NULL, argv[0], char *, 3);
+                               if (argv[0] == NULL)
+                                       err(1, NULL);
+                               (void)strcpy(argv[0], "-s");
+                       }
+               }
+}
+
+static void
+usage(is_ex)
+       int is_ex;
+{
+#define        EX_USAGE \
+       "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
+#define        VI_USAGE \
+       "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
+
+       (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE);
+       exit(1);
+}
diff --git a/usr.bin/vi/mark.c b/usr.bin/vi/mark.c
new file mode 100644 (file)
index 0000000..c510a22
--- /dev/null
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mark.c     8.12 (Berkeley) 12/27/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+
+static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T));
+
+/*
+ * Marks are maintained in a key sorted doubly 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 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.
+ */
+int
+mark_init(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       MARK *mp;
+
+       /*
+        * Make sure the marks have been set up.  If they
+        * haven't, do so, and create the absolute mark.
+        */
+       MALLOC_RET(sp, mp, MARK *, sizeof(MARK));
+       mp->lno = 1;
+       mp->cno = 0;
+       mp->name = ABSMARK1;
+       mp->flags = 0;
+       LIST_INSERT_HEAD(&ep->marks, mp, q);
+       return (0);
+}
+
+/*
+ * mark_end --
+ *     Free up the marks.
+ */
+int
+mark_end(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       MARK *mp;
+
+       while ((mp = ep->marks.lh_first) != NULL) {
+               LIST_REMOVE(mp, q);
+               FREE(mp, sizeof(MARK));
+       }
+       return (0);
+}
+
+/*
+ * mark_get --
+ *     Get the location referenced by a mark.
+ */
+MARK *
+mark_get(sp, ep, key)
+       SCR *sp;
+       EXF *ep;
+       ARG_CHAR_T key;
+{
+       MARK *mp;
+       size_t len;
+       char *p;
+
+       if (key == ABSMARK2)
+               key = ABSMARK1;
+
+       mp = mark_find(sp, ep, key);
+       if (mp == NULL || mp->name != key) {
+               msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key));
+                return (NULL);
+       }
+       if (F_ISSET(mp, MARK_DELETED)) {
+               msgq(sp, M_BERR,
+                   "Mark %s: the line was deleted.", charname(sp, key));
+                return (NULL);
+       }
+       if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL ||
+           mp->cno > len || mp->cno == len && len != 0) {
+               msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.",
+                   charname(sp, key));
+               return (NULL);
+       }
+       return (mp);
+}
+
+/*
+ * mark_set --
+ *     Set the location referenced by a mark.
+ */
+int
+mark_set(sp, ep, key, value, userset)
+       SCR *sp;
+       EXF *ep;
+       ARG_CHAR_T key;
+       MARK *value;
+       int userset;
+{
+       MARK *mp, *mt;
+
+       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.
+        */
+       mp = mark_find(sp, ep, key);
+       if (mp == NULL || mp->name != key) {
+               MALLOC_RET(sp, mt, MARK *, sizeof(MARK));
+               if (mp == NULL) {
+                       LIST_INSERT_HEAD(&ep->marks, mt, q);
+               } else
+                       LIST_INSERT_AFTER(mp, mt, q);
+               mp = mt;
+       } else if (!userset &&
+           !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET))
+               return (0);
+
+       mp->lno = value->lno;
+       mp->cno = value->cno;
+       mp->name = key;
+       mp->flags = userset ? MARK_USERSET : 0;
+       return (0);
+}
+
+/*
+ * mark_find --
+ *     Find the requested mark, or, the slot immediately before
+ *     where it would go.
+ */
+static MARK *
+mark_find(sp, ep, key)
+       SCR *sp;
+       EXF *ep;
+       ARG_CHAR_T key;
+{
+       MARK *mp, *lastmp;
+
+       /*
+        * Return the requested mark or the slot immediately before
+        * where it should go.
+        */
+       for (lastmp = NULL, mp = ep->marks.lh_first;
+           mp != NULL; lastmp = mp, mp = mp->q.le_next)
+               if (mp->name >= key)
+                       return (mp->name == key ? mp : lastmp);
+       return (lastmp);
+}
+
+/*
+ * mark_insdel --
+ *     Update the marks based on an insertion or deletion.
+ */
+void
+mark_insdel(sp, ep, op, lno)
+       SCR *sp;
+       EXF *ep;
+       enum operation op;
+       recno_t lno;
+{
+       MARK *mp;
+
+       switch (op) {
+       case LINE_APPEND:
+               return;
+       case LINE_DELETE:
+               for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
+                       if (mp->lno >= lno)
+                               if (mp->lno == lno) {
+                                       F_SET(mp, MARK_DELETED);
+                                       (void)log_mark(sp, ep, mp);
+                               } else
+                                       --mp->lno;
+               return;
+       case LINE_INSERT:
+               for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
+                       if (mp->lno >= lno)
+                               ++mp->lno;
+               return;
+       case LINE_RESET:
+               return;
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/mark.h b/usr.bin/vi/mark.h
new file mode 100644 (file)
index 0000000..9c28151
--- /dev/null
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)mark.h      8.5 (Berkeley) 12/27/93
+ */
+
+/*
+ * The MARK structure defines a position in the file.  Because of the different
+ * interfaces used by the db(3) package, curses, and users, the line number is
+ * 1 based, while the column number is 0 based.  Additionally, it is known that
+ * the out-of-band line number is less than any legal line number.  The line
+ * number is of type recno_t, as that's the underlying type of the database.
+ * The column number is of type size_t, guaranteeing that we can malloc a line.
+ */
+struct _mark {
+       LIST_ENTRY(_mark) q;            /* Linked list of marks. */
+#define        OOBLNO          0               /* Out-of-band line number. */
+       recno_t lno;                    /* Line number. */
+       size_t  cno;                    /* Column number. */
+       CHAR_T  name;                   /* Mark name. */
+
+#define        MARK_DELETED    0x01            /* Mark was deleted. */
+#define        MARK_USERSET    0x02            /* User set this mark. */
+       u_char  flags;
+};
+
+#define        ABSMARK1        '\''            /* Absolute mark name. */
+#define        ABSMARK2        '`'             /* Absolute mark name. */
+
+/* Mark routines. */
+int     mark_end __P((SCR *, EXF *));
+MARK   *mark_get __P((SCR *, EXF *, ARG_CHAR_T));
+int     mark_init __P((SCR *, EXF *));
+void    mark_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int     mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int));
diff --git a/usr.bin/vi/mem.h b/usr.bin/vi/mem.h
new file mode 100644 (file)
index 0000000..0f8fac4
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)mem.h       8.2 (Berkeley) 12/19/93
+ */
+
+/* Increase the size of a malloc'd buffer.  Two versions, one that
+ * returns, one that jumps to an error label.
+ */
+#define        BINC_GOTO(sp, lp, llen, nlen) {                                 \
+       if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen))            \
+               goto binc_err;                                          \
+}
+#define        BINC_RET(sp, lp, llen, nlen) {                                  \
+       if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen))            \
+               return (1);                                             \
+}
+
+/*
+ * 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, bp, blen, nlen) {                            \
+       GS *__gp = (sp)->gp;                                            \
+       if (F_ISSET(__gp, G_TMP_INUSE)) {                               \
+               bp = NULL;                                              \
+               blen = 0;                                               \
+               BINC_GOTO(sp, bp, blen, nlen);                          \
+       } else {                                                        \
+               BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen);      \
+               bp = __gp->tmp_bp;                                      \
+               blen = __gp->tmp_blen;                                  \
+               F_SET(__gp, G_TMP_INUSE);                               \
+       }                                                               \
+}
+#define        GET_SPACE_RET(sp, bp, blen, nlen) {                             \
+       GS *__gp = (sp)->gp;                                            \
+       if (F_ISSET(__gp, G_TMP_INUSE)) {                               \
+               bp = NULL;                                              \
+               blen = 0;                                               \
+               BINC_RET(sp, bp, blen, nlen);                           \
+       } else {                                                        \
+               BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen);       \
+               bp = __gp->tmp_bp;                                      \
+               blen = __gp->tmp_blen;                                  \
+               F_SET(__gp, G_TMP_INUSE);                               \
+       }                                                               \
+}
+
+/*
+ * 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, bp, blen, nlen) {                            \
+       GS *__gp = (sp)->gp;                                            \
+       if (bp == __gp->tmp_bp) {                                       \
+               F_CLR(__gp, G_TMP_INUSE);                               \
+               BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen);      \
+               bp = __gp->tmp_bp;                                      \
+               blen = __gp->tmp_blen;                                  \
+               F_SET(__gp, G_TMP_INUSE);                               \
+       } else                                                          \
+               BINC_GOTO(sp, bp, blen, nlen);                          \
+}
+#define        ADD_SPACE_RET(sp, bp, blen, nlen) {                             \
+       GS *__gp = (sp)->gp;                                            \
+       if (bp == __gp->tmp_bp) {                                       \
+               F_CLR(__gp, G_TMP_INUSE);                               \
+               BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen);       \
+               bp = __gp->tmp_bp;                                      \
+               blen = __gp->tmp_blen;                                  \
+               F_SET(__gp, G_TMP_INUSE);                               \
+       } else                                                          \
+               BINC_RET(sp, bp, blen, nlen);                           \
+}
+
+/* Free memory, optionally making pointers unusable. */
+#ifdef DEBUG
+#define        FREE(p, sz) {                                                   \
+       memset(p, 0xff, sz);                                            \
+       free(p);                                                        \
+}
+#else
+#define        FREE(p, sz)     free(p);
+#endif
+
+/* Free a GET_SPACE returned buffer. */
+#define        FREE_SPACE(sp, bp, blen) {                                      \
+       if (bp == sp->gp->tmp_bp)                                       \
+               F_CLR(sp->gp, G_TMP_INUSE);                             \
+       else                                                            \
+               FREE(bp, blen);                                         \
+}
+
+/*
+ * Malloc a buffer, casting the return pointer.  Various versions.
+ *
+ * !!!
+ * The cast should be unnecessary, malloc(3) and friends return void *'s,
+ * which is all we need.  However, some systems that nvi needs to run on
+ * don't do it right yet, resulting in the compiler printing out roughly
+ * a million warnings.  After awhile, it seemed easier to put the casts
+ * in instead of explaining it all the time.
+ */
+#define        CALLOC_NOMSG(sp, p, cast, nmemb, size) {                        \
+       p = (cast)calloc(nmemb, size);                                  \
+}
+#define        CALLOC(sp, p, cast, nmemb, size) {                              \
+       if ((p = (cast)calloc(nmemb, size)) == NULL)                    \
+               msgq(sp, M_SYSERR, NULL);                               \
+}
+#define        CALLOC_RET(sp, p, cast, nmemb, size) {                          \
+       if ((p = (cast)calloc(nmemb, size)) == NULL) {                  \
+               msgq(sp, M_SYSERR, NULL);                               \
+               return (1);                                             \
+       }                                                               \
+}
+#define        MALLOC_NOMSG(sp, p, cast, size) {                               \
+       p = (cast)malloc(size);                                         \
+}
+#define        MALLOC(sp, p, cast, size) {                                     \
+       if ((p = (cast)malloc(size)) == NULL)                           \
+               msgq(sp, M_SYSERR, NULL);                               \
+}
+#define        MALLOC_RET(sp, p, cast, size) {                                 \
+       if ((p = (cast)malloc(size)) == NULL) {                         \
+               msgq(sp, M_SYSERR, NULL);                               \
+               return (1);                                             \
+       }                                                               \
+}
+#define        REALLOC(sp, p, cast, size) {                                    \
+       if ((p = (cast)realloc(p, size)) == NULL)                       \
+               msgq(sp, M_SYSERR, NULL);                               \
+}
+
+int    binc __P((SCR *, void *, size_t *, size_t));
diff --git a/usr.bin/vi/msg.h b/usr.bin/vi/msg.h
new file mode 100644 (file)
index 0000000..6b20bb4
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)msg.h       8.8 (Berkeley) 11/18/93
+ */
+
+/*
+ * M_BERR      -- Error: ring a bell if O_VERBOSE not set, else
+ *                display in inverse video.
+ * M_ERR       -- Error: display in inverse video.
+ * M_INFO      -- Info: display in normal video.
+ * M_SYSERR    -- M_ERR, but use standard error message.
+ * M_VINFO     -- Info: display only if O_VERBOSE set.
+ *
+ * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the
+ * error messages shorter.  In this version, O_TERSE has no effect
+ * and O_VERBOSE results in informational displays about common
+ * errors.
+ */
+enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO };
+
+typedef struct _msgh MSGH;     /* MESG list head structure. */
+LIST_HEAD(_msgh, _msg);
+
+struct _msg {
+       LIST_ENTRY(_msg) q;     /* Linked list of messages. */
+       char    *mbuf;          /* Message buffer. */
+       size_t   blen;          /* Message buffer length. */
+       size_t   len;           /* Message length. */
+
+#define        M_EMPTY         0x01    /* No message. */
+#define        M_INV_VIDEO     0x02    /* Inverse video. */
+       u_int    flags;         /* Flags. */
+};
+
+/* Messages. */
+void   msg_app __P((GS *, SCR *, int, char *, size_t));
+int    msg_rpt __P((SCR *, int));
+void   msgq __P((SCR *, enum msgtype, const char *, ...));
diff --git a/usr.bin/vi/options.c b/usr.bin/vi/options.c
new file mode 100644 (file)
index 0000000..9c74160
--- /dev/null
@@ -0,0 +1,822 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options.c  8.36 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+static int              opts_abbcmp __P((const void *, const void *));
+static int              opts_cmp __P((const void *, const void *));
+static OPTLIST const   *opts_prefix __P((char *));
+static int              opts_print __P((SCR *, OPTLIST const *, OPTION *));
+
+/*
+ * 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.
+ */
+static OPTLIST const optlist[] = {
+/* O_ALTWERASE   4.4BSD */
+       {"altwerase",   f_altwerase,    OPT_0BOOL,      0},
+/* O_AUTOINDENT            4BSD */
+       {"autoindent",  NULL,           OPT_0BOOL,      0},
+/* O_AUTOPRINT     4BSD */
+       {"autoprint",   NULL,           OPT_1BOOL,      0},
+/* O_AUTOWRITE     4BSD */
+       {"autowrite",   NULL,           OPT_0BOOL,      0},
+/* O_BEAUTIFY      4BSD */
+       {"beautify",    NULL,           OPT_0BOOL,      0},
+/* O_COLUMNS     4.4BSD */
+       {"columns",     f_columns,      OPT_NUM,        OPT_NOSAVE},
+/* O_COMMENT     4.4BSD */
+       {"comment",     NULL,           OPT_0BOOL,      0},
+/* O_DIGRAPH     XXX: Elvis */
+       {"digraph",     NULL,           OPT_0BOOL,      0},
+/* O_DIRECTORY     4BSD */
+       {"directory",   NULL,           OPT_STR,        0},
+/* O_EDCOMPATIBLE   4BSD */
+       {"edcompatible",NULL,           OPT_0BOOL,      0},
+/* O_ERRORBELLS            4BSD */
+       {"errorbells",  NULL,           OPT_0BOOL,      0},
+/* O_EXRC      System V (undocumented) */
+       {"exrc",        NULL,           OPT_0BOOL,      0},
+/* O_EXTENDED    4.4BSD */
+       {"extended",    NULL,           OPT_0BOOL,      0},
+/* O_FLASH         HPUX */
+       {"flash",       NULL,           OPT_1BOOL,      0},
+/* O_HARDTABS      4BSD */
+       {"hardtabs",    NULL,           OPT_NUM,        0},
+/* O_IGNORECASE            4BSD */
+       {"ignorecase",  NULL,           OPT_0BOOL,      0},
+/* O_KEYTIME     4.4BSD */
+       {"keytime",     f_keytime,      OPT_NUM,        0},
+/* O_LEFTRIGHT   4.4BSD */
+       {"leftright",   f_leftright,    OPT_0BOOL,      0},
+/* O_LINES       4.4BSD */
+       {"lines",       f_lines,        OPT_NUM,        OPT_NOSAVE},
+/* O_LISP          4BSD */
+       {"lisp",        f_lisp,         OPT_0BOOL,      0},
+/* O_LIST          4BSD */
+       {"list",        f_list,         OPT_0BOOL,      0},
+/* O_MAGIC         4BSD */
+       {"magic",       NULL,           OPT_1BOOL,      0},
+/* O_MATCHTIME   4.4BSD */
+       {"matchtime",   f_matchtime,    OPT_NUM,        0},
+/* O_MESG          4BSD */
+       {"mesg",        f_mesg,         OPT_1BOOL,      0},
+/* O_MODELINE      4BSD */
+       {"modeline",    f_modeline,     OPT_0BOOL,      0},
+/* O_NUMBER        4BSD */
+       {"number",      f_number,       OPT_0BOOL,      0},
+/* O_OPEN          4BSD */
+       {"open",        NULL,           OPT_1BOOL,      0},
+/* O_OPTIMIZE      4BSD */
+       {"optimize",    f_optimize,     OPT_1BOOL,      0},
+/* O_PARAGRAPHS            4BSD */
+       {"paragraphs",  f_paragraph,    OPT_STR,        0},
+/* O_PROMPT        4BSD */
+       {"prompt",      NULL,           OPT_1BOOL,      0},
+/* O_READONLY      4BSD (undocumented) */
+       {"readonly",    f_readonly,     OPT_0BOOL,      0},
+/* O_RECDIR      4.4BSD */
+       {"recdir",      NULL,           OPT_STR,        0},
+/* O_REDRAW        4BSD */
+       {"redraw",      NULL,           OPT_0BOOL,      0},
+/* O_REMAP         4BSD */
+       {"remap",       NULL,           OPT_1BOOL,      0},
+/* O_REPORT        4BSD */
+       {"report",      NULL,           OPT_NUM,        OPT_NOSTR},
+/* O_RULER       4.4BSD */
+       {"ruler",       f_ruler,        OPT_0BOOL,      0},
+/* O_SCROLL        4BSD */
+       {"scroll",      NULL,           OPT_NUM,        0},
+/* O_SECTIONS      4BSD */
+       {"sections",    f_section,      OPT_STR,        0},
+/* O_SHELL         4BSD */
+       {"shell",       NULL,           OPT_STR,        0},
+/* O_SHIFTWIDTH            4BSD */
+       {"shiftwidth",  f_shiftwidth,   OPT_NUM,        0},
+/* O_SHOWDIRTY   4.4BSD */
+       {"showdirty",   NULL,           OPT_0BOOL,      0},
+/* O_SHOWMATCH     4BSD */
+       {"showmatch",   NULL,           OPT_0BOOL,      0},
+/* O_SHOWMODE    4.4BSD */
+       {"showmode",    NULL,           OPT_0BOOL,      0},
+/* O_SIDESCROLL          4.4BSD */
+       {"sidescroll",  f_sidescroll,   OPT_NUM,        0},
+/* O_SLOWOPEN      4BSD  */
+       {"slowopen",    NULL,           OPT_0BOOL,      0},
+/* O_SOURCEANY     4BSD (undocumented) */
+       {"sourceany",   f_sourceany,    OPT_0BOOL,      0},
+/* O_TABSTOP       4BSD */
+       {"tabstop",     f_tabstop,      OPT_NUM,        0},
+/* O_TAGLENGTH     4BSD */
+       {"taglength",   NULL,           OPT_NUM,        OPT_NOSTR},
+/* O_TAGS          4BSD */
+       {"tags",        f_tags,         OPT_STR,        0},
+/* O_TERM          4BSD */
+       {"term",        f_term,         OPT_STR,        OPT_NOSAVE},
+/* O_TERSE         4BSD */
+       {"terse",       NULL,           OPT_0BOOL,      0},
+/* O_TIMEOUT       4BSD (undocumented) */
+       {"timeout",     NULL,           OPT_1BOOL,      0},
+/* O_TTYWERASE   4.4BSD */
+       {"ttywerase",   f_ttywerase,    OPT_0BOOL,      0},
+/* O_VERBOSE     4.4BSD */
+       {"verbose",     NULL,           OPT_0BOOL,      0},
+/* O_W1200         4BSD */
+       {"w1200",       f_w1200,        OPT_NUM,        OPT_NEVER},
+/* O_W300          4BSD */
+       {"w300",        f_w300,         OPT_NUM,        OPT_NEVER},
+/* O_W9600         4BSD */
+       {"w9600",       f_w9600,        OPT_NUM,        OPT_NEVER},
+/* O_WARN          4BSD */
+       {"warn",        NULL,           OPT_1BOOL,      0},
+/* O_WINDOW        4BSD */
+       {"window",      f_window,       OPT_NUM,        0},
+/* O_WRAPMARGIN            4BSD */
+       {"wrapmargin",  f_wrapmargin,   OPT_NUM,        OPT_NOSTR},
+/* O_WRAPSCAN      4BSD */
+       {"wrapscan",    NULL,           OPT_1BOOL,      0},
+/* O_WRITEANY      4BSD */
+       {"writeany",    NULL,           OPT_0BOOL,      0},
+       {NULL},
+};
+
+typedef struct abbrev {
+        char *name;
+        int offset;
+} OABBREV;
+
+static OABBREV const abbrev[] = {
+       {"ai",          O_AUTOINDENT},          /*     4BSD */
+       {"ap",          O_AUTOPRINT},           /*     4BSD */
+       {"aw",          O_AUTOWRITE},           /*     4BSD */
+       {"bf",          O_BEAUTIFY},            /*     4BSD */
+       {"co",          O_COLUMNS},             /*   4.4BSD */
+       {"dir",         O_DIRECTORY},           /*     4BSD */
+       {"eb",          O_ERRORBELLS},          /*     4BSD */
+       {"ed",          O_EDCOMPATIBLE},        /*     4BSD (undocumented) */
+       {"ex",          O_EXRC},                /* System V (undocumented) */
+       {"ht",          O_HARDTABS},            /*     4BSD */
+       {"ic",          O_IGNORECASE},          /*     4BSD */
+       {"li",          O_LINES},               /*   4.4BSD */
+       {"modelines",   O_MODELINE},            /*     HPUX */
+       {"nu",          O_NUMBER},              /*     4BSD */
+       {"opt",         O_OPTIMIZE},            /*     4BSD */
+       {"para",        O_PARAGRAPHS},          /*     4BSD */
+       {"re",          O_REDRAW},              /* O'Reilly */
+       {"ro",          O_READONLY},            /*     4BSD (undocumented) */
+       {"scr",         O_SCROLL},              /*     4BSD (undocumented) */
+       {"sect",        O_SECTIONS},            /* O'Reilly */
+       {"sh",          O_SHELL},               /*     4BSD */
+       {"slow",        O_SLOWOPEN},            /*     4BSD */
+       {"sm",          O_SHOWMATCH},           /*     4BSD */
+       {"sw",          O_SHIFTWIDTH},          /*     4BSD */
+       {"tag",         O_TAGS},                /*     4BSD (undocumented) */
+       {"tl",          O_TAGLENGTH},           /*     4BSD */
+       {"to",          O_TIMEOUT},             /*     4BSD (undocumented) */
+       {"ts",          O_TABSTOP},             /*     4BSD */
+       {"tty",         O_TERM},                /*     4BSD (undocumented) */
+       {"ttytype",     O_TERM},                /*     4BSD (undocumented) */
+       {"w",           O_WINDOW},              /* O'Reilly */
+       {"wa",          O_WRITEANY},            /*     4BSD */
+       {"wi",          O_WINDOW},              /*     4BSD (undocumented) */
+       {"wm",          O_WRAPMARGIN},          /*     4BSD */
+       {"ws",          O_WRAPSCAN},            /*     4BSD */
+       {NULL},
+};
+
+/*
+ * opts_init --
+ *     Initialize some of the options.  Since the user isn't really
+ *     "setting" these variables, don't set their OPT_SET bits.
+ */
+int
+opts_init(sp)
+       SCR *sp;
+{
+       ARGS *argv[2], a, b;
+       OPTLIST const *op;
+       u_long v;
+       int cnt;
+       char *s, b1[1024];
+
+       a.bp = b1;
+       a.len = 0;
+       b.bp = NULL;
+       b.len = 0;
+       argv[0] = &a;
+       argv[1] = &b;
+
+#define        SET_DEF(opt, str) {                                             \
+       if (str != b1)          /* GCC puts strings in text-space. */   \
+               (void)strcpy(b1, str);                                  \
+       a.len = strlen(b1);                                             \
+       if (opts_set(sp, argv)) {                                       \
+               msgq(sp, M_ERR,                                         \
+                   "Unable to set default %s option", optlist[opt]);   \
+               return (1);                                             \
+       }                                                               \
+       F_CLR(&sp->opts[opt], OPT_SET);                                 \
+}
+       /* Set default values. */
+       for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
+               if (op->type == OPT_0BOOL)
+                       O_CLR(sp, cnt);
+               else if (op->type == OPT_1BOOL)
+                       O_SET(sp, cnt);
+                       
+       /*
+        * !!!
+        * 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)snprintf(b1, sizeof(b1), "directory=%s",
+           (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
+       SET_DEF(O_DIRECTORY, b1);
+       SET_DEF(O_KEYTIME, "keytime=6");
+       SET_DEF(O_MATCHTIME, "matchtime=7");
+       SET_DEF(O_REPORT, "report=5");
+       SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
+       (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
+       SET_DEF(O_RECDIR, b1);
+       (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2);
+       SET_DEF(O_SCROLL, b1);
+       SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
+       (void)snprintf(b1, sizeof(b1),
+           "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
+       SET_DEF(O_SHELL, b1);
+       SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
+       SET_DEF(O_SIDESCROLL, "sidescroll=16");
+       SET_DEF(O_TABSTOP, "tabstop=8");
+       (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
+       SET_DEF(O_TAGS, b1);
+       (void)snprintf(b1, sizeof(b1),
+           "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s);
+       SET_DEF(O_TERM, b1);
+
+       /*
+        * The default window option values are:
+        *              8 if baud rate <=  600
+        *             16 if baud rate <= 1200
+        *      LINES - 1 if baud rate  > 1200
+        */
+       v = baud_from_bval(sp);
+       if (v <= 600)
+               v = 8;
+       else if (v <= 1200)
+               v = 16;
+       else
+               v = O_VAL(sp, O_LINES) - 1;
+       (void)snprintf(b1, sizeof(b1), "window=%lu", v);
+       SET_DEF(O_WINDOW, b1);
+
+       SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
+
+       /*
+        * By default, the historic vi always displayed information
+        * about two options, redraw and term.  Term seems sufficient.
+        */
+       F_SET(&sp->opts[O_TERM], OPT_SET);
+       return (0);
+}
+
+/*
+ * opts_set --
+ *     Change the values of one or more options.
+ */
+int
+opts_set(sp, argv)
+       SCR *sp;
+       ARGS *argv[];
+{
+       enum optdisp disp;
+       OABBREV atmp, *ap;
+       OPTLIST const *op;
+       OPTLIST otmp;
+       OPTION *spo;
+       u_long value, turnoff;
+       int ch, offset, rval;
+       char *endp, *equals, *name, *p;
+       
+       disp = NO_DISPLAY;
+       for (rval = 0; (*argv)->len != 0; ++argv) {
+               /*
+                * The historic vi dumped the options for each occurrence of
+                * "all" in the set list.  Puhleeze.
+                */
+               if (!strcmp(argv[0]->bp, "all")) {
+                       disp = ALL_DISPLAY;
+                       continue;
+               }
+                       
+               /* Find equals sign or end of set, skipping backquoted chars. */
+               for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p)
+                       switch(ch) {
+                       case '=':
+                               equals = p;
+                               break;
+                       case '\\':
+                               /* Historic vi just used the backslash. */
+                               if (p[1] == '\0')
+                                       break;
+                               ++p;
+                               break;
+                       }
+
+               turnoff = 0;
+               op = NULL;
+               if (equals)
+                       *equals++ = '\0';
+
+               /* Check list of abbreviations. */
+               atmp.name = name;
+               if ((ap = bsearch(&atmp, abbrev,
+                   sizeof(abbrev) / sizeof(OABBREV) - 1,
+                   sizeof(OABBREV), opts_abbcmp)) != NULL) {
+                       op = optlist + ap->offset;
+                       goto found;
+               }
+
+               /* Check list of options. */
+               otmp.name = name;
+               if ((op = bsearch(&otmp, optlist,
+                   sizeof(optlist) / sizeof(OPTLIST) - 1,
+                   sizeof(OPTLIST), opts_cmp)) != NULL)
+                       goto found;
+
+               /* Try the name without any leading "no". */
+               if (name[0] == 'n' && name[1] == 'o') {
+                       turnoff = 1;
+                       name += 2;
+               } else
+                       goto prefix;
+
+               /* Check list of abbreviations. */
+               atmp.name = name;
+               if ((ap = bsearch(&atmp, abbrev,
+                   sizeof(abbrev) / sizeof(OABBREV) - 1,
+                   sizeof(OABBREV), opts_abbcmp)) != NULL) {
+                       op = optlist + ap->offset;
+                       goto found;
+               }
+
+               /* Check list of options. */
+               otmp.name = name;
+               if ((op = bsearch(&otmp, optlist,
+                   sizeof(optlist) / sizeof(OPTLIST) - 1,
+                   sizeof(OPTLIST), opts_cmp)) != NULL)
+                       goto found;
+
+               /* Check for prefix match. */
+prefix:                op = opts_prefix(name);
+
+found:         if (op == NULL) {
+                       msgq(sp, M_ERR,
+                           "no %s option: 'set all' gives all option values",
+                           name);
+                       continue;
+               }
+
+               /* Find current option values. */
+               offset = op - optlist;
+               spo = sp->opts + offset;
+
+               /* Set name, value. */
+               switch (op->type) {
+               case OPT_0BOOL:
+               case OPT_1BOOL:
+                       if (equals) {
+                               msgq(sp, M_ERR,
+                                   "set: [no]%s option doesn't take a value",
+                                   name);
+                               break;
+                       }
+                       if (op->func != NULL) {
+                               if (op->func(sp, spo, NULL, turnoff)) {
+                                       rval = 1;
+                                       break;
+                               }
+                       } else if (turnoff)
+                               O_CLR(sp, offset);
+                       else
+                               O_SET(sp, offset);
+                       goto change;
+               case OPT_NUM:
+                       /*
+                        * !!!
+                        * Extension to historic vi.  If the OPT_NOSTR flag is
+                        * set, a numeric option may be turned off by using a
+                        * "no" prefix, e.g. "nowrapmargin".  (We assume that
+                        * setting the value to 0 turns a numeric option off.)
+                        */
+                       if (turnoff) {
+                               if (F_ISSET(op, OPT_NOSTR)) {
+                                       value = 0;
+                                       goto nostr;
+                               }
+                               msgq(sp, M_ERR,
+                                   "set: %s option isn't a boolean", name);
+                               break;
+                       }
+                       if (!equals) {
+                               if (!disp)
+                                       disp = SELECT_DISPLAY;
+                               F_SET(spo, OPT_SELECTED);
+                               break;
+                       }
+                       value = strtol(equals, &endp, 10);
+                       if (*endp && !isblank(*endp)) {
+                               msgq(sp, M_ERR,
+                                   "set %s: illegal number %s", name, equals);
+                               break;
+                       }
+nostr:                 if (op->func != NULL) {
+                               if (op->func(sp, spo, equals, value)) {
+                                       rval = 1;
+                                       break;
+                               }
+                       } else
+                               O_VAL(sp, offset) = value;
+                       goto change;
+               case OPT_STR:
+                       if (turnoff) {
+                               msgq(sp, M_ERR,
+                                   "set: %s option isn't a boolean", name);
+                               break;
+                       }
+                       if (!equals) {
+                               if (!disp)
+                                       disp = SELECT_DISPLAY;
+                               F_SET(spo, OPT_SELECTED);
+                               break;
+                       }
+                       if (op->func != NULL) {
+                               if (op->func(sp, spo, equals, (u_long)0)) {
+                                       rval = 1;
+                                       break;
+                               }
+                       } else {
+                               if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
+                                       free(O_STR(sp, offset));
+                               if ((O_STR(sp, offset) =
+                                   strdup(equals)) == NULL) {
+                                       msgq(sp, M_SYSERR, NULL);
+                                       rval = 1;
+                                       break;
+                               } else
+                                       F_SET(&sp->opts[offset], OPT_ALLOCATED);
+                       }
+change:                        if (sp->s_optchange != NULL)
+                               (void)sp->s_optchange(sp, offset);
+                       F_SET(&sp->opts[offset], OPT_SET);
+                       break;
+               default:
+                       abort();
+               }
+       }
+       if (disp)
+               opts_dump(sp, disp);
+       return (rval);
+}
+
+/*
+ * opts_dump --
+ *     List the current values of selected options.
+ */
+void
+opts_dump(sp, type)
+       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.
+        */
+#define        BOUND   6
+
+       /* Find a column width we can live with. */
+       for (cnt = 6; cnt > 1; --cnt) {
+               colwidth = (sp->cols - 1) / cnt & ~(BOUND - 1);
+               if (colwidth >= 10) {
+                       colwidth = (colwidth + BOUND) & ~(BOUND - 1);
+                       break;
+               }
+               colwidth = 0;
+       }
+
+       /* 
+        * Two passes.  First, get the set of options to list, entering them
+        * into the column list or the overflow list.  No error checking,
+        * since we know that at least one option (O_TERM) has the OPT_SET bit
+        * set.
+        */
+       for (b_num = s_num = 0, op = optlist; op->name; ++op) {
+               cnt = op - optlist;
+
+               /* If OPT_NEVER set, it's never displayed. */
+               if (F_ISSET(op, OPT_NEVER))
+                       continue;
+
+               switch (type) {
+               case ALL_DISPLAY:               /* Display all. */
+                       break;
+               case CHANGED_DISPLAY:           /* Display changed. */
+                       if (!F_ISSET(&sp->opts[cnt], OPT_SET))
+                               continue;
+                       break;
+               case SELECT_DISPLAY:            /* Display selected. */
+                       if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
+                               continue;
+                       break;
+               default:
+               case NO_DISPLAY:
+                       abort();
+                       /* NOTREACHED */
+               }
+               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:
+                       curlen += strlen(O_STR(sp, cnt)) + 3;
+                       break;
+               }
+               /* Offset by two so there's a gap. */
+               if (curlen < colwidth - 2)
+                       s_op[s_num++] = cnt;
+               else
+                       b_op[b_num++] = cnt;
+       }
+
+       numcols = (sp->cols - 1) / colwidth;
+       if (s_num > numcols) {
+               numrows = s_num / numcols;
+               if (s_num % numcols)
+                       ++numrows;
+       } else
+               numrows = 1;
+
+       for (row = 0; row < numrows;) {
+               for (base = row, col = 0; col < numcols; ++col) {
+                       cnt = opts_print(sp,
+                           &optlist[s_op[base]], &sp->opts[s_op[base]]);
+                       if ((base += numrows) >= s_num)
+                               break;
+                       (void)ex_printf(EXCOOKIE,
+                           "%*s", (int)(colwidth - cnt), "");
+               }
+               if (++row < numrows || b_num)
+                       (void)ex_printf(EXCOOKIE, "\n");
+       }
+
+       for (row = 0; row < b_num;) {
+               (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]);
+               if (++row < b_num)
+                       (void)ex_printf(EXCOOKIE, "\n");
+       }
+       (void)ex_printf(EXCOOKIE, "\n");
+}
+
+/*
+ * opts_print --
+ *     Print out an option.
+ */
+static int
+opts_print(sp, op, spo)
+       SCR *sp;
+       OPTLIST const *op;
+       OPTION *spo;
+{
+       int curlen, offset;
+
+       curlen = 0;
+       offset = op - optlist;
+       switch (op->type) {
+       case OPT_0BOOL:
+       case OPT_1BOOL:
+               curlen += ex_printf(EXCOOKIE,
+                   "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
+               break;
+       case OPT_NUM:
+               curlen += ex_printf(EXCOOKIE,
+                    "%s=%ld", op->name, O_VAL(sp, offset));
+               break;
+       case OPT_STR:
+               curlen += ex_printf(EXCOOKIE,
+                   "%s=\"%s\"", op->name, O_STR(sp, offset));
+               break;
+       }
+       return (curlen);
+}
+
+/*
+ * opts_save --
+ *     Write the current configuration to a file.
+ */
+int
+opts_save(sp, fp)
+       SCR *sp;
+       FILE *fp;
+{
+       OPTION *spo;
+       OPTLIST const *op;
+       int ch, cnt;
+       char *p;
+
+       for (spo = sp->opts, op = optlist; op->name; ++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 %s\n", op->name);
+                       else
+                               (void)fprintf(fp, "set no%s\n", op->name);
+                       break;
+               case OPT_NUM:
+                       (void)fprintf(fp,
+                           "set %s=%-3d\n", op->name, O_VAL(sp, cnt));
+                       break;
+               case OPT_STR:
+                       (void)fprintf(fp, "set ");
+                       for (p = op->name; (ch = *p) != '\0'; ++p) {
+                               if (isblank(ch))
+                                       (void)putc('\\', fp);
+                               (void)putc(ch, fp);
+                       }
+                       (void)putc('=', fp);
+                       for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
+                               if (isblank(ch))
+                                       (void)putc('\\', fp);
+                               (void)putc(ch, fp);
+                       }
+                       (void)putc('\n', fp);
+                       break;
+               }
+               if (ferror(fp)) {
+                       msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * opts_prefix --
+ *     Check to see if the name is the prefix of one (and only one)
+ *     option.  If so, return the option.
+ */
+static OPTLIST const *
+opts_prefix(name)
+       char *name;
+{
+       OPTLIST const *op, *save_op;
+       size_t len;
+
+       save_op = NULL;
+       len = strlen(name);
+       for (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 (save_op != NULL)
+                               return (NULL);
+                       save_op = op;
+               }
+       }
+       return (save_op);
+}
+
+static int
+opts_abbcmp(a, b)
+        const void *a, *b;
+{
+        return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
+}
+
+static int
+opts_cmp(a, b)
+        const void *a, *b;
+{
+        return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
+}
+
+/*
+ * opts_free --
+ *     Free all option strings
+ */
+void
+opts_free(sp)
+       SCR *sp;
+{
+       int cnt;
+       char *p;
+
+       for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
+               if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
+                       p = O_STR(sp, cnt);
+                       FREE(p, strlen(p) + 1);
+               }
+}
+
+/*
+ * opts_copy --
+ *     Copy a screen's OPTION array.
+ */
+int
+opts_copy(orig, sp)
+       SCR *orig, *sp;
+{
+       OPTION *op;
+       int cnt;
+
+       /* Copy most everything without change. */
+       memmove(sp->opts, orig->opts, sizeof(orig->opts));
+
+       /*
+        * Allocate copies of the strings -- keep trying to reallocate
+        * after ENOMEM failure, otherwise end up with more than one
+        * screen referencing the original memory.
+        */
+       for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
+               if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
+                   (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
+                       msgq(orig, M_SYSERR, NULL);
+                       return (1);
+               }
+       return (0);
+}
diff --git a/usr.bin/vi/options.h.stub b/usr.bin/vi/options.h.stub
new file mode 100644 (file)
index 0000000..f5f52b5
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)options.h.stub      8.14 (Berkeley) 12/20/93
+ */
+
+struct _option {
+       union {
+               u_long   val;           /* Value or boolean. */
+               char    *str;           /* String. */
+       } o_u;
+       size_t  len;                    /* String length. */
+
+#define        OPT_ALLOCATED   0x01            /* Allocated space. */
+#define        OPT_SELECTED    0x02            /* Selected for display. */
+#define        OPT_SET         0x04            /* Set (display for the user). */
+       u_char  flags;
+};
+
+struct _optlist {
+       char    *name;                  /* Name. */
+                                       /* Change function. */
+       int     (*func) __P((SCR *, OPTION *, char *, u_long));
+                                       /* Type of object. */   
+       enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type;
+
+#define        OPT_NEVER       0x01            /* Never display the option. */
+#define        OPT_NOSAVE      0x02            /* Mkexrc command doesn't save. */
+#define        OPT_NOSTR       0x04            /* String that takes a "no". */
+       u_int    flags;
+};
+
+/* Clear, set, test boolean options. */
+#define        O_SET(sp, o)            (sp)->opts[(o)].o_u.val = 1
+#define        O_CLR(sp, o)            (sp)->opts[(o)].o_u.val = 0
+#define        O_ISSET(sp, o)          ((sp)->opts[(o)].o_u.val)
+
+/* Get option values. */
+#define        O_LEN(sp, o)            (sp)->opts[(o)].len
+#define        O_STR(sp, o)            (sp)->opts[(o)].o_u.str
+#define        O_VAL(sp, o)            (sp)->opts[(o)].o_u.val
+
+/* Option routines. */
+enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
+
+int    opts_copy __P((SCR *, SCR *));
+void   opts_dump __P((SCR *, enum optdisp));
+void   opts_free __P((SCR *));
+int    opts_init __P((SCR *));
+int    opts_save __P((SCR *, FILE *));
+int    opts_set __P((SCR *, ARGS *[]));
+
+/* Per-option change routines. */
+int    f_altwerase __P((SCR *, OPTION *, char *, u_long));
+int    f_columns __P((SCR *, OPTION *, char *, u_long));
+int    f_keytime __P((SCR *, OPTION *, char *, u_long));
+int    f_leftright __P((SCR *, OPTION *, char *, u_long));
+int    f_lines __P((SCR *, OPTION *, char *, u_long));
+int    f_lisp __P((SCR *, OPTION *, char *, u_long));
+int    f_list __P((SCR *, OPTION *, char *, u_long));
+int    f_matchtime __P((SCR *, OPTION *, char *, u_long));
+int    f_mesg __P((SCR *, OPTION *, char *, u_long));
+int    f_modeline __P((SCR *, OPTION *, char *, u_long));
+int    f_number __P((SCR *, OPTION *, char *, u_long));
+int    f_optimize __P((SCR *, OPTION *, char *, u_long));
+int    f_paragraph __P((SCR *, OPTION *, char *, u_long));
+int    f_readonly __P((SCR *, OPTION *, char *, u_long));
+int    f_ruler __P((SCR *, OPTION *, char *, u_long));
+int    f_section __P((SCR *, OPTION *, char *, u_long));
+int    f_shiftwidth __P((SCR *, OPTION *, char *, u_long));
+int    f_sidescroll __P((SCR *, OPTION *, char *, u_long));
+int    f_sourceany __P((SCR *, OPTION *, char *, u_long));
+int    f_tabstop __P((SCR *, OPTION *, char *, u_long));
+int    f_tags __P((SCR *, OPTION *, char *, u_long));
+int    f_term __P((SCR *, OPTION *, char *, u_long));
+int    f_ttywerase __P((SCR *, OPTION *, char *, u_long));
+int    f_w1200 __P((SCR *, OPTION *, char *, u_long));
+int    f_w300 __P((SCR *, OPTION *, char *, u_long));
+int    f_w9600 __P((SCR *, OPTION *, char *, u_long));
+int    f_window __P((SCR *, OPTION *, char *, u_long));
+int    f_wrapmargin __P((SCR *, OPTION *, char *, u_long));
diff --git a/usr.bin/vi/options_f.c b/usr.bin/vi/options_f.c
new file mode 100644 (file)
index 0000000..efd596f
--- /dev/null
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options_f.c        8.25 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "tag.h"
+
+static int     opt_putenv __P((char *));
+
+#define        DECL(f)                                                         \
+       int                                                             \
+       f(sp, op, str, val)                                             \
+               SCR *sp;                                                \
+               OPTION *op;                                             \
+               char *str;                                              \
+               u_long val;
+#define        CALL(f)                                                         \
+       f(sp, op, str, val)
+
+#define        turnoff val
+
+DECL(f_altwerase)
+{
+       if (turnoff)
+               O_CLR(sp, O_ALTWERASE);
+       else {
+               O_SET(sp, O_ALTWERASE);
+               O_CLR(sp, O_TTYWERASE);
+       }
+       return (0);
+}
+
+DECL(f_ttywerase)
+{
+       if (turnoff)
+               O_CLR(sp, O_TTYWERASE);
+       else {
+               O_SET(sp, O_TTYWERASE);
+               O_CLR(sp, O_ALTWERASE);
+       }
+       return (0);
+}
+
+DECL(f_columns)
+{
+       char buf[25];
+
+       /* Validate the number. */
+       if (val < MINIMUM_SCREEN_COLS) {
+               msgq(sp, M_ERR, "Screen columns too small, less than %d.",
+                   MINIMUM_SCREEN_COLS);
+               return (1);
+       }
+       if (val < O_VAL(sp, O_SHIFTWIDTH)) {
+               msgq(sp, M_ERR,
+                   "Screen columns too small, less than shiftwidth.");
+               return (1);
+       }
+       if (val < O_VAL(sp, O_SIDESCROLL)) {
+               msgq(sp, M_ERR,
+                   "Screen columns too small, less than sidescroll.");
+               return (1);
+       }
+       if (val < O_VAL(sp, O_TABSTOP)) {
+               msgq(sp, M_ERR,
+                   "Screen columns too small, less than tabstop.");
+               return (1);
+       }
+       if (val < O_VAL(sp, O_WRAPMARGIN)) {
+               msgq(sp, M_ERR,
+                   "Screen columns too small, less than wrapmargin.");
+               return (1);
+       }
+#ifdef XXX_NOT_RIGHT
+       /*
+        * This has to be checked by reaching down into the screen code.
+        */
+       if (val < O_NUMBER_LENGTH) {
+               msgq(sp, M_ERR,
+                   "Screen columns too small, less than number option.");
+               return (1);
+       }
+#endif
+       /* Set the columns value in the environment for curses. */
+       (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val);
+       if (opt_putenv(buf))
+               return (1);
+
+       /* This is expensive, don't do it unless it's necessary. */
+       if (O_VAL(sp, O_COLUMNS) == val)
+               return (0);
+
+       /* Set the value. */
+       O_VAL(sp, O_COLUMNS) =  val;
+
+       F_SET(sp, S_RESIZE);
+       return (0);
+}
+
+DECL(f_keytime)
+{
+       O_VAL(sp, O_KEYTIME) = val;
+       return (0);
+}
+
+DECL(f_leftright)
+{
+       if (turnoff)
+               O_CLR(sp, O_LEFTRIGHT);
+       else
+               O_SET(sp, O_LEFTRIGHT);
+       F_SET(sp, S_REFORMAT | S_REDRAW);
+       return (0);
+}
+
+DECL(f_lines)
+{
+       char buf[25];
+
+       /* Validate the number. */
+       if (val < MINIMUM_SCREEN_ROWS) {
+               msgq(sp, M_ERR, "Screen lines too small, less than %d.",
+                   MINIMUM_SCREEN_ROWS);
+               return (1);
+       }
+
+       /* Set the rows value in the environment for curses. */
+       (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val);
+       if (opt_putenv(buf))
+               return (1);
+
+       /* This is expensive, don't do it unless it's necessary. */
+       if (O_VAL(sp, O_LINES) == val)
+               return (0);
+
+       /* Set the value. */
+       O_VAL(sp, O_LINES) = val;
+
+       /*
+        * If no window value set, set a new default window and,
+        * optionally, a new scroll value.
+        */
+       if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) {
+               O_VAL(sp, O_WINDOW) = val - 1;
+               if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+                       O_VAL(sp, O_SCROLL) = val / 2;
+       }
+
+       F_SET(sp, S_RESIZE);
+       return (0);
+}
+
+DECL(f_lisp)
+{
+       msgq(sp, M_ERR, "The lisp option is not implemented.");
+       return (0);
+}
+
+DECL(f_list)
+{
+       if (turnoff)
+               O_CLR(sp, O_LIST);
+       else
+               O_SET(sp, O_LIST);
+
+       F_SET(sp, S_REFORMAT | S_REDRAW);
+       return (0);
+}
+
+DECL(f_matchtime)
+{
+       O_VAL(sp, O_MATCHTIME) = val;
+       return (0);
+}
+
+DECL(f_mesg)
+{
+       struct stat sb;
+       char *tty;
+
+       /* Find the tty. */
+       if ((tty = ttyname(STDERR_FILENO)) == NULL) {
+               msgq(sp, M_ERR, "ttyname: %s.", strerror(errno));
+               return (1);
+       }
+
+       /* Save the tty mode for later; only save it once. */
+       if (!F_ISSET(sp->gp, G_SETMODE)) {
+               F_SET(sp->gp, G_SETMODE);
+               if (stat(tty, &sb) < 0) {
+                       msgq(sp, M_ERR, "%s: %s.", tty, strerror(errno));
+                       return (1);
+               }
+               sp->gp->origmode = sb.st_mode;
+       }
+
+       if (turnoff) {
+               if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) {
+                       msgq(sp, M_ERR, "messages not turned off: %s: %s.",
+                           tty, strerror(errno));
+                       return (1);
+               }
+               O_CLR(sp, O_MESG);
+       } else {
+               if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) {
+                       msgq(sp, M_ERR, "messages not turned on: %s: %s.",
+                           tty, strerror(errno));
+                       return (1);
+               }
+               O_SET(sp, O_MESG);
+       }
+       return (0);
+}
+
+/*
+ * f_modeline --
+ *     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.
+ */
+DECL(f_modeline)
+{
+       if (!turnoff)
+               msgq(sp, M_ERR, "The modeline(s) option may never be set.");
+       return (0);
+}
+
+DECL(f_number)
+{
+       if (turnoff)
+               O_CLR(sp, O_NUMBER);
+       else
+               O_SET(sp, O_NUMBER);
+
+       F_SET(sp, S_REFORMAT | S_REDRAW);
+       return (0);
+}
+
+DECL(f_optimize)
+{
+       msgq(sp, M_ERR, "The optimize option is not implemented.");
+       return (0);
+}
+
+DECL(f_paragraph)
+{
+       if (strlen(str) & 1) {
+               msgq(sp, M_ERR,
+                   "Paragraph options must be in sets of two characters.");
+               return (1);
+       }
+
+       if (F_ISSET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED))
+               free(O_STR(sp, O_PARAGRAPHS));
+       if ((O_STR(sp, O_PARAGRAPHS) = strdup(str)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       F_SET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED | OPT_SET);
+       return (0);
+}
+
+DECL(f_readonly)
+{
+       if (turnoff) {
+               O_CLR(sp, O_READONLY);
+               if (sp->frp != NULL)
+                       F_CLR(sp->frp, FR_RDONLY);
+       } else {
+               O_SET(sp, O_READONLY);
+               if (sp->frp != NULL)
+                       F_SET(sp->frp, FR_RDONLY);
+       }
+       return (0);
+}
+
+DECL(f_ruler)
+{
+       if (turnoff)
+               O_CLR(sp, O_RULER);
+       else
+               O_SET(sp, O_RULER);
+       return (0);
+}
+
+DECL(f_section)
+{
+       if (strlen(str) & 1) {
+               msgq(sp, M_ERR,
+                   "Section options must be in sets of two characters.");
+               return (1);
+       }
+
+       if (F_ISSET(&sp->opts[O_SECTIONS], OPT_ALLOCATED))
+               free(O_STR(sp, O_SECTIONS));
+       if ((O_STR(sp, O_SECTIONS) = strdup(str)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       F_SET(&sp->opts[O_SECTIONS], OPT_ALLOCATED | OPT_SET);
+       return (0);
+}
+
+DECL(f_shiftwidth)
+{
+       if (val == 0) {
+               msgq(sp, M_ERR, "The shiftwidth can't be set to 0.");
+               return (1);
+       }
+       if (val > O_VAL(sp, O_COLUMNS)) {
+               msgq(sp, M_ERR,
+                   "Shiftwidth can't be larger than screen size.");
+               return (1);
+       }
+       O_VAL(sp, O_SHIFTWIDTH) = val;
+       return (0);
+}
+
+DECL(f_sidescroll)
+{
+       if (val > O_VAL(sp, O_COLUMNS)) {
+               msgq(sp, M_ERR,
+                   "Sidescroll can't be larger than screen size.");
+               return (1);
+       }
+       O_VAL(sp, O_SIDESCROLL) = val;
+       return (0);
+}
+
+/*
+ * f_sourceany --
+ *     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.
+ */
+DECL(f_sourceany)
+{
+       if (!turnoff)
+               msgq(sp, M_ERR, "The sourceany option may never be set.");
+       return (0);
+}
+
+DECL(f_tabstop)
+{
+       if (val == 0) {
+               msgq(sp, M_ERR, "Tab stops can't be set to 0.");
+               return (1);
+       }
+#define        MAXTABSTOP      20
+       if (val > MAXTABSTOP) {
+               msgq(sp, M_ERR,
+                   "Tab stops can't be larger than %d.", MAXTABSTOP);
+               return (1);
+       }
+       if (val > O_VAL(sp, O_COLUMNS)) {
+               msgq(sp, M_ERR,
+                   "Tab stops can't be larger than screen size.",
+                   MAXTABSTOP);
+               return (1);
+       }
+       O_VAL(sp, O_TABSTOP) = val;
+
+       F_SET(sp, S_REFORMAT | S_REDRAW);
+       return (0);
+}
+
+DECL(f_tags)
+{
+       char *p;
+
+       /* Copy for user display. */
+       p = O_STR(sp, O_TAGS);
+       if ((O_STR(sp, O_TAGS) = strdup(str)) == NULL) {
+               O_STR(sp, O_TAGS) = p;
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       if (F_ISSET(&sp->opts[O_TAGS], OPT_ALLOCATED))
+               FREE(p, strlen(p) + 1);
+       F_SET(&sp->opts[O_TAGS], OPT_ALLOCATED | OPT_SET);
+       return (0);
+}
+
+DECL(f_term)
+{
+       char buf[256];
+
+       if (F_ISSET(&sp->opts[O_TERM], OPT_ALLOCATED))
+               free(O_STR(sp, O_TERM));
+       if ((O_STR(sp, O_TERM) = strdup(str)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       F_SET(&sp->opts[O_TERM], OPT_ALLOCATED | OPT_SET);
+
+       /* Set the terminal value in the environment for curses. */
+       (void)snprintf(buf, sizeof(buf), "TERM=%s", str);
+       if (opt_putenv(buf))
+               return (1);
+
+       if (set_window_size(sp, 0, 0))
+               return (1);
+       return (0);
+}
+
+DECL(f_w300)
+{
+       /* Historical behavior for w300 was < 1200. */
+       if (baud_from_bval(sp) >= 1200)
+               return (0);
+
+       if (CALL(f_window))
+               return (1);
+
+       if (val > O_VAL(sp, O_LINES) - 1)
+               val = O_VAL(sp, O_LINES) - 1;
+       O_VAL(sp, O_W300) = val;
+       return (0);
+}
+
+DECL(f_w1200)
+{
+       u_long v;
+
+       /* Historical behavior for w1200 was == 1200. */
+       v = baud_from_bval(sp);
+       if (v < 1200 || v > 4800)
+               return (0);
+
+       if (CALL(f_window))
+               return (1);
+
+       if (val > O_VAL(sp, O_LINES) - 1)
+               val = O_VAL(sp, O_LINES) - 1;
+       O_VAL(sp, O_W1200) = val;
+       return (0);
+}
+
+DECL(f_w9600)
+{
+       speed_t v;
+
+       /* Historical behavior for w9600 was > 1200. */
+       v = baud_from_bval(sp);
+       if (v <= 4800)
+               return (0);
+
+       if (CALL(f_window))
+               return (1);
+
+       if (val > O_VAL(sp, O_LINES) - 1)
+               val = O_VAL(sp, O_LINES) - 1;
+       O_VAL(sp, O_W9600) = val;
+       return (0);
+}
+
+DECL(f_window)
+{
+       if (val < MINIMUM_SCREEN_ROWS) {
+               msgq(sp, M_ERR, "Window too small, less than %d.",
+                   MINIMUM_SCREEN_ROWS);
+               return (1);
+       }
+       if (val > O_VAL(sp, O_LINES) - 1)
+               val = O_VAL(sp, O_LINES) - 1;
+       O_VAL(sp, O_WINDOW) = val;
+       O_VAL(sp, O_SCROLL) = val / 2;
+
+       return (0);
+}
+
+DECL(f_wrapmargin)
+{
+       if (val > O_VAL(sp, O_COLUMNS)) {
+               msgq(sp, M_ERR,
+                   "Wrapmargin value can't be larger than screen size.");
+               return (1);
+       }
+       O_VAL(sp, O_WRAPMARGIN) = val;
+       return (0);
+}
+
+/*
+ * opt_putenv --
+ *     Put a value into the environment.  We use putenv(3) because it's
+ *     more portable.  The following hack is because some moron decided
+ *     to keep a reference to the memory passed to putenv(3), instead of
+ *     having it allocate its own.  Someone clearly needs to get promoted
+ *     into management.
+ */
+static int
+opt_putenv(s)
+       char *s;
+{
+       char *t;
+
+       /* Memory leak. */
+       if ((t = strdup(s)) == NULL)
+               return (1);
+       return (putenv(t));
+}
diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h
new file mode 100644 (file)
index 0000000..be6352b
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)pathnames.h 8.5 (Berkeley) 12/21/93
+ */
+
+#define        _PATH_BSHELL    "/bin/sh"
+#define        _PATH_DEVNULL   "/dev/null"
+#define        _PATH_EXRC      ".exrc"
+#define        _PATH_NEXRC     ".nexrc"
+#define        _PATH_PRESERVE  "/var/tmp/vi.recover"
+#define        _PATH_SENDMAIL  "/usr/sbin/sendmail"
+#define        _PATH_SYSEXRC   "/etc/vi.exrc"
+#define        _PATH_TAGS      "tags /var/db/libc.tags /sys/kern/tags"
+#define        _PATH_TMP       "/tmp"
+#define        _PATH_TTY       "/dev/tty"
diff --git a/usr.bin/vi/recover.c b/usr.bin/vi/recover.c
new file mode 100644 (file)
index 0000000..f9a4832
--- /dev/null
@@ -0,0 +1,622 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)recover.c  8.40 (Berkeley) 12/21/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * We include <sys/file.h>, because the flock(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 <netdb.h>                     /* MAXHOSTNAMELEN on some systems. */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "pathnames.h"
+
+/*
+ * Recovery code.
+ *
+ * The basic scheme is there's a btree file, whose name we specify.  The first
+ * time a file is modified, and then at RCV_PERIOD intervals after that, the
+ * btree file is synced to disk.  Each time a keystroke is requested for a file
+ * the terminal routines check to see if the file needs to be synced.  This, of
+ * course means that the data structures had better be consistent each time the
+ * key routines are called.
+ *
+ * We don't use timers other than to flag that the file should be synced.  This
+ * would require that the SCR and EXF data structures be locked, the dbopen(3)
+ * routines lock out the timers for each update, etc.  It's just not worth it.
+ * The only way we can lose in the current scheme is if the file is saved, then
+ * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more.
+ * Not likely.
+ *
+ * When a file is first modified, a file which can be handed off to the mailer
+ * is created.  The file contains normal headers, with two additions, which
+ * occur in THIS order, as the FIRST TWO headers:
+ *
+ *     Vi-recover-file: file_name
+ *     Vi-recover-path: recover_path
+ *
+ * Since newlines delimit the headers, this means that file names cannot
+ * have newlines in them, but that's probably okay.
+ *
+ * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
+ */
+
+#define        VI_FHEADER      "Vi-recover-file: "
+#define        VI_PHEADER      "Vi-recover-path: "
+
+static void    rcv_alrm __P((int));
+static int     rcv_mailfile __P((SCR *, EXF *));
+static void    rcv_syncit __P((SCR *, int));
+
+/*
+ * rcv_tmp --
+ *     Build a file name that will be used as the recovery file.
+ */
+int
+rcv_tmp(sp, ep, name)
+       SCR *sp;
+       EXF *ep;
+       char *name;
+{
+       struct stat sb;
+       int fd;
+       char *dp, *p, path[MAXPATHLEN];
+
+       /*
+        * 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 users recovery files.  If
+        * the sticky bit has the BSD semantics, that too will be impossible.
+        */
+       dp = O_STR(sp, O_RECDIR);
+       if (stat(dp, &sb)) {
+               if (errno != ENOENT || mkdir(dp, 0)) {
+                       msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno));
+                       return (1);
+               }
+               (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
+       }
+               
+       /* Newlines delimit the mail messages. */
+       for (p = name; *p; ++p)
+               if (*p == '\n') {
+                       msgq(sp, M_ERR,
+                   "Files with newlines in the name are unrecoverable.");
+                       return (1);
+               }
+
+       (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
+
+       /*
+        * !!!
+        * We depend on mkstemp(3) setting the permissions correctly.
+        * GP's, we do it ourselves, to keep the window as small as
+        * possible.
+        */
+       if ((fd = mkstemp(path)) == -1) {
+               msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno));
+               return (1);
+       }
+       (void)chmod(path, S_IRUSR | S_IWUSR);
+       (void)close(fd);
+
+       if ((ep->rcv_path = strdup(path)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               (void)unlink(path);
+               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.
+ */
+int
+rcv_init(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       struct itimerval value;
+       struct sigaction act;
+       recno_t lno;
+
+       F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON);
+
+       /* Build file to mail to the user. */
+       if (rcv_mailfile(sp, ep))
+               goto err;
+
+       /* Force read of entire file. */
+       if (file_lline(sp, ep, &lno))
+               goto err;
+
+       /* Turn on a busy message, and sync it to backing store. */
+       busy_on(sp, 1, "Copying file for recovery...");
+       if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+               msgq(sp, M_ERR, "Preservation failed: %s: %s",
+                   ep->rcv_path, strerror(errno));
+               busy_off(sp);
+               goto err;
+       }
+       busy_off(sp);
+
+       if (!F_ISSET(sp->gp, G_RECOVER_SET)) {
+               /* Install the recovery timer handler. */
+               act.sa_handler = rcv_alrm;
+               sigemptyset(&act.sa_mask);
+               act.sa_flags = 0;
+               (void)sigaction(SIGALRM, &act, NULL);
+
+               /* Start the recovery timer. */
+               value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD;
+               value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+               if (setitimer(ITIMER_REAL, &value, NULL)) {
+                       msgq(sp, M_ERR,
+                           "Error: setitimer: %s", strerror(errno));
+err:                   msgq(sp, M_ERR,
+                           "Recovery after system crash not possible.");
+                       return (1);
+               }
+       }
+
+       /* We believe the file is recoverable. */
+       F_SET(ep, F_RCV_ON);
+       return (0);
+}
+
+/*
+ * rcv_alrm --
+ *     Recovery timer interrupt handler.
+ */
+static void
+rcv_alrm(signo)
+       int signo;
+{
+       F_SET(__global_list, G_SIGALRM);
+}
+
+/*
+ * rcv_mailfile --
+ *     Build the file to mail to the user.
+ */
+static int
+rcv_mailfile(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       struct passwd *pw;
+       uid_t uid;
+       FILE *fp;
+       time_t now;
+       int fd;
+       char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN];
+
+       if ((pw = getpwuid(uid = getuid())) == NULL) {
+               msgq(sp, M_ERR, "Information on user id %u not found.", uid);
+               return (1);
+       }
+
+       (void)snprintf(path, sizeof(path),
+           "%s/recover.XXXXXX", O_STR(sp, O_RECDIR));
+       if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) {
+               msgq(sp, M_ERR,
+                   "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno));
+               if (fd != -1)
+                       (void)close(fd);
+               return (1);
+       }
+
+       /*
+        * 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.
+        */
+       if ((ep->rcv_fd = dup(fd)) != -1)
+               (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB);
+
+       if ((ep->rcv_mpath = strdup(path)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               (void)fclose(fp);
+               return (1);
+       }
+       
+       t = FILENAME(sp->frp);
+       if ((p = strrchr(t, '/')) == NULL)
+               p = t;
+       else
+               ++p;
+       (void)time(&now);
+       (void)gethostname(host, sizeof(host));
+       (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
+           VI_FHEADER, p,                      /* Non-standard. */
+           VI_PHEADER, ep->rcv_path,           /* Non-standard. */
+           "Reply-To: root",
+           "From: root (Nvi recovery program)",
+           "To: ", pw->pw_name,
+           "Subject: Nvi saved the file ", p,
+           "Precedence: bulk");                        /* For vacation(1). */
+       (void)fprintf(fp, "%s%.24s%s%s\n%s%s", 
+           "On ", ctime(&now),
+           ", the user ", pw->pw_name,
+           "was editing a file named ", p);
+       if (p != t)
+               (void)fprintf(fp, " (%s)", t);
+       (void)fprintf(fp, "\n%s%s%s\n",
+           "on the machine ", host, ", when it was saved for\nrecovery.");
+       (void)fprintf(fp, "\n%s\n%s\n%s\n\n",
+           "You can recover most, if not all, of the changes",
+           "to this file using the -l and -r options to nvi(1)",
+           "or nex(1).");
+
+       if (fflush(fp) || ferror(fp)) {
+               msgq(sp, M_SYSERR, NULL);
+               (void)fclose(fp);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * rcv_sync --
+ *     Sync the backing file.
+ */
+int
+rcv_sync(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       struct itimerval value;
+
+       if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+               msgq(sp, M_ERR, "Automatic file backup failed: %s: %s",
+                   ep->rcv_path, strerror(errno));
+               value.it_interval.tv_sec = value.it_interval.tv_usec = 0;
+               value.it_value.tv_sec = value.it_value.tv_usec = 0;
+               (void)setitimer(ITIMER_REAL, &value, NULL);
+               F_CLR(ep, F_RCV_ON);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * rcv_hup --
+ *     Recovery SIGHUP interrupt handler.  (Modem line dropped, or
+ *     xterm window closed.)
+ */
+void
+rcv_hup()
+{
+       SCR *sp;
+
+       /*
+        * Walk the lists of screens, sync'ing the files; only sync
+        * each file once.  Send email to the user for each file saved.
+        */
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               rcv_syncit(sp, 1);
+       for (sp = __global_list->hq.cqh_first;
+           sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+               rcv_syncit(sp, 1);
+
+       /*
+        * Die with the proper exit status.  Don't bother using
+        * sigaction(2) 'cause we want the default behavior.
+        */
+       (void)signal(SIGHUP, SIG_DFL);
+       (void)kill(0, SIGHUP);
+
+       /* NOTREACHED */
+       exit (1);
+}
+
+/*
+ * rcv_term --
+ *     Recovery SIGTERM interrupt handler.  (Reboot or halt is running.)
+ */
+void
+rcv_term()
+{
+       SCR *sp;
+
+       /*
+        * Walk the lists of screens, sync'ing the files; only sync
+        * each file once.
+        */
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               rcv_syncit(sp, 0);
+       for (sp = __global_list->hq.cqh_first;
+           sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+               rcv_syncit(sp, 0);
+
+       /*
+        * Die with the proper exit status.  Don't bother using
+        * sigaction(2) 'cause we want the default behavior.
+        */
+       (void)signal(SIGTERM, SIG_DFL);
+       (void)kill(0, SIGTERM);
+
+       /* NOTREACHED */
+       exit (1);
+}
+
+/*
+ * rcv_syncit --
+ *     Sync the file, optionally send mail.
+ */
+static void
+rcv_syncit(sp, email)
+       SCR *sp;
+       int email;
+{
+       EXF *ep;
+       char comm[1024];
+
+       if ((ep = sp->ep) == NULL ||
+           !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON))
+               return;
+
+       (void)ep->db->sync(ep->db, R_RECNOSYNC);
+       F_SET(ep, F_RCV_NORM);
+
+       /*
+        * !!!
+        * If you need to port this to a system that doesn't have sendmail,
+        * the -t flag being used causes sendmail to read the message for
+        * the recipients instead of us specifying them some other way.
+        */
+       if (email) {
+               (void)snprintf(comm, sizeof(comm),
+                   "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath);
+               (void)system(comm);
+       }
+       (void)file_end(sp, ep, 1);
+}
+
+/*
+ *     people making love
+ *     never exactly the same
+ *     just like a snowflake 
+ *
+ * rcv_list --
+ *     List the files that can be recovered by this user.
+ */
+int
+rcv_list(sp)
+       SCR *sp;
+{
+       struct dirent *dp;
+       struct stat sb;
+       DIR *dirp;
+       FILE *fp;
+       int found;
+       char *p, file[1024];
+
+       if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) {
+               (void)fprintf(stderr,
+                   "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno));
+               return (1);
+       }
+
+       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;
+
+               /* If it's locked, it's live. */
+               if (flock(fileno(fp), LOCK_EX | LOCK_NB)) {
+                       (void)fclose(fp);
+                       continue;
+               }
+
+               /* Check the header, get the file name. */
+               if (fgets(file, sizeof(file), fp) == NULL ||
+                   strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+                   (p = strchr(file, '\n')) == NULL) {
+                       (void)fprintf(stderr,
+                           "vi: %s: malformed recovery file.\n", dp->d_name);
+                       goto next;
+               }
+               *p = '\0';
+
+               /* Get the last modification time. */
+               if (fstat(fileno(fp), &sb)) {
+                       (void)fprintf(stderr,
+                           "vi: %s: %s\n", dp->d_name, strerror(errno));
+                       goto next;
+               }
+
+               /* Display. */
+               (void)printf("%s: %s",
+                   file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime));
+               found = 1;
+
+next:          (void)fclose(fp);
+       }
+       if (found == 0)
+               (void)printf("vi: no files to recover.\n");
+       (void)closedir(dirp);
+       return (0);
+}
+
+/*
+ * rcv_read --
+ *     Start a recovered file as the file to edit.
+ */
+int
+rcv_read(sp, name)
+       SCR *sp;
+       char *name;
+{
+       struct dirent *dp;
+       struct stat sb;
+       DIR *dirp;
+       FREF *frp;
+       FILE *fp;
+       time_t rec_mtime;
+       int found, requested;
+       char *p, *t, *recp, *pathp;
+       char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN];
+               
+       if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) {
+               msgq(sp, M_ERR,
+                   "%s: %s", O_STR(sp, O_RECDIR), strerror(errno));
+               return (1);
+       }
+
+       recp = pathp = NULL;
+       for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
+               if (strncmp(dp->d_name, "recover.", 8))
+                       continue;
+
+               /* If it's readable, it's recoverable. */
+               (void)snprintf(recpath, sizeof(recpath),
+                   "%s/%s", O_STR(sp, O_RECDIR), dp->d_name);
+               if ((fp = fopen(recpath, "r")) == NULL)
+                       continue;
+
+               /* If it's locked, it's live. */
+               if (flock(fileno(fp), LOCK_EX | LOCK_NB)) {
+                       (void)fclose(fp);
+                       continue;
+               }
+
+               /* Check the headers. */
+               if (fgets(file, sizeof(file), fp) == NULL ||
+                   strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+                   (p = strchr(file, '\n')) == NULL ||
+                   fgets(path, sizeof(path), fp) == NULL ||
+                   strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+                   (t = strchr(path, '\n')) == NULL) {
+                       msgq(sp, M_ERR,
+                           "%s: malformed recovery file.", recpath);
+                       goto next;
+               }
+               ++found;
+               *t = *p = '\0';
+
+               /* Get the last modification time. */
+               if (fstat(fileno(fp), &sb)) {
+                       msgq(sp, M_ERR,
+                           "vi: %s: %s", dp->d_name, strerror(errno));
+                       goto next;
+               }
+
+               /* Check the file name. */
+               if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
+                       goto next;
+
+               ++requested;
+
+               /* If we've found more than one, take the most recent. */
+               if (recp == NULL || rec_mtime < sb.st_mtime) {
+                       p = recp;
+                       t = pathp;
+                       if ((recp = strdup(recpath)) == NULL) {
+                               msgq(sp, M_ERR,
+                                   "vi: Error: %s.\n", strerror(errno));
+                               recp = p;
+                               goto next;
+                       }
+                       if ((pathp = strdup(path)) == NULL) {
+                               msgq(sp, M_ERR,
+                                   "vi: Error: %s.\n", strerror(errno));
+                               FREE(recp, strlen(recp) + 1);
+                               recp = p;
+                               pathp = t;
+                               goto next;
+                       }
+                       if (p != NULL) {
+                               FREE(p, strlen(p) + 1);
+                               FREE(t, strlen(t) + 1);
+                       }
+                       rec_mtime = sb.st_mtime;
+               }
+
+next:          (void)fclose(fp);
+       }
+       (void)closedir(dirp);
+
+       if (recp == NULL) {
+               msgq(sp, M_INFO,
+                   "No files named %s, owned by you, to edit.", name);
+               return (1);
+       }
+       if (found) {
+               if (requested > 1)
+                       msgq(sp, M_INFO,
+                  "There are older versions of this file for you to recover.");
+               if (found > requested)
+                       msgq(sp, M_INFO,
+                           "There are other files that you can recover.");
+       }
+
+       /* Create the FREF structure, start the btree file. */
+       if ((frp = file_add(sp, NULL, name, 0)) == NULL ||
+           file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
+               FREE(recp, strlen(recp) + 1);
+               FREE(pathp, strlen(pathp) + 1);
+               return (1);
+       }
+       sp->ep->rcv_mpath = recp;
+       return (0);
+}
diff --git a/usr.bin/vi/screen.c b/usr.bin/vi/screen.c
new file mode 100644 (file)
index 0000000..e18bd02
--- /dev/null
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)screen.c   8.51 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "tag.h"
+
+/*
+ * screen_init --
+ *     Do the default initialization of an SCR structure.
+ */
+int
+screen_init(orig, spp, flags)
+       SCR *orig, **spp;
+       u_int flags;
+{
+       SCR *sp;
+       size_t len;
+
+       *spp = NULL;
+       CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
+       *spp = sp;
+
+/* INITIALIZED AT SCREEN CREATE. */
+       sp->gp = __global_list;                 /* All ref the GS structure. */
+
+       LIST_INIT(&sp->msgq);
+       CIRCLEQ_INIT(&sp->frefq);
+
+       sp->ccnt = 2;                           /* Anything > 1 */
+
+       FD_ZERO(&sp->rdfd);
+
+       CIRCLEQ_INIT(&sp->tiq);
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+       if (orig == NULL) {
+               sp->searchdir = NOTSET;
+               sp->csearchdir = CNOTSET;
+
+               switch (flags & S_SCREENS) {
+               case S_EX:
+                       if (sex_screen_init(sp))
+                               return (1);
+                       break;
+               case S_VI_CURSES:
+                       if (svi_screen_init(sp))
+                               return (1);
+                       break;
+               case S_VI_XAW:
+                       if (xaw_screen_init(sp))
+                               return (1);
+                       break;
+               default:
+                       abort();
+               }
+
+               sp->flags = flags;
+       } else {
+               if (orig->alt_name != NULL &&
+                   (sp->alt_name = strdup(orig->alt_name)) == NULL)
+                       goto mem;
+
+               /* Retain all searching/substitution information. */
+               if (F_ISSET(orig, S_SRE_SET)) {
+                       F_SET(sp, S_SRE_SET);
+                       sp->sre = orig->sre;
+               }
+               if (F_ISSET(orig, S_SUBRE_SET)) {
+                       F_SET(sp, S_SUBRE_SET);
+                       sp->subre = orig->subre;
+               }
+               sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
+               sp->csearchdir = CNOTSET;
+               sp->lastckey = orig->lastckey;
+
+               if (orig->matchsize) {
+                       len = orig->matchsize * sizeof(regmatch_t);
+                       MALLOC(sp, sp->match, regmatch_t *, len);
+                       if (sp->match == NULL)
+                               goto mem;
+                       sp->matchsize = orig->matchsize;
+                       memmove(sp->match, orig->match, len);
+               }
+               if (orig->repl_len) {
+                       MALLOC(sp, sp->repl, char *, orig->repl_len);
+                       if (sp->repl == NULL)
+                               goto mem;
+                       sp->repl_len = orig->repl_len;
+                       memmove(sp->repl, orig->repl, orig->repl_len);
+               }
+               if (orig->newl_len) {
+                       len = orig->newl_len * sizeof(size_t);
+                       MALLOC(sp, sp->newl, size_t *, len);
+                       if (sp->newl == NULL)
+                               goto mem;
+                       sp->newl_len = orig->newl_len;
+                       sp->newl_cnt = orig->newl_cnt;
+                       memmove(sp->newl, orig->newl, len);
+               }
+
+               sp->saved_vi_mode = orig->saved_vi_mode;
+
+               if (opts_copy(orig, sp)) {
+mem:                   msgq(orig, M_SYSERR, "new screen attributes");
+                       (void)screen_end(sp);
+                       return (1);
+               }
+
+               sp->s_bell              = orig->s_bell;
+               sp->s_bg                = orig->s_bg;
+               sp->s_busy              = orig->s_busy;
+               sp->s_change            = orig->s_change;
+               sp->s_chposition        = orig->s_chposition;
+               sp->s_clear             = orig->s_clear;
+               sp->s_column            = orig->s_column;
+               sp->s_confirm           = orig->s_confirm;
+               sp->s_down              = orig->s_down;
+               sp->s_edit              = orig->s_edit;
+               sp->s_end               = orig->s_end;
+               sp->s_ex_cmd            = orig->s_ex_cmd;
+               sp->s_ex_run            = orig->s_ex_run;
+               sp->s_ex_write          = orig->s_ex_write;
+               sp->s_fg                = orig->s_fg;
+               sp->s_fill              = orig->s_fill;
+               sp->s_get               = orig->s_get;
+               sp->s_key_read          = orig->s_key_read;
+               sp->s_optchange         = orig->s_optchange;
+               sp->s_position          = orig->s_position;
+               sp->s_rabs              = orig->s_rabs;
+               sp->s_refresh           = orig->s_refresh;
+               sp->s_relative          = orig->s_relative;
+               sp->s_rrel              = orig->s_rrel;
+               sp->s_split             = orig->s_split;
+               sp->s_suspend           = orig->s_suspend;
+               sp->s_up                = orig->s_up;
+
+               F_SET(sp, F_ISSET(orig, S_SCREENS));
+       }
+
+       if (xaw_screen_copy(orig, sp))          /* Init S_VI_XAW screen. */
+               return (1);
+       if (svi_screen_copy(orig, sp))          /* Init S_VI_CURSES screen. */
+               return (1);
+       if (sex_screen_copy(orig, sp))          /* Init S_EX screen. */
+               return (1);
+       if (v_screen_copy(orig, sp))            /* Init vi. */
+               return (1);
+       if (ex_screen_copy(orig, sp))           /* Init ex. */
+               return (1);
+
+       *spp = sp;
+       return (0);
+}
+
+/*
+ * screen_end --
+ *     Release a screen.
+ */
+int
+screen_end(sp)
+       SCR *sp;
+{
+       int rval;
+
+       rval = 0;
+       if (xaw_screen_end(sp))                 /* End S_VI_XAW screen. */
+               rval = 1;
+       if (svi_screen_end(sp))                 /* End S_VI_CURSES screen. */
+               rval = 1;
+       if (sex_screen_end(sp))                 /* End S_EX screen. */
+               rval = 1;
+       if (v_screen_end(sp))                   /* End vi. */
+               rval = 1;
+       if (ex_screen_end(sp))                  /* End ex. */
+               rval = 1;
+
+       /* Free FREF's. */
+       { FREF *frp;
+               while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) {
+                       CIRCLEQ_REMOVE(&sp->frefq, frp, q);
+                       if (frp->cname != NULL)
+                               free(frp->cname);
+                       if (frp->name != NULL)
+                               free(frp->name);
+                       if (frp->tname != NULL)
+                               free(frp->tname);
+                       FREE(frp, sizeof(FREF));
+               }
+       }
+
+       /* Free any text input. */
+       text_lfree(&sp->tiq);
+
+       /* Free any script information. */
+       if (F_ISSET(sp, S_SCRIPT))
+               sscr_end(sp);
+
+       /* Free alternate file name. */
+       if (sp->alt_name != NULL)
+               FREE(sp->alt_name, strlen(sp->alt_name) + 1);
+
+       /* Free up search information. */
+       if (sp->match != NULL)
+               FREE(sp->match, sizeof(regmatch_t));
+       if (sp->repl != NULL)
+               FREE(sp->repl, sp->repl_len);
+       if (sp->newl != NULL)
+               FREE(sp->newl, sp->newl_len);
+
+       /* Free all the options */
+       opts_free(sp);
+
+       /*
+        * Free the message chain last, so previous failures have a place
+        * to put messages.  Copy messages to (in order) a related screen,
+        * any screen, the global area. 
+        */
+       { SCR *c_sp; MSG *mp, *next;
+               if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
+                       if (F_ISSET(sp, S_BELLSCHED))
+                               F_SET(c_sp, S_BELLSCHED);
+               } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
+                       if (F_ISSET(sp, S_BELLSCHED))
+                               F_SET(c_sp, S_BELLSCHED);
+               } else if ((c_sp =
+                   sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) {
+                       if (F_ISSET(sp, S_BELLSCHED))
+                               F_SET(c_sp, S_BELLSCHED);
+               } else {
+                       c_sp = NULL;
+                       if (F_ISSET(sp, S_BELLSCHED))
+                               F_SET(sp->gp, G_BELLSCHED);
+               }
+
+               for (mp = sp->msgq.lh_first; mp != NULL; mp = next) {
+                       if (!F_ISSET(mp, M_EMPTY))
+                               msg_app(sp->gp, c_sp,
+                                   mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+                       next = mp->q.le_next;
+                       if (mp->mbuf != NULL)
+                               FREE(mp->mbuf, mp->blen);
+                       FREE(mp, sizeof(MSG));
+               }
+       }
+
+       /* Remove the screen from the displayed queue. */
+       CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
+
+       /* Free the screen itself. */
+       FREE(sp, sizeof(SCR));
+
+       return (rval);
+}
diff --git a/usr.bin/vi/screen.h b/usr.bin/vi/screen.h
new file mode 100644 (file)
index 0000000..d951793
--- /dev/null
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)screen.h    8.81 (Berkeley) 12/29/93
+ */
+
+/*
+ * There are minimum values that vi has to have to display a screen.  The
+ * row minimum is fixed at 1 line for the text, and 1 line for any error
+ * messages.  The column calculation is a lot trickier.  For example, you
+ * have to have enough columns to display the line number, not to mention
+ * guaranteeing that tabstop and shiftwidth values are smaller than the
+ * current column value.  It's a lot simpler to have a fixed value and not
+ * worry about it.
+ *
+ * XXX
+ * MINIMUM_SCREEN_COLS is probably wrong.
+ */
+#define        MINIMUM_SCREEN_ROWS      2
+#define        MINIMUM_SCREEN_COLS     20
+                                       /* Line operations. */
+enum operation { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET };
+                                       /* Position values. */
+enum position { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP };
+
+/*
+ * Structure for holding file references.  Each SCR structure contains a
+ * linked list of these (the user's argument list) as well as pointers to
+ * the current and previous files.  The structure contains the name of the
+ * file, along with the information that follows the name.  A file has up
+ * to three "names".  The tname field is the path of the temporary backing
+ * file, if any.  The name field is the name the user originally used to
+ * specify the file to be edited.  The cname field is the changed name if
+ * the user changed the name.
+ *
+ * Note that the read-only bit follows the file name, not the file itself.
+ *
+ * XXX
+ * The mtime field should be a struct timespec, but time_t is more portable.
+ */
+struct _fref {
+       CIRCLEQ_ENTRY(_fref) q;         /* Linked list of file references. */
+       char    *cname;                 /* Changed file name. */
+       char    *name;                  /* File name. */
+       char    *tname;                 /* Temporary file name. */
+
+       recno_t  lno;                   /* 1-N: file cursor line. */
+       size_t   cno;                   /* 0-N: file cursor column. */
+       time_t   mtime;                 /* Last modification time. */
+
+#define        FR_CHANGEWRITE  0x001           /* Name changed and then written. */
+#define        FR_CURSORSET    0x002           /* If lno/cno valid. */
+#define        FR_EDITED       0x004           /* If the file was ever edited. */
+#define        FR_IGNORE       0x008           /* File isn't part of argument list. */
+#define        FR_NEWFILE      0x010           /* File doesn't really exist yet. */
+#define        FR_RDONLY       0x020           /* File is read-only. */
+       u_int    flags;
+};
+
+/*
+ * There's a file name hierarchy -- if the user has changed the name, we
+ * use it, otherwise, we use the original name, if there was one, othewise
+ * use the temporary name.
+ */
+#define        FILENAME(frp)                                                   \
+       ((frp)->cname != NULL) ? (frp)->cname :                         \
+       ((frp)->name != NULL) ? (frp)->name : (frp)->tname
+
+/*
+ * SCR --
+ *     The screen structure.  To the extent possible, all screen information
+ *     is stored in the various private areas.  The only information here
+ *     is used by global routines or is shared by too many screens.
+ */
+struct _scr {
+/* INITIALIZED AT SCREEN CREATE. */
+       CIRCLEQ_ENTRY(_scr) q;          /* Screens. */
+
+       GS      *gp;                    /* Pointer to global area. */
+
+       SCR     *nextdisp;              /* Next display screen. */
+
+       EXF     *ep;                    /* Screen's current EXF structure. */
+
+       MSGH     msgq;                  /* Message list. */
+                                       /* FREF list. */
+       CIRCLEQ_HEAD(_frefh, _fref) frefq;
+       FREF    *frp;                   /* FREF being edited. */
+       FREF    *a_frp;                 /* Last argument list FREF edited. */
+       FREF    *p_frp;                 /* Last FREF edited. */
+
+       u_long   ccnt;                  /* Command count. */
+       u_long   q_ccnt;                /* Quit or ZZ command count. */
+
+                                       /* Screen's: */
+       size_t   rows;                  /* 1-N: number of rows. */
+       size_t   cols;                  /* 1-N: number of columns. */
+       size_t   woff;                  /* 0-N: row offset in screen. */
+       size_t   t_rows;                /* 1-N: cur number of text rows. */
+       size_t   t_maxrows;             /* 1-N: max number of text rows. */
+       size_t   t_minrows;             /* 1-N: min number of text rows. */
+
+                                       /* Cursor's: */
+       recno_t  lno;                   /* 1-N: file line. */
+       size_t   cno;                   /* 0-N: file character in line. */
+
+       size_t   rcm;                   /* Vi: 0-N: Column suck. */
+#define        RCM_FNB         0x01            /* Column suck: first non-blank. */
+#define        RCM_LAST        0x02            /* Column suck: last. */
+       u_int    rcmflags;
+
+#define        L_ADDED         0               /* Added lines. */
+#define        L_CHANGED       1               /* Changed lines. */
+#define        L_COPIED        2               /* Copied lines. */
+#define        L_DELETED       3               /* Deleted lines. */
+#define        L_JOINED        4               /* Joined lines. */
+#define        L_MOVED         5               /* Moved lines. */
+#define        L_PUT           6               /* Put lines. */
+#define        L_LSHIFT        7               /* Left shift lines. */
+#define        L_RSHIFT        8               /* Right shift lines. */
+#define        L_YANKED        9               /* Yanked lines. */
+       recno_t  rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
+
+       FILE    *stdfp;                 /* Ex output file pointer. */
+
+       char    *if_name;               /* Ex input file name, for messages. */
+       recno_t  if_lno;                /* Ex input file line, for messages. */
+
+       fd_set   rdfd;                  /* Ex/vi: read fd select mask. */
+
+       TEXTH    tiq;                   /* Ex/vi: text input queue. */
+
+       SCRIPT  *script;                /* Vi: script mode information .*/
+
+       char const *time_msg;           /* ITIMER_REAL message. */
+       struct itimerval time_value;    /* ITIMER_REAL saved value. */
+       struct sigaction time_handler;  /* ITIMER_REAL saved handler. */
+
+       void    *vi_private;            /* Vi private area. */
+       void    *ex_private;            /* Ex private area. */
+       void    *svi_private;           /* Vi curses screen private area. */
+       void    *xaw_private;           /* Vi XAW screen private area. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+       char    *alt_name;              /* Ex/vi: alternate file name. */
+
+                                       /* Ex/vi: search/substitute info. */
+       regex_t  sre;                   /* Last search RE. */
+       regex_t  subre;                 /* Last substitute RE. */
+       enum direction  searchdir;      /* File search direction. */
+       enum cdirection csearchdir;     /* Character search direction. */
+       CHAR_T   lastckey;              /* Last search character. */
+       regmatch_t     *match;          /* Substitute match array. */
+       size_t   matchsize;             /* Substitute match array size. */
+       char    *repl;                  /* Substitute replacement. */
+       size_t   repl_len;              /* Substitute replacement length.*/
+       size_t  *newl;                  /* Newline offset array. */
+       size_t   newl_len;              /* Newline array size. */
+       size_t   newl_cnt;              /* Newlines in replacement. */
+
+       u_int    saved_vi_mode;         /* Saved vi display type. */
+
+       OPTION   opts[O_OPTIONCOUNT];   /* Options. */
+
+/*
+ * SCREEN SUPPORT ROUTINES.
+ *
+ * A SCR * MUST be the first argument to these routines.
+ */
+                                       /* Ring the screen bell. */
+       void     (*s_bell) __P((SCR *));
+                                       /* Background the screen. */
+       int      (*s_bg) __P((SCR *));
+                                       /* Put up a busy message. */
+       int      (*s_busy) __P((SCR *, char const *));
+                                       /* Change a screen line. */
+       int      (*s_change) __P((SCR *, EXF *, recno_t, enum operation));
+                                       /* Return column close to specified. */
+       size_t   (*s_chposition) __P((SCR *, EXF *, recno_t, size_t));
+                                       /* Clear the screen. */
+       int      (*s_clear) __P((SCR *));
+                                       /* Return the logical cursor column. */
+       int      (*s_column) __P((SCR *, EXF *, size_t *));
+       enum confirm                    /* Confirm an action with the user. */
+                (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *));
+                                       /* Move down the screen. */
+       int      (*s_down) __P((SCR *, EXF *, MARK *, recno_t, int));
+                                       /* Edit a file. */
+       int      (*s_edit) __P((SCR *, EXF *));
+                                       /* End a screen. */
+       int      (*s_end) __P((SCR *));
+                                       /* Run a single ex command. */
+       int      (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *));
+                                       /* Run user's ex commands. */
+       int      (*s_ex_run) __P((SCR *, EXF *, MARK *));
+                                       /* Screen's ex write function. */
+       int      (*s_ex_write) __P((void *, const char *, int));
+                                       /* Foreground the screen. */
+       int      (*s_fg) __P((SCR *, CHAR_T *));
+                                       /* Fill the screen's map. */
+       int      (*s_fill) __P((SCR *, EXF *, recno_t, enum position));
+       enum input                      /* Get a line from the user. */
+                (*s_get) __P((SCR *, EXF *, TEXTH *, int, u_int));
+       enum input                      /* Get a key from the user. */
+                (*s_key_read) __P((SCR *, int *, struct timeval *));
+                                       /* Tell the screen an option changed. */
+       int      (*s_optchange) __P((SCR *, int));
+                                       /* Return column at screen position. */
+       int      (*s_position) __P((SCR *, EXF *,
+                   MARK *, u_long, enum position));
+                                       /* Change the absolute screen size. */
+       int      (*s_rabs) __P((SCR *, long));
+                                       /* Refresh the screen. */
+       int      (*s_refresh) __P((SCR *, EXF *));
+                                       /* Return column close to last char. */
+       size_t   (*s_relative) __P((SCR *, EXF *, recno_t));
+                                       /* Change the relative screen size. */
+       int      (*s_rrel) __P((SCR *, long));
+                                       /* Split the screen. */
+       int      (*s_split) __P((SCR *, ARGS *[]));
+                                       /* Suspend the screen. */
+       int      (*s_suspend) __P((SCR *));
+                                       /* Move up the screen. */
+       int      (*s_up) __P((SCR *, EXF *, MARK *, recno_t, int));
+
+/* Editor screens. */
+#define        S_EX            0x0000001       /* Ex screen. */
+#define        S_VI_CURSES     0x0000002       /* Vi: curses screen. */
+#define        S_VI_XAW        0x0000004       /* Vi: Athena widgets screen. */
+
+#define        IN_EX_MODE(sp)                  /* If in ex mode. */            \
+       (F_ISSET(sp, S_EX))
+#define        IN_VI_MODE(sp)                  /* If in vi mode. */            \
+       (F_ISSET(sp, S_VI_CURSES | S_VI_XAW))
+#define        S_SCREENS                       /* Screens. */                  \
+       (S_EX | S_VI_CURSES | S_VI_XAW)
+
+/* Major screen/file changes. */
+#define        S_EXIT          0x0000008       /* Exiting (not forced). */
+#define        S_EXIT_FORCE    0x0000010       /* Exiting (forced). */
+#define        S_FSWITCH       0x0000020       /* Switch files. */
+#define        S_SSWITCH       0x0000040       /* Switch screens. */
+#define        S_MAJOR_CHANGE                  /* Screen or file changes. */   \
+       (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH)
+
+#define        S_BELLSCHED     0x0000080       /* Bell scheduled. */
+#define        S_CONTINUE      0x0000100       /* Need to ask the user to continue. */
+#define        S_EXSILENT      0x0000200       /* Ex batch script. */
+#define        S_GLOBAL        0x0000400       /* Doing a global command. */
+#define        S_INPUT         0x0000800       /* Doing text input. */
+#define        S_INTERRUPTED   0x0001000       /* If have been interrupted. */
+#define        S_INTERRUPTIBLE 0x0002000       /* If can be interrupted. */
+#define        S_REDRAW        0x0004000       /* Redraw the screen. */
+#define        S_REFORMAT      0x0008000       /* Reformat the screen. */
+#define        S_REFRESH       0x0010000       /* Refresh the screen. */
+#define        S_RENUMBER      0x0020000       /* Renumber the screen. */
+#define        S_RESIZE        0x0040000       /* Resize the screen. */
+#define        S_SCRIPT        0x0080000       /* Window is a shell script. */
+#define        S_SRE_SET       0x0100000       /* The search RE has been set. */
+#define        S_SUBRE_SET     0x0200000       /* The substitute RE has been set. */
+#define        S_TIMER_SET     0x0400000       /* If a busy timer is running. */
+#define        S_UPDATE_MODE   0x0800000       /* Don't repaint modeline. */
+       u_int flags;
+};
+
+/* Generic routines to start/stop a screen. */
+int    screen_end __P((SCR *));
+int    screen_init __P((SCR *, SCR **, u_int));
+
+/* Public interfaces to the underlying screens. */
+int    ex_screen_copy __P((SCR *, SCR *));
+int    ex_screen_end __P((SCR *));
+int    ex_screen_init __P((SCR *));
+int    sex_screen_copy __P((SCR *, SCR *));
+int    sex_screen_end __P((SCR *));
+int    sex_screen_init __P((SCR *));
+int    svi_screen_copy __P((SCR *, SCR *));
+int    svi_screen_end __P((SCR *));
+int    svi_screen_init __P((SCR *));
+int    v_screen_copy __P((SCR *, SCR *));
+int    v_screen_end __P((SCR *));
+int    v_screen_init __P((SCR *));
+int    xaw_screen_copy __P((SCR *, SCR *));
+int    xaw_screen_end __P((SCR *));
+int    xaw_screen_init __P((SCR *));
diff --git a/usr.bin/vi/search.c b/usr.bin/vi/search.c
new file mode 100644 (file)
index 0000000..22aadef
--- /dev/null
@@ -0,0 +1,860 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)search.c   8.32 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "interrupt.h"
+
+static int     check_delta __P((SCR *, EXF *, long, recno_t));
+static int     ctag_conv __P((SCR *, char **, int *));
+static int     get_delta __P((SCR *, char **, long *, u_int *));
+static int     resetup __P((SCR *, regex_t **, enum direction,
+                   char *, char **, long *, u_int *));
+static void    search_intr __P((int));
+
+/*
+ * resetup --
+ *     Set up a search for a regular expression.
+ */
+static int
+resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
+       SCR *sp;
+       regex_t **rep;
+       enum direction dir;
+       char *ptrn, **epp;
+       long *deltap;
+       u_int *flagp;
+{
+       u_int flags;
+       int delim, eval, re_flags, replaced;
+       char *p, *t;
+
+       /* Set return information the default. */
+       *deltap = 0;
+
+       /*
+        * Use saved pattern if no pattern supplied, or if only a delimiter
+        * character is supplied.  Only the pattern was saved, historic vi
+        * did not reuse any delta supplied.
+        */
+       flags = *flagp;
+       if (ptrn == NULL)
+               goto prev;
+       if (ptrn[1] == '\0') {
+               if (epp != NULL)
+                       *epp = ptrn + 1;
+               goto prev;
+       }
+       if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
+               if (epp != NULL)
+                       *epp = ptrn + 2;
+prev:          if (!F_ISSET(sp, S_SRE_SET)) {
+                       msgq(sp, M_ERR, "No previous search pattern.");
+                       return (1);
+               }
+               *rep = &sp->sre;
+
+               /* Empty patterns set the direction. */
+               if (LF_ISSET(SEARCH_SET)) {
+                       F_SET(sp, S_SRE_SET);
+                       sp->searchdir = dir;
+                       sp->sre = **rep;
+               }
+               return (0);
+       }
+
+       re_flags = 0;                           /* Set RE flags. */
+       if (O_ISSET(sp, O_EXTENDED))
+               re_flags |= REG_EXTENDED;
+       if (O_ISSET(sp, O_IGNORECASE))
+               re_flags |= REG_ICASE;
+
+       if (LF_ISSET(SEARCH_PARSE)) {           /* Parse the string. */
+               /* Set delimiter. */
+               delim = *ptrn++;
+
+               /* Find terminating delimiter, handling escaped delimiters. */
+               for (p = t = ptrn;;) {
+                       if (p[0] == '\0' || p[0] == delim) {
+                               if (p[0] == delim)
+                                       ++p;
+                               *t = '\0';
+                               break;
+                       }
+                       if (p[1] == delim && p[0] == '\\')
+                               ++p;
+                       *t++ = *p++;
+               }
+
+               /*
+                * If characters after the terminating delimiter, it may
+                * be an error, or may be an offset.  In either case, we
+                * return the end of the string, whatever it may be.
+                */
+               if (*p) {
+                       if (get_delta(sp, &p, deltap, flagp))
+                               return (1);
+                       if (*p && LF_ISSET(SEARCH_TERM)) {
+                               msgq(sp, M_ERR,
+                       "Characters after search string and/or delta.");
+                               return (1);
+                       }
+               }
+               if (epp != NULL)
+                       *epp = p;
+
+               /* Check for "/   " or other such silliness. */
+               if (*ptrn == '\0')
+                       goto prev;
+
+               if (re_conv(sp, &ptrn, &replaced))
+                       return (1);
+       } else if (LF_ISSET(SEARCH_TAG)) {
+               if (ctag_conv(sp, &ptrn, &replaced))
+                       return (1);
+               re_flags &= ~(REG_EXTENDED | REG_ICASE);
+       }
+
+       /* Compile the RE. */
+       if (eval = regcomp(*rep, ptrn, re_flags))
+               re_error(sp, eval, *rep);
+       else if (LF_ISSET(SEARCH_SET)) {
+               F_SET(sp, S_SRE_SET);
+               sp->searchdir = dir;
+               sp->sre = **rep;
+       }
+
+       /* Free up any extra memory. */
+       if (replaced)
+               FREE_SPACE(sp, ptrn, 0);
+       return (eval);
+}
+
+/*
+ * ctag_conv --
+ *     Convert a tags search path into something that the POSIX
+ *     1003.2 RE functions can handle.
+ */
+static int
+ctag_conv(sp, ptrnp, replacedp)
+       SCR *sp;
+       char **ptrnp;
+       int *replacedp;
+{
+       size_t blen, len;
+       int lastdollar;
+       char *bp, *p, *t;
+
+       *replacedp = 0;
+
+       len = strlen(p = *ptrnp);
+
+       /* Max memory usage is 2 times the length of the string. */
+       GET_SPACE_RET(sp, bp, blen, len * 2);
+
+       t = bp;
+
+       /* The last charcter is a '/' or '?', we just strip it. */
+       if (p[len - 1] == '/' || p[len - 1] == '?')
+               p[len - 1] = '\0';
+
+       /* The next-to-last character is a '$', and it's magic. */
+       if (p[len - 2] == '$') {
+               lastdollar = 1;
+               p[len - 2] = '\0';
+       } else
+               lastdollar = 0;
+
+       /* The first character is a '/' or '?', we just strip it. */
+       if (p[0] == '/' || p[0] == '?')
+               ++p;
+
+       /* The second character is a '^', and it's magic. */
+       if (p[0] == '^')
+               *t++ = *p++;
+               
+       /*
+        * Escape every other magic character we can find, stripping the
+        * backslashes ctags inserts to escape the search delimiter
+        * characters.
+        */
+       while (p[0]) {
+               /* Ctags escapes the search delimiter characters. */
+               if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
+                       ++p;
+               else if (strchr("^.[]$*", p[0]))
+                       *t++ = '\\';
+               *t++ = *p++;
+       }
+       if (lastdollar)
+               *t++ = '$';
+       *t++ = '\0';
+
+       *ptrnp = bp;
+       *replacedp = 1;
+       return (0);
+}
+
+/*
+ * search_intr --
+ *     Set the interrupt bit in any screen that is interruptible.
+ *
+ * XXX
+ * In the future this may be a problem.  The user should be able to move to
+ * another screen and keep typing while this runs.  If so, and the user has
+ * more than one search/global (see ex/ex_global.c) running, it will be hard
+ * to decide which one to stop.
+ */
+static void
+search_intr(signo)
+       int signo;
+{
+       SCR *sp;
+
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               if (F_ISSET(sp, S_INTERRUPTIBLE))
+                       F_SET(sp, S_INTERRUPTED);
+}
+
+#define        EMPTYMSG        "File empty; nothing to search."
+#define        EOFMSG          "Reached end-of-file without finding the pattern."
+#define        NOTFOUND        "Pattern not found."
+#define        SOFMSG          "Reached top-of-file without finding the pattern."
+#define        WRAPMSG         "Search wrapped."
+
+int
+f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+       SCR *sp;
+       EXF *ep;
+       MARK *fm, *rm;
+       char *ptrn, **eptrn;
+       u_int *flagp;
+{
+       DECLARE_INTERRUPTS;
+       regmatch_t match[1];
+       regex_t *re, lre;
+       recno_t lno;
+       size_t coff, len;
+       long delta;
+       u_int flags;
+       int eval, rval, wrapped;
+       char *l;
+
+       if (file_lline(sp, ep, &lno))
+               return (1);
+       flags = *flagp;
+       if (lno == 0) {
+               if (LF_ISSET(SEARCH_MSG))
+                       msgq(sp, M_INFO, EMPTYMSG);
+               return (1);
+       }
+
+       re = &lre;
+       if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
+               return (1);
+
+       /*
+        * Start searching immediately after the cursor.  If at the end of the
+        * line, start searching on the next line.  This is incompatible (read
+        * bug fix) with the historic vi -- searches for the '$' pattern never
+        * moved forward, and "-t foo" didn't work if "foo" was the first thing
+        * in the file.
+        */
+       if (LF_ISSET(SEARCH_FILE)) {
+               lno = 1;
+               coff = 0;
+       } else {
+               if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               if (fm->cno + 1 >= len) {
+                       if (fm->lno == lno) {
+                               if (!O_ISSET(sp, O_WRAPSCAN)) {
+                                       if (LF_ISSET(SEARCH_MSG))
+                                               msgq(sp, M_INFO, EOFMSG);
+                                       return (1);
+                               }
+                               lno = 1;
+                       } else
+                               lno = fm->lno + 1;
+                       coff = 0;
+               } else {
+                       lno = fm->lno;
+                       coff = fm->cno + 1;
+               }
+       }
+
+       /*
+        * Set up busy message, interrupts.
+        *
+        * F_search is called from the ex_tagfirst() routine, which runs
+        * before the screen really exists.  Make sure we don't step on
+        * anything.
+        */
+       if (sp->s_position != NULL)
+               busy_on(sp, 1, "Searching...");
+       SET_UP_INTERRUPTS(search_intr);
+
+       for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
+               if (F_ISSET(sp, S_INTERRUPTED)) {
+                       msgq(sp, M_INFO, "Interrupted.");
+                       break;
+               }
+               if (wrapped && lno > fm->lno ||
+                   (l = file_gline(sp, ep, lno, &len)) == NULL) {
+                       if (wrapped) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, NOTFOUND);
+                               break;
+                       }
+                       if (!O_ISSET(sp, O_WRAPSCAN)) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, EOFMSG);
+                               break;
+                       }
+                       lno = 0;
+                       wrapped = 1;
+                       continue;
+               }
+
+               /* If already at EOL, just keep going. */
+               if (len && coff == len)
+                       continue;
+
+               /* Set the termination. */
+               match[0].rm_so = coff;
+               match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+               TRACE(sp, "F search: %lu from %u to %u\n",
+                   lno, coff, len ? len - 1 : len);
+#endif
+               /* Search the line. */
+               eval = regexec(re, l, 1, match,
+                   (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
+               if (eval == REG_NOMATCH)
+                       continue;
+               if (eval != 0) {
+                       re_error(sp, eval, re);
+                       break;
+               }
+               
+               /* Warn if wrapped. */
+               if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+                       msgq(sp, M_INFO, WRAPMSG);
+
+               /*
+                * If an offset, see if it's legal.  It's possible to match
+                * past the end of the line with $, so check for that case.
+                */
+               if (delta) {
+                       if (check_delta(sp, ep, delta, lno))
+                               break;
+                       rm->lno = delta + lno;
+                       rm->cno = 0;
+               } else {
+#if defined(DEBUG) && 0
+                       TRACE(sp, "found: %qu to %qu\n",
+                           match[0].rm_so, match[0].rm_eo);
+#endif
+                       rm->lno = lno;
+                       rm->cno = match[0].rm_so;
+
+                       /*
+                        * If a change command, it's possible to move beyond
+                        * the end of a line.  Historic vi generally got this
+                        * wrong (try "c?$<cr>").  Not all that sure this gets
+                        * it right, there are lots of strange cases.
+                        */
+                       if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
+                               rm->cno = len ? len - 1 : 0;
+               }
+               rval = 0;
+               break;
+       }
+
+interrupt_err:
+
+       /* Turn off busy message, interrupts. */
+       if (sp->s_position != NULL)
+               busy_off(sp);
+       TEAR_DOWN_INTERRUPTS;
+
+       return (rval);
+}
+
+int
+b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+       SCR *sp;
+       EXF *ep;
+       MARK *fm, *rm;
+       char *ptrn, **eptrn;
+       u_int *flagp;
+{
+       DECLARE_INTERRUPTS;
+       regmatch_t match[1];
+       regex_t *re, lre;
+       recno_t lno;
+       size_t coff, len, last;
+       long delta;
+       u_int flags;
+       int eval, rval, wrapped;
+       char *l;
+
+       if (file_lline(sp, ep, &lno))
+               return (1);
+       flags = *flagp;
+       if (lno == 0) {
+               if (LF_ISSET(SEARCH_MSG))
+                       msgq(sp, M_INFO, EMPTYMSG);
+               return (1);
+       }
+
+       re = &lre;
+       if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
+               return (1);
+
+       /* If in the first column, start searching on the previous line. */
+       if (fm->cno == 0) {
+               if (fm->lno == 1) {
+                       if (!O_ISSET(sp, O_WRAPSCAN)) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, SOFMSG);
+                               return (1);
+                       }
+               } else
+                       lno = fm->lno - 1;
+       } else
+               lno = fm->lno;
+
+       /* Turn on busy message, interrupts. */
+       busy_on(sp, 1, "Searching...");
+
+       if (F_ISSET(sp->gp, G_ISFROMTTY))
+               SET_UP_INTERRUPTS(search_intr);
+
+       for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
+               if (F_ISSET(sp, S_INTERRUPTED)) {
+                       msgq(sp, M_INFO, "Interrupted.");
+                       break;
+               }
+               if (wrapped && lno < fm->lno || lno == 0) {
+                       if (wrapped) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, NOTFOUND);
+                               break;
+                       }
+                       if (!O_ISSET(sp, O_WRAPSCAN)) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, SOFMSG);
+                               break;
+                       }
+                       if (file_lline(sp, ep, &lno))
+                               goto err;
+                       if (lno == 0) {
+                               if (LF_ISSET(SEARCH_MSG))
+                                       msgq(sp, M_INFO, EMPTYMSG);
+                               break;
+                       }
+                       ++lno;
+                       wrapped = 1;
+                       continue;
+               }
+
+               if ((l = file_gline(sp, ep, lno, &len)) == NULL)
+                       goto err;
+
+               /* Set the termination. */
+               match[0].rm_so = 0;
+               match[0].rm_eo = coff ? coff : len;
+
+#if defined(DEBUG) && 0
+               TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
+#endif
+               /* Search the line. */
+               eval = regexec(re, l, 1, match,
+                   (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
+               if (eval == REG_NOMATCH)
+                       continue;
+               if (eval != 0) {
+                       re_error(sp, eval, re);
+                       break;
+               }
+
+               /* Warn if wrapped. */
+               if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+                       msgq(sp, M_INFO, WRAPMSG);
+               
+               if (delta) {
+                       if (check_delta(sp, ep, delta, lno))
+                               break;
+                       rm->lno = delta + lno;
+                       rm->cno = 0;
+               } else {
+#if defined(DEBUG) && 0
+                       TRACE(sp, "found: %qu to %qu\n",
+                           match[0].rm_so, match[0].rm_eo);
+#endif
+                       /*
+                        * Find the last acceptable one in this line.  This
+                        * is really painful, we need a cleaner interface to
+                        * regexec to make this possible.
+                        */
+                       for (;;) {
+                               last = match[0].rm_so;
+                               match[0].rm_so = match[0].rm_eo + 1;
+                               if (match[0].rm_so >= len ||
+                                   coff && match[0].rm_so >= coff)
+                                       break;
+                               match[0].rm_eo = coff ? coff : len;
+                               eval = regexec(re, l, 1, match,
+                                   (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
+                                   REG_STARTEND);
+                               if (eval == REG_NOMATCH)
+                                       break;
+                               if (eval != 0) {
+                                       re_error(sp, eval, re);
+                                       goto err;
+                               }
+                       }
+                       rm->lno = lno;
+
+                       /* See comment in f_search(). */
+                       if (!LF_ISSET(SEARCH_EOL) && last >= len)
+                               rm->cno = len ? len - 1 : 0;
+                       else
+                               rm->cno = last;
+               }
+               rval = 0;
+               break;
+       }
+
+       /* Turn off busy message, interrupts. */
+interrupt_err:
+err:   busy_off(sp);
+
+       if (F_ISSET(sp->gp, G_ISFROMTTY))
+               TEAR_DOWN_INTERRUPTS;
+
+       return (rval);
+}
+
+/*
+ * 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.
+ */
+int
+re_conv(sp, ptrnp, replacedp)
+       SCR *sp;
+       char **ptrnp;
+       int *replacedp;
+{
+       size_t blen, needlen;
+       int magic;
+       char *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 the malloc and memmove's.
+        */
+       for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
+               switch (*p) {
+               case '\\':
+                       switch (*++p) {
+                       case '<':
+                               magic = 1;
+                               needlen += sizeof(RE_WSTART);
+                               break;
+                       case '>':
+                               magic = 1;
+                               needlen += sizeof(RE_WSTOP);
+                               break;
+                       case '~':
+                               if (!O_ISSET(sp, O_MAGIC)) {
+                                       magic = 1;
+                                       needlen += sp->repl_len;
+                               }
+                               break;
+                       case '.':
+                       case '[':
+                       case ']':
+                       case '*':
+                               if (!O_ISSET(sp, O_MAGIC)) {
+                                       magic = 1;
+                                       needlen += 1;
+                               }
+                               break;
+                       default:
+                               needlen += 2;
+                       }
+                       break;
+               case '~':
+                       if (O_ISSET(sp, O_MAGIC)) {
+                               magic = 1;
+                               needlen += sp->repl_len;
+                       }
+                       break;
+               case '.':
+               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.
+        *
+        * XXX
+        * It's nul-terminated, for now.
+        */
+       GET_SPACE_RET(sp, bp, blen, needlen + 1);
+
+       for (p = *ptrnp, t = bp; *p != '\0'; ++p)
+               switch (*p) {
+               case '\\':
+                       switch (*++p) {
+                       case '<':
+                               memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
+                               t += sizeof(RE_WSTART) - 1;
+                               break;
+                       case '>':
+                               memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
+                               t += sizeof(RE_WSTOP) - 1;
+                               break;
+                       case '~':
+                               if (O_ISSET(sp, O_MAGIC))
+                                       *t++ = '~';
+                               else {
+                                       memmove(t, sp->repl, sp->repl_len);
+                                       t += sp->repl_len;
+                               }
+                               break;
+                       case '.':
+                       case '[':
+                       case ']':
+                       case '*':
+                               if (O_ISSET(sp, O_MAGIC))
+                                       *t++ = '\\';
+                               *t++ = *p;
+                               break;
+                       default:
+                               *t++ = '\\';
+                               *t++ = *p;
+                       }
+                       break;
+               case '~':
+                       if (O_ISSET(sp, O_MAGIC)) {
+                               memmove(t, sp->repl, sp->repl_len);
+                               t += sp->repl_len;
+                       } else
+                               *t++ = '~';
+                       break;
+               case '.':
+               case '[':
+               case ']':
+               case '*':
+                       if (!O_ISSET(sp, O_MAGIC))
+                               *t++ = '\\';
+                       *t++ = *p;
+                       break;
+               default:
+                       *t++ = *p;
+                       break;
+               }
+       *t = '\0';
+
+       *ptrnp = bp;
+       *replacedp = 1;
+       return (0);
+}
+
+/*
+ * get_delta --
+ *     Get a line delta.  The trickiness is that the delta can be pretty
+ *     complicated, i.e. "+3-2+3++- ++" is allowed.
+ *
+ * !!!
+ * In historic vi, if you had a delta on a search pattern which was used as
+ * a motion command, the command became a line mode command regardless of the
+ * cursor positions.  A fairly common trick is to use a delta of "+0" to make
+ * the command a line mode command.  This is the only place that knows about
+ * delta's, so we set the return flag information here.
+ */
+static int
+get_delta(sp, dp, valp, flagp)
+       SCR *sp;
+       char **dp;
+       long *valp;
+       u_int *flagp;
+{
+       char *p;
+       long val, tval;
+
+       for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
+               if (isblank(*p)) {
+                       ++p;
+                       continue;
+               }
+               if (*p == '+' || *p == '-') {
+                       if (!isdigit(*(p + 1))) {
+                               if (*p == '+') {
+                                       if (tval == LONG_MAX)
+                                               goto overflow;
+                                       ++tval;
+                               } else {
+                                       if (tval == LONG_MIN)
+                                               goto underflow;
+                                       --tval;
+                               }
+                               ++p;
+                               continue;
+                       }
+               } else
+                       if (!isdigit(*p))
+                               break;
+
+               errno = 0;
+               val = strtol(p, &p, 10);
+               if (errno == ERANGE) {
+                       if (val == LONG_MAX)
+overflow:                      msgq(sp, M_ERR, "Delta value overflow.");
+                       else if (val == LONG_MIN)
+underflow:                     msgq(sp, M_ERR, "Delta value underflow.");
+                       else
+                               msgq(sp, M_SYSERR, NULL);
+                       return (1);
+               }
+               if (val >= 0) {
+                       if (LONG_MAX - val < tval)
+                               goto overflow;
+               } else
+                       if (-(LONG_MIN - tval) > val)
+                               goto underflow;
+               tval += val;
+       }
+       *dp = p;
+       *valp = tval;
+       return (0);
+}
+
+/*
+ * check_delta --
+ *     Check a line delta to see if it's legal.
+ */
+static int
+check_delta(sp, ep, delta, lno)
+       SCR *sp;
+       EXF *ep;
+       long delta;
+       recno_t lno;
+{
+       /* A delta can overflow a record number. */
+       if (delta < 0) {
+               if (lno < LONG_MAX && delta >= (long)lno) {
+                       msgq(sp, M_ERR, "Search offset before line 1.");
+                       return (1);
+               }
+       } else {
+               if (ULONG_MAX - lno < delta) {
+                       msgq(sp, M_ERR, "Delta value overflow.");
+                       return (1);
+               }
+               if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
+                       msgq(sp, M_ERR, "Search offset past end-of-file.");
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * re_error --
+ *     Report a regular expression error.
+ */
+void
+re_error(sp, errcode, preg)
+       SCR *sp;
+       int errcode;
+       regex_t *preg;
+{
+       size_t s;
+       char *oe;
+
+       s = regerror(errcode, preg, "", 0);
+       if ((oe = malloc(s)) == NULL)
+               msgq(sp, M_SYSERR, NULL);
+       else {
+               (void)regerror(errcode, preg, oe, s);
+               msgq(sp, M_ERR, "RE error: %s", oe);
+       }
+       free(oe);
+}
diff --git a/usr.bin/vi/search.h b/usr.bin/vi/search.h
new file mode 100644 (file)
index 0000000..00feff6
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)search.h    8.7 (Berkeley) 11/18/93
+ */
+
+#define        RE_WSTART       "[[:<:]]"       /* Not-in-word search patterns. */
+#define        RE_WSTOP        "[[:>:]]"
+
+#define        SEARCH_DELTA    0x001           /* A delta part of the search.*/
+#define        SEARCH_EOL      0x002           /* Offset past EOL is okay. */
+#define        SEARCH_FILE     0x004           /* Search the entire file. */
+#define        SEARCH_MSG      0x008           /* Display search warning messages. */
+#define        SEARCH_PARSE    0x010           /* Parse the search pattern. */
+#define        SEARCH_SET      0x020           /* Set search direction. */
+#define        SEARCH_TAG      0x040           /* Search pattern is a tag pattern. */
+#define        SEARCH_TERM     0x080           /* Search pattern should terminate. */
+
+enum direction { NOTSET, FORWARD, BACKWARD };
+enum cdirection        { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH };
+
+/* Search functions. */
+int    b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int    f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int    re_conv __P((SCR *, char **, int *));
+void   re_error __P((SCR *, int, regex_t *));
diff --git a/usr.bin/vi/seq.c b/usr.bin/vi/seq.c
new file mode 100644 (file)
index 0000000..4d6d449
--- /dev/null
@@ -0,0 +1,299 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)seq.c      8.21 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * seq_set --
+ *     Internal version to enter a sequence.
+ */
+int
+seq_set(sp, name, nlen, input, ilen, output, olen, stype, userdef)
+       SCR *sp;
+       char *name, *input, *output;
+       size_t nlen, ilen, olen;
+       enum seqtype stype;
+       int userdef;
+{
+       SEQ *lastqp, *qp;
+       CHAR_T *p;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "seq_set: name {%s} input {%s} output {%s}\n",
+           name ? name : "", input, output);
+#endif
+       /* Just replace the output field in any previous occurrence. */
+       if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) {
+               if ((p = v_strdup(sp, output, olen)) == NULL)
+                       goto mem1;
+               FREE(qp->output, qp->olen);
+               qp->olen = olen;
+               qp->output = p;
+               return (0);
+       }
+
+       /* Allocate and initialize space. */
+       CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ));
+       if (qp == NULL)
+               goto mem1;
+       if (name == NULL)
+               qp->name = NULL;
+       else if ((qp->name = v_strdup(sp, name, nlen)) == NULL)
+               goto mem2;
+       if ((qp->input = v_strdup(sp, input, ilen)) == NULL)
+               goto mem3;
+       if ((qp->output = v_strdup(sp, output, olen)) == NULL) {
+               FREE(qp->input, ilen);
+mem3:          if (qp->name != NULL)
+                       FREE(qp->name, nlen);
+mem2:          FREE(qp, sizeof(SEQ));
+mem1:          msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+
+       qp->stype = stype;
+       qp->nlen = nlen;
+       qp->ilen = ilen;
+       qp->olen = olen;
+       qp->flags = userdef ? S_USERDEF : 0;
+
+       /* Link into the chain. */
+       if (lastqp == NULL) {
+               LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
+       } else {
+               LIST_INSERT_AFTER(lastqp, qp, q);
+       }
+
+       /* Set the fast lookup bit. */
+       bit_set(sp->gp->seqb, qp->input[0]);
+
+       return (0);
+}
+
+/*
+ * seq_delete --
+ *     Delete a sequence.
+ */
+int
+seq_delete(sp, input, ilen, stype)
+       SCR *sp;
+       char *input;
+       size_t ilen;
+       enum seqtype stype;
+{
+       SEQ *qp;
+
+       if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL)
+               return (1);
+
+       LIST_REMOVE(qp, q);
+       if (qp->name != NULL)
+               FREE(qp->name, qp->nlen);
+       FREE(qp->input, qp->ilen);
+       FREE(qp->output, qp->olen);
+       FREE(qp, sizeof(SEQ));
+       return (0);
+}
+
+/*
+ * seq_find --
+ *     Search the sequence list for a match to a buffer, if ispartial
+ *     isn't NULL, partial matches count.
+ */
+SEQ *
+seq_find(sp, lastqp, input, ilen, stype, ispartialp)
+       SCR *sp;
+       SEQ **lastqp;
+       char *input;
+       size_t ilen;
+       enum seqtype stype;
+       int *ispartialp;
+{
+       SEQ *lqp, *qp;
+       int diff;
+
+       /*
+        * Ispartialp is a location where we return if there was a
+        * partial match, i.e. if the string were extended it might
+        * match something.
+        * 
+        * XXX
+        * Overload the meaning of ispartialp; only the terminal key
+        * search doesn't want the search limited to complete matches,
+        * i.e. ilen may be longer than the match.
+        */
+       if (ispartialp != NULL)
+               *ispartialp = 0;
+       for (lqp = NULL, qp = sp->gp->seqq.lh_first;
+           qp != NULL; lqp = qp, qp = qp->q.le_next) {
+               /* Fast checks on the first character and type. */
+               if (qp->input[0] > input[0])
+                       break;
+               if (qp->input[0] < input[0] || qp->stype != stype)
+                       continue;
+
+               /* Check on the real comparison. */
+               diff = memcmp(qp->input, input, MIN(qp->ilen, ilen));
+               if (diff > 0)
+                       break;
+               if (diff < 0)
+                       continue;
+               /*
+                * If the entry is the same length as the string, return a
+                * match.  If the entry is shorter than the string, return a
+                * match if called from the terminal key routine.  Otherwise,
+                * keep searching for a complete match.
+                */
+               if (qp->ilen <= ilen) {
+                       if (qp->ilen == ilen || ispartialp != NULL) {
+                               if (lastqp != NULL)
+                                       *lastqp = lqp;
+                               return (qp);
+                       }
+                       continue;
+               }
+               /*
+                * If the entry longer than the string, return partial match
+                * if called from the terminal key routine.  Otherwise, no
+                * match.
+                */
+               if (ispartialp != NULL)
+                       *ispartialp = 1;
+               break;
+       }
+       if (lastqp != NULL)
+               *lastqp = lqp;
+       return (NULL);
+}
+
+/*
+ * seq_dump --
+ *     Display the sequence entries of a specified type.
+ */
+int
+seq_dump(sp, stype, isname)
+       SCR *sp;
+       enum seqtype stype;
+       int isname;
+{
+       CHNAME const *cname;
+       SEQ *qp;
+       int cnt, len, olen, tablen;
+       char *p;
+
+       cnt = 0;
+       cname = sp->gp->cname;
+       tablen = O_VAL(sp, O_TABSTOP);
+       for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+               if (stype != qp->stype)
+                       continue;
+               ++cnt;
+               for (p = qp->input,
+                   olen = qp->ilen, len = 0; olen > 0; --olen, ++len)
+                       (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name);
+               for (len = tablen - len % tablen; len; --len)
+                       (void)ex_printf(EXCOOKIE, " ");
+
+               for (p = qp->output, olen = qp->olen; olen > 0; --olen)
+                       (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name);
+
+               if (isname && qp->name != NULL) {
+                       for (len = tablen - len % tablen; len; --len)
+                               (void)ex_printf(EXCOOKIE, " ");
+                       for (p = qp->name, olen = qp->nlen; olen > 0; --olen)
+                               (void)ex_printf(EXCOOKIE,
+                                   "%s", cname[*p++].name);
+               }
+               (void)ex_printf(EXCOOKIE, "\n");
+       }
+       return (cnt);
+}
+
+/*
+ * seq_save --
+ *     Save the sequence entries to a file.
+ */
+int
+seq_save(sp, fp, prefix, stype)
+       SCR *sp;
+       FILE *fp;
+       char *prefix;
+       enum seqtype stype;
+{
+       CHAR_T esc;
+       SEQ *qp;
+       size_t olen;
+       int ch;
+       char *p;
+
+       /* Write a sequence command for all keys the user defined. */
+       (void)term_key_ch(sp, K_VLNEXT, &esc);
+       for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+               if (!F_ISSET(qp, S_USERDEF))
+                       continue;
+               if (stype != qp->stype)
+                       continue;
+               if (prefix)
+                       (void)fprintf(fp, "%s", prefix);
+               for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
+                       ch = *p++;
+                       if (ch == esc || ch == '|' ||
+                           isblank(ch) || term_key_val(sp, ch) == K_NL)
+                               (void)putc(esc, fp);
+                       (void)putc(ch, fp);
+               }
+               (void)putc(' ', fp);
+               for (p = qp->output, olen = qp->olen; olen > 0; --olen) {
+                       ch = *p++;
+                       if (ch == esc || ch == '|' ||
+                           term_key_val(sp, ch) == K_NL)
+                               (void)putc(esc, fp);
+                       (void)putc(ch, fp);
+               }
+               (void)putc('\n', fp);
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/seq.h b/usr.bin/vi/seq.h
new file mode 100644 (file)
index 0000000..b9d9ac6
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)seq.h       8.7 (Berkeley) 12/2/93
+ */
+
+/*
+ * Map and abbreviation structures.
+ *
+ * The map structure is doubly linked list, sorted by input string and by
+ * input length within the string.  (The latter is necessary so that short
+ * matches will happen before long matches when the list is searched.)
+ * Additionally, there is a bitmap which has bits set if there are entries
+ * starting with the corresponding character.  This keeps us from walking
+ * the list unless it's necessary.
+ *
+ * XXX
+ * The fast-lookup bits are never turned off -- users don't usually unmap
+ * things, though, so it's probably not a big deal.
+ */
+                                       /* Sequence type. */
+enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT };
+
+struct _seq {
+       LIST_ENTRY(_seq) q;             /* Linked list of all sequences. */
+       enum seqtype stype;             /* Sequence type. */
+       char    *name;                  /* Sequence name (if any). */
+       size_t   nlen;                  /* Name length. */
+       char    *input;                 /* Sequence input keys. */
+       size_t   ilen;                  /* Input keys length. */
+       char    *output;                /* Sequence output keys. */
+       size_t   olen;                  /* Output keys length. */
+
+#define        S_USERDEF       0x01            /* If sequence user defined. */
+       u_char   flags;
+};
+
+int     abbr_save __P((SCR *, FILE *));
+int     map_save __P((SCR *, FILE *));
+int     seq_delete __P((SCR *, char *, size_t, enum seqtype));
+int     seq_dump __P((SCR *, enum seqtype, int));
+SEQ    *seq_find __P((SCR *, SEQ **, char *, size_t, enum seqtype, int *));
+void    seq_init __P((SCR *));
+int     seq_save __P((SCR *, FILE *, char *, enum seqtype));
+int     seq_set __P((SCR *, char *, size_t,
+           char *, size_t, char *, size_t, enum seqtype, int));
diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c
new file mode 100644 (file)
index 0000000..8b55fad
--- /dev/null
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_confirm.c      8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+enum confirm
+sex_confirm(sp, ep, fp, tp)
+       SCR *sp;
+       EXF *ep;
+       MARK *fp, *tp;
+{
+       CH ikey;
+       recno_t cnt;
+
+       if (ex_print(sp, ep, fp, tp, 0))
+               return (CONF_QUIT);
+
+       for (cnt = fp->cno; cnt; --cnt)
+               (void)putc(' ', stdout);
+       for (cnt = tp->cno; cnt; --cnt)
+               (void)putc('^', stdout);
+       (void)fprintf(stdout, "[ynq]");
+
+       if (term_key(sp, &ikey, 0) != INP_OK)
+               return (CONF_QUIT);
+       switch (ikey.ch) {
+       case YES_CH:
+               return (CONF_YES);
+       case QUIT_CH:
+               return (CONF_QUIT);
+       default:
+       case NO_CH:
+               return (CONF_NO);
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c
new file mode 100644 (file)
index 0000000..54b7343
--- /dev/null
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_get.c  8.12 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+static void    repaint __P((SCR *, int, char *, size_t));
+
+#define        DISPLAY(wval, ch, col) {                                        \
+       size_t __len;                                                   \
+       int __ch;                                                       \
+       if ((__ch = (ch)) == '\t') {                                    \
+               __len = O_VAL(sp, O_TABSTOP) -                          \
+                   ((col) % O_VAL(sp, O_TABSTOP));                     \
+               (col) += (wval) = __len;                                \
+               while (__len--)                                         \
+                       putc(' ', stdout);                              \
+       } else {                                                        \
+               (col) += (wval) = cname[(__ch)].len;                    \
+               (void)fprintf(stdout,                                   \
+                   "%.*s", cname[(__ch)].len, cname[(__ch)].name);     \
+       }                                                               \
+}
+
+#define        ERASECH {                                                       \
+       for (cnt = tp->wd[tp->len]; cnt > 0; --cnt, --col)              \
+               (void)fprintf(stdout, "%s", "\b \b");                   \
+}
+
+/*
+ * sex_get --
+ *     Fill a buffer from the terminal for ex.
+ */
+enum input
+sex_get(sp, ep, tiqh, prompt, flags)
+       SCR *sp;
+       EXF *ep;
+       TEXTH *tiqh;
+       int prompt;
+       u_int flags;
+{
+       enum { Q_NOTSET, Q_THISCHAR } quoted;
+       CHNAME const *cname;            /* Character map. */
+       TEXT *tp;                       /* Input text structures. */
+       CH ikey;                        /* Input character. */
+       size_t col;                     /* 0-N: screen column. */
+       size_t cnt;
+       u_int iflags;                   /* Input flags. */
+       int rval;
+
+#ifdef DEBUG
+       if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR))
+               abort();
+#endif
+       /*
+        * 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 (tiqh->cqh_first != (void *)tiqh) {
+               tp = tiqh->cqh_first;
+               if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
+                       text_lfree(tiqh);
+                       goto newtp;
+               }
+               tp->len = 0;
+       } else {
+newtp:         if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
+                       return (INP_ERR);
+               CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+       }
+
+       cname = sp->gp->cname;
+       if (LF_ISSET(TXT_PROMPT) && O_ISSET(sp, O_PROMPT)) {
+               (void)fprintf(stdout, "%s", cname[prompt].name);
+               col = cname[prompt].len;
+       } else
+               col = 0;
+
+       iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+       for (quoted = Q_NOTSET;;) {
+               (void)fflush(stdout);
+
+               if (rval = term_key(sp, &ikey, iflags))
+                       return (rval);
+
+               BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+               BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1);
+
+               if (quoted == Q_THISCHAR) {
+                       ERASECH;
+                       goto ins_ch;
+               }
+
+               switch (ikey.value) {
+               case K_CNTRLZ:
+                       sex_suspend(sp);
+                       /* FALLTHROUGH */
+               case K_CNTRLR:
+                       repaint(sp, prompt, tp->lb, tp->len);
+                       break;
+               case K_CR:
+               case K_NL:
+                       if (LF_ISSET(TXT_NLECHO)) {
+                               (void)putc('\r', stdout);
+                               (void)putc('\n', stdout);
+                               (void)fflush(stdout);
+                       }
+                       /* Terminate with a newline, needed by filter. */
+                       tp->lb[tp->len] = '\0';
+                       return (INP_OK);
+               case K_VERASE:
+                       if (tp->len) {
+                               --tp->len;
+                               ERASECH;
+                       }
+                       break;
+               case K_VKILL:
+                       for (; tp->len; --tp->len)
+                               ERASECH;
+                       break;
+               case K_VLNEXT:
+                       (void)fprintf(stdout, "%s%c", cname['^'].name, '\b');
+                       quoted = Q_THISCHAR;
+                       break;
+               case K_VWERASE:
+                       /* Move to the last non-space character. */
+                       while (tp->len)
+                               if (!isblank(tp->lb[--tp->len])) {
+                                       ++tp->len;
+                                       break;
+                               } else
+                                       ERASECH;
+
+                       /* Move to the last space character. */
+                       while (tp->len)
+                               if (isblank(tp->lb[--tp->len])) {
+                                       ++tp->len;
+                                       break;
+                               } else
+                                       ERASECH;
+                       break;
+               default:
+ins_ch:                        tp->lb[tp->len] = ikey.ch;
+                       DISPLAY(tp->wd[tp->len], ikey.ch, col);
+                       ++tp->len;
+                       quoted = Q_NOTSET;
+                       break;
+               }
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * sex_get_notty --
+ *     Fill a buffer from the terminal for ex, but don't echo
+ *     input.
+ */
+enum input
+sex_get_notty(sp, ep, tiqh, prompt, flags)
+       SCR *sp;
+       EXF *ep;
+       TEXTH *tiqh;
+       int prompt;
+       u_int flags;
+{
+       enum { Q_NOTSET, Q_THISCHAR } quoted;
+       CHNAME const *cname;            /* Character map. */
+       TEXT *tp;                       /* Input text structures. */
+       CH ikey;                        /* Input character. */
+       size_t col;                     /* 0-N: screen column. */
+       u_int iflags;                   /* Input flags. */
+       int rval;
+
+#ifdef DEBUG
+       if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR))
+               abort();
+#endif
+       /*
+        * 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 (tiqh->cqh_first != (void *)tiqh) {
+               tp = tiqh->cqh_first;
+               if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
+                       text_lfree(tiqh);
+                       goto newtp;
+               }
+               tp->len = 0;
+       } else {
+newtp:         if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
+                       return (INP_ERR);
+               CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+       }
+
+       cname = sp->gp->cname;
+       col = 0;
+
+       iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+       for (quoted = Q_NOTSET;;) {
+               if (rval = term_key(sp, &ikey, iflags))
+                       return (rval);
+
+               BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+               BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1);
+
+               if (quoted == Q_THISCHAR)
+                       goto ins_ch;
+
+               switch (ikey.value) {
+               case K_CNTRLZ:
+                       sex_suspend(sp);
+                       /* FALLTHROUGH */
+               case K_CNTRLR:
+                       break;
+               case K_CR:
+               case K_NL:
+                       /* Terminate with a newline, needed by filter. */
+                       tp->lb[tp->len] = '\0';
+                       return (INP_OK);
+               case K_VERASE:
+                       if (tp->len)
+                               --tp->len;
+                       break;
+               case K_VKILL:
+                       tp->len = 0;
+                       break;
+               case K_VLNEXT:
+                       quoted = Q_THISCHAR;
+                       break;
+               case K_VWERASE:
+                       /* Move to the last non-space character. */
+                       while (tp->len)
+                               if (!isblank(tp->lb[--tp->len])) {
+                                       ++tp->len;
+                                       break;
+                               }
+
+                       /* Move to the last space character. */
+                       while (tp->len)
+                               if (isblank(tp->lb[--tp->len])) {
+                                       ++tp->len;
+                                       break;
+                               }
+                       break;
+               default:
+ins_ch:                        tp->lb[tp->len] = ikey.ch;
+                       ++tp->len;
+                       quoted = Q_NOTSET;
+                       break;
+               }
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * repaint --
+ *     Repaint the line.
+ */
+static void
+repaint(sp, prompt, p, len)
+       SCR *sp;
+       int prompt;
+       char *p;
+       size_t len;
+{
+       CHNAME const *cname;
+       size_t col;
+       u_char width;
+
+       cname = sp->gp->cname;
+
+       (void)putc('\n', stdout);
+       if (prompt && O_ISSET(sp, O_PROMPT)) {  /* Display prompt. */
+               (void)fprintf(stdout, "%s", cname[prompt].name);
+               col = cname[prompt].len;
+       } else
+               col = 0;
+
+       while (len--)
+               DISPLAY(width, *p++, col);
+}
diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c
new file mode 100644 (file)
index 0000000..0ba5892
--- /dev/null
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_refresh.c      8.5 (Berkeley) 11/18/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "sex_screen.h"
+
+/*
+ * sex_refresh --
+ *     In ex, just display any messages.
+ */
+int
+sex_refresh(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       MSG *mp;
+
+       if (F_ISSET(sp, S_RESIZE)) {
+               sp->rows = O_VAL(sp, O_LINES);
+               sp->cols = O_VAL(sp, O_COLUMNS);
+               F_CLR(sp, S_RESIZE);
+       }
+
+       /* Ring the bell. */
+       if (F_ISSET(sp, S_BELLSCHED)) {
+               sex_bell(sp);
+               F_CLR(sp, S_BELLSCHED);
+       }
+
+       for (mp = sp->msgq.lh_first;
+           mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) {
+               (void)fprintf(stdout, "%.*s\n", (int)mp->len, mp->mbuf);
+               F_SET(mp, M_EMPTY);
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c
new file mode 100644 (file)
index 0000000..d087d9f
--- /dev/null
@@ -0,0 +1,214 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_screen.c       8.30 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+#include "../svi/svi_screen.h"
+
+static void    sex_abort __P((void));
+static int     sex_noop __P((void));
+static int     sex_nope __P((SCR *));
+
+/*
+ * sex_screen_init --
+ *     Initialize the ex screen.
+ */
+int
+sex_screen_init(sp)
+       SCR *sp;
+{
+       /* Initialize support routines. */
+       sp->s_bell              = sex_bell;
+       sp->s_bg                = (int (*)())sex_nope;
+       sp->s_busy              = (int (*)())sex_busy;
+       sp->s_change            = (int (*)())sex_noop; 
+       sp->s_chposition        = (size_t (*)())sex_abort; 
+       sp->s_clear             = (int (*)())sex_noop; 
+       sp->s_column            = (int (*)())sex_abort;
+       sp->s_confirm           = sex_confirm;
+       sp->s_down              = (int (*)())sex_abort;
+       sp->s_edit              = sex_screen_edit;
+       sp->s_end               = (int (*)())sex_noop;
+       sp->s_ex_cmd            = (int (*)())sex_abort;
+       sp->s_ex_run            = (int (*)())sex_abort;
+       sp->s_ex_write          = (int (*)())sex_abort;
+       sp->s_fg                = (int (*)())sex_nope;
+       sp->s_fill              = (int (*)())sex_abort;
+       sp->s_get               = F_ISSET(sp->gp,
+                                   G_ISFROMTTY) ? sex_get : sex_get_notty;
+       sp->s_key_read          = sex_key_read;
+       sp->s_optchange         = (int (*)())sex_noop;
+       sp->s_position          = (int (*)())sex_abort;
+       sp->s_rabs              = (int (*)())sex_nope;
+       sp->s_refresh           = sex_refresh;
+       sp->s_relative          = (size_t (*)())sex_abort;
+       sp->s_rrel              = (int (*)())sex_nope;
+       sp->s_split             = (int (*)())sex_nope;
+       sp->s_suspend           = sex_suspend;
+       sp->s_up                = (int (*)())sex_abort;
+
+       return (0);
+}
+
+/*
+ * sex_screen_copy --
+ *     Copy to a new screen.
+ */
+int
+sex_screen_copy(orig, sp)
+       SCR *orig, *sp;
+{
+       return (0);
+}
+
+/*
+ * sex_screen_end --
+ *     End a screen.
+ */
+int
+sex_screen_end(sp)
+       SCR *sp;
+{
+       return (0);
+}
+
+/*
+ * sex_screen_edit --
+ *     Main ex screen loop.  The ex screen is relatively uncomplicated.
+ *     As long as it has a stdio FILE pointer for output, it's happy.
+ */
+int
+sex_screen_edit(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       struct termios rawt, t;
+       GS *saved_gp;
+       int force, rval;
+
+       /* Initialize the terminal state. */
+       if (F_ISSET(sp->gp, G_ISFROMTTY))
+               SEX_RAW(t, rawt);
+
+       /* Write to the terminal. */
+       sp->stdfp = stdout;
+
+       for (;;) {
+               sp->rows = O_VAL(sp, O_LINES);
+               sp->cols = O_VAL(sp, O_COLUMNS);
+
+               /*
+                * Run ex.  If ex fails, sex data structures
+                * may be corrupted, be careful what you do.
+                */
+               if (rval = ex(sp, sp->ep)) {
+                       if (F_ISSET(ep, F_RCV_ON)) {
+                               F_SET(ep, F_RCV_NORM);
+                               (void)rcv_sync(sp, sp->ep);
+                       }
+                       (void)file_end(sp, sp->ep, 1);
+                       (void)screen_end(sp);           /* General SCR info. */
+                       break;
+               }
+
+               saved_gp = sp->gp;
+
+               force = 0;
+               switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+               case S_EXIT_FORCE:
+                       force = 1;
+                       /* FALLTHROUGH */
+               case S_EXIT:
+                       F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+                       if (file_end(sp, sp->ep, force))
+                               break;
+                       (void)screen_end(sp);   /* General SCR info. */
+                       goto ret;
+               case 0:                         /* Changing from ex mode. */
+                       goto ret;
+               case S_FSWITCH:
+                       F_CLR(sp, S_FSWITCH);
+                       break;
+               case S_SSWITCH:
+               default:
+                       abort();
+               }
+       }
+
+       /* Reset the terminal state. */
+ret:   if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t))
+               rval = 1;
+       return (rval);
+}
+
+/*
+ * sex_abort --
+ *     Fake function.  Die.
+ */
+static void
+sex_abort()
+{
+       abort();
+}
+
+/*
+ * sex_noop --
+ *     Fake function.  Do nothing.
+ */
+static int
+sex_noop()
+{
+       return (0);
+}
+
+/*
+ * sex_nope --
+ *     Fake function.  Not in ex, you don't.
+ */
+static int
+sex_nope(sp)
+       SCR *sp;
+{
+       msgq(sp, M_ERR, "Command not applicable to ex mode.");
+       return (1);
+}
diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h
new file mode 100644 (file)
index 0000000..419a2f2
--- /dev/null
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)sex_screen.h        8.12 (Berkeley) 11/29/93
+ */
+
+#define        SEX_NORAW(t)                                                    \
+           tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t))
+
+#define        SEX_RAW(t, rawt) {                                              \
+       if (tcgetattr(STDIN_FILENO, &(t)))                              \
+               return (1);                                             \
+       (rawt) = (t);                                                   \
+       (rawt).c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL);    \
+       (rawt).c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);            \
+       (rawt).c_cc[VMIN] = 1;                                          \
+       (rawt).c_cc[VTIME] = 0;                                         \
+       if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(rawt)))     \
+               return (1);                                             \
+}
+
+void   sex_bell __P((SCR *));
+void   sex_busy __P((SCR *, char const *));
+enum confirm
+       sex_confirm __P((SCR *, EXF *, MARK *, MARK *));
+enum input
+       sex_get __P((SCR *, EXF *, TEXTH *, int, u_int));
+enum input
+       sex_get_notty __P((SCR *, EXF *, TEXTH *, int, u_int));
+enum input
+       sex_key_read __P((SCR *, int *, struct timeval *));
+int    sex_refresh __P((SCR *, EXF *));
+int    sex_screen_copy __P((SCR *, SCR *));
+int    sex_screen_edit __P((SCR *, EXF *));
+int    sex_screen_end __P((SCR *));
+int    sex_split __P((SCR *, char *[]));
+int    sex_suspend __P((SCR *));
diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c
new file mode 100644 (file)
index 0000000..4e3980f
--- /dev/null
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_term.c 8.24 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "script.h"
+
+/*
+ * sex_key_read --
+ *     Read characters from the input.
+ */
+enum input
+sex_key_read(sp, nrp, timeout)
+       SCR *sp;
+       int *nrp;
+       struct timeval *timeout;
+{
+       struct timeval t, *tp;
+       IBUF *tty;
+       SCR *tsp;
+       int maxfd, nr;
+
+       *nrp = 0;
+       tty = sp->gp->tty;
+
+       /*
+        * We're about to block; check for signals.  If a signal received,
+        * clear it immediately, so that if it's reset while being serviced
+        * we won't miss it.
+        *
+        * Signal recipients set global flags.  If one is set, we either
+        * set local flags or call handling routines.  None of this has
+        * anything to do with input keys, but it's something that can't
+        * be done asynchronously without doing a lot of locking to handle
+        * race conditions, and which needs to be done periodically.
+        */
+sigchk:        while (F_ISSET(sp->gp,
+           G_SIGALRM | G_SIGHUP | G_SIGTERM | G_SIGWINCH)) {
+               if (F_ISSET(sp->gp, G_SIGALRM)) {
+                       F_CLR(sp->gp, G_SIGALRM);
+                       for (tsp = sp->gp->dq.cqh_first;
+                           tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+                               if (tsp->ep != NULL &&
+                                   F_ISSET(tsp->ep, F_RCV_ON))
+                                       F_SET(tsp->ep, F_RCV_ALRM);
+               }
+               if (F_ISSET(sp->ep, F_RCV_ALRM)) {
+                       F_CLR(sp->ep, F_RCV_ALRM);
+                       (void)rcv_sync(sp, sp->ep);
+               }
+               if (F_ISSET(sp->gp, G_SIGHUP)) {
+                       F_CLR(sp->gp, G_SIGHUP);
+                       rcv_hup();
+                       /* NOTREACHED */
+               }
+               if (F_ISSET(sp->gp, G_SIGTERM)) {
+                       F_CLR(sp->gp, G_SIGTERM);
+                       rcv_term();
+                       /* NOTREACHED */
+               }
+               if (F_ISSET(sp->gp, G_SIGWINCH)) {
+                       F_CLR(sp->gp, G_SIGWINCH);
+                       set_window_size(sp, 0, 1);
+                       F_SET(sp, S_RESIZE);
+                       (void)sp->s_refresh(sp, sp->ep);
+               }
+       }
+
+       /*
+        * There are three cases here:
+        *
+        * 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(sp->gp, G_ISFROMTTY)) {
+               if ((nr = read(STDIN_FILENO,
+                   tty->ch + tty->next + tty->cnt,
+                   tty->len - (tty->next + tty->cnt))) > 0) {
+                       tty->cnt += *nrp = nr;
+                       return (INP_OK);
+               }
+               return (INP_EOF);
+       }
+
+       /*
+        * 2: A read with an associated timeout.  In this case, we are trying
+        *    to complete a map sequence.  Ignore script windows and timeout
+        *    as specified.  If input arrives, we fall into #3, but because
+        *    timeout isn't NULL, don't read anything but command input.
+        *
+        * If interrupted, go back and check to see what it was.
+        */
+       if (timeout != NULL) {
+               if (F_ISSET(sp, S_SCRIPT))
+                       FD_CLR(sp->script->sh_master, &sp->rdfd);
+               FD_SET(STDIN_FILENO, &sp->rdfd);
+               for (;;) {
+                       switch (select(STDIN_FILENO + 1,
+                           &sp->rdfd, NULL, NULL, timeout)) {
+                       case -1:                /* Error or interrupt. */
+                               if (errno == EINTR)
+                                       goto sigchk;
+                               goto err;
+                       case  1:                /* Characters ready. */
+                               break;
+                       case  0:                /* Timeout. */
+                               return (INP_OK);
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * 3: At this point, we'll take anything that comes.  Select on the
+        *    command file descriptor and the file descriptor for the script
+        *    window if there is one.  Poll the fd's, increasing the timeout
+        *    each time each time we don't get anything until we're blocked
+        *    on I/O.
+        *
+        * If interrupted, go back and check to see what it was.
+        */
+       for (t.tv_sec = t.tv_usec = 0;;) {
+               /*
+                * Reset each time -- sscr_input() may call other
+                * routines which could reset bits.
+                */
+               if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) {
+                       tp = &t;
+
+                       FD_SET(STDIN_FILENO, &sp->rdfd);
+                       if (F_ISSET(sp, S_SCRIPT)) {
+                               FD_SET(sp->script->sh_master, &sp->rdfd);
+                               maxfd =
+                                   MAX(STDIN_FILENO, sp->script->sh_master);
+                       } else
+                               maxfd = STDIN_FILENO;
+               } else {
+                       tp = NULL;
+
+                       FD_SET(STDIN_FILENO, &sp->rdfd);
+                       if (F_ISSET(sp, S_SCRIPT))
+                               FD_CLR(sp->script->sh_master, &sp->rdfd);
+                       maxfd = STDIN_FILENO;
+               }
+               
+               switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) {
+               case -1:                /* Error or interrupt. */
+                       if (errno == EINTR)
+                               goto sigchk;
+err:                   msgq(sp, M_SYSERR, "select");
+                       return (INP_ERR);
+               case 0:                 /* Timeout. */
+                       if (t.tv_usec) {
+                               ++t.tv_sec;
+                               t.tv_usec = 0;
+                       } else
+                               t.tv_usec += 500000;
+                       continue;
+               }
+
+               if (timeout == NULL && F_ISSET(sp, S_SCRIPT) &&
+                   FD_ISSET(sp->script->sh_master, &sp->rdfd)) {
+                       sscr_input(sp);
+                       continue;
+               }
+
+               switch (nr = read(STDIN_FILENO,
+                   tty->ch + tty->next + tty->cnt,
+                   tty->len - (tty->next + tty->cnt))) {
+               case  0:                        /* EOF. */
+                       return (INP_EOF);
+               case -1:                        /* Error or interrupt. */
+                       if (errno == EINTR)
+                               goto sigchk;
+                       msgq(sp, M_SYSERR, "read");
+                       return (INP_ERR);
+               default:
+                       tty->cnt += *nrp = nr;
+                       return (INP_OK);
+               }
+               /* NOTREACHED */
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c
new file mode 100644 (file)
index 0000000..25eb17f
--- /dev/null
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_util.c 8.9 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "sex_screen.h"
+
+/*
+ * sex_bell --
+ *     Ring the bell.
+ */
+void
+sex_bell(sp)
+       SCR *sp;
+{
+       (void)write(STDOUT_FILENO, "\07", 1);           /* \a */
+}
+
+void
+sex_busy(sp, msg)
+       SCR *sp;
+       char const *msg;
+{
+       (void)fprintf(stdout, "%s\n", msg);
+       (void)fflush(stdout);
+}
+
+/*
+ * sex_suspend --
+ *     Suspend an ex screen.
+ */
+int
+sex_suspend(sp)
+       SCR *sp;
+{
+       struct termios t;
+       int rval;
+
+       /* Save ex/vi terminal settings, and restore the original ones. */
+       if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+               (void)tcgetattr(STDIN_FILENO, &t);
+               (void)tcsetattr(STDIN_FILENO,
+                   TCSADRAIN, &sp->gp->original_termios);
+       }
+
+       /* Kill the process group. */
+       F_SET(sp->gp, G_SLEEPING);
+       if (rval = kill(0, SIGTSTP))
+               msgq(sp, M_SYSERR, "SIGTSTP");
+       F_CLR(sp->gp, G_SLEEPING);
+
+       /* Restore ex/vi terminal settings. */
+       if (F_ISSET(sp->gp, G_ISFROMTTY))
+               (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &t);
+
+       return (rval);
+}
diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c
new file mode 100644 (file)
index 0000000..01546d7
--- /dev/null
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_confirm.c      8.5 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+enum confirm
+svi_confirm(sp, ep, fp, tp)
+       SCR *sp;
+       EXF *ep;
+       MARK *fp, *tp;
+{
+       CH ikey;
+       size_t oldy, oldx;
+
+       /*
+        * Refresh the cursor first -- this means that we won't have to
+        * set S_UPDATE_MODE to keep refresh from erasing the mode line
+        * or SVI_CUR_INVALID because we sneaked the cursor off somewhere
+        * else.
+        */
+       sp->lno = fp->lno;
+       sp->cno = fp->cno;
+       if (svi_paint(sp, ep))
+               return (CONF_QUIT);
+
+       getyx(stdscr, oldy, oldx);
+       MOVE(sp, INFOLINE(sp), 0);
+       clrtoeol();
+       ADDNSTR(CONFSTRING, sizeof(CONFSTRING) - 1);
+       MOVEA(sp, oldy, oldx);
+       refresh();
+
+       if (term_key(sp, &ikey, 0) != INP_OK)
+               return (CONF_QUIT);
+       switch (ikey.ch) {
+       case YES_CH:
+               return (CONF_YES);
+       case QUIT_CH:
+               return (CONF_QUIT);
+       default:
+       case NO_CH:
+               return (CONF_NO);
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c
new file mode 100644 (file)
index 0000000..7a83d40
--- /dev/null
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_ex.c   8.36 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int     svi_ex_done __P((SCR *, EXF *, MARK *));
+static int     svi_ex_scroll __P((SCR *, int, int, CH *));
+
+/*
+ * svi_ex_cmd --
+ *     Execute an ex command.
+ */
+int
+svi_ex_cmd(sp, ep, exp, rp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *exp;
+       MARK *rp;
+{
+       SVI_PRIVATE *svp;
+       int rval;
+
+       svp = SVP(sp);
+       svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+       (void)svi_busy(sp, NULL);
+       rval = exp->cmd->fn(sp, ep, exp);
+
+       /* No longer interruptible. */
+       F_CLR(sp, S_INTERRUPTIBLE);
+
+       (void)msg_rpt(sp, 0);
+       (void)ex_fflush(EXCOOKIE);
+
+       /*
+        * If displayed anything, figure out if we have to wait.  If the
+        * screen wasn't trashed, only one line and there are no waiting
+        * messages, don't wait, but don't overwrite it with mode information
+        * either.  If there's a screen under this one, change the line to
+        * inverse video.
+        */
+       if (svp->extotalcount > 0)
+               if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 &&
+                   (sp->msgq.lh_first == NULL ||
+                   F_ISSET(sp->msgq.lh_first, M_EMPTY)))
+                       F_SET(sp, S_UPDATE_MODE);
+               else
+                       (void)svi_ex_scroll(sp, 1, 0, NULL);
+       return (svi_ex_done(sp, ep, rp) || rval);
+}
+
+/*
+ * svi_ex_run --
+ *     Execute strings of ex commands.
+ */
+int
+svi_ex_run(sp, ep, rp)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+{
+       enum input (*get) __P((SCR *, EXF *, TEXTH *, int, u_int));
+       struct termios rawt, t;
+       CH ikey;
+       SVI_PRIVATE *svp;
+       TEXT *tp;
+       int flags, in_exmode, rval;
+
+       svp = SVP(sp);
+       svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+       /*
+        * There's some tricky stuff going on here to handle when a user has
+        * mapped a key to multiple ex commands.  Historic practice was that
+        * vi ran without any special actions, as if the user were entering
+        * the characters, until ex trashed the screen, e.g. something like a
+        * '!' command.  At that point, we no longer know what the screen
+        * looks like, so we can't afford to overwrite anything.  The solution
+        * is to go into real ex mode until we get to the end of the command
+        * strings.
+        */
+       get = svi_get;
+       flags = TXT_BS | TXT_PROMPT;
+       for (in_exmode = rval = 0;;) {
+               if (get(sp, ep, &sp->tiq, ':', flags) != INP_OK) {
+                       rval = 1;
+                       break;
+               }
+
+               /*
+                * Len is 0 if the user backspaced over the prompt,
+                * 1 if only a CR was entered.
+                */
+               tp = sp->tiq.cqh_first;
+               if (tp->len == 0)
+                       break;
+
+               if (!in_exmode)
+                       (void)svi_busy(sp, NULL);
+
+               (void)ex_icmd(sp, ep, tp->lb, tp->len);
+               (void)ex_fflush(EXCOOKIE);
+
+               /*
+                * The file or screen may have changed, in which case,
+                * the main editor loop takes care of it.
+                */
+               if (F_ISSET(sp, S_MAJOR_CHANGE))
+                       break;
+               
+               /*
+                * If continue not required, and one or no lines, and there
+                * are no waiting messages, don't wait, but don't overwrite
+                * it with mode information either.  If there's a screen under
+                * this one, change the line to inverse video.
+                */
+               if (!F_ISSET(sp, S_CONTINUE) &&
+                   (svp->extotalcount == 0 || svp->extotalcount == 1 &&
+                   (sp->msgq.lh_first == NULL ||
+                   F_ISSET(sp->msgq.lh_first, M_EMPTY)))) {
+                       if (svp->extotalcount == 1)
+                               F_SET(sp, S_UPDATE_MODE);
+                       break;
+               }
+
+               /* If the screen is trashed, go into ex mode. */
+               if (!in_exmode && F_ISSET(sp, S_REFRESH)) {
+                       /* Initialize the terminal state. */
+                       if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+                               SEX_RAW(t, rawt);
+                               get = sex_get;
+                       } else
+                               get = sex_get_notty;
+                       flags = TXT_CR | TXT_NLECHO | TXT_PROMPT;
+                       in_exmode = 1;
+               }
+                       
+               /*
+                * If the user hasn't already indicated that they're done,
+                * they may continue in ex mode by entering a ':'.
+                */
+               if (F_ISSET(sp, S_INTERRUPTED))
+                       break;
+
+               /* No longer interruptible. */
+               F_CLR(sp, S_INTERRUPTIBLE);
+
+               if (in_exmode) {
+                       (void)write(STDOUT_FILENO,
+                           CONTMSG, sizeof(CONTMSG) - 1);
+                       for (;;) {
+                               if (term_user_key(sp, &ikey) != INP_OK) {
+                                       rval = 1;
+                                       goto ret;
+                               }
+                               if (ikey.ch == ' ' || ikey.ch == ':')
+                                       break;
+                               if (ikey.value == K_CR || ikey.value == K_NL)
+                                       break;
+                               sex_bell(sp);
+                       }
+               } else
+                       (void)svi_ex_scroll(sp, 1, 1, &ikey);
+               if (ikey.ch != ':')
+                        break;
+
+               if (in_exmode)
+                       (void)write(STDOUT_FILENO, "\r\n", 2);
+               else {
+                       ++svp->extotalcount;
+                       ++svp->exlinecount;
+               }
+       }
+
+ret:   if (in_exmode) {
+               /* Reset the terminal state. */
+               if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t))
+                       rval = 1;
+               F_SET(sp, S_REFRESH);
+       } else
+               if (svi_ex_done(sp, ep, rp))
+                       rval = 1;
+       F_CLR(sp, S_CONTINUE);
+       return (rval);
+}
+
+/*
+ * svi_ex_done --
+ *     Cleanup from dipping into ex.
+ */
+static int
+svi_ex_done(sp, ep, rp)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+{
+       SMAP *smp;
+       SVI_PRIVATE *svp;
+       recno_t lno;
+       size_t cnt, len;
+
+       /*
+        * The file or screen may have changed, in which case,
+        * the main editor loop takes care of it.
+        */
+       if (F_ISSET(sp, S_MAJOR_CHANGE))
+               return (0);
+
+       /*
+        * Otherwise, the only cursor modifications will be real, however, the
+        * underlying line may have changed; don't trust anything.  This code
+        * has been a remarkably fertile place for bugs.
+        *
+        * Repaint the entire screen if at least half the screen is trashed.
+        * Else, repaint only over the overwritten lines.  The "-2" comes
+        * from one for the mode line and one for the fact that it's an offset.
+        * Note the check for small screens.
+        *
+        * Don't trust ANYTHING.
+        */
+       svp = SVP(sp);
+       if (svp->extotalcount >= HALFTEXT(sp))
+               F_SET(sp, S_REDRAW);
+       else
+               for (cnt = sp->rows - 2; svp->extotalcount--; --cnt)
+                       if (cnt > sp->t_rows) {
+                               MOVE(sp, cnt, 0);
+                               clrtoeol();
+                       } else {
+                               smp = HMAP + cnt;
+                               SMAP_FLUSH(smp);
+                               if (svi_line(sp, ep, smp, NULL, NULL))
+                                       return (1);
+                       }
+       /*
+        * Do a reality check on a cursor value, and make sure it's okay.
+        * If necessary, change it.  Ex keeps track of the line number,
+        * but ex doesn't care about the column and it may have disappeared.
+        */
+       if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno != 0)
+                       GETLINE_ERR(sp, sp->lno);
+               sp->lno = 1;
+               sp->cno = 0;
+       } else if (sp->cno >= len)
+               sp->cno = len ? len - 1 : 0;
+
+       rp->lno = sp->lno;
+       rp->cno = sp->cno;
+       return (0);
+}
+
+/*
+ * svi_ex_write --
+ *     Write out the ex messages.
+ *
+ * There is no tab or character translation going on, so, whatever the ex
+ * and/or curses routines do with special characters is all that gets done.
+ * This is probably okay, I don't see any reason that user's tab settings
+ * should affect ex output, and ex should have displayed everything else
+ * exactly as it wanted it on the screen.
+ */
+int
+svi_ex_write(cookie, line, llen)
+       void *cookie;
+       const char *line;
+       int llen;
+{
+       SCR *sp;
+       SVI_PRIVATE *svp;
+       size_t oldy, oldx;
+       int len, rlen;
+       const char *p;
+
+       /*
+        * XXX
+        * If it's a 4.4BSD system, we could just use fpurge(3).
+        * This shouldn't be too expensive, though.
+        */
+       sp = cookie;
+       svp = SVP(sp);
+       if (F_ISSET(sp, S_INTERRUPTED))
+               return (llen);
+
+       p = line;                       /* In case of a write of 0. */
+       for (rlen = llen; llen;) {
+               /* Get the next 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 + svp->exlcontinue > sp->cols)
+                       len = sp->cols - svp->exlcontinue;
+
+               /*
+                * If the first line output, do nothing.
+                * If the second line output, draw the divider line.
+                * If drew a full screen, remove the divider line.
+                * If it's a continuation line, move to the continuation
+                * point, else, move the screen up.
+                */
+               if (svp->exlcontinue == 0) {
+                       if (svp->extotalcount == 1) {
+                               MOVE(sp, INFOLINE(sp) - 1, 0);
+                               clrtoeol();
+                               if (svi_divider(sp))
+                                       return (-1);
+                               F_SET(svp, SVI_DIVIDER);
+                               ++svp->extotalcount;
+                               ++svp->exlinecount;
+                       }
+                       if (svp->extotalcount == sp->t_maxrows &&
+                           F_ISSET(svp, SVI_DIVIDER)) {
+                               --svp->extotalcount;
+                               --svp->exlinecount;
+                               F_CLR(svp, SVI_DIVIDER);
+                       }
+                       if (svp->extotalcount != 0 &&
+                           svi_ex_scroll(sp, 0, 0, NULL))
+                               return (-1);
+                       MOVE(sp, INFOLINE(sp), 0);
+                       ++svp->extotalcount;
+                       ++svp->exlinecount;
+                       if (F_ISSET(sp, S_INTERRUPTIBLE) &&
+                           F_ISSET(sp, S_INTERRUPTED))
+                               break;
+               } else
+                       MOVE(sp, INFOLINE(sp), svp->exlcontinue);
+
+               /* Display the line. */
+               if (len)
+                       ADDNSTR(line, len);
+
+               /* Clear to EOL. */
+               getyx(stdscr, oldy, oldx);
+               if (oldx < sp->cols)
+                       clrtoeol();
+
+               /* If we loop, it's a new line. */
+               svp->exlcontinue = 0;
+
+               /* Reset for the next line. */
+               line += len;
+               llen -= len;
+               if (p != NULL) {
+                       ++line;
+                       --llen;
+               }
+       }
+       /* Refresh the screen, even if it's a partial. */
+       refresh();
+
+       /* Set up next continuation line. */
+       if (p == NULL)
+               getyx(stdscr, oldy, svp->exlcontinue);
+       return (rlen);
+}
+
+/*
+ * svi_ex_scroll --
+ *     Scroll the screen for ex output.
+ */
+static int
+svi_ex_scroll(sp, mustwait, colon_ok, chp)
+       SCR *sp;
+       int mustwait, colon_ok;
+       CH *chp;
+{
+       CH ikey;
+       SVI_PRIVATE *svp;
+
+       /*
+        * 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.
+        */
+       svp = SVP(sp);
+       if (svp->extotalcount >= sp->rows) {
+               MOVE(sp, 0, 0);
+       } else
+               MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0);
+
+       deleteln();
+
+       /* If there are screens below us, push them back into place. */
+       if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+               MOVE(sp, INFOLINE(sp), 0);
+               insertln();
+       }
+
+       /* If just displayed a full screen, wait. */
+       if (mustwait || svp->exlinecount == sp->t_maxrows) {
+               MOVE(sp, INFOLINE(sp), 0);
+               if (F_ISSET(sp, S_INTERRUPTIBLE)) {
+                       ADDNSTR(CONTMSG_I, (int)sizeof(CONTMSG_I) - 1);
+               } else {
+                       ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1);
+               }
+               clrtoeol();
+               refresh();
+               for (;;) {
+                       if (term_user_key(sp, &ikey) != INP_OK)
+                               return (-1);
+                       if (ikey.ch == ' ')
+                               break;
+                       if (colon_ok && ikey.ch == ':')
+                               break;
+                       if (ikey.value == K_CR || ikey.value == K_NL)
+                               break;
+                       if (ikey.ch == QUIT_CH &&
+                           F_ISSET(sp, S_INTERRUPTIBLE)) {
+                               F_SET(sp, S_INTERRUPTED);
+                               break;
+                       }
+                       svi_bell(sp);
+               }
+               if (chp != NULL)
+                       *chp = ikey;
+               svp->exlinecount = 0;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c
new file mode 100644 (file)
index 0000000..46f5f0e
--- /dev/null
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_get.c  8.19 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "svi_screen.h"
+
+/*
+ * svi_get --
+ *     Fill a buffer from the terminal for vi.
+ */
+enum input
+svi_get(sp, ep, tiqh, prompt, flags)
+       SCR *sp;
+       EXF *ep;
+       TEXTH *tiqh;
+       int prompt;
+       u_int flags;
+{
+       MARK save;
+       SMAP *esmp;
+       recno_t bot_lno, top_lno;
+       size_t bot_off, cnt, top_off;
+       int eval;
+
+       /*
+        * The approach used is to 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.
+        */
+       bot_lno = TMAP->lno;
+       bot_off = TMAP->off;
+       top_lno = HMAP->lno;
+       top_off = HMAP->off;
+       save.lno = sp->lno;
+       save.cno = sp->cno;
+
+       /*
+        * If it's a small screen, TMAP may be small for the screen.
+        * Fix it, filling in fake lines as we go.
+        */
+       if (ISSMALLSCREEN(sp))
+               for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
+                       TMAP[1].lno = TMAP[0].lno + 1;
+                       TMAP[1].off = 1;
+               }
+
+       /* Build the fake entry. */
+       TMAP[1].lno = TMAP[0].lno + 1;
+       TMAP[1].off = 1;
+       SMAP_FLUSH(&TMAP[1]);
+       ++TMAP;
+
+       /* Move to it. */
+       sp->lno = TMAP[0].lno;
+       sp->cno = 0;
+
+       if (O_ISSET(sp, O_ALTWERASE))
+               LF_SET(TXT_ALTWERASE);
+       if (O_ISSET(sp, O_TTYWERASE))
+               LF_SET(TXT_TTYWERASE);
+       LF_SET(TXT_APPENDEOL |
+           TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
+
+       /* Don't update the modeline for now. */
+       F_SET(SVP(sp), SVI_INFOLINE);
+
+       eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags);
+
+       F_CLR(SVP(sp), SVI_INFOLINE);
+
+       /* Put it all back. */
+       --TMAP;
+       sp->lno = save.lno;
+       sp->cno = save.cno;
+
+       /*
+        * If it's a small screen, TMAP may be wrong.  Clear any
+        * lines that might have been overwritten.
+        */
+       if (ISSMALLSCREEN(sp)) {
+               for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+                       MOVE(sp, cnt, 0);
+                       clrtoeol();
+               }
+               TMAP = HMAP + (sp->t_rows - 1);
+       }
+
+       /*
+        * 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 it's not worth caring.
+        */
+       while (bot_lno != TMAP->lno || bot_off != TMAP->off)
+               if (svi_sm_1down(sp, ep))
+                       return (INP_ERR);
+
+       /*
+        * Invalidate the cursor, the line never really existed.  This fixes
+        * a bug where the user searches for the last line on the screen + 1
+        * and the refresh routine thinks that's where we just were.
+        */
+       F_SET(SVP(sp), SVI_CUR_INVALID);
+
+       return (eval ? INP_ERR : INP_OK);
+}
diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c
new file mode 100644 (file)
index 0000000..80d736d
--- /dev/null
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_line.c 8.16 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+#if defined(DEBUG) && 0
+#define        TABCH   '-'
+#define        TABSTR  "--------------------"
+#else
+#define        TABSTR  "                    "
+#define        TABCH   ' '
+#endif
+
+/*
+ * svi_line --
+ *     Update one line on the screen.
+ */
+int
+svi_line(sp, ep, smp, yp, xp)
+       SCR *sp;
+       EXF *ep;
+       SMAP *smp;
+       size_t *xp, *yp;
+{
+       CHNAME const *cname;
+       SMAP *tsmp;
+       size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens;
+       size_t offset_in_char, offset_in_line;
+       size_t oldy, oldx;
+       int ch, is_cached, is_infoline, is_partial, is_tab, listset;
+       char *p, nbuf[10];
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "svi_line: row %u: line: %u off: %u\n",
+           smp - HMAP, smp->lno, smp->off);
+#endif
+
+       /*
+        * 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)
+               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 svi_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.
+        */
+       getyx(stdscr, oldy, oldx);
+       MOVE(sp, smp - HMAP, 0);
+
+       /* Get the character map. */
+       cname = sp->gp->cname;
+
+       /* Get a copy of the line. */
+       p = file_gline(sp, ep, smp->lno, &len);
+
+       /*
+        * Special case if we're printing the info/mode line.  Skip printing
+        * the leading number, as well as other minor setup.  If painting the
+        * line between two screens, it's always in reverse video.  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 screens to skip until a character is displayed.
+        * Left-right screens are special, because we don't bother building
+        * a buffer to be skipped over.
+        *
+        * Set the number of columns for this screen.
+        */
+       cols_per_screen = sp->cols;
+       if (is_infoline = ISINFOLINE(sp, smp)) {
+               listset = 0;
+               if (O_ISSET(sp, O_LEFTRIGHT))
+                       skip_screens = 0;
+               else
+                       skip_screens = smp->off - 1;
+       } else {
+               listset = O_ISSET(sp, O_LIST);
+               skip_screens = smp->off - 1;
+
+               /*
+                * If O_NUMBER is set and it's line number 1 or the line exists
+                * and this is the first screen of a folding line or any left-
+                * right line, display the line number.
+                */
+               if (O_ISSET(sp, O_NUMBER)) {
+                       cols_per_screen -= O_NUMBER_LENGTH;
+                       if ((smp->lno == 1 || p != NULL) && skip_screens == 0) {
+                               (void)snprintf(nbuf,
+                                   sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+                               ADDSTR(nbuf);
+                       }
+               }
+       }
+
+       /*
+        * Special case non-existent lines and the first line of an empty
+        * file.  In both cases, the cursor position is 0, but corrected
+        * for the O_NUMBER field if it was displayed.
+        */
+       if (p == NULL || 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)
+                       goto ret;
+
+               /* Set line cacheing information. */
+               smp->c_sboff = smp->c_eboff = 0;
+               smp->c_scoff = smp->c_eclen = 0;
+
+               if (p == NULL) {
+                       if (smp->lno != 1)
+                               ADDCH(listset && skip_screens == 0 ? '$' : '~');
+               } else if (listset && skip_screens == 0)
+                       ADDCH('$');
+
+               clrtoeol();
+               MOVEA(sp, oldy, oldx);
+               return (0);
+       }
+
+       /*
+        * If we wrote a line that's this or a previous one, we can do this
+        * much more quickly -- 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, 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.
+        */
+       if (smp->off == 1) {
+               smp->c_sboff = offset_in_line = 0;
+               smp->c_scoff = offset_in_char = 0;
+               p = &p[offset_in_line];
+       } else if (is_cached) {
+               offset_in_line = smp->c_sboff;
+               offset_in_char = smp->c_scoff;
+               p = &p[offset_in_line];
+       } else 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];
+       } else {
+               offset_in_line = 0;
+               offset_in_char = 0;
+
+               /* This is the loop that skips through screens. */
+               if (skip_screens == 0) {
+                       smp->c_sboff = offset_in_line;
+                       smp->c_scoff = offset_in_char;
+               } else for (scno = 0; offset_in_line < len; ++offset_in_line) {
+                       scno += chlen =
+                           (ch = *(u_char *)p++) == '\t' && !listset ?
+                           TAB_OFF(sp, scno) : cname[ch].len;
+                       if (scno < cols_per_screen)
+                               continue;
+                       /*
+                        * Reset cols_per_screen to second and subsequent line
+                        * length.
+                        */
+                       scno -= cols_per_screen;
+                       cols_per_screen = sp->cols;
+
+                       /*
+                        * If crossed the last skipped screen boundary, start
+                        * displaying the characters.
+                        */
+                       if (--skip_screens)
+                               continue;
+
+                       /* Put starting info for this line in the cache. */
+                       if (scno) {
+                               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;
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Set the number of characters to skip before reaching the cursor
+        * character.  Offset by 1 and use 0 as a flag value.  Svi_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)
+                       goto ret;
+       } else
+               cno_cnt = (sp->cno - offset_in_line) + 1;
+
+       /* This is the loop that actually displays characters. */
+       for (is_partial = 0, scno = 0;
+           offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
+               if ((ch = *(u_char *)p++) == '\t' && !listset) {
+                       scno += chlen = TAB_OFF(sp, scno) - offset_in_char;
+                       is_tab = 1;
+               } else {
+                       scno += chlen = cname[ch].len - 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) {
+                       smp->c_ecsize = chlen;
+                       chlen -= scno - cols_per_screen;
+                       smp->c_eclen = chlen;
+                       smp->c_eboff = offset_in_line;
+                       if (scno > cols_per_screen)
+                               is_partial = 1;
+
+                       /* 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, S_INPUT) || !is_partial)) {
+                       *yp = smp - HMAP;
+                       if (F_ISSET(sp, S_INPUT))
+                               *xp = scno - chlen;
+                       else
+                               *xp = scno - 1;
+                       if (O_ISSET(sp, O_NUMBER) &&
+                           !is_infoline && smp->off == 1)
+                               *xp += O_NUMBER_LENGTH;
+
+                       /* If the line is on the screen, quit. */
+                       if (is_cached)
+                               goto ret;
+               }
+
+               /* If the line is on the screen, don't display anything. */
+               if (is_cached)
+                       continue;
+
+               /*
+                * Display the character.  If it's a tab and tabs aren't some
+                * ridiculous length, do it fast.  (We do tab expansion here
+                * because curses doesn't have a way to set the tab length.)
+                */
+               if (is_tab) {
+                       if (chlen <= sizeof(TABSTR) - 1) {
+                               ADDNSTR(TABSTR, chlen);
+                       } else
+                               while (chlen--)
+                                       ADDCH(TABCH);
+               } else
+                       ADDNSTR(cname[ch].name + offset_in_char, chlen);
+       }
+
+       if (scno < cols_per_screen) {
+               /* If didn't paint the whole line, update the cache. */
+               smp->c_ecsize = smp->c_eclen = cname[ch].len;
+               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 (listset) {
+                       ++scno;
+                       ADDCH('$');
+               }
+
+               /* If still didn't paint the whole line, clear the rest. */
+               if (scno < cols_per_screen)
+                       clrtoeol();
+       }
+
+ret:   MOVEA(sp, oldy, oldx);
+       return (0);
+}
+
+/*
+ * svi_number --
+ *     Repaint the numbers on all the lines.
+ */
+int
+svi_number(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       SMAP *smp;
+       recno_t lno;
+       size_t oldy, oldx;
+       char *p, nbuf[10];
+
+       /*
+        * 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.
+        *
+        * If that test fails, we have to check each line for existence.
+        *
+        * XXX
+        * The problem is that file_lline will lie, and tell us that the
+        * info line is the last line in the file.
+        */
+       if ((p = file_gline(sp, ep, TMAP->lno - 1, NULL)) != NULL)
+               lno = TMAP->lno + 1;
+
+       getyx(stdscr, oldy, oldx);
+       for (smp = HMAP; smp <= TMAP; ++smp) {
+               if (smp->off != 1)
+                       continue;
+               if (ISINFOLINE(sp, smp))
+                       break;
+               if (smp->lno != 1)
+                       if (p != NULL) {
+                               if (smp->lno > lno)
+                                       break;
+                       } else {
+                               if ((p =
+                                   file_gline(sp, ep, smp->lno, NULL)) == NULL)
+                                       break;
+                               p = NULL;
+                       }
+               MOVE(sp, smp - HMAP, 0);
+               (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+               ADDSTR(nbuf);
+       }
+       MOVEA(sp, oldy, oldx);
+       return (0);
+}
diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c
new file mode 100644 (file)
index 0000000..353c081
--- /dev/null
@@ -0,0 +1,839 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_refresh.c      8.43 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+#include "sex/sex_screen.h"
+
+static int     svi_modeline __P((SCR *, EXF *));
+static int     svi_msgflush __P((SCR *));
+
+int
+svi_refresh(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       SCR *tsp;
+       u_int paintbits;
+
+       /*
+        * 1: Resize the screen.
+        *
+        * Notice that a resize is requested, and set up everything so that
+        * the file gets reinitialized.  Done here, instead of in the vi loop
+        * because there may be other initialization that other screens need
+        * to do.  The actual changing of the row/column values was done by
+        * calling the ex options code which put them into the environment,
+        * which is used by curses.  Stupid, but ugly.
+        */
+       if (F_ISSET(sp, S_RESIZE)) {
+               /* Reinitialize curses. */
+               if (svi_curses_end(sp) || svi_curses_init(sp))
+                       return (1);
+
+               /* Lose any svi_screens() cached information. */
+               SVP(sp)->ss_lno = OOBLNO;
+
+               /*
+                * Fill the map, incidentally losing any svi_line()
+                * cached information.
+                */
+               if (sp->s_fill(sp, ep, sp->lno, P_FILL))
+                       return (1);
+               F_CLR(sp, S_RESIZE | S_REFORMAT);
+               F_SET(sp, S_REDRAW);
+       }
+
+       /*
+        * 2: S_REFRESH
+        *
+        * If S_REFRESH is set in the current screen, repaint everything
+        * that we can find.
+        */
+       if (F_ISSET(sp, S_REFRESH))
+               for (tsp = sp->gp->dq.cqh_first;
+                   tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+                       if (tsp != sp)
+                               F_SET(tsp, S_REDRAW);
+       /*
+        * 3: 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 with paint or dirty bits
+        * set, or where messages are waiting.  Finally, if we refresh any
+        * screens other than the current one, the cursor will be trashed.
+        */
+       paintbits = S_REDRAW | S_REFORMAT | S_REFRESH;
+       if (O_ISSET(sp, O_NUMBER))
+               paintbits |= S_RENUMBER;
+       for (tsp = sp->gp->dq.cqh_first;
+           tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+               if (tsp != sp &&
+                   (F_ISSET(tsp, paintbits) ||
+                   F_ISSET(SVP(tsp), SVI_SCREENDIRTY) ||
+                   tsp->msgq.lh_first != NULL &&
+                   !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) {
+                       (void)svi_paint(tsp, tsp->ep);
+                       F_CLR(SVP(tsp), SVI_SCREENDIRTY);
+                       F_SET(SVP(sp), SVI_CUR_INVALID);
+               }
+
+       /*
+        * 4: Refresh the current screen.
+        *
+        * Always refresh the current screen, it may be a cursor movement.
+        * Also, always do it last -- that way, S_REFRESH can be set in
+        * the current screen only, and the screen won't flash.
+        */
+       F_CLR(sp, SVI_SCREENDIRTY);
+       return (svi_paint(sp, ep));
+}
+
+/*
+ * svi_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.
+ */
+int
+svi_paint(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       CHNAME const *cname;
+       SMAP *smp, tmp;
+       SVI_PRIVATE *svp;
+       recno_t lastline, lcnt;
+       size_t cwtotal, cnt, len, x, y;
+       int ch, didpaint;
+       char *p;
+
+#define         LNO    sp->lno
+#define        OLNO    svp->olno
+#define         CNO    sp->cno
+#define        OCNO    svp->ocno
+#define        SCNO    svp->sc_col
+
+       didpaint = 0;
+       svp = SVP(sp);
+
+       /*
+        * 1: 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, S_REFORMAT)) {
+               /* Toss svi_screens() cached information. */
+               SVP(sp)->ss_lno = OOBLNO;
+
+               /* Toss svi_line() cached information. */
+               if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP))
+                       return (1);
+               if (O_ISSET(sp, O_LEFTRIGHT) &&
+                   (cnt = svi_screens(sp, ep, LNO, &CNO)) != 1)
+                       for (smp = HMAP; smp <= TMAP; ++smp)
+                               smp->off = cnt;
+               F_CLR(sp, S_REFORMAT);
+               F_SET(sp, S_REDRAW);
+       }
+
+       /*
+        * 2: Line movement.
+        *
+        * Line changes can cause the top line to change as well.  As
+        * before, if the movement is large, the screen is repainted.
+        *
+        * 2a: Tiny screens.
+        *
+        * Tiny screens cannot be permitted into the "scrolling" parts of
+        * the smap code for two reasons.  If the screen size is 1 line,
+        * HMAP == TMAP and the code will quickly drop core.  If the screen
+        * size is 2, none of the divisions by 2 will work, and scrolling
+        * won't work.  In fact, because no line change will be less than
+        * HALFTEXT(sp), we always ending up "filling" the map, with a
+        * P_MIDDLE flag, which isn't what the user wanted.  Tiny screens
+        * can go into the "fill" portions of the smap code, however.
+        */
+       if (sp->t_rows <= 2) {
+               if (LNO < HMAP->lno) {
+                       if (svi_sm_fill(sp, ep, LNO, P_TOP))
+                               return (1);
+               } else if (LNO > TMAP->lno)
+                       if (svi_sm_fill(sp, ep, LNO, P_BOTTOM))
+                               return (1);
+               if (sp->t_rows == 1) {
+                       HMAP->off = svi_screens(sp, ep, LNO, &CNO);
+                       goto paint;
+               }
+               F_SET(sp, S_REDRAW);
+               goto adjust;
+       }
+
+       /*
+        * 2b: 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 (ISSMALLSCREEN(sp))
+               if (LNO < HMAP->lno) {
+                       lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows);
+                       if (lcnt <= HALFSCREEN(sp))
+                               for (; lcnt && sp->t_rows != sp->t_maxrows;
+                                    --lcnt, ++sp->t_rows) {
+                                       ++TMAP;
+                                       if (svi_sm_1down(sp, ep))
+                                               return (1);
+                               }
+                       else
+                               goto small_fill;
+               } else if (LNO > TMAP->lno) {
+                       lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows);
+                       if (lcnt <= HALFSCREEN(sp))
+                               for (; lcnt && sp->t_rows != sp->t_maxrows;
+                                    --lcnt, ++sp->t_rows) {
+                                       if (svi_sm_next(sp, ep, TMAP, TMAP + 1))
+                                               return (1);
+                                       ++TMAP;
+                                       if (svi_line(sp, ep, TMAP, NULL, NULL))
+                                               return (1);
+                               }
+                       else {
+small_fill:                    MOVE(sp, INFOLINE(sp), 0);
+                               clrtoeol();
+                               for (; sp->t_rows > sp->t_minrows;
+                                   --sp->t_rows, --TMAP) {
+                                       MOVE(sp, TMAP - HMAP, 0);
+                                       clrtoeol();
+                               }
+                               if (svi_sm_fill(sp, ep, LNO, P_FILL))
+                                       return (1);
+                               F_SET(sp, S_REDRAW);
+                               goto adjust;
+                       }
+               }
+
+       /*
+        * 3a: Line down.
+        */
+       if (LNO >= HMAP->lno) {
+               if (LNO <= TMAP->lno)
+                       goto adjust;
+
+               /*
+                * If less than half a screen away, scroll down until the
+                * line is on the screen.
+                */
+               lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp));
+               if (lcnt < HALFTEXT(sp)) {
+                       while (lcnt--)
+                               if (svi_sm_1up(sp, ep))
+                                       return (1);
+                       goto adjust;
+               }
+
+               /*
+                * If less than a full screen from the bottom of the file, put
+                * the last line of the file on the bottom of the screen.  The
+                * calculation is safe because we know there's at least one
+                * full screen of lines, otherwise couldn't have gotten here.
+                */
+               if (file_lline(sp, ep, &lastline))
+                       return (1);
+               tmp.lno = LNO;
+               tmp.off = 1;
+               lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows);
+               if (lcnt < sp->t_rows) {
+                       if (svi_sm_fill(sp, ep, lastline, P_BOTTOM))
+                               return (1);
+                       F_SET(sp, S_REDRAW);
+                       goto adjust;
+               }
+
+               /*
+                * If more than a full screen from the last line of the file,
+                * put the new line in the middle of the screen.
+                */
+               goto middle;
+       }
+
+       /*
+        * 3b: Line up.
+        *
+        * If less than half a screen away, scroll up until the line is
+        * the first line on the screen.
+        */
+       lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp));
+       if (lcnt < HALFTEXT(sp)) {
+               while (lcnt--)
+                       if (svi_sm_1down(sp, ep))
+                               return (1);
+               goto adjust;
+       }
+
+       /*
+        * 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.off = 1;
+       lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp));
+       if (lcnt < HALFTEXT(sp)) {
+               if (svi_sm_fill(sp, ep, 1, P_TOP))
+                       return (1);
+       } else
+middle:                if (svi_sm_fill(sp, ep, LNO, P_MIDDLE))
+                       return (1);
+       F_SET(sp, S_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 = svi_screens(sp, ep, LNO, &CNO);
+               if (LNO == HMAP->lno && cnt < HMAP->off)
+                       if ((HMAP->off - cnt) > HALFTEXT(sp)) {
+                               HMAP->off = cnt;
+                               svi_sm_fill(sp, ep, OOBLNO, P_TOP);
+                               F_SET(sp, S_REDRAW);
+                       } else
+                               while (cnt < HMAP->off)
+                                       if (svi_sm_1down(sp, ep))
+                                               return (1);
+               if (LNO == TMAP->lno && cnt > TMAP->off)
+                       if ((cnt - TMAP->off) > HALFTEXT(sp)) {
+                               TMAP->off = cnt;
+                               svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM);
+                               F_SET(sp, S_REDRAW);
+                       } else
+                               while (cnt > TMAP->off)
+                                       if (svi_sm_1up(sp, ep))
+                                               return (1);
+       }
+
+       /* If the screen needs to be repainted, skip cursor optimization. */
+       if (F_ISSET(sp, S_REDRAW))
+               goto paint;
+
+       /*
+        * 4: Cursor movements.
+        *
+        * 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.  Note, if we think that the cursor "hasn't moved", reparse
+        * the line.  This is 'cause if it hasn't moved, we've almost always
+        * lost track of it.
+        *
+        * 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, reparse. */
+       if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) {
+               F_CLR(SVP(sp), SVI_CUR_INVALID);
+               goto slow;
+       }
+
+       /* Otherwise, if nothing's changed, go fast. */
+       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 ((p = file_gline(sp, ep, LNO, &len)) == NULL) {
+               if (file_lline(sp, ep, &lastline))
+                       return (1);
+               if (lastline == 0)
+                       goto slow;
+               GETLINE_ERR(sp, LNO);
+               return (1);
+       }
+
+#ifdef DEBUG
+       /* This is just a test. */
+       if (CNO >= len && len != 0) {
+               msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
+                    tail(__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.
+        */
+       cname = sp->gp->cname;
+       if (CNO < OCNO) {
+               /*
+                * 4a: 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;
+
+               /*
+                * Quit 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 lscreen;
+
+               /*
+                * Count up the widths of the characters.  If it's a tab
+                * character, go do it the the slow way.
+                */
+               for (cwtotal = 0; cnt--; cwtotal += cname[ch].len)
+                       if ((ch = *(u_char *)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 (cname[ch].len > 1)
+                       cwtotal -= cname[ch].len - 1;
+
+               /*
+                * If the new column moved us out of the current screen,
+                * calculate a new screen.
+                */
+               if (SCNO < cwtotal) {
+lscreen:               if (O_ISSET(sp, O_LEFTRIGHT)) {
+                               for (smp = HMAP; smp <= TMAP; ++smp)
+                                       --smp->off;
+                               goto paint;
+                       }
+                       goto slow;
+               }
+               SCNO -= cwtotal;
+       } else {
+               /*
+                * 4b: 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 the slow way.  If we cross a
+                * screen boundary, we can quit.
+                */
+               for (cwtotal = SCNO; cnt--;) {
+                       if ((ch = *(u_char *)p++) == '\t')
+                               goto slow;
+                       if ((cwtotal += cname[ch].len) >= SCREEN_COLS(sp))
+                               break;
+               }
+
+               /*
+                * Increment the screen cursor by the total width of the
+                * characters.
+                */
+               SCNO = cwtotal;
+
+               /*
+                * If the new column moved us out of the current screen,
+                * calculate a new screen.
+                */
+               if (SCNO >= SCREEN_COLS(sp)) {
+                       if (O_ISSET(sp, O_LEFTRIGHT)) {
+                               SCNO -= SCREEN_COLS(sp);
+                               for (smp = HMAP; smp <= TMAP; ++smp)
+                                       ++smp->off;
+                               goto paint;
+                       }
+                       goto slow;
+               }
+       }
+
+       /*
+        * 4c: Fast cursor update.
+        *
+        * Retrieve the current cursor position, and correct it
+        * for split screens.
+        */
+fast:  getyx(stdscr, y, x);
+       y -= sp->woff;
+       goto number;
+
+       /*
+        * 4d: Slow cursor update.
+        *
+        * Walk through the map and find the current line.  If doing left-right
+        * scrolling and the cursor movement has changed the screen displayed,
+        * scroll the screen left or right, unless we're updating the info line
+        * in which case we just scroll that one line.  Then update the screen
+        * lines for this file line until we have a new screen cursor position.
+        */
+slow:  for (smp = HMAP; smp->lno != LNO; ++smp);
+       if (O_ISSET(sp, O_LEFTRIGHT)) {
+               cnt = svi_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp);
+               if (cnt != HMAP->off) {
+                       if (ISINFOLINE(sp, smp))
+                               smp->off = cnt;
+                       else
+                               for (smp = HMAP; smp <= TMAP; ++smp)
+                                       smp->off = cnt;
+                       goto paint;
+               }
+       }
+       for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) {
+               if (svi_line(sp, ep, smp, &y, &SCNO))
+                       return (1);
+               if (y != -1)
+                       break;
+       }
+       goto number;
+
+       /*
+        * 5: Repaint the entire screen.
+        *
+        * Lost big, do what you have to do.  We flush the cache as S_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 (smp = HMAP; smp <= TMAP; ++smp)
+               if (svi_line(sp, ep, smp, &y, &SCNO))
+                       return (1);
+       /*
+        * If it's a small screen and we're redrawing, clear the unused lines,
+        * ex may have overwritten them.
+        */
+       if (F_ISSET(sp, S_REDRAW)) {
+               if (ISSMALLSCREEN(sp))
+                       for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+                               MOVE(sp, cnt, 0);
+                               clrtoeol();
+                       }
+               F_CLR(sp, S_REDRAW);
+       }
+
+       didpaint = 1;
+
+       /*
+        * 6: Repaint the line numbers.
+        *
+        * If O_NUMBER is set and the S_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(sp, S_RENUMBER) && !didpaint) {
+               if (svi_number(sp, ep))
+                       return (1);
+               F_CLR(sp, S_RENUMBER);
+       }
+
+       /*
+        * 7: Refresh the screen.
+        *
+        * If the screen was corrupted, refresh it.
+        */
+       if (F_ISSET(sp, S_REFRESH)) {
+               wrefresh(curscr);
+               F_CLR(sp, S_REFRESH);
+       }
+
+       if (F_ISSET(sp, S_BELLSCHED))
+               svi_bell(sp);
+       /*
+        * If the bottom line isn't in use by the colon command:
+        *
+        *      Display any messages.  Don't test S_UPDATE_MODE.  The
+        *      message printing routine set it to avoid anyone else
+        *      destroying the message we're about to display.
+        *
+        *      If the bottom line isn't in use by anyone, put out the
+        *      standard status line.
+        */
+       if (!F_ISSET(SVP(sp), SVI_INFOLINE))
+               if (sp->msgq.lh_first != NULL &&
+                   !F_ISSET(sp->msgq.lh_first, M_EMPTY))
+                       svi_msgflush(sp);
+               else if (!F_ISSET(sp, S_UPDATE_MODE))
+                       svi_modeline(sp, ep);
+
+       /* Update saved information. */
+       OCNO = CNO;
+       OLNO = LNO;
+
+       /* Place the cursor. */
+       MOVE(sp, y, SCNO);
+
+       /* Flush it all out. */
+       refresh();
+
+       return (0);
+}
+
+/*
+ * svi_msgflush --
+ *     Flush any accumulated messages.
+ */
+static int
+svi_msgflush(sp)
+       SCR *sp;
+{
+       CH ikey;
+       CHAR_T ch;
+       CHNAME const *cname;
+       MSG *mp;
+       size_t chlen, len;
+       char *p;
+
+#define        MCONTMSG        " [More ...]"
+
+       /* Display the messages. */
+       cname = sp->gp->cname;
+       for (mp = sp->msgq.lh_first, p = NULL;
+           mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) {
+               p = mp->mbuf;
+
+lcont:         /* Move to the message line and clear it. */
+               MOVE(sp, INFOLINE(sp), 0);
+               clrtoeol();
+
+               /*
+                * Turn on standout mode if requested, or, if we've split
+                * the screen and need a divider.
+                */
+               if (F_ISSET(mp, M_INV_VIDEO) ||
+                   sp->q.cqe_next != (void *)&sp->gp->dq)
+                       standout();
+
+               /*
+                * Print up to the "more" message.  Avoid the last character
+                * in the last line, some hardware doesn't like it.
+                */
+               if (svi_ncols(sp, p, mp->len, NULL) < sp->cols - 1)
+                       len = sp->cols - 1;
+               else
+                       len = (sp->cols - sizeof(MCONTMSG)) - 1;
+               for (;; ++p) {
+                       if (!mp->len)
+                               break;
+                       ch = *(u_char *)p;
+                       chlen = cname[ch].len;
+                       if (chlen >= len)
+                               break;
+                       len -= chlen;
+                       --mp->len;
+                       ADDNSTR(cname[ch].name, chlen);
+               }
+
+               /*
+                * If more, print continue message.  If user key fails,
+                * keep showing the messages anyway.
+                */
+               if (mp->len || (mp->q.le_next != NULL &&
+                   !F_ISSET(mp->q.le_next, M_EMPTY))) {
+                       ADDNSTR(MCONTMSG, sizeof(MCONTMSG) - 1);
+                       refresh();
+                       for (;;) {
+                               if (term_user_key(sp, &ikey) != INP_OK)
+                                       break;
+                               if (ikey.value == K_CR ||
+                                   ikey.value == K_NL || ikey.ch == ' ')
+                                       break;
+                               svi_bell(sp);
+                       }
+               }
+
+               /* Turn off standout mode. */
+               if (F_ISSET(mp, M_INV_VIDEO) ||
+                   sp->q.cqe_next != (void *)&sp->gp->dq)
+                       standend();
+
+               if (mp->len)
+                       goto lcont;
+
+               refresh();
+               F_SET(mp, M_EMPTY);
+       }
+       return (0);
+}
+
+#define        RULERSIZE       15
+#define        MODESIZE        (RULERSIZE + 15)
+
+/*
+ * svi_modeline --
+ *     Update the mode line.
+ */
+static int
+svi_modeline(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       char *s, buf[RULERSIZE];
+
+       MOVE(sp, INFOLINE(sp), 0);
+       clrtoeol();
+
+       /* Display a dividing line if not the bottom screen. */
+       if (sp->q.cqe_next != (void *)&sp->gp->dq)
+               svi_divider(sp);
+
+       /* Display the ruler. */
+       if (O_ISSET(sp, O_RULER) && sp->cols > RULERSIZE + 2) {
+               MOVE(sp, INFOLINE(sp), sp->cols / 2 - RULERSIZE / 2);
+               clrtoeol();
+               (void)snprintf(buf,
+                   sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1);
+               ADDSTR(buf);
+       }
+
+       /* Show the modified bit. */
+       if (O_ISSET(sp, O_SHOWDIRTY) &&
+           F_ISSET(ep, F_MODIFIED) && sp->cols > MODESIZE) {
+               MOVE(sp, INFOLINE(sp), sp->cols - 9);
+               ADDSTR("*");
+       }
+
+       /*
+        * Show the mode.  Leave the last character blank, in case it's a
+        * really dumb terminal with hardware scroll.  Second, don't try
+        * to *paint* the last character, SunOS 4.1.1 and Ultrix 4.2 curses
+        * won't let you paint the last character in the screen.
+        */
+       if (O_ISSET(sp, O_SHOWMODE) && sp->cols > MODESIZE) {
+               MOVE(sp, INFOLINE(sp), sp->cols - 8);
+               s = F_ISSET(sp, S_INPUT) ? "  Input" : "Command";
+               ADDSTR(s);
+       }
+
+       return (0);
+}
+
+/*
+ * svi_divider --
+ *     Draw a dividing line between the screens.
+ */
+int
+svi_divider(sp)
+       SCR *sp;
+{
+       size_t len;
+
+#define        DIVIDESTR       "+=+=+=+=+=+=+=+"
+       len = sizeof(DIVIDESTR) - 1 > sp->cols ?
+           sp->cols : sizeof(DIVIDESTR) - 1;
+       ADDNSTR(DIVIDESTR, len);
+       return (0);
+}
diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c
new file mode 100644 (file)
index 0000000..7daa972
--- /dev/null
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_relative.c     8.7 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_column --
+ *     Return the logical column of the cursor.
+ */
+int
+svi_column(sp, ep, cp)
+       SCR *sp;
+       EXF *ep;
+       size_t *cp;
+{
+       size_t col;
+
+       col = SVP(sp)->sc_col;
+       if (O_ISSET(sp, O_NUMBER))
+               col -= O_NUMBER_LENGTH;
+       *cp = col;
+       return (0);
+}
+
+/*
+ * svi_relative --
+ *     Return the physical column from the line that will display a
+ *     character closest to the currently most attractive character
+ *     position.  If it's not easy, uses the underlying routine that
+ *     really figures it out.  It's broken into two parts because the
+ *     svi_lrelative routine handles "logical" offsets, which nobody
+ *     but the screen routines understand.
+ */
+size_t
+svi_relative(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+{
+       size_t cno;
+
+       /* First non-blank character. */
+       if (sp->rcmflags == RCM_FNB) {
+               cno = 0;
+               (void)nonblank(sp, ep, lno, &cno);
+               return (cno);
+       }
+
+       /* First character is easy, and common. */
+       if (sp->rcmflags != RCM_LAST && sp->rcm == 0)
+               return (0);
+
+       return (svi_lrelative(sp, ep, lno, 1));
+}
+
+/*
+ * svi_lrelative --
+ *     Return the physical column from the line that will display a
+ *     character closest to the currently most attractive character
+ *     position.  The offset is for the commands that move logical
+ *     distances, i.e. if it's a logical scroll the closest physical
+ *     distance is based on the logical line, not the physical line.
+ */
+size_t
+svi_lrelative(sp, ep, lno, off)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       size_t off;
+{
+       CHNAME const *cname;
+       size_t len, llen, scno;
+       int ch, listset;
+       char *lp, *p;
+
+       /* Need the line to go any further. */
+       if ((lp = file_gline(sp, ep, lno, &len)) == NULL)
+               return (0);
+
+       /* Empty lines are easy. */
+       if (len == 0)
+               return (0);
+
+       /* Last character is easy, and common. */
+       if (sp->rcmflags == RCM_LAST)
+               return (len - 1);
+
+       /* Discard logical lines. */
+       cname = sp->gp->cname;
+       listset = O_ISSET(sp, O_LIST);
+       for (scno = 0, p = lp, llen = len; --off;) {
+               for (; len && scno < sp->cols; --len)
+                       SCNO_INCREMENT;
+               if (len == 0)
+                       return (llen - 1);
+               scno -= sp->cols;
+       }
+
+       /* Step through the line until reach the right character. */
+       while (len--) {
+               SCNO_INCREMENT;
+               if (scno >= sp->rcm) {
+                       /* Get the offset of this character. */
+                       len = p - lp;
+
+                       /*
+                        * May be the next character, not this one,
+                        * so check to see if we've gone too far.
+                        */
+                       if (scno == sp->rcm)
+                               return (len < llen - 1 ? len : llen - 1);
+                       /* It's this character. */
+                       return (len - 1);
+               }
+       }
+       /* No such character; return start of last character. */
+       return (llen - 1);
+}
+
+/*
+ * svi_chposition --
+ *     Return the physical column from the line that will display a
+ *     character closest to the specified column.
+ */
+size_t
+svi_chposition(sp, ep, lno, cno)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       size_t cno;
+{
+       CHNAME const *cname;
+       size_t len, llen, scno;
+       int ch, listset;
+       char *lp, *p;
+
+       /* Need the line to go any further. */
+       if ((lp = file_gline(sp, ep, lno, &llen)) == NULL)
+               return (0);
+
+       /* Empty lines are easy. */
+       if (llen == 0)
+               return (0);
+
+       /* Step through the line until reach the right character. */
+       cname = sp->gp->cname;
+       listset = O_ISSET(sp, O_LIST);
+       for (scno = 0, len = llen, p = lp; len--;) {
+               SCNO_INCREMENT;
+               if (scno >= cno) {
+                       /* Get the offset of this character. */
+                       len = p - lp;
+
+                       /*
+                        * May be the next character, not this one,
+                        * so check to see if we've gone too far.
+                        */
+                       if (scno == cno)
+                               return (len < llen - 1 ? len : llen - 1);
+                       /* It's this character. */
+                       return (len - 1);
+               }
+       }
+       /* No such character; return start of last character. */
+       return (llen - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c
new file mode 100644 (file)
index 0000000..16e2c36
--- /dev/null
@@ -0,0 +1,534 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_screen.c       8.57 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "sex/sex_screen.h"
+
+static void    d_to_h __P((SCR *, char *));
+static int     svi_initscr_kluge __P((SCR *, struct termios *));
+static void    svi_keypad __P((SCR *, int));
+static void    svi_keypad_pc __P((int));
+
+/*
+ * svi_screen_init --
+ *     Initialize a screen.
+ */
+int
+svi_screen_init(sp)
+       SCR *sp;
+{
+       /* Initialize support routines. */
+       sp->s_bell              = svi_bell;
+       sp->s_bg                = svi_bg;
+       sp->s_busy              = svi_busy;
+       sp->s_change            = svi_change;
+       sp->s_chposition        = svi_chposition;
+       sp->s_clear             = svi_clear;
+       sp->s_column            = svi_column;
+       sp->s_confirm           = svi_confirm;
+       sp->s_down              = svi_sm_down;
+       sp->s_edit              = svi_screen_edit;
+       sp->s_end               = svi_screen_end;
+       sp->s_ex_cmd            = svi_ex_cmd;
+       sp->s_ex_run            = svi_ex_run;
+       sp->s_ex_write          = svi_ex_write;
+       sp->s_fg                = svi_fg;
+       sp->s_fill              = svi_sm_fill;
+       sp->s_get               = svi_get;
+       sp->s_key_read          = sex_key_read;
+       sp->s_optchange         = svi_optchange;
+       sp->s_position          = svi_sm_position;
+       sp->s_rabs              = svi_rabs;
+       sp->s_refresh           = svi_refresh;
+       sp->s_relative          = svi_relative;
+       sp->s_rrel              = svi_rrel;
+       sp->s_split             = svi_split;
+       sp->s_suspend           = svi_suspend;
+       sp->s_up                = svi_sm_up;
+
+       return (0);
+}
+
+/*
+ * svi_screen_copy --
+ *     Copy to a new screen.
+ */
+int
+svi_screen_copy(orig, sp)
+       SCR *orig, *sp;
+{
+       SVI_PRIVATE *osvi, *nsvi;
+
+       /* Create the private screen structure. */
+       CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE));
+       sp->svi_private = nsvi;
+
+/* INITIALIZED AT SCREEN CREATE. */
+       /* Lose svi_screens() cached information. */
+       nsvi->ss_lno = OOBLNO;
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+       if (orig == NULL) {
+       } else {
+               osvi = SVP(orig);
+               nsvi->srows = osvi->srows;
+               if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       return (1);
+               }
+
+               F_SET(nsvi, F_ISSET(osvi, SVI_NO_VBELL));
+       }
+       return (0);
+}
+
+/*
+ * svi_screen_end --
+ *     End a screen.
+ */
+int
+svi_screen_end(sp)
+       SCR *sp;
+{
+       int rval;
+
+       rval = 0;
+
+       /*
+        * XXX
+        * If this is the last screen, end curses
+        * while we still have screen information.
+        */
+       if (sp->q.cqe_prev == (void *)&sp->gp->dq &&
+           sp->q.cqe_next == (void *)&sp->gp->dq &&
+           sp->gp->hq.cqh_first == (void *)&sp->gp->hq &&
+           svi_curses_end(sp))
+               rval = 1;
+
+       /* Free screen map. */
+       if (HMAP != NULL)
+               FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP));
+
+       /* Free visual bell string. */
+       if (SVP(sp)->VB != NULL)
+               FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1);
+
+       /* Free private memory. */
+       FREE(SVP(sp), sizeof(SVI_PRIVATE));
+       sp->svi_private = NULL;
+
+       return (rval);
+}
+
+/*
+ * We use a single curses "window" for each vi screen.  The model would be
+ * simpler with two windows (one for the text, and one for the modeline)
+ * because scrolling the text window down would work correctly then, not
+ * affecting the mode line.  As it is we have to play games to make it look
+ * right.  The reason for this choice is that it would be difficult for
+ * curses to optimize the movement, i.e. detect that the downward scroll
+ * isn't going to change the modeline, set the scrolling region on the
+ * terminal and only scroll the first part of the text window.  (Even if
+ * curses did detect it, the set-scrolling-region terminal commands can't
+ * be used by curses because it's indeterminate where the cursor ends up
+ * after they are sent.)
+ */
+/*
+ * svi_screen_edit --
+ *     Main vi curses screen loop.
+ */
+int
+svi_screen_edit(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       SCR *tsp;
+       int force;
+
+       /* Get refresh to init curses. */
+       F_SET(sp, S_RESIZE);
+
+       for (;;) {
+               /* Reset the cursor. */
+               F_SET(SVP(sp), SVI_CUR_INVALID);
+
+               /*
+                * Run vi.  If vi fails, svi data structures may be
+                * corrupted, be extremely careful what you free up.
+                */
+               if (vi(sp, sp->ep)) {
+                       if (F_ISSET(ep, F_RCV_ON)) {
+                               F_SET(ep, F_RCV_NORM);
+                               (void)rcv_sync(sp, sp->ep);
+                       }
+                       (void)file_end(sp, sp->ep, 1);  /* End the file. */
+                       (void)svi_curses_end(sp);       /* End curses. */
+                       (void)screen_end(sp);           /* End the screen. */
+                       return (1);
+               }
+
+               force = 0;
+               switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+               case S_EXIT_FORCE:
+                       force = 1;
+                       /* FALLTHROUGH */
+               case S_EXIT:
+                       F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+                       if (file_end(sp, sp->ep, force))/* End the file. */
+                               break;
+                       (void)svi_join(sp, &tsp);       /* Find a new screen. */
+                       if (tsp == NULL)
+                               (void)svi_swap(sp, &tsp, NULL);
+                       (void)screen_end(sp);           /* End the screen. */
+                       if ((sp = tsp) == NULL)         /* Switch. */
+                               return (0);
+                       break;
+               case 0:                                 /* Exit vi mode. */
+                       (void)svi_curses_end(sp);       /* End curses. */
+                       d_to_h(sp, "Exit from vi");
+                       return (0);
+               case S_FSWITCH:                         /* File switch. */
+                       F_CLR(sp, S_FSWITCH);
+                       F_SET(sp, S_REFORMAT);
+                       break;
+               case S_SSWITCH:                         /* Screen switch. */
+                       F_CLR(sp, S_SSWITCH);
+                       sp = sp->nextdisp;
+                       break;
+               default:
+                       abort();
+               }
+       }
+       /* NOTREACHED */
+}
+
+/*
+ * svi_curses_init --
+ *     Initialize curses.
+ */
+int
+svi_curses_init(sp)
+       SCR *sp;
+{
+       struct termios t;
+       int ixoff, ixon;
+       char *p, kbuf[2048];
+
+       /*
+        * Vi wants raw mode, excepting flow control characters.  Both
+        * cbreak and raw modes have problems for us.  In cbreak mode,
+        * we have to turn all the signals off explicitly.  In raw mode,
+        * we have to turn flow control back on.  Raw chosen for no strong
+        * reason.  In both cases we have to periodically turn various
+        * signals on.
+        */
+       if (tcgetattr(STDIN_FILENO, &t)) {
+               msgq(sp, M_SYSERR, "tcgetattr");
+               return (1);
+       }
+       ixon = t.c_iflag & IXON;
+       ixoff = t.c_iflag & IXOFF;
+
+       /*
+        * The initscr() in SunOS curses flushes the terminal driver queue.
+        * I have no idea if this stark raving insanity appears elsewhere,
+        * but since the SunOS curses is likely derived from the System III
+        * or System V versions, here's the workaround.
+        */
+       if (svi_initscr_kluge(sp, &t))
+               return (1);
+       
+       /*
+        * Start the screen.  Initscr() doesn't provide useful error values
+        * or messages.  Generally, either malloc failed or the terminal
+        * was unknown or lacked some necesssary feature.  Try and guess so
+        * the user isn't even more pissed off because of the error message.
+        */
+       errno = 0;
+       if (initscr() == NULL) {
+               if (errno)
+                       msgq(sp, M_SYSERR, "initscr failed");
+               else
+                       msgq(sp, M_ERR, "Error: initscr failed.");
+               if ((p = getenv("TERM")) == NULL)
+                       msgq(sp, M_ERR,
+                           "Error: no terminal environment variable set.");
+               else if (tgetent(kbuf, p) != 1)
+                       msgq(sp, M_ERR,
+"Error: %s: unknown terminal type, or terminal lacking necessary features.", p);
+               else
+                       msgq(sp, M_ERR,
+                   "Error: %s: terminal type lacking necessary features.", p);
+               return (1);
+       }
+
+       /*
+        * !!!
+        * If raw isn't turning off echo and newlines, something's wrong.
+        * However, just in case...
+        */
+       noecho();                       /* No character echo. */
+       nonl();                         /* No CR/NL translation. */
+       raw();                          /* No special characters. */
+       idlok(stdscr, 1);               /* Use hardware insert/delete line. */
+
+       /*
+        * Vi wants the cursor keys in application mode.  The historic version
+        * of curses had no way to do this, and the newer versions (System V)
+        * only enable it through the wgetch() interface.  Do it roughly, here.
+        */
+       svi_keypad(sp, 1);
+
+       /*
+        * XXX
+        * Major compatibility kluge.  When we call the curses raw() routine,
+        * XON/XOFF flow control is turned off.  Old terminals like to have
+        * it, so if it's originally set for the tty, we turn it back on.  For
+        * some unknown reason, this causes System V curses to NOT correctly
+        * restore the terminal modes when SIGTSTP is received.
+        */
+       if ((ixon || ixoff) &&
+           !tcgetattr(STDIN_FILENO, &sp->gp->s5_curses_botch)) {
+               t = sp->gp->s5_curses_botch;
+               if (ixon)
+                       t.c_iflag |= IXON;
+               if (ixoff)
+                       t.c_iflag |= IXOFF;
+               F_SET(sp->gp, G_CURSES_S5CB);
+               (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+       }
+
+       /*
+        * The first screen in the list gets it all.  All other screens
+        * are hidden and lose their maps.
+        */
+       d_to_h(sp, "Window resize");
+
+       /* Initialize terminal values. */
+       SVP(sp)->srows = O_VAL(sp, O_LINES);
+
+       /*
+        * Initialize screen values.
+        *
+        * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b.
+        *
+        * 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 = SVP(sp)->srows;
+       sp->cols = O_VAL(sp, O_COLUMNS);
+       sp->woff = 0;
+       sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
+       if (sp->t_rows > sp->rows - 1) {
+               sp->t_minrows = sp->t_rows = sp->rows - 1;
+               msgq(sp, M_INFO,
+                   "Windows option value is too large, max is %u", sp->t_rows);
+       }
+       sp->t_maxrows = sp->rows - 1;
+
+       /* Create the screen map. */
+       if ((HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       memset(HMAP, 0, SIZE_HMAP(sp) * sizeof(SMAP));
+       TMAP = HMAP + (sp->t_rows - 1);
+
+       F_SET(sp->gp, G_CURSES_INIT);           /* Curses initialized. */
+       F_SET(SVP(sp), SVI_CUR_INVALID);        /* Cursor is invalid. */
+       return (0);
+}
+
+/*
+ * svi_rrel --
+ *     Change the relative size of the current screen.
+ */
+int
+svi_rrel(sp, count)
+       SCR *sp;
+       long count;
+{
+       /* Can't grow beyond the size of the window. */
+       if (count > O_VAL(sp, O_WINDOW))
+               count = O_VAL(sp, O_WINDOW);
+
+       sp->t_minrows = sp->t_rows = count;
+       if (sp->t_rows > sp->rows - 1)
+               sp->t_minrows = sp->t_rows = sp->rows - 1;
+       TMAP = HMAP + (sp->t_rows - 1);
+       F_SET(sp, S_REDRAW);
+       return (0);
+}
+
+/*
+ * svi_curses_end --
+ *     Move to the bottom of the screen, end curses.
+ */
+int
+svi_curses_end(sp)
+       SCR *sp;
+{
+       /* We get called before anything has been initialized. */
+       if (!F_ISSET(sp->gp, G_CURSES_INIT))
+               return (0);
+       F_CLR(sp->gp, G_CURSES_INIT);
+
+       /* Move to the bottom of the screen. */
+       if (move(INFOLINE(sp), 0) == OK) {
+               clrtoeol();
+               refresh();
+       }
+
+       /*
+        * XXX
+        * See comment in svi_curses_init().
+        */
+       if (F_ISSET(sp->gp, G_CURSES_S5CB))
+               (void)tcsetattr(STDIN_FILENO,
+                   TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch);
+       F_CLR(sp->gp, G_CURSES_S5CB);
+
+       svi_keypad(sp, 0);
+       endwin();
+       return (0);
+}
+
+/*
+ * svi_keypad --
+ *     Put the keypad/cursor arrows into or out of application mode.
+ */
+static void
+svi_keypad(sp, on)
+       SCR *sp;
+       int on;
+{
+       char *sbp, *t, kbuf[2048], sbuf[128];
+
+       if (tgetent(kbuf, O_STR(sp, O_TERM)) != 1)
+               return;
+       sbp = sbuf;
+       if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL)
+               return;
+       (void)tputs(t, 0, svi_keypad_pc);
+}
+
+/*
+ * svi_keypad_pc --
+ *     putchar routine for tputs().
+ */
+static void
+svi_keypad_pc(argch)
+       int argch;
+{
+       char ch;
+
+       ch = argch;
+       (void)write(STDOUT_FILENO, &ch, sizeof(ch));
+}
+
+/*
+ * svi_initscr_kluge --
+ *     Read all of the waiting keys before calling initscr().
+ */
+static int
+svi_initscr_kluge(sp, tp)
+       SCR *sp;
+       struct termios *tp;
+{
+       struct termios rawt;
+       int rval;
+
+       /*
+        * Turn off canonical input processing so that we get partial
+        * lines as well as complete ones.  Also, set the MIN/TIME
+        * values -- System V and SMI systems overload VMIN and VTIME,
+        * such that VMIN is the same as the VEOF element, and VTIME is
+        * the same as the VEOL element.  This means, that if VEOF was
+        * ^D, the default VMIN is 4.  Majorly stupid.
+        */
+       rawt = *tp;
+       rawt.c_cc[VMIN] = 1;
+       rawt.c_cc[VTIME] = 0;
+       rawt.c_lflag &= ~ICANON;
+       if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, &rawt))
+               return (1);
+       rval = term_key_queue(sp);
+       if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, tp) || rval)
+               return (1);
+       return (0);
+}
+
+/*
+ * d_to_h --
+ *     Move all but the current screen to the hidden queue.
+ */
+static void
+d_to_h(sp, emsg)
+       SCR *sp;
+       char *emsg;
+{
+       SCR *tsp;
+       int hidden;
+
+       for (hidden = 0;
+           (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) {
+               if (_HMAP(tsp) != NULL)
+                       FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP));
+               CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q);
+               CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q);
+       }
+       CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+       CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+       if (hidden > 1)
+               msgq(sp, M_INFO,
+           "%s backgrounded %d screens; use :display to list the screens.",
+                   emsg, hidden - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h
new file mode 100644 (file)
index 0000000..4bf7544
--- /dev/null
@@ -0,0 +1,238 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)svi_screen.h        8.30 (Berkeley) 12/22/93
+ */
+
+/*
+ * Structure for mapping lines to the screen.  An SMAP is an array, with one
+ * structure element per screen line, which holds information describing the
+ * physical line which is displayed in the screen line.  The first two fields
+ * (lno and off) are all that are necessary to describe a line.  The rest of
+ * the information is useful to keep information from being re-calculated.
+ *
+ * Lno is the line number.  Off is the screen offset into the line.  For
+ * example, the pair 2:1 would be the first screen of line 2, and 2:2 would
+ * be the second.  If doing left-right scrolling, all of the offsets will be
+ * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc.  If doing the
+ * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1,
+ * etc.
+ *
+ * The SMAP is always as large as the physical screen, plus a slot for the
+ * info line, so that there is room to add any screen into another one at
+ * screen exit.
+ */
+typedef struct _smap {
+       recno_t  lno;           /* 1-N: Physical file line number. */
+       size_t   off;           /* 1-N: Screen offset in the line. */
+
+                               /* svi_line() cache information. */
+       size_t   c_sboff;       /* 0-N: offset of first character byte. */
+       size_t   c_eboff;       /* 0-N: offset of  last character byte. */
+       u_char   c_scoff;       /* 0-N: offset into the first character. */
+       u_char   c_eclen;       /* 1-N: columns from the last character. */
+       u_char   c_ecsize;      /* 1-N: size of the  last character. */
+} SMAP;
+
+                               /* Macros to flush/test cached information. */
+#define        SMAP_CACHE(smp)         ((smp)->c_ecsize != 0)
+#define        SMAP_FLUSH(smp)         ((smp)->c_ecsize = 0)
+
+typedef struct _svi_private {
+/* INITIALIZED AT SCREEN CREATE. */
+       SMAP    *h_smap;        /* First slot of the line map. */
+       SMAP    *t_smap;        /*  Last slot of the line map. */
+
+       size_t   exlinecount;   /* Ex overwrite count. */
+       size_t   extotalcount;  /* Ex overwrite count. */
+       size_t   exlcontinue;   /* Ex line continue value. */
+
+                               /* svi_screens() cache information. */
+       recno_t  ss_lno;        /* 1-N: Line number. */
+       size_t   ss_screens;    /* Return value. */
+
+       recno_t  olno;          /* 1-N: old cursor file line. */
+       size_t   ocno;          /* 0-N: old file cursor column. */
+       size_t   sc_col;        /* 0-N: LOGICAL screen column. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+       size_t   srows;         /* 1-N: Rows in the terminal/window. */
+
+       char    *VB;            /* Visual bell termcap string. */
+
+#define        SVI_CUR_INVALID 0x001   /* Cursor position is unknown. */
+#define        SVI_DIVIDER     0x002   /* Screen divider is displayed. */
+#define        SVI_INFOLINE    0x004   /* The infoline is being used by v_ntext(). */
+#define        SVI_NO_VBELL    0x008   /* No visual bell available. */
+#define        SVI_SCREENDIRTY 0x010   /* Screen needs refreshing. */
+       u_int    flags;
+} SVI_PRIVATE;
+
+#define        SVP(sp)         ((SVI_PRIVATE *)((sp)->svi_private))
+#define         HMAP           (SVP(sp)->h_smap)
+#define         TMAP           (SVP(sp)->t_smap)
+#define        _HMAP(sp)       (SVP(sp)->h_smap)
+#define        _TMAP(sp)       (SVP(sp)->t_smap)
+
+/*
+ * One extra slot is always allocated for the map so that we can use
+ * it to do vi :colon command input; see svi_get().
+ */
+#define        SIZE_HMAP(sp)   (SVP(sp)->srows + 1)
+
+#define        O_NUMBER_FMT    "%7lu "                 /* O_NUMBER format, length. */
+#define        O_NUMBER_LENGTH 8
+                                               /* Columns on a screen. */
+#define        SCREEN_COLS(sp)                                                 \
+       ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols))
+
+#define        HALFSCREEN(sp)  ((sp)->t_maxrows / 2)   /* Half the screen. */
+#define        HALFTEXT(sp)    ((sp)->t_rows / 2)      /* Half the text. */
+
+#define        INFOLINE(sp)    ((sp)->t_maxrows)       /* Info line test, offset. */
+#define        ISINFOLINE(sp, smp)     (((smp) - HMAP) == INFOLINE(sp))
+
+                                               /* Small screen test. */
+#define        ISSMALLSCREEN(sp)       ((sp)->t_minrows != (sp)->t_maxrows)
+
+                                               /* Next tab offset. */
+#define        TAB_OFF(sp, c)  (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP))
+
+                                               
+#define SCNO_INCREMENT                         /* Step through line. */\
+       scno += (ch = *(u_char *)p++) == '\t' && !listset ?             \
+           TAB_OFF(sp, scno) : cname[ch].len
+
+/* Move in a screen (absolute), and fail if it doesn't work. */
+#define        MOVEA(sp, lno, cno) {                                           \
+       if (move(lno, cno) == ERR) {                                    \
+               msgq(sp, M_ERR,                                         \
+                   "Error: %s/%d: move:l(%u), c(%u), abs.",            \
+                   tail(__FILE__), __LINE__, lno, cno);                \
+               return (1);                                             \
+       }                                                               \
+}
+
+/* Move in a window, and fail if it doesn't work. */
+#define        MOVE(sp, lno, cno) {                                            \
+       size_t __lno = (sp)->woff + (lno);                              \
+       if (move(__lno, cno) == ERR) {                                  \
+               msgq(sp, M_ERR,                                         \
+                   "Error: %s/%d: move:l(%u), c(%u), o(%u).",          \
+                   tail(__FILE__), __LINE__, lno, cno, sp->woff);      \
+               return (1);                                             \
+       }                                                               \
+}
+
+/* Add a character. */
+#define        ADDCH(ch) {                                                     \
+       int __ch = (ch);                                                \
+       ADDNSTR(cname[__ch].name, cname[__ch].len);                     \
+}
+
+/* Add a string len bytes long. */
+#define        ADDNSTR(str, len) {                                             \
+       if (addnstr(str, len) == ERR) {                                 \
+               int __x, __y;                                           \
+               getyx(stdscr, __y, __x);                                \
+               msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u).",      \
+                   tail(__FILE__), __LINE__, __y, __x);                \
+               return (1);                                             \
+       }                                                               \
+}
+
+/* Add a string. */
+#define        ADDSTR(str) {                                                   \
+       if (addstr(str) == ERR) {                                       \
+               int __x, __y;                                           \
+               getyx(stdscr, __y, __x);                                \
+               msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u).",       \
+                   tail(__FILE__), __LINE__, __y, __x);                \
+               return (1);                                             \
+       }                                                               \
+}
+
+/* Public routines. */
+void   svi_bell __P((SCR *));
+int    svi_bg __P((SCR *));
+int    svi_busy __P((SCR *, char const *));
+int    svi_change __P((SCR *, EXF *, recno_t, enum operation));
+size_t svi_chposition __P((SCR *, EXF *, recno_t, size_t));
+int    svi_column __P((SCR *, EXF *, size_t *));
+enum confirm
+       svi_confirm __P((SCR *, EXF *, MARK *, MARK *));
+int    svi_clear __P((SCR *));
+int    svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *));
+int    svi_ex_run __P((SCR *, EXF *, MARK *));
+int    svi_ex_write __P((void *, const char *, int));
+int    svi_fg __P((SCR *, CHAR_T *));
+enum input
+       svi_get __P((SCR *, EXF *, TEXTH *, int, u_int));
+int    svi_optchange __P((SCR *, int));
+int    svi_rabs __P((SCR *, long));
+int    svi_refresh __P((SCR *, EXF *));
+size_t svi_relative __P((SCR *, EXF *, recno_t));
+int    svi_rrel __P((SCR *, long));
+int    svi_screen_copy __P((SCR *, SCR *));
+int    svi_screen_edit __P((SCR *, EXF *));
+int    svi_screen_end __P((SCR *));
+int    svi_sm_down __P((SCR *, EXF *, MARK *, recno_t, int));
+int    svi_sm_fill __P((SCR *, EXF *, recno_t, enum position));
+int    svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position));
+int    svi_sm_up __P((SCR *, EXF *, MARK *, recno_t, int));
+int    svi_split __P((SCR *, ARGS *[]));
+int    svi_suspend __P((SCR *));
+int    svi_swap __P((SCR *, SCR **, char *));
+
+/* Private routines. */
+int    svi_curses_end __P((SCR *));
+int    svi_curses_init __P((SCR *));
+int    svi_divider __P((SCR *));
+int    svi_init __P((SCR *));
+int    svi_join __P((SCR *, SCR **));
+int    svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *));
+size_t svi_lrelative __P((SCR *, EXF *, recno_t, size_t));
+size_t svi_ncols __P((SCR *, u_char *, size_t, size_t *));
+int    svi_number __P((SCR *, EXF *));
+int    svi_paint __P((SCR *, EXF *));
+size_t svi_screens __P((SCR *, EXF *, recno_t, size_t *));
+int    svi_sm_1down __P((SCR *, EXF *));
+int    svi_sm_1up __P((SCR *, EXF *));
+int    svi_sm_cursor __P((SCR *, EXF *, SMAP **));
+int    svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *));
+recno_t        svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t));
+int    svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *));
+
+/* Private debugging routines. */
+#ifdef DEBUG
+int    svi_gdbrefresh __P((void));
+#endif
diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c
new file mode 100644 (file)
index 0000000..5bcb6e2
--- /dev/null
@@ -0,0 +1,1041 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_smap.c 8.29 (Berkeley) 11/30/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "svi_screen.h"
+
+static int     svi_deleteln __P((SCR *, int));
+static int     svi_insertln __P((SCR *, int));
+static int     svi_sm_delete __P((SCR *, EXF *, recno_t));
+static int     svi_sm_insert __P((SCR *, EXF *, recno_t));
+static int     svi_sm_reset __P((SCR *, EXF *, recno_t));
+
+/*
+ * svi_change --
+ *     Make a change to the screen.
+ */
+int
+svi_change(sp, ep, lno, op)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       enum operation op;
+{
+       SMAP *p;
+       size_t oldy, oldx;
+
+       /* 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; p <= TMAP; ++p)
+                               --p->lno;
+                       if (sp->lno >= lno)
+                               --sp->lno;
+                       F_SET(sp, S_RENUMBER);
+                       break;
+               case LINE_INSERT:
+                       for (p = HMAP; p <= TMAP; ++p)
+                               ++p->lno;
+                       if (sp->lno >= lno)
+                               ++sp->lno;
+                       F_SET(sp, S_RENUMBER);
+                       break;
+               case LINE_RESET:
+                       break;
+               }
+               return (0);
+       }
+
+       F_SET(SVP(sp), SVI_SCREENDIRTY);
+
+       /* Flush cached information from svi_screens(). */
+       SVP(sp)->ss_lno = OOBLNO;
+
+       /* Invalidate the cursor, if it's on this line. */
+       if (sp->lno == lno)
+               F_SET(SVP(sp), SVI_CUR_INVALID);
+
+       getyx(stdscr, oldy, oldx);
+
+       switch (op) {
+       case LINE_DELETE:
+               if (svi_sm_delete(sp, ep, lno))
+                       return (1);
+               F_SET(sp, S_RENUMBER);
+               break;
+       case LINE_INSERT:
+               if (svi_sm_insert(sp, ep, lno))
+                       return (1);
+               F_SET(sp, S_RENUMBER);
+               break;
+       case LINE_RESET:
+               if (svi_sm_reset(sp, ep, lno))
+                       return (1);
+               break;
+       default:
+               abort();
+       }
+
+       MOVEA(sp, oldy, oldx);
+
+       return (0);
+}
+
+/*
+ * svi_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.
+ */
+int
+svi_sm_fill(sp, ep, lno, pos)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       enum position pos;
+{
+       SMAP *p, tmp;
+       
+       /* Flush all cached information from the SMAP. */
+       for (p = HMAP; p <= TMAP; ++p)
+               SMAP_FLUSH(p);  
+
+       switch (pos) {
+       case P_FILL:
+               tmp.lno = 1;
+               tmp.off = 1;
+
+               /* See if less than half a screen from the top. */
+               if (svi_sm_nlines(sp, ep,
+                   &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+                       lno = 1;
+                       goto top;
+               }
+
+               /* See if less than half a screen from the bottom. */
+               if (file_lline(sp, ep, &tmp.lno))
+                       return (1);
+               tmp.off = svi_screens(sp, ep, tmp.lno, NULL);
+               if (svi_sm_nlines(sp, ep,
+                   &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+                       TMAP->lno = tmp.lno;
+                       TMAP->off = tmp.off;
+                       goto bottom;
+               }
+               goto middle;
+       case P_TOP:
+               if (lno != OOBLNO) {
+top:                   HMAP->lno = lno;
+                       HMAP->off = 1;
+               }
+               /* If we fail, just punt. */
+               for (p = HMAP; p < TMAP; ++p)
+                       if (svi_sm_next(sp, ep, p, p + 1))
+                               goto err;
+               break;
+       case P_MIDDLE:
+               /* If we fail, guess that the file is too small. */
+middle:                p = HMAP + (TMAP - HMAP) / 2;
+               for (p->lno = lno, p->off = 1; p > HMAP; --p)
+                       if (svi_sm_prev(sp, ep, p, p - 1)) {
+                               lno = 1;
+                               goto top;
+                       }
+
+               /* If we fail, just punt. */
+               p = HMAP + (TMAP - HMAP) / 2;
+               for (; p < TMAP; ++p)
+                       if (svi_sm_next(sp, ep, p, p + 1))
+                               goto err;
+               break;
+       case P_BOTTOM:
+               if (lno != OOBLNO) {
+                       TMAP->lno = lno;
+                       TMAP->off = svi_screens(sp, ep, lno, NULL);
+               }
+               /* If we fail, guess that the file is too small. */
+bottom:                for (p = TMAP; p > HMAP; --p)
+                       if (svi_sm_prev(sp, ep, p, p - 1)) {
+                               lno = 1;
+                               goto top;
+                       }
+               break;
+       }
+       return (0);
+
+       /*
+        * Try and put *something* on the screen.  If this fails,
+        * we have a serious hard error.
+        */
+err:   HMAP->lno = 1;
+       HMAP->off = 1;
+       for (p = HMAP; p < TMAP; ++p)
+               if (svi_sm_next(sp, ep, p, p + 1))
+                       return (1);
+       return (0);
+}
+
+/*
+ * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
+ * screen only contains one line, or, if the line is the entire screen, this
+ * gets fairly exciting.  Skip the fun and simply return if there's only one
+ * line in the screen, or just call fill.  Fill may not be entirely accurate,
+ * i.e. we may be painting the screen with something not even close to the
+ * cursor, but it's not like we're into serious performance issues here, and
+ * the refresh routine will fix it for us.
+ */
+#define        TOO_WEIRD {                                                     \
+       if (cnt_orig >= sp->t_rows) {                                   \
+               if (cnt_orig == 1)                                      \
+                       return (0);                                     \
+               if (file_gline(sp, ep, lno, NULL) == NULL)              \
+                       if (file_lline(sp, ep, &lno))                   \
+                               return (1);                             \
+               F_SET(sp, S_REDRAW);                                    \
+               return (svi_sm_fill(sp, ep, lno, P_TOP));               \
+       }                                                               \
+}
+
+/*
+ * svi_sm_delete --
+ *     Delete a line out of the SMAP.
+ */
+static int
+svi_sm_delete(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       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);
+       for (cnt_orig = 1, t = p + 1;
+           t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+
+       TOO_WEIRD;
+
+       /* Delete that many lines from the screen. */
+       MOVE(sp, p - HMAP, 0);
+       if (svi_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 && svi_sm_next(sp, ep, p, p + 1))
+                       return (1);
+               /* svi_sm_next() flushed the cache. */
+               if (svi_line(sp, ep, ++p, NULL, NULL))
+                       return (1);
+               if (p == TMAP)
+                       break;
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_insert --
+ *     Insert a line into the SMAP.
+ */
+static int
+svi_sm_insert(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+{
+       SMAP *p, *t;
+       size_t cnt_orig, cnt;
+
+       /*
+        * 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 = svi_screens(sp, ep, lno, NULL);
+
+       TOO_WEIRD;
+
+       /*
+        * 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. */
+       MOVE(sp, p - HMAP, 0);
+       if (svi_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->off = cnt;
+               SMAP_FLUSH(t);
+               if (svi_line(sp, ep, t, NULL, NULL))
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_reset --
+ *     Reset a line in the SMAP.
+ */
+static int
+svi_sm_reset(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       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);
+       for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+       cnt_new = svi_screens(sp, ep, lno, NULL);
+
+       TOO_WEIRD;
+
+       if (cnt_orig == cnt_new) {
+               do {
+                       SMAP_FLUSH(p);
+                       if (svi_line(sp, ep, 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;
+
+               /* Push down the extra lines. */
+               MOVE(sp, p - HMAP, 0);
+               if (svi_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->off = cnt;
+                       SMAP_FLUSH(t);
+                       if (svi_line(sp, ep, t, NULL, NULL))
+                               return (1);
+               }
+       } else {
+               /* Get the difference. */
+               diff = cnt_orig - cnt_new;
+
+               /* Delete that many lines from the screen. */
+               MOVE(sp, p - HMAP, 0);
+               if (svi_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->off = cnt;
+                       SMAP_FLUSH(t);
+                       if (svi_line(sp, ep, t, NULL, NULL))
+                               return (1);
+               }
+
+               /* Display the new lines at the bottom of the screen. */
+               for (t = TMAP - diff;;) {
+                       if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
+                               return (1);
+                       /* svi_sm_next() flushed the cache. */
+                       if (svi_line(sp, ep, ++t, NULL, NULL))
+                               return (1);
+                       if (t == TMAP)
+                               break;
+               }
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_up --
+ *     Scroll the SMAP up count logical lines.
+ */
+int
+svi_sm_up(sp, ep, rp, count, cursor_move)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+       recno_t count;
+       int cursor_move;
+{
+       SMAP *p, svmap, tmp;
+       int ignore_cursor;
+
+       /* Set the default return position. */
+       rp->lno = sp->lno;
+       rp->cno = sp->cno;
+
+       /*
+        * Invalidate the cursor.  The line is probably going to change,
+        * but if cursor_move isn't set it may not.  In any case, this
+        * routine moves the cursor to draw things.
+        */
+       F_SET(SVP(sp), SVI_CUR_INVALID);
+
+       /*
+        * There are two forms of this command, one where the cursor tries to
+        * follow the line, and one where it doesn't.  In the latter, we try
+        * and keep the cursor at the same position on the screen, but, if the
+        * screen is small enough and the line length large enough, the cursor
+        * can end up in very strange places.  Probably not worth fixing.
+        *
+        * Find the line in the SMAP -- ignore the cursor if it wasn't on the
+        * screen.
+        */
+       if (svi_sm_cursor(sp, ep, &p))
+               return (1);
+       if (p == NULL)
+               ignore_cursor = 1;
+       else {
+               svmap = *p;
+               ignore_cursor = 0;
+       }
+
+       /*
+        * Check to see if movement is possible.  Lots of checks...
+        *
+        * Find out if it's possible to move past the end of the map.  If
+        * that's okay because we think that we can move the cursor down
+        * in the map, check to make sure that the map isn't mostly empty.
+        */
+       if (svi_sm_next(sp, ep, TMAP, &tmp))
+               return (1);
+       if (tmp.lno > TMAP->lno &&
+           !file_gline(sp, ep, tmp.lno, NULL) ||
+           tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
+               if (!cursor_move || ignore_cursor || p == TMAP) {
+                       v_eof(sp, ep, NULL);
+                       return (1);
+               }
+               if (svi_sm_next(sp, ep, p, &tmp))
+                       return (1);
+               if (!file_gline(sp, ep, tmp.lno, NULL) ||
+                   tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
+                       v_eof(sp, ep, NULL);
+                       return (1);
+               }
+       }
+                       
+       /*
+        * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+        *
+        * If it's a small screen, and the movement is small, open up the
+        * screen.  Otherwise, compress and repaint.  If we compress, we
+        * ignore the cursor, the movement is too large to care.
+        */
+       if (ISSMALLSCREEN(sp))
+               if (count <= HALFTEXT(sp)) {
+                       for (; count && sp->t_rows != sp->t_maxrows;
+                            --count, ++sp->t_rows) {
+                               if (svi_sm_next(sp, ep, TMAP, &tmp))
+                                       return (1);
+                               if (TMAP->lno != tmp.lno &&
+                                   !file_gline(sp, ep, tmp.lno, NULL))
+                                       break;
+                               *++TMAP = tmp;
+                               /* svi_sm_next() flushed the cache. */
+                               if (svi_line(sp, ep, TMAP, NULL, NULL))
+                                       return (1);
+                       }
+                       if (count == 0)
+                               return (0);
+               } else {
+                       MOVE(sp, INFOLINE(sp), 0);
+                       clrtoeol();
+                       for (;
+                           sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+                               MOVE(sp, TMAP - HMAP, 0);
+                               clrtoeol();
+                       }
+                       ignore_cursor = 1;
+               }
+
+       for (; count; --count) {
+               /* Decide what would show up on the screen. */
+               if (svi_sm_next(sp, ep, TMAP, &tmp))
+                       return (1);
+
+               /* If the line doesn't exist, we're done. */
+               if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL))
+                       break;
+                       
+               /* Scroll the screen cursor up one logical line. */
+               if (svi_sm_1up(sp, ep))
+                       return (1);
+               if (!cursor_move && !ignore_cursor && p > HMAP)
+                       --p;
+       }
+
+       /* If ignoring the cursor, we're done. */
+       if (ignore_cursor)
+               return (0);
+
+       if (cursor_move) {
+               /*
+                * If we didn't move enough, head toward EOF.  Check to make
+                * sure the lines actually, if the file is smaller than the
+                * screen they may not.
+                */
+               for (; count; --count, ++p)
+                       if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL))
+                               break;
+       } else {
+               /*
+                * If the line itself moved, invalidate the cursor, because
+                * the comparison with the old line/new line won't be right
+                */
+               F_SET(SVP(sp), SVI_CUR_INVALID);
+
+               /* If didn't move enough, it's an error. */
+               if (count) {
+                       v_eof(sp, ep, NULL);
+                       return (1);
+               }
+
+               /* If the cursor moved off the screen, move it to the top. */
+               if (sp->lno < HMAP->lno)
+                       p = HMAP;
+       }
+       /*
+        * On a logical movement, we 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 (p->lno != svmap.lno || p->off != svmap.off) {
+               rp->lno = p->lno;
+               rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_1up --
+ *     Scroll the SMAP up one.
+ */
+int
+svi_sm_1up(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       /*
+        * Delete the top line of the screen.  Shift the screen map up.
+        * Display a new line at the bottom of the screen.
+        */
+       MOVE(sp, 0, 0);
+       if (svi_deleteln(sp, 1))
+               return (1);
+
+       /* One-line screens can fail. */
+       if (HMAP == TMAP) {
+               if (svi_sm_next(sp, ep, TMAP, TMAP))
+                       return (1);
+       } else {
+               memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
+               if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
+                       return (1);
+       }
+       /* svi_sm_next() flushed the cache. */
+       if (svi_line(sp, ep, TMAP, NULL, NULL))
+               return (1);
+       return (0);
+}
+
+/*
+ * svi_deleteln --
+ *     Delete a line a la curses, make sure to put the information
+ *     line and other screens back.
+ */
+static int
+svi_deleteln(sp, cnt)
+       SCR *sp;
+       int cnt;
+{
+       size_t oldy, oldx;
+
+       getyx(stdscr, oldy, oldx);
+       while (cnt--) {
+               deleteln();
+               MOVE(sp, INFOLINE(sp) - 1, 0);
+               insertln();
+               MOVEA(sp, oldy, oldx);
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_down --
+ *     Scroll the SMAP down count logical lines.
+ */
+int
+svi_sm_down(sp, ep, rp, count, cursor_move)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+       recno_t count;
+       int cursor_move;
+{
+       SMAP *p, svmap;
+       int ignore_cursor;
+
+       /* Set the default return position. */
+       rp->lno = sp->lno;
+       rp->cno = sp->cno;
+
+       /*
+        * Invalidate the cursor.  The line is probably going to change,
+        * but if cursor_move isn't set it may not.  In any case, this
+        * routine moves the cursor to draw things.
+        */
+       F_SET(SVP(sp), SVI_CUR_INVALID);
+
+       /*
+        * There are two forms of this command, one where the cursor tries to
+        * follow the line, and one where it doesn't.  In the latter, we try
+        * and keep the cursor at the same position on the screen, but, if the
+        * screen is small enough and the line length large enough, the cursor
+        * can end up in very strange places.  Probably not worth fixing.
+        *
+        * Find the line in the SMAP -- ignore the cursor if it wasn't on the
+        * screen.
+        */
+       if (svi_sm_cursor(sp, ep, &p))
+               return (1);
+       if (p == NULL)
+               ignore_cursor = 1;
+       else {
+               svmap = *p;
+               ignore_cursor = 0;
+       }
+
+       /* Check to see if movement is possible. */
+       if (HMAP->lno == 1 && HMAP->off == 1 &&
+           (!cursor_move || ignore_cursor || p == HMAP)) {
+               v_sof(sp, NULL);
+               return (1);
+       }
+
+       /*
+        * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+        *
+        * If it's a small screen, and the movement is small, open up the
+        * screen.  Otherwise, compress and repaint.  If we compress, we
+        * ignore the cursor, the movement is too large to care.
+        */
+       if (ISSMALLSCREEN(sp))
+               if (count <= HALFTEXT(sp)) {
+                       for (; count && sp->t_rows != sp->t_maxrows &&
+                           (HMAP->lno > 1 || HMAP->off > 1);
+                           --count, ++sp->t_rows) {
+                               ++TMAP;
+                               if (svi_sm_1down(sp, ep))
+                                       return (1);
+                               if (!cursor_move)
+                                       ++p;
+                       }
+                       if (count == 0)
+                               return (0);
+               } else {
+                       MOVE(sp, INFOLINE(sp), 0);
+                       clrtoeol();
+                       for (;
+                           sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+                               MOVE(sp, TMAP - HMAP, 0);
+                               clrtoeol();
+                       }
+                       ignore_cursor = 1;
+               }
+
+       for (; count; --count) {
+               /* If the line doesn't exist, we're done. */
+               if (HMAP->lno == 1 && HMAP->off == 1)
+                       break;
+                       
+               /* Scroll the screen and cursor down one logical line. */
+               if (svi_sm_1down(sp, ep))
+                       return (1);
+               if (!cursor_move && !ignore_cursor && p < TMAP)
+                       ++p;
+       }
+
+       /* If ignoring the cursor, we're done. */
+       if (ignore_cursor)
+               return (0);
+
+       if (cursor_move) {
+               /* If we didn't move enough, move to SOF. */
+               if (count)
+                       p = HMAP;
+       } else {
+               /*
+                * If the line itself moved, invalidate the cursor, because
+                * the comparison with the old line/new line won't be right.
+                */
+               F_SET(SVP(sp), SVI_CUR_INVALID);
+
+               /* If didn't move enough, it's an error. */
+               if (count) {
+                       v_sof(sp, NULL);
+                       return (1);
+               }
+
+               /* If the cursor moved off the screen, move it to the bottom. */
+               if (sp->lno > TMAP->lno)
+                       p = TMAP;
+       }
+
+       /*
+        * On a logical movement, we 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 (p->lno != svmap.lno || p->off != svmap.off) {
+               rp->lno = p->lno;
+               rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_1down --
+ *     Scroll the SMAP down one.
+ */
+int
+svi_sm_1down(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       /*
+        * Clear the bottom line of the screen, insert a line at the top
+        * of the screen.  Shift the screen map down, display a new line
+        * at the top of the screen.
+        */
+       MOVE(sp, sp->t_rows, 0);
+       clrtoeol();
+       MOVE(sp, 0, 0);
+       if (svi_insertln(sp, 1))
+               return (1);
+       memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
+       if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
+               return (1);
+       /* svi_sm_prev() flushed the cache. */
+       if (svi_line(sp, ep, HMAP, NULL, NULL))
+               return (1);
+       return (0);
+}
+
+/*
+ * svi_insertln --
+ *     Insert a line a la curses, make sure to put the information
+ *     line and other screens back.
+ */
+static int
+svi_insertln(sp, cnt)
+       SCR *sp;
+       int cnt;
+{
+       size_t oldy, oldx;
+
+       getyx(stdscr, oldy, oldx);
+       while (cnt--) {
+               MOVE(sp, INFOLINE(sp) - 1, 0);
+               deleteln();
+               MOVEA(sp, oldy, oldx);
+               insertln();
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_next --
+ *     Fill in the next entry in the SMAP.
+ */
+int
+svi_sm_next(sp, ep, p, t)
+       SCR *sp;
+       EXF *ep;
+       SMAP *p, *t;
+{
+       size_t lcnt;
+
+       SMAP_FLUSH(t);
+       if (O_ISSET(sp, O_LEFTRIGHT)) {
+               t->lno = p->lno + 1;
+               t->off = p->off;
+       } else {
+               lcnt = svi_screens(sp, ep, p->lno, NULL);
+               if (lcnt == p->off) {
+                       t->lno = p->lno + 1;
+                       t->off = 1;
+               } else {
+                       t->lno = p->lno;
+                       t->off = p->off + 1;
+               }
+       }
+       return (0);
+}
+
+/*
+ * svi_sm_prev --
+ *     Fill in the previous entry in the SMAP.
+ */
+int
+svi_sm_prev(sp, ep, p, t)
+       SCR *sp;
+       EXF *ep;
+       SMAP *p, *t;
+{
+       SMAP_FLUSH(t);
+       if (O_ISSET(sp, O_LEFTRIGHT)) {
+               t->lno = p->lno - 1;
+               t->off = p->off;
+       } else if (p->off != 1) {
+               t->lno = p->lno;
+               t->off = p->off - 1;
+       } else {
+               t->lno = p->lno - 1;
+               t->off = svi_screens(sp, ep, t->lno, NULL);
+       }
+       return (t->lno == 0);
+}
+
+/*
+ * svi_sm_cursor --
+ *     Return the SMAP entry referenced by the cursor.
+ */
+int
+svi_sm_cursor(sp, ep, smp)
+       SCR *sp;
+       EXF *ep;
+       SMAP **smp;
+{
+       SMAP *p;
+
+       /* See if the cursor is not in the map. */
+       if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) {
+               *smp = NULL;
+               return (0);
+       }
+
+       /* 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) {
+                       *smp = p;
+                       return (0);
+               }
+               if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
+                       return (1);
+               if (p->c_eboff >= sp->cno) {
+                       *smp = p;
+                       return (0);
+               }
+       }
+
+       /* It was past the end of the map after all. */
+       *smp = NULL;
+       return (0);
+}
+
+/*
+ * svi_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.
+ */
+int
+svi_sm_position(sp, ep, rp, cnt, pos)
+       SCR *sp;
+       EXF *ep;
+       MARK *rp;
+       u_long cnt;
+       enum position pos;
+{
+       SMAP *smp;
+       recno_t last;
+       
+       switch (pos) {
+       case P_TOP:
+               if (cnt > TMAP - HMAP)
+                       goto err;
+               smp = HMAP + cnt;
+               break;
+       case P_MIDDLE:
+               if (cnt > (TMAP - HMAP) / 2)
+                       goto err;
+               smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
+               goto eof;
+       case P_BOTTOM:
+               if (cnt > TMAP - HMAP) {
+err:                   msgq(sp, M_BERR, "Movement past the end-of-screen.");
+                       return (1);
+               }
+               smp = TMAP - cnt;
+eof:           if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
+                       if (file_lline(sp, ep, &last))
+                               return (1);
+                       for (; smp->lno > last && smp > HMAP; --smp);
+               }
+               break;
+       default:
+               abort();
+       }
+
+       if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
+               return (1);
+       rp->lno = smp->lno;
+       rp->cno = smp->c_sboff;
+
+       return (0);
+}
+
+/*
+ * svi_sm_nlines --
+ *     Return the number of screen lines from an SMAP entry to the
+ *     start of some file line, less than a maximum value.
+ */
+recno_t
+svi_sm_nlines(sp, ep, from_sp, to_lno, max)
+       SCR *sp;
+       EXF *ep;
+       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->off - 1);
+
+       if (from_sp->lno > to_lno) {
+               lcnt = from_sp->off - 1;        /* Correct for off-by-one. */
+               for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
+                       lcnt += svi_screens(sp, ep, lno, NULL);
+       } else {
+               lno = from_sp->lno;
+               lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
+               for (; ++lno < to_lno && lcnt <= max;)
+                       lcnt += svi_screens(sp, ep, lno, NULL);
+       }
+       return (lcnt);
+}
diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c
new file mode 100644 (file)
index 0000000..f6be03d
--- /dev/null
@@ -0,0 +1,581 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_split.c        8.29 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_split --
+ *     Split the screen.
+ */
+int
+svi_split(sp, argv)
+       SCR *sp;
+       ARGS *argv[];
+{
+       MSG *mp, *next;
+       SCR *tsp, saved_sp;
+       SVI_PRIVATE saved_svp;
+       SMAP *smp;
+       size_t cnt, half;
+       int issmallscreen, splitup;
+
+       /* Check to see if it's possible. */
+       half = sp->rows / 2;
+       if (half < MINIMUM_SCREEN_ROWS) {
+               msgq(sp, M_ERR, "Screen must be larger than %d to split",
+                    MINIMUM_SCREEN_ROWS);
+               return (1);
+       }
+
+       /* Get a new screen. */
+       if (screen_init(sp, &tsp, 0))
+               return (1);
+       MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
+       if (_HMAP(tsp) == NULL)
+               return (1);
+
+       /*
+        * We're about to modify the current screen.  Save the contents
+        * in case something goes horribly, senselessly wrong.
+        */
+       saved_sp = *sp;
+       saved_svp = *SVP(sp);
+
+/* INITIALIZED AT SCREEN CREATE. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+       /*
+        * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+        * Set a flag so we know to fix the screen up later.
+        */
+       issmallscreen = ISSMALLSCREEN(sp);
+
+       /*
+        * Split the screen, and link the screens together.  If the cursor
+        * is in the top half of the current screen, the new screen goes
+        * under the current screen.  Else, it goes above the current screen.
+        *
+        * The columns in the screen don't change.
+        */
+       tsp->cols = sp->cols;
+       
+       cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
+       if (cnt <= half) {                      /* Parent is top half. */
+               /* Child. */
+               tsp->rows = sp->rows - half;
+               tsp->woff = sp->woff + half;
+               tsp->t_maxrows = tsp->rows - 1;
+
+               /* Parent. */
+               sp->rows = half;
+               sp->t_maxrows = sp->rows - 1;
+
+               splitup = 0;
+       } else {                                /* Parent is bottom half. */
+               /* Child. */
+               tsp->rows = sp->rows - half;
+               tsp->woff = sp->woff;
+               tsp->t_maxrows = tsp->rows - 1;
+
+               /* Parent. */
+               sp->rows = half;
+               sp->woff += tsp->rows;
+               sp->t_maxrows = sp->rows - 1;
+
+               /* Shift the parent's map down. */
+               memmove(_HMAP(sp),
+                   _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
+
+               splitup = 1;
+       }
+
+       /*
+        * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+        *
+        * The child may have different screen options sizes than the
+        * parent, so use them.  Make sure that the text counts aren't
+        * larger than the new screen sizes.
+        */
+       if (issmallscreen) {
+               /* Fix the text line count for the parent. */
+               if (splitup)
+                       sp->t_rows -= tsp->rows;
+
+               /* Fix the parent screen. */
+               if (sp->t_rows > sp->t_maxrows)
+                       sp->t_rows = sp->t_maxrows;
+               if (sp->t_minrows > sp->t_maxrows)
+                       sp->t_minrows = sp->t_maxrows;
+
+               /* Fix the child screen. */
+               tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+               if (tsp->t_rows > tsp->t_maxrows)
+                       tsp->t_rows = tsp->t_maxrows;
+               if (tsp->t_minrows > tsp->t_maxrows)
+                       tsp->t_minrows = tsp->t_maxrows;
+
+               /*
+                * If we split up, i.e. the child is on top, lines that
+                * were painted in the parent may not be painted in the
+                * child.  Clear any lines not being used in the child
+                * screen.
+                *
+                */
+               if (splitup)
+                       for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+                               MOVE(tsp, cnt, 0)
+                               clrtoeol();
+                       }
+       } else {
+               sp->t_minrows = sp->t_rows = sp->rows - 1;
+
+               /*
+                * The new screen may be a small screen, even though the
+                * parent was not.  Don't complain if O_WINDOW is too large,
+                * we're splitting the screen so the screen is much smaller
+                * than normal.  Clear any lines not being used in the child
+                * screen.
+                */
+               tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+               if (tsp->t_rows > tsp->rows - 1)
+                       tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
+               else
+                       for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+                               MOVE(tsp, cnt, 0)
+                               clrtoeol();
+                       }
+       }
+
+       /* Adjust the ends of both maps. */
+       _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
+       _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
+
+       /*
+        * In any case, if the size of the scrolling region hasn't been
+        * modified by the user, reset it so it's reasonable for the split
+        * screen.
+        */
+       if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) {
+               O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+               O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2;
+       }
+
+       /*
+        * If files specified, build the file list, else, link to the
+        * current file.
+        */
+       if (argv == NULL) {
+               if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL)
+                       goto err;
+       } else
+               for (; (*argv)->len != 0; ++argv)
+                       if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL)
+                               goto err;
+
+       /* Set up the argument and current FREF pointers. */
+       if ((tsp->frp = file_first(tsp)) == NULL) {
+               msgq(sp, M_ERR, "No files in the file list.");
+               goto err;
+       }
+
+       tsp->a_frp = tsp->frp;
+
+       /*
+        * Copy the file state flags, start the file.  Fill the child's
+        * screen map.  If the file is unchanged, keep the screen and
+        * cursor the same.
+        */
+       if (argv == NULL) {
+               tsp->ep = sp->ep;
+               ++sp->ep->refcnt;
+
+               tsp->frp->flags = sp->frp->flags;
+               tsp->frp->lno = sp->lno;
+               tsp->frp->cno = sp->cno;
+               F_SET(tsp->frp, FR_CURSORSET);
+
+               /* Copy the parent's map into the child's map. */
+               memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
+       } else {
+               if (file_init(tsp, tsp->frp, NULL, 0))
+                       goto err;
+               (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
+       }
+
+       /* Everything's initialized, put the screen on the displayed queue.*/
+       if (splitup) {
+               /* Link in before the parent. */
+               CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
+       } else {
+               /* Link in after the parent. */
+               CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
+       }
+
+       /* Clear the current information lines in both screens. */
+       MOVE(sp, INFOLINE(sp), 0);
+       clrtoeol();
+       MOVE(tsp, INFOLINE(tsp), 0);
+       clrtoeol();
+
+       /* Redraw the status line for the parent screen. */
+       (void)status(sp, sp->ep, sp->lno, 0);
+
+       /* Save the parent screen's cursor information. */
+       sp->frp->lno = sp->lno;
+       sp->frp->cno = sp->cno;
+       F_SET(sp->frp, FR_CURSORSET);
+
+       /* Completely redraw the child screen. */
+       F_SET(tsp, S_REDRAW);
+
+       /* Switch screens. */
+       sp->nextdisp = tsp;
+       F_SET(sp, S_SSWITCH);
+       return (0);
+
+       /* Recover the original screen. */
+err:   *sp = saved_sp;
+       *SVP(sp) = saved_svp;
+
+       /* Copy any (probably error) messages in the new screen. */
+       for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) {
+               if (!F_ISSET(mp, M_EMPTY))
+                       msg_app(sp->gp, sp,
+                           mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+               next = mp->q.le_next;
+               if (mp->mbuf != NULL)
+                       free(mp->mbuf);
+               free(mp);
+       }
+
+       /* Free the new screen. */
+       free(_HMAP(tsp));
+       free(SVP(tsp));
+       FREE(tsp, sizeof(SCR));
+       return (1);
+}
+
+/*
+ * svi_bg --
+ *     Background the screen, and switch to the next one.
+ */
+int
+svi_bg(csp)
+       SCR *csp;
+{
+       SCR *sp;
+
+       /* Try and join with another screen. */
+       if ((svi_join(csp, &sp)))
+               return (1);
+       if (sp == NULL) {
+               msgq(csp, M_ERR,
+                   "You may not background your only displayed screen.");
+               return (1);
+       }
+
+       /* Move the old screen to the hidden queue. */
+       CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+       CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+
+       /* Switch screens. */
+       csp->nextdisp = sp;
+       F_SET(csp, S_SSWITCH);
+
+       return (0);
+}
+
+/*
+ * svi_join --
+ *     Join the screen into a related screen, if one exists,
+ *     and return that screen.
+ */
+int
+svi_join(csp, nsp)
+       SCR *csp, **nsp;
+{
+       SCR *sp;
+       size_t cnt;
+
+       /*
+        * If a split screen, add space to parent/child.  Make no effort
+        * to clean up the screen's values.  If it's not exiting, we'll
+        * get it when the user asks to show it again.
+        */
+       if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
+               if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
+                       *nsp = NULL;
+                       return (0);
+               }
+               sp->woff = csp->woff;
+       }
+       sp->rows += csp->rows;
+       if (ISSMALLSCREEN(sp)) {
+               sp->t_maxrows += csp->rows;
+               for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
+                       MOVE(sp, cnt, 0);
+                       clrtoeol();
+               }
+               TMAP = HMAP + (sp->t_rows - 1);
+       } else {
+               sp->t_maxrows += csp->rows;
+               sp->t_rows = sp->t_minrows = sp->t_maxrows;
+               TMAP = HMAP + (sp->t_rows - 1);
+               if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+                       return (1);
+               F_SET(sp, S_REDRAW);
+       }
+
+       /*
+        * If the size of the scrolling region hasn't been modified by
+        * the user, reset it so it's reasonable for the new screen.
+        */
+       if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+               O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+
+       *nsp = sp;
+       return (0);
+}
+
+/*
+ * svi_fg --
+ *     Background the current screen, and foreground a new one.
+ */
+int
+svi_fg(csp, name)
+       SCR *csp;
+       CHAR_T *name;
+{
+       SCR *sp;
+
+       if (svi_swap(csp, &sp, name))
+               return (1);
+       if (sp == NULL) {
+               if (name == NULL)
+                       msgq(csp, M_ERR, "There are no background screens.");
+               else
+                       msgq(csp, M_ERR,
+                   "There's no background screen editing a file named %s.",
+                           name);
+               return (1);
+       }
+
+       /* Move the old screen to the hidden queue. */
+       CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+       CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+
+       return (0);
+}
+
+/*
+ * svi_swap --
+ *     Swap the current screen with a hidden one.
+ */
+int
+svi_swap(csp, nsp, name)
+       SCR *csp, **nsp;
+       char *name;
+{
+       SCR *sp;
+       int issmallscreen;
+
+       /* Find the screen, or, if name is NULL, the first screen. */
+       for (sp = csp->gp->hq.cqh_first;
+           sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
+               if (name == NULL || !strcmp(FILENAME(sp->frp), name))
+                       break;
+       if (sp == (void *)&csp->gp->hq) {
+               *nsp = NULL;
+               return (0);
+       }
+       *nsp = sp;
+               
+       /* Save the old screen's cursor information. */
+       csp->frp->lno = csp->lno;
+       csp->frp->cno = csp->cno;
+       F_SET(csp->frp, FR_CURSORSET);
+
+       /* Switch screens. */
+       csp->nextdisp = sp;
+       F_SET(csp, S_SSWITCH);
+
+       /* Initialize terminal information. */
+       SVP(sp)->srows = SVP(csp)->srows;
+
+       issmallscreen = ISSMALLSCREEN(sp);
+
+       /* Initialize screen information. */
+       sp->rows = csp->rows;
+       sp->cols = csp->cols;
+       sp->woff = csp->woff;
+
+       /*
+        * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+        *
+        * The new screens may have different screen options sizes than the
+        * old one, so use them.  Make sure that text counts aren't larger
+        * than the new screen sizes.
+        */
+       if (issmallscreen) {
+               sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
+               if (sp->t_rows > csp->t_maxrows)
+                       sp->t_rows = sp->t_maxrows;
+               if (sp->t_minrows > csp->t_maxrows)
+                       sp->t_minrows = sp->t_maxrows;
+       } else
+               sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1;
+
+       /*
+        * If the size of the scrolling region hasn't been modified by
+        * the user, reset it so it's reasonable for the new screen.
+        */
+       if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+               O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2;
+
+       /*
+        * Don't change the screen's cursor information other than to
+        * note that the cursor is wrong.
+        */
+       F_SET(SVP(sp), SVI_CUR_INVALID);
+
+       /*
+        * The HMAP may be NULL, if the screen got resized and
+        * a bunch of screens had to be hidden.
+        */
+       if (HMAP == NULL)
+               MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP));
+       TMAP = HMAP + (sp->t_rows - 1);
+
+       /* Fill the map. */
+       if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+               return (1);
+
+       /*
+        * The new screen replaces the old screen in the parent/child list.
+        * We insert the new screen after the old one.  If we're exiting,
+        * the exit will delete the old one, if we're foregrounding, the fg
+        * code will move the old one to the hidden queue.
+        */
+       CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+       CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
+
+       F_SET(sp, S_REDRAW);
+       return (0);
+}
+
+/*
+ * svi_rabs --
+ *     Change the absolute size of the current screen.
+ */
+int
+svi_rabs(sp, count)
+       SCR *sp;
+       long count;
+{
+       SCR *g, *s;
+
+       /*
+        * Figure out which screens will grow, which will shrink, and
+        * make sure it's possible.
+        */
+       if (count == 0)
+               return (0);
+       if (count < 0) {
+               count = -count;
+               s = sp;
+               if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+                       goto toosmall;
+               if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+                       if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
+                               goto toobig;
+                       g->woff -= count;
+               } else
+                       s->woff += count;
+       } else {
+               g = sp;
+               if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
+                       if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+                               s = NULL;
+                       else
+                               s->woff += count;
+               else
+                       s = NULL;
+               if (s == NULL) {
+                       if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+toobig:                                msgq(sp, M_BERR, "The screen cannot %s.",
+                                   count < 0 ? "shrink" : "grow");
+                               return (1);
+                       }
+                       if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
+toosmall:                      msgq(sp, M_BERR,
+                                   "The screen can only shrink to %d rows.",
+                                   MINIMUM_SCREEN_ROWS);
+                               return (1);
+                       }
+                       g->woff -= count;
+               }
+       }
+
+       /* Update the screens. */
+       g->rows += count;
+       g->t_rows += count;
+       if (g->t_minrows == g->t_maxrows)
+               g->t_minrows += count;
+       g->t_maxrows += count;
+       _TMAP(g) = _HMAP(g) + (g->t_rows - 1);
+       (void)status(g, g->ep, g->lno, 0);
+       F_SET(g, S_REDRAW);
+
+       s->rows -= count;
+       s->t_rows -= count;
+       s->t_maxrows -= count;
+       if (s->t_minrows > s->t_maxrows)
+               s->t_minrows = s->t_maxrows;
+       _TMAP(s) = _HMAP(s) + (s->t_rows - 1);
+       (void)status(s, s->ep, s->lno, 0);
+       F_SET(s, S_REDRAW);
+
+       return (0);
+}
diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c
new file mode 100644 (file)
index 0000000..3a751b5
--- /dev/null
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_util.c 8.26 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+
+/*
+ * svi_screens --
+ *     Return the number of screens required by the line, or,
+ *     if a column is specified, by the column within the line.
+ */
+size_t
+svi_screens(sp, ep, lno, cnop)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       size_t *cnop;
+{
+       size_t cols, len, screens;
+       char *p;
+
+       /*
+        * Check for single cached value.  The cache is because, if
+        * the line is large, this routine gets called repeatedly.
+        * One other hack, lots of time the user is on column one,
+        * which is an easy one.
+        */
+       if (cnop == NULL) {
+               if (SVP(sp)->ss_lno == lno)
+                       return (SVP(sp)->ss_screens);
+       } else if (*cnop == 0)
+               return (1);
+
+       /* Get a copy of the line. */
+       if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0)
+               return (1);
+
+       /* Figure out how many columns the line/column needs. */
+       cols = svi_ncols(sp, p, len, cnop);
+
+       /* Leading number if O_NUMBER option set. */
+       if (O_ISSET(sp, O_NUMBER))
+               cols += O_NUMBER_LENGTH;
+
+       /* Trailing '$' if O_LIST option set. */
+       if (O_ISSET(sp, O_LIST) && cnop == NULL)
+               cols += sp->gp->cname['$'].len;
+
+       screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+       if (cnop == NULL) {
+               SVP(sp)->ss_lno = lno;
+               SVP(sp)->ss_screens = screens;
+       }
+       return (screens);
+}
+
+/*
+ * svi_ncols --
+ *     Return the number of columns required by the line, or,
+ *     if a column is specified, by the column within the line.
+ */
+size_t
+svi_ncols(sp, p, len, cnop)
+       SCR *sp;
+       u_char *p;
+       size_t len, *cnop;
+{
+       CHNAME const *cname;
+       size_t cno_cnt, scno;
+       int ch, listset;
+
+       cname = sp->gp->cname;
+       listset = O_ISSET(sp, O_LIST);
+
+       if (cnop == NULL)
+               for (scno = 0; len; --len)
+                       SCNO_INCREMENT;
+       else
+               for (cno_cnt = *cnop, scno = 0; len; --len) {
+                       SCNO_INCREMENT;
+                       if (cno_cnt == 0)
+                               break;
+                       --cno_cnt;
+               }
+       return (scno);
+}
+
+/*
+ * bell_putchar --
+ *     Functional version of putchar, for tputs.
+ */
+static void
+bell_putchar(ch)
+       int ch;
+{
+       (void)putchar(ch);
+}
+
+/*
+ * vbell --
+ *     Set up the visual bell information.  Broken out into a
+ *     separate routine so don't allocate 4K every time we beep.
+ */
+static int
+vbell(sp)
+       SCR *sp;
+{
+       size_t len;
+       char *s, *t, b1[2048], b2[2048];
+
+       /* Get the termcap information. */
+       s = O_STR(sp, O_TERM);
+       if (tgetent(b1, s) != 1) {
+               msgq(sp, M_ERR, "No termcap entry for %s", s);
+               return (1);
+       }
+
+       /* Get the visual bell string. */
+       t = b2;
+       if (tgetstr("vb", &t) == NULL) {
+               msgq(sp, M_VINFO,
+                   "No visual bell for %s terminal type", s);
+               return (1);
+       }
+       len = t - b2;
+       MALLOC_RET(sp, s, char *, len);
+       memmove(s, b2, len);
+       if (SVP(sp)->VB != NULL)
+               free(SVP(sp)->VB);
+       SVP(sp)->VB = t;
+       return (0);
+}
+
+/*
+ * svi_bell --
+ *     Ring the bell.
+ */
+void
+svi_bell(sp)
+       SCR *sp;
+{
+       if (O_ISSET(sp, O_FLASH) && !F_ISSET(SVP(sp), SVI_NO_VBELL))
+               if (SVP(sp)->VB != NULL) {
+                       (void)tputs(SVP(sp)->VB, 1, bell_putchar);
+                       (void)fflush(stdout);
+               } else {
+                       if (vbell(sp))
+                               F_SET(SVP(sp), SVI_NO_VBELL);
+                       svi_bell(sp);
+               }
+       else
+               (void)write(STDOUT_FILENO, "\007", 1);  /* '\a' */
+       F_CLR(sp, S_BELLSCHED);
+}
+
+/*
+ * svi_optchange --
+ *     Screen specific "option changed" routine.
+ */
+int
+svi_optchange(sp, opt)
+       SCR *sp;
+       int opt;
+{
+       switch (opt) {
+       case O_TERM:
+               /* Toss any saved visual bell information. */
+               if (SVP(sp)->VB != NULL) {
+                       FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1);
+                       SVP(sp)->VB = NULL;
+               }
+               F_CLR(SVP(sp), SVI_NO_VBELL);
+               F_SET(sp, S_RESIZE);
+               break;
+       case O_WINDOW:
+               if (svi_rrel(sp, O_VAL(sp, O_WINDOW)))
+                       return (1);
+               break;
+       }
+
+       (void)v_optchange(sp, opt);
+       (void)ex_optchange(sp, opt);
+
+       return (0);
+}
+
+/*
+ * svi_busy --
+ *     Put the cursor somewhere so the user will think we're busy.
+ */
+int
+svi_busy(sp, msg)
+       SCR *sp;
+       char const *msg;
+{
+       MOVE(sp, INFOLINE(sp), 0);
+       if (msg) {
+               ADDSTR(msg);
+               clrtoeol();
+       }
+       refresh();
+       F_SET(SVP(sp), SVI_CUR_INVALID);
+       return (0);
+}
+
+/*
+ * svi_clear --
+ *     Clear from the row down to the end of the screen.
+ */
+int
+svi_clear(sp)
+       SCR *sp;
+{
+       size_t oldy, oldx, row;
+
+       getyx(stdscr, oldy, oldx);
+       for (row = SVP(sp)->srows - 1; row >= oldy; --row) {
+               MOVEA(sp, row, 0);
+               clrtoeol();
+       }
+       MOVEA(sp, oldy, oldx);
+       refresh();
+       return (0);
+}
+
+/*
+ * svi_suspend --
+ *     Suspend the svi screen; don't kill the process group, curses is
+ *     expected to do that for us.
+ */
+int
+svi_suspend(sp)
+       SCR *sp;
+{
+       struct termios t;
+       int rval;
+
+       /*
+        * XXX
+        * See comment in svi_curses_init().
+        */
+       if (F_ISSET(sp->gp, G_CURSES_S5CB)) {
+               (void)tcgetattr(STDIN_FILENO, &t);
+               (void)tcsetattr(STDIN_FILENO,
+                   TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch);
+       }
+
+       F_SET(sp->gp, G_SLEEPING);
+       if (rval = kill(getpid(), SIGTSTP))
+               msgq(sp, M_SYSERR, "SIGTSTP");
+       F_CLR(sp->gp, G_SLEEPING);
+
+       if (F_ISSET(sp->gp, G_CURSES_S5CB))
+               (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+
+       return (rval);
+}
+
+/*
+ * svi_gdbrefresh --
+ *     Stub routine so can flush out screen changes using gdb.
+ */
+#ifdef DEBUG
+int
+svi_gdbrefresh()
+{
+       refresh();
+       return (0);
+}
+#endif
diff --git a/usr.bin/vi/term.c b/usr.bin/vi/term.c
new file mode 100644 (file)
index 0000000..aa6fd79
--- /dev/null
@@ -0,0 +1,687 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)term.c     8.40 (Berkeley) 1/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "seq.h"
+
+static int keycmp __P((const void *, const void *));
+
+/*
+ * If we're reading less than 20 characters, up the size of the tty buffer.
+ * This shouldn't ever happen, other than the first time through, but it's
+ * possible if a map is large enough.
+ */
+#define        term_read_grow(sp, tty)                                 \
+       (tty)->len - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty)
+static int __term_read_grow __P((SCR *, IBUF *));
+
+/*
+ * 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. */
+} TKLIST;
+static TKLIST const tklist[] = {
+       {"kA",    "O", "insert line"},
+       {"kD",    "x", "delete character"},
+       {"kd",    "j", "cursor down"},
+       {"kE",    "D", "delete to eol"},
+       {"kF", "\004", "scroll down"},
+       {"kH",    "$", "go to eol"},
+       {"kh",    "^", "go to sol"},
+       {"kI",    "i", "insert at cursor"},
+       {"kL",   "dd", "delete line"},
+       {"kl",    "h", "cursor left"},
+       {"kN", "\006", "page down"},
+       {"kP", "\002", "page up"},
+       {"kR", "\025", "scroll up"},
+       {"kS",   "dG", "delete to end of screen"},
+       {"kr",    "l", "cursor right"},
+       {"ku",    "k", "cursor up"},
+       {NULL},
+};
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET.
+ */
+typedef struct _keylist {
+       u_char  value;                  /* Special value. */
+       CHAR_T  ch;                     /* Key. */
+} KEYLIST;
+static KEYLIST keylist[] = {
+       {K_CARAT,          '^'},
+       {K_CNTRLR,      '\022'},
+       {K_CNTRLT,      '\024'},
+       {K_CNTRLZ,      '\032'},
+       {K_COLON,          ':'},
+       {K_CR,            '\r'},
+       {K_ESCAPE,      '\033'},
+       {K_FORMFEED,      '\f'},
+       {K_NL,            '\n'},
+       {K_RIGHTBRACE,     '}'},
+       {K_RIGHTPAREN,     ')'},
+       {K_TAB,           '\t'},
+       {K_VEOF,        '\004'},
+       {K_VERASE,        '\b'},
+       {K_VINTR,       '\003'},
+       {K_VKILL,       '\025'},
+       {K_VLNEXT,      '\026'},
+       {K_VWERASE,     '\027'},
+       {K_ZERO,           '0'},
+};
+
+/*
+ * term_init --
+ *     Initialize the special key lookup table, and the special keys
+ *     defined by the terminal's termcap entry.
+ */
+int
+term_init(sp)
+       SCR *sp;
+{
+       extern CHNAME const asciiname[];        /* XXX */
+       GS *gp;
+       KEYLIST *kp;
+       TKLIST const *tkp;
+       cc_t ch;
+       int cnt;
+       char *sbp, *t, buf[2 * 1024], sbuf[128];
+
+       /*
+        * XXX
+        * 8-bit, ASCII only, for now.  Recompilation should get you
+        * any 8-bit character set, as long as nul isn't a character.
+        */
+       gp = sp->gp;
+       gp->cname = asciiname;                  /* XXX */
+
+       /* Set keys found in the termios structure. */
+#define        TERMSET(name, val) {                                            \
+       if ((ch = gp->original_termios.c_cc[name]) != _POSIX_VDISABLE)  \
+               for (kp = keylist;; ++kp)                               \
+                       if (kp->value == (val)) {                       \
+                               kp->ch = ch;                            \
+                               break;                                  \
+                       }                                               \
+}
+/*
+ * VEOF, VERASE, VKILL are required by POSIX 1003.1-1990,
+ * VWERASE is a 4.4BSD extension.
+ */
+#ifdef VEOF
+       TERMSET(VEOF, K_VEOF);
+#endif
+#ifdef VERASE
+       TERMSET(VERASE, K_VERASE);
+#endif
+#ifdef VINTR
+       TERMSET(VINTR, K_VINTR);
+#endif
+#ifdef VKILL
+       TERMSET(VKILL, K_VKILL);
+#endif
+#ifdef VWERASE
+       TERMSET(VWERASE, K_VWERASE);
+#endif
+
+       /* Sort the special key list. */
+       qsort(keylist,
+           sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp);
+
+       /* Initialize the fast lookup table. */
+       CALLOC_RET(sp,
+           gp->special_key, u_char *, MAX_FAST_KEY + 1, sizeof(u_char));
+       for (gp->max_special = 0, kp = keylist,
+           cnt = sizeof(keylist) / sizeof(keylist[0]); cnt--; ++kp) {
+               if (gp->max_special < kp->value)
+                       gp->max_special = kp->value;
+               if (kp->ch <= MAX_FAST_KEY)
+                       gp->special_key[kp->ch] = kp->value;
+       }
+       
+       /* Set key sequences found in the termcap entry. */
+       switch (tgetent(buf, O_STR(sp, O_TERM))) {
+       case -1:
+               msgq(sp, M_ERR,
+                   "tgetent: %s: %s.", O_STR(sp, O_TERM), strerror(errno));
+               return (0);
+       case 0:
+               msgq(sp, M_ERR,
+                   "%s: unknown terminal type.", O_STR(sp, O_TERM));
+               return (0);
+       }
+
+       for (tkp = tklist; tkp->name != NULL; ++tkp) {
+               sbp = sbuf;
+               if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+                       continue;
+               if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+                   tkp->output, strlen(tkp->output), SEQ_COMMAND, 0))
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * term_push --
+ *     Push keys onto the front of a buffer.
+ *
+ * There is a single input buffer in ex/vi.  Characters are read onto the
+ * end of the buffer by the terminal input routines, and pushed onto the
+ * front of the buffer various other functions in ex/vi.  Each key has an
+ * associated flag value, which indicates if it has already been quoted,
+ * if it is the result of a mapping or an abbreviation.
+ */ 
+int
+term_push(sp, s, len, cmap, flags)
+       SCR *sp;
+       CHAR_T *s;                      /* Characters. */
+       size_t len;                     /* Number of chars. */
+       u_int cmap;                     /* Map count. */
+       u_int flags;                    /* CH_* flags. */
+{
+       IBUF *tty;
+       size_t nlen;
+
+       /* If we have room, stuff the keys into the buffer. */
+       tty = sp->gp->tty;
+       if (len <= tty->next ||
+           (tty->ch != NULL && tty->cnt == 0 && len <= tty->len)) {
+               if (tty->cnt != 0)
+                       tty->next -= len;
+               tty->cnt += len;
+               memmove(tty->ch + tty->next, s, len * sizeof(CHAR_T));
+               memset(tty->chf + tty->next, flags, len);
+               memset(tty->cmap + tty->next, cmap, len);
+               return (0);
+       }
+
+       /* Get enough space plus a little extra. */
+       nlen = tty->cnt + len;
+       if (nlen > tty->len) {
+               size_t olen;
+
+               nlen += 64;
+               olen = tty->len;
+               BINC_RET(sp, tty->ch, olen, nlen * sizeof(tty->ch[0]));
+               olen = tty->len;
+               BINC_RET(sp, tty->chf, olen, nlen * sizeof(tty->chf[0]));
+               BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0]));
+       }
+
+       /*
+        * If there are currently characters in the queue, shift them up,
+        * leaving some extra room.
+        */
+#define        TERM_PUSH_SHIFT 30
+       if (tty->cnt) {
+               memmove(tty->ch + TERM_PUSH_SHIFT + len,
+                   tty->ch + tty->next, tty->cnt * sizeof(tty->ch[0]));
+               memmove(tty->chf + TERM_PUSH_SHIFT + len,
+                   tty->chf + tty->next, tty->cnt * sizeof(tty->chf[0]));
+               memmove(tty->cmap + TERM_PUSH_SHIFT + len,
+                   tty->cmap + tty->next, tty->cnt * sizeof(tty->cmap[0]));
+       }
+
+       /* Put the new characters into the queue. */
+       tty->next = TERM_PUSH_SHIFT;
+       tty->cnt += len;
+       memmove(tty->ch + TERM_PUSH_SHIFT, s, len * sizeof(tty->ch[0]));
+       memset(tty->chf + TERM_PUSH_SHIFT, flags, len * sizeof(tty->chf[0]));
+       memset(tty->cmap + TERM_PUSH_SHIFT, cmap, len * sizeof(tty->cmap[0]));
+       return (0);
+}
+
+/*
+ * Remove characters from the queue, simultaneously clearing the
+ * flag and map counts.
+ */
+#define        QREM_HEAD(q, len) {                                             \
+       size_t __off = (q)->next;                                       \
+       if (len == 1) {                                                 \
+               tty->chf[__off] = 0;                                    \
+               tty->cmap[__off] = 0;                                   \
+       } else {                                                        \
+               memset(tty->chf + __off, 0, len);                       \
+               memset(tty->cmap + __off, 0, len);                      \
+       }                                                               \
+       if (((q)->cnt -= len) == 0)                                     \
+               (q)->next = 0;                                          \
+       else                                                            \
+               (q)->next += len;                                       \
+}
+#define        QREM_TAIL(q, len) {                                             \
+       size_t __off = (q)->next + (q)->cnt - 1;                        \
+       if (len == 1) {                                                 \
+               tty->chf[__off] = 0;                                    \
+               tty->cmap[__off] = 0;                                   \
+       } else {                                                        \
+               memset(tty->chf + __off, 0, len);                       \
+               memset(tty->cmap + __off, 0, len);                      \
+       }                                                               \
+       if (((q)->cnt -= len) == 0)                                     \
+               (q)->next = 0;                                          \
+}
+
+/*
+ * term_key --
+ *     Get the next key.
+ *
+ * !!!
+ * The flag TXT_MAPNODIGIT 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 (TXT_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 TXT_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 that 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.  This implementation counts how many times each input character has
+ * been mapped.  If it reaches some arbitrary value, we flush all mapped keys
+ * and return an error.
+ *
+ * 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.
+ */
+enum input
+term_key(sp, chp, flags)
+       SCR *sp;
+       CH *chp;
+       u_int flags;
+{
+       enum input rval;
+       struct timeval t, *tp;
+       CHAR_T ch;
+       GS *gp;
+       IBUF *tty;
+       SEQ *qp;
+       int cmap, ispartial, nr;
+
+       gp = sp->gp;
+       tty = gp->tty;
+
+       /*
+        * If the queue is empty, read more keys in.  Since no timeout is
+        * requested, s_key_read will either return an error or will read
+        * some number of characters.
+        */
+loop:  if (tty->cnt == 0) {
+               if (term_read_grow(sp, tty))
+                       return (INP_ERR);
+               if (rval = sp->s_key_read(sp, &nr, NULL))
+                       return (rval);
+               /*
+                * If there's something on the mode line that we wanted
+                * the user to see, they just entered a character so we
+                * can presume they saw it.
+                */
+               if (F_ISSET(sp, S_UPDATE_MODE))
+                       F_CLR(sp, S_UPDATE_MODE);
+       }
+
+       /* If the key is mappable and should be mapped, look it up. */
+       if (!(tty->chf[tty->next] & CH_NOMAP) &&
+           LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) {
+               /* Set up timeout value. */
+               if (O_ISSET(sp, O_TIMEOUT)) {
+                       tp = &t;
+                       t.tv_sec = O_VAL(sp, O_KEYTIME) / 10;
+                       t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L;
+               } else
+                       tp = NULL;
+
+               /* Get the next key. */
+newmap:                ch = tty->ch[tty->next];
+               if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch))
+                       goto nomap;
+
+               /* Search the map. */
+remap:         qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt,
+                   LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT,
+                   &ispartial);
+
+               /*
+                * If get a partial match, read more characters and retry
+                * the map.  If no characters read, return the characters
+                * unmapped.
+                */
+               if (ispartial) {
+                       if (term_read_grow(sp, tty))
+                               return (INP_ERR);
+                       if (rval = sp->s_key_read(sp, &nr, tp))
+                               return (rval);
+                       if (nr)
+                               goto remap;
+                       goto nomap;
+               }
+
+               /* If no map, return the character. */
+               if (qp == NULL)
+                       goto nomap;
+
+               /*
+                * 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(TXT_MAPNODIGIT) && !isdigit(qp->output[0]))
+                       goto not_digit_ch;
+
+               /*
+                * Only permit a character to be remapped a certain number
+                * of times before we figure that it's not going to finish.
+                */
+               if ((cmap = tty->cmap[tty->next]) > MAX_MAP_COUNT) {
+                       term_map_flush(sp, "Character remapped too many times");
+                       return (INP_ERR);
+               }
+
+               /* Delete the mapped characters from the queue. */
+               QREM_HEAD(tty, qp->ilen);
+
+               /* If remapping characters, push the character on the queue. */
+               if (O_ISSET(sp, O_REMAP)) {
+                       if (term_push(sp, qp->output, qp->olen, ++cmap, 0))
+                               return (INP_ERR);
+                       goto newmap;
+               }
+
+               /* Else, push the characters on the queue and return one. */
+               if (term_push(sp, qp->output, qp->olen, 0, CH_NOMAP))
+                       return (INP_ERR);
+       }
+
+nomap: ch = tty->ch[tty->next];
+       if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) {
+not_digit_ch:  chp->ch = NOT_DIGIT_CH;
+               chp->value = 0;
+               chp->flags = 0;
+               return (INP_OK);
+       }
+
+       /* Fill in the return information. */
+       chp->ch = ch;
+       chp->flags = tty->chf[tty->next];
+       chp->value = term_key_val(sp, ch);
+
+       /* Delete the character from the queue. */
+       QREM_HEAD(tty, 1);
+
+       /*
+        * O_BEAUTIFY eliminates all control characters except
+        * escape, form-feed, newline and tab.
+        */
+       if (isprint(ch) ||
+           !LF_ISSET(TXT_BEAUTIFY) || !O_ISSET(sp, O_BEAUTIFY) ||
+           chp->value == K_ESCAPE || chp->value == K_FORMFEED ||
+           chp->value == K_NL || chp->value == K_TAB)
+               return (INP_OK);
+
+       goto loop;
+}
+
+/*
+ * term_ab_flush --
+ *     Flush any abbreviated keys.
+ */
+void
+term_ab_flush(sp, msg)
+       SCR *sp;
+       char *msg;
+{
+       IBUF *tty;
+
+       tty = sp->gp->tty;
+       if (!tty->cnt || !(tty->chf[tty->next] & CH_ABBREVIATED))
+               return;
+       do {
+               QREM_HEAD(tty, 1);
+       } while (tty->cnt && tty->chf[tty->next] & CH_ABBREVIATED);
+       msgq(sp, M_ERR, "%s: keys flushed.", msg);
+       
+}
+/*
+ * term_map_flush --
+ *     Flush any mapped keys.
+ */
+void
+term_map_flush(sp, msg)
+       SCR *sp;
+       char *msg;
+{
+       IBUF *tty;
+
+       tty = sp->gp->tty;
+       if (!tty->cnt || !tty->cmap[tty->next])
+               return;
+       do {
+               QREM_HEAD(tty, 1);
+       } while (tty->cnt && tty->cmap[tty->next]);
+       msgq(sp, M_ERR, "%s: keys flushed.", msg);
+       
+}
+
+/*
+ * term_user_key --
+ *     Get the next key, but require the user enter one.
+ */
+enum input
+term_user_key(sp, chp)
+       SCR *sp;
+       CH *chp;
+{
+       enum input rval;
+       IBUF *tty;
+       int nr;
+
+       /*
+        * Read any keys the user has waiting.  Make the race
+        * condition as short as possible.
+        */
+       if (rval = term_key_queue(sp))
+               return (rval);
+
+       /* Wait and read another key. */
+       if (rval = sp->s_key_read(sp, &nr, NULL))
+               return (rval);
+                       
+       /* Fill in the return information. */
+       tty = sp->gp->tty;
+       chp->ch = tty->ch[tty->next + (tty->cnt - 1)];
+       chp->flags = 0;
+       chp->value = term_key_val(sp, chp->ch);
+
+       QREM_TAIL(tty, 1);
+       return (INP_OK);
+}
+
+/* 
+ * term_key_queue --
+ *     Read the keys off of the terminal queue until it's empty.
+ */
+int
+term_key_queue(sp)
+       SCR *sp;
+{
+       enum input rval;
+       struct timeval t;
+       IBUF *tty;
+       int nr;
+
+       t.tv_sec = 0;
+       t.tv_usec = 0;
+       for (tty = sp->gp->tty;;) {
+               if (term_read_grow(sp, tty))
+                       return (INP_ERR);
+               if (rval = sp->s_key_read(sp, &nr, &t))
+                       return (rval);
+               if (nr == 0)
+                       break;
+       }
+       return (INP_OK);
+}
+
+/*
+ * term_key_ch --
+ *     Fill in the key for a value.
+ */
+int
+term_key_ch(sp, val, chp)
+       SCR *sp;
+       int val;
+       CHAR_T *chp;
+{
+       KEYLIST *kp;
+
+       for (kp = keylist;; ++kp)
+               if (kp->value == val) {
+                       *chp = kp->ch;
+                       return (0);
+               }
+       return (1);
+}
+
+/*
+ * __term_key_val --
+ *     Fill in the value for a key.  This routine is the backup
+ *     for the term_key_val() macro.
+ */
+int
+__term_key_val(sp, ch)
+       SCR *sp;
+       ARG_CHAR_T ch;
+{
+       KEYLIST k, *kp;
+
+       k.ch = ch;
+       kp = bsearch(&k, keylist,
+           sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp);
+       return (kp == NULL ? 0 : kp->value);
+}
+
+/*
+ * __term_read_grow --
+ *     Grow the terminal queue.  This routine is the backup for
+ *     the term_read_grow() macro.
+ */
+static int
+__term_read_grow(sp, tty)
+       SCR *sp;
+       IBUF *tty;
+{
+       size_t alen, len, nlen;
+
+       nlen = tty->len + 64;
+       alen = tty->len - (tty->next + tty->cnt);
+
+       len = tty->len;
+       BINC_RET(sp, tty->ch, len, nlen * sizeof(tty->ch[0]));
+       memset(tty->ch + tty->next + tty->cnt, 0, alen * sizeof(tty->ch[0]));
+
+       len = tty->len;
+       BINC_RET(sp, tty->chf, len, nlen * sizeof(tty->chf[0]));
+       memset(tty->chf + tty->next + tty->cnt, 0, alen * sizeof(tty->chf[0]));
+
+       BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0]));
+       memset(tty->cmap +
+           tty->next + tty->cnt, 0, alen * sizeof(tty->cmap[0]));
+       return (0);
+}
+
+static int
+keycmp(ap, bp)
+       const void *ap, *bp;
+{
+       return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
+}
diff --git a/usr.bin/vi/term.h b/usr.bin/vi/term.h
new file mode 100644 (file)
index 0000000..67f7f5d
--- /dev/null
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)term.h      8.26 (Berkeley) 1/7/94
+ */
+
+/* Structure to return a character and associated information. */
+struct _ch {
+       CHAR_T   ch;            /* Character. */
+
+#define        K_CARAT          1
+#define        K_CNTRLR         2
+#define        K_CNTRLT         3
+#define        K_CNTRLZ         4
+#define        K_COLON          5
+#define        K_CR             6
+#define        K_ESCAPE         7
+#define        K_FORMFEED       8
+#define        K_NL             9
+#define        K_RIGHTBRACE    10
+#define        K_RIGHTPAREN    11
+#define        K_TAB           12
+#define        K_VEOF          13
+#define        K_VERASE        14
+#define        K_VINTR         15
+#define        K_VKILL         16
+#define        K_VLNEXT        17
+#define        K_VWERASE       18
+#define        K_ZERO          19
+       u_char   value;         /* Special character flag values. */
+
+#define        CH_ABBREVIATED  0x01    /* Character from an abbreviation. */
+#define        CH_NOMAP        0x02    /* Do not attempt to map the character. */
+#define        CH_QUOTED       0x04    /* Character is already quoted. */
+       u_char   flags;
+};
+
+/*
+ * Structure for the key input buffer.
+ *
+ * MAX_MAP_COUNT was chosen based on the vi maze script, which remaps
+ * characters roughly 250 times.
+ */
+struct _ibuf {
+       CHAR_T  *ch;            /* Array of characters. */
+       u_char  *chf;           /* Array of character flags (CH_*). */
+#define        MAX_MAP_COUNT   270     /* Maximum times a character can remap. */
+       u_char  *cmap;          /* Number of times character has been mapped. */
+
+       size_t   cnt;           /* Count of remaining characters. */
+       size_t   len;           /* Array length. */
+       size_t   next;          /* Offset of next array entry. */
+};
+                               /* Return if more keys in queue. */
+#define        KEYS_WAITING(sp)        ((sp)->gp->tty->cnt)
+#define        MAPPED_KEYS_WAITING(sp)                                         \
+       (KEYS_WAITING(sp) && sp->gp->tty->cmap[sp->gp->tty->next])
+
+/*
+ * Structure to name a character.  Used both as an interface to the
+ * screen and to name objects named by characters in error messages.
+ */
+struct _chname {
+       char    *name;          /* Character name. */
+       u_char   len;           /* Length of the character name. */
+};
+
+/*
+ * Routines that return a key as a side-effect return:
+ *
+ *     INP_OK          Returning a character; must be 0.
+ *     INP_EOF         EOF.
+ *     INP_ERR         Error.
+ *
+ * The vi structure depends on the key routines being able to return INP_EOF
+ * multiple times without failing -- eventually enough things will end due to
+ * INP_EOF that vi will reach the command level for the screen, at which point
+ * the exit flags will be set and vi will exit.
+ */
+enum input     { INP_OK=0, INP_EOF, INP_ERR };
+
+/*
+ * 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.
+ */
+enum confirm   { CONF_NO, CONF_QUIT, CONF_YES };
+
+/*
+ * Ex/vi commands are generally separated by whitespace characters.  We
+ * can't use the standard isspace(3) macro because it returns true for
+ * characters like ^K in the ASCII character set.  The 4.4BSD isblank(3)
+ * macro does exactly what we want, but it's not portable yet.
+ *
+ * XXX
+ * Note side effect, ch is evaluated multiple times.
+ */
+#ifndef isblank
+#define        isblank(ch)     ((ch) == ' ' || (ch) == '\t')
+#endif
+
+/* Various special characters, messages. */
+#define        CURSOR_CH       ' '                     /* Cursor character. */
+#define        END_CH          '$'                     /* End of a range. */
+#define        HEX_CH          'x'                     /* Leading hex number. */
+#define        NOT_DIGIT_CH    'a'                     /* A non-isdigit() character. */
+#define        NO_CH           'n'                     /* No. */
+#define        QUIT_CH         'q'                     /* Quit. */
+#define        YES_CH          'y'                     /* Yes. */
+#define        CONFSTRING      "confirm? [ynq]"
+#define        CONTMSG         "Enter return to continue: "
+#define        CONTMSG_I       "Enter return to continue [q to quit]: "
+
+/* Flags describing how input is handled. */
+#define        TXT_AICHARS     0x000001        /* Leading autoindent chars. */
+#define        TXT_ALTWERASE   0x000002        /* Option: altwerase. */
+#define        TXT_APPENDEOL   0x000004        /* Appending after EOL. */
+#define        TXT_AUTOINDENT  0x000008        /* Autoindent set this line. */
+#define        TXT_BEAUTIFY    0x000010        /* Only printable characters. */
+#define        TXT_BS          0x000020        /* Backspace returns the buffer. */
+#define        TXT_CNTRLT      0x000040        /* Control-T is an indent special. */
+#define        TXT_CR          0x000080        /* CR returns the buffer. */
+#define        TXT_EMARK       0x000100        /* End of replacement mark. */
+#define        TXT_ESCAPE      0x000200        /* Escape returns the buffer. */
+#define        TXT_INFOLINE    0x000400        /* Editing the info line. */
+#define        TXT_MAPCOMMAND  0x000800        /* Apply the command map. */
+#define        TXT_MAPINPUT    0x001000        /* Apply the input map. */
+#define        TXT_MAPNODIGIT  0x002000        /* Return to a digit. */
+#define        TXT_NLECHO      0x004000        /* Echo the newline. */
+#define        TXT_OVERWRITE   0x008000        /* Overwrite characters. */
+#define        TXT_PROMPT      0x010000        /* Display a prompt. */
+#define        TXT_RECORD      0x020000        /* Record for replay. */
+#define        TXT_REPLACE     0x040000        /* Replace; don't delete overwrite. */
+#define        TXT_REPLAY      0x080000        /* Replay the last input. */
+#define        TXT_RESOLVE     0x100000        /* Resolve the text into the file. */
+#define        TXT_SHOWMATCH   0x200000        /* Option: showmatch. */
+#define        TXT_TTYWERASE   0x400000        /* Option: ttywerase. */
+#define        TXT_WRAPMARGIN  0x800000        /* Option: wrapmargin. */
+
+#define        TXT_VALID_EX                                                    \
+       (TXT_BEAUTIFY | TXT_CR | TXT_NLECHO | TXT_PROMPT)
+
+/* Support keyboard routines. */
+int            __term_key_val __P((SCR *, ARG_CHAR_T));
+void           term_ab_flush __P((SCR *, char *));
+int            term_init __P((SCR *));
+enum input     term_key __P((SCR *, CH *, u_int));
+int            term_key_ch __P((SCR *, int, CHAR_T *));
+int            term_key_queue __P((SCR *));
+void           term_map_flush __P((SCR *, char *));
+int            term_push __P((SCR *, CHAR_T *, size_t, u_int, u_int));
+enum input     term_user_key __P((SCR *, CH *));
+int            term_waiting __P((SCR *));
diff --git a/usr.bin/vi/timer.c b/usr.bin/vi/timer.c
new file mode 100644 (file)
index 0000000..e2bcfb1
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)timer.c    8.7 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/time.h>
+
+#include "vi.h"
+
+static void busy_handler __P((int));
+
+/*
+ * XXX
+ * There are two uses of timers in nvi.  The first is to push the recovery
+ * information out to disk at periodic intervals.  The second is to display
+ * a "busy" message if an operation takes too long.  Rather than solve this
+ * in a general fashion, we depend on the fact that only a single screen in
+ * a window is active at a time, and that there are only two parts of the
+ * systems that use timers.
+ *
+ * It would be nice to reimplement this with multiple timers, a la POSIX
+ * 1003.1, but not many systems offer them yet.
+ */
+
+/*
+ * busy_on --
+ *     Display a message if too much time passes.
+ */
+void
+busy_on(sp, seconds, msg)
+       SCR *sp;
+       int seconds;
+       char const *msg;
+{
+       struct itimerval value;
+       struct sigaction act;
+
+       /* No busy messages in batch mode. */
+       if (F_ISSET(sp, S_EXSILENT))
+               return;
+
+       /* Turn off the current timer, saving its current value. */
+       value.it_interval.tv_sec = value.it_value.tv_sec = 0;
+       value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+       if (setitimer(ITIMER_REAL, &value, &sp->time_value))
+               return;
+
+       /*
+        * Decrement the original timer by the number of seconds
+        * we're going to wait.
+        */
+       if (sp->time_value.it_value.tv_sec > seconds)
+               sp->time_value.it_value.tv_sec -= seconds;
+       else
+               sp->time_value.it_value.tv_sec = 1;
+
+       /* Reset the handler, saving its current value. */
+       act.sa_handler = busy_handler;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       (void)sigaction(SIGALRM, &act, &sp->time_handler);
+
+       /* Reset the timer. */
+       value.it_value.tv_sec = seconds;
+       value.it_interval.tv_sec = 0;
+       value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+       (void)setitimer(ITIMER_REAL, &value, NULL);
+
+       sp->time_msg = msg;
+       F_SET(sp, S_TIMER_SET);
+}
+
+/*
+ * busy_off --
+ *     Reset the timer handlers.
+ */
+void
+busy_off(sp)
+       SCR *sp;
+{
+       struct itimerval ovalue, value;
+
+       /* No busy messages in batch mode. */
+       if (F_ISSET(sp, S_EXSILENT))
+               return;
+
+       /* If the timer flag isn't set, it must have fired. */
+       if (!F_ISSET(sp, S_TIMER_SET))
+               return;
+
+       /* Ignore it if first on one of following system calls. */
+       F_CLR(sp, S_TIMER_SET);
+
+       /* Turn off the current timer. */
+       value.it_interval.tv_sec = value.it_value.tv_sec = 0;
+       value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+       if (setitimer(ITIMER_REAL, &value, &ovalue))
+               return;
+
+       /* If the timer wasn't running, we're done. */
+       if (sp->time_handler.sa_handler == SIG_DFL)
+               return;
+
+       /*
+        * Increment the old timer by the number of seconds
+        * remaining in the new one.
+        */
+       sp->time_value.it_value.tv_sec += ovalue.it_value.tv_sec;
+
+       /* Reset the handler to the original handler. */
+       (void)sigaction(SIGALRM, &sp->time_handler, NULL);
+
+       /* Reset the timer. */
+       (void)setitimer(ITIMER_REAL, &sp->time_value, NULL);
+}
+
+/*
+ * busy_handler --
+ *     Display a message when the timer goes off, and restore the
+ *     timer to its original values.
+ */
+static void
+busy_handler(signo)
+       int signo;
+{
+       SCR *sp;
+
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               if (F_ISSET(sp, S_TIMER_SET)) {
+                       sp->s_busy(sp, sp->time_msg);
+                       busy_off(sp);
+               }
+}
diff --git a/usr.bin/vi/trace.c b/usr.bin/vi/trace.c
new file mode 100644 (file)
index 0000000..f97e47b
--- /dev/null
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)trace.c     8.1 (Berkeley) 6/9/93
+ */
+
+#ifdef DEBUG
+#include <sys/types.h>
+
+#include "vi.h"
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#ifdef __STDC__
+TRACE(SCR *sp, const char *fmt, ...)
+#else
+TRACE(sp, fmt, va_alist)
+       SCR *sp;
+       char *fmt;
+       va_dcl
+#endif
+{
+       FILE *tfp;
+       va_list ap;
+
+       if ((tfp = sp->gp->tracefp) == NULL)
+               return;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)vfprintf(tfp, fmt, ap);
+       va_end(ap);
+
+       (void)fflush(tfp);
+}
+#endif
diff --git a/usr.bin/vi/util.c b/usr.bin/vi/util.c
new file mode 100644 (file)
index 0000000..ab71610
--- /dev/null
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c     8.34 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "vi.h"
+
+/*
+ * msgq --
+ *     Display a message.
+ */
+void
+#ifdef __STDC__
+msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
+#else
+msgq(sp, mt, fmt, va_alist)
+       SCR *sp;
+       enum msgtype mt;
+        char *fmt;
+        va_dcl
+#endif
+{
+        va_list ap;
+       int len;
+       char msgbuf[1024];
+
+#ifdef __STDC__
+        va_start(ap, fmt);
+#else
+        va_start(ap);
+#endif
+       /*
+        * It's possible to enter msg when there's no screen to hold
+        * the message.  Always check sp before using it, and, if it's
+        * NULL, use __global_list.
+        */
+       switch (mt) {
+       case M_BERR:
+               if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) {
+                       F_SET(sp, S_BELLSCHED);
+                       return;
+               }
+               mt = M_ERR;
+               break;
+       case M_VINFO:
+               if (sp != NULL && !O_ISSET(sp, O_VERBOSE))
+                       return;
+               mt = M_INFO;
+               /* FALLTHROUGH */
+       case M_INFO:
+               if (F_ISSET(sp, S_EXSILENT))
+                       return;
+               break;
+       case M_ERR:
+       case M_SYSERR:
+               break;
+       default:
+               abort();
+       }
+
+       /* Length is the min length of the message or the buffer. */
+       if (mt == M_SYSERR)
+               if (sp->if_name != NULL)
+                       len = snprintf(msgbuf, sizeof(msgbuf),
+                           "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno,
+                           fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
+                           strerror(errno));
+               else
+                       len = snprintf(msgbuf, sizeof(msgbuf),
+                           "Error: %s%s%s.",
+                           fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
+                           strerror(errno));
+       else {
+               len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf),
+                       "%s, %d: ", sp->if_name, sp->if_lno);
+               len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
+       }
+
+       /*
+        * If len >= the size, some characters were discarded.
+        * Ignore trailing nul.
+        */
+       if (len >= sizeof(msgbuf))
+               len = sizeof(msgbuf) - 1;
+
+       msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
+}
+
+/*
+ * msg_app --
+ *     Append a message into the queue.  This can fail, but there's
+ *     nothing we can do if it does.
+ */
+void
+msg_app(gp, sp, inv_video, p, len)
+       GS *gp;
+       SCR *sp;
+       int inv_video;
+       char *p;
+       size_t len;
+{
+       static int reenter;             /* STATIC: Re-entrancy check. */
+       MSG *mp, *nmp;
+
+       /*
+        * It's possible to reenter msg when it allocates space.
+        * We're probably dead anyway, but no reason to drop core.
+        */
+       if (reenter)
+               return;
+       reenter = 1;
+
+       /*
+        * Find an empty structure, or allocate a new one.  Use the
+        * screen structure if possible, otherwise the global one.
+        */
+       if (sp != NULL) {
+               if ((mp = sp->msgq.lh_first) == NULL) {
+                       CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+                       if (mp == NULL)
+                               goto ret;
+                       LIST_INSERT_HEAD(&sp->msgq, mp, q);
+                       goto store;
+               }
+       } else if ((mp = gp->msgq.lh_first) == NULL) {
+               CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+               if (mp == NULL)
+                       goto ret;
+               LIST_INSERT_HEAD(&gp->msgq, mp, q);
+               goto store;
+       }
+       while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
+               mp = mp->q.le_next;
+       if (!F_ISSET(mp, M_EMPTY)) {
+               CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
+               if (nmp == NULL)
+                       goto ret;
+               LIST_INSERT_AFTER(mp, nmp, q);
+               mp = nmp;
+       }
+
+       /* Get enough memory for the message. */
+store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
+               goto ret;
+
+       /* Store the message. */
+       memmove(mp->mbuf, p, len);
+       mp->len = len;
+       mp->flags = inv_video ? M_INV_VIDEO : 0;
+
+ret:   reenter = 0;
+}
+
+/*
+ * msgrpt --
+ *     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 isn't true -- edit a large file and do "100d|1".  We don't implement
+ * this semantic as it would require that we track each line that changes
+ * during a command instead of just keeping count.
+ */
+int
+msg_rpt(sp, is_message)
+       SCR *sp;
+       int is_message;
+{
+       static const char *const action[] = {
+               "added", "changed", "copied", "deleted", "joined", "moved",     
+               "put", "left shifted", "right shifted", "yanked", NULL,
+       };
+       recno_t total;
+       u_long rval;
+       int first, cnt;
+       size_t blen, len;
+       const char *const *ap;
+       char *bp, *p, number[40];
+
+       if (F_ISSET(sp, S_EXSILENT))
+               return (0);
+
+       if ((rval = O_VAL(sp, O_REPORT)) == 0)
+               goto norpt;
+
+       GET_SPACE_RET(sp, bp, blen, 512);
+       p = bp;
+
+       total = 0;
+       for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
+               if (sp->rptlines[cnt] != 0) {
+                       total += sp->rptlines[cnt];
+                       len = snprintf(number, sizeof(number),
+                           "%s%lu line%s %s", first ? "" : "; ",
+                           sp->rptlines[cnt],
+                           sp->rptlines[cnt] > 1 ? "s" : "", *ap);
+                       memmove(p, number, len);
+                       p += len;
+                       first = 0;
+               }
+
+       /*
+        * If nothing to report, return.  Note that the number of lines
+        * must be > than the user's value, not >=.  This is historic
+        * practice and means that users cannot report on single line
+        * changes.
+        */
+       if (total > rval) {
+               *p = '\0';
+
+               if (is_message)
+                       msgq(sp, M_INFO, "%s", bp);
+               else
+                       ex_printf(EXCOOKIE, "%s\n", bp);
+       }
+
+       FREE_SPACE(sp, bp, blen);
+
+       /* Clear after each report. */
+norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
+       return (0);
+}
+
+/*
+ * binc --
+ *     Increase the size of a buffer.
+ */
+int
+binc(sp, argp, bsizep, min)
+       SCR *sp;                        /* MAY BE NULL */
+       void *argp;
+       size_t *bsizep, min;
+{
+       void *bpp;
+       size_t csize;
+
+       /* If already larger than the minimum, just return. */
+       csize = *bsizep;
+       if (min && csize >= min)
+               return (0);
+
+       csize += MAX(min, 256);
+       bpp = *(char **)argp;
+
+       /* For non-ANSI C realloc implementations. */
+       if (bpp == NULL)
+               bpp = malloc(csize * sizeof(CHAR_T));
+       else
+               bpp = realloc(bpp, csize * sizeof(CHAR_T));
+       if (bpp == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               *bsizep = 0;
+               return (1);
+       }
+       *(char **)argp = bpp;
+       *bsizep = csize;
+       return (0);
+}
+
+/*
+ * 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.
+ */
+int
+nonblank(sp, ep, lno, cnop)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+       size_t *cnop;
+{
+       char *p;
+       size_t cnt, len, off;
+
+       /* Default. */
+       off = *cnop;
+       *cnop = 0;
+
+       /* Get the line. */
+       if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       return (0);
+               GETLINE_ERR(sp, lno);
+               return (1);
+       }
+
+       /* 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);
+}
+
+/*
+ * tail --
+ *     Return tail of a path.
+ */
+char *
+tail(path)
+       char *path;
+{
+       char *p;
+
+       if ((p = strrchr(path, '/')) == NULL)
+               return (path);
+       return (p + 1);
+}
+
+/*
+ * set_window_size --
+ *     Set the window size, the row may be provided as an argument.
+ */
+int
+set_window_size(sp, set_row, ign_env)
+       SCR *sp;
+       u_int set_row;
+       int ign_env;
+{
+       struct winsize win;
+       size_t col, row;
+       int user_set;
+       ARGS *argv[2], a, b;
+       char *s, buf[2048];
+
+       /*
+        * 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;
+#ifdef TIOCGWINSZ
+       if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
+               row = win.ws_row;
+               col = win.ws_col;
+       }
+#endif
+
+       /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
+       if (row == 0 || col == 0) {
+               s = NULL;
+               if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
+                       s = O_STR(sp, O_TERM);
+               else
+                       s = getenv("TERM");
+               if (s != NULL && tgetent(buf, s) == 1) {
+                       if (row == 0)
+                               row = tgetnum("li");
+                       if (col == 0)
+                               col = tgetnum("co");
+               }
+       }
+       /* If nothing else, well, it's probably a VT100. */
+       if (row == 0)
+               row = 24;
+       if (col == 0)
+               col = 80;
+
+       /*
+        * POSIX 1003.2 requires the environment to override, however,
+        * if we're here because of a signal, we don't want to use the
+        * old values.
+        */
+       if (!ign_env) {
+               if ((s = getenv("LINES")) != NULL)
+                       row = strtol(s, NULL, 10);
+               if ((s = getenv("COLUMNS")) != NULL)
+                       col = strtol(s, NULL, 10);
+       }
+
+       /* But, if we got an argument for the rows, use it. */
+       if (set_row)
+               row = set_row;
+
+       a.bp = buf;
+       b.bp = NULL;
+       b.len = 0;
+       argv[0] = &a;
+       argv[1] = &b;;
+
+       /*
+        * Tell the options code that the screen size has changed.
+        * Since the user didn't do the set, clear the set bits.
+        */
+       user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
+       a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
+       if (opts_set(sp, argv))
+               return (1);
+       if (user_set)
+               F_CLR(&sp->opts[O_LINES], OPT_SET);
+       user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
+       a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
+       if (opts_set(sp, argv))
+               return (1);
+       if (user_set)
+               F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
+       return (0);
+}
+
+/*
+ * set_alt_name --
+ *     Set the alternate file name.
+ *
+ * Swap the alternate file name.  It's a routine because I wanted some place
+ * to hang this comment.  The alternate file name (normally referenced using
+ * the special character '#' during file expansion) is set by many
+ * operations.  In the historic vi, the commands "ex", and "edit" obviously
+ * set the alternate file name because they switched the underlying file.
+ * Less obviously, the "read", "file", "write" and "wq" commands set it as
+ * well.  In this implementation, some new commands have been added to the
+ * list.  Where it gets interesting is that the alternate file name is set
+ * multiple times by some commands.  If an edit attempt fails (for whatever
+ * reason, like the current file is modified but as yet unwritten), it is
+ * set to the file name that the user was unable to edit.  If the edit
+ * succeeds, it is set to the last file name that was edited.  Good fun.
+ *
+ * If the user edits a temporary file, there are time when there isn't an
+ * alternative file name.  A name argument of NULL turns it off.
+ */
+void
+set_alt_name(sp, name)
+       SCR *sp;
+       char *name;
+{
+       if (sp->alt_name != NULL)
+               FREE(sp->alt_name, strlen(sp->alt_name) + 1);
+       if (name == NULL)
+               sp->alt_name = NULL;
+       else if ((sp->alt_name = strdup(name)) == NULL)
+               msgq(sp, M_SYSERR, NULL);
+}
+
+/*
+ * baud_from_bval --
+ *     Return the baud rate using the standard defines.
+ */
+u_long
+baud_from_bval(sp)
+       SCR *sp;
+{
+       speed_t v;
+
+       switch (v = cfgetospeed(&sp->gp->original_termios)) {
+       case B50:
+               return (50);
+       case B75:
+               return (75);
+       case B110:
+               return (110);
+       case B134:
+               return (134);
+       case B150:
+               return (150);
+       case B200:
+               return (200);
+       case B300:
+               return (300);
+       case B600:
+               return (600);
+       case B1200:
+               return (1200);
+       case B1800:
+               return (1800);
+       case B2400:
+               return (2400);
+       case B4800:
+               return (4800);
+       case B0:                                /* Hangup -- ignore. */
+       case B9600:
+               return (9600);
+       case B19200:
+               return (19200);
+       case B38400:
+               return (38400);
+       default:
+               /*
+                * EXTA and EXTB aren't required by POSIX 1003.1, and
+                * are almost certainly the same as some of the above
+                * values, so they can't be part of the case statement.
+                */
+#ifdef EXTA
+               if (v == EXTA)
+                       return (19200);
+#endif
+#ifdef EXTB
+               if (v == EXTB)
+                       return (38400);
+#endif
+               msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
+               return (9600);
+       }
+}
+
+/*
+ * v_strdup --
+ *     Strdup for wide character strings with an associated length.
+ */
+CHAR_T *
+v_strdup(sp, str, len)
+       SCR *sp;
+       CHAR_T *str;
+       size_t len;
+{
+       CHAR_T *copy;
+
+       MALLOC(sp, copy, CHAR_T *, len);
+       if (copy == NULL)
+               return (NULL);
+       memmove(copy, str, len * sizeof(CHAR_T));
+       return (copy);
+}
diff --git a/usr.bin/vi/vi.h b/usr.bin/vi/vi.h
new file mode 100644 (file)
index 0000000..c36dc8b
--- /dev/null
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *     @(#)vi.h        8.33 (Berkeley) 1/9/94
+ */
+
+/* System includes. */
+#include <sys/queue.h>         /* Required by screen.h. */
+#include <sys/time.h>          /* Required by screen.h. */
+
+#include <bitstring.h>         /* Required by screen.h. */
+#include <limits.h>            /* Required by screen.h. */
+#include <signal.h>            /* Required by screen.h. */
+#include <stdio.h>             /* Required by screen.h. */
+#include <termios.h>           /* Required by gs.h. */
+
+/*
+ * Required by screen.h.  This is the first include that can pull
+ * in "compat.h".  Should be after every other system include.
+ */
+#include <regex.h>
+
+/*
+ * Forward structure declarations.  Not pretty, but the include files
+ * are far too interrelated for a clean solution.
+ */
+typedef struct _cb             CB;
+typedef struct _ch             CH;
+typedef struct _chname         CHNAME;
+typedef struct _excmdarg       EXCMDARG;
+typedef struct _exf            EXF;
+typedef struct _fref           FREF;
+typedef struct _gs             GS;
+typedef struct _ibuf           IBUF;
+typedef struct _mark           MARK;
+typedef struct _msg            MSG;
+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 _text           TEXT;
+
+/*
+ * Fundamental character types.
+ *
+ * CHAR_T      An integral type that can hold any character.
+ * ARG_CHAR_T  The type of a CHAR_T when passed as an argument using
+ *             traditional promotion rules.  It should also be able
+ *             to be compared against any CHAR_T for equality without
+ *             problems.
+ * MAX_CHAR_T  The maximum value of any character.
+ *
+ * If no integral type can hold a character, don't even try the port.
+ */
+typedef        u_char          CHAR_T; 
+typedef        u_int           ARG_CHAR_T;
+#define        MAX_CHAR_T      0xff
+
+/* The maximum number of columns any character can take up on a screen. */
+#define        MAX_CHARACTER_COLUMNS   4
+
+/*
+ * Local includes.
+ */
+#include <db.h>                        /* Required by exf.h; includes compat.h. */
+
+#include "search.h"            /* Required by screen.h. */
+#include "args.h"              /* Required by options.h. */
+#include "options.h"           /* Required by screen.h. */
+#include "term.h"              /* Required by screen.h. */
+
+#include "msg.h"               /* Required by gs.h. */
+#include "cut.h"               /* Required by gs.h. */
+#include "gs.h"                        /* Required by screen.h. */
+#include "screen.h"            /* Required by exf.h. */
+#include "mark.h"              /* Required by exf.h. */
+#include "exf.h"               
+#include "log.h"
+#include "mem.h"
+
+#if FWOPEN_NOT_AVAILABLE       /* See PORT/clib/fwopen.c. */
+#define        EXCOOKIE        sp
+int     ex_fflush __P((SCR *));
+int     ex_printf __P((SCR *, const char *, ...));
+FILE   *fwopen __P((SCR *, void *));
+#else
+#define        EXCOOKIE        sp->stdfp
+#define        ex_fflush       fflush
+#define        ex_printf       fprintf
+#endif
+
+/* Macros to set/clear/test flags. */
+#define        F_SET(p, f)     (p)->flags |= (f)
+#define        F_CLR(p, f)     (p)->flags &= ~(f)
+#define        F_ISSET(p, f)   ((p)->flags & (f))
+
+#define        LF_INIT(f)      flags = (f)
+#define        LF_SET(f)       flags |= (f)
+#define        LF_CLR(f)       flags &= ~(f)
+#define        LF_ISSET(f)     (flags & (f))
+
+/*
+ * XXX
+ * MIN/MAX have traditionally been in <sys/param.h>.  Don't
+ * try to get them from there, it's just not worth the effort.
+ */
+#ifndef        MAX
+#define        MAX(_a,_b)      ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef        MIN
+#define        MIN(_a,_b)      ((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Function prototypes that don't seem to belong anywhere else. */
+u_long  baud_from_bval __P((SCR *));
+char   *charname __P((SCR *, ARG_CHAR_T));
+void    busy_off __P((SCR *));
+void    busy_on __P((SCR *, int, char const *));
+int     nonblank __P((SCR *, EXF *, recno_t, size_t *));
+void    set_alt_name __P((SCR *, char *));
+int     set_window_size __P((SCR *, u_int, int));
+int     status __P((SCR *, EXF *, recno_t, int));
+char   *tail __P((char *));
+CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t));
+
+#ifdef DEBUG
+void   TRACE __P((SCR *, const char *, ...));
+#endif
+
+/* Digraphs (not currently real). */
+int    digraph __P((SCR *, int, int));
+int    digraph_init __P((SCR *));
+void   digraph_save __P((SCR *, int));
diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c
new file mode 100644 (file)
index 0000000..ec76a06
--- /dev/null
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1993 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)xaw_screen.c       8.4 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include "vi.h"
+
+/*
+ * xaw_init --
+ *     Athena widget screen initialization.
+ */
+int
+xaw_screen_init(sp)
+       SCR *sp;
+{
+       msgq(sp, M_ERR, "The Athena widget screen not yet implemented.");
+       return (1);
+}
+
+/*
+ * xaw_screen_copy --
+ *     Copy to a new screen.
+ */
+int
+xaw_screen_copy(orig, sp)
+       SCR *orig, *sp;
+{
+       return (0);
+}
+
+/*
+ * xaw_screen_end --
+ *     End a screen.
+ */
+int
+xaw_screen_end(sp)
+       SCR *sp;
+{
+       return (0);
+}
+
+/*
+ * xaw --
+ *     Main vi Athena widget screen loop.
+ */
+int
+xaw(sp, ep, spp)
+       SCR *sp, **spp;
+       EXF *ep;
+{
+       *spp = NULL;
+       return (0);
+}