aboutsummaryrefslogtreecommitdiffstats
path: root/usr.bin/vi/vi
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vi/vi')
-rw-r--r--usr.bin/vi/vi/getc.c267
-rw-r--r--usr.bin/vi/vi/v_again.c70
-rw-r--r--usr.bin/vi/vi/v_at.c71
-rw-r--r--usr.bin/vi/vi/v_ch.c321
-rw-r--r--usr.bin/vi/vi/v_delete.c145
-rw-r--r--usr.bin/vi/vi/v_ex.c81
-rw-r--r--usr.bin/vi/vi/v_filter.c101
-rw-r--r--usr.bin/vi/vi/v_increment.c162
-rw-r--r--usr.bin/vi/vi/v_init.c241
-rw-r--r--usr.bin/vi/vi/v_join.c84
-rw-r--r--usr.bin/vi/vi/v_left.c286
-rw-r--r--usr.bin/vi/vi/v_mark.c156
-rw-r--r--usr.bin/vi/vi/v_match.c186
-rw-r--r--usr.bin/vi/vi/v_ntext.c1827
-rw-r--r--usr.bin/vi/vi/v_paragraph.c349
-rw-r--r--usr.bin/vi/vi/v_put.c141
-rw-r--r--usr.bin/vi/vi/v_redraw.c66
-rw-r--r--usr.bin/vi/vi/v_replace.c190
-rw-r--r--usr.bin/vi/vi/v_right.c163
-rw-r--r--usr.bin/vi/vi/v_screen.c89
-rw-r--r--usr.bin/vi/vi/v_scroll.c414
-rw-r--r--usr.bin/vi/vi/v_search.c365
-rw-r--r--usr.bin/vi/vi/v_section.c201
-rw-r--r--usr.bin/vi/vi/v_sentence.c386
-rw-r--r--usr.bin/vi/vi/v_shift.c86
-rw-r--r--usr.bin/vi/vi/v_status.c138
-rw-r--r--usr.bin/vi/vi/v_stop.c74
-rw-r--r--usr.bin/vi/vi/v_switch.c95
-rw-r--r--usr.bin/vi/vi/v_tag.c86
-rw-r--r--usr.bin/vi/vi/v_text.c848
-rw-r--r--usr.bin/vi/vi/v_ulcase.c172
-rw-r--r--usr.bin/vi/vi/v_undo.c143
-rw-r--r--usr.bin/vi/vi/v_util.c159
-rw-r--r--usr.bin/vi/vi/v_word.c569
-rw-r--r--usr.bin/vi/vi/v_xchar.c135
-rw-r--r--usr.bin/vi/vi/v_yank.c92
-rw-r--r--usr.bin/vi/vi/v_z.c143
-rw-r--r--usr.bin/vi/vi/v_zexit.c88
-rw-r--r--usr.bin/vi/vi/vcmd.c530
-rw-r--r--usr.bin/vi/vi/vcmd.h329
-rw-r--r--usr.bin/vi/vi/vi.c801
41 files changed, 10850 insertions, 0 deletions
diff --git a/usr.bin/vi/vi/getc.c b/usr.bin/vi/vi/getc.c
new file mode 100644
index 000000000000..67aee2ff4692
--- /dev/null
+++ b/usr.bin/vi/vi/getc.c
@@ -0,0 +1,267 @@
+/*-
+ * 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[] = "@(#)getc.c 8.8 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * Character stream routines --
+ * These routines return the file a character at a time. There are two
+ * special cases. First, the end of a line, end of a file, start of a
+ * file and empty lines are returned as special cases, and no character
+ * is returned. Second, empty lines include lines that have only white
+ * space in them, because the vi search functions don't care about white
+ * space, and this makes it easier for them to be consistent.
+ */
+
+/*
+ * cs_init --
+ * Initialize character stream routines.
+ */
+int
+cs_init(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t lno;
+
+ if ((csp->cs_bp =
+ file_gline(sp, ep, csp->cs_lno, &csp->cs_len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ msgq(sp, M_BERR, "Empty file.");
+ else
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ return (0);
+}
+
+/*
+ * cs_next --
+ * Retrieve the next character.
+ */
+int
+cs_next(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get next line. */
+ case CS_EOL: /* EOL; get next line. */
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp =
+ file_gline(sp, ep, ++csp->cs_lno, &csp->cs_len)) == NULL) {
+ csp->cs_lno = slno;
+ if (file_lline(sp, ep, &slno))
+ return (1);
+ if (slno > csp->cs_lno) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ csp->cs_flags = CS_EOF;
+ } else if (csp->cs_len == 0 ||
+ v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno = 0];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == csp->cs_len - 1)
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[++csp->cs_cno];
+ break;
+ case CS_EOF: /* EOF. */
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_fspace --
+ * If on a space, eat forward until something other than a
+ * whitespace character.
+ *
+ * XXX
+ * Semantics of checking the current character were coded for the fword()
+ * function -- once the other word routines are converted, they may have
+ * to change.
+ */
+int
+cs_fspace(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ return (0);
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_fblank --
+ * Eat forward to the next non-whitespace character.
+ */
+int
+cs_fblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_prev --
+ * Retrieve the previous character.
+ */
+int
+cs_prev(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get previous line. */
+ case CS_EOL: /* EOL; get previous line. */
+ if (csp->cs_lno == 1) { /* SOF. */
+ csp->cs_flags = CS_SOF;
+ break;
+ }
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp = /* Line should exist. */
+ file_gline(sp, ep, --csp->cs_lno, &csp->cs_len)) == NULL) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ csp->cs_lno = slno;
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_cno = csp->cs_len - 1;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == 0)
+ if (csp->cs_lno == 1)
+ csp->cs_flags = CS_SOF;
+ else
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[--csp->cs_cno];
+ break;
+ case CS_SOF: /* SOF. */
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_bblank --
+ * Eat backward to the next non-whitespace character.
+ */
+int
+cs_bblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_prev(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_again.c b/usr.bin/vi/vi/v_again.c
new file mode 100644
index 000000000000..a96ae8036f04
--- /dev/null
+++ b/usr.bin/vi/vi/v_again.c
@@ -0,0 +1,70 @@
+/*-
+ * 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[] = "@(#)v_again.c 8.4 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_again -- &
+ * Repeat the previous substitution.
+ */
+int
+v_again(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1, "");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_at.c b/usr.bin/vi/vi/v_at.c
new file mode 100644
index 000000000000..c2d446843ca2
--- /dev/null
+++ b/usr.bin/vi/vi/v_at.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1991, 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[] = "@(#)v_at.c 8.5 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_at -- @
+ * Execute a buffer.
+ */
+int
+v_at(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_AT, 0, OOBLNO, OOBLNO, 0, NULL);
+ cmd.buffer = vp->buffer;
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_ch.c b/usr.bin/vi/vi/v_ch.c
new file mode 100644
index 000000000000..69fbf6865c47
--- /dev/null
+++ b/usr.bin/vi/vi/v_ch.c
@@ -0,0 +1,321 @@
+/*-
+ * 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[] = "@(#)v_ch.c 8.7 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static void notfound __P((SCR *, ARG_CHAR_T));
+static void noprev __P((SCR *));
+
+/*
+ * v_chrepeat -- [count];
+ * Repeat the last F, f, T or t search.
+ */
+int
+v_chrepeat(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ vp->character = VIP(sp)->lastckey;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ return (v_chF(sp, ep, vp));
+ case fSEARCH:
+ return (v_chf(sp, ep, vp));
+ case TSEARCH:
+ return (v_chT(sp, ep, vp));
+ case tSEARCH:
+ return (v_cht(sp, ep, vp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * v_chrrepeat -- [count],
+ * Repeat the last F, f, T or t search in the reverse direction.
+ */
+int
+v_chrrepeat(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum cdirection savedir;
+ int rval;
+
+ vp->character = VIP(sp)->lastckey;
+ savedir = VIP(sp)->csearchdir;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ rval = v_chf(sp, ep, vp);
+ break;
+ case fSEARCH:
+ rval = v_chF(sp, ep, vp);
+ break;
+ case TSEARCH:
+ rval = v_cht(sp, ep, vp);
+ break;
+ case tSEARCH:
+ rval = v_chT(sp, ep, vp);
+ break;
+ default:
+ abort();
+ }
+ VIP(sp)->csearchdir = savedir;
+ return (rval);
+}
+
+/*
+ * v_cht -- [count]tc
+ * Search forward in the line for the next occurrence of the character.
+ * Place the cursor on it if it's a motion command, to its left if not.
+ */
+int
+v_cht(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (v_chf(sp, ep, vp))
+ return (1);
+
+ /*
+ * v_chf places the cursor on the character, and the 't' command
+ * wants it to its left. We know this is safe since we had to
+ * have moved right for v_chf() to have succeeded.
+ */
+ --vp->m_stop.cno;
+
+ VIP(sp)->csearchdir = tSEARCH;
+ return (0);
+}
+
+/*
+ * v_chf -- [count]fc
+ * Search forward in the line for the next occurrence of the character.
+ */
+int
+v_chf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t len;
+ recno_t lno;
+ u_long cnt;
+ int key;
+ char *endp, *p, *startp;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which we're
+ * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = fSEARCH;
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+
+ endp = (startp = p) + len;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (++p < endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = p - startp;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_chT -- [count]Tc
+ * Search backward in the line for the next occurrence of the character.
+ * Place the cursor to its right.
+ */
+int
+v_chT(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (v_chF(sp, ep, vp))
+ return (1);
+
+ /*
+ * v_chF places the cursor on the character, and the 'T' command
+ * wants it to its right. We know this is safe since we had to
+ * have moved left for v_chF() to have succeeded.
+ */
+ ++vp->m_stop.cno;
+ ++vp->m_final.cno;
+
+ VIP(sp)->csearchdir = TSEARCH;
+ return (0);
+}
+
+/*
+ * v_chF -- [count]Fc
+ * Search backward in the line for the next occurrence of the character.
+ * Place the cursor on it.
+ */
+int
+v_chF(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+ u_long cnt;
+ int key;
+ char *endp, *p;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which
+ * we're searching, e.g. in "df1|f2|.|;", the ';' searches
+ * for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = FSEARCH;
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+
+ endp = p - 1;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (--p > endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = (p - endp) - 1;
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+static void
+noprev(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "No previous F, f, T or t search.");
+}
+
+static void
+notfound(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ msgq(sp, M_BERR, "%s not found.", charname(sp, ch));
+}
diff --git a/usr.bin/vi/vi/v_delete.c b/usr.bin/vi/vi/v_delete.c
new file mode 100644
index 000000000000..46ce24d65d23
--- /dev/null
+++ b/usr.bin/vi/vi/v_delete.c
@@ -0,0 +1,145 @@
+/*-
+ * 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[] = "@(#)v_delete.c 8.10 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Delete -- [buffer][count]D
+ * Delete line command.
+ */
+int
+v_Delete(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ return (0);
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0)
+ return (0);
+
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = len - 1;
+
+ /* Yank the lines. */
+ if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_DELETE))
+ return (1);
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, 0))
+ return (1);
+
+ vp->m_final.lno = vp->m_start.lno;
+ vp->m_final.cno = vp->m_start.cno ? vp->m_start.cno - 1 : 0;
+ return (0);
+}
+
+/*
+ * v_delete -- [buffer][count]d[count]motion
+ * Delete a range of text.
+ */
+int
+v_delete(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t nlines;
+ size_t len;
+ int lmode;
+
+ /* Yank the lines. */
+ lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
+ if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode | CUT_DELETE))
+ return (1);
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /*
+ * Check for deletion of the entire file. Try to check a close
+ * by line so we don't go to the end of the file unnecessarily.
+ */
+ if (file_gline(sp, ep, vp->m_final.lno + 1, &len) == NULL) {
+ if (file_lline(sp, ep, &nlines))
+ return (1);
+ if (nlines == 0) {
+ vp->m_final.lno = 1;
+ vp->m_final.cno = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * One special correction, in case we've deleted the current line or
+ * character. We check it here instead of checking in every command
+ * that can be a motion component.
+ */
+ if (file_gline(sp, ep, vp->m_final.lno, &len) == NULL) {
+ if (file_gline(sp, ep, nlines, &len) == NULL) {
+ GETLINE_ERR(sp, nlines);
+ return (1);
+ }
+ vp->m_final.lno = nlines;
+ }
+ if (vp->m_final.cno >= len)
+ vp->m_final.cno = len ? len - 1 : 0;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_ex.c b/usr.bin/vi/vi/v_ex.c
new file mode 100644
index 000000000000..49f688844efb
--- /dev/null
+++ b/usr.bin/vi/vi/v_ex.c
@@ -0,0 +1,81 @@
+/*-
+ * 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[] = "@(#)v_ex.c 8.3 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_ex -- :
+ * Execute a colon command line.
+ */
+int
+v_ex(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (sp->s_ex_run(sp, ep, &vp->m_final));
+}
+
+/*
+ * v_exmode -- Q
+ * Switch the editor into EX mode.
+ */
+int
+v_exmode(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ sp->saved_vi_mode = F_ISSET(sp, S_VI_CURSES | S_VI_XAW);
+ F_CLR(sp, S_SCREENS);
+ F_SET(sp, S_EX);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_filter.c b/usr.bin/vi/vi/v_filter.c
new file mode 100644
index 000000000000..39844a8a356e
--- /dev/null
+++ b/usr.bin/vi/vi/v_filter.c
@@ -0,0 +1,101 @@
+/*-
+ * 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[] = "@(#)v_filter.c 8.12 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+/*
+ * v_filter -- [count]!motion command(s)
+ * Run range through shell commands, replacing text.
+ */
+int
+v_filter(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+ TEXT *tp;
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. This is
+ * handled as a special case in the ex_bang routine. Don't
+ * modify this setup without understanding that one. In
+ * particular, note that we're manipulating the ex argument
+ * structures behind ex's back.
+ */
+ SETCMDARG(cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0, NULL);
+ EXP(sp)->argsoff = 0; /* XXX */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ if (argv_exp1(sp, ep, &cmd, "!", 1, 1))
+ return (1);
+ } else {
+ /* Get the command from the user. */
+ if (sp->s_get(sp, ep, &sp->tiq,
+ '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+ /*
+ * Len is 0 if backspaced over the prompt,
+ * 1 if only CR entered.
+ */
+ tp = sp->tiq.cqh_first;
+ if (tp->len <= 1)
+ return (0);
+
+ if (argv_exp1(sp, ep, &cmd, tp->lb + 1, tp->len - 1, 1))
+ return (1);
+ }
+ cmd.argc = EXP(sp)->argsoff; /* XXX */
+ cmd.argv = EXP(sp)->args; /* XXX */
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_increment.c b/usr.bin/vi/vi/v_increment.c
new file mode 100644
index 000000000000..aee358922662
--- /dev/null
+++ b/usr.bin/vi/vi/v_increment.c
@@ -0,0 +1,162 @@
+/*-
+ * 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[] = "@(#)v_increment.c 8.8 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static char * const fmt[] = {
+#define DEC 0
+ "%ld",
+#define SDEC 1
+ "%+ld",
+#define HEXC 2
+ "%#0.*lX",
+#define HEXL 3
+ "%#0.*lx",
+#define OCTAL 4
+ "%#0.*lo",
+};
+
+/*
+ * v_increment -- [count]#[#+-]
+ * Increment/decrement a keyword number.
+ */
+int
+v_increment(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VI_PRIVATE *vip;
+ u_long ulval;
+ long lval;
+ size_t blen, len, nlen;
+ int rval;
+ char *bp, *ntype, *p, nbuf[100];
+
+ vip = VIP(sp);
+
+ /* Do repeat operations. */
+ if (vp->character == '#')
+ vp->character = vip->inc_lastch;
+
+ /* Get new value. */
+ if (F_ISSET(vp, VC_C1SET))
+ vip->inc_lastval = vp->count;
+
+ if (vp->character != '+' && vp->character != '-') {
+ msgq(sp, M_ERR, "usage: %s.", vp->kp->usage);
+ return (1);
+ }
+ vip->inc_lastch = vp->character;
+
+ /* Figure out the resulting type and number. */
+ p = vp->keyword;
+ len = vp->klen;
+ if (len > 1 && p[0] == '0') {
+ if (vp->character == '+') {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ULONG_MAX - ulval < vip->inc_lastval)
+ goto overflow;
+ ulval += vip->inc_lastval;
+ } else {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ulval < vip->inc_lastval)
+ goto underflow;
+ ulval -= vip->inc_lastval;
+ }
+ ntype = fmt[OCTAL];
+ if (len > 2)
+ if (p[1] == 'X')
+ ntype = fmt[HEXC];
+ else if (p[1] == 'x')
+ ntype = fmt[HEXL];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, len, ulval);
+ } else {
+ if (vp->character == '+') {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval > 0 && LONG_MAX - lval < vip->inc_lastval) {
+overflow: msgq(sp, M_ERR, "Resulting number too large.");
+ return (1);
+ }
+ lval += vip->inc_lastval;
+ } else {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval < 0 && -(LONG_MIN - lval) < vip->inc_lastval) {
+underflow: msgq(sp, M_ERR, "Resulting number too small.");
+ return (1);
+ }
+ lval -= vip->inc_lastval;
+ }
+ ntype = lval != 0 &&
+ (*vp->keyword == '+' || *vp->keyword == '-') ?
+ fmt[SDEC] : fmt[DEC];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval);
+ }
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, len + nlen);
+ memmove(bp, p, vp->m_start.cno);
+ memmove(bp + vp->m_start.cno, nbuf, nlen);
+ memmove(bp + vp->m_start.cno + nlen,
+ p + vp->m_start.cno + vp->klen, len - vp->m_start.cno - vp->klen);
+ len = len - vp->klen + nlen;
+
+ rval = file_sline(sp, ep, vp->m_start.lno, bp, len);
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_init.c b/usr.bin/vi/vi/v_init.c
new file mode 100644
index 000000000000..122c6c60e263
--- /dev/null
+++ b/usr.bin/vi/vi/v_init.c
@@ -0,0 +1,241 @@
+/*-
+ * 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[] = "@(#)v_init.c 8.21 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int v_comment __P((SCR *, EXF *));
+
+/*
+ * v_screen_copy --
+ * Copy vi screen.
+ */
+int
+v_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ VI_PRIVATE *ovip, *nvip;
+
+ /* Create the private vi structure. */
+ CALLOC_RET(orig, nvip, VI_PRIVATE *, 1, sizeof(VI_PRIVATE));
+ sp->vi_private = nvip;
+
+ if (orig == NULL) {
+ nvip->inc_lastch = '+';
+ nvip->inc_lastval = 1;
+ nvip->csearchdir = CNOTSET;
+ } else {
+ ovip = VIP(orig);
+
+ /* User can replay the last input, but nothing else. */
+ if (ovip->rep_len != 0) {
+ MALLOC(orig, nvip->rep, char *, ovip->rep_len);
+ if (nvip->rep != NULL) {
+ memmove(nvip->rep, ovip->rep, ovip->rep_len);
+ nvip->rep_len = ovip->rep_len;
+ }
+ }
+
+ nvip->inc_lastch = ovip->inc_lastch;
+ nvip->inc_lastval = ovip->inc_lastval;
+
+ if (ovip->paragraph != NULL &&
+ (nvip->paragraph = strdup(ovip->paragraph)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ nvip->lastckey = ovip->lastckey;
+ nvip->csearchdir = ovip->csearchdir;
+ }
+ return (0);
+}
+
+/*
+ * v_screen_end --
+ * End a vi screen.
+ */
+int
+v_screen_end(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+
+ vip = VIP(sp);
+
+ if (vip->rep != NULL)
+ FREE(vip->rep, vip->rep_len);
+
+ if (vip->paragraph != NULL)
+ FREE(vip->paragraph, vip->paragraph_len);
+
+ /* Free private memory. */
+ FREE(vip, sizeof(VI_PRIVATE));
+ sp->vi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * v_init --
+ * Initialize vi.
+ */
+int
+v_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t len;
+
+ /*
+ * The default address is line 1, column 0. If the address set
+ * bit is on for this file, load the address, ensuring that it
+ * exists.
+ */
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (sp->lno != 1 || sp->cno != 0) {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+ } else if (sp->cno >= len)
+ sp->cno = 0;
+ } else {
+ sp->lno = 1;
+ sp->cno = 0;
+
+ if (O_ISSET(sp, O_COMMENT) && v_comment(sp, ep))
+ return (1);
+ }
+
+ /* Reset strange attraction. */
+ sp->rcm = 0;
+ sp->rcmflags = 0;
+
+ /* Make ex display to a special function. */
+ if ((sp->stdfp = fwopen(sp, sp->s_ex_write)) == NULL) {
+ msgq(sp, M_SYSERR, "ex output");
+ return (1);
+ }
+#ifdef MAKE_EX_OUTPUT_LINE_BUFFERED
+ (void)setvbuf(sp->stdfp, NULL, _IOLBF, 0);
+#endif
+
+ /* Display the status line. */
+ return (status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * v_end --
+ * End vi session.
+ */
+int
+v_end(sp)
+ SCR *sp;
+{
+ /* Close down ex output file descriptor. */
+ (void)fclose(sp->stdfp);
+
+ return (0);
+}
+
+/*
+ * v_optchange --
+ * Handle change of options for vi.
+ */
+int
+v_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_PARAGRAPHS:
+ case O_SECTIONS:
+ return (v_buildparagraph(sp));
+ }
+ return (0);
+}
+
+/*
+ * v_comment --
+ * Skip the first comment.
+ */
+static int
+v_comment(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ recno_t lno;
+ size_t len;
+ char *p;
+
+ for (lno = 1;
+ (p = file_gline(sp, ep, lno, &len)) != NULL && len == 0; ++lno);
+ if (p == NULL || len <= 1 || memcmp(p, "/*", 2))
+ return (0);
+ do {
+ for (; len; --len, ++p)
+ if (p[0] == '*' && len > 1 && p[1] == '/') {
+ sp->lno = lno;
+ return (0);
+ }
+ } while ((p = file_gline(sp, ep, ++lno, &len)) != NULL);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_join.c b/usr.bin/vi/vi/v_join.c
new file mode 100644
index 000000000000..e7eeaa235d85
--- /dev/null
+++ b/usr.bin/vi/vi/v_join.c
@@ -0,0 +1,84 @@
+/*-
+ * 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[] = "@(#)v_join.c 8.5 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_join -- [count]J
+ * Join lines together.
+ */
+int
+v_join(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+ int lno;
+
+ /*
+ * YASC.
+ * The general rule is that '#J' joins # lines, counting the current
+ * line. However, 'J' and '1J' are the same as '2J', i.e. join the
+ * current and next lines. This doesn't map well into the ex command
+ * (which takes two line numbers), so we handle it here. Note that
+ * we never test for EOF -- historically going past the end of file
+ * worked just fine.
+ */
+ lno = vp->m_start.lno + 1;
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
+ lno = vp->m_start.lno + (vp->count - 1);
+
+ SETCMDARG(cmd, C_JOIN, 2, vp->m_start.lno, lno, 0, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_left.c b/usr.bin/vi/vi/v_left.c
new file mode 100644
index 000000000000..8c44657f0d20
--- /dev/null
+++ b/usr.bin/vi/vi/v_left.c
@@ -0,0 +1,286 @@
+/*-
+ * 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[] = "@(#)v_left.c 8.8 (Berkeley) 3/10/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_left -- [count]^H, [count]h
+ * Move left by columns.
+ */
+int
+v_left(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t cnt;
+
+ /*
+ * !!!
+ * The ^H and h commands always failed in the first column.
+ */
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /* Find the end of the range. */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.cno > cnt)
+ vp->m_stop.cno = vp->m_start.cno - cnt;
+ else
+ vp->m_stop.cno = 0;
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+/*
+ * v_cfirst -- [count]_
+ * Move to the first non-blank character in a line.
+ */
+int
+v_cfirst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t cnt;
+
+ /*
+ * !!!
+ * If the _ is a motion component, it makes the command a line motion
+ * e.g. "d_" deletes the line. It also means that the cursor doesn't
+ * move.
+ *
+ * The _ command never failed in the first column.
+ */
+ if (ISMOTION(vp))
+ F_SET(vp, VM_LMODE);
+ /*
+ * !!!
+ * Historically a specified count makes _ move down count - 1
+ * rows, so, "3_" is the same as "2j_".
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt != 1) {
+ --vp->count;
+ return (v_down(sp, ep, vp));
+ }
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case _ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_first -- ^
+ * Move to the first non-blank character in this line.
+ */
+int
+v_first(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * !!!
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case ^ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * !!!
+ * The ^ command succeeded if used as a command without a whitespace
+ * character preceding the cursor in the line, but failed if used as
+ * a motion component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+/*
+ * v_ncol -- [count]|
+ * Move to column count or the first column on this line. If the
+ * requested column is past EOL, move to EOL. The nasty part is
+ * that we have to know character column widths to make this work.
+ */
+int
+v_ncol(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
+ --vp->count;
+ vp->m_stop.cno =
+ sp->s_colpos(sp, ep, vp->m_start.lno, (size_t)vp->count);
+ /*
+ * !!!
+ * The | command succeeded if used as a command and the cursor
+ * didn't move, but failed if used as a motion component in the
+ * same situation.
+ */
+ if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+ } else {
+ /*
+ * !!!
+ * The | command succeeded if used as a command in column 0
+ * without a count, but failed if used as a motion component
+ * in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ }
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * VC_D and VC_Y stay at the start. If moving left, non-motion and
+ * VC_D commands move to the end of the range. VC_Y remains at the
+ * start. Ignore VC_C and VC_S. Motion left commands adjust the
+ * starting point to the character before the current one.
+ */
+ if (vp->m_start.cno < vp->m_stop.cno)
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ else {
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_Y))
+ vp->m_final = vp->m_start;
+ --vp->m_start.cno;
+ }
+ }
+ return (0);
+}
+
+/*
+ * v_zero -- 0
+ * Move to the first column on this line.
+ */
+int
+v_zero(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * !!!
+ * The 0 command succeeded if used as a command in the first column
+ * but failed if used as a motion component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_stop.cno = 0;
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_mark.c b/usr.bin/vi/vi/v_mark.c
new file mode 100644
index 000000000000..ae0585d930ef
--- /dev/null
+++ b/usr.bin/vi/vi/v_mark.c
@@ -0,0 +1,156 @@
+/*-
+ * 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[] = "@(#)v_mark.c 8.6 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_mark -- m[a-z]
+ * Set a mark.
+ */
+int
+v_mark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark_set(sp, ep, vp->character, &vp->m_start, 1));
+}
+
+static int mark __P((SCR *, EXF *, VICMDARG *, enum direction));
+
+/*
+ * v_bmark -- `['`a-z]
+ * Move to a mark.
+ *
+ * Moves to a mark, setting both row and column.
+ *
+ * !!!
+ * Although not commonly known, the "'`" and "'`" forms are historically
+ * valid. The behavior is determined by the first character, so "`'" is
+ * the same as "``". Remember this fact -- you'll be amazed at how many
+ * people don't know it and will be delighted that you are able to tell
+ * them.
+ */
+int
+v_bmark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark(sp, ep, vp, BACKWARD));
+}
+
+/*
+ * v_fmark -- '['`a-z]
+ * Move to a mark.
+ *
+ * Move to the first nonblank character of the line containing the mark.
+ */
+int
+v_fmark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark(sp, ep, vp, FORWARD));
+}
+
+static int
+mark(sp, ep, vp, dir)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum direction dir;
+{
+ if (mark_get(sp, ep, vp->character, &vp->m_stop))
+ return (1);
+
+ /* Forward marks move to the first non-blank. */
+ if (dir == FORWARD) {
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ }
+
+ /* Non-motion commands move to the end of the range. */
+ if (!ISMOTION(vp)) {
+ vp->m_final = vp->m_stop;
+ return (0);
+ }
+
+ /*
+ * !!!
+ * If a motion component, the cursor has to move.
+ */
+ if (vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+
+ /*
+ * If moving right, VC_D and VC_Y stay at the start. If moving left,
+ * VC_D commands move to the end of the range and VC_Y remains at the
+ * start. Ignore VC_C and VC_S. Motion left commands adjust the
+ * starting point to the character before the current one.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno) {
+ if (F_ISSET(vp, VC_D))
+ vp->m_final = vp->m_stop;
+ else
+ vp->m_final = vp->m_start;
+ --vp->m_start.cno;
+ } else
+ vp->m_final = vp->m_start;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_match.c b/usr.bin/vi/vi/v_match.c
new file mode 100644
index 000000000000..0e5642ef9c49
--- /dev/null
+++ b/usr.bin/vi/vi/v_match.c
@@ -0,0 +1,186 @@
+/*-
+ * 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[] = "@(#)v_match.c 8.10 (Berkeley) 3/10/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_match -- %
+ * Search to matching character.
+ */
+int
+v_match(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VCS cs;
+ MARK *mp;
+ recno_t lno;
+ size_t cno, len, off;
+ int cnt, matchc, startc, (*gc)__P((SCR *, EXF *, VCS *));
+ char *p;
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ goto nomatch;
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historical practice was to search for the initial character
+ * in the forward direction only.
+ */
+ for (off = vp->m_start.cno;; ++off) {
+ if (off >= len) {
+nomatch: msgq(sp, M_BERR, "No match character on this line.");
+ return (1);
+ }
+ switch (startc = p[off]) {
+ case '(':
+ matchc = ')';
+ gc = cs_next;
+ break;
+ case ')':
+ matchc = '(';
+ gc = cs_prev;
+ break;
+ case '[':
+ matchc = ']';
+ gc = cs_next;
+ break;
+ case ']':
+ matchc = '[';
+ gc = cs_prev;
+ break;
+ case '{':
+ matchc = '}';
+ gc = cs_next;
+ break;
+ case '}':
+ matchc = '{';
+ gc = cs_prev;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = off;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+ for (cnt = 1;;) {
+ if (gc(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
+ break;
+ continue;
+ }
+ if (cs.cs_ch == startc)
+ ++cnt;
+ else if (cs.cs_ch == matchc && --cnt == 0)
+ break;
+ }
+ if (cnt) {
+ msgq(sp, M_BERR, "Matching character not found.");
+ return (1);
+ }
+
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * VC_D and VC_Y stay at the start. If moving left, non-motion and
+ * VC_D commands move to the end of the range. VC_Y remains at the
+ * start. Ignore VC_C and VC_S.
+ *
+ * !!!
+ * Don't correct for leftward movement -- historic vi deleted the
+ * starting cursor position when deleting to a match.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno < vp->m_stop.cno)
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ else
+ vp->m_final = ISMOTION(vp) &&
+ F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+
+ /*
+ * !!!
+ * If the motion is across lines, and the earliest cursor position
+ * is at or before any non-blank characters in its line, i.e. the
+ * movement is cutting all of the line's text, the buffer is in line
+ * mode.
+ */
+ if (ISMOTION(vp) && vp->m_start.lno != vp->m_stop.lno) {
+ mp = vp->m_start.lno < vp->m_stop.lno ?
+ &vp->m_start : &vp->m_stop;
+ if (mp->cno == 0) {
+ F_SET(vp, VM_LMODE);
+ return (0);
+ }
+ cno = 0;
+ if (nonblank(sp, ep, mp->lno, &cno))
+ return (1);
+ if (cno >= mp->cno)
+ F_SET(vp, VM_LMODE);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_ntext.c b/usr.bin/vi/vi/v_ntext.c
new file mode 100644
index 000000000000..e4060af97bc8
--- /dev/null
+++ b/usr.bin/vi/vi/v_ntext.c
@@ -0,0 +1,1827 @@
+/*-
+ * 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[] = "@(#)v_ntext.c 8.95 (Berkeley) 3/24/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *));
+static void txt_ai_resolve __P((SCR *, TEXT *));
+static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int));
+static void txt_err __P((SCR *, EXF *, TEXTH *));
+static int txt_hex __P((SCR *, TEXT *, int *, CHAR_T *));
+static int txt_indent __P((SCR *, TEXT *));
+static int txt_margin __P((SCR *, TEXT *, int *, CHAR_T *));
+static int txt_outdent __P((SCR *, TEXT *));
+static void txt_showmatch __P((SCR *, EXF *));
+static void txt_Rcleanup __P((SCR *,
+ TEXTH *, TEXT *, const char *, const size_t));
+static int txt_resolve __P((SCR *, EXF *, TEXTH *));
+static void txt_unmap __P((SCR *, TEXT *, u_int *));
+
+/* Cursor character (space is hard to track on the screen). */
+#if defined(DEBUG) && 0
+#undef CURSOR_CH
+#define CURSOR_CH '+'
+#endif
+
+/* Local version of BINC. */
+#define TBINC(sp, lp, llen, nlen) { \
+ if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
+ goto err; \
+}
+
+/*
+ * v_ntext --
+ * Read in text from the user.
+ *
+ * !!!
+ * Historic vi did a special screen optimization for tab characters. For
+ * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of
+ * the string when it was displayed. Because this implementation redisplays
+ * the entire line on each keystroke, the "bcd" gets pushed to the right as
+ * we ignore that the user has "promised" to change the rest of the characters.
+ * Users have noticed, but this isn't worth fixing, and, the way that the
+ * historic vi did it results in an even worse bug. Given the keystrokes
+ * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
+ * on the second <esc> key.
+ */
+int
+v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ MARK *tm; /* To MARK. */
+ const char *lp; /* Input line. */
+ const size_t len; /* Input line length. */
+ MARK *rp; /* Return MARK. */
+ int prompt; /* Prompt to display. */
+ recno_t ai_line; /* Line number to use for autoindent count. */
+ u_int flags; /* TXT_ flags. */
+{
+ /* State of abbreviation checks. */
+ enum { A_NOTSET, A_NOTWORD, A_INWORD } abb;
+ /* State of the "[^0]^D" sequences. */
+ enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st;
+ /* State of the hex input character. */
+ enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex;
+ /* State of quotation. */
+ enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted;
+ CH ikey; /* Input character structure. */
+ CHAR_T ch; /* Input character. */
+ TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */
+ size_t rcol; /* 0-N: insert offset in the replay buffer. */
+ size_t col; /* Current column. */
+ u_long margin; /* Wrapmargin value. */
+ u_int iflags; /* Input flags. */
+ int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */
+ int eval; /* Routine return value. */
+ int replay; /* If replaying a set of input. */
+ int showmatch; /* Showmatch set on this character. */
+ int testnr; /* Test first character for nul replay. */
+ int max, tmp;
+ int unmap_tst; /* Input map needs testing. */
+ char *p;
+
+ /*
+ * Set the input flag, so tabs get displayed correctly
+ * and everyone knows that the text buffer is in use.
+ */
+ F_SET(sp, S_INPUT);
+
+ /* Local initialization. */
+ eval = 0;
+
+ /*
+ * Get one TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.) If changing a line,
+ * copy it into the TEXT buffer.
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->ai = tp->insert = tp->offset = tp->owrite = 0;
+ if (lp != NULL) {
+ tp->len = len;
+ memmove(tp->lb, lp, len);
+ } else
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
+ return (1);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ /* Set the starting line number. */
+ tp->lno = sp->lno;
+
+ /*
+ * Set the insert and overwrite counts. If overwriting characters,
+ * do insertion afterward. If not overwriting characters, assume
+ * doing insertion. If change is to a mark, emphasize it with an
+ * END_CH.
+ */
+ if (len) {
+ if (LF_ISSET(TXT_OVERWRITE)) {
+ tp->owrite = (tm->cno - sp->cno) + 1;
+ tp->insert = (len - tm->cno) - 1;
+ } else
+ tp->insert = len - sp->cno;
+
+ if (LF_ISSET(TXT_EMARK))
+ tp->lb[tm->cno] = END_CH;
+ }
+
+ /*
+ * Many of the special cases in this routine are to handle autoindent
+ * support. Somebody decided that it would be a good idea if "^^D"
+ * and "0^D" deleted all of the autoindented characters. In an editor
+ * that takes single character input from the user, this beggars the
+ * imagination. Note also, "^^D" resets the next lines' autoindent,
+ * but "0^D" doesn't.
+ *
+ * We assume that autoindent only happens on empty lines, so insert
+ * and overwrite will be zero. If doing autoindent, figure out how
+ * much indentation we need and fill it in. Update input column and
+ * screen cursor as necessary.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
+ if (txt_auto(sp, ep, ai_line, NULL, 0, tp))
+ return (1);
+ sp->cno = tp->ai;
+ } else {
+ /*
+ * The cc and S commands have a special feature -- leading
+ * <blank> characters are handled as autoindent characters.
+ * Beauty!
+ */
+ if (LF_ISSET(TXT_AICHARS)) {
+ tp->offset = 0;
+ tp->ai = sp->cno;
+ } else
+ tp->offset = sp->cno;
+ }
+
+ /* If getting a command buffer from the user, there may be a prompt. */
+ if (LF_ISSET(TXT_PROMPT)) {
+ tp->lb[sp->cno++] = prompt;
+ ++tp->len;
+ ++tp->offset;
+ }
+
+ /*
+ * If appending after the end-of-line, add a space into the buffer
+ * and move the cursor right. This space is inserted, i.e. pushed
+ * along, and then deleted when the line is resolved. Assumes that
+ * the cursor is already positioned at the end of the line. This
+ * avoids the nastiness of having the cursor reside on a magical
+ * column, i.e. a column that doesn't really exist. The only down
+ * side is that we may wrap lines or scroll the screen before it's
+ * strictly necessary. Not a big deal.
+ */
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ tp->lb[sp->cno] = CURSOR_CH;
+ ++tp->len;
+ ++tp->insert;
+ }
+
+ /*
+ * Historic practice is that the wrapmargin value was a distance
+ * from the RIGHT-HAND column, not the left. It's more useful to
+ * us as a distance from the left-hand column.
+ *
+ * !!!
+ * Replay commands are not affected by wrapmargin values. What
+ * I found surprising was that people actually depend on it, as
+ * in this gem of a macro which centers lines:
+ *
+ * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p
+ *
+ * XXX
+ * Setting margin causes a significant performance hit. Normally
+ * we don't update the screen if there are keys waiting, but we
+ * have to if margin is set, otherwise the screen routines don't
+ * know where the cursor is.
+ */
+ if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN))
+ margin = 0;
+ else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
+ margin = sp->cols - margin;
+
+ /* Initialize abbreviations checks. */
+ if (F_ISSET(sp->gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) {
+ abb = A_INWORD;
+ ab_cnt = ab_turnoff = 0;
+ } else
+ abb = A_NOTSET;
+
+ /*
+ * Set up the dot command. Dot commands are done by saving the
+ * actual characters and replaying the input. We have to push
+ * the characters onto the key stack and then handle them normally,
+ * otherwise things like wrapmargin will fail.
+ *
+ * XXX
+ * It would be nice if we could swallow backspaces and such, but
+ * it's not all that easy to do. Another possibility would be to
+ * recognize full line insertions, which could be performed quickly,
+ * without replay.
+ */
+nullreplay:
+ rcol = 0;
+ if (replay = LF_ISSET(TXT_REPLAY)) {
+ /*
+ * !!!
+ * Historically, it wasn't an error to replay non-existent
+ * input. This test is necessary, we get here by the user
+ * doing an input command followed by a nul.
+ *
+ * !!!
+ * Historically, vi did not remap or reabbreviate replayed
+ * input. It did, however, beep at you if you changed an
+ * abbreviation and then replayed the input. We're not that
+ * compatible.
+ */
+ if (VIP(sp)->rep == NULL)
+ return (0);
+ if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP))
+ return (1);
+ testnr = 0;
+ abb = A_NOTSET;
+ LF_CLR(TXT_RECORD);
+ } else
+ testnr = 1;
+
+ unmap_tst = LF_ISSET(TXT_MAPINPUT) && LF_ISSET(TXT_INFOLINE);
+ iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+ for (showmatch = 0,
+ carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) {
+ /*
+ * Reset the line and update the screen. (The txt_showmatch()
+ * code refreshes the screen for us.) Don't refresh unless
+ * we're about to wait on a character or we need to know where
+ * the cursor really is.
+ */
+ if (showmatch || margin || !KEYS_WAITING(sp)) {
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+ if (showmatch) {
+ showmatch = 0;
+ txt_showmatch(sp, ep);
+ } else if (sp->s_refresh(sp, ep))
+ goto err;
+ }
+
+ /* Get the next character. */
+next_ch: if (term_key(sp, &ikey, quoted == Q_THISCHAR ?
+ iflags & ~(TXT_MAPCOMMAND | TXT_MAPINPUT) :
+ iflags) != INP_OK)
+ goto err;
+ ch = ikey.ch;
+
+ /* Abbreviation check. See comment in txt_abbrev(). */
+#define MAX_ABBREVIATION_EXPANSION 256
+ if (ikey.flags & CH_ABBREVIATED) {
+ if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) {
+ term_ab_flush(sp,
+ "Abbreviation exceeded maximum number of characters");
+ ab_cnt = 0;
+ continue;
+ }
+ } else
+ ab_cnt = 0;
+
+ /*
+ * !!!
+ * Historic feature. If the first character of the input is
+ * a nul, replay the previous input. This isn't documented
+ * anywhere, and is a great test of vi clones.
+ */
+ if (ch == '\0' && testnr) {
+ LF_SET(TXT_REPLAY);
+ goto nullreplay;
+ }
+ testnr = 0;
+
+ /*
+ * Check to see if the character fits into the input (and
+ * replay, if necessary) buffers. It isn't necessary to
+ * have tp->len bytes, since it doesn't consider overwrite
+ * characters, but not worth fixing.
+ */
+ if (LF_ISSET(TXT_RECORD)) {
+ TBINC(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1);
+ VIP(sp)->rep[rcol++] = ch;
+ }
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+
+ /*
+ * If the character was quoted, replace the last character
+ * (the literal mark) with the new character. If quoted
+ * by someone else, simply insert the character.
+ *
+ * !!!
+ * Extension -- if the quoted character is HEX_CH, enter hex
+ * mode. If the user enters "<HEX_CH>[isxdigit()]*" we will
+ * try to use the value as a character. Anything else resets
+ * hex mode.
+ */
+ if (ikey.flags & CH_QUOTED)
+ goto insq_ch;
+ if (quoted == Q_THISCHAR) {
+ --sp->cno;
+ ++tp->owrite;
+ quoted = Q_NOTSET;
+
+ if (ch == HEX_CH)
+ hex = H_NEXTCHAR;
+ goto insq_ch;
+ }
+
+ switch (ikey.value) {
+ case K_CR:
+ case K_NL: /* New line. */
+#define LINE_RESOLVE { \
+ /* \
+ * Handle abbreviations. If there was one, \
+ * discard the replay characters. \
+ */ \
+ if (abb == A_INWORD && !replay) { \
+ if (txt_abbrev(sp, tp, &ch, \
+ LF_ISSET(TXT_INFOLINE), &tmp, \
+ &ab_turnoff)) \
+ goto err; \
+ if (tmp) { \
+ if (LF_ISSET(TXT_RECORD)) \
+ rcol -= tmp; \
+ goto next_ch; \
+ } \
+ } \
+ if (abb != A_NOTSET) \
+ abb = A_NOTWORD; \
+ if (unmap_tst) \
+ txt_unmap(sp, tp, &iflags); \
+ /* Handle hex numbers. */ \
+ if (hex == H_INHEX) { \
+ if (txt_hex(sp, tp, &tmp, &ch)) \
+ goto err; \
+ if (tmp) { \
+ hex = H_NOTSET; \
+ goto next_ch; \
+ } \
+ } \
+ /* Clean up for the 'R' command. */ \
+ if (LF_ISSET(TXT_REPLACE)) \
+ txt_Rcleanup(sp, tiqh, tp, lp, len); \
+ /* Delete any appended cursor. */ \
+ if (LF_ISSET(TXT_APPENDEOL)) { \
+ --tp->len; \
+ --tp->insert; \
+ } \
+}
+ LINE_RESOLVE;
+
+ /* CR returns from the vi command line. */
+ if (LF_ISSET(TXT_CR)) {
+ /*
+ * If a script window and not the colon
+ * line, push a <cr> so it gets executed.
+ */
+ if (F_ISSET(sp, S_SCRIPT) &&
+ !LF_ISSET(TXT_INFOLINE))
+ (void)term_push(sp,
+ "\r", 1, 0, CH_NOMAP);
+ goto k_escape;
+ }
+
+ /*
+ * Historic practice was to delete any <blank>
+ * characters following the inserted newline.
+ * This affects the 'R', 'c', and 's' commands.
+ */
+ for (p = tp->lb + sp->cno + tp->owrite;
+ tp->insert && isblank(*p);
+ ++p, ++tp->owrite, --tp->insert);
+
+ /*
+ * Move any remaining insert characters into
+ * a new TEXT structure.
+ */
+ if ((ntp = text_init(sp,
+ tp->lb + sp->cno + tp->owrite,
+ tp->insert, tp->insert + 32)) == NULL)
+ goto err;
+
+ /* Set bookkeeping for the new line. */
+ ntp->lno = tp->lno + 1;
+ ntp->insert = tp->insert;
+
+ /*
+ * Note if the user inserted any characters on this
+ * line. Done before calling txt_ai_resolve() because
+ * it changes the value of sp->cno without making the
+ * corresponding changes to tp->ai.
+ */
+ tmp = sp->cno <= tp->ai;
+
+ /*
+ * Resolve autoindented characters for the old line.
+ * Reset the autoindent line value. 0^D keeps the ai
+ * line from changing, ^D changes the level, even if
+ * there are no characters in the old line. Note,
+ * if using the current tp structure, use the cursor
+ * as the length, the user may have erased autoindent
+ * characters.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ txt_ai_resolve(sp, tp);
+
+ if (carat_st == C_NOCHANGE) {
+ if (txt_auto(sp, ep,
+ OOBLNO, &ait, ait.ai, ntp))
+ goto err;
+ FREE_SPACE(sp, ait.lb, ait.lb_len);
+ } else
+ if (txt_auto(sp, ep,
+ OOBLNO, tp, sp->cno, ntp))
+ goto err;
+ carat_st = C_NOTSET;
+ }
+
+ /*
+ * If the user hasn't entered any characters, delete
+ * any autoindent characters.
+ *
+ * !!!
+ * Historic vi didn't get the insert test right, if
+ * there were characters after the cursor, entering
+ * a <cr> left the autoindent characters on the line.
+ */
+ if (tmp)
+ sp->cno = 0;
+
+ /* Reset bookkeeping for the old line. */
+ tp->len = sp->cno;
+ tp->ai = tp->insert = tp->owrite = 0;
+
+ /* New cursor position. */
+ sp->cno = ntp->ai;
+
+ /* New lines are TXT_APPENDEOL if nothing to insert. */
+ if (ntp->insert == 0) {
+ TBINC(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ ntp->lb[sp->cno] = CURSOR_CH;
+ ++ntp->insert;
+ ++ntp->len;
+ }
+
+ /* Update the old line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+
+ /*
+ * Swap old and new TEXT's, and insert the new TEXT
+ * into the queue. (DON'T insert until the old line
+ * has been updated, or the inserted line count in
+ * line.c:file_gline() will be wrong.)
+ */
+ tp = ntp;
+ CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
+
+ /* Reset the cursor. */
+ sp->lno = tp->lno;
+
+ /* Update the new line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_INSERT))
+ goto err;
+
+ /* Set the renumber bit. */
+ F_SET(sp, S_RENUMBER);
+
+ /* Refresh if nothing waiting. */
+ if ((margin || !KEYS_WAITING(sp)) &&
+ sp->s_refresh(sp, ep))
+ goto err;
+ goto next_ch;
+ case K_ESCAPE: /* Escape. */
+ if (!LF_ISSET(TXT_ESCAPE))
+ goto ins_ch;
+
+ LINE_RESOLVE;
+
+ /*
+ * If there aren't any trailing characters in the line
+ * and the user hasn't entered any characters, delete
+ * the autoindent characters.
+ */
+ if (!tp->insert && sp->cno <= tp->ai) {
+ tp->len = tp->owrite = 0;
+ sp->cno = 0;
+ } else if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp);
+
+ /* If there are insert characters, copy them down. */
+k_escape: if (tp->insert && tp->owrite)
+ memmove(tp->lb + sp->cno,
+ tp->lb + sp->cno + tp->owrite, tp->insert);
+ tp->len -= tp->owrite;
+
+ /*
+ * Delete any lines that were inserted into the text
+ * structure and then erased.
+ */
+ while (tp->q.cqe_next != (void *)tiqh) {
+ ntp = tp->q.cqe_next;
+ CIRCLEQ_REMOVE(tiqh, ntp, q);
+ text_free(ntp);
+ }
+
+ /*
+ * If not resolving the lines into the file, end
+ * it with a nul.
+ *
+ * XXX
+ * This is wrong, should pass back a length.
+ */
+ if (LF_ISSET(TXT_RESOLVE)) {
+ if (txt_resolve(sp, ep, tiqh))
+ goto err;
+ /*
+ * Clear input flag -- input buffer no longer
+ * valid.
+ */
+ F_CLR(sp, S_INPUT);
+ } else {
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+ tp->lb[tp->len] = '\0';
+ }
+
+ /*
+ * Set the return cursor position to rest on the last
+ * inserted character.
+ */
+ if (rp != NULL) {
+ rp->lno = tp->lno;
+ rp->cno = sp->cno ? sp->cno - 1 : 0;
+ if (sp->s_change(sp, ep, rp->lno, LINE_RESET))
+ goto err;
+ }
+ goto ret;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_ZEROSET;
+ goto ins_ch;
+ case K_CNTRLD: /* Delete autoindent char. */
+ /*
+ * If in the first column or no characters to erase,
+ * ignore the ^D (this matches historic practice). If
+ * not doing autoindent or already inserted non-ai
+ * characters, it's a literal. The latter test is done
+ * in the switch, as the CARAT forms are N + 1, not N.
+ */
+ if (!LF_ISSET(TXT_AUTOINDENT))
+ goto ins_ch;
+ if (sp->cno == 0 || tp->ai == 0)
+ break;
+ switch (carat_st) {
+ case C_CARATSET: /* ^^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ TBINC(sp, ait.lb, ait.lb_len, tp->ai);
+ memmove(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat_st = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+ carat_st = C_NOTSET;
+leftmargin: tp->lb[sp->cno - 1] = ' ';
+ tp->owrite += sp->cno - tp->offset;
+ tp->ai = 0;
+ sp->cno = tp->offset;
+ break;
+ case C_NOTSET: /* ^D */
+ if (sp->cno > tp->ai + tp->offset)
+ goto ins_ch;
+ (void)txt_outdent(sp, tp);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case K_VERASE: /* Erase the last character. */
+ /*
+ * If can erase over the prompt, return. Len is 0
+ * if backspaced over the prompt, 1 if only CR entered.
+ */
+ if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) {
+ tp->len = 0;
+ goto ret;
+ }
+
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ break;
+ }
+
+ /* If nothing to erase, bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /* Drop back one character. */
+ --sp->cno;
+
+ /*
+ * Increment overwrite, decrement ai if deleted.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ ++tp->owrite;
+ if (sp->cno < tp->ai)
+ --tp->ai;
+ break;
+ case K_VINTR:
+ /*
+ * !!!
+ * Historically, <interrupt> exited the user from
+ * editing the infoline, and returned to the main
+ * screen. It also beeped the terminal, but that
+ * seems excessive.
+ */
+ if (LF_ISSET(TXT_INFOLINE)) {
+ tp->lb[tp->len = 0] = '\0';
+ goto ret;
+ }
+ goto ins_ch;
+ case K_VWERASE: /* Skip back one word. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /*
+ * If at offset, nothing to erase so bell the user.
+ */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /*
+ * First werase goes back to any autoindent
+ * and second werase goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+
+ /* Skip over trailing space characters. */
+ while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) {
+ --sp->cno;
+ ++tp->owrite;
+ }
+ if (sp->cno == max)
+ break;
+ /*
+ * There are three types of word erase found on UNIX
+ * systems. They can be identified by how the string
+ * /a/b/c is treated -- as 1, 3, or 6 words. Historic
+ * vi had two classes of characters, and strings were
+ * delimited by them and <blank>'s, so, 6 words. The
+ * historic tty interface used <blank>'s to delimit
+ * strings, so, 1 word. The algorithm offered in the
+ * 4.4BSD tty interface (as stty altwerase) treats it
+ * as 3 words -- there are two classes of characters,
+ * and strings are delimited by them and <blank>'s.
+ * The difference is that the type of the first erased
+ * character erased is ignored, which is exactly right
+ * when erasing pathname components. Here, the options
+ * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD
+ * tty interface and the historic tty driver behavior,
+ * respectively, and the default is the same as the
+ * historic vi behavior.
+ */
+ if (LF_ISSET(TXT_TTYWERASE))
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ else {
+ if (LF_ISSET(TXT_ALTWERASE)) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ if (sp->cno > max)
+ tmp = inword(tp->lb[sp->cno - 1]);
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (tmp != inword(tp->lb[sp->cno - 1])
+ || isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ }
+ break;
+ case K_VKILL: /* Restart this line. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /* If at offset, nothing to erase so bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase.");
+ break;
+ }
+
+ /*
+ * First kill goes back to any autoindent
+ * and second kill goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+ tp->owrite += sp->cno - max;
+ sp->cno = max;
+ break;
+ case K_CNTRLT: /* Add autoindent char. */
+ if (!LF_ISSET(TXT_CNTRLT))
+ goto ins_ch;
+ if (txt_indent(sp, tp))
+ goto err;
+ goto ebuf_chk;
+ case K_CNTRLZ:
+ (void)sp->s_suspend(sp);
+ break;
+#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT
+ case K_FORMFEED:
+ F_SET(sp, S_REFRESH);
+ break;
+#endif
+ case K_RIGHTBRACE:
+ case K_RIGHTPAREN:
+ showmatch = LF_ISSET(TXT_SHOWMATCH);
+ goto ins_ch;
+ case K_VLNEXT: /* Quote the next character. */
+ /* If in hex mode, see if we've entered a hex value. */
+ if (hex == H_INHEX) {
+ if (txt_hex(sp, tp, &tmp, &ch))
+ goto err;
+ if (tmp) {
+ hex = H_NOTSET;
+ goto next_ch;
+ }
+ }
+ ch = '^';
+ quoted = Q_NEXTCHAR;
+ goto insq_ch;
+ default: /* Insert the character. */
+ins_ch: /*
+ * Historically, vi eliminated nul's out of hand. If
+ * the beautify option was set, it also deleted any
+ * unknown ASCII value less than space (040) and the
+ * del character (0177), except for tabs. Unknown is
+ * a key word here. Most vi documentation claims that
+ * it deleted everything but <tab>, <nl> and <ff>, as
+ * that's what the original 4BSD documentation said.
+ * This is obviously wrong, however, as <esc> would be
+ * included in that list. What we do is eliminate any
+ * unquoted, iscntrl() character that wasn't a replay
+ * and wasn't handled specially, except <tab> or <ff>.
+ */
+ if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ch) &&
+ ikey.value != K_FORMFEED && ikey.value != K_TAB) {
+ msgq(sp, M_BERR,
+ "Illegal character; quote to enter.");
+ break;
+ }
+insq_ch: /*
+ * If entering a non-word character after a word, check
+ * for abbreviations. If there was one, discard the
+ * replay characters. If entering a blank character,
+ * check for unmap commands, as well.
+ */
+ if (!inword(ch)) {
+ if (abb == A_INWORD && !replay) {
+ if (txt_abbrev(sp, tp, &ch,
+ LF_ISSET(TXT_INFOLINE),
+ &tmp, &ab_turnoff))
+ goto err;
+ if (tmp) {
+ if (LF_ISSET(TXT_RECORD))
+ rcol -= tmp;
+ goto next_ch;
+ }
+ }
+ if (isblank(ch) && unmap_tst)
+ txt_unmap(sp, tp, &iflags);
+ }
+ /* If in hex mode, see if we've entered a hex value. */
+ if (hex == H_INHEX && !isxdigit(ch)) {
+ if (txt_hex(sp, tp, &tmp, &ch))
+ goto err;
+ if (tmp) {
+ hex = H_NOTSET;
+ goto next_ch;
+ }
+ }
+ /* Check to see if we've crossed the margin. */
+ if (margin) {
+ if (sp->s_column(sp, ep, &col))
+ goto err;
+ if (col >= margin) {
+ if (txt_margin(sp, tp, &tmp, &ch))
+ goto err;
+ if (tmp)
+ goto next_ch;
+ }
+ }
+ if (abb != A_NOTSET)
+ abb = inword(ch) ? A_INWORD : A_NOTWORD;
+
+ if (tp->owrite) /* Overwrite a character. */
+ --tp->owrite;
+ else if (tp->insert) { /* Insert a character. */
+ ++tp->len;
+ if (tp->insert == 1)
+ tp->lb[sp->cno + 1] = tp->lb[sp->cno];
+ else
+ memmove(tp->lb + sp->cno + 1,
+ tp->lb + sp->cno, tp->insert);
+ }
+
+ tp->lb[sp->cno++] = ch;
+
+ /*
+ * If we've reached the end of the buffer, then we
+ * need to switch into insert mode. This happens
+ * when there's a change to a mark and the user puts
+ * in more characters than the length of the motion.
+ */
+ebuf_chk: if (sp->cno >= tp->len) {
+ TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ tp->lb[sp->cno] = CURSOR_CH;
+ ++tp->insert;
+ ++tp->len;
+ }
+
+ if (hex == H_NEXTCHAR)
+ hex = H_INHEX;
+ if (quoted == Q_NEXTCHAR)
+ quoted = Q_THISCHAR;
+ break;
+ }
+#if defined(DEBUG) && 1
+ if (sp->cno + tp->insert + tp->owrite != tp->len)
+ msgq(sp, M_ERR,
+ "len %u != cno: %u ai: %u insert %u overwrite %u",
+ tp->len, sp->cno, tp->ai, tp->insert, tp->owrite);
+ tp->len = sp->cno + tp->insert + tp->owrite;
+#endif
+ }
+
+ /* Clear input flag. */
+ret: F_CLR(sp, S_INPUT);
+
+ if (LF_ISSET(TXT_RECORD))
+ VIP(sp)->rep_cnt = rcol;
+ return (eval);
+
+ /* Error jump. */
+err: eval = 1;
+ txt_err(sp, ep, tiqh);
+ goto ret;
+}
+
+/*
+ * txt_abbrev --
+ * Handle abbreviations.
+ */
+static int
+txt_abbrev(sp, tp, pushcp, isinfoline, didsubp, turnoffp)
+ SCR *sp;
+ TEXT *tp;
+ CHAR_T *pushcp;
+ int isinfoline, *didsubp, *turnoffp;
+{
+ CHAR_T ch;
+ SEQ *qp;
+ size_t len, off;
+ char *p;
+
+ /*
+ * Find the start of the "word". Historically, abbreviations
+ * could be preceded by any non-word character.
+ */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (!inword(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * !!!
+ * Historic vi exploded abbreviations on the command line. This has
+ * obvious problems in that unabbreviating the string can be extremely
+ * tricky, particularly if the string has, say, an embedded escape
+ * character. Personally, I think it's a stunningly bad idea. Other
+ * examples of problems this caused in historic vi are:
+ * :ab foo bar
+ * :ab foo baz
+ * results in "bar" being abbreviated to "baz", which wasn't what the
+ * user had in mind at all. Also, the commands:
+ * :ab foo bar
+ * :unab foo<space>
+ * resulted in an error message that "bar" wasn't mapped. Finally,
+ * since the string was already exploded by the time the unabbreviate
+ * command got it, all it knew was that an abbreviation had occurred.
+ * Cleverly, it checked the replacement string for its unabbreviation
+ * match, which meant that the commands:
+ * :ab foo1 bar
+ * :ab foo2 bar
+ * :unab foo2
+ * unabbreviates "foo1", and the commands:
+ * :ab foo bar
+ * :ab bar baz
+ * unabbreviates "foo"!
+ *
+ * Anyway, people neglected to first ask my opinion before they wrote
+ * macros that depend on this stuff, so, we make this work as follows.
+ * When checking for an abbreviation on the command line, if we get a
+ * string which is <blank> terminated and which starts at the beginning
+ * of the line, we check to see it is the abbreviate or unabbreviate
+ * commands. If it is, turn abbreviations off and return as if no
+ * abbreviation was found. Note also, minor trickiness, so that if the
+ * user erases the line and starts another command, we go ahead an turn
+ * abbreviations back on.
+ *
+ * This makes the layering look like a Nachos Supreme.
+ */
+ *didsubp = 0;
+ if (isinfoline)
+ if (off == tp->ai || off == tp->offset)
+ if (ex_is_abbrev(p, len)) {
+ *turnoffp = 1;
+ return (0);
+ } else
+ *turnoffp = 0;
+ else
+ if (*turnoffp)
+ return (0);
+
+ /* Check for any abbreviations. */
+ if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
+ return (0);
+
+ /*
+ * Push the abbreviation onto the tty stack. Historically, characters
+ * resulting from an abbreviation expansion were themselves subject to
+ * map expansions, O_SHOWMATCH matching etc. This means the expanded
+ * characters will be re-tested for abbreviations. It's difficult to
+ * know what historic practice in this case was, since abbreviations
+ * were applied to :colon command lines, so entering abbreviations that
+ * looped was tricky, although possible. In addition, obvious loops
+ * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will
+ * silently only implement and/or display the last abbreviation.)
+ *
+ * This implementation doesn't recover well from such abbreviations.
+ * The main input loop counts abbreviated characters, and, when it
+ * reaches a limit, discards any abbreviated characters on the queue.
+ * It's difficult to back up to the original position, as the replay
+ * queue would have to be adjusted, and the line state when an initial
+ * abbreviated character was received would have to be saved.
+ */
+ ch = *pushcp;
+ if (term_push(sp, &ch, 1, 0, CH_ABBREVIATED))
+ return (1);
+ if (term_push(sp, qp->output, qp->olen, 0, CH_ABBREVIATED))
+ return (1);
+
+ /*
+ * Move the cursor to the start of the abbreviation,
+ * adjust the length.
+ */
+ sp->cno -= len;
+ tp->len -= len;
+
+ /* Copy any insert characters back. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len, tp->insert);
+
+ /*
+ * We return the length of the abbreviated characters. This is so
+ * the calling routine can replace the replay characters with the
+ * abbreviation. This means that subsequent '.' commands will produce
+ * the same text, regardless of intervening :[un]abbreviate commands.
+ * This is historic practice.
+ */
+ *didsubp = len;
+ return (0);
+}
+
+/*
+ * txt_unmap --
+ * Handle the unmap command.
+ */
+static void
+txt_unmap(sp, tp, iflagsp)
+ SCR *sp;
+ TEXT *tp;
+ u_int *iflagsp;
+{
+ size_t len, off;
+ char *p;
+
+ /* Find the beginning of this "word". */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (isblank(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * !!!
+ * Historic vi exploded input mappings on the command line. See the
+ * txt_abbrev() routine for an explanation of the problems inherent
+ * in this.
+ *
+ * We make this work as follows. If we get a string which is <blank>
+ * terminated and which starts at the beginning of the line, we check
+ * to see it is the unmap command. If it is, we return that the input
+ * mapping should be turned off. Note also, minor trickiness, so that
+ * if the user erases the line and starts another command, we go ahead
+ * an turn mapping back on.
+ */
+ if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
+ *iflagsp &= ~TXT_MAPINPUT;
+ else
+ *iflagsp |= TXT_MAPINPUT;
+}
+
+
+/* Offset to next column of stop size. */
+#define STOP_OFF(c, stop) (stop - (c) % stop)
+
+/*
+ * txt_ai_resolve --
+ * When a line is resolved by <esc> or <cr>, review autoindent
+ * characters.
+ */
+static void
+txt_ai_resolve(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long ts;
+ int del;
+ size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
+ char *p;
+
+ /*
+ * If the line is empty, has an offset, or no autoindent
+ * characters, we're done.
+ */
+ if (!tp->len || tp->offset || !tp->ai)
+ return;
+
+ /*
+ * The autoindent characters plus any leading <blank> characters
+ * in the line are resolved into the minimum number of characters.
+ * Historic practice.
+ */
+ ts = O_VAL(sp, O_TABSTOP);
+
+ /* Figure out the last <blank> screen column. */
+ for (p = tp->lb, scno = 0, len = tp->len,
+ spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
+ if (*p == '\t') {
+ if (spaces)
+ tab_after_sp = 1;
+ scno += STOP_OFF(scno, ts);
+ } else {
+ ++spaces;
+ ++scno;
+ }
+
+ /*
+ * If there are no spaces, or no tabs after spaces and less than
+ * ts spaces, it's already minimal.
+ */
+ if (!spaces || !tab_after_sp && spaces < ts)
+ return;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /*
+ * Figure out how many characters we're dropping -- if we're not
+ * dropping any, it's already minimal, we're done.
+ */
+ old = p - tp->lb;
+ new = spaces + tabs;
+ if (old == new)
+ return;
+
+ /* Shift the rest of the characters down, adjust the counts. */
+ del = old - new;
+ memmove(p - del, p, tp->len - old);
+ sp->cno -= del;
+ tp->len -= del;
+
+ /* Fill in space/tab characters. */
+ for (p = tp->lb; tabs--;)
+ *p++ = '\t';
+ while (spaces--)
+ *p++ = ' ';
+}
+
+/*
+ * txt_auto --
+ * Handle autoindent. If aitp isn't NULL, use it, otherwise,
+ * retrieve the line.
+ */
+int
+txt_auto(sp, ep, lno, aitp, len, tp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t len;
+ TEXT *aitp, *tp;
+{
+ size_t nlen;
+ char *p, *t;
+
+ if (aitp == NULL) {
+ if ((t = file_gline(sp, ep, lno, &len)) == NULL)
+ return (0);
+ } else
+ t = aitp->lb;
+
+ /* Count whitespace characters. */
+ for (p = t; len > 0; ++p, --len)
+ if (!isblank(*p))
+ break;
+
+ /* Set count, check for no indentation. */
+ if ((nlen = (p - t)) == 0)
+ return (0);
+
+ /* Make sure the buffer's big enough. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+
+ /* Copy the buffer's current contents up. */
+ if (tp->len != 0)
+ memmove(tp->lb + nlen, tp->lb, tp->len);
+ tp->len += nlen;
+
+ /* Copy the indentation into the new buffer. */
+ memmove(tp->lb, t, nlen);
+
+ /* Set the autoindent count. */
+ tp->ai = nlen;
+ return (0);
+}
+
+/*
+ * txt_backup --
+ * Back up to the previously edited line.
+ */
+static TEXT *
+txt_backup(sp, ep, tiqh, tp, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ TEXT *tp;
+ u_int flags;
+{
+ TEXT *ntp;
+ recno_t lno;
+ size_t total;
+
+ /* Get a handle on the previous TEXT structure. */
+ if ((ntp = tp->q.cqe_prev) == (void *)tiqh) {
+ msgq(sp, M_BERR, "Already at the beginning of the insert");
+ return (tp);
+ }
+
+ /* Make sure that we have enough space. */
+ total = ntp->len + tp->insert;
+ if (LF_ISSET(TXT_APPENDEOL))
+ ++total;
+ if (total > ntp->lb_len &&
+ binc(sp, &ntp->lb, &ntp->lb_len, total))
+ return (NULL);
+
+ /*
+ * Append a cursor or copy inserted bytes to the end of the old line.
+ * Test for appending a cursor first, because the TEXT insert field
+ * will be 1 if we're appending a cursor. I don't think there's a
+ * third case, so abort() if there is.
+ */
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ ntp->lb[ntp->len] = CURSOR_CH;
+ ntp->insert = 1;
+ } else if (tp->insert) {
+ memmove(ntp->lb + ntp->len, tp->lb + tp->owrite, tp->insert);
+ ntp->insert = tp->insert;
+ } else
+ abort();
+
+ /* Set bookkeeping information. */
+ sp->lno = ntp->lno;
+ sp->cno = ntp->len;
+ ntp->len += ntp->insert;
+
+ /* Release the current TEXT. */
+ lno = tp->lno;
+ CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+
+ /* Update the old line on the screen. */
+ if (sp->s_change(sp, ep, lno, LINE_DELETE))
+ return (NULL);
+
+ /* Return the old line. */
+ return (ntp);
+}
+
+/*
+ * txt_err --
+ * Handle an error during input processing.
+ */
+static void
+txt_err(sp, ep, tiqh)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+{
+ recno_t lno;
+ size_t len;
+
+ /*
+ * The problem with input processing is that the cursor is at an
+ * indeterminate position since some input may have been lost due
+ * to a malloc error. So, try to go back to the place from which
+ * the cursor started, knowing that it may no longer be available.
+ *
+ * We depend on at least one line number being set in the text
+ * chain.
+ */
+ for (lno = tiqh->cqh_first->lno;
+ file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno);
+
+ sp->lno = lno == 0 ? 1 : lno;
+ sp->cno = 0;
+
+ /* Redraw the screen, just in case. */
+ F_SET(sp, S_REDRAW);
+}
+
+/*
+ * txt_hex --
+ * Let the user insert any character value they want.
+ *
+ * !!!
+ * This is an extension. The pattern "^Vx[0-9a-fA-F]*" is a way
+ * for the user to specify a character value which their keyboard
+ * may not be able to enter.
+ */
+static int
+txt_hex(sp, tp, was_hex, pushcp)
+ SCR *sp;
+ TEXT *tp;
+ int *was_hex;
+ CHAR_T *pushcp;
+{
+ CHAR_T ch, savec;
+ size_t len, off;
+ u_long value;
+ char *p, *wp;
+
+ /*
+ * Null-terminate the string. Since nul isn't a legal hex value,
+ * this should be okay, and lets us use a local routine, which
+ * presumably understands the character set, to convert the value.
+ */
+ savec = tp->lb[sp->cno];
+ tp->lb[sp->cno] = 0;
+
+ /* Find the previous HEX_CH. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (*p == HEX_CH) {
+ wp = p + 1;
+ break;
+ }
+ ++len;
+ /* If not on this line, there's nothing to do. */
+ if (off == tp->ai || off == tp->offset)
+ goto nothex;
+ }
+
+ /* If no length, then it wasn't a hex value. */
+ if (len == 0)
+ goto nothex;
+
+ /* Get the value. */
+ value = strtol(wp, NULL, 16);
+ if (value == LONG_MIN || value == LONG_MAX || value > MAX_CHAR_T) {
+nothex: tp->lb[sp->cno] = savec;
+ *was_hex = 0;
+ return (0);
+ }
+
+ ch = *pushcp;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+ ch = value;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+
+ tp->lb[sp->cno] = savec;
+
+ /* Move the cursor to the start of the hex value, adjust the length. */
+ sp->cno -= len + 1;
+ tp->len -= len + 1;
+
+ /* Copy any insert characters back. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len + 1, tp->insert);
+
+ *was_hex = 1;
+ return (0);
+}
+
+/*
+ * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements
+ * to the next or previous shiftwidth value, i.e. for a 1-based numbering,
+ * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to
+ * the 10th column, and ^D moves it back.
+ *
+ * !!!
+ * The ^T and ^D characters in historical vi only had special meaning when
+ * they were the first characters typed after entering text input mode.
+ * Since normal erase characters couldn't erase autoindent (in this case
+ * ^T) characters, this meant that inserting text into previously existing
+ * text was quite strange, ^T only worked if it was the first keystroke,
+ * and then it could only be erased by using ^D. This implementation treats
+ * ^T specially anywhere it occurs in the input, and permits the standard
+ * erase characters to erase characters inserted using it.
+ *
+ * XXX
+ * Technically, txt_indent, txt_outdent should part of the screen interface,
+ * as they require knowledge of the size of a space character on the screen.
+ * (Not the size of tabs, because tabs are logically composed of spaces.)
+ * They're left in the text code because they're complicated, not to mention
+ * the gruesome awareness that if spaces aren't a single column on the screen
+ * for any language, we're into some serious, ah, for lack of a better word,
+ * "issues".
+ */
+
+/*
+ * txt_indent --
+ * Handle ^T indents.
+ */
+static int
+txt_indent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces, tabs;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0;
+ cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /* Put space/tab characters in place of any overwrite characters. */
+ for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+
+ if (!tabs && !spaces)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces + tabs,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space/tab characters. */
+ for (; tabs--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_outdent --
+ * Handle ^D outdents.
+ *
+ */
+static int
+txt_outdent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Get the previous shiftwidth column. */
+ for (cno = scno; --scno % sw != 0;);
+
+ /* Decrement characters until less than or equal to that slot. */
+ for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite)
+ if (tp->lb[--off] == '\t')
+ cno -= STOP_OFF(cno, ts);
+ else
+ --cno;
+
+ /* Spaces needed to get to the target. */
+ spaces = scno - cno;
+
+ /* Maybe just a delete. */
+ if (spaces == 0)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces);
+
+ /* Use up any overwrite characters. */
+ for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite)
+ tp->lb[sp->cno++] = ' ';
+
+ /* Maybe that was enough. */
+ if (spaces == 0)
+ return (0);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space characters. */
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_resolve --
+ * Resolve the input text chain into the file.
+ */
+static int
+txt_resolve(sp, ep, tiqh)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+{
+ TEXT *tp;
+ recno_t lno;
+
+ /* The first line replaces a current line. */
+ tp = tiqh->cqh_first;
+ if (file_sline(sp, ep, tp->lno, tp->lb, tp->len))
+ return (1);
+
+ /* All subsequent lines are appended into the file. */
+ for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno)
+ if (file_aline(sp, ep, 0, lno, tp->lb, tp->len))
+ return (1);
+ return (0);
+}
+
+/*
+ * txt_showmatch --
+ * Show a character match.
+ *
+ * !!!
+ * Historic vi tried to display matches even in the :colon command line.
+ * I think not.
+ */
+static void
+txt_showmatch(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct timeval second;
+ VCS cs;
+ MARK m;
+ fd_set zero;
+ int cnt, endc, startc;
+
+ /*
+ * Do a refresh first, in case the v_ntext() code hasn't done
+ * one in awhile, so the user can see what we're complaining
+ * about.
+ */
+ if (sp->s_refresh(sp, ep))
+ return;
+ /*
+ * We don't display the match if it's not on the screen. Find
+ * out what the first character on the screen is.
+ */
+ if (sp->s_position(sp, ep, &m, 0, P_TOP))
+ return;
+
+ /* Initialize the getc() interface. */
+ cs.cs_lno = sp->lno;
+ cs.cs_cno = sp->cno - 1;
+ if (cs_init(sp, ep, &cs))
+ return;
+ startc = (endc = cs.cs_ch) == ')' ? '(' : '{';
+
+ /* Search for the match. */
+ for (cnt = 1;;) {
+ if (cs_prev(sp, ep, &cs))
+ return;
+ if (cs.cs_lno < m.lno ||
+ cs.cs_lno == m.lno && cs.cs_cno < m.cno)
+ return;
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
+ (void)sp->s_bell(sp);
+ return;
+ }
+ continue;
+ }
+ if (cs.cs_ch == endc)
+ ++cnt;
+ else if (cs.cs_ch == startc && --cnt == 0)
+ break;
+ }
+
+ /* Move to the match. */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ sp->lno = cs.cs_lno;
+ sp->cno = cs.cs_cno;
+ (void)sp->s_refresh(sp, ep);
+
+ /*
+ * Sleep(3) is eight system calls. Do it fast -- besides,
+ * I don't want to wait an entire second.
+ */
+ FD_ZERO(&zero);
+ second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10;
+ second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L;
+ (void)select(0, &zero, &zero, &zero, &second);
+
+ /* Return to the current location. */
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ (void)sp->s_refresh(sp, ep);
+}
+
+/*
+ * txt_margin --
+ * Handle margin wrap.
+ *
+ * !!!
+ * Historic vi belled the user each time a character was entered after
+ * crossing the margin until a space was entered which could be used to
+ * break the line. I don't, it tends to wake the cats.
+ */
+static int
+txt_margin(sp, tp, didbreak, pushcp)
+ SCR *sp;
+ TEXT *tp;
+ int *didbreak;
+ CHAR_T *pushcp;
+{
+ CHAR_T ch;
+ size_t len, off, tlen;
+ char *p, *wp;
+
+ /* Find the closest previous blank. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (isblank(*p)) {
+ wp = p + 1;
+ break;
+ }
+ ++len;
+ /* If it's the beginning of the line, there's nothing to do. */
+ if (off == tp->ai || off == tp->offset) {
+ *didbreak = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * Historic practice is to delete any trailing whitespace
+ * from the previous line.
+ */
+ for (tlen = len;; --p, --off) {
+ if (!isblank(*p))
+ break;
+ ++tlen;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ ch = *pushcp;
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP))
+ return (1);
+ if (len && term_push(sp, wp, len, 0, CH_NOMAP | CH_QUOTED))
+ return (1);
+ ch = '\n';
+ if (term_push(sp, &ch, 1, 0, CH_NOMAP))
+ return (1);
+
+ sp->cno -= tlen;
+ tp->owrite += tlen;
+ *didbreak = 1;
+ return (0);
+}
+
+/*
+ * txt_Rcleanup --
+ * Resolve the input line for the 'R' command.
+ */
+static void
+txt_Rcleanup(sp, tiqh, tp, lp, len)
+ SCR *sp;
+ TEXTH *tiqh;
+ TEXT *tp;
+ const char *lp;
+ const size_t len;
+{
+ size_t tmp;
+
+ /*
+ * The 'R' command restores any overwritable characters in the
+ * first line to the original characters. Check to make sure
+ * that the cursor hasn't moved beyond the end of the original
+ * line.
+ */
+ if (tp != tiqh->cqh_first || tp->owrite == 0 || sp->cno >= len)
+ return;
+
+ /* Restore whatever we can restore from the original line. */
+ tmp = MIN(tp->owrite, len - sp->cno);
+ memmove(tp->lb + sp->cno, lp + sp->cno, tmp);
+
+ /*
+ * There can be more overwrite characters if the user extended the
+ * line and then erased it. What we have to do is delete whatever
+ * the user inserted and then erased. Regardless, we increase the
+ * insert character count to make the TEXT structure look right.
+ * (There shouldn't be any insert characters as 'R' replaces the
+ * entire line; if there are, this code isn't going to work).
+ */
+ if (tp->owrite > tmp)
+ tp->len -= tp->owrite - tmp;
+ tp->owrite = 0;
+ tp->insert = tmp;
+}
diff --git a/usr.bin/vi/vi/v_paragraph.c b/usr.bin/vi/vi/v_paragraph.c
new file mode 100644
index 000000000000..ceed57c7613e
--- /dev/null
+++ b/usr.bin/vi/vi/v_paragraph.c
@@ -0,0 +1,349 @@
+/*-
+ * 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[] = "@(#)v_paragraph.c 8.9 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * Paragraphs are empty lines after text or values from the paragraph
+ * or section options.
+ */
+
+/*
+ * v_paragraphf -- [count]}
+ * Move forward count paragraphs.
+ */
+int
+v_paragraphf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t lastlen, len;
+ recno_t cnt, lastlno, lno;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * If the starting cursor position is at or before any non-blank
+ * characters in the line, i.e. the movement is cutting all of the
+ * line's text, the buffer is in line mode. It's a lot easier to
+ * check here, because we know that the end is going to be the start
+ * or end of a line.
+ *
+ * This was historical practice in vi, with a single exception. If
+ * the paragraph movement was from the start of the last line to EOF,
+ * then all the characters were deleted from the last line, but the
+ * line itself remained. If somebody complains, don't pause, don't
+ * hesitate, just hit them.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ else {
+ vp->m_stop = vp->m_start;
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ if (vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+ }
+
+ /* Figure out what state we're currently in. */
+ lno = vp->m_start.lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto eof;
+
+ /*
+ * If we start in text, we want to switch states
+ * (2 * N - 1) times, in non-text, (2 * N) times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+ }
+
+ for (;;) {
+ lastlno = lno;
+ lastlen = len;
+ if ((p = file_gline(sp, ep, ++lno, &len)) == NULL)
+ goto eof;
+ switch (pstate) {
+ case P_INTEXT:
+ if (p[0] == '.' && len >= 2)
+ for (lp = VIP(sp)->paragraph; *lp; lp += 2)
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' || lp[1] == p[2]) &&
+ !--cnt)
+ goto found;
+ if (len == 0 || v_isempty(p, len)) {
+ if (!--cnt)
+ goto found;
+ pstate = P_INBLANK;
+ }
+ break;
+ case P_INBLANK:
+ if (len == 0 || v_isempty(p, len))
+ break;
+ if (--cnt) {
+ pstate = P_INTEXT;
+ break;
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range,
+ * VC_D and VC_Y stay at the start. Ignore VC_C and
+ * VC_S. Adjust end of the range for motion commands;
+ * historically, a motion component was to the end of
+ * the previous line, whereas the movement command was
+ * to the start of the new "paragraph".
+ */
+found: if (ISMOTION(vp)) {
+ vp->m_stop.lno = lastlno;
+ vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
+ vp->m_final = vp->m_start;
+ } else {
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ vp->m_final = vp->m_stop;
+ }
+ return (0);
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * !!!
+ * Adjust end of the range for motion commands; EOF is a movement
+ * sink. The } command historically moved to the end of the last
+ * line, not the beginning, from any position before the end of the
+ * last line.
+ */
+eof: if (vp->m_start.lno == lno - 1) {
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ if (vp->m_start.cno == (len ? len - 1 : 0)) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range, VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S.
+ *
+ * If deleting the line (which happens if deleting to EOF),
+ * then cursor movement is to the first nonblank.
+ */
+ if (F_ISSET(vp, VC_D)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ vp->m_stop.lno = lno - 1;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_paragraphb -- [count]{
+ * Move backward count paragraphs.
+ */
+int
+v_paragraphb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * Check for SOF. The historic vi didn't complain if users hit SOF
+ * repeatedly, unless it was part of a motion command. There is no
+ * question but that Emerson's editor of choice was vi.
+ *
+ * The { command historically moved to the beginning of the first
+ * line if invoked on the first line.
+ *
+ * !!!
+ * If the starting cursor position is in the first column (backward
+ * paragraph movements did NOT historically pay attention to non-blank
+ * characters) i.e. the movement is cutting the entire line, the buffer
+ * is in line mode. Cuts from the beginning of the line also did not
+ * cut the current line, but started at the previous EOL.
+ *
+ * Correct for a left motion component while we're thinking about it.
+ */
+ lno = vp->m_start.lno;
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0) {
+ if (vp->m_start.lno == 1) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ } else
+ --vp->m_start.lno;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+
+ if (vp->m_start.lno <= 1)
+ goto sof;
+
+ /* Figure out what state we're currently in. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto sof;
+
+ /*
+ * If we start in text, we want to switch states
+ * (2 * N - 1) times, in non-text, (2 * N) times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+ }
+
+ for (;;) {
+ if ((p = file_gline(sp, ep, --lno, &len)) == NULL)
+ goto sof;
+ switch (pstate) {
+ case P_INTEXT:
+ if (p[0] == '.' && len >= 2)
+ for (lp = VIP(sp)->paragraph; *lp; lp += 2)
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' || lp[1] == p[2]) &&
+ !--cnt)
+ goto ret;
+ if (len == 0 || v_isempty(p, len)) {
+ if (!--cnt)
+ goto ret;
+ pstate = P_INBLANK;
+ }
+ break;
+ case P_INBLANK:
+ if (len != 0 && !v_isempty(p, len)) {
+ if (!--cnt)
+ goto ret;
+ pstate = P_INTEXT;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /* SOF is a movement sink. */
+sof: lno = 1;
+
+ret: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_buildparagraph --
+ * Build the paragraph command search pattern.
+ */
+int
+v_buildparagraph(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+ size_t p_len, s_len;
+ char *p, *p_p, *s_p;
+
+ /*
+ * The vi paragraph command searches for either a paragraph or
+ * section option macro.
+ */
+ p_len = (p_p = O_STR(sp, O_PARAGRAPHS)) == NULL ? 0 : strlen(p_p);
+ s_len = (s_p = O_STR(sp, O_SECTIONS)) == NULL ? 0 : strlen(s_p);
+
+ if (p_len == 0 && s_len == 0)
+ return (0);
+
+ MALLOC_RET(sp, p, char *, p_len + s_len + 1);
+
+ vip = VIP(sp);
+ if (vip->paragraph != NULL)
+ FREE(vip->paragraph, vip->paragraph_len);
+
+ if (p_p != NULL)
+ memmove(p, p_p, p_len + 1);
+ if (s_p != NULL)
+ memmove(p + p_len, s_p, s_len + 1);
+ vip->paragraph = p;
+ vip->paragraph_len = p_len + s_len + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_put.c b/usr.bin/vi/vi/v_put.c
new file mode 100644
index 000000000000..d427c332add5
--- /dev/null
+++ b/usr.bin/vi/vi/v_put.c
@@ -0,0 +1,141 @@
+/*-
+ * 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[] = "@(#)v_put.c 8.8 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static void inc_buf __P((SCR *, VICMDARG *));
+
+/*
+ * v_Put -- [buffer]P
+ * Insert the contents of the buffer before the cursor.
+ */
+int
+v_Put(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ return (put(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 0));
+}
+
+/*
+ * v_put -- [buffer]p
+ * Insert the contents of the buffer after the cursor.
+ */
+int
+v_put(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ return (put(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 1));
+}
+
+/*
+ * !!!
+ * Historical whackadoo. The dot command `puts' the numbered buffer
+ * after the last one put. For example, `"4p.' would put buffer #4
+ * and buffer #5. If the user continued to enter '.', the #9 buffer
+ * would be repeatedly output. This was not documented, and is a bit
+ * tricky to reconstruct. Historical versions of vi also dropped the
+ * contents of the default buffer after each put, so after `"4p' the
+ * default buffer would be empty. This makes no sense to me, so we
+ * don't bother. Don't assume sequential order of numeric characters.
+ *
+ * And, if that weren't exciting enough, failed commands don't normally
+ * set the dot command. Well, boys and girls, an exception is that
+ * the buffer increment gets done regardless of the success of the put.
+ */
+static void
+inc_buf(sp, vp)
+ SCR *sp;
+ VICMDARG *vp;
+{
+ CHAR_T v;
+
+ switch (vp->buffer) {
+ case '1':
+ v = '2';
+ break;
+ case '2':
+ v = '3';
+ break;
+ case '3':
+ v = '4';
+ break;
+ case '4':
+ v = '5';
+ break;
+ case '5':
+ v = '6';
+ break;
+ case '6':
+ v = '7';
+ break;
+ case '7':
+ v = '8';
+ break;
+ case '8':
+ v = '9';
+ break;
+ default:
+ return;
+ }
+ VIP(sp)->sdot.buffer = vp->buffer = v;
+}
diff --git a/usr.bin/vi/vi/v_redraw.c b/usr.bin/vi/vi/v_redraw.c
new file mode 100644
index 000000000000..b124e4acae44
--- /dev/null
+++ b/usr.bin/vi/vi/v_redraw.c
@@ -0,0 +1,66 @@
+/*-
+ * 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[] = "@(#)v_redraw.c 8.4 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_redraw -- ^R
+ * Redraw the screen.
+ */
+int
+v_redraw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ F_SET(sp, S_REFRESH);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_replace.c b/usr.bin/vi/vi/v_replace.c
new file mode 100644
index 000000000000..a9e7c470a270
--- /dev/null
+++ b/usr.bin/vi/vi/v_replace.c
@@ -0,0 +1,190 @@
+/*-
+ * 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[] = "@(#)v_replace.c 8.15 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_replace -- [count]rc
+ *
+ * !!!
+ * The r command in historic vi was almost beautiful in its badness. For
+ * example, "r<erase>" and "r<word erase>" beeped the terminal and deleted
+ * a single character. "Nr<carriage return>", where N was greater than 1,
+ * inserted a single carriage return. This may not be right, but at least
+ * it's not insane.
+ */
+int
+v_replace(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ CH ikey;
+ TEXT *tp;
+ recno_t lno;
+ size_t blen, len;
+ u_long cnt;
+ int rval;
+ char *bp, *p;
+
+ /*
+ * If the line doesn't exist, or it's empty, replacement isn't
+ * allowed. It's not hard to implement, but:
+ *
+ * 1: It's historic practice.
+ * 2: For consistency, this change would require that the more
+ * general case, "Nr", when the user is < N characters from
+ * the end of the line, also work.
+ * 3: Replacing a newline has somewhat odd semantics.
+ */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ goto nochar;
+ }
+ if (len == 0) {
+nochar: msgq(sp, M_BERR, "No characters to replace.");
+ return (1);
+ }
+
+ /*
+ * Figure out how many characters to be replace. For no particular
+ * reason (other than that the semantics of replacing the newline
+ * are confusing) only permit the replacement of the characters in
+ * the current line. I suppose we could append replacement characters
+ * to the line, but I see no compelling reason to do so.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = vp->m_start.cno + cnt - 1;
+ if (vp->m_stop.cno > len - 1) {
+ v_eol(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /*
+ * Get the character. Literal escapes escape any character,
+ * single escapes return.
+ */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ ikey.ch = VIP(sp)->rlast;
+ ikey.value = term_key_val(sp, ikey.ch);
+ } else {
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ switch (ikey.value) {
+ case K_ESCAPE:
+ return (0);
+ case K_VLNEXT:
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ break;
+ }
+ VIP(sp)->rlast = ikey.ch;
+ }
+
+ /* Copy the line. */
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ p = bp;
+
+ if (ikey.value == K_CR || ikey.value == K_NL) {
+ /* Set return line. */
+ vp->m_stop.lno = vp->m_start.lno + cnt;
+ vp->m_stop.cno = 0;
+
+ /* The first part of the current line. */
+ if (file_sline(sp, ep, vp->m_start.lno, p, vp->m_start.cno))
+ goto err_ret;
+
+ /*
+ * The rest of the current line. And, of course, now it gets
+ * tricky. Any white space after the replaced character is
+ * stripped, and autoindent is applied. Put the cursor on the
+ * last indent character as did historic vi.
+ */
+ for (p += vp->m_start.cno + cnt, len -= vp->m_start.cno + cnt;
+ len && isblank(*p); --len, ++p);
+
+ if ((tp = text_init(sp, p, len, len)) == NULL)
+ goto err_ret;
+ if (txt_auto(sp, ep, vp->m_start.lno, NULL, 0, tp))
+ goto err_ret;
+ vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
+ if (file_aline(sp, ep, 1, vp->m_start.lno, tp->lb, tp->len))
+ goto err_ret;
+ text_free(tp);
+
+ rval = 0;
+
+ /* All of the middle lines. */
+ while (--cnt)
+ if (file_aline(sp, ep, 1, vp->m_start.lno, "", 0)) {
+err_ret: rval = 1;
+ break;
+ }
+ } else {
+ memset(bp + vp->m_start.cno, ikey.ch, cnt);
+ rval = file_sline(sp, ep, vp->m_start.lno, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ vp->m_final = vp->m_stop;
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_right.c b/usr.bin/vi/vi/v_right.c
new file mode 100644
index 000000000000..3d78e781400c
--- /dev/null
+++ b/usr.bin/vi/vi/v_right.c
@@ -0,0 +1,163 @@
+/*-
+ * 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[] = "@(#)v_right.c 8.6 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_right -- [count]' ', [count]l
+ * Move right by columns.
+ */
+int
+v_right(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /* It's always illegal to move right on empty lines. */
+ if (len == 0) {
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S. Adjust the
+ * end of the range for motion commands.
+ *
+ * !!!
+ * Historically, "[cdsy]l" worked at the end of a line. Also,
+ * EOL is a count sink.
+ */
+ vp->m_stop.cno = vp->m_start.cno +
+ (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (vp->m_start.cno == len - 1) {
+ if (!ISMOTION(vp)) {
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+ vp->m_stop.cno = vp->m_start.cno;
+ } else if (vp->m_stop.cno > len - 1)
+ vp->m_stop.cno = len - 1;
+
+ if (ISMOTION(vp)) {
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_dollar -- [count]$
+ * Move to the last column.
+ */
+int
+v_dollar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ /*
+ * !!!
+ * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
+ */
+ if ((F_ISSET(vp, VC_C1SET) ? vp->count : 1) != 1) {
+ /*
+ * !!!
+ * Historically, if the $ is a motion, and deleting from
+ * at or before the first non-blank of the line, it's a
+ * line motion, and the line motion flag is set.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_stop.cno))
+ return (1);
+ if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+
+ --vp->count;
+ if (v_down(sp, ep, vp))
+ return (1);
+ }
+
+ if (file_gline(sp, ep, vp->m_stop.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range.
+ * VC_D and VC_Y stay at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_screen.c b/usr.bin/vi/vi/v_screen.c
new file mode 100644
index 000000000000..0a4e8b009feb
--- /dev/null
+++ b/usr.bin/vi/vi/v_screen.c
@@ -0,0 +1,89 @@
+/*-
+ * 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[] = "@(#)v_screen.c 8.10 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_screen -- ^W
+ * Switch screens.
+ */
+int
+v_screen(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Try for the next lower screen, or, go back to the first
+ * screen on the stack.
+ */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ sp->nextdisp = sp->q.cqe_next;
+ else if (sp->gp->dq.cqh_first == sp) {
+ msgq(sp, M_ERR, "No other screen to switch to.");
+ return (1);
+ } else
+ sp->nextdisp = sp->gp->dq.cqh_first;
+
+ /*
+ * Display the old screen's status line so the user can
+ * find the screen they want.
+ */
+ (void)status(sp, ep, vp->m_start.lno, 0);
+
+ /* Save the old screen's cursor information. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_SSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_scroll.c b/usr.bin/vi/vi/v_scroll.c
new file mode 100644
index 000000000000..ec9c26dc2955
--- /dev/null
+++ b/usr.bin/vi/vi/v_scroll.c
@@ -0,0 +1,414 @@
+/*-
+ * 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[] = "@(#)v_scroll.c 8.14 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+static void goto_adjust __P((VICMDARG *));
+
+/*
+ * The historic vi had a problem in that all movements were by physical
+ * lines, not by logical, or screen lines. Arguments can be made that this
+ * is the right thing to do. For example, single line movements, such as
+ * 'j' or 'k', should probably work on physical lines. Commands like "dj",
+ * or "j.", where '.' is a change command, make more sense for physical lines
+ * than they do for logical lines.
+ *
+ * These arguments, however, don't apply to scrolling commands like ^D and
+ * ^F -- if the window is fairly small, using physical lines can result in
+ * a half-page scroll repainting the entire screen, which is not what the
+ * user wanted. Second, if the line is larger than the screen, using physical
+ * lines can make it impossible to display parts of the line -- there aren't
+ * any commands that don't display the beginning of the line in historic vi,
+ * and if both the beginning and end of the line can't be on the screen at
+ * the same time, you lose. This is even worse in the case of the H, L, and
+ * M commands -- for large lines, they may all refer to the same line and
+ * will result in no movement at all.
+ *
+ * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the
+ * cursor positioning commands (H, L, M) commands using logical lines, not
+ * physical.
+ *
+ * Another issue is that page and half-page scrolling commands historically
+ * moved to the first non-blank character in the new line. If the line is
+ * approximately the same size as the screen, this loses because the cursor
+ * before and after a ^D, may refer to the same location on the screen. In
+ * this implementation, scrolling commands set the cursor to the first non-
+ * blank character if the line changes because of the scroll. Otherwise,
+ * the cursor is left alone.
+ */
+
+/*
+ * v_lgoto -- [count]G
+ * Go to first non-blank character of the line count, the last line
+ * of the file by default.
+ */
+int
+v_lgoto(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t nlines;
+
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (file_gline(sp, ep, vp->count, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->count;
+ } else {
+ if (file_lline(sp, ep, &nlines))
+ return (1);
+ vp->m_stop.lno = nlines ? nlines : 1;
+ }
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_home -- [count]H
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the top of the screen, 0 by default.
+ */
+int
+v_home(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (sp->s_position(sp, ep, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_middle -- M
+ * Move to the first non-blank character of the logical line
+ * in the middle of the screen.
+ */
+int
+v_middle(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+ if (sp->s_position(sp, ep, &vp->m_stop, 0, P_MIDDLE))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_bottom -- [count]L
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the bottom of the screen, 0 by default.
+ */
+int
+v_bottom(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (sp->s_position(sp, ep, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+static void
+goto_adjust(vp)
+ VICMDARG *vp;
+{
+ /*
+ * !!!
+ * If it's not a yank to the current line or greater, and we've
+ * changed lines, move to the first non-blank of the line.
+ */
+ if (!F_ISSET(vp, VC_Y) || vp->m_stop.lno < vp->m_start.lno) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETLFNB);
+ }
+
+ /* Non-motion commands go to the end of the range. */
+ vp->m_final = vp->m_stop;
+ if (!ISMOTION(vp))
+ return;
+
+ /*
+ * If moving backward in the file, VC_D and VC_Y move to the end
+ * of the range, unless the line didn't change, in which case VC_Y
+ * doesn't move. If moving forward in the file, VC_D and VC_Y stay
+ * at the start of the range. Ignore VC_C and VC_S.
+ */
+ if (vp->m_stop.lno < vp->m_start.lno ||
+ vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno < vp->m_start.cno) {
+ if (F_ISSET(vp, VC_Y) && vp->m_stop.lno == vp->m_start.lno)
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_start;
+}
+
+/*
+ * v_up -- [count]^P, [count]k, [count]-
+ * Move up by lines.
+ */
+int
+v_up(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+
+ lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.lno <= lno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->m_start.lno - lno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_cr -- [count]^M
+ * In a script window, send the line to the shell.
+ * In a regular window, move down by lines.
+ */
+int
+v_cr(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * If it's a script window, exec the line,
+ * otherwise it's the same as v_down().
+ */
+ return (F_ISSET(sp, S_SCRIPT) ?
+ sscr_exec(sp, ep, vp->m_start.lno) : v_down(sp, ep, vp));
+}
+
+/*
+ * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
+ * Move down by lines.
+ */
+int
+v_down(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+
+ lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (file_gline(sp, ep, lno, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = lno;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpageup -- [count]^U
+ * Page up half screens.
+ */
+int
+v_hpageup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Half screens always succeed unless already at SOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command ultimately
+ * failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ O_VAL(sp, O_SCROLL) = vp->count;
+ else
+ vp->count = O_VAL(sp, O_SCROLL);
+
+ if (sp->s_down(sp, ep, &vp->m_stop, (recno_t)O_VAL(sp, O_SCROLL), 1))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpagedown -- [count]^D
+ * Page down half screens.
+ */
+int
+v_hpagedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Half screens always succeed unless already at EOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command ultimately
+ * failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ O_VAL(sp, O_SCROLL) = vp->count;
+ else
+ vp->count = O_VAL(sp, O_SCROLL);
+
+ if (sp->s_up(sp, ep, &vp->m_stop, (recno_t)O_VAL(sp, O_SCROLL), 1))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_pageup -- [count]^B
+ * Page up full screens.
+ *
+ * !!!
+ * Historic vi did not move to the SOF if the screen couldn't move, i.e.
+ * if SOF was already displayed on the screen. This implementation does
+ * move to SOF in that case, making ^B more like the the historic ^U.
+ */
+int
+v_pageup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Calculation from POSIX 1003.2/D8. */
+ if (sp->s_down(sp, ep, &vp->m_stop,
+ (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1), 1))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_pagedown -- [count]^F
+ * Page down full screens.
+ * !!!
+ * Historic vi did not move to the EOF if the screen couldn't move, i.e.
+ * if EOF was already displayed on the screen. This implementation does
+ * move to EOF in that case, making ^F more like the the historic ^D.
+ */
+int
+v_pagedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Calculation from POSIX 1003.2/D8. */
+ if (sp->s_up(sp, ep, &vp->m_stop,
+ (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1), 1))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_lineup -- [count]^Y
+ * Page up by lines.
+ */
+int
+v_lineup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * The cursor moves down, staying with its original line, unless it
+ * reaches the bottom of the screen.
+ */
+ if (sp->s_down(sp, ep,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_linedown -- [count]^E
+ * Page down by lines.
+ */
+int
+v_linedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * The cursor moves up, staying with its original line, unless it
+ * reaches the top of the screen.
+ */
+ if (sp->s_up(sp, ep,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_search.c b/usr.bin/vi/vi/v_search.c
new file mode 100644
index 000000000000..c6c950b88f81
--- /dev/null
+++ b/usr.bin/vi/vi/v_search.c
@@ -0,0 +1,365 @@
+/*-
+ * 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[] = "@(#)v_search.c 8.22 (Berkeley) 3/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int bcorrect __P((SCR *, EXF *, VICMDARG *, u_int));
+static int fcorrect __P((SCR *, EXF *, VICMDARG *, u_int));
+static int getptrn __P((SCR *, EXF *, int, char **));
+static int search __P((SCR *, EXF *, VICMDARG *, char *, int, enum direction));
+
+/*
+ * v_searchn -- n
+ * Repeat last search.
+ */
+int
+v_searchn(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (search(sp, ep, vp, NULL, SEARCH_MSG, sp->searchdir));
+}
+
+/*
+ * v_searchN -- N
+ * Reverse last search.
+ */
+int
+v_searchN(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum direction dir;
+
+ switch (sp->searchdir) {
+ case BACKWARD:
+ dir = FORWARD;
+ break;
+ case FORWARD:
+ dir = BACKWARD;
+ break;
+ default: /* NOTSET handled in search(). */
+ dir = sp->searchdir;
+ break;
+ }
+ return (search(sp, ep, vp, NULL, SEARCH_MSG, dir));
+}
+
+/*
+ * v_searchb -- [count]?RE[? offset]
+ * Search backward.
+ */
+int
+v_searchb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, '?', &ptrn))
+ return (1);
+ if (ptrn == NULL) {
+ F_SET(vp, VM_NOMOTION);
+ return (0);
+ }
+ }
+ return (search(sp, ep, vp, ptrn,
+ SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM, BACKWARD));
+}
+
+/*
+ * v_searchf -- [count]/RE[/ offset]
+ * Search forward.
+ */
+int
+v_searchf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, '/', &ptrn))
+ return (1);
+ if (ptrn == NULL) {
+ F_SET(vp, VM_NOMOTION);
+ return (0);
+ }
+ }
+ return (search(sp, ep, vp, ptrn,
+ SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM, FORWARD));
+}
+
+/*
+ * v_searchw -- [count]^A
+ * Search for the word under the cursor.
+ */
+int
+v_searchw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t blen, len;
+ int rval;
+ char *bp;
+
+ len = vp->kbuflen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
+ GET_SPACE_RET(sp, bp, blen, len);
+ (void)snprintf(bp, blen, "%s%s%s", RE_WSTART, vp->keyword, RE_WSTOP);
+
+ rval = search(sp, ep, vp, bp, SEARCH_MSG, FORWARD);
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+static int
+search(sp, ep, vp, ptrn, flags, dir)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ int flags;
+ char *ptrn;
+ enum direction dir;
+{
+ if (ISMOTION(vp))
+ flags |= SEARCH_EOL;
+ switch (dir) {
+ case BACKWARD:
+ if (b_search(sp, ep,
+ &vp->m_start, &vp->m_stop, ptrn, NULL, &flags))
+ return (1);
+ /* Non-motion commands move to the end of the range. */
+ if (!ISMOTION(vp))
+ vp->m_final = vp->m_stop;
+ else if (bcorrect(sp, ep, vp, flags))
+ return (1);
+ break;
+ case FORWARD:
+ if (f_search(sp, ep,
+ &vp->m_start, &vp->m_stop, ptrn, NULL, &flags))
+ return (1);
+ /* Non-motion commands move to the end of the range. */
+ if (!ISMOTION(vp))
+ vp->m_final = vp->m_stop;
+ else if (fcorrect(sp, ep, vp, flags))
+ return (1);
+ break;
+ case NOTSET:
+ msgq(sp, M_ERR, "No previous search pattern.");
+ return (1);
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * getptrn --
+ * Get the search pattern.
+ */
+static int
+getptrn(sp, ep, prompt, storep)
+ SCR *sp;
+ EXF *ep;
+ int prompt;
+ char **storep;
+{
+ TEXT *tp;
+
+ if (sp->s_get(sp, ep, &sp->tiq, prompt,
+ TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+
+ /* Len is 0 if backspaced over the prompt, 1 if only CR entered. */
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 0)
+ *storep = NULL;
+ else
+ *storep = tp->lb;
+ return (0);
+}
+
+/*
+ * !!!
+ * Historically, commands didn't affect the line searched to if the motion
+ * command was a search and the pattern match was the start or end of the
+ * line. There were some special cases, however, concerning search to the
+ * start of end of a line.
+ *
+ * Vi was not, however, consistent, and it was fairly easy to confuse it.
+ * For example, given the two lines:
+ *
+ * abcdefghi
+ * ABCDEFGHI
+ *
+ * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
+ * 'k' and put would no longer work correctly. In any case, we try to do
+ * the right thing, but it's not going to exactly match historic practice.
+ */
+
+/*
+ * bcorrect --
+ * Handle command with a backward search as the motion.
+ */
+static int
+bcorrect(sp, ep, vp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int flags;
+{
+ size_t len;
+
+ /*
+ * VC_D commands move to the end of the range. VC_Y stays at the
+ * start unless the end of the range is on a different line, when
+ * it moves to the end of the range. Ignore VC_C and VC_S.
+ */
+ if (F_ISSET(vp, VC_D) ||
+ F_ISSET(vp, VC_Y) && vp->m_start.lno != vp->m_stop.lno)
+ vp->m_final = vp->m_stop;
+ else
+ vp->m_final = vp->m_start;
+
+ /*
+ * !!!
+ * Correct backward searches which start at column 0 to be the last
+ * column of the previous line. Otherwise, adjust the starting point
+ * to the character before the current one.
+ *
+ * Backward searches become line mode operations if they start
+ * at column 0 and end at column 0 of another line.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno && vp->m_start.cno == 0) {
+ if (file_gline(sp, ep, --vp->m_start.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ if (vp->m_stop.cno == 0)
+ F_SET(vp, VM_LMODE);
+ vp->m_start.cno = len ? len - 1 : 0;
+ } else
+ --vp->m_start.cno;
+
+ /*
+ * !!!
+ * Commands become line mode operations if there was a delta
+ * specified to the search pattern.
+ */
+ if (LF_ISSET(SEARCH_DELTA)) {
+ F_SET(vp, VM_LMODE);
+ return (0);
+ }
+ return (0);
+}
+
+/*
+ * fcorrect --
+ * Handle command with a forward search as the motion.
+ */
+static int
+fcorrect(sp, ep, vp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int flags;
+{
+ size_t len;
+
+ /* VC_D and VC_Y commands stay at the start. Ignore VC_C and VC_S. */
+ vp->m_final = vp->m_start;
+
+ /*
+ * !!!
+ * Correct forward searches which end at column 0 to be the last
+ * column of the previous line. Otherwise, adjust the ending
+ * point to the character before the current one.
+ *
+ * Forward searches become line mode operations if they start
+ * at column 0 and end at column 0 of another line.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
+ if (file_gline(sp, ep, --vp->m_stop.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else
+ --vp->m_stop.cno;
+
+ /*
+ * !!!
+ * Commands become line mode operations if there was a delta
+ * specified to the search pattern.
+ */
+ if (LF_ISSET(SEARCH_DELTA)) {
+ F_SET(vp, VM_LMODE);
+ return (0);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_section.c b/usr.bin/vi/vi/v_section.c
new file mode 100644
index 000000000000..0ad8d76be282
--- /dev/null
+++ b/usr.bin/vi/vi/v_section.c
@@ -0,0 +1,201 @@
+/*-
+ * 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[] = "@(#)v_section.c 8.6 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * In historic vi, the section commands ignored empty lines, unlike the
+ * paragraph commands, which was probably okay. However, they also moved
+ * to the start of the last line when there where no more sections instead
+ * of the end of the last line like the paragraph commands. I've changed
+ * the latter behavior to match the paragraph commands.
+ *
+ * In historic vi, a "function" was defined as the first character of the
+ * line being an open brace, which could be followed by anything. This
+ * implementation follows that historic practice.
+ *
+ * !!!
+ * The historic vi documentation (USD:15-10) claimed:
+ * The section commands interpret a preceding count as a different
+ * window size in which to redraw the screen at the new location,
+ * and this window size is the base size for newly drawn windows
+ * until another size is specified. This is very useful if you are
+ * on a slow terminal ...
+ *
+ * I can't get the 4BSD vi to do this, it just beeps at me. For now, a
+ * count to the section commands simply repeats the command.
+ */
+
+static int section __P((SCR *, EXF *, VICMDARG *, int, enum direction));
+
+/*
+ * v_sectionf -- [count]]]
+ * Move forward count sections/functions.
+ */
+int
+v_sectionf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (section(sp, ep, vp, 1, FORWARD));
+}
+
+/*
+ * v_sectionb -- [count][[
+ * Move backward count sections/functions.
+ */
+int
+v_sectionb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* An empty file or starting from line 1 is always illegal. */
+ if (vp->m_start.lno <= 1) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+ return (section(sp, ep, vp, -1, BACKWARD));
+}
+
+static int
+section(sp, ep, vp, off, dir)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ int off;
+ enum direction dir;
+{
+ size_t len;
+ recno_t cnt, lno;
+ int closeok;
+ char *p, *list, *lp;
+
+ /* Get the macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ /*
+ * !!!
+ * Using ]] as a motion command was a bit special, historically.
+ * It could match } as well as the usual { and section values. If
+ * it matched a { or a section, it did NOT include the matched line.
+ * If it matched a }, it did include the line. Not a clue why.
+ */
+ closeok = ISMOTION(vp) && dir == FORWARD;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = vp->m_start.lno;
+ (p = file_gline(sp, ep, lno += off, &len)) != NULL;) {
+ if (len == 0)
+ continue;
+ if (p[0] == '{' || closeok && p[0] == '}') {
+ if (!--cnt) {
+ if (dir == FORWARD && ISMOTION(vp) &&
+ p[0] == '{' &&
+ file_gline(sp, ep, --lno, &len) == NULL)
+ return (1);
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ goto ret;
+ }
+ continue;
+ }
+ if (p[0] != '.' || len < 3)
+ continue;
+ for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' || lp[1] == p[2]) && !--cnt) {
+ if (dir == FORWARD && ISMOTION(vp) &&
+ file_gline(sp, ep, --lno, &len) == NULL)
+ return (1);
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ goto ret;
+ }
+ }
+
+ /*
+ * If moving forward, reached EOF, if moving backward, reached SOF.
+ * Both are movement sinks. The calling code has already checked
+ * for SOF, so all we check is EOF.
+ */
+ if (dir == FORWARD) {
+ if (vp->m_start.lno == lno - 1) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ vp->m_stop.lno = lno - 1;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else {
+ vp->m_stop.lno = 1;
+ vp->m_stop.cno = 0;
+ }
+
+ /*
+ * Non-motion commands go to the end of the range. If moving backward
+ * in the file, VC_D and VC_Y move to the end of the range. If moving
+ * forward in the file, VC_D and VC_Y stay at the start of the range.
+ * Ignore VC_C and VC_S.
+ *
+ * !!!
+ * Historic practice is the section cut was in line mode if it started
+ * from column 0 and was in the backward direction. I don't know why
+ * you'd want to cut an entire section in character mode, so I do it in
+ * line mode in both directions if the cut starts in column 0.
+ */
+ret: if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ vp->m_final = ISMOTION(vp) && dir == FORWARD ? vp->m_start : vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_sentence.c b/usr.bin/vi/vi/v_sentence.c
new file mode 100644
index 000000000000..678995b7057d
--- /dev/null
+++ b/usr.bin/vi/vi/v_sentence.c
@@ -0,0 +1,386 @@
+/*-
+ * 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[] = "@(#)v_sentence.c 8.12 (Berkeley) 3/15/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * In historic vi, a sentence was delimited by a '.', '?' or '!' character
+ * followed by TWO spaces or a newline. One or more empty lines was also
+ * treated as a separate sentence. The Berkeley documentation for historical
+ * vi states that any number of ')', ']', '"' and '\'' characters can be
+ * between the delimiter character and the spaces or end of line, however,
+ * the historical implementation did not handle additional '"' characters.
+ * We follow the documentation here, not the implementation.
+ *
+ * Once again, historical vi didn't do sentence movements associated with
+ * counts consistently, mostly in the presence of lines containing only
+ * white-space characters.
+ *
+ * This implementation also permits a single tab to delimit sentences, and
+ * treats lines containing only white-space characters as empty lines.
+ * Finally, tabs are eaten (along with spaces) when skipping to the start
+ * of the text following a "sentence".
+ */
+
+/*
+ * v_sentencef -- [count])
+ * Move forward count sentences.
+ */
+int
+v_sentencef(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { BLANK, NONE, PERIOD } state;
+ VCS cs;
+ size_t len;
+ u_long cnt;
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * !!!
+ * If in white-space, the next start of sentence counts as one.
+ * This may not handle " . " correctly, but it's real unclear
+ * what correctly means in that case.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0) {
+ if (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.cno != cs.cs_cno)
+ goto okret;
+ return (1);
+ }
+ }
+
+ for (state = NONE;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ if ((state == PERIOD || state == BLANK) && --cnt == 0) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 &&
+ isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ state = NONE;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */
+ if (--cnt == 0)
+ goto okret;
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0)
+ goto okret;
+ state = NONE;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ state = PERIOD;
+ break;
+ case ')':
+ case ']':
+ case '"':
+ case '\'':
+ if (state != PERIOD)
+ state = NONE;
+ break;
+ case '\t':
+ if (state == PERIOD)
+ state = BLANK;
+ /* FALLTHROUGH */
+ case ' ':
+ if (state == PERIOD) {
+ state = BLANK;
+ break;
+ }
+ if (state == BLANK && --cnt == 0) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ /* FALLTHROUGH */
+ default:
+ state = NONE;
+ break;
+ }
+ }
+
+ /* EOF is a movement sink, but it's an error not to have moved. */
+ if (vp->m_start.lno == cs.cs_lno && vp->m_start.cno == cs.cs_cno) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * Historic, uh, features, yeah, that's right, call 'em features.
+ * If the ending cursor position is at the first column in the
+ * line, i.e. the movement is cutting an entire line, the buffer
+ * is in line mode, and the ending position is the last character
+ * of the previous line.
+ *
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S. Adjust the
+ * end of the range for motion commands.
+ */
+ if (ISMOTION(vp)) {
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (file_gline(sp, ep,
+ --vp->m_stop.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ vp->m_stop.cno = len ? len - 1 : 0;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_sentenceb -- [count](
+ * Move backward count sentences.
+ */
+int
+v_sentenceb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VCS cs;
+ recno_t slno;
+ size_t len, scno;
+ u_long cnt;
+ int last;
+
+ /*
+ * !!!
+ * Historic vi permitted the user to hit SOF repeatedly.
+ */
+ if (vp->m_start.lno == 1 && vp->m_start.cno == 0)
+ return (0);
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * !!!
+ * In empty lines, skip to the previous non-white-space character.
+ * If in text, skip to the prevous white-space character. Believe
+ * it or not, in the paragraph:
+ * ab cd.
+ * AB CD.
+ * if the cursor is on the 'A' or 'B', ( moves to the 'a'. If it
+ * is on the ' ', 'C' or 'D', it moves to the 'A'. Yes, Virginia,
+ * Berkeley was once a major center of drug activity.
+ */
+ if (cs.cs_flags == CS_EMP) {
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != CS_EOL)
+ break;
+ }
+ } else if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+
+ for (last = 0;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ last = 1;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) {
+ if (--cnt == 0)
+ goto ret;
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ last = 0;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ if (!last || --cnt != 0) {
+ last = 0;
+ continue;
+ }
+
+ret: slno = cs.cs_lno;
+ scno = cs.cs_cno;
+
+ /*
+ * Move to the start of the sentence, skipping blanks
+ * and special characters.
+ */
+ do {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ } while (!cs.cs_flags &&
+ (cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\''));
+ if ((cs.cs_flags || isblank(cs.cs_ch)) &&
+ cs_fblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * If it was ". xyz", with the cursor on the 'x', or
+ * "end. ", with the cursor in the spaces, or the
+ * beginning of a sentence preceded by an empty line,
+ * we can end up where we started. Fix it.
+ */
+ if (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.cno != cs.cs_cno)
+ goto okret;
+
+ /*
+ * Well, if an empty line preceded possible blanks
+ * and the sentence, it could be a real sentence.
+ */
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOL)
+ continue;
+ if (cs.cs_flags == 0 && isblank(cs.cs_ch))
+ continue;
+ break;
+ }
+ if (cs.cs_flags == CS_EMP)
+ goto okret;
+
+ /* But it wasn't; try again. */
+ ++cnt;
+ cs.cs_lno = slno;
+ cs.cs_cno = scno;
+ last = 0;
+ break;
+ case '\t':
+ last = 1;
+ break;
+ default:
+ last =
+ cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
+ cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
+ }
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * If the starting and stopping cursor positions are at the first
+ * columns in the line, i.e. the movement is cutting an entire line,
+ * the buffer is in line mode, and the starting position is the last
+ * character of the previous line.
+ *
+ * VC_D and non-motion commands move to the end of the range.
+ * VC_Y stays at the start. Ignore VC_C and VC_S. Adjust the
+ * start of the range for motion commands.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (file_gline(sp, ep,
+ --vp->m_start.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ vp->m_start.cno = len ? len - 1 : 0;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_shift.c b/usr.bin/vi/vi/v_shift.c
new file mode 100644
index 000000000000..529e6d460e17
--- /dev/null
+++ b/usr.bin/vi/vi/v_shift.c
@@ -0,0 +1,86 @@
+/*-
+ * 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[] = "@(#)v_shift.c 8.5 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_shiftl -- [count]<motion
+ * Shift lines left.
+ */
+int
+v_shiftl(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0, "<");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_shiftr -- [count]>motion
+ * Shift lines right.
+ */
+int
+v_shiftr(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0, ">");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_status.c b/usr.bin/vi/vi/v_status.c
new file mode 100644
index 000000000000..26cb191a06dc
--- /dev/null
+++ b/usr.bin/vi/vi/v_status.c
@@ -0,0 +1,138 @@
+/*-
+ * 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[] = "@(#)v_status.c 8.12 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_status -- ^G
+ * Show the file status.
+ */
+int
+v_status(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+
+ /*
+ * ^G in historic vi reset the cursor column to the first
+ * non-blank character in the line. This doesn't seem of
+ * any usefulness whatsoever, so I don't bother.
+ */
+ return (status(sp, ep, vp->m_start.lno, 1));
+}
+
+int
+status(sp, ep, lno, showlast)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ int showlast;
+{
+ recno_t last;
+ char *mo, *nc, *nf, *ro, *pid;
+#ifdef DEBUG
+ char pbuf[50];
+
+ (void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
+ pid = pbuf;
+#else
+ pid = "";
+#endif
+ /*
+ * See nvi/exf.c:file_init() for a description of how and
+ * when the read-only bit is set. Possible displays are:
+ *
+ * new file
+ * new file, readonly
+ * [un]modified
+ * [un]modified, readonly
+ * name changed, [un]modified
+ * name changed, [un]modified, readonly
+ *
+ * !!!
+ * The historic display for "name changed" was "[Not edited]".
+ */
+ if (F_ISSET(sp->frp, FR_NEWFILE)) {
+ F_CLR(sp->frp, FR_NEWFILE);
+ nf = "new file";
+ mo = nc = "";
+ } else {
+ nf = "";
+ if (sp->frp->cname != NULL) {
+ nc = "name changed";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ ", modified" : ", unmodified";
+ } else {
+ nc = "";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ "modified" : "unmodified";
+ }
+ }
+ ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
+ if (showlast) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (last >= 1)
+ msgq(sp, M_INFO,
+ "%s: %s%s%s%s: line %lu of %lu [%ld%%]%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, lno,
+ last, (lno * 100) / last, pid);
+ else
+ msgq(sp, M_INFO, "%s: %s%s%s%s: empty file%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, pid);
+ } else
+ msgq(sp, M_INFO, "%s: %s%s%s%s: line %lu%s",
+ FILENAME(sp->frp), nf, nc, mo, ro, lno, pid);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_stop.c b/usr.bin/vi/vi/v_stop.c
new file mode 100644
index 000000000000..d6fee83cbb05
--- /dev/null
+++ b/usr.bin/vi/vi/v_stop.c
@@ -0,0 +1,74 @@
+/*-
+ * 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[] = "@(#)v_stop.c 8.7 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_stop -- ^Z
+ * Suspend vi.
+ */
+int
+v_stop(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* If autowrite is set, write out the file. */
+ if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ if (sp->s_refresh(sp, ep))
+ return (1);
+ }
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/vi/v_switch.c b/usr.bin/vi/vi/v_switch.c
new file mode 100644
index 000000000000..abdd128f0936
--- /dev/null
+++ b/usr.bin/vi/vi/v_switch.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[] = "@(#)v_switch.c 8.7 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+/*
+ * v_switch -- ^^
+ * Switch to the previous file.
+ */
+int
+v_switch(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+ char *name;
+
+ /*
+ * Try the alternate file name, then the previous file
+ * name. Use the real name, not the user's current name.
+ */
+ if (sp->alt_name != NULL)
+ name = sp->alt_name;
+ else if (sp->p_frp != NULL)
+ name = sp->p_frp->name;
+ else {
+ msgq(sp, M_ERR, "No previous file to edit.");
+ return (1);
+ }
+
+ /* If autowrite is set, write out the file. */
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ } else {
+ msgq(sp, M_ERR,
+ "Modified since last write; write or use :edit! to override.");
+ return (1);
+ }
+
+ SETCMDARG(cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, name);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_tag.c b/usr.bin/vi/vi/v_tag.c
new file mode 100644
index 000000000000..caa36f473bfc
--- /dev/null
+++ b/usr.bin/vi/vi/v_tag.c
@@ -0,0 +1,86 @@
+/*-
+ * 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[] = "@(#)v_tag.c 8.4 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_tagpush -- ^[
+ * Do a tag search on a the cursor keyword.
+ */
+int
+v_tagpush(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_TAG, 0, OOBLNO, 0, 0, vp->keyword);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_tagpop -- ^T
+ * Pop the tags stack.
+ */
+int
+v_tagpop(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ EXCMDARG cmd;
+
+ SETCMDARG(cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
diff --git a/usr.bin/vi/vi/v_text.c b/usr.bin/vi/vi/v_text.c
new file mode 100644
index 000000000000..01d17442fc00
--- /dev/null
+++ b/usr.bin/vi/vi/v_text.c
@@ -0,0 +1,848 @@
+/*-
+ * 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[] = "@(#)v_text.c 8.30 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * Repeated input in the historic vi is mostly wrong and this isn't very
+ * backward compatible. For example, if the user entered "3Aab\ncd" in
+ * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
+ * appended to the result. There was also a hack which I don't remember
+ * right now, where "3o" would open 3 lines and then let the user fill them
+ * in, to make screen movements on 300 baud modems more tolerable. I don't
+ * think it's going to be missed.
+ */
+
+#define SET_TXT_STD(sp, f) { \
+ LF_INIT((f) | TXT_CNTRLT | TXT_ESCAPE | \
+ TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \
+ if (O_ISSET(sp, O_ALTWERASE)) \
+ LF_SET(TXT_ALTWERASE); \
+ if (O_ISSET(sp, O_AUTOINDENT)) \
+ LF_SET(TXT_AUTOINDENT); \
+ if (O_ISSET(sp, O_BEAUTIFY)) \
+ LF_SET(TXT_BEAUTIFY); \
+ if (O_ISSET(sp, O_SHOWMATCH)) \
+ LF_SET(TXT_SHOWMATCH); \
+ if (O_ISSET(sp, O_WRAPMARGIN)) \
+ LF_SET(TXT_WRAPMARGIN); \
+ if (F_ISSET(sp, S_SCRIPT)) \
+ LF_SET(TXT_CR); \
+ if (O_ISSET(sp, O_TTYWERASE)) \
+ LF_SET(TXT_TTYWERASE); \
+}
+
+/*
+ * !!!
+ * There's a problem with the way that we do logging for change commands with
+ * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the
+ * starting cursor position before the change command "moves" the cursor, the
+ * cursor position to which we return on undo will be where the user entered
+ * the change command, not the start of the change. Several of the following
+ * routines re-log the cursor to make this work correctly. Historic vi tried
+ * to do the same thing, and mostly got it right. (The only spectacular way
+ * it fails is if the user entered 'o' from anywhere but the last character of
+ * the line, the undo returned the cursor to the start of the line. If the
+ * user was on the last character of the line, the cursor returned to that
+ * position.) We also check for mapped keys waiting, i.e. if we're in the
+ * middle of a map, don't bother logging the cursor.
+ */
+#define LOG_CORRECT { \
+ if (!MAPPED_KEYS_WAITING(sp)) \
+ (void)log_cursor(sp, ep); \
+}
+#define LOG_CORRECT_FIRST { \
+ if (first == 1) { \
+ LOG_CORRECT; \
+ first = 0; \
+ } \
+}
+
+static int v_CS __P((SCR *, EXF *, VICMDARG *, u_int));
+
+/*
+ * v_iA -- [count]A
+ * Append text to the end of the line.
+ */
+int
+v_iA(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /* Move the cursor to the end of the line + 1. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ /* Correct logging for implied cursor motion. */
+ if (first == 1) {
+ sp->cno = len == 0 ? 0 : len - 1;
+ LOG_CORRECT;
+ first = 0;
+ }
+
+ /* Start the change after the line. */
+ sp->cno = len;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ia -- [count]a
+ * Append text to the cursor position.
+ */
+int
+v_ia(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ u_int flags;
+ size_t len;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor one column to the right and
+ * repaint the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else if (len) {
+ if (len == sp->cno + 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ ++sp->cno;
+ } else
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iI -- [count]I
+ * Insert text at the first non-blank character in the line.
+ */
+int
+v_iI(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor to the start of the line and repaint
+ * the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ sp->cno = 0;
+ if (nonblank(sp, ep, lno, &sp->cno))
+ return (1);
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+ }
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ii -- [count]i
+ * Insert text at the cursor position.
+ */
+int
+v_ii(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ }
+ /* If len == sp->cno, it's a replay caused by a count. */
+ if (len == 0 || len == sp->cno)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * On replay, if the line isn't empty, advance the insert
+ * by one (make it an append).
+ */
+ SET_TXT_STD(sp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ if ((sp->cno = vp->m_final.cno) != 0)
+ ++sp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iO -- [count]O
+ * Insert text above this line.
+ */
+int
+v_iO(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+
+ if (file_iline(sp, ep, sp->lno, p, 0))
+ return (1);
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno + 1;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, ai_line, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_io -- [count]o
+ * Insert text after this line.
+ */
+int
+v_io(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ SET_TXT_STD(sp, TXT_APPENDEOL);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ for (first = 1,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+
+ len = 0;
+ if (file_aline(sp, ep, 1, sp->lno, p, len))
+ return (1);
+ if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno - 1;
+ }
+
+ if (v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, ai_line, flags))
+ return (1);
+
+ SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_Change -- [buffer][count]C
+ * Change line command.
+ */
+int
+v_Change(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (v_CS(sp, ep, vp, 0));
+}
+
+/*
+ * v_Subst -- [buffer][count]S
+ * Line substitute command.
+ */
+int
+v_Subst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_int flags;
+
+ /*
+ * The S command is the same as a 'C' command from the beginning
+ * of the line. This is hard to do in the parser, so do it here.
+ *
+ * If autoindent is on, the change is from the first *non-blank*
+ * character of the line, not the first character. And, to make
+ * it just a bit more exciting, the initial space is handled as
+ * auto-indent characters.
+ */
+ LF_INIT(0);
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ vp->m_start.cno = 0;
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_start.cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ } else
+ vp->m_start.cno = 0;
+ sp->cno = vp->m_start.cno;
+ return (v_CS(sp, ep, vp, flags));
+}
+
+/*
+ * v_CS --
+ * C and S commands.
+ */
+static int
+v_CS(sp, ep, vp, iflags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int iflags;
+{
+ MARK *tm;
+ recno_t lno;
+ size_t len;
+ char *p;
+ u_int flags;
+
+ SET_TXT_STD(sp, iflags);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ /*
+ * There are two cases -- if a count is supplied, we do a line
+ * mode change where we delete the lines and then insert text
+ * into a new line. Otherwise, we replace the current line.
+ */
+ vp->m_stop.lno =
+ vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (vp->m_start.lno != vp->m_stop.lno) {
+ /* Make sure that the to line is real. */
+ if (file_gline(sp, ep,
+ vp->m_stop.lno, &vp->m_stop.cno) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ if (vp->m_stop.cno != 0)
+ --vp->m_stop.cno;
+
+ /* Cut the lines. */
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_LINEMODE))
+ return (1);
+
+ /* Insert a line while we still can... */
+ if (file_iline(sp, ep, vp->m_start.lno, "", 0))
+ return (1);
+ ++vp->m_start.lno;
+ ++vp->m_stop.lno;
+
+ /* Delete the lines. */
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, 1))
+ return (1);
+
+ /* Get the inserted line. */
+ if ((p = file_gline(sp, ep, --vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ tm = NULL;
+ sp->lno = vp->m_start.lno;
+ sp->cno = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ /* The line may be empty, but that's okay. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ vp->m_stop.cno = len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0) {
+ vp->m_stop.cno = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ vp->m_stop.cno = len - 1;
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_LINEMODE))
+ return (1);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ tm = &vp->m_stop;
+ }
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT;
+
+ return (v_ntext(sp, ep,
+ &sp->tiq, tm, p, len, &vp->m_final, 0, OOBLNO, flags));
+}
+
+/*
+ * v_change -- [buffer][count]c[count]motion
+ * Change command.
+ */
+int
+v_change(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t blen, len;
+ u_int flags;
+ int lmode, rval;
+ char *bp, *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ /*
+ * Move the cursor to the start of the change. Note, if autoindent
+ * is turned on, the cc command in line mode changes from the first
+ * *non-blank* character of the line, not the first character. And,
+ * to make it just a bit more exciting, the initial space is handled
+ * as auto-indent characters.
+ */
+ lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
+ if (lmode) {
+ vp->m_start.cno = 0;
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_start.cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ }
+ }
+ sp->lno = vp->m_start.lno;
+ sp->cno = vp->m_start.cno;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT;
+
+ /*
+ * If changing within a single line, the line either currently has
+ * text or it doesn't. If it doesn't, just insert text. Otherwise,
+ * copy it and overwrite it.
+ */
+ if (vp->m_start.lno == vp->m_stop.lno) {
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (p == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ }
+ vp->m_stop.cno = len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (cut(sp, ep,
+ NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ return (v_ntext(sp, ep, &sp->tiq,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags));
+ }
+
+ /*
+ * It's trickier if changing over multiple lines. If we're in
+ * line mode we delete all of the lines and insert a replacement
+ * line which the user edits. If there was leading whitespace
+ * in the first line being changed, we copy it and use it as the
+ * replacement. If we're not in line mode, we just delete the
+ * text and start inserting.
+ *
+ * Copy the text.
+ */
+ if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /* If replacing entire lines and there's leading text. */
+ if (lmode && vp->m_start.cno) {
+ /* Get a copy of the first line changed. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ /* Copy the leading text elsewhere. */
+ GET_SPACE_RET(sp, bp, blen, vp->m_start.cno);
+ memmove(bp, p, vp->m_start.cno);
+ } else
+ bp = NULL;
+
+ /* Delete the text. */
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /* If replacing entire lines, insert a replacement line. */
+ if (lmode) {
+ if (file_iline(sp, ep, vp->m_start.lno, bp, vp->m_start.cno))
+ return (1);
+ sp->lno = vp->m_start.lno;
+ len = sp->cno = vp->m_start.cno;
+ }
+
+ /* Get the line we're editing. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ }
+
+ /* Check to see if we're appending to the line. */
+ if (vp->m_start.cno >= len)
+ LF_SET(TXT_APPENDEOL);
+
+ rval = v_ntext(sp, ep,
+ &sp->tiq, NULL, p, len, &vp->m_final, 0, OOBLNO, flags);
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * v_Replace -- [count]R
+ * Overwrite multiple characters.
+ */
+int
+v_Replace(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ if (v_ntext(sp, ep, &sp->tiq,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * Special case. The historic vi handled [count]R badly, in that R
+ * would replace some number of characters, and then the count would
+ * append count-1 copies of the replacing chars to the replaced space.
+ * This seems wrong, so this version counts R commands. There is some
+ * trickiness in moving back to where the user stopped replacing after
+ * each R command. Basically, if the user ended with a newline, we
+ * want to use vp->m_final.cno (which will be 0). Otherwise, use the
+ * column after the returned cursor, unless it would be past the end of
+ * the line, in which case we append to the line.
+ */
+ while (--cnt) {
+ if ((p = file_gline(sp, ep, vp->m_final.lno, &len)) == NULL)
+ GETLINE_ERR(sp, vp->m_final.lno);
+ SET_TXT_STD(sp, TXT_REPLAY);
+
+ sp->lno = vp->m_final.lno;
+
+ if (len == 0 || vp->m_final.cno == len - 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ sp->cno = vp->m_final.cno;
+ if (vp->m_final.cno != 0)
+ ++sp->cno;
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+
+ vp->m_stop.lno = sp->lno;
+ vp->m_stop.cno = sp->cno;
+ if (v_ntext(sp, ep, &sp->tiq,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * v_subst -- [buffer][count]s
+ * Substitute characters.
+ */
+int
+v_subst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ SET_TXT_STD(sp, 0);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno =
+ vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (vp->m_stop.cno > len - 1)
+ vp->m_stop.cno = len - 1;
+
+ if (p != NULL &&
+ cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+
+ return (v_ntext(sp, ep, &sp->tiq,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags));
+}
diff --git a/usr.bin/vi/vi/v_ulcase.c b/usr.bin/vi/vi/v_ulcase.c
new file mode 100644
index 000000000000..ab2ef1af4b19
--- /dev/null
+++ b/usr.bin/vi/vi/v_ulcase.c
@@ -0,0 +1,172 @@
+/*-
+ * 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[] = "@(#)v_ulcase.c 8.6 (Berkeley) 3/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_ulcase -- [count]~
+ * Toggle upper & lower case letters.
+ *
+ * !!!
+ * In historic vi, the count was ignored. It would have been better
+ * if there had been an associated motion, but it's too late to change
+ * it now.
+ */
+int
+v_ulcase(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t blen, lcnt, len;
+ u_long cnt;
+ int ch, change, rval;
+ char *bp, *p;
+
+ /* Get some memory. */
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /*
+ * !!!
+ * Historic vi didn't permit ~ to cross newline boundaries. I can
+ * think of no reason why it shouldn't, which at least lets the user
+ * auto-repeat through a paragraph.
+ */
+ rval = 0;
+ for (change = -1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt;) {
+ /* Get the line; EOF is an infinite sink. */
+ if ((p = file_gline(sp, ep, vp->m_stop.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno)) {
+ rval = 1;
+ goto ret;
+ }
+ if (lno >= vp->m_stop.lno) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ rval = 1;
+ goto ret;
+ }
+ if (change == -1) {
+ v_eof(sp, ep, NULL);
+ rval = 1;
+ goto ret;
+ }
+ break;
+ }
+
+ /* Set current line number. */
+ lno = vp->m_stop.lno;
+
+ /* Empty lines just decrement the count. */
+ if (len == 0) {
+ --cnt;
+ ++vp->m_stop.lno;
+ vp->m_stop.cno = 0;
+ change = 0;
+ continue;
+ }
+
+ /* Get a copy of the line. */
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+
+ /* Set starting pointer. */
+ if (change == -1)
+ p = bp + vp->m_stop.cno;
+ else
+ p = bp;
+
+ /*
+ * Figure out how many characters get changed in this
+ * line. Set the final cursor column.
+ */
+ if (vp->m_stop.cno + cnt >= len) {
+ lcnt = len - vp->m_stop.cno;
+ ++vp->m_stop.lno;
+ vp->m_stop.cno = 0;
+ } else
+ vp->m_stop.cno += lcnt = cnt;
+ cnt -= lcnt;
+
+ /* Change the line. */
+ for (change = 0; lcnt--; ++p) {
+ ch = *(u_char *)p;
+ if (islower(ch)) {
+ *p = toupper(ch);
+ change = 1;
+ } else if (isupper(ch)) {
+ *p = tolower(ch);
+ change = 1;
+ }
+ }
+
+ /* Update the line if necessary. */
+ if (change && file_sline(sp, ep, lno, bp, len)) {
+ rval = 1;
+ break;
+ }
+ }
+
+ /* If changed lines, could be on an illegal line. */
+ if (vp->m_stop.lno != lno &&
+ file_gline(sp, ep, vp->m_stop.lno, &len) == NULL) {
+ --vp->m_stop.lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ }
+ vp->m_final = vp->m_stop;
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_undo.c b/usr.bin/vi/vi/v_undo.c
new file mode 100644
index 000000000000..767be7fda304
--- /dev/null
+++ b/usr.bin/vi/vi/v_undo.c
@@ -0,0 +1,143 @@
+/*-
+ * 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[] = "@(#)v_undo.c 8.9 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Undo -- U
+ * Undo changes to this line.
+ */
+int
+v_Undo(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Historically, U reset the cursor to the first column in the line
+ * (not the first non-blank). This seems a bit non-intuitive, but,
+ * considering that we may have undone multiple changes, anything
+ * else (including the cursor position stored in the logging records)
+ * is going to appear random.
+ */
+ vp->m_final.cno = 0;
+
+ /*
+ * !!!
+ * Set up the flags so that an immediately subsequent 'u' will roll
+ * forward, instead of backward. In historic vi, a 'u' following a
+ * 'U' redid all of the changes to the line. Given that the user has
+ * explicitly discarded those changes by entering 'U', it seems likely
+ * that the user wants something between the original and end forms of
+ * the line, so starting to replay the changes seems the best way to
+ * get to there.
+ */
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+
+ return (log_setline(sp, ep));
+}
+
+/*
+ * v_undo -- u
+ * Undo the last change.
+ */
+int
+v_undo(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Set the command count. */
+ VIP(sp)->u_ccnt = sp->ccnt;
+
+ /*
+ * !!!
+ * In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u'
+ * undid the last undo. However, if there has been a change since
+ * the last undo/redo, we always do an undo. To make this work when
+ * the user can undo multiple operations, we leave the old semantic
+ * unchanged, but make '.' after a 'u' do another undo/redo operation.
+ * This has two problems.
+ *
+ * The first is that 'u' didn't set '.' in historic vi. So, if a
+ * user made a change, realized it was in the wrong place, does a
+ * 'u' to undo it, moves to the right place and then does '.', the
+ * change was reapplied. To make this work, we only apply the '.'
+ * to the undo command if it's the command immediately following an
+ * undo command. See vi/vi.c:getcmd() for the details.
+ *
+ * The second is that the traditional way to view the numbered cut
+ * buffers in vi was to enter the commands "1pu.u.u.u. which will
+ * no longer work because the '.' immediately follows the 'u' command.
+ * Since we provide a much better method of viewing buffers, and
+ * nobody can think of a better way of adding in multiple undo, this
+ * remains broken.
+ */
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+ } else if (!F_ISSET(vp, VC_ISDOT))
+ ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD;
+
+ switch (ep->lundo) {
+ case BACKWARD:
+ return (log_backward(sp, ep, &vp->m_final));
+ case FORWARD:
+ return (log_forward(sp, ep, &vp->m_final));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/vi/v_util.c b/usr.bin/vi/vi/v_util.c
new file mode 100644
index 000000000000..8959b5618af6
--- /dev/null
+++ b/usr.bin/vi/vi/v_util.c
@@ -0,0 +1,159 @@
+/*-
+ * 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[] = "@(#)v_util.c 8.8 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <unistd.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_eof --
+ * Vi end-of-file error.
+ */
+void
+v_eof(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ u_long lno;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-file.");
+ else {
+ if (file_lline(sp, ep, &lno))
+ return;
+ if (mp->lno >= lno)
+ msgq(sp, M_BERR, "Already at end-of-file.");
+ else
+ msgq(sp, M_BERR,
+ "Movement past the end-of-file.");
+ }
+}
+
+/*
+ * v_eol --
+ * Vi end-of-line error.
+ */
+void
+v_eol(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ size_t len;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-line.");
+ else {
+ if (file_gline(sp, ep, mp->lno, &len) == NULL) {
+ GETLINE_ERR(sp, mp->lno);
+ return;
+ }
+ if (mp->cno == len - 1)
+ msgq(sp, M_BERR, "Already at end-of-line.");
+ else
+ msgq(sp, M_BERR, "Movement past the end-of-line.");
+ }
+}
+
+/*
+ * v_nomove --
+ * Vi no cursor movement error.
+ */
+void
+v_nomove(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "No cursor movement made.");
+}
+
+/*
+ * v_sof --
+ * Vi start-of-file error.
+ */
+void
+v_sof(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ if (mp == NULL || mp->lno == 1)
+ msgq(sp, M_BERR, "Already at the beginning of the file.");
+ else
+ msgq(sp, M_BERR, "Movement past the beginning of the file.");
+}
+
+/*
+ * v_sol --
+ * Vi start-of-line error.
+ */
+void
+v_sol(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "Already in the first column.");
+}
+
+/*
+ * v_isempty --
+ * Return if the line contains nothing but white-space characters.
+ */
+int
+v_isempty(p, len)
+ char *p;
+ size_t len;
+{
+ for (; len--; ++p)
+ if (!isblank(*p))
+ return (0);
+ return (1);
+}
diff --git a/usr.bin/vi/vi/v_word.c b/usr.bin/vi/vi/v_word.c
new file mode 100644
index 000000000000..63f0539618b0
--- /dev/null
+++ b/usr.bin/vi/vi/v_word.c
@@ -0,0 +1,569 @@
+/*-
+ * 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[] = "@(#)v_word.c 8.18 (Berkeley) 3/15/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * There are two types of "words". Bigwords are easy -- groups of anything
+ * delimited by whitespace. Normal words are trickier. They are either a
+ * group of characters, numbers and underscores, or a group of anything but,
+ * delimited by whitespace. When for a word, if you're in whitespace, it's
+ * easy, just remove the whitespace and go to the beginning or end of the
+ * word. Otherwise, figure out if the next character is in a different group.
+ * If it is, go to the beginning or end of that group, otherwise, go to the
+ * beginning or end of the current group. The historic version of vi didn't
+ * get this right, so, for example, there were cases where "4e" was not the
+ * same as "eeee" -- in particular, single character words, and commands that
+ * began in whitespace were almost always handled incorrectly. To get it right
+ * you have to resolve the cursor after each search so that the look-ahead to
+ * figure out what type of "word" the cursor is in will be correct.
+ *
+ * Empty lines, and lines that consist of only white-space characters count
+ * as a single word, and the beginning and end of the file counts as an
+ * infinite number of words.
+ *
+ * Movements associated with commands are different than movement commands.
+ * For example, in "abc def", with the cursor on the 'a', "cw" is from
+ * 'a' to 'c', while "w" is from 'a' to 'd'. In general, trailing white
+ * space is discarded from the change movement. Another example is that,
+ * in the same string, a "cw" on any white space character replaces that
+ * single character, and nothing else. Ain't nothin' in here that's easy.
+ *
+ * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
+ * would treat groups of empty lines as individual words, i.e. the command
+ * would move the cursor to each new empty line. The 'e' and 'E' commands
+ * would treat groups of empty lines as a single word, i.e. the first use
+ * would move past the group of lines. The 'b' command would just beep at
+ * you, or, if you did it from the start of the line as part of a motion
+ * command, go absolutely nuts. If the lines contained only white-space
+ * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
+ * 'b', 'E' and 'e' commands would treat the group as a single word, and
+ * the 'B' and 'b' commands will treat the lines as individual words. This
+ * implementation treats all of these cases as a single white-space word.
+ */
+
+enum which {BIGWORD, LITTLEWORD};
+
+static int bword __P((SCR *, EXF *, VICMDARG *, enum which));
+static int eword __P((SCR *, EXF *, VICMDARG *, enum which));
+static int fword __P((SCR *, EXF *, VICMDARG *, enum which));
+
+/*
+ * v_wordW -- [count]W
+ * Move forward a bigword at a time.
+ */
+int
+v_wordW(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (fword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_wordw -- [count]w
+ * Move forward a word at a time.
+ */
+int
+v_wordw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (fword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * fword --
+ * Move forward by words.
+ */
+static int
+fword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * If in white-space:
+ * If the count is 1, and it's a change command, we're done.
+ * Else, move to the first non-white-space character, which
+ * counts as a single word move. If it's a motion command,
+ * don't move off the end of the line.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs.cs_flags != CS_EMP && cnt == 1) {
+ if (F_ISSET(vp, VC_C))
+ return (0);
+ if (F_ISSET(vp, VC_D | VC_Y)) {
+ if (cs_fspace(sp, ep, &cs))
+ return (1);
+ goto ret;
+ }
+ }
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ --cnt;
+ }
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'w' command, the definition of a word keeps
+ * switching.
+ */
+ if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * If a motion command and we're at the end of the
+ * last word, we're done. Delete and yank eat any
+ * trailing blanks, but we don't move off the end
+ * of the line regardless.
+ */
+ if (cnt == 0 && ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0 && ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /* Adjust the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+ if (ISMOTION(vp) && cs.cs_flags == 0)
+ --vp->m_stop.cno;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_wordE -- [count]E
+ * Move forward to the end of the bigword.
+ */
+int
+v_wordE(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (eword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_worde -- [count]e
+ * Move forward to the end of the word.
+ */
+int
+v_worde(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (eword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * eword --
+ * Move forward to the end of the word.
+ */
+static int
+eword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the next character is whitespace, move past
+ * it. (This doesn't count as a word move.) Stay at the character
+ * past the current one, it sets word "state" for the 'e' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'e' command, the definition of a word keeps
+ * switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the start of the word after the last
+ * word, we're done. If we changed state, back up one
+ * to the end of the previous word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_prev(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_prev(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_S.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_WordB -- [count]B
+ * Move backward a bigword at a time.
+ */
+int
+v_wordB(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (bword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_wordb -- [count]b
+ * Move backward a word at a time.
+ */
+int
+v_wordb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (bword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * bword --
+ * Move backward by words.
+ */
+static int
+bword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the previous character is whitespace, move
+ * past it. (This doesn't count as a word move.) Stay at the
+ * character before the current one, it sets word "state" for the
+ * 'b' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the beginning of the previous word -- this
+ * involves skipping over word characters and then any trailing
+ * non-word characters. Note, for the 'b' command, the definition
+ * of a word keeps switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the end of the word before the last
+ * word, we're done. If we changed state, move forward
+ * one to the end of the next word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_next(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_next(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+
+ /* If we didn't move, we must be at SOF. */
+ret: if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D commands
+ * move to the end of the range. VC_Y stays at the start unless the
+ * end of the range is on a different line, when it moves to the end
+ * of the range. Ignore VC_C and VC_S. Motion commands adjust the
+ * starting point to the character before the current one.
+ */
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp)) {
+ --vp->m_start.cno;
+ if (F_ISSET(vp, VC_Y) && vp->m_start.lno == vp->m_stop.lno)
+ vp->m_final = vp->m_start;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_xchar.c b/usr.bin/vi/vi/v_xchar.c
new file mode 100644
index 000000000000..c788db4852b2
--- /dev/null
+++ b/usr.bin/vi/vi/v_xchar.c
@@ -0,0 +1,135 @@
+/*-
+ * 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[] = "@(#)v_xchar.c 8.6 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_xchar -- [count]x
+ * Deletes the character(s) on which the cursor sits.
+ */
+int
+v_xchar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ goto nodel;
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ if (len == 0) {
+nodel: msgq(sp, M_BERR, "No characters to delete.");
+ return (1);
+ }
+
+ /*
+ * Delete from the cursor toward the end of line, w/o moving the
+ * cursor.
+ *
+ * !!!
+ * Note, "2x" at EOL isn't the same as "xx" because the left movement
+ * of the cursor as part of the 'x' command isn't taken into account.
+ * Historically correct.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ vp->m_stop.cno += vp->count - 1;
+ if (vp->m_stop.cno >= len - 1) {
+ vp->m_stop.cno = len - 1;
+ vp->m_final.cno = vp->m_start.cno ? vp->m_start.cno - 1 : 0;
+ } else
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (delete(sp, ep, &vp->m_start, &vp->m_stop, 0));
+}
+
+/*
+ * v_Xchar -- [count]X
+ * Deletes the character(s) immediately before the current cursor
+ * position.
+ */
+int
+v_Xchar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_long cnt;
+
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt >= vp->m_start.cno)
+ vp->m_start.cno = 0;
+ else
+ vp->m_start.cno -= cnt;
+ --vp->m_stop.cno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (delete(sp, ep, &vp->m_start, &vp->m_stop, 0));
+}
diff --git a/usr.bin/vi/vi/v_yank.c b/usr.bin/vi/vi/v_yank.c
new file mode 100644
index 000000000000..c5bce64dfbd8
--- /dev/null
+++ b/usr.bin/vi/vi/v_yank.c
@@ -0,0 +1,92 @@
+/*-
+ * 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[] = "@(#)v_yank.c 8.13 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_yank -- [buffer][count]Y
+ * [buffer][count]y[count][motion]
+ * Yank text (or lines of text) into a cut buffer.
+ *
+ * !!!
+ * Historic vi moved the cursor to the from MARK if it was before the current
+ * cursor and on a different line, e.g., "yj" moves the cursor but "yk" and
+ * "yh" do not. Unfortunately, it's too late to change this now. Matching
+ * the historic semantics isn't easy. The line number was always changed and
+ * column movement was usually relative. However, "y'a" moved the cursor to
+ * the first non-blank of the line marked by a, while "y`a" moved the cursor
+ * to the line and column marked by a. Hopefully, the motion component code
+ * got it right... Unlike delete, we make no adjustments here.
+ */
+int
+v_yank(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ int lmode;
+
+ /* The line may not exist in line mode cuts, check to be sure. */
+ if (F_ISSET(vp, VM_LMODE)) {
+ if (file_gline(sp, ep, vp->m_stop.lno, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ lmode = CUT_LINEMODE;
+ } else
+ lmode = 0;
+ if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ sp->rptlines[L_YANKED] += (vp->m_stop.lno - vp->m_start.lno) + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_z.c b/usr.bin/vi/vi/v_z.c
new file mode 100644
index 000000000000..ebae985f99c7
--- /dev/null
+++ b/usr.bin/vi/vi/v_z.c
@@ -0,0 +1,143 @@
+/*-
+ * 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[] = "@(#)v_z.c 8.11 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_z -- [count]z[count][-.+^<CR>]
+ * Move the screen.
+ */
+int
+v_z(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t last, lno;
+ u_int value;
+
+ /*
+ * The first count is the line to use. If the value doesn't
+ * exist, use the last line.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ lno = vp->count;
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (lno > last)
+ lno = last;
+ } else
+ lno = vp->m_start.lno;
+
+ /* Set default return cursor values. */
+ vp->m_final.lno = lno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ /*
+ * The second count is the displayed window size, i.e. the 'z'
+ * command is another way to get artificially small windows.
+ *
+ * !!!
+ * A window size of 0 was historically allowed, and simply ignored.
+ * Also, this could be much more simply done by modifying the value
+ * of the O_WINDOW option, but that's not how it worked historically.
+ */
+ if (F_ISSET(vp, VC_C2SET) &&
+ vp->count2 != 0 && sp->s_crel(sp, vp->count2))
+ return (1);
+
+ switch (vp->character) {
+ case '-': /* Put the line at the bottom. */
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ break;
+ case '.': /* Put the line in the middle. */
+ if (sp->s_fill(sp, ep, lno, P_MIDDLE))
+ return (1);
+ break;
+ default: /* Put the line at the top for <cr>. */
+ value = term_key_val(sp, vp->character);
+ if (value != K_CR && value != K_NL) {
+ msgq(sp, M_ERR, "usage: %s.", vp->kp->usage);
+ return (1);
+ }
+ /* FALLTHROUGH */
+ case '+': /* Put the line at the top. */
+ if (sp->s_fill(sp, ep, lno, P_TOP))
+ return (1);
+ break;
+ case '^': /* Print the screen before the z- screen. */
+ /*
+ * !!!
+ * Historic practice isn't real clear on this one. It seems
+ * that the command "70z^" is the same as ":70<cr>z-z^" with
+ * an off-by-one difference. So, until I find documentation
+ * to the contrary, the z^ command in this implementation
+ * displays the screen immediately before the current one.
+ * Fill the screen with the selected line at the bottom, then,
+ * scroll the screen down a page, and move to the middle line
+ * of the screen. Historic vi moved the cursor to some random
+ * place in the screen, as far as I can tell.
+ */
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ if (sp->s_down(sp, ep, &vp->m_final, sp->t_maxrows - 1, 1))
+ return (1);
+ if (sp->s_position(sp, ep, &vp->m_final, 0, P_MIDDLE))
+ return (1);
+ break;
+ }
+
+ /* If the map changes, have to redraw the entire screen. */
+ F_SET(sp, S_REDRAW);
+
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_zexit.c b/usr.bin/vi/vi/v_zexit.c
new file mode 100644
index 000000000000..053646e1383a
--- /dev/null
+++ b/usr.bin/vi/vi/v_zexit.c
@@ -0,0 +1,88 @@
+/*-
+ * 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[] = "@(#)v_zexit.c 8.7 (Berkeley) 3/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_zexit -- ZZ
+ * Save the file and exit.
+ */
+int
+v_zexit(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (F_ISSET(ep, F_MODIFIED) &&
+ file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ *
+ * Check for related screens; quit if they exist, the user will
+ * get a message on the last screen.
+ */
+ if (sp->ccnt != sp->q_ccnt + 1 &&
+ ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+ sp->q_ccnt = sp->ccnt;
+ msgq(sp, M_ERR,
+ "More files to edit; use \":n\" to go to the next file");
+ return (1);
+ }
+
+ F_SET(sp, S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/vcmd.c b/usr.bin/vi/vi/vcmd.c
new file mode 100644
index 000000000000..445f6507c4c3
--- /dev/null
+++ b/usr.bin/vi/vi/vcmd.c
@@ -0,0 +1,530 @@
+/*-
+ * 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[] = "@(#)vcmd.c 8.26 (Berkeley) 3/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * This array maps keystrokes to vi command functions. It is known
+ * in ex/ex_usage.c that it takes four columns to name a vi character.
+ */
+VIKEYS const vikeys [MAXVIKEY + 1] = {
+/* 000 NUL -- The code in vi.c expects key 0 to be undefined. */
+ {NULL},
+/* 001 ^A */
+ {v_searchw, V_ABS|V_CNT|V_MOVE|V_KEYW|VM_RCM_SET,
+ "[count]^A",
+ "^A search forward for cursor word"},
+/* 002 ^B */
+ {v_pageup, V_ABS|V_CNT|VM_RCM_SETLFNB,
+ "[count]^B",
+ "^B page up by screens"},
+/* 003 ^C */
+ {NULL, 0,
+ "^C",
+ "^C interrupt a search or global command"},
+/* 004 ^D */
+ {v_hpagedown, V_ABS|V_CNT|VM_RCM_SETLFNB,
+ "[count]^D",
+ "^D page down by half screens (setting count)"},
+/* 005 ^E */
+ {v_linedown, V_CNT,
+ "[count]^E",
+ "^E page down by lines"},
+/* 006 ^F */
+ {v_pagedown, V_ABS|V_CNT|VM_RCM_SETLFNB,
+ "[count]^F",
+ "^F page down by screens"},
+/* 007 ^G */
+ {v_status, 0,
+ "^G",
+ "^G file status"},
+/* 010 ^H */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]^H",
+ "^H move left by columns"},
+/* 011 ^I */
+ {NULL},
+/* 012 ^J */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^J",
+ "^J move down by lines"},
+/* 013 ^K */
+ {NULL},
+/* 014 ^L */
+ {v_redraw, 0,
+ "^L",
+ "^L redraw screen"},
+/* 015 ^M */
+ {v_cr, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]^M",
+ "^M move down by lines (to first non-blank)"},
+/* 016 ^N */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^N",
+ "^N move down by lines"},
+/* 017 ^O */
+ {NULL},
+/* 020 ^P */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^P",
+ "^P move up by lines"},
+/* 021 ^Q -- not available, used for hardware flow control. */
+ {NULL},
+/* 022 ^R */
+ {v_redraw, 0,
+ "^R",
+ "^R redraw screen"},
+/* 023 ^S -- not available, used for hardware flow control. */
+ {NULL},
+/* 024 ^T */
+ {v_tagpop, VM_RCM_SET,
+ "^T",
+ "^T tag pop"},
+/* 025 ^U */
+ {v_hpageup, V_ABS|V_CNT|VM_RCM_SETLFNB,
+ "[count]^U",
+ "^U half page up (set count)"},
+/* 026 ^V */
+ {NULL, 0,
+ "^V",
+ "^V input a literal character"},
+/* 027 ^W */
+ {v_screen, 0,
+ "^W",
+ "^W move to next screen"},
+/* 030 ^X */
+ {NULL},
+/* 031 ^Y */
+ {v_lineup, V_CNT,
+ "[count]^Y",
+ "^Y page up by lines"},
+/* 032 ^Z */
+ {v_stop, 0,
+ "^Z",
+ "^Z suspend editor"},
+/* 033 ^[ */
+ {NULL, 0,
+ "^[ <escape>",
+ "^[ <escape> leave input mode, return to command mode"},
+/* 034 ^\ */
+ {NULL},
+/* 035 ^] */
+ {v_tagpush, V_KEYW|VM_RCM_SET,
+ "^]",
+ "^] tag push cursor word"},
+/* 036 ^^ */
+ {v_switch, 0,
+ "^^",
+ "^^ switch to previous file"},
+/* 037 ^_ */
+ {NULL},
+/* 040 ' ' */
+ {v_right, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]' '",
+ " <space> move right by columns"},
+/* 041 ! */
+ {v_filter, V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
+ "[count]![count]motion command(s)",
+ " ! filter through command(s) to motion"},
+/* 042 " */
+ {NULL},
+/* 043 # */
+ {v_increment, V_CHAR|V_CNT|V_DOT|V_KEYNUM|VM_RCM_SET,
+ "[count]#[#+-]",
+ " # number increment/decrement"},
+/* 044 $ */
+ {v_dollar, V_CNT|V_MOVE|VM_RCM_SETLAST,
+ " [count]$",
+ " $ move to last column"},
+/* 045 % */
+ {v_match, V_ABS|V_MOVE|VM_RCM_SET,
+ "%",
+ " % move to match"},
+/* 046 & */
+ {v_again, 0,
+ "&",
+ " & repeat substitution"},
+/* 047 ' */
+ {v_fmark, V_ABS|V_CHAR|V_MOVE|VM_LMODE,
+ "'['a-z]",
+ " ' move to mark (to first non-blank)"},
+/* 050 ( */
+ {v_sentenceb, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count](",
+ " ( move back sentence"},
+/* 051 ) */
+ {v_sentencef, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count])",
+ " ) move forward sentence"},
+/* 052 * */
+ {NULL},
+/* 053 + */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]+",
+ " + move down by lines (to first non-blank)"},
+/* 054 , */
+ {v_chrrepeat, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count],",
+ " , reverse last F, f, T or t search"},
+/* 055 - */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]-",
+ " - move up by lines (to first non-blank)"},
+/* 056 . */
+ {NULL, 0,
+ ".",
+ " . repeat the last command"},
+/* 057 / */
+ {v_searchf, V_ABS|V_MOVE|VM_RCM_SET,
+ "/RE[/ offset]",
+ " / search forward"},
+/* 060 0 */
+ {v_zero, V_MOVE|VM_RCM_SET,
+ "0",
+ " 0 move to first character"},
+/* 061 1 */
+ {NULL},
+/* 062 2 */
+ {NULL},
+/* 063 3 */
+ {NULL},
+/* 064 4 */
+ {NULL},
+/* 065 5 */
+ {NULL},
+/* 066 6 */
+ {NULL},
+/* 067 7 */
+ {NULL},
+/* 070 8 */
+ {NULL},
+/* 071 9 */
+ {NULL},
+/* 072 : */
+ {v_ex, 0,
+ ":command [| command] ...",
+ " : ex command"},
+/* 073 ; */
+ {v_chrepeat, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count];",
+ " ; repeat last F, f, T or t search"},
+/* 074 < */
+ {v_shiftl, V_CNT|V_DOT|V_MOTION|VC_S|VM_RCM_SET,
+ "[count]<[count]motion",
+ " < shift lines left to motion"},
+/* 075 = */
+ {NULL},
+/* 076 > */
+ {v_shiftr, V_CNT|V_DOT|V_MOTION|VC_S|VM_RCM_SET,
+ "[count]>[count]motion",
+ " > shift lines right to motion"},
+/* 077 ? */
+ {v_searchb, V_ABS|V_MOVE|VM_RCM_SET,
+ "?RE[? offset]",
+ " ? search backward"},
+/* 100 @ */
+ {v_at, V_RBUF|VM_RCM_SET,
+ "@buffer",
+ " @ execute buffer"},
+/* 101 A */
+ {v_iA, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]A",
+ " A append to the line"},
+/* 102 B */
+ {v_wordB, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]B",
+ " B move back bigword"},
+/* 103 C */
+ {v_Change, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]C",
+ " C change to end-of-line"},
+/* 104 D */
+ {v_Delete, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]D",
+ " D delete to end-of-line"},
+/* 105 E */
+ {v_wordE, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]E",
+ " E move to end of bigword"},
+/* 106 F */
+ {v_chF, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]F character",
+ " F character in line backward search"},
+/* 107 G */
+ {v_lgoto, V_ABS|V_CNT|V_MOVE|VM_LMODE,
+ "[count]G",
+ " G move to line"},
+/* 110 H */
+ {v_home, V_ABS|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]H",
+ " H move to count lines from screen top"},
+/* 111 I */
+ {v_iI, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]I",
+ " I insert at line beginning"},
+/* 112 J */
+ {v_join, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]J",
+ " J join lines"},
+/* 113 K */
+ {NULL},
+/* 114 L */
+ {v_bottom, V_ABS|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]L",
+ " L move to screen bottom"},
+/* 115 M */
+ {v_middle, V_ABS|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "M",
+ " M move to screen middle"},
+/* 116 N */
+ {v_searchN, V_ABS|V_MOVE|VM_RCM_SET,
+ "n",
+ " N reverse last search"},
+/* 117 O */
+ {v_iO, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]O",
+ " O insert above line"},
+/* 120 P */
+ {v_Put, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer]P",
+ " P insert before cursor from buffer"},
+/* 121 Q */
+ {v_exmode, 0,
+ "Q",
+ " Q switch to ex mode"},
+/* 122 R */
+ {v_Replace, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]R",
+ " R replace characters"},
+/* 123 S */
+ {v_Subst, V_CNT|V_DOT|V_OBUF|VM_LMODE|VM_RCM_SET,
+ "[buffer][count]S",
+ " S substitute for the line(s)"},
+/* 124 T */
+ {v_chT, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]T character",
+ " T before character in line backward search"},
+/* 125 U */
+ {v_Undo, VM_RCM_SET,
+ "U",
+ " U Restore the current line"},
+/* 126 V */
+ {NULL},
+/* 127 W */
+ {v_wordW, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]W",
+ " W move to next bigword"},
+/* 130 X */
+ {v_Xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]X",
+ " X delete character before cursor"},
+/* 131 Y */
+ {v_yank, V_CNT|VM_LMODE|V_OBUF,
+ "[buffer][count]Y",
+ " Y copy line"},
+/* 132 Z */
+ {v_zexit, 0,
+ "ZZ",
+ "ZZ save file and exit"},
+/* 133 [ */
+ {v_sectionb, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[[",
+ "[[ move back section"},
+/* 134 \ */
+ {NULL},
+/* 135 ] */
+ {v_sectionf, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "]]",
+ "]] move forward section"},
+/* 136 ^ */
+ /*
+ * DON'T set the VM_RCM_SETFNB flag, the function has to do the work
+ * anyway, in case it's a motion component. DO set VM_RCM_SET, so
+ * that any motion that's part of a command is preserved.
+ */
+ {v_first, V_CNT|V_MOVE|VM_RCM_SET,
+ "^",
+ " ^ move to first non-blank"},
+/* 137 _ */
+ /*
+ * Needs both to set the VM_RCM_SETFNB flag, and to do the work
+ * in the function, in case it's a delete.
+ */
+ {v_cfirst, V_CNT|V_MOVE|VM_RCM_SETFNB,
+ "_",
+ " _ move to first non-blank"},
+/* 140 ` */
+ {v_bmark, V_ABS|V_CHAR|V_MOVE|VM_RCM_SET,
+ "`[`a-z]",
+ " ` move to mark"},
+/* 141 a */
+ {v_ia, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]a",
+ " a append after cursor"},
+/* 142 b */
+ {v_wordb, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]b",
+ " b move back word"},
+/* 143 c */
+ {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|VC_C|VM_RCM_SET,
+ "[buffer][count]c[count]motion",
+ " c change to motion"},
+/* 144 d */
+ {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|VC_D|VM_RCM_SET,
+ "[buffer][count]d[count]motion",
+ " d delete to motion"},
+/* 145 e */
+ {v_worde, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]e",
+ " e move to end of word"},
+/* 146 f */
+ {v_chf, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]f character",
+ " f character in line forward search"},
+/* 147 g */
+ {NULL},
+/* 150 h */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]h",
+ " h move left by columns"},
+/* 151 i */
+ {v_ii, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]i",
+ " i insert before cursor"},
+/* 152 j */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]j",
+ " j move down by lines"},
+/* 153 k */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]k",
+ " k move up by lines"},
+/* 154 l */
+ {v_right, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]l",
+ " l move right by columns"},
+/* 155 m */
+ {v_mark, V_CHAR,
+ "m[a-z]",
+ " m set mark"},
+/* 156 n */
+ {v_searchn, V_ABS|V_MOVE|VM_RCM_SET,
+ "n",
+ " n repeat last search"},
+/* 157 o */
+ {v_io, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]o",
+ " o append after line"},
+/* 160 p */
+ {v_put, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer]p",
+ " p insert after cursor from buffer"},
+/* 161 q */
+ {NULL},
+/* 162 r */
+ {v_replace, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]r character",
+ " r replace character"},
+/* 163 s */
+ {v_subst, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]s",
+ " s substitute character"},
+/* 164 t */
+ {v_cht, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]t character",
+ " t before character in line forward search"},
+/* 165 u */
+ /*
+ * DON'T set the V_DOT flag, it' more complicated than that.
+ * See vi/vi.c for details.
+ */
+ {v_undo, VM_RCM_SET,
+ "u",
+ " u undo last change"},
+/* 166 v */
+ {NULL},
+/* 167 w */
+ {v_wordw, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]w",
+ " w move to next word"},
+/* 170 x */
+ {v_xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]x",
+ " x delete character"},
+/* 171 y */
+ {v_yank, V_CNT|V_MOTION|V_OBUF|VC_Y|VM_RCM_SET,
+ "[buffer][count]y[count]motion",
+ " y copy text to motion into a cut buffer"},
+/* 172 z */
+ /*
+ * DON'T set the V_CHAR flag, the char isn't required,
+ * so it's handled specially in getcmd().
+ */
+ {v_z, V_CNT|VM_RCM_SETFNB,
+ "[line]z[window_size][-|.|+|^|<CR>]",
+ " z redraw window"},
+/* 173 { */
+ {v_paragraphb, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]{",
+ " { move back paragraph"},
+/* 174 | */
+ {v_ncol, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]|",
+ " | move to column"},
+/* 175 } */
+ {v_paragraphf, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]}",
+ " } move forward paragraph"},
+/* 176 ~ */
+ {v_ulcase, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]~",
+ " ~ reverse case"},
+};
diff --git a/usr.bin/vi/vi/vcmd.h b/usr.bin/vi/vi/vcmd.h
new file mode 100644
index 000000000000..f469296c1822
--- /dev/null
+++ b/usr.bin/vi/vi/vcmd.h
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vcmd.h 8.27 (Berkeley) 3/17/94
+ */
+
+typedef struct _vikeys VIKEYS;
+
+/* Structure passed around to functions implementing vi commands. */
+typedef struct _vicmdarg {
+#define vp_startzero buffer /* START ZERO OUT. */
+ CHAR_T key; /* Command key. */
+ CHAR_T buffer; /* Buffer. */
+ CHAR_T character; /* Character. */
+ u_long count; /* Count. */
+ u_long count2; /* Second count (only used by z). */
+ VIKEYS const *kp; /* VIKEYS key. */
+ size_t klen; /* Keyword length. */
+
+ /*
+ * Historic vi allowed "dl" when the cursor was on the last column,
+ * deleting the last character, and similarly allowed "dw" when
+ * the cursor was on the last column of the file. It didn't allow
+ * "dh" when the cursor was on column 1, although these cases are
+ * not strictly analogous. The point is that some movements would
+ * succeed if they were associated with a motion command, and fail
+ * otherwise. This is part of the off-by-1 schizophrenia that
+ * plagued vi. Other examples are that "dfb" deleted everything
+ * up to and including the next 'b' character, while "d/b" deleted
+ * everything up to the next 'b' character. While this implementation
+ * regularizes the interface to the extent possible, there are many
+ * special cases that can't be fixed. The special cases are handled
+ * by setting flags per command so that the underlying motion routines
+ * know what's really going on.
+ *
+ * The VC_* and VM_* flags are set in the vikeys array, and the VM_*
+ * flags may be set by the underlying functions (motion component or
+ * command) as well. For this reason, the flags in the VICMDARG and
+ * VIKEYS structures live in the same name space.
+ */
+#define VC_C 0x00000001 /* The 'c' command. */
+#define VC_D 0x00000002 /* The 'd' command. */
+#define VC_S 0x00000004 /* The '>' command. */
+#define VC_Y 0x00000008 /* The 'y' command. */
+#define VC_COMMASK 0x0000000f /* Mask for special flags. */
+#define ISMOTION(vp) F_ISSET(vp, VC_COMMASK)
+
+ /*
+ * The VM_RCM_* flags are single usage, i.e. if you set one, you have
+ * to clear the others.
+ */
+#define VM_LMODE 0x00000010 /* Motion is line oriented. */
+#define VM_NOMOTION 0x00000020 /* Motion command not entered. */
+#define VM_RCM 0x00000040 /* Use relative cursor movment (RCM). */
+#define VM_RCM_SET 0x00000080 /* RCM: set to current position. */
+#define VM_RCM_SETFNB 0x00000100 /* RCM: set to first non-blank (FNB). */
+#define VM_RCM_SETLAST 0x00000200 /* RCM: set to last character. */
+#define VM_RCM_SETLFNB 0x00000400 /* RCM: set to FNB if cursor moved. */
+#define VM_RCM_SETNNB 0x00000800 /* RCM: set to next non-blank. */
+#define VM_RCM_MASK 0x00000fc0 /* Mask for RCM flags. */
+
+ /* Flags for the underlying function. */
+#define VC_BUFFER 0x00001000 /* The buffer was set. */
+#define VC_C1RESET 0x00002000 /* Reset C1SET flag for dot commands. */
+#define VC_C1SET 0x00004000 /* Count 1 was set. */
+#define VC_C2SET 0x00008000 /* Count 2 was set. */
+#define VC_ISDOT 0x00010000 /* Command was the dot command. */
+ u_int32_t flags;
+
+#define vp_endzero keyword /* END ZERO OUT. */
+ char *keyword; /* Keyword. */
+ size_t kbuflen; /* Keyword buffer length. */
+ /*
+ * There are four cursor locations that we worry about: the initial
+ * cursor position, the start of the range, the end of the range,
+ * and the final cursor position. The initial cursor position and
+ * the start of the range are both m_start, and are always the same.
+ * All locations are initialized to the starting cursor position by
+ * the main vi routines, and the underlying functions depend on this.
+ *
+ * Commands that can be motion components set the end of the range
+ * cursor position, m_stop. All commands must set the ending cursor
+ * position, m_final. The reason that m_stop isn't the same as m_final
+ * is that there are situations where the final position of the cursor
+ * is outside of the cut/delete range (e.g. 'd[[' from the first column
+ * of a line). The final cursor position often varies based on the
+ * direction of the movement, as well as the command. The only special
+ * case that the delete code handles is that it will make adjustments
+ * if the final cursor position is deleted.
+ *
+ * The reason for all of this is that the historic vi semantics were
+ * defined command-by-command. Every function has to roll its own
+ * starting and stopping positions, and adjust if it's being used as a
+ * motion component. The general rules are as follows:
+ * 1: If not a motion component, the final cursor is at the end
+ * of the range.
+ * 2: If moving backward in the file:
+ * a: VC_D moves to the end of the range.
+ * b: If the line hasn't changed, VC_Y doesn't move, else it
+ * moves to the end of the range.
+ * 3: If moving forward in the file, VC_D and VC_Y stay at the
+ * start of the range.
+ *
+ * Usually, if moving backward in the file and it's a motion component,
+ * the starting cursor is decremented by a single character (or, in a
+ * few cases, to the end of the previous line) so that the starting
+ * cursor character isn't cut or deleted. No cursor adjustment is
+ * needed for moving forward, because the cut/delete routines handle
+ * m_stop inclusively, i.e. the last character in the range is cut or
+ * deleted. This makes cutting to the EOF/EOL reasonable.
+ *
+ * We ignore VC_C and VC_S everywhere, because the text input routines
+ * always set the cursor to the last character inserted.
+ */
+ MARK m_start; /* mark: initial cursor, range start. */
+ MARK m_stop; /* mark: range end. */
+ MARK m_final; /* mark: final cursor position. */
+} VICMDARG;
+
+/* Vi command structure. */
+struct _vikeys { /* Underlying function. */
+ int (*func) __P((SCR *, EXF *, VICMDARG *));
+#define V_ABS 0x00020000 /* Absolute movement, set '' mark. */
+#define V_CHAR 0x00040000 /* Character (required, trailing). */
+#define V_CNT 0x00080000 /* Count (optional, leading). */
+#define V_DOT 0x00100000 /* On success, sets dot command. */
+#define V_KEYNUM 0x00200000 /* Cursor referenced number. */
+#define V_KEYW 0x00400000 /* Cursor referenced word. */
+#define V_MOTION 0x00800000 /* Motion (required, trailing). */
+#define V_MOVE 0x01000000 /* Command defines movement. */
+#define V_OBUF 0x02000000 /* Buffer (optional, leading). */
+#define V_RBUF 0x04000000 /* Buffer (required, trailing). */
+ u_int32_t flags;
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+};
+#define MAXVIKEY 126 /* List of vi commands. */
+extern VIKEYS const vikeys[MAXVIKEY + 1];
+
+/* Definition of a vi "word". */
+#define inword(ch) (isalnum(ch) || (ch) == '_')
+
+/* Character stream structure, prototypes. */
+typedef struct _vcs {
+ recno_t cs_lno; /* Line. */
+ size_t cs_cno; /* Column. */
+ char *cs_bp; /* Buffer. */
+ size_t cs_len; /* Length. */
+ int cs_ch; /* Character. */
+#define CS_EMP 1 /* Empty line. */
+#define CS_EOF 2 /* End-of-file. */
+#define CS_EOL 3 /* End-of-line. */
+#define CS_SOF 4 /* Start-of-file. */
+ int cs_flags; /* Return flags. */
+} VCS;
+
+int cs_bblank __P((SCR *, EXF *, VCS *));
+int cs_fblank __P((SCR *, EXF *, VCS *));
+int cs_fspace __P((SCR *, EXF *, VCS *));
+int cs_init __P((SCR *, EXF *, VCS *));
+int cs_next __P((SCR *, EXF *, VCS *));
+int cs_prev __P((SCR *, EXF *, VCS *));
+
+ /* Character search information. */
+enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH };
+
+/* Vi private, per-screen memory. */
+typedef struct _vi_private {
+ VICMDARG sdot; /* Saved dot, motion command. */
+ VICMDARG sdotmotion;
+
+ CHAR_T rlast; /* Last 'r' command character. */
+
+ char *rep; /* Input replay buffer. */
+ size_t rep_len; /* Input replay buffer length. */
+ size_t rep_cnt; /* Input replay buffer characters. */
+
+ CHAR_T inc_lastch; /* Last increment character. */
+ long inc_lastval; /* Last increment value. */
+
+ char *paragraph; /* Paragraph search list. */
+ size_t paragraph_len; /* Paragraph search list length. */
+
+ u_long u_ccnt; /* Undo command count. */
+
+ CHAR_T lastckey; /* Last search character. */
+ enum cdirection csearchdir; /* Character search direction. */
+} VI_PRIVATE;
+
+#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private))
+
+/* Vi function prototypes. */
+int txt_auto __P((SCR *, EXF *, recno_t, TEXT *, size_t, TEXT *));
+int v_buildparagraph __P((SCR *));
+int v_end __P((SCR *));
+void v_eof __P((SCR *, EXF *, MARK *));
+void v_eol __P((SCR *, EXF *, MARK *));
+int v_exwrite __P((void *, const char *, int));
+int v_init __P((SCR *, EXF *));
+int v_isempty __P((char *, size_t));
+int v_msgflush __P((SCR *));
+void v_nomove __P((SCR *));
+int v_ntext __P((SCR *, EXF *, TEXTH *, MARK *,
+ const char *, const size_t, MARK *, int, recno_t, u_int));
+int v_optchange __P((SCR *, int));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+void v_sof __P((SCR *, MARK *));
+void v_sol __P((SCR *));
+int vi __P((SCR *, EXF *));
+
+#define VIPROTO(name) int name __P((SCR *, EXF *, VICMDARG *))
+VIPROTO(v_again);
+VIPROTO(v_at);
+VIPROTO(v_bmark);
+VIPROTO(v_bottom);
+VIPROTO(v_cfirst);
+VIPROTO(v_Change);
+VIPROTO(v_change);
+VIPROTO(v_chF);
+VIPROTO(v_chf);
+VIPROTO(v_chrepeat);
+VIPROTO(v_chrrepeat);
+VIPROTO(v_chT);
+VIPROTO(v_cht);
+VIPROTO(v_cr);
+VIPROTO(v_Delete);
+VIPROTO(v_delete);
+VIPROTO(v_dollar);
+VIPROTO(v_down);
+VIPROTO(v_ex);
+VIPROTO(v_exmode);
+VIPROTO(v_filter);
+VIPROTO(v_first);
+VIPROTO(v_fmark);
+VIPROTO(v_home);
+VIPROTO(v_hpagedown);
+VIPROTO(v_hpageup);
+VIPROTO(v_iA);
+VIPROTO(v_ia);
+VIPROTO(v_iI);
+VIPROTO(v_ii);
+VIPROTO(v_increment);
+VIPROTO(v_iO);
+VIPROTO(v_io);
+VIPROTO(v_join);
+VIPROTO(v_left);
+VIPROTO(v_lgoto);
+VIPROTO(v_linedown);
+VIPROTO(v_lineup);
+VIPROTO(v_mark);
+VIPROTO(v_match);
+VIPROTO(v_middle);
+VIPROTO(v_ncol);
+VIPROTO(v_pagedown);
+VIPROTO(v_pageup);
+VIPROTO(v_paragraphb);
+VIPROTO(v_paragraphf);
+VIPROTO(v_Put);
+VIPROTO(v_put);
+VIPROTO(v_redraw);
+VIPROTO(v_Replace);
+VIPROTO(v_replace);
+VIPROTO(v_right);
+VIPROTO(v_screen);
+VIPROTO(v_searchb);
+VIPROTO(v_searchf);
+VIPROTO(v_searchN);
+VIPROTO(v_searchn);
+VIPROTO(v_searchw);
+VIPROTO(v_sectionb);
+VIPROTO(v_sectionf);
+VIPROTO(v_sentenceb);
+VIPROTO(v_sentencef);
+VIPROTO(v_shiftl);
+VIPROTO(v_shiftr);
+VIPROTO(v_status);
+VIPROTO(v_stop);
+VIPROTO(v_Subst);
+VIPROTO(v_subst);
+VIPROTO(v_switch);
+VIPROTO(v_tagpop);
+VIPROTO(v_tagpush);
+VIPROTO(v_ulcase);
+VIPROTO(v_Undo);
+VIPROTO(v_undo);
+VIPROTO(v_up);
+VIPROTO(v_wordB);
+VIPROTO(v_wordb);
+VIPROTO(v_wordE);
+VIPROTO(v_worde);
+VIPROTO(v_wordW);
+VIPROTO(v_wordw);
+VIPROTO(v_Xchar);
+VIPROTO(v_xchar);
+VIPROTO(v_Yank);
+VIPROTO(v_yank);
+VIPROTO(v_z);
+VIPROTO(v_zero);
+VIPROTO(v_zexit);
diff --git a/usr.bin/vi/vi/vi.c b/usr.bin/vi/vi/vi.c
new file mode 100644
index 000000000000..67fac036e1a5
--- /dev/null
+++ b/usr.bin/vi/vi/vi.c
@@ -0,0 +1,801 @@
+/*-
+ * 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[] = "@(#)vi.c 8.57 (Berkeley) 3/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <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 <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int getcmd __P((SCR *, EXF *,
+ VICMDARG *, VICMDARG *, VICMDARG *, int *));
+static inline int
+ getcount __P((SCR *, ARG_CHAR_T, u_long *));
+static inline int
+ getkey __P((SCR *, CH *, u_int));
+static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int));
+static int getmotion __P((SCR *, EXF *, VICMDARG *, VICMDARG *));
+
+/*
+ * Side-effect:
+ * The dot structure can be set by the underlying vi functions,
+ * see v_Put() and v_put().
+ */
+#define DOT (&VIP(sp)->sdot)
+#define DOTMOTION (&VIP(sp)->sdotmotion)
+
+/*
+ * vi --
+ * Main vi command loop.
+ */
+int
+vi(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MARK abs;
+ VICMDARG cmd, *vp;
+ u_int flags, saved_mode;
+ int comcount, eval;
+
+ /* Start vi and paint the screen. */
+ if (v_init(sp, ep))
+ return (1);
+ if (sp->s_refresh(sp, ep)) {
+ (void)v_end(sp);
+ return (1);
+ }
+
+ /* Command initialization. */
+ memset(&cmd, 0, sizeof(VICMDARG));
+
+ for (eval = 0, vp = &cmd;;) {
+ if (!MAPPED_KEYS_WAITING(sp) && log_cursor(sp, ep))
+ goto err;
+
+ /*
+ * We get a command, which may or may not have an associated
+ * motion. If it does, we get it too, calling its underlying
+ * function to get the resulting mark. We then call the
+ * command setting the cursor to the resulting mark.
+ */
+ if (getcmd(sp, ep, DOT, vp, NULL, &comcount))
+ goto err;
+
+ /*
+ * Historical practice: if a dot command gets a new count,
+ * any motion component goes away, i.e. "d3w2." deletes a
+ * total of 5 words.
+ */
+ if (F_ISSET(vp, VC_ISDOT) && comcount)
+ DOTMOTION->count = 1;
+
+ /* Copy the key flags into the local structure. */
+ F_SET(vp, vp->kp->flags);
+
+ /* Get any associated keyword. */
+ if (F_ISSET(vp, V_KEYNUM | V_KEYW) &&
+ getkeyword(sp, ep, vp, vp->flags))
+ goto err;
+
+ /* If a non-relative movement, copy the future absolute mark. */
+ if (F_ISSET(vp, V_ABS)) {
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ }
+
+ /*
+ * Set the three cursor locations to the current cursor. The
+ * underlying routines don't bother if the cursor doesn't move.
+ * This also handles line commands (e.g. Y) defaulting to the
+ * current line.
+ */
+ vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
+ vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
+
+ /*
+ * Do any required motion; getmotion sets the from MARK and the
+ * line mode flag. We save off the RCM mask and only restore
+ * it if it no RCM flags are set by the motion command. This
+ * means that the motion command is expected to determine where
+ * the cursor ends up!
+ */
+ if (F_ISSET(vp, V_MOTION)) {
+ flags = F_ISSET(vp, VM_RCM_MASK);
+ F_CLR(vp, VM_RCM_MASK);
+ if (getmotion(sp, ep, DOTMOTION, vp))
+ goto err;
+ if (F_ISSET(vp, VM_NOMOTION))
+ goto err;
+ if (!F_ISSET(vp, VM_RCM_MASK))
+ F_SET(vp, flags);
+ }
+
+ /*
+ * If a count is set and the command is line oriented, set the
+ * to MARK here relative to the cursor/from MARK. This is for
+ * commands that take both counts and motions, i.e. "4yy" and
+ * "y%". As there's no way the command can know which the user
+ * did, we have to do it here. (There are commands that are
+ * line oriented and that take counts ("#G", "#H"), for which
+ * this calculation is either completely meaningless or wrong.
+ * Each command must validate the value for itself.
+ */
+ if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
+ vp->m_stop.lno += vp->count - 1;
+
+ /* Increment the command count. */
+ ++sp->ccnt;
+
+ /* Save the mode and call the function. */
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ if ((vp->kp->func)(sp, ep, vp))
+ goto err;
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+ msgq(sp, M_ERR,
+ "Error: vi: temporary buffer not released.");
+ return (1);
+ }
+#endif
+ /*
+ * If that command took us out of vi or changed the screen,
+ * then exit the loop without further action.
+ */
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+ /* Set the absolute mark. */
+ if (F_ISSET(vp, V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1))
+ goto err;
+
+ /* Set the dot command structure. */
+ if (F_ISSET(vp, V_DOT)) {
+ *DOT = cmd;
+ F_SET(DOT, VC_ISDOT);
+ /*
+ * If a count was supplied for both the command and
+ * its motion, the count was used only for the motion.
+ * Turn the count back on for the dot structure.
+ */
+ if (F_ISSET(vp, VC_C1RESET))
+ F_SET(DOT, VC_C1SET);
+ }
+
+ /*
+ * Some vi row movements are "attracted" to the last position
+ * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
+ * commands' candle. It's broken into two parts. Here we deal
+ * with the command flags. In sp->relative(), we deal with the
+ * screen flags. If the movement is to the EOL the vi command
+ * handles it. If it's to the beginning, we handle it here.
+ *
+ * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
+ * flag, but do the work themselves. The reason is that they
+ * have to modify the column in case they're being used as a
+ * motion component. Other similar commands (e.g. +, -) don't
+ * have to modify the column because they are always line mode
+ * operations when used as motions, so the column number isn't
+ * of any interest.
+ *
+ * Does this totally violate the screen and editor layering?
+ * You betcha. As they say, if you think you understand it,
+ * you don't.
+ */
+ switch (F_ISSET(vp, VM_RCM_MASK)) {
+ case 0:
+ case VM_RCM_SET:
+ break;
+ case VM_RCM:
+ vp->m_final.cno = sp->s_rcm(sp, ep, vp->m_final.lno);
+ break;
+ case VM_RCM_SETLAST:
+ sp->rcmflags = RCM_LAST;
+ break;
+ case VM_RCM_SETLFNB:
+ /*
+ * If we changed lines, move to the first non-blank.
+ * This is the hack that makes logical scrolling on
+ * really long lines work.
+ */
+ if (vp->m_start.lno != vp->m_final.lno) {
+ vp->m_final.cno = 0;
+ if (nonblank(sp, ep,
+ vp->m_final.lno, &vp->m_final.cno))
+ goto err;
+ sp->rcmflags = RCM_FNB;
+ }
+ break;
+ case VM_RCM_SETFNB:
+ vp->m_final.cno = 0;
+ /* FALLTHROUGH */
+ case VM_RCM_SETNNB:
+ if (nonblank(sp, ep, vp->m_final.lno, &vp->m_final.cno))
+ goto err;
+ sp->rcmflags = RCM_FNB;
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor. */
+ sp->lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+
+ if (!MAPPED_KEYS_WAITING(sp)) {
+ (void)msg_rpt(sp, 1);
+
+ if (0)
+err: term_map_flush(sp, "Vi error");
+ }
+
+ /* Refresh the screen. */
+ if (sp->s_refresh(sp, ep)) {
+ eval = 1;
+ break;
+ }
+
+ /* Set the new favorite position. */
+ if (F_ISSET(vp, VM_RCM_SET)) {
+ sp->rcmflags = 0;
+ (void)sp->s_column(sp, ep, &sp->rcm);
+ }
+ }
+
+ return (v_end(sp) || eval);
+}
+
+#define KEY(key, map) { \
+ if (getkey(sp, &ikey, map)) \
+ return (1); \
+ key = ikey.ch; \
+}
+
+/*
+ * getcmd --
+ *
+ * The command structure for vi is less complex than ex (and don't think
+ * I'm not grateful!) The command syntax is:
+ *
+ * [count] [buffer] [count] key [[motion] | [buffer] [character]]
+ *
+ * and there are several special cases. The motion value is itself a vi
+ * command, with the syntax:
+ *
+ * [count] key [character]
+ */
+static int
+getcmd(sp, ep, dp, vp, ismotion, comcountp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dp, *vp;
+ VICMDARG *ismotion; /* Previous key if getting motion component. */
+ int *comcountp;
+{
+ VIKEYS const *kp;
+ u_int flags;
+ CH ikey;
+ CHAR_T key;
+
+ /* Refresh the command structure. */
+ memset(&vp->vp_startzero, 0,
+ (char *)&vp->vp_endzero - (char *)&vp->vp_startzero);
+
+ /* An escape bells the user if in command mode. */
+ if (getkey(sp, &ikey, TXT_MAPCOMMAND)) {
+ if (ikey.value == K_ESCAPE && ismotion == NULL)
+ msgq(sp, M_BERR, "Already in command mode");
+ return (1);
+ }
+
+ key = ikey.ch;
+ if (key > MAXVIKEY) {
+ msgq(sp, M_BERR, "%s isn't a vi command", charname(sp, key));
+ return (1);
+ }
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /*
+ * Pick up optional count, where a leading 0 is not a count,
+ * it's a command.
+ */
+ if (isdigit(key) && key != '0') {
+ if (getcount(sp, key, &vp->count))
+ return (1);
+ F_SET(vp, VC_C1SET);
+ *comcountp = 1;
+ KEY(key, TXT_MAPCOMMAND);
+ } else
+ *comcountp = 0;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ if (F_ISSET(vp, VC_BUFFER)) {
+ msgq(sp, M_ERR, "Only one buffer can be specified.");
+ return (1);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /*
+ * Find the command. The only legal command with no underlying
+ * function is dot.
+ */
+ kp = vp->kp = &vikeys[vp->key = key];
+ if (kp->func == NULL) {
+ if (key != '.') {
+ msgq(sp, M_ERR,
+ "%s isn't a command", charname(sp, key));
+ return (1);
+ }
+
+ /* If called for a motion command, stop now. */
+ if (dp == NULL)
+ goto usage;
+
+ /* A repeatable command must have been executed. */
+ if (!F_ISSET(dp, VC_ISDOT)) {
+ msgq(sp, M_ERR, "No command to repeat.");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * If a '.' is immediately entered after an undo command, we
+ * replay the log instead of redoing the last command. This
+ * is necessary because 'u' can't set the dot command -- see
+ * vi/v_undo.c:v_undo for details.
+ */
+ if (VIP(sp)->u_ccnt == sp->ccnt) {
+ vp->kp = &vikeys['u'];
+ F_SET(vp, VC_ISDOT);
+ return (0);
+ }
+
+ /* Set new count/buffer, if any, and return. */
+ if (F_ISSET(vp, VC_C1SET)) {
+ F_SET(dp, VC_C1SET);
+ dp->count = vp->count;
+ }
+ if (F_ISSET(vp, VC_BUFFER))
+ dp->buffer = vp->buffer;
+ *vp = *dp;
+ return (0);
+ }
+
+ flags = kp->flags;
+
+ /* Check for illegal count. */
+ if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
+ goto usage;
+
+ /* Illegal motion command. */
+ if (ismotion == NULL) {
+ /* Illegal buffer. */
+ if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
+ goto usage;
+
+ /* Required buffer. */
+ if (LF_ISSET(V_RBUF))
+ KEY(vp->buffer, 0);
+ }
+
+ /*
+ * Special case: '[', ']' and 'Z' commands. Doesn't the fact that
+ * the *single* characters don't mean anything but the *doubled*
+ * characters do just frost your shorts?
+ */
+ if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
+ KEY(key, TXT_MAPCOMMAND);
+ if (vp->key != key) {
+usage: msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ?
+ vikeys[ismotion->key].usage : kp->usage);
+ return (1);
+ }
+ }
+ /* Special case: 'z' command. */
+ if (vp->key == 'z') {
+ KEY(vp->character, 0);
+ if (isdigit(vp->character)) {
+ if (getcount(sp, vp->character, &vp->count2))
+ return (1);
+ F_SET(vp, VC_C2SET);
+ KEY(vp->character, 0);
+ }
+ }
+
+ /*
+ * Commands that have motion components can be doubled to
+ * imply the current line.
+ */
+ if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
+ msgq(sp, M_ERR, "%s may not be used as a motion command.",
+ charname(sp, key));
+ return (1);
+ }
+
+ /* Required character. */
+ if (LF_ISSET(V_CHAR))
+ KEY(vp->character, 0);
+
+ return (0);
+}
+
+/*
+ * getmotion --
+ *
+ * Get resulting motion mark.
+ */
+static int
+getmotion(sp, ep, dm, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dm, *vp;
+{
+ MARK m;
+ VICMDARG motion;
+ size_t len;
+ u_long cnt;
+ int notused;
+
+ /* If '.' command, use the dot motion, else get the motion command. */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ motion = *dm;
+ F_SET(&motion, VC_ISDOT);
+ } else if (getcmd(sp, ep, NULL, &motion, vp, &notused))
+ return (1);
+
+ /*
+ * A count may be provided both to the command and to the motion, in
+ * which case the count is multiplicative. For example, "3y4y" is the
+ * same as "12yy". This count is provided to the motion command and
+ * not to the regular function.
+ */
+ cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
+ if (F_ISSET(vp, VC_C1SET)) {
+ motion.count *= vp->count;
+ F_SET(&motion, VC_C1SET);
+
+ /*
+ * Set flags to restore the original values of the command
+ * structure so dot commands can change the count values,
+ * e.g. "2dw" "3." deletes a total of five words.
+ */
+ F_CLR(vp, VC_C1SET);
+ F_SET(vp, VC_C1RESET);
+ }
+
+ /*
+ * Some commands can be repeated to indicate the current line. In
+ * this case, or if the command is a "line command", set the flags
+ * appropriately. If not a doubled command, run the function to get
+ * the resulting mark.
+ */
+ if (vp->key == motion.key) {
+ F_SET(vp, VM_LMODE);
+
+ /* Set the origin of the command. */
+ vp->m_start.lno = sp->lno;
+ vp->m_start.cno = 0;
+
+ /*
+ * Set the end of the command.
+ *
+ * If the current line is missing, i.e. the file is empty,
+ * historic vi permitted a "cc" or "!!" command to insert
+ * text.
+ */
+ vp->m_stop.lno = sp->lno + motion.count - 1;
+ if (file_gline(sp, ep, vp->m_stop.lno, &len) == NULL) {
+ if (vp->m_stop.lno != 1 ||
+ vp->key != 'c' && vp->key != '!') {
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ v_eof(sp, ep, &m);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ } else
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else {
+ /*
+ * Motion commands change the underlying movement (*snarl*).
+ * For example, "l" is illegal at the end of a line, but "dl"
+ * is not. Set flags so the function knows the situation.
+ */
+ F_SET(&motion, vp->kp->flags & VC_COMMASK);
+
+ /* Copy the key flags into the local structure. */
+ F_SET(&motion, motion.kp->flags);
+
+ /*
+ * Set the three cursor locations to the current cursor. This
+ * permits commands like 'j' and 'k', that are line oriented
+ * motions and have special cursor suck semantics when they are
+ * used as standalone commands, to ignore column positioning.
+ */
+ motion.m_final.lno =
+ motion.m_stop.lno = motion.m_start.lno = sp->lno;
+ motion.m_final.cno =
+ motion.m_stop.cno = motion.m_start.cno = sp->cno;
+
+ /* Run the function. */
+ if ((motion.kp->func)(sp, ep, &motion))
+ return (1);
+
+ /*
+ * Copy line mode and cursor position information from the
+ * motion command structure. The commands can flag the
+ * movement as a line motion (see v_sentence) as well as set
+ * the VM_RCM_* flags explicitly.
+ */
+ F_SET(vp,
+ F_ISSET(&motion, VM_LMODE | VM_NOMOTION | VM_RCM_MASK));
+
+ /*
+ * Motion commands can reset all of the cursor information.
+ * If the motion is in the reverse direction, switch the
+ * from and to MARK's so that it's in a forward direction.
+ * Motions are from the from MARK to the to MARK (inclusive).
+ */
+ if (motion.m_start.lno > motion.m_stop.lno ||
+ motion.m_start.lno == motion.m_stop.lno &&
+ motion.m_start.cno > motion.m_stop.cno) {
+ vp->m_start = motion.m_stop;
+ vp->m_stop = motion.m_start;
+ } else {
+ vp->m_start = motion.m_start;
+ vp->m_stop = motion.m_stop;
+ }
+ vp->m_final = motion.m_final;
+ }
+
+ /*
+ * If the command sets dot, save the motion structure. The motion
+ * count was changed above and needs to be reset, that's why this
+ * is done here, and not in the calling routine.
+ */
+ if (F_ISSET(vp->kp, V_DOT)) {
+ *dm = motion;
+ dm->count = cnt;
+ }
+ return (0);
+}
+
+#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
+
+/*
+ * getkeyword --
+ * Get the "word" the cursor is on.
+ */
+static int
+getkeyword(sp, ep, kp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *kp;
+ u_int flags;
+{
+ recno_t lno;
+ size_t beg, end, len;
+ char *p;
+
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eof(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ beg = sp->cno;
+
+ /* May not be a keyword at all. */
+ if (p == NULL || len == 0 ||
+ LF_ISSET(V_KEYW) && !inword(p[beg]) ||
+ LF_ISSET(V_KEYNUM) && !innum(p[beg]) &&
+ p[beg] != '-' && p[beg] != '+') {
+noword: msgq(sp, M_BERR, "Cursor not in a %s",
+ LF_ISSET(V_KEYW) ? "word" : "number");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Find the beginning/end of the keyword. Keywords (V_KEYW) are
+ * used for cursor-word searching and for tags. Historical vi
+ * only used the word in a tag search from the cursor to the end
+ * of the word, i.e. if the cursor was on the 'b' in " abc ", the
+ * tag was "bc". For no particular reason, we make cursor word
+ * searches follow the same rule.
+ */
+ if (beg != 0)
+ if (LF_ISSET(V_KEYW)) {
+#ifdef MOVE_TO_KEYWORD_BEGINNING
+ for (;;) {
+ --beg;
+ if (!inword(p[beg])) {
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+#endif
+ } else {
+ for (;;) {
+ --beg;
+ if (!innum(p[beg])) {
+ if (beg > 0 && p[beg - 1] == '0' &&
+ (p[beg] == 'X' || p[beg] == 'x'))
+ --beg;
+ else
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+
+ /* Skip possible leading sign. */
+ if (beg != 0 && p[beg] != '0' &&
+ (p[beg - 1] == '+' || p[beg - 1] == '-'))
+ --beg;
+ }
+
+ if (LF_ISSET(V_KEYW)) {
+ for (end = sp->cno; ++end < len && inword(p[end]););
+ --end;
+ } else {
+ for (end = sp->cno; ++end < len;) {
+ if (p[end] == 'X' || p[end] == 'x') {
+ if (end != beg + 1 || p[beg] != '0')
+ break;
+ continue;
+ }
+ if (!innum(p[end]))
+ break;
+ }
+
+ /* Just a sign isn't a number. */
+ if (end == beg && (p[beg] == '+' || p[beg] == '-'))
+ goto noword;
+ --end;
+ }
+
+ /*
+ * Getting a keyword implies moving the cursor to its beginning.
+ * Refresh now.
+ */
+ if (beg != sp->cno) {
+ sp->cno = beg;
+ sp->s_refresh(sp, ep);
+ }
+
+ /*
+ * XXX
+ * 8-bit clean problem. Numeric keywords are handled using strtol(3)
+ * and friends. This would have to be fixed in v_increment and here
+ * to not depend on a trailing NULL.
+ */
+ len = (end - beg) + 2; /* XXX */
+ kp->klen = (end - beg) + 1;
+ BINC_RET(sp, kp->keyword, kp->kbuflen, len);
+ memmove(kp->keyword, p + beg, kp->klen);
+ kp->keyword[kp->klen] = '\0'; /* XXX */
+ return (0);
+}
+
+/*
+ * getcount --
+ * Return the next count.
+ */
+static inline int
+getcount(sp, fkey, countp)
+ SCR *sp;
+ ARG_CHAR_T fkey;
+ u_long *countp;
+{
+ u_long count, tc;
+ CH ikey;
+
+ ikey.ch = fkey;
+ count = tc = 0;
+ do {
+ /* Assume that overflow results in a smaller number. */
+ tc = count * 10 + ikey.ch - '0';
+ if (count > tc) {
+ /* Toss to the next non-digit. */
+ do {
+ if (getkey(sp, &ikey,
+ TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX);
+ return (1);
+ }
+ count = tc;
+ if (getkey(sp, &ikey, TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ *countp = count;
+ return (0);
+}
+
+/*
+ * getkey --
+ * Return the next key.
+ */
+static inline int
+getkey(sp, ikeyp, map)
+ SCR *sp;
+ CH *ikeyp;
+ u_int map;
+{
+ switch (term_key(sp, ikeyp, map)) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ case INP_ERR:
+ F_SET(sp, S_EXIT_FORCE);
+ return (1);
+ }
+ return (ikeyp->value == K_ESCAPE);
+}