summaryrefslogtreecommitdiff
path: root/usr.bin/vi/svi
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /usr.bin/vi/svi
initial import of NetBSD tree
Diffstat (limited to 'usr.bin/vi/svi')
-rw-r--r--usr.bin/vi/svi/svi_confirm.c95
-rw-r--r--usr.bin/vi/svi/svi_curses.c252
-rw-r--r--usr.bin/vi/svi/svi_ex.c650
-rw-r--r--usr.bin/vi/svi/svi_get.c161
-rw-r--r--usr.bin/vi/svi/svi_line.c441
-rw-r--r--usr.bin/vi/svi/svi_refresh.c818
-rw-r--r--usr.bin/vi/svi/svi_relative.c334
-rw-r--r--usr.bin/vi/svi/svi_screen.c336
-rw-r--r--usr.bin/vi/svi/svi_screen.h262
-rw-r--r--usr.bin/vi/svi/svi_smap.c1216
-rw-r--r--usr.bin/vi/svi/svi_split.c635
-rw-r--r--usr.bin/vi/svi/svi_term.c310
-rw-r--r--usr.bin/vi/svi/svi_util.c347
13 files changed, 5857 insertions, 0 deletions
diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c
new file mode 100644
index 00000000000..e3f703ed1a7
--- /dev/null
+++ b/usr.bin/vi/svi/svi_confirm.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.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(STR_CONFIRM, sizeof(STR_CONFIRM) - 1);
+ MOVEA(sp, oldy, oldx);
+ refresh();
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (CONF_QUIT);
+ switch (ikey.ch) {
+ case CH_YES:
+ return (CONF_YES);
+ case CH_QUIT:
+ return (CONF_QUIT);
+ default:
+ case CH_NO:
+ return (CONF_NO);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/svi/svi_curses.c b/usr.bin/vi/svi/svi_curses.c
new file mode 100644
index 00000000000..6fec89550fb
--- /dev/null
+++ b/usr.bin/vi/svi/svi_curses.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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_curses.c 8.5 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_curses_init --
+ * Initialize curses.
+ */
+int
+svi_curses_init(sp)
+ SCR *sp;
+{
+ struct termios t;
+ char *p;
+
+#ifdef SYSV_CURSES
+ /*
+ * The SunOS/System V initscr() isn't reentrant. Don't even think
+ * about trying to use it. It fails in subtle ways (e.g. select(2)
+ * on fileno(stdin) stops working). We don't care about the SCREEN
+ * reference returned by newterm, we never have more than one SCREEN
+ * at a time.
+ */
+ errno = 0;
+ if (newterm(O_STR(sp, O_TERM), stdout, stdin) == NULL) {
+ msgq(sp, errno ? M_SYSERR : M_ERR, "newterm failed");
+ return (1);
+ }
+#else
+ /*
+ * Initscr() doesn't provide useful error values or messages. The
+ * reasonable guess is that either malloc failed or the terminal was
+ * unknown or lacking some essential feature. Try and guess so the
+ * user isn't even more pissed off because of the error message.
+ */
+ errno = 0;
+ if (initscr() == NULL) {
+ char kbuf[2048];
+ msgq(sp, errno ? M_SYSERR : M_ERR, "initscr failed");
+ if ((p = getenv("TERM")) == NULL || !strcmp(p, "unknown"))
+ msgq(sp, M_ERR,
+ "No TERM environment variable set, or TERM set to \"unknown\"");
+ else if (tgetent(kbuf, p) != 1)
+ msgq(sp, M_ERR,
+"%s: unknown terminal type, or terminal lacks necessary features", p);
+ else
+ msgq(sp, M_ERR,
+ "%s: terminal type lacks necessary features", p);
+ return (1);
+ }
+#endif
+ /*
+ * We use raw mode. What we want is 8-bit clean, however, signals
+ * and flow control should continue to work. Admittedly, it sounds
+ * like cbreak, but it isn't. Using cbreak() can get you additional
+ * things like IEXTEN, which turns on things like DISCARD and LNEXT.
+ *
+ * !!!
+ * If raw isn't turning off echo and newlines, something's wrong.
+ * However, it doesn't hurt.
+ */
+ noecho(); /* No character echo. */
+ nonl(); /* No CR/NL translation. */
+ raw(); /* 8-bit clean. */
+ idlok(stdscr, 1); /* Use hardware insert/delete line. */
+
+ /*
+ * XXX
+ * Historic implementations of curses handled SIGTSTP signals
+ * in one of three ways. They either:
+ *
+ * 1: Set their own handler, regardless.
+ * 2: Did not set a handler if a handler was already installed.
+ * 3: Set their own handler, but then called any previously set
+ * handler after completing their own cleanup.
+ *
+ * We don't try and figure out which behavior is in place, we
+ * just set it to SIG_DFL after initializing the curses interface.
+ */
+ (void)signal(SIGTSTP, SIG_DFL);
+
+ /*
+ * If flow control was on, turn it back on. Turn signals on. ISIG
+ * turns on VINTR, VQUIT, VDSUSP and VSUSP. See signal.c:sig_init()
+ * for a discussion of what's going on here. To sum up, sig_init()
+ * already installed a handler for VINTR. We're going to disable the
+ * other three.
+ *
+ * XXX
+ * We want to use ^Y as a vi scrolling command. If the user has the
+ * DSUSP character set to ^Y (common practice) clean it up. As it's
+ * equally possible that the user has VDSUSP set to 'a', we disable
+ * it regardless. It doesn't make much sense to suspend vi at read,
+ * so I don't think anyone will care. Alternatively, we could look
+ * it up in the table of legal command characters and turn it off if
+ * it matches one. VDSUSP wasn't in POSIX 1003.1-1990, so we test for
+ * it.
+ *
+ * XXX
+ * We don't check to see if the user had signals enabled to start with.
+ * If they didn't, it's unclear what we're supposed to do here, but it
+ * is also pretty unlikely.
+ */
+ if (!tcgetattr(STDIN_FILENO, &t)) {
+ if (sp->gp->original_termios.c_iflag & IXON)
+ t.c_iflag |= IXON;
+ if (sp->gp->original_termios.c_iflag & IXOFF)
+ t.c_iflag |= IXOFF;
+
+ t.c_lflag |= ISIG;
+#ifdef VDSUSP
+ t.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+ t.c_cc[VQUIT] = _POSIX_VDISABLE;
+ t.c_cc[VSUSP] = _POSIX_VDISABLE;
+
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+ }
+
+ /* Put the cursor keys into application mode. */
+ svi_keypad(sp, 1);
+
+ /*
+ * The first screen in the list gets it all. All other screens
+ * are hidden and lose their maps.
+ */
+ svi_dtoh(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. */
+ CALLOC(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ if (HMAP == NULL) {
+ if (endwin() == ERR)
+ msgq(sp, M_SYSERR, "endwin");
+ return (1);
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */
+ F_SET(SVP(sp), SVI_CURSES_INIT); /* It's initialized. */
+
+ return (0);
+}
+
+/*
+ * svi_curses_end --
+ * Move to the bottom of the screen, end curses.
+ */
+int
+svi_curses_end(sp)
+ SCR *sp;
+{
+ /*
+ * XXX
+ * By the time we get here, the screen private area (SVI_PRIVATE)
+ * is probably gone. Don't use it, and don't call any routines
+ * that do.
+ *
+ * Restore the cursor keys to normal mode.
+ */
+ svi_keypad(sp, 0);
+
+ /* Move to the bottom of the screen. */
+ if (move(INFOLINE(sp), 0) == OK) {
+ clrtoeol();
+ refresh();
+ }
+
+ /* End curses window. */
+ if (endwin() == ERR)
+ msgq(sp, M_SYSERR, "endwin");
+
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c
new file mode 100644
index 00000000000..0d20b013c4c
--- /dev/null
+++ b/usr.bin/vi/svi/svi_ex.c
@@ -0,0 +1,650 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.56 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int svi_ex_divider __P((SCR *));
+static int svi_ex_done __P((SCR *, EXF *, MARK *));
+static int svi_ex_inv __P((SCR *));
+static int svi_ex_scroll __P((SCR *, int, CH *));
+
+#define MSGS_WAITING(sp) \
+ ((sp)->msgq.lh_first != NULL && \
+ !F_ISSET((sp)->msgq.lh_first, M_EMPTY))
+
+/*
+ * 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);
+
+ (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 output and there are no
+ * waiting messages, don't wait, but don't overwrite it with mode
+ * information either.
+ */
+ if (svp->extotalcount > 0)
+ if (!F_ISSET(sp, S_REFRESH) &&
+ svp->extotalcount == 1 && !MSGS_WAITING(sp)) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ (void)svi_ex_inv(sp);
+ } else {
+ /* This message isn't interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+ (void)svi_ex_scroll(sp, 1, 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 *, ARG_CHAR_T, u_int));
+ struct termios 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;;) {
+ /*
+ * Get the next command. Interrupt flag manipulation is safe
+ * because ex_icmd clears them all.
+ */
+ F_SET(sp, S_INTERRUPTIBLE);
+ if (get(sp, ep, sp->tiqp, ':', flags) != INP_OK) {
+ rval = 1;
+ break;
+ }
+ if (INTERRUPTED(sp))
+ break;
+
+ /*
+ * Len is 0 if the user backspaced over the prompt,
+ * 1 if only a CR was entered.
+ */
+ tp = sp->tiqp->cqh_first;
+ if (tp->len == 0)
+ break;
+
+ if (!in_exmode)
+ (void)svi_busy(sp, NULL);
+
+ /* Ignore return, presumably an error message was displayed. */
+ (void)ex_icmd(sp, ep, tp->lb, tp->len, 0);
+ (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 (!F_ISSET(sp, S_CONTINUE) && (svp->extotalcount == 0 ||
+ svp->extotalcount == 1 && !MSGS_WAITING(sp))) {
+ if (svp->extotalcount == 1) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ svi_ex_inv(sp);
+ }
+ break;
+ }
+
+ if (INTERRUPTED(sp))
+ break;
+
+ /*
+ * If the screen is trashed, or there are messages waiting,
+ * go into ex mode.
+ */
+ if (!in_exmode &&
+ (F_ISSET(sp, S_REFRESH) || MSGS_WAITING(sp))) {
+ /* Initialize the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY))
+ SEX_RAW(t);
+ get = sex_get;
+ flags = TXT_CR | TXT_NLECHO | TXT_PROMPT;
+ in_exmode = 1;
+ }
+
+ /* Display any waiting messages. */
+ if (MSGS_WAITING(sp))
+ (void)sex_refresh(sp, ep);
+
+ /*
+ * Get a continue character; users may continue in ex mode by
+ * entering a ':'.
+ *
+ * !!!
+ * Historic practice is that any key can be used to continue.
+ * Nvi used to require that the user enter a <carriage-return>
+ * or <newline>, but this broke historic users.
+ */
+ if (in_exmode) {
+ (void)write(STDOUT_FILENO,
+ STR_CMSG, sizeof(STR_CMSG) - 1);
+ if (term_key(sp, &ikey, 0) != INP_OK) {
+ rval = 1;
+ goto ret;
+ }
+ } else {
+ /* This message isn't interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+ (void)svi_ex_scroll(sp, 1, &ikey);
+ }
+ if (ikey.ch != ':')
+ break;
+
+ if (in_exmode)
+ (void)write(STDOUT_FILENO, "\n", 1);
+ else {
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ }
+ }
+
+ret: if (in_exmode) {
+ /* Reset the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY) && 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_msgflush --
+ * Flush any accumulated messages.
+ */
+int
+svi_msgflush(sp)
+ SCR *sp;
+{
+ enum {INVERSE, NORMAL} inverse;
+ SVI_PRIVATE *svp;
+ MSG *mp;
+ int rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ /*
+ * XXX
+ * S_IVIDEO is a bit of a kluge. We can only pass a single magic
+ * cookie into the svi_ex_write routine, and it has to be the SCR
+ * structure. So, the inverse video bit has to be there.
+ */
+ inverse = NORMAL;
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) {
+ /*
+ * If the second and subsequent messages fit on the current
+ * line, write a separator. Otherwise, put out a newline
+ * and break the line.
+ */
+ if (mp != sp->msgq.lh_first)
+ if (mp->len + svp->exlcontinue + 3 >= sp->cols) {
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ".\n", 2);
+ F_CLR(sp, S_IVIDEO);
+ } else {
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ";", 1);
+ F_CLR(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, " ", 2);
+ }
+
+ inverse = F_ISSET(mp, M_INV_VIDEO) ? INVERSE : NORMAL;
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, mp->mbuf, mp->len);
+ F_CLR(sp, S_IVIDEO);
+
+ F_SET(mp, M_EMPTY);
+ }
+
+ /*
+ * None of the messages end with periods, we do it in the message
+ * flush routine, which makes it possible to join messages.
+ */
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ".", 1);
+ F_CLR(sp, S_IVIDEO);
+
+ /*
+ * Figure out if we have to wait. Don't wait for only one line,
+ * but don't overwrite it with mode information either.
+ */
+ if (svp->extotalcount == 1) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ svi_ex_inv(sp);
+ return (0);
+ }
+
+ rval = svi_ex_scroll(sp, 1, NULL);
+ if (svi_ex_done(sp, sp->ep, NULL))
+ rval = 1;
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ 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);
+ }
+
+ /* Ignore the cursor if the caller doesn't care. */
+ if (rp == NULL)
+ return (0);
+
+ /*
+ * 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 it 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.
+ */
+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, tlen;
+ const char *p, *t;
+
+ /*
+ * 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 (INTERRUPTED(sp))
+ 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_ex_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, NULL))
+ return (-1);
+ MOVE(sp, INFOLINE(sp), 0);
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ if (F_ISSET(sp, S_INTERRUPTIBLE) && INTERRUPTED(sp))
+ break;
+ } else
+ MOVE(sp, INFOLINE(sp), svp->exlcontinue);
+
+ /* Display the line, doing character translation. */
+ if (F_ISSET(sp, S_IVIDEO))
+ standout();
+ for (t = line, tlen = len; tlen--; ++t)
+ ADDCH(*t);
+ if (F_ISSET(sp, S_IVIDEO))
+ standend();
+
+ /* 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, chp)
+ SCR *sp;
+ int mustwait;
+ 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(STR_QMSG, (int)sizeof(STR_QMSG) - 1);
+ } else {
+ ADDNSTR(STR_CMSG, (int)sizeof(STR_CMSG) - 1);
+ }
+ clrtoeol();
+ refresh();
+ /*
+ * !!!
+ * Historic practice is that any key can be used to continue.
+ * Nvi used to require that the user enter a <carriage-return>
+ * or <newline>, but this broke historic users.
+ */
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (-1);
+ if (ikey.ch == CH_QUIT && F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+ if (chp != NULL)
+ *chp = ikey;
+ svp->exlinecount = 0;
+ }
+ return (0);
+}
+
+/*
+ * svi_ex_inv --
+ * Change whatever is on the info line to inverse video so we have
+ * a divider line between split screens.
+ */
+static int
+svi_ex_inv(sp)
+ SCR *sp;
+{
+ CHAR_T ch;
+ size_t spcnt, col, row;
+
+ row = INFOLINE(sp);
+
+ /*
+ * Walk through the line, retrieving each character and writing
+ * it back out in inverse video. Since curses doesn't have an
+ * EOL marker, only put out trailing spaces if we find another
+ * character.
+ *
+ * XXX
+ * This is a major kluge -- curses should have an interface
+ * that allows us to change attributes on a per line basis.
+ */
+ MOVE(sp, row, 0);
+ standout();
+ for (spcnt = col = 0;;) {
+ ch = winch(stdscr);
+ if (isspace(ch)) {
+ ++spcnt;
+ if (++col >= sp->cols)
+ break;
+ MOVE(sp, row, col);
+ } else {
+ if (spcnt) {
+ MOVE(sp, row, col - spcnt);
+ for (; spcnt > 0; --spcnt)
+ ADDCH(' ');
+ }
+ ADDCH(ch);
+ if (++col >= sp->cols)
+ break;
+ }
+ }
+ standend();
+ return (0);
+}
+
+/*
+ * svi_ex_divider --
+ * Draw a dividing line between the screens.
+ */
+static int
+svi_ex_divider(sp)
+ SCR *sp;
+{
+ size_t len;
+
+#define DIVIDESTR "+=+=+=+=+=+=+=+"
+ len = sizeof(DIVIDESTR) - 1 > sp->cols ?
+ sp->cols : sizeof(DIVIDESTR) - 1;
+ standout();
+ ADDNSTR(DIVIDESTR, len);
+ standend();
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c
new file mode 100644
index 00000000000..b58100b21e0
--- /dev/null
+++ b/usr.bin/vi/svi/svi_get.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.27 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/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;
+ ARG_CHAR_T prompt;
+ u_int flags;
+{
+ MARK save;
+ SMAP *esmp;
+ recno_t bot_lno;
+ size_t bot_off, cnt;
+ 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;
+ 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 and the line size cache, the line never
+ * really existed. This fixes bugs where the user searches for
+ * the last line on the screen + 1 and the refresh routine thinks
+ * that's where we just were.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ 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
index 00000000000..e1d50da122d
--- /dev/null
+++ b/usr.bin/vi/svi/svi_line.c
@@ -0,0 +1,441 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.27 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.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;
+{
+ 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;
+ int list_tab, list_dollar;
+ 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 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;
+ list_tab = O_ISSET(sp, O_LIST);
+ if (is_infoline = ISINFOLINE(sp, smp)) {
+ list_dollar = 0;
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ skip_screens = 0;
+ else
+ skip_screens = smp->off - 1;
+ } else {
+ list_dollar = list_tab;
+ 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;
+
+ /* Lots of special cases for empty lines. */
+ if (skip_screens == 0)
+ if (p == NULL) {
+ if (smp->lno == 1) {
+ if (list_dollar) {
+ ch = '$';
+ goto empty;
+ }
+ } else {
+ ch = '~';
+ goto empty;
+ }
+ } else
+ if (list_dollar) {
+ ch = '$';
+empty: ADDCH(ch);
+ }
+
+ 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];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } 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];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } 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' && !list_tab ?
+ TAB_OFF(sp, scno) : KEY_LEN(sp, ch);
+ 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' && !list_tab) {
+ scno += chlen = TAB_OFF(sp, scno) - offset_in_char;
+ is_tab = 1;
+ } else {
+ scno += chlen = KEY_LEN(sp, ch) - offset_in_char;
+ is_tab = 0;
+ }
+
+ /*
+ * Only display up to the right-hand column. Set a flag if
+ * the entire character wasn't displayed for use in setting
+ * the cursor. If reached the end of the line, set the cache
+ * info for the screen. Don't worry about there not being
+ * characters to display on the next screen, its lno/off won't
+ * match up in that case.
+ */
+ if (scno >= cols_per_screen) {
+ 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(KEY_NAME(sp, ch) + 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 = KEY_LEN(sp, ch);
+ smp->c_eboff = len - 1;
+
+ /*
+ * If not the info/mode line, and O_LIST set, and at the
+ * end of the line, and the line ended on this screen,
+ * add a trailing $.
+ */
+ if (list_dollar) {
+ ++scno;
+ 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;
+ size_t oldy, oldx;
+ char *lp, 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.
+ */
+ lp = file_gline(sp, ep, TMAP->lno + 1, NULL);
+
+ getyx(stdscr, oldy, oldx);
+ for (smp = HMAP; smp <= TMAP; ++smp) {
+ if (smp->off != 1)
+ continue;
+ if (ISINFOLINE(sp, smp))
+ break;
+ if (smp->lno != 1 && lp == NULL &&
+ file_gline(sp, ep, smp->lno, NULL) == NULL)
+ break;
+ 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
index 00000000000..7b3b89fccc0
--- /dev/null
+++ b/usr.bin/vi/svi/svi_refresh.c
@@ -0,0 +1,818 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.62 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int svi_modeline __P((SCR *, EXF *));
+
+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);
+
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ /*
+ * Fill the map, incidentally losing any svi_line()
+ * cached information.
+ */
+ if (svi_sm_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;
+{
+ SMAP *smp, tmp;
+ SVI_PRIVATE *svp;
+ recno_t lastline, lcnt;
+ size_t cwtotal, cnt, len, x, y;
+ int ch, didpaint, leftright_warp;
+ char *p;
+
+#define LNO sp->lno
+#define OLNO svp->olno
+#define CNO sp->cno
+#define OCNO svp->ocno
+#define SCNO svp->sc_col
+
+ didpaint = leftright_warp = 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)) {
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ /* 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_opt_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_opt_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, or current screen.
+ */
+ if (LNO >= HMAP->lno) {
+ /* Current screen. */
+ if (LNO <= TMAP->lno)
+ goto adjust;
+
+ /*
+ * If less than half a screen above the line, 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;
+ }
+ goto bottom;
+ }
+
+ /*
+ * 3b: Line up.
+ */
+ lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ /*
+ * If less than half a screen below the line, scroll up until
+ * the line is the first line on the screen. Special check so
+ * that if the screen has been emptied, we refill it.
+ */
+ if (file_gline(sp, ep, HMAP->lno, &len) != NULL) {
+ while (lcnt--)
+ if (svi_sm_1down(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.
+ */
+bottom: 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;
+ }
+ /* It's not close, just put the line in the middle. */
+ goto middle;
+ }
+
+ /*
+ * If less than half a screen from the top of the file, put the first
+ * line of the file at the top of the screen. Otherwise, put the line
+ * in the middle of the screen.
+ */
+ tmp.lno = 1;
+ tmp.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_opt_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.
+ * However, in the code above we skipped leftright scrolling on
+ * the grounds that the cursor code would handle it. Make sure
+ * the right screen is up.
+ */
+ if (F_ISSET(sp, S_REDRAW)) {
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO);
+ if (HMAP->off != cnt)
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ }
+ 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. 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.
+ */
+ 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;
+
+ /*
+ * Quick sanity check -- it's hard to figure out exactly when
+ * we cross a screen boundary as we do in the cursor right
+ * movement. If cnt is so large that we're going to cross the
+ * boundary no matter what, stop now.
+ */
+ if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
+ goto 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 += KEY_LEN(sp, ch))
+ 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 (KEY_LEN(sp, ch) > 1)
+ cwtotal -= KEY_LEN(sp, ch) - 1;
+
+ /*
+ * If the new column moved us off of the current logical line,
+ * calculate a new one. If doing leftright scrolling, we've
+ * moved off of the current screen, as well. Since most files
+ * don't have more than two screens, we optimize moving from
+ * screen 2 to screen 1.
+ */
+ if (SCNO < cwtotal) {
+lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = HMAP->off == 2 ? 1 :
+ svi_opt_screens(sp, ep, LNO, &CNO);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ leftright_warp = 1;
+ 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 += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp))
+ break;
+ }
+
+ /*
+ * Increment the screen cursor by the total width of the
+ * characters.
+ */
+ SCNO = cwtotal;
+
+ /* See screen change comment in section 4a. */
+ if (SCNO >= SCREEN_COLS(sp)) {
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ leftright_warp = 1;
+ 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_opt_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;
+ leftright_warp = 1;
+ }
+ 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, and
+ * we're not in the middle of a map:
+ *
+ * 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) && !KEYS_WAITING(sp))
+ 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();
+
+ /*
+ * XXX
+ * Recalculate the "most favorite" cursor position. Vi doesn't know
+ * that we've warped the screen and it's going to have a completely
+ * wrong idea about where the cursor should be. This is vi's problem,
+ * and fixing it here is a gross violation of layering.
+ */
+ if (leftright_warp)
+ (void)svi_column(sp, ep, &sp->rcm);
+
+ return (0);
+}
+
+/*
+ * svi_modeline --
+ * Update the mode line.
+ */
+static int
+svi_modeline(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t cols, curlen, endpoint, len, midpoint;
+ char *p, buf[20];
+
+ /* Clear the mode line. */
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+
+ /*
+ * We put down the file name, the ruler, the mode and the dirty flag.
+ * If there's not enough room, there's not enough room, we don't play
+ * any special games. We try to put the ruler in the middle and the
+ * mode and dirty flag at the end.
+ *
+ * !!!
+ * Leave the last character blank, in case it's a really dumb terminal
+ * with hardware scroll. Second, don't paint the last character in the
+ * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
+ */
+ cols = sp->cols - 1;
+
+ curlen = 0;
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ for (p = sp->frp->name; *p != '\0'; ++p);
+ while (--p > sp->frp->name) {
+ if (*p == '/') {
+ ++p;
+ break;
+ }
+ if ((curlen += KEY_LEN(sp, *(u_char *)p)) > cols) {
+ curlen -= KEY_LEN(sp, *(u_char *)p);
+ ++p;
+ break;
+ }
+ }
+
+ MOVE(sp, INFOLINE(sp), 0);
+ standout();
+ for (; *p != '\0'; ++p)
+ ADDCH(*p);
+ standend();
+ }
+
+ /*
+ * Display the ruler. If we're not at the midpoint yet, move there.
+ * Otherwise, just add in two extra spaces.
+ *
+ * XXX
+ * Assume that numbers, commas, and spaces only take up a single
+ * column on the screen.
+ */
+ if (O_ISSET(sp, O_RULER)) {
+ len = snprintf(buf,
+ sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1);
+ midpoint = (cols - ((len + 1) / 2)) / 2;
+ if (curlen < midpoint) {
+ MOVE(sp, INFOLINE(sp), midpoint);
+ ADDSTR(buf);
+ curlen += len;
+ } else if (curlen + 2 + len < cols) {
+ ADDSTR(" ");
+ ADDSTR(buf);
+ curlen += 2 + len;
+ }
+ }
+
+ /*
+ * Display the mode and the modified flag, as close to the end of the
+ * line as possible, but guaranteeing at least two spaces between the
+ * ruler and the modified flag.
+ *
+ * XXX
+ * Assume that mode name characters, asterisks, and spaces only take
+ * up a single column on the screen.
+ */
+ endpoint = cols;
+ if (O_ISSET(sp, O_SHOWDIRTY) && F_ISSET(ep, F_MODIFIED))
+ --endpoint;
+
+#define MODESIZE 9
+ if (O_ISSET(sp, O_SHOWMODE))
+ endpoint -= MAX_MODE_NAME;
+
+ if (endpoint < curlen + 2)
+ return (0);
+
+ MOVE(sp, INFOLINE(sp), endpoint);
+ if (O_ISSET(sp, O_SHOWDIRTY) && F_ISSET(ep, F_MODIFIED))
+ ADDSTR("*");
+ if (O_ISSET(sp, O_SHOWMODE))
+ ADDSTR(sp->showmode);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c
new file mode 100644
index 00000000000..31575b975f9
--- /dev/null
+++ b/usr.bin/vi/svi/svi_relative.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+static size_t svi_screens
+ __P((SCR *, EXF *, char *, size_t, recno_t, size_t *));
+
+/*
+ * 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_opt_screens --
+ * Return the screen columns necessary to display the line, or
+ * if specified, the physical character column within the line,
+ * including space required for the O_NUMBER and O_LIST options.
+ */
+size_t
+svi_opt_screens(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t cols, screens;
+
+ /*
+ * Check for a cached value. We maintain a cache because, if the
+ * line is large, this routine gets called repeatedly. One other
+ * hack, lots of time the cursor is on column one, which is an easy
+ * one.
+ */
+ if (cnop == NULL) {
+ if (SVP(sp)->ss_lno == lno)
+ return (SVP(sp)->ss_screens);
+ } else if (*cnop == 0)
+ return (1);
+
+ /* Figure out how many columns the line/column needs. */
+ cols = svi_screens(sp, ep, NULL, 0, lno, 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 += KEY_LEN(sp, '$');
+
+ screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+ if (screens == 0)
+ screens = 1;
+
+ /* Cache the value. */
+ if (cnop == NULL) {
+ SVP(sp)->ss_lno = lno;
+ SVP(sp)->ss_screens = screens;
+ }
+ return (screens);
+}
+
+/*
+ * svi_screens --
+ * Return the screen columns necessary to display the line, or,
+ * if specified, the physical character column within the line.
+ */
+static size_t
+svi_screens(sp, ep, lp, llen, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ char *lp;
+ size_t llen;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t chlen, cno, len, scno, tab_off;
+ int ch, listset;
+ char *p;
+
+ /* Need the line to go any further. */
+ if (lp == NULL)
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+#define SET_CHLEN { \
+ chlen = (ch = *(u_char *)p++) == '\t' && \
+ !listset ? TAB_OFF(sp, tab_off) : KEY_LEN(sp, ch); \
+}
+#define TAB_RESET { \
+ /* \
+ * If past the end of the screen, and the character was a tab, \
+ * reset the screen column to 0. Otherwise, display the rest \
+ * of the character on the next line. \
+ */ \
+ if ((tab_off += chlen) >= sp->cols) \
+ if (ch == '\t') { \
+ tab_off = 0; \
+ scno -= scno % sp->cols; \
+ } else \
+ tab_off -= sp->cols; \
+}
+ p = lp;
+ len = llen;
+ scno = tab_off = 0;
+ if (cnop == NULL)
+ while (len--) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ }
+ else
+ for (cno = *cnop; len--; --cno) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ if (cno == 0)
+ break;
+ }
+ return (scno);
+}
+
+/*
+ * svi_rcm --
+ * Return the physical column from the line that will display a
+ * character closest to the currently most attractive character
+ * position (which is stored as a screen column).
+ */
+size_t
+svi_rcm(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ size_t len;
+
+ /* Last character is easy, and common. */
+ if (sp->rcm_last)
+ return (file_gline(sp,
+ ep, lno, &len) == NULL || len == 0 ? 0 : len - 1);
+
+ /* First character is easy, and common. */
+ if (HMAP->off == 1 && sp->rcm == 0)
+ return (0);
+
+ /*
+ * Get svi_cm_private() to do the hard work. If doing leftright
+ * scrolling, we use the current screen offset, otherwise, use
+ * the first screen, i.e. an offset of 1.
+ *
+ * XXX
+ * I'm not sure that an offset of 1 is right. What happens is that
+ * the vi main loop calls us for the VM_RCM case. By using an offset
+ * of 1, we're assuming that every VM_RCM command changes lines, and
+ * that we want to position on the first screen for that line. This
+ * is currently the way it works, but it's not clean. I'd prefer it if
+ * we could find the SMAP entry the cursor references, and use that
+ * screen offset. Unfortunately, that's not going to be easy, as we
+ * don't keep that information around and it may be expensive to get.
+ */
+ return (svi_cm_private(sp, ep, lno,
+ O_ISSET(sp, O_LEFTRIGHT) ? HMAP->off : 1, sp->rcm));
+}
+
+/*
+ * svi_cm_public --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column.
+ *
+ * The extra interface is because it's called by vi, which doesn't
+ * have a handle on the SMAP structure.
+ */
+size_t
+svi_cm_public(sp, ep, lno, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t cno;
+{
+ return (svi_cm_private(sp, ep, lno, HMAP->off, cno));
+}
+
+/*
+ * svi_cm_private --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column, taking into
+ * account the screen offset.
+ *
+ * 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_cm_private(sp, ep, lno, off, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t off, cno;
+{
+ size_t chlen, len, llen, scno, tab_off;
+ int ch, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+ /* Discard screen (logical) lines. */
+ for (scno = 0, p = lp, len = llen; --off;) {
+ for (; len && scno < sp->cols; --len)
+ scno += (ch = *(u_char *)p++) == '\t' &&
+ !listset ? TAB_OFF(sp, scno) : KEY_LEN(sp, ch);
+
+ /*
+ * If reached the end of the physical line, return
+ * the last physical character in the line.
+ */
+ if (len == 0)
+ return (llen - 1);
+
+ /*
+ * If the character was a tab, reset the screen column to 0.
+ * Otherwise, the rest of the character is displayed on the
+ * next line.
+ */
+ if (ch == '\t')
+ scno = 0;
+ else
+ scno -= sp->cols;
+ }
+
+ /* Step through the line until reach the right character or EOL. */
+ for (tab_off = scno; len--;) {
+ SET_CHLEN;
+
+ /*
+ * If we've reached the specific character, there are three
+ * cases.
+ *
+ * 1: scno == cno, i.e. the current character ends at the
+ * screen character we care about.
+ * a: off < llen - 1, i.e. not the last character in
+ * the line, return the offset of the next character.
+ * b: else return the offset of the last character.
+ * 2: scno != cno, i.e. this character overruns the character
+ * we care about, return the offset of this character.
+ */
+ if ((scno += chlen) >= cno) {
+ off = p - lp;
+ return (scno == cno ?
+ (off < llen - 1 ? off : llen - 1) : off - 1);
+ }
+
+ TAB_RESET;
+ }
+
+ /* No such character; return the start of the last character. */
+ return (llen - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c
new file mode 100644
index 00000000000..f7d3f88703f
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.94 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * 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_clear = svi_clear;
+ sp->s_colpos = svi_cm_public;
+ sp->s_column = svi_column;
+ sp->s_confirm = svi_confirm;
+ sp->s_crel = svi_crel;
+ 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_fmap = svi_fmap;
+ sp->s_position = svi_sm_position;
+ sp->s_rabs = svi_rabs;
+ sp->s_rcm = svi_rcm;
+ sp->s_refresh = svi_refresh;
+ sp->s_scroll = svi_sm_scroll;
+ sp->s_split = svi_split;
+ sp->s_suspend = svi_suspend;
+ sp->s_window = sex_window;
+
+ 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. */
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(nsvi);
+
+/* 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_CURSES_INIT));
+ }
+ return (0);
+}
+
+/*
+ * svi_screen_end --
+ * End a screen.
+ */
+int
+svi_screen_end(sp)
+ SCR *sp;
+{
+ SVI_PRIVATE *svp;
+
+ svp = SVP(sp);
+
+ /* Free the screen map. */
+ if (HMAP != NULL)
+ FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP));
+
+ /* Free the visual bell string. */
+ if (svp->VB != NULL)
+ free(svp->VB);
+
+ /* Free private memory. */
+ FREE(svp, sizeof(SVI_PRIVATE));
+ sp->svi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * 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 ecurses, escreen, force, rval;
+
+ escreen = ecurses = rval = 0;
+
+ /* Initialize curses. */
+ if (svi_curses_init(sp)) {
+ escreen = 1;
+ goto err;
+ }
+ ecurses = 1;
+
+ /*
+ * The resize bit is probably set, as a result of the terminal being
+ * set. We clear it as we just finished initializing the screen.
+ * However, we will want to fill in the map from scratch, so provide
+ * a line number just in case, and set the reformat flag.
+ */
+ HMAP->lno = 1;
+ F_CLR(sp, S_RESIZE);
+ F_SET(sp, S_REFORMAT);
+
+ /*
+ * The historic 4BSD curses had an uneasy relationship with termcap.
+ * Termcap used a static buffer to hold the terminal information,
+ * which was was then used by the curses functions. We want to use
+ * it too, for lots of random things, but we've put it off until after
+ * svi_curses_init:initscr() was called. Do it now.
+ */
+ if (svi_term_init(sp))
+ goto err;
+
+ 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)) {
+ (void)rcv_sync(sp, sp->ep,
+ RCV_EMAIL | RCV_ENDSESSION | RCV_PRESERVE);
+ escreen = 1;
+ goto err;
+ }
+
+ 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))/* File end. */
+ break;
+ /*
+ * !!!
+ * NB: sp->frp may now be NULL, if it was a tmp file.
+ */
+ (void)svi_join(sp, &tsp); /* Find a new screen. */
+ if (tsp == NULL)
+ (void)svi_swap(sp, &tsp, NULL);
+ if (tsp == NULL) {
+ escreen = 1;
+ goto ret;
+ }
+ (void)screen_end(sp); /* Screen end. */
+ sp = tsp;
+ break;
+ case 0: /* Exit vi mode. */
+ svi_dtoh(sp, "Exit from vi");
+ goto ret;
+ 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();
+ }
+ }
+
+ if (0) {
+err: rval = 1;
+ }
+
+ret: if (svi_term_end(sp)) /* Terminal end (uses sp). */
+ rval = 1;
+ if (ecurses && svi_curses_end(sp)) /* Curses end (uses sp). */
+ rval = 1;
+ if (escreen && screen_end(sp)) /* Screen end. */
+ rval = 1;
+ return (rval);
+}
+
+/*
+ * svi_crel --
+ * Change the relative size of the current screen.
+ */
+int
+svi_crel(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_dtoh --
+ * Move all but the current screen to the hidden queue.
+ */
+void
+svi_dtoh(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));
+ _HMAP(tsp) = NULL;
+ }
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q);
+ SIGUNBLOCK(sp->gp);
+ }
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+ SIGUNBLOCK(sp->gp);
+ 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
index 00000000000..3b4643dc10d
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.h
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.52 (Berkeley) 7/20/94
+ */
+
+/*
+ * 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_opt_screens() cache information. */
+#define SVI_SCR_CFLUSH(svp) svp->ss_lno = OOBLNO
+ 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_CURSES_INIT 0x001 /* Curses/termcap initialized. */
+#define SVI_CUR_INVALID 0x002 /* Cursor position is unknown. */
+#define SVI_DIVIDER 0x004 /* Screen divider is displayed. */
+#define SVI_INFOLINE 0x008 /* The infoline is being used by v_ntext(). */
+#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */
+ u_int8_t 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.
+ *
+ * !!!
+ * There are problems with how the historical vi handled tabs. For example,
+ * by doing "set ts=3" and building lines that fold, you can get it to step
+ * through tabs as if they were spaces and move inserted characters to new
+ * positions when <esc> is entered. I think that nvi does tabs correctly,
+ * but there may be some historical incompatibilities.
+ */
+#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP))
+
+/* Move in a screen (absolute), and fail if it doesn't work. */
+#ifdef DEBUG
+#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); \
+ } \
+}
+#else
+#define MOVEA(sp, lno, cno) (void)move(lno, cno)
+#endif
+
+/* Move in a window, and fail if it doesn't work. */
+#ifdef DEBUG
+#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); \
+ } \
+}
+#else
+#define MOVE(sp, lno, cno) (void)move((sp)->woff + (lno), cno)
+#endif
+
+/* Add a character. */
+#define ADDCH(ch) { \
+ CHAR_T __ch = ch; \
+ ADDNSTR(KEY_NAME(sp, __ch), KEY_LEN(sp, __ch)); \
+}
+
+/* Add a string len bytes long. */
+#ifdef DEBUG
+#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); \
+ } \
+}
+#else
+#define ADDNSTR(str, len) (void)addnstr(str, len)
+#endif
+
+/* Add a string. */
+#ifdef DEBUG
+#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); \
+ } \
+}
+#else
+#define ADDSTR(str) (void)addstr(str);
+#endif
+
+/* 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_cm_public __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_crel __P((SCR *, long));
+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 *));
+int svi_fmap __P((SCR *, enum seqtype, CHAR_T *, size_t, CHAR_T *, size_t));
+enum input
+ svi_get __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
+int svi_optchange __P((SCR *, int));
+int svi_rabs __P((SCR *, long, enum adjust));
+size_t svi_rcm __P((SCR *, EXF *, recno_t));
+int svi_refresh __P((SCR *, EXF *));
+int svi_screen_copy __P((SCR *, SCR *));
+int svi_screen_edit __P((SCR *, EXF *));
+int svi_screen_end __P((SCR *));
+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_scroll __P((SCR *, EXF *, MARK *, recno_t, enum sctype));
+int svi_split __P((SCR *, ARGS *[], int));
+int svi_suspend __P((SCR *));
+int svi_swap __P((SCR *, SCR **, char *));
+
+/* Private routines. */
+size_t svi_cm_private __P((SCR *, EXF *, recno_t, size_t, size_t));
+int svi_curses_end __P((SCR *));
+int svi_curses_init __P((SCR *));
+void svi_dtoh __P((SCR *, char *));
+int svi_init __P((SCR *));
+int svi_join __P((SCR *, SCR **));
+void svi_keypad __P((SCR *, int));
+int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *));
+int svi_msgflush __P((SCR *));
+int svi_number __P((SCR *, EXF *));
+size_t svi_opt_screens __P((SCR *, EXF *, recno_t, size_t *));
+int svi_paint __P((SCR *, EXF *));
+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 *));
+int svi_term_end __P((SCR *sp));
+int svi_term_init __P((SCR *sp));
+
+/* 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
index 00000000000..a9205b15208
--- /dev/null
+++ b/usr.bin/vi/svi/svi_smap.c
@@ -0,0 +1,1216 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.48 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/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_down __P((SCR *, EXF *,
+ MARK *, recno_t, enum sctype, SMAP *));
+static int svi_sm_erase __P((SCR *));
+static int svi_sm_insert __P((SCR *, EXF *, recno_t));
+static int svi_sm_reset __P((SCR *, EXF *, recno_t));
+static int svi_sm_up __P((SCR *, EXF *,
+ MARK *, recno_t, enum sctype, SMAP *));
+
+/*
+ * 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);
+
+ /* Invalidate the cursor, if it's on this line. */
+ if (sp->lno == lno)
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ 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);
+
+ /* If the map is filled, the screen must be redrawn. */
+ F_SET(sp, S_REDRAW);
+
+ 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);
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ tmp.off = svi_opt_screens(sp, ep, tmp.lno, NULL);
+ if (svi_sm_nlines(sp, ep,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ TMAP->lno = tmp.lno;
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ 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;
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ TMAP->off = svi_opt_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;
+ default:
+ abort();
+ }
+ return (0);
+
+ /*
+ * Try and put *something* on the screen. If this fails,
+ * we have a serious hard error.
+ */
+err: HMAP->lno = 1;
+ HMAP->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);
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ cnt_orig = 1;
+ else
+ for (cnt_orig = 1, t = p + 1;
+ t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+
+ 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);
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ cnt_orig = 1;
+ else
+ cnt_orig = svi_opt_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);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t = p;
+ cnt_orig = cnt_new = 1;
+ } else {
+ for (cnt_orig = 0,
+ t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+ cnt_new = svi_opt_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_scroll
+ * Scroll the SMAP up/down count logical lines. Different
+ * semantics based on the vi command, *sigh*.
+ */
+int
+svi_sm_scroll(sp, ep, rp, count, scmd)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ enum sctype scmd;
+{
+ SMAP *smp;
+
+ /*
+ * Invalidate the cursor. The line is probably going to change,
+ * (although for ^E and ^Y it may not). In any case, the scroll
+ * routines move the cursor to draw things.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* Find the cursor in the screen. */
+ if (svi_sm_cursor(sp, ep, &smp))
+ return (1);
+
+ switch (scmd) {
+ case CNTRL_B:
+ case CNTRL_U:
+ case CNTRL_Y:
+ case Z_CARAT:
+ if (svi_sm_down(sp, ep, rp, count, scmd, smp))
+ return (1);
+ break;
+ case CNTRL_D:
+ case CNTRL_E:
+ case CNTRL_F:
+ case Z_PLUS:
+ if (svi_sm_up(sp, ep, rp, count, scmd, smp))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * !!!
+ * If we're at the start of a line, go for the first non-blank.
+ * This makes it look like the old vi, even though we're moving
+ * around by logical lines, not physical ones.
+ *
+ * XXX
+ * In the presence of a long line, which has more than a screen
+ * width of leading spaces, this code can cause a cursor warp.
+ * Live with it.
+ */
+ if (scmd != CNTRL_E && scmd != CNTRL_Y &&
+ rp->cno == 0 && nonblank(sp, ep, rp->lno, &rp->cno))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * svi_sm_up --
+ * Scroll the SMAP up count logical lines.
+ */
+static int
+svi_sm_up(sp, ep, rp, count, scmd, smp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ enum sctype scmd;
+ recno_t count;
+ SMAP *smp;
+{
+ int cursor_set, echanged, zset;
+ SMAP s1, s2;
+
+ /*
+ * Check to see if movement is possible.
+ *
+ * Get the line after the map. If that line is a new one (and if
+ * O_LEFTRIGHT option is set, this has to be true), and the next
+ * line doesn't exist, and the cursor doesn't move, or the cursor
+ * isn't even on the screen, or the cursor is already at the last
+ * line in the map, it's an error. If that test succeeded because
+ * the cursor wasn't at the end of the map, test to see if the map
+ * is mostly empty.
+ */
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+ if (s1.lno > TMAP->lno && !file_gline(sp, ep, s1.lno, NULL)) {
+ if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ if (svi_sm_next(sp, ep, smp, &s1))
+ return (1);
+ if (s1.lno > smp->lno && !file_gline(sp, ep, s1.lno, NULL)) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 2b.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves to the
+ * first line displayed. Otherwise, erase/compress and repaint,
+ * and move the cursor to the first line in the screen. Note,
+ * the ^F command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = 0;
+ if (ISSMALLSCREEN(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_F) {
+ s1 = TMAP[0];
+ if (svi_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (svi_sm_next(sp, ep, &s1, &s2))
+ return (1);
+ if (s2.lno != s1.lno &&
+ !file_gline(sp, ep, s2.lno, NULL))
+ break;
+ }
+ TMAP[0] = s2;
+ if (svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM))
+ return (1);
+ return (svi_sm_position(sp, ep, rp, 0, P_TOP));
+ }
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+ if (TMAP->lno != s1.lno &&
+ !file_gline(sp, ep, s1.lno, NULL))
+ break;
+ *++TMAP = s1;
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+
+ if (scmd != CNTRL_E && !cursor_set) {
+ cursor_set = 1;
+ rp->lno = TMAP->lno;
+ rp->cno = TMAP->c_sboff;
+ }
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (echanged = zset = 0; count; --count) {
+ /* Decide what would show up on the screen. */
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+
+ /* If the line doesn't exist, we're done. */
+ if (TMAP->lno != s1.lno && !file_gline(sp, ep, s1.lno, NULL))
+ break;
+
+ /* Scroll the screen cursor up one logical line. */
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ switch (scmd) {
+ case CNTRL_E:
+ if (smp > HMAP)
+ --smp;
+ else
+ echanged = 1;
+ break;
+ case Z_PLUS:
+ if (zset) {
+ if (smp > HMAP)
+ --smp;
+ } else {
+ smp = TMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_E:
+ /*
+ * On a ^E that was forced to change lines, try and keep the
+ * cursor as close as possible to the last position, but also
+ * set it up so that the next "real" movement will return the
+ * cursor to the closest position to the last real movement.
+ */
+ if (echanged) {
+ rp->lno = smp->lno;
+ rp->cno =
+ svi_cm_private(sp, ep, smp->lno, smp->off, sp->rcm);
+ }
+ return (0);
+ case CNTRL_F:
+ /*
+ * If there are more lines, the ^F command is always
+ * positioned at the first line of the screen.
+ */
+ if (!count) {
+ smp = HMAP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_D:
+ /*
+ * The ^D and ^F commands move the cursor towards EOF
+ * if there are more lines to move. Check to be sure
+ * the lines actually exist. (They may not if the
+ * file is smaller than the screen.)
+ */
+ for (; count; --count, ++smp)
+ if (smp == TMAP ||
+ !file_gline(sp, ep, smp[1].lno, NULL))
+ break;
+ break;
+ case Z_PLUS:
+ /* The z+ command moves the cursor to the first new line. */
+ 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_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.
+ */
+static int
+svi_sm_down(sp, ep, rp, count, scmd, smp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ SMAP *smp;
+ enum sctype scmd;
+{
+ SMAP s1, s2;
+ int cursor_set, ychanged, zset;
+
+ /* Check to see if movement is possible. */
+ if (HMAP->lno == 1 && HMAP->off == 1 &&
+ (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 2b.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves to the
+ * first line displayed. Otherwise, erase/compress and repaint,
+ * and move the cursor to the first line in the screen. Note,
+ * the ^B command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = scmd == CNTRL_Y;
+ if (ISSMALLSCREEN(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_B) {
+ s1 = HMAP[0];
+ if (svi_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (svi_sm_prev(sp, ep, &s1, &s2))
+ return (1);
+ if (s2.lno == 1 && s2.off == 1)
+ break;
+ }
+ HMAP[0] = s2;
+ if (svi_sm_fill(sp, ep, OOBLNO, P_TOP))
+ return (1);
+ return (svi_sm_position(sp, ep, rp, 0, P_BOTTOM));
+ }
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (HMAP->lno == 1 || HMAP->off == 1)
+ break;
+ ++TMAP;
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (scmd != CNTRL_Y && !cursor_set) {
+ cursor_set = 1;
+ if (svi_sm_position(sp, ep, rp, 0, P_BOTTOM))
+ return (1);
+ }
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (ychanged = zset = 0; 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);
+ switch (scmd) {
+ case CNTRL_Y:
+ if (smp < TMAP)
+ ++smp;
+ else
+ ychanged = 1;
+ break;
+ case Z_CARAT:
+ if (zset) {
+ if (smp < TMAP)
+ ++smp;
+ } else {
+ smp = HMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (scmd != CNTRL_Y && cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_B:
+ /*
+ * If there are more lines, the ^B command is always
+ * positioned at the last line of the screen.
+ */
+ if (!count) {
+ smp = TMAP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_U:
+ /*
+ * The ^B and ^U commands move the cursor towards SOF
+ * if there are more lines to move.
+ */
+ if (count < smp - HMAP)
+ smp -= count;
+ else
+ smp = HMAP;
+ break;
+ case CNTRL_Y:
+ /*
+ * On a ^Y that was forced to change lines, try and keep the
+ * cursor as close as possible to the last position, but also
+ * set it up so that the next "real" movement will return the
+ * cursor to the closest position to the last real movement.
+ */
+ if (ychanged) {
+ rp->lno = smp->lno;
+ rp->cno =
+ svi_cm_private(sp, ep, smp->lno, smp->off, sp->rcm);
+ }
+ return (0);
+ case Z_CARAT:
+ /* The z^ command moves the cursor to the first new line. */
+ 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_erase --
+ * Erase the small screen area for the scrolling functions.
+ */
+static int
+svi_sm_erase(sp)
+ SCR *sp;
+{
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ 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_opt_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_opt_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, smpp)
+ SCR *sp;
+ EXF *ep;
+ SMAP **smpp;
+{
+ SMAP *p;
+
+ /* See if the cursor is not in the map. */
+ if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
+ return (1);
+
+ /* Find the first occurence of the line. */
+ for (p = HMAP; p->lno != sp->lno; ++p);
+
+ /* Fill in the map information until we find the right line. */
+ for (; p <= TMAP; ++p) {
+ /* Short lines are common and easy to detect. */
+ if (p != TMAP && (p + 1)->lno != p->lno) {
+ *smpp = p;
+ return (0);
+ }
+ if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
+ return (1);
+ if (p->c_eboff >= sp->cno) {
+ *smpp = p;
+ return (0);
+ }
+ }
+
+ /* It was past the end of the map after all. */
+ return (1);
+}
+
+/*
+ * 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:
+ /*
+ * !!!
+ * Historically, an invalid count to the H command failed.
+ * We do nothing special here, just making sure that H in
+ * an empty screen works.
+ */
+ if (cnt > TMAP - HMAP)
+ goto sof;
+ smp = HMAP + cnt;
+ if (cnt && file_gline(sp, ep, smp->lno, NULL) == NULL) {
+sof: msgq(sp, M_BERR, "Movement past the end-of-screen");
+ return (1);
+ }
+ break;
+ case P_MIDDLE:
+ /*
+ * !!!
+ * Historically, a count to the M command was ignored.
+ * If the screen isn't filled, find the middle of what's
+ * real and move there.
+ */
+ if (file_gline(sp, ep, TMAP->lno, NULL) == NULL) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
+ if (smp > HMAP)
+ smp -= (smp - HMAP) / 2;
+ } else
+ smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
+ break;
+ case P_BOTTOM:
+ /*
+ * !!!
+ * Historically, an invalid count to the L command failed.
+ * If the screen isn't filled, find the bottom of what's
+ * real and try to offset from there.
+ */
+ if (cnt > TMAP - HMAP)
+ goto eof;
+ smp = TMAP - cnt;
+ if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ for (; smp->lno > last && smp > HMAP; --smp);
+ if (cnt > smp - HMAP) {
+eof: msgq(sp, M_BERR,
+ "Movement past the beginning-of-screen");
+ return (1);
+ }
+ smp -= cnt;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Make sure that the cached information is valid. */
+ if (!SMAP_CACHE(smp) && 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_opt_screens(sp, ep, lno, NULL);
+ } else {
+ lno = from_sp->lno;
+ lcnt = (svi_opt_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
+ for (; ++lno < to_lno && lcnt <= max;)
+ lcnt += svi_opt_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
index 00000000000..dbf151ce0bc
--- /dev/null
+++ b/usr.bin/vi/svi/svi_split.c
@@ -0,0 +1,635 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.49 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_split --
+ * Split the screen.
+ */
+int
+svi_split(sp, argv, argc)
+ SCR *sp;
+ ARGS *argv[];
+ int argc;
+{
+ MSG *mp, *next;
+ SCR *tsp, saved_sp;
+ SVI_PRIVATE saved_svp;
+ SMAP *smp;
+ size_t cnt, half;
+ int issmallscreen, splitup;
+ char **ap;
+
+ /* 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);
+ CALLOC(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);
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = sp->t_maxrows / 2;
+ tsp->defscroll = tsp->t_maxrows / 2;
+
+ /*
+ * If files specified, build the file list, else, link to the
+ * current file.
+ */
+ if (argv == NULL) {
+ if ((tsp->frp = file_add(tsp, sp->frp->name)) == NULL)
+ goto err;
+ } else {
+ /* Create a new argument list. */
+ CALLOC(sp, tsp->argv, char **, argc + 1, sizeof(char *));
+ if (tsp->argv == NULL)
+ goto err;
+ for (ap = tsp->argv, argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ goto err;
+ *ap = NULL;
+
+ /* Switch to the first one. */
+ tsp->cargv = tsp->argv;
+ if ((tsp->frp = file_add(tsp, *tsp->cargv)) == NULL)
+ goto err;
+ }
+
+ /*
+ * 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.*/
+ SIGBLOCK(sp->gp);
+ 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);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* 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)msg_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. */
+ if (tsp->argv != NULL) {
+ for (ap = tsp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(tsp->argv);
+ }
+ 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. */
+ SIGBLOCK(csp->gp);
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+ SIGUNBLOCK(csp->gp);
+
+ /* 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);
+ }
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = sp->t_maxrows / 2;
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), if the underlying file was a tmp
+ * file it may have gone away.
+ */
+ if (csp->frp != NULL) {
+ csp->frp->lno = csp->lno;
+ csp->frp->cno = csp->cno;
+ F_SET(csp->frp, FR_CURSORSET);
+ }
+
+ *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. */
+ SIGBLOCK(csp->gp);
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+ SIGUNBLOCK(csp->gp);
+
+ 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(sp->frp->name, name))
+ break;
+ if (sp == (void *)&csp->gp->hq) {
+ *nsp = NULL;
+ return (0);
+ }
+ *nsp = sp;
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), if the underlying file was a tmp
+ * file it may have gone away.
+ */
+ if (csp->frp != NULL) {
+ 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;
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = 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)
+ CALLOC_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.
+ */
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
+ SIGUNBLOCK(sp->gp);
+
+ F_SET(sp, S_REDRAW);
+ return (0);
+}
+
+/*
+ * svi_rabs --
+ * Change the absolute size of the current screen.
+ */
+int
+svi_rabs(sp, count, adj)
+ SCR *sp;
+ long count;
+ enum adjust adj;
+{
+ SCR *g, *s;
+
+ /*
+ * Figure out which screens will grow, which will shrink, and
+ * make sure it's possible.
+ */
+ if (count == 0)
+ return (0);
+ if (adj == A_SET) {
+ if (sp->t_maxrows == count)
+ return (0);
+ if (sp->t_maxrows > count) {
+ adj = A_DECREASE;
+ count = sp->t_maxrows - count;
+ } else {
+ adj = A_INCREASE;
+ count = count - sp->t_maxrows;
+ }
+ }
+ if (adj == A_DECREASE) {
+ 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",
+ adj == A_DECREASE ? "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; we could optimize the reformatting of the
+ * screen, but this isn't likely to be a common enough operation
+ * to make it worthwhile.
+ */
+ g->rows += count;
+ g->t_rows += count;
+ if (g->t_minrows == g->t_maxrows)
+ g->t_minrows += count;
+ g->t_maxrows += count;
+ _TMAP(g) += count;
+ (void)msg_status(g, g->ep, g->lno, 0);
+ F_SET(g, S_REFORMAT);
+
+ 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) -= count;
+ (void)msg_status(s, s->ep, s->lno, 0);
+ F_SET(s, S_REFORMAT);
+
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_term.c b/usr.bin/vi/svi/svi_term.c
new file mode 100644
index 00000000000..2b2add3c44a
--- /dev/null
+++ b/usr.bin/vi/svi/svi_term.c
@@ -0,0 +1,310 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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_term.c 8.7 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
+ */
+typedef struct _tklist {
+ char *ts; /* Key's termcap string. */
+ char *output; /* Corresponding vi command. */
+ char *name; /* Name. */
+ u_char value; /* Special value (for lookup). */
+} TKLIST;
+static TKLIST const c_tklist[] = { /* Command mappings. */
+#ifdef SYSV_CURSES
+ {"kil1", "O", "insert line"},
+ {"kdch1", "x", "delete character"},
+ {"kcud1", "j", "cursor down"},
+ {"kel", "D", "delete to eol"},
+ {"kind", "\004", "scroll down"},
+ {"kll", "$", "go to eol"},
+ {"khome", "^", "go to sol"},
+ {"kich1", "i", "insert at cursor"},
+ {"kdl1", "dd", "delete line"},
+ {"kcub1", "h", "cursor left"},
+ {"knp", "\006", "page down"},
+ {"kpp", "\002", "page up"},
+ {"kri", "\025", "scroll up"},
+ {"ked", "dG", "delete to end of screen"},
+ {"kcuf1", "l", "cursor right"},
+ {"kcuu1", "k", "cursor up"},
+#else
+ {"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"},
+#endif
+ {NULL},
+};
+static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */
+ {NULL},
+};
+static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */
+#ifdef SYSV_CURSES
+ {"kcud1", "\033ja", "cursor down"},
+ {"kcub1", "\033ha", "cursor left"},
+ {"kcuu1", "\033ka", "cursor up"},
+ {"kcuf1", "\033la", "cursor right"},
+#else
+ {"kd", "\033ja", "cursor down"},
+ {"kl", "\033ha", "cursor left"},
+ {"ku", "\033ka", "cursor up"},
+ {"kr", "\033la", "cursor right"},
+#endif
+ {NULL},
+};
+
+/*
+ * svi_term_init --
+ * Initialize the special keys defined by the termcap/terminfo entry.
+ */
+int
+svi_term_init(sp)
+ SCR *sp;
+{
+ KEYLIST *kp;
+ SEQ *qp;
+ TKLIST const *tkp;
+ size_t len;
+ char *sbp, *s, *t, sbuf[1024];
+
+ /* Command mappings. */
+ for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+ tkp->output, strlen(tkp->output), SEQ_COMMAND, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings needing to be looked up. */
+ for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ for (kp = keylist;; ++kp)
+ if (kp->value == tkp->value)
+ break;
+ if (kp == NULL)
+ continue;
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), &kp->ch, 1, SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings that are already set or are text deletions. */
+ for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ /*
+ * !!!
+ * Some terminals' <cursor_left> keys send single <backspace>
+ * characters. This is okay in command mapping, but not okay
+ * in input mapping. That combination is the only one we'll
+ * ever see, hopefully, so kluge it here for now.
+ */
+ if (!strcmp(t, "\b"))
+ continue;
+ if (tkp->output == NULL) {
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), NULL, 0, SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ } else
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), tkp->output, strlen(tkp->output),
+ SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Rework any function key mappings. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (!F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ (void)svi_fmap(sp, qp->stype,
+ qp->input, qp->ilen, qp->output, qp->olen);
+ }
+
+ /* Set up the visual bell information. */
+ t = sbuf;
+ if (tgetstr("vb", &t) != NULL && (len = t - sbuf) != 0) {
+ MALLOC_RET(sp, s, char *, len);
+ memmove(s, sbuf, len);
+ if (SVP(sp)->VB != NULL)
+ free(SVP(sp)->VB);
+ SVP(sp)->VB = s;
+ return (0);
+ }
+
+ return (0);
+}
+
+/*
+ * svi_term_end --
+ * End the special keys defined by the termcap/terminfo entry.
+ */
+int
+svi_term_end(sp)
+ SCR *sp;
+{
+ SEQ *qp, *nqp;
+
+ /* Delete screen specific mappings. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = nqp) {
+ nqp = qp->q.le_next;
+ if (!F_ISSET(qp, SEQ_SCREEN))
+ continue;
+ (void)seq_mdel(qp);
+ }
+ return (0);
+}
+
+/*
+ * svi_fmap --
+ * Map a function key.
+ */
+int
+svi_fmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ enum seqtype stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ char *t, keyname[64];
+ size_t nlen;
+
+ /* If the terminal isn't initialized, there's nothing to do. */
+ if (!F_ISSET(SVP(sp), SVI_CURSES_INIT))
+ return (0);
+
+#ifdef SYSV_CURSES
+ (void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1));
+ if ((t = tigetstr(keyname)) == NULL || t == (char *)-1)
+ t = NULL;
+#else
+ /*
+ * !!!
+ * Historically, the 4BSD termcap code didn't support functions keys
+ * greater than 9. This was silently enforced -- asking for key k12
+ * returned the value for k1. We try and get around this by using
+ * the tables specified in the terminfo(TI_ENV) man page from the 3rd
+ * Edition SVID. This assumes that the implementors of any System V
+ * compatibility code or an extended termcap used those codes.
+ */
+ { int n; char *sbp, sbuf[1024];
+ static const char codes[] = {
+/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
+/* 11-19 */ '1', '2', '3', '4', '5', '6', '7', '8', '9',
+/* 20-63 */ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ };
+ if ((n = atoi(from + 1)) > 63) {
+ msgq(sp, M_ERR,
+ "Termcap has no code for the %s function key",
+ from);
+ return (1);
+ }
+ (void)snprintf(keyname, sizeof(keyname),
+ "%c%c", n <= 10 ? 'k' : 'F', codes[n]);
+ sbp = sbuf;
+ t = tgetstr(keyname, &sbp);
+ }
+#endif
+ if (t == NULL) {
+ msgq(sp, M_ERR, "This terminal has no %s key", from);
+ return (1);
+ }
+ nlen = snprintf(keyname,
+ sizeof(keyname), "function key %d", atoi(from + 1));
+ return (seq_set(sp, keyname, nlen, t, strlen(t),
+ to, tlen, stype, SEQ_SCREEN | SEQ_USERDEF));
+}
diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c
new file mode 100644
index 00000000000..65422ede1a6
--- /dev/null
+++ b/usr.bin/vi/svi/svi_util.c
@@ -0,0 +1,347 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.55 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * svi_bell --
+ * Ring the bell.
+ */
+void
+svi_bell(sp)
+ SCR *sp;
+{
+#ifdef SYSV_CURSES
+ if (O_ISSET(sp, O_FLASH))
+ flash();
+ else
+ beep();
+#else
+ if (O_ISSET(sp, O_FLASH) && SVP(sp)->VB != NULL) {
+ (void)tputs(SVP(sp)->VB, 1, vi_putchar);
+ (void)fflush(stdout);
+ } else
+ (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */
+#endif
+ 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;
+ }
+
+ /* Reset the screen size. */
+ if (sp->s_window(sp, 0))
+ return (1);
+ F_SET(sp, S_RESIZE);
+ break;
+ case O_WINDOW:
+ if (svi_crel(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;
+{
+ /*
+ * search.c:f_search() is called from ex/ex_tag.c:ex_tagfirst(),
+ * which runs before the screen really exists. Make sure we don't
+ * step on anything.
+ *
+ * If the terminal isn't initialized, there's nothing to do.
+ */
+ if (!F_ISSET(SVP(sp), SVI_CURSES_INIT))
+ return (0);
+
+ MOVE(sp, INFOLINE(sp), 0);
+ if (msg) {
+ ADDSTR(msg);
+ clrtoeol();
+ }
+ refresh();
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ return (0);
+}
+
+/*
+ * svi_keypad --
+ * Put the keypad/cursor arrows into or out of application mode.
+ */
+void
+svi_keypad(sp, on)
+ SCR *sp;
+ int on;
+{
+#ifdef SYSV_CURSES
+ keypad(stdscr, on ? TRUE : FALSE);
+#else
+ char *sbp, *t, sbuf[128];
+
+ sbp = sbuf;
+ if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL)
+ return;
+ (void)tputs(t, 0, vi_putchar);
+ (void)fflush(stdout);
+#endif
+}
+
+/*
+ * 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 an svi screen.
+ *
+ * See signal.c for a long discussion of what's going on here. Let
+ * me put it this way, it's NOT my fault.
+ */
+int
+svi_suspend(sp)
+ SCR *sp;
+{
+ struct termios sv_term;
+ sigset_t set;
+ int oldx, oldy, rval;
+ char *sbp, *t, sbuf[128];
+
+ rval = 0;
+
+ /*
+ * Block SIGALRM, because vi uses timers to decide when to paint
+ * busy messages on the screen.
+ */
+ (void)sigemptyset(&set);
+ (void)sigaddset(&set, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &set, NULL)) {
+ msgq(sp, M_SYSERR, "suspend: sigblock");
+ return (1);
+ }
+
+ /* Save the current cursor position. */
+ getyx(stdscr, oldy, oldx);
+
+ /*
+ * Move the cursor to the bottom of the screen.
+ *
+ * XXX
+ * Some curses implementations don't turn off inverse video when
+ * standend() is called, waiting to see what the next character is
+ * going to be, instead. Write a character to force inverse video
+ * off, and then clear the line.
+ */
+ MOVE(sp, INFOLINE(sp), 0);
+ ADDCH('.');
+ refresh();
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ refresh();
+
+ /* Restore the cursor keys to normal mode. */
+ svi_keypad(sp, 0);
+
+ /* Send VE/TE. */
+#ifdef SYSV_CURSES
+ if ((t = tigetstr("cnorm")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+ if ((t = tigetstr("rmcup")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+#else
+ sbp = sbuf;
+ if ((t = tgetstr("ve", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+ sbp = sbuf;
+ if ((t = tgetstr("te", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+#endif
+ (void)fflush(stdout);
+
+ /* Save current terminal settings, and restore the original ones. */
+ if (tcgetattr(STDIN_FILENO, &sv_term)) {
+ msgq(sp, M_SYSERR, "suspend: tcgetattr");
+ return (1);
+ }
+ if (tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &sp->gp->original_termios)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr original");
+ return (1);
+ }
+
+ /* Push out any waiting messages. */
+ (void)write(STDOUT_FILENO, "\n", 1);
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Stop the process group. */
+ if (kill(0, SIGTSTP)) {
+ msgq(sp, M_SYSERR, "suspend: kill");
+ rval = 1;
+ }
+
+ /* Time passes ... */
+
+ /* Restore current terminal settings. */
+ if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &sv_term)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr current");
+ rval = 1;
+ }
+
+ /* Send TI/VS. */
+#ifdef SYSV_CURSES
+ if ((t = tigetstr("smcup")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+ if ((t = tigetstr("cvvis")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+#else
+ sbp = sbuf;
+ if ((t = tgetstr("ti", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+ sbp = sbuf;
+ if ((t = tgetstr("vs", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+#endif
+ (void)fflush(stdout);
+
+ /* Put the cursor keys into application mode. */
+ svi_keypad(sp, 1);
+
+ /*
+ * If the screen changed size, do a full refresh. Otherwise,
+ * System V has curses repaint it. 4BSD curses will repaint
+ * it in the wrefresh() call below.
+ */
+ if (!sp->s_window(sp, 1))
+ (void)sp->s_refresh(sp, sp->ep);
+#ifdef SYSV_CURSES
+ else
+ redrawwin(stdscr);
+#endif
+
+ /*
+ * Restore the cursor.
+ *
+ * !!!
+ * Don't use MOVE/MOVEA, we don't want to return without resetting
+ * the signals, regardless.
+ */
+ (void)move(oldy, oldx);
+ (void)wrefresh(curscr);
+
+ /* Reset the signals. */
+ if (sigprocmask(SIG_UNBLOCK, &set, NULL)) {
+ msgq(sp, M_SYSERR, "suspend: sigblock");
+ rval = 1;
+ }
+ return (rval);
+}
+
+/*
+ * svi_gdbrefresh --
+ * Stub routine so can flush out screen changes using gdb.
+ */
+#ifdef DEBUG
+int
+svi_gdbrefresh()
+{
+ refresh();
+ return (0);
+}
+#endif