Changing all of the occurences of the "vi" and "ex" directory to "vi.d"
authorAndreas Schulz <ats@g386bsd.first.bmd.de>
Fri, 28 Jan 1994 01:53:56 +0000 (01:53 +0000)
committerAndreas Schulz <ats@g386bsd.first.bmd.de>
Fri, 28 Jan 1994 01:53:56 +0000 (01:53 +0000)
and "ex.d". This should prevent the problems with the "make cleandir"
and also with a make without a previous "make obj".

93 files changed:
usr.bin/vi/Makefile
usr.bin/vi/nex/ex.c [new file with mode: 0644]
usr.bin/vi/nex/ex_abbrev.c [new file with mode: 0644]
usr.bin/vi/nex/ex_append.c [new file with mode: 0644]
usr.bin/vi/nex/ex_args.c [new file with mode: 0644]
usr.bin/vi/nex/ex_argv.c [new file with mode: 0644]
usr.bin/vi/nex/ex_at.c [new file with mode: 0644]
usr.bin/vi/nex/ex_bang.c [new file with mode: 0644]
usr.bin/vi/nex/ex_cd.c [new file with mode: 0644]
usr.bin/vi/nex/ex_delete.c [new file with mode: 0644]
usr.bin/vi/nex/ex_digraph.c [new file with mode: 0644]
usr.bin/vi/nex/ex_display.c [new file with mode: 0644]
usr.bin/vi/nex/ex_edit.c [new file with mode: 0644]
usr.bin/vi/nex/ex_equal.c [new file with mode: 0644]
usr.bin/vi/nex/ex_exit.c [new file with mode: 0644]
usr.bin/vi/nex/ex_file.c [new file with mode: 0644]
usr.bin/vi/nex/ex_global.c [new file with mode: 0644]
usr.bin/vi/nex/ex_init.c [new file with mode: 0644]
usr.bin/vi/nex/ex_join.c [new file with mode: 0644]
usr.bin/vi/nex/ex_map.c [new file with mode: 0644]
usr.bin/vi/nex/ex_mark.c [new file with mode: 0644]
usr.bin/vi/nex/ex_mkexrc.c [new file with mode: 0644]
usr.bin/vi/nex/ex_move.c [new file with mode: 0644]
usr.bin/vi/nex/ex_open.c [new file with mode: 0644]
usr.bin/vi/nex/ex_preserve.c [new file with mode: 0644]
usr.bin/vi/nex/ex_print.c [new file with mode: 0644]
usr.bin/vi/nex/ex_put.c [new file with mode: 0644]
usr.bin/vi/nex/ex_read.c [new file with mode: 0644]
usr.bin/vi/nex/ex_screen.c [new file with mode: 0644]
usr.bin/vi/nex/ex_script.c [new file with mode: 0644]
usr.bin/vi/nex/ex_set.c [new file with mode: 0644]
usr.bin/vi/nex/ex_shell.c [new file with mode: 0644]
usr.bin/vi/nex/ex_shift.c [new file with mode: 0644]
usr.bin/vi/nex/ex_source.c [new file with mode: 0644]
usr.bin/vi/nex/ex_stop.c [new file with mode: 0644]
usr.bin/vi/nex/ex_subst.c [new file with mode: 0644]
usr.bin/vi/nex/ex_tag.c [new file with mode: 0644]
usr.bin/vi/nex/ex_undo.c [new file with mode: 0644]
usr.bin/vi/nex/ex_usage.c [new file with mode: 0644]
usr.bin/vi/nex/ex_util.c [new file with mode: 0644]
usr.bin/vi/nex/ex_version.c [new file with mode: 0644]
usr.bin/vi/nex/ex_visual.c [new file with mode: 0644]
usr.bin/vi/nex/ex_write.c [new file with mode: 0644]
usr.bin/vi/nex/ex_yank.c [new file with mode: 0644]
usr.bin/vi/nex/ex_z.c [new file with mode: 0644]
usr.bin/vi/nex/excmd.c [new file with mode: 0644]
usr.bin/vi/nex/excmd.h.stub [new file with mode: 0644]
usr.bin/vi/nex/filter.c [new file with mode: 0644]
usr.bin/vi/nex/script.h [new file with mode: 0644]
usr.bin/vi/nex/tag.h [new file with mode: 0644]
usr.bin/vi/nvi/getc.c [new file with mode: 0644]
usr.bin/vi/nvi/v_again.c [new file with mode: 0644]
usr.bin/vi/nvi/v_at.c [new file with mode: 0644]
usr.bin/vi/nvi/v_ch.c [new file with mode: 0644]
usr.bin/vi/nvi/v_delete.c [new file with mode: 0644]
usr.bin/vi/nvi/v_ex.c [new file with mode: 0644]
usr.bin/vi/nvi/v_exit.c [new file with mode: 0644]
usr.bin/vi/nvi/v_exmode.c [new file with mode: 0644]
usr.bin/vi/nvi/v_filter.c [new file with mode: 0644]
usr.bin/vi/nvi/v_increment.c [new file with mode: 0644]
usr.bin/vi/nvi/v_init.c [new file with mode: 0644]
usr.bin/vi/nvi/v_join.c [new file with mode: 0644]
usr.bin/vi/nvi/v_left.c [new file with mode: 0644]
usr.bin/vi/nvi/v_mark.c [new file with mode: 0644]
usr.bin/vi/nvi/v_match.c [new file with mode: 0644]
usr.bin/vi/nvi/v_ntext.c [new file with mode: 0644]
usr.bin/vi/nvi/v_paragraph.c [new file with mode: 0644]
usr.bin/vi/nvi/v_put.c [new file with mode: 0644]
usr.bin/vi/nvi/v_redraw.c [new file with mode: 0644]
usr.bin/vi/nvi/v_replace.c [new file with mode: 0644]
usr.bin/vi/nvi/v_right.c [new file with mode: 0644]
usr.bin/vi/nvi/v_screen.c [new file with mode: 0644]
usr.bin/vi/nvi/v_scroll.c [new file with mode: 0644]
usr.bin/vi/nvi/v_search.c [new file with mode: 0644]
usr.bin/vi/nvi/v_section.c [new file with mode: 0644]
usr.bin/vi/nvi/v_sentence.c [new file with mode: 0644]
usr.bin/vi/nvi/v_shift.c [new file with mode: 0644]
usr.bin/vi/nvi/v_status.c [new file with mode: 0644]
usr.bin/vi/nvi/v_stop.c [new file with mode: 0644]
usr.bin/vi/nvi/v_switch.c [new file with mode: 0644]
usr.bin/vi/nvi/v_tag.c [new file with mode: 0644]
usr.bin/vi/nvi/v_text.c [new file with mode: 0644]
usr.bin/vi/nvi/v_ulcase.c [new file with mode: 0644]
usr.bin/vi/nvi/v_undo.c [new file with mode: 0644]
usr.bin/vi/nvi/v_util.c [new file with mode: 0644]
usr.bin/vi/nvi/v_word.c [new file with mode: 0644]
usr.bin/vi/nvi/v_xchar.c [new file with mode: 0644]
usr.bin/vi/nvi/v_yank.c [new file with mode: 0644]
usr.bin/vi/nvi/v_z.c [new file with mode: 0644]
usr.bin/vi/nvi/vcmd.c [new file with mode: 0644]
usr.bin/vi/nvi/vcmd.h [new file with mode: 0644]
usr.bin/vi/nvi/vi.c [new file with mode: 0644]
usr.bin/vi/svi/svi_util.c

index 4932dbe..7bdd2fe 100644 (file)
@@ -6,9 +6,9 @@ BINDIR?=        /usr/bin
 
 #CFLAGS=-g -DDEBUG
 #CFLAGS+=-pg
 
 #CFLAGS=-g -DDEBUG
 #CFLAGS+=-pg
-CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/../include -I${.CURDIR}/../curses -I${.CURDIR}/ex -I${.CURDIR}/vi -I${.CURDIR}/../regex
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/../include -I${.CURDIR}/../curses -I${.CURDIR}/ex.d -I${.CURDIR}/vi.d -I${.CURDIR}/../regex
 #STRIP=
 #STRIP=
-.PATH: ${.CURDIR}/ex ${.CURDIR}/sex ${.CURDIR}/vi ${.CURDIR}/svi \
+.PATH: ${.CURDIR}/ex.d ${.CURDIR}/sex ${.CURDIR}/vi.d ${.CURDIR}/svi \
        ${.CURDIR}/xaw
 CLEANFILES+=ex
 
        ${.CURDIR}/xaw
 CLEANFILES+=ex
 
@@ -54,13 +54,13 @@ LDADD+=     -L${.CURDIR}/../curses/obj -L${.CURDIR}/../curses \
        -lregex -lcurses -ltermlib -lutil
 SPECHDR=excmd.h options.h
 CLEANFILES+=${SPECHDR}
        -lregex -lcurses -ltermlib -lutil
 SPECHDR=excmd.h options.h
 CLEANFILES+=${SPECHDR}
-LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view
+LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/view
 
 all: vi vi.1
 
 warn:: ${SRCS}
        -(cd ${.CURDIR} && \
 
 all: vi vi.1
 
 warn:: ${SRCS}
        -(cd ${.CURDIR} && \
-           gcc -Wall -O -DDEBUG -Iobj -Ivi -Iex -I. ${.ALLSRC} \
+           gcc -Wall -O -DDEBUG -Iobj -Ivi.d -Iex.d -I. ${.ALLSRC} \
            -lcurses -ltermlib 2>&1 | \
            sed -e "/warning: .*sccsid.*defined but not used/d" \
                -e "/warning: suggest parentheses around/d" \
            -lcurses -ltermlib 2>&1 | \
            sed -e "/warning: .*sccsid.*defined but not used/d" \
                -e "/warning: suggest parentheses around/d" \
@@ -86,13 +86,13 @@ options.h: options.h.stub options.c # Makefile
 
 excmd.h: excmd.h.stub excmd.c # Makefile
        rm -f excmd.h
 
 excmd.h: excmd.h.stub excmd.c # Makefile
        rm -f excmd.h
-       cp ${.CURDIR}/ex/excmd.h.stub excmd.h
+       cp ${.CURDIR}/ex.d/excmd.h.stub excmd.h
        chmod 664 excmd.h
        (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \
         echo 'printf("#define %s %d\n", $$2, cnt++)'; \
         echo 'next'; \
         echo '}') > /tmp/__vi.excmd.h
        chmod 664 excmd.h
        (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \
         echo 'printf("#define %s %d\n", $$2, cnt++)'; \
         echo 'next'; \
         echo '}') > /tmp/__vi.excmd.h
-        awk -f /tmp/__vi.excmd.h ${.CURDIR}/ex/excmd.c >> excmd.h
+        awk -f /tmp/__vi.excmd.h ${.CURDIR}/ex.d/excmd.c >> excmd.h
         rm -f /tmp/__vi.excmd.h
 
 .include <bsd.prog.mk>
         rm -f /tmp/__vi.excmd.h
 
 .include <bsd.prog.mk>
diff --git a/usr.bin/vi/nex/ex.c b/usr.bin/vi/nex/ex.c
new file mode 100644 (file)
index 0000000..3f5beac
--- /dev/null
@@ -0,0 +1,1523 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex.c       8.90 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static inline EXCMDLIST const *
+               ex_comm_search __P((char *, size_t));
+static int     ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *));
+static int     ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *));
+
+#define        DEFCOM  ".+1"
+
+/*
+ * ex --
+ *     Read an ex command and execute it.
+ */
+int
+ex(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       TEXT *tp;
+       u_int saved_mode;
+       int eval;
+       char defcom[sizeof(DEFCOM)];
+
+       if (ex_init(sp, ep))
+               return (1);
+
+       if (sp->s_refresh(sp, ep))
+               return (ex_end(sp));
+
+       /* If reading from a file, messages should have line info. */
+       if (!F_ISSET(sp->gp, G_ISFROMTTY)) {
+               sp->if_lno = 1;
+               sp->if_name = strdup("input");
+       }
+       for (eval = 0;; ++sp->if_lno) {
+               /* Get the next command. */
+               switch (sp->s_get(sp, ep, &sp->tiq, ':', TXT_CR | TXT_PROMPT)) {
+               case INP_OK:
+                       break;
+               case INP_EOF:
+                       F_SET(sp, S_EXIT_FORCE);
+                       goto ret;
+               case INP_ERR:
+                       continue;
+               }
+
+               saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+               tp = sp->tiq.cqh_first;
+               if (tp->len == 0) {
+                       if (F_ISSET(sp->gp, G_ISFROMTTY)) {
+                               (void)fputc('\r', stdout);
+                               (void)fflush(stdout);
+                       }
+                       memmove(defcom, DEFCOM, sizeof(DEFCOM));
+                       (void)ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1);
+               } else {
+                       if (F_ISSET(sp->gp, G_ISFROMTTY))
+                               (void)fputc('\n', stdout);
+                       (void)ex_icmd(sp, ep, tp->lb, tp->len);
+               }
+               (void)msg_rpt(sp, 0);
+
+               if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+                       break;
+
+               if (sp->s_refresh(sp, ep)) {
+                       eval = 1;
+                       break;
+               }
+       }
+ret:   if (sp->if_name != NULL) {
+               FREE(sp->if_name, strlen(sp->if_name) + 1);
+               sp->if_name = NULL;
+       }
+       return (ex_end(sp) || eval);
+}
+
+/*
+ * ex_cfile --
+ *     Execute ex commands from a file.
+ */
+int
+ex_cfile(sp, ep, filename)
+       SCR *sp;
+       EXF *ep;
+       char *filename;
+{
+       struct stat sb;
+       int fd, len, rval;
+       char *bp;
+
+       bp = NULL;
+       if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+               goto err;
+
+       /*
+        * XXX
+        * We'd like to test if the file is too big to malloc.  Since we don't
+        * know what size or type off_t's or size_t's are, what the largest
+        * unsigned integral type is, or what random insanity the local C
+        * compiler will perpetrate, doing the comparison in a portable way
+        * is flatly impossible.  Hope that malloc fails if the file is too
+        * large.
+        */
+       MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);
+       if (bp == NULL)
+               goto err;
+
+       len = read(fd, bp, (int)sb.st_size);
+       if (len == -1 || len != sb.st_size) {
+               if (len != sb.st_size)
+                       errno = EIO;
+err:           rval = 1;
+               msgq(sp, M_SYSERR, filename);
+       } else {
+               bp[sb.st_size] = '\0';          /* XXX */
+
+               /* Run the command.  Messages include file/line information. */
+               sp->if_lno = 1;
+               sp->if_name = strdup(filename);
+               rval = ex_icmd(sp, ep, bp, len);
+               FREE(sp->if_name, strlen(sp->if_name) + 1);
+               sp->if_name = NULL;
+       }
+
+       /*
+        * !!!
+        * THE UNDERLYING EXF MAY HAVE CHANGED.
+        */
+       if (bp != NULL)
+               FREE(bp, sb.st_size);
+       if (fd >= 0)
+               (void)close(fd);
+       return (rval);
+}
+
+/*
+ * ex_icmd --
+ *     Call ex_cmd() after turning off interruptible bits.
+ */
+int
+ex_icmd(sp, ep, cmd, len)
+       SCR *sp;
+       EXF *ep;
+       char *cmd;
+       size_t len;
+{
+       /*
+        * Ex goes through here for each vi :colon command and for each ex
+        * command, however, globally executed commands don't go through
+        * here, instead, they call ex_cmd directly.  So, reset all of the
+        * interruptible flags now.
+        */
+       F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE);
+
+       return (ex_cmd(sp, ep, cmd, len));
+}
+
+/* Special command structure for :s as a repeat substitution command. */
+static EXCMDLIST const cmd_subagain = 
+       {"s",           ex_subagain,    E_ADDR2|E_NORC,
+           "s",
+           "[line [,line]] s [cgr] [count] [#lp]",
+           "repeat the last subsitution"};
+
+/*
+ * ex_cmd --
+ *     Parse and execute a string containing ex commands.
+ */
+int
+ex_cmd(sp, ep, cmd, cmdlen)
+       SCR *sp;
+       EXF *ep;
+       char *cmd;
+       size_t cmdlen;
+{
+       CHAR_T vlit;
+       EX_PRIVATE *exp;
+       EXCMDARG exc;
+       EXCMDLIST const *cp;
+       MARK cur;
+       recno_t lno, num;
+       size_t arg1_len, len, save_cmdlen;
+       long flagoff;
+       u_int saved_mode;
+       int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp;
+       char *arg1, *save_cmd, *p, *t;
+
+       /* Init. */
+       nl = 0;
+loop:  if (nl) {
+               nl = 0;
+               ++sp->if_lno;
+       }
+       arg1 = NULL;
+       save_cmdlen = 0;
+
+       /*
+        * It's possible that we've been interrupted during a
+        * command.
+        */
+       if (F_ISSET(sp, S_INTERRUPTED))
+               return (0);
+
+       /* Skip whitespace, separators, newlines. */
+       for (; cmdlen > 0; ++cmd, --cmdlen)
+               if ((ch = *cmd) == '\n')
+                       ++sp->if_lno;
+               else if (!isblank(ch))
+                       break;
+       if (cmdlen == 0)
+               return (0);
+
+       /* Command lines that start with a double-quote are comments. */
+       if (ch == '"') {
+               while (--cmdlen > 0 && *++cmd != '\n');
+               if (*cmd == '\n') {
+                       ++cmd;
+                       --cmdlen;
+                       ++sp->if_lno;
+               }
+               goto loop;
+       }
+
+       /*
+        * !!!
+        * Permit extra colons at the start of the line.  Historically,
+        * ex/vi allowed a single extra one.  It's simpler not to count.
+        * The stripping is done here because, historically, any command
+        * could have preceding colons, e.g. ":g/pattern/:p" worked.
+        */
+       if (ch == ':')
+               while (--cmdlen > 0 && *++cmd == ':');
+
+       /* Skip whitespace. */
+       for (; cmdlen > 0; ++cmd, --cmdlen) {
+               ch = *cmd;
+               if (!isblank(ch))
+                       break;
+       }
+
+       /* The last point at which an empty line means do nothing. */
+       if (cmdlen == 0)
+               return (0);
+
+       /* Initialize the structure passed to underlying functions. */
+       memset(&exc, 0, sizeof(EXCMDARG));
+       exp = EXP(sp);
+       if (argv_init(sp, ep, &exc))
+               goto err;
+
+       /* Parse command addresses. */
+       if (ep_range(sp, ep, &exc, &cmd, &cmdlen))
+               goto err;
+
+       /* Skip whitespace. */
+       for (; cmdlen > 0; ++cmd, --cmdlen) {
+               ch = *cmd;
+               if (!isblank(ch))
+                       break;
+       }
+
+       /*
+        * If no command, ex does the last specified of p, l, or #, and vi
+        * moves to the line.  Otherwise, determine the length of the command
+        * name by looking for the first non-alphabetic character.  (There
+        * are a few non-alphabetic characters in command names, but they're
+        * all single character commands.)  This isn't a great test, because
+        * it means that, for the command ":e +cut.c file", we'll report that
+        * the command "cut" wasn't known.  However, it makes ":e+35 file" work
+        * correctly.
+        */
+#define        SINGLE_CHAR_COMMANDS    "!#&<=>@~"
+       if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') {
+               if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) {
+                       p = cmd;
+                       ++cmd;
+                       --cmdlen;
+                       namelen = 1;
+               } else {
+                       for (p = cmd; cmdlen > 0; --cmdlen, ++cmd)
+                               if (!isalpha(*cmd))
+                                       break;
+                       if ((namelen = cmd - p) == 0) {
+                               msgq(sp, M_ERR, "Unknown command name.");
+                               goto err;
+                       }
+               }
+
+               /*
+                * Search the table for the command.
+                *
+                * !!!
+                * Historic vi permitted the mark to immediately follow the
+                * 'k' in the 'k' command.  Make it work.
+                *
+                * !!!
+                * Historic vi permitted pretty much anything to follow the
+                * substitute command, e.g. "s/e/E/|s|sgc3p" was fine.  Make
+                * it work.
+                *
+                * Use of msgq below is safe, command names are all alphabetics.
+                */
+               if ((cp = ex_comm_search(p, namelen)) == NULL)
+                       if (p[0] == 'k' && p[1] && !p[2]) {
+                               cmd -= namelen - 1;
+                               cmdlen += namelen - 1;
+                               cp = &cmds[C_K];
+                       } else if (p[0] == 's') {
+                               cmd -= namelen - 1;
+                               cmdlen += namelen - 1;
+                               cp = &cmd_subagain;
+                       } else {
+                               msgq(sp, M_ERR,
+                                   "The %.*s command is unknown.", namelen, p);
+                               goto err;
+                       }
+
+               /* Some commands are either not implemented or turned off. */
+               if (F_ISSET(cp, E_NOPERM)) {
+                       msgq(sp, M_ERR,
+                           "The %s command is not currently supported.",
+                           cp->name);
+                       goto err;
+               }
+
+               /* Some commands aren't okay in globals. */
+               if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) {
+                       msgq(sp, M_ERR,
+               "The %s command can't be used as part of a global command.",
+                           cp->name);
+                       goto err;
+               }
+
+               /*
+                * Multiple < and > characters; another "feature".  Note,
+                * The string passed to the underlying function may not be
+                * nul terminated in this case.
+                */
+               if ((cp == &cmds[C_SHIFTL] && *p == '<') ||
+                   (cp == &cmds[C_SHIFTR] && *p == '>')) {
+                       for (ch = *p; cmdlen > 0; --cmdlen, ++cmd)
+                               if (*cmd != ch)
+                                       break;
+                       if (argv_exp0(sp, ep, &exc, p, cmd - p))
+                               goto err;
+               }
+
+               /*
+                * The visual command has a different syntax when called
+                * from ex than when called from a vi colon command.  FMH.
+                */
+               if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp))
+                       cp = &cmds[C_VISUAL_VI];
+
+               uselastcmd = 0;
+       } else {
+               cp = exp->lastcmd;
+               uselastcmd = 1;
+       }
+
+       /* Initialize local flags to the command flags. */
+       LF_INIT(cp->flags);
+
+       /*
+        * File state must be checked throughout this code, because it is
+        * called when reading the .exrc file and similar things.  There's
+        * this little chicken and egg problem -- if we read the file first,
+        * we won't know how to display it.  If we read/set the exrc stuff
+        * first, we can't allow any command that requires file state.
+        * Historic vi generally took the easy way out and dropped core.
+        */
+       if (LF_ISSET(E_NORC) && ep == NULL) {
+               msgq(sp, M_ERR,
+       "The %s command requires that a file already have been read in.",
+                   cp->name);
+               goto err;
+       }
+
+       /*
+        * There are three normal termination cases for an ex command.  They
+        * are the end of the string (cmdlen), or unescaped (by literal next
+        * characters) newline or '|' characters.  As we're past any addresses,
+        * we can now determine how long the command is, so we don't have to
+        * look for all the possible terminations.  There are three exciting
+        * special cases:
+        *
+        * 1: The bang, global, vglobal and the filter versions of the read and
+        *    write commands are delimited by newlines (they can contain shell
+        *    pipes).
+        * 2: The ex, edit and visual in vi mode commands take ex commands as
+        *    their first arguments.
+        * 3: The substitute command takes an RE as its first argument, and
+        *    wants it to be specially delimited.
+        *
+        * Historically, '|' characters in the first argument of the ex, edit,
+        * and substitute commands did not delimit the command.  And, in the
+        * filter cases for read and write, and the bang, global and vglobal
+        * commands, they did not delimit the command at all.
+        *
+        * For example, the following commands were legal:
+        *
+        *      :edit +25|s/abc/ABC/ file.c
+        *      :substitute s/|/PIPE/
+        *      :read !spell % | columnate
+        *      :global/pattern/p|l
+        *
+        * It's not quite as simple as it sounds, however.  The command:
+        *
+        *      :substitute s/a/b/|s/c/d|set
+        *
+        * was also legal, i.e. the historic ex parser (using the word loosely,
+        * since "parser" implies some regularity) delimited the RE's based on
+        * its delimiter and not anything so irretrievably vulgar as a command
+        * syntax.
+        *
+        * One thing that makes this easier is that we can ignore most of the
+        * command termination conditions for the commands that want to take
+        * the command up to the next newline.  None of them are legal in .exrc
+        * files, so if we're here, we only dealing with a single line, and we
+        * can just eat it.
+        *
+        * Anyhow, the following code makes this all work.  First, for the
+        * special cases we move past their special argument.  Then, we do
+        * normal command processing on whatever is left.  Barf-O-Rama.
+        */
+       arg1_len = 0;
+       save_cmd = cmd;
+       (void)term_key_ch(sp, K_VLNEXT, &vlit);
+       if (cp == &cmds[C_EDIT] ||
+           cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) {
+               /*
+                * Move to the next non-whitespace character.  As '+' must
+                * be the character after the command name, if there isn't
+                * one, we're done.
+                */
+               for (; cmdlen > 0; --cmdlen, ++cmd) {
+                       ch = *cmd;
+                       if (!isblank(ch))
+                               break;
+               }
+               /*
+                * QUOTING NOTE:
+                *
+                * The historic implementation ignored all escape characters
+                * so there was no way to put a space or newline into the +cmd
+                * field.  We do a simplistic job of fixing it by moving to the
+                * first whitespace character that isn't escaped by a literal
+                * next character.  The literal next characters are stripped
+                * as they're no longer useful.
+                */
+               if (cmdlen > 0 && ch == '+') {
+                       ++cmd;
+                       --cmdlen;
+                       for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+                               if ((ch = *cmd) == vlit && cmdlen > 1) {
+                                       --cmdlen;
+                                       ch = *++cmd;
+                               } else if (isblank(ch))
+                                       break;
+                               *p++ = ch;
+                       }
+                       arg1_len = cmd - arg1;
+
+                       /* Reset, so the first argument isn't reparsed. */
+                       save_cmd = cmd;
+               }
+       } else if (cp == &cmds[C_BANG] ||
+           cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) {
+               cmd += cmdlen;
+               cmdlen = 0;
+       } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) {
+               /*
+                * Move to the next character.  If it's a '!', it's a filter
+                * command and we want to eat it all, otherwise, we're done.
+                */
+               for (; cmdlen > 0; --cmdlen, ++cmd) {
+                       ch = *cmd;
+                       if (!isblank(ch))
+                               break;
+               }
+               if (cmdlen > 0 && ch == '!') {
+                       cmd += cmdlen;
+                       cmdlen = 0;
+               }
+       } else if (cp == &cmds[C_SUBSTITUTE]) {
+               /*
+                * Move to the next non-whitespace character, we'll use it as
+                * the delimiter.  If the character isn't an alphanumeric or
+                * a '|', it's the delimiter, so parse it.  Otherwise, we're
+                * into something like ":s g", so use the special substitute
+                * command.
+                */
+               for (; cmdlen > 0; --cmdlen, ++cmd)
+                       if (!isblank(cmd[0]))
+                               break;
+
+               if (isalnum(cmd[0]) || cmd[0] == '|')
+                       cp = &cmd_subagain;
+               else if (cmdlen > 0) {
+                       /*
+                        * QUOTING NOTE:
+                        *
+                        * Backslashes quote delimiter characters for RE's.
+                        * The backslashes are NOT removed since they'll be
+                        * used by the RE code.  Move to the third delimiter
+                        * that's not escaped (or the end of the command).
+                        */
+                       delim = *cmd;
+                       ++cmd;
+                       --cmdlen;
+                       for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd)
+                               if (cmd[0] == '\\' && cmdlen > 1) {
+                                       ++cmd;
+                                       --cmdlen;
+                               } else if (cmd[0] == delim)
+                                       --cnt;
+               }
+       }
+       /*
+        * Use normal quoting and termination rules to find the end
+        * of this command.
+        *
+        * QUOTING NOTE:
+        *
+        * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
+        * file.  It was almost certainly a bug, but that's what bug-for-bug
+        * compatibility means, Grasshopper.  Also, ^V's escape the command 
+        * delimiters.  Literal next quote characters in front of the newlines,
+        * '|' characters or literal next characters are stripped as as they're
+        * no longer useful.
+        */
+       for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {
+               if ((ch = cmd[0]) == vlit && cmdlen > 1) {
+                       ch = cmd[1];
+                       if (ch == '\n' || ch == '|') {
+                               if (ch == '\n')
+                                       ++sp->if_lno;
+                               --cmdlen;
+                               ++cmd;
+                               ++cnt;
+                       } else
+                               ch = vlit;
+               } else if (ch == '\n' || ch == '|') {
+                       if (ch == '\n')
+                               nl = 1;
+                       --cmdlen;
+                       break;
+               }
+               *p++ = ch;
+       }
+
+       /*
+        * Save off the next command information, go back to the
+        * original start of the command.
+        */
+       p = cmd + 1;
+       cmd = save_cmd;
+       save_cmd = p;
+       save_cmdlen = cmdlen;
+       cmdlen = ((save_cmd - cmd) - 1) - cnt;
+
+       /*
+        * !!!
+        * The "set tags" command historically used a backslash, not the
+        * user's literal next character, to escape whitespace.  Handle
+        * it here instead of complicating the argv_exp3() code.  Note,
+        * this isn't a particularly complex trap, and if backslashes were
+        * legal in set commands, this would have to be much more complicated.
+        */
+       if (cp == &cmds[C_SET])
+               for (p = cmd, len = cmdlen; len > 0; --len, ++p)
+                       if (*p == '\\')
+                               *p = vlit;
+
+       /*
+        * Set the default addresses.  It's an error to specify an address for
+        * a command that doesn't take them.  If two addresses are specified
+        * for a command that only takes one, lose the first one.  Two special
+        * cases here, some commands take 0 or 2 addresses.  For most of them
+        * (the E_ADDR2_ALL flag), 0 defaults to the entire file.  For one
+        * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
+        *
+        * Also, if the file is empty, some commands want to use an address of
+        * 0, i.e. the entire file is 0 to 0, and the default first address is
+        * 0.  Otherwise, an entire file is 1 to N and the default line is 1.
+        * Note, we also add the E_ZERO flag to the command flags, for the case
+        * where the 0 address is only valid if it's a default address.
+        *
+        * Also, set a flag if we set the default addresses.  Some commands
+        * (ex: z) care if the user specified an address of if we just used
+        * the current cursor.
+        */
+       switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) {
+       case E_ADDR1:                           /* One address: */
+               switch (exc.addrcnt) {
+               case 0:                         /* Default cursor/empty file. */
+                       exc.addrcnt = 1;
+                       F_SET(&exc, E_ADDRDEF);
+                       if (LF_ISSET(E_ZERODEF)) {
+                               if (file_lline(sp, ep, &lno))
+                                       goto err;
+                               if (lno == 0) {
+                                       exc.addr1.lno = 0;
+                                       LF_SET(E_ZERO);
+                               } else
+                                       exc.addr1.lno = sp->lno;
+                       } else
+                               exc.addr1.lno = sp->lno;
+                       exc.addr1.cno = sp->cno;
+                       break;
+               case 1:
+                       break;
+               case 2:                         /* Lose the first address. */
+                       exc.addrcnt = 1;
+                       exc.addr1 = exc.addr2;
+               }
+               break;
+       case E_ADDR2_NONE:                      /* Zero/two addresses: */
+               if (exc.addrcnt == 0)           /* Default to nothing. */
+                       break;
+               goto two;
+       case E_ADDR2_ALL:                       /* Zero/two addresses: */
+               if (exc.addrcnt == 0) {         /* Default entire/empty file. */
+                       exc.addrcnt = 2;
+                       F_SET(&exc, E_ADDRDEF);
+                       if (file_lline(sp, ep, &exc.addr2.lno))
+                               goto err;
+                       if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) {
+                               exc.addr1.lno = 0;
+                               LF_SET(E_ZERO);
+                       } else
+                               exc.addr1.lno = 1;
+                       exc.addr1.cno = exc.addr2.cno = 0;
+                       F_SET(&exc, E_ADDR2_ALL);
+                       break;
+               }
+               /* FALLTHROUGH */
+       case E_ADDR2:                           /* Two addresses: */
+two:           switch (exc.addrcnt) {
+               case 0:                         /* Default cursor/empty file. */
+                       exc.addrcnt = 2;
+                       F_SET(&exc, E_ADDRDEF);
+                       if (LF_ISSET(E_ZERODEF) && sp->lno == 1) {
+                               if (file_lline(sp, ep, &lno))
+                                       goto err;
+                               if (lno == 0) {
+                                       exc.addr1.lno = exc.addr2.lno = 0;
+                                       LF_SET(E_ZERO);
+                               } else 
+                                       exc.addr1.lno = exc.addr2.lno = sp->lno;
+                       } else
+                               exc.addr1.lno = exc.addr2.lno = sp->lno;
+                       exc.addr1.cno = exc.addr2.cno = sp->cno;
+                       break;
+               case 1:                         /* Default to first address. */
+                       exc.addrcnt = 2;
+                       exc.addr2 = exc.addr1;
+                       break;
+               case 2:
+                       break;
+               }
+               break;
+       default:
+               if (exc.addrcnt)                /* Error. */
+                       goto usage;
+       }
+
+       flagoff = 0;
+       for (p = cp->syntax; *p != '\0'; ++p) {
+               /*
+                * The write command is sensitive to leading whitespace, e.g.
+                * "write !" is different from "write!".  If not the write
+                * command, skip leading whitespace.
+                */
+               if (cp != &cmds[C_WRITE])
+                       for (; cmdlen > 0; --cmdlen, ++cmd) {
+                               ch = *cmd;
+                               if (!isblank(ch))
+                                       break;
+                       }
+
+               /*
+                * Quit when reach the end of the command, unless it's a
+                * command that does its own parsing, in which case we want
+                * to build a reasonable argv for it.  This code guarantees
+                * that there will be an argv when the function gets called,
+                * so the correct test is for a length of 0, not for the
+                * argc > 0.
+                */
+               if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's')
+                       break;
+
+               switch (*p) {
+               case '!':                               /* ! */
+                       if (*cmd == '!') {
+                               ++cmd;
+                               --cmdlen;
+                               F_SET(&exc, E_FORCE);
+                       }
+                       break;
+               case '1':                               /* +, -, #, l, p */
+                       /*
+                        * !!!
+                        * Historically, some flags were ignored depending
+                        * on where they occurred in the command line.  For
+                        * example, in the command, ":3+++p--#", historic vi
+                        * acted on the '#' flag, but ignored the '-' flags.
+                        * It's unambiguous what the flags mean, so we just
+                        * handle them regardless of the stupidity of their
+                        * location.
+                        */
+                       for (; cmdlen; --cmdlen, ++cmd)
+                               switch (*cmd) {
+                               case '+':
+                                       ++flagoff;
+                                       break;
+                               case '-':
+                                       --flagoff;
+                                       break;
+                               case '#':
+                                       F_SET(&exc, E_F_HASH);
+                                       break;
+                               case 'l':
+                                       F_SET(&exc, E_F_LIST);
+                                       break;
+                               case 'p':
+                                       F_SET(&exc, E_F_PRINT);
+                                       break;
+                               default:
+                                       goto end1;
+                               }
+end1:                  break;
+               case '2':                               /* -, ., +, ^ */
+               case '3':                               /* -, ., +, ^, = */
+                       for (; cmdlen; --cmdlen, ++cmd)
+                               switch (*cmd) {
+                               case '-':
+                                       F_SET(&exc, E_F_DASH);
+                                       break;
+                               case '.':
+                                       F_SET(&exc, E_F_DOT);
+                                       break;
+                               case '+':
+                                       F_SET(&exc, E_F_PLUS);
+                                       break;
+                               case '^':
+                                       F_SET(&exc, E_F_CARAT);
+                                       break;
+                               case '=':
+                                       if (*p == '3') {
+                                               F_SET(&exc, E_F_EQUAL);
+                                               break;
+                                       }
+                                       /* FALLTHROUGH */
+                               default:
+                                       goto end2;
+                               }
+end2:                  break;
+               case 'b':                               /* buffer */
+                       /*
+                        * Digits can't be buffer names in ex commands, or the
+                        * command "d2" would be a delete into buffer '2', and
+                        * not a two-line deletion.
+                        */
+                       if (!isdigit(cmd[0])) {
+                               exc.buffer = *cmd;
+                               ++cmd;
+                               --cmdlen;
+                               F_SET(&exc, E_BUFFER);
+                       }
+                       break;
+               case 'c':                               /* count [01+a] */
+                       ++p;
+                       if (!isdigit(*cmd) &&
+                           (*p != '+' || (*cmd != '+' && *cmd != '-')))
+                               break;
+/* 8-bit XXX */                if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') {
+                               msgq(sp, M_ERR, "Count may not be zero.");
+                               goto err;
+                       }
+                       cmdlen -= (t - cmd);
+                       cmd = t;
+                       /*
+                        * Count as address offsets occur in commands taking
+                        * two addresses.  Historic vi practice was to use
+                        * the count as an offset from the *second* address.
+                        *
+                        * Set a count flag; some underlying commands (see
+                        * join) do different things with counts than with
+                        * line addresses.
+                        */
+                       if (*p == 'a') {
+                               exc.addr1 = exc.addr2;
+                               exc.addr2.lno = exc.addr1.lno + lno - 1;
+                       } else
+                               exc.count = lno;
+                       F_SET(&exc, E_COUNT);
+                       break;
+               case 'f':                               /* file */
+                       if (argv_exp2(sp, ep,
+                           &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+                               goto err;
+                       goto countchk;
+               case 'l':                               /* line */
+                       if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+                               goto err;
+                       /* Line specifications are always required. */
+                       if (!tmp) {
+                               msgq(sp, M_ERR, 
+                                    "%s: bad line specification", cmd);
+                               goto err;
+                       }
+                       exc.lineno = cur.lno;
+                       break;
+               case 'S':                               /* string, file exp. */
+                       if (argv_exp1(sp, ep,
+                           &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+                               goto err;
+                       goto addr2;
+               case 's':                               /* string */
+                       if (argv_exp0(sp, ep, &exc, cmd, cmdlen))
+                               goto err;
+                       goto addr2;
+               case 'W':                               /* word string */
+                       /*
+                        * QUOTING NOTE:
+                        *
+                        * Literal next characters escape the following
+                        * character.  Quoting characters are stripped
+                        * here since they are no longer useful.
+                        *
+                        * First there was the word.
+                        */
+                       for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+                               if ((ch = *cmd) == vlit && cmdlen > 1) {
+                                       --cmdlen;
+                                       *p++ = *++cmd;
+                               } else if (isblank(ch)) {
+                                       ++cmd;
+                                       --cmdlen;
+                                       break;
+                               } else
+                                       *p++ = ch;
+                       }
+                       if (argv_exp0(sp, ep, &exc, t, p - t))
+                               goto err;
+                               
+                       /* Delete intervening whitespace. */
+                       for (; cmdlen > 0; --cmdlen, ++cmd) {
+                               ch = *cmd;
+                               if (!isblank(ch))
+                                       break;
+                       }
+                       if (cmdlen == 0)
+                               goto usage;
+
+                       /* Followed by the string. */
+                       for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p)
+                               if ((ch = *cmd) == vlit && cmdlen > 1) {
+                                       --cmdlen;
+                                       *p = *++cmd;
+                               } else
+                                       *p = ch;
+                       if (argv_exp0(sp, ep, &exc, t, p - t))
+                               goto err;
+                       goto addr2;
+               case 'w':                               /* word */
+                       if (argv_exp3(sp, ep, &exc, cmd, cmdlen))
+                               goto err;
+countchk:              if (*++p != 'N') {              /* N */
+                               /*
+                                * If a number is specified, must either be
+                                * 0 or that number, if optional, and that
+                                * number, if required.
+                                */
+                               num = *p - '0';
+                               if ((*++p != 'o' || exp->argsoff != 0) &&
+                                   exp->argsoff != num)
+                                       goto usage;
+                       }
+                       goto addr2;
+               default:
+                       msgq(sp, M_ERR,
+                           "Internal syntax table error (%s: %c).",
+                           cp->name, *p);
+               }
+       }
+
+       /* Skip trailing whitespace. */
+       for (; cmdlen; --cmdlen) {
+               ch = *cmd++;
+               if (!isblank(ch))
+                       break;
+       }
+
+       /*
+        * There shouldn't be anything left, and no more required
+        * fields, i.e neither 'l' or 'r' in the syntax string.
+        */
+       if (cmdlen || strpbrk(p, "lr")) {
+usage:         msgq(sp, M_ERR, "Usage: %s.", cp->usage);
+               goto err;
+       }
+
+       /* Verify that the addresses are legal. */
+addr2: switch (exc.addrcnt) {
+       case 2:
+               if (file_lline(sp, ep, &lno))
+                       goto err;
+               /*
+                * Historic ex/vi permitted commands with counts to go past
+                * EOF.  So, for example, if the file only had 5 lines, the
+                * ex command "1,6>" would fail, but the command ">300"
+                * would succeed.  Since we don't want to have to make all
+                * of the underlying commands handle random line numbers,
+                * fix it here.
+                */
+               if (exc.addr2.lno > lno)
+                       if (F_ISSET(&exc, E_COUNT))
+                               exc.addr2.lno = lno;
+                       else {
+                               if (lno == 0)
+                                       msgq(sp, M_ERR, "The file is empty.");
+                               else
+                                       msgq(sp, M_ERR,
+                                           "Only %lu line%s in the file",
+                                           lno, lno > 1 ? "s" : "");
+                               goto err;
+                       }
+               /* FALLTHROUGH */
+       case 1:
+               num = exc.addr1.lno;
+               /*
+                * If it's a "default vi command", zero is okay.  Historic
+                * vi allowed this, note, it's also the hack that allows
+                * "vi + nonexistent_file" to work.
+                */
+               if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) &&
+                   !LF_ISSET(E_ZERO)) {
+                       msgq(sp, M_ERR,
+                           "The %s command doesn't permit an address of 0.",
+                           cp->name);
+                       goto err;
+               }
+               if (file_lline(sp, ep, &lno))
+                       goto err;
+               if (num > lno) {
+                       if (lno == 0)
+                               msgq(sp, M_ERR, "The file is empty.");
+                       else
+                               msgq(sp, M_ERR, "Only %lu line%s in the file",
+                                   lno, lno > 1 ? "s" : "");
+                       goto err;
+               }
+               break;
+       }
+
+       /* If doing a default command, vi just moves to the line. */
+       if (IN_VI_MODE(sp) && uselastcmd) {
+               switch (exc.addrcnt) {
+               case 2:
+                       sp->lno = exc.addr2.lno ? exc.addr2.lno : 1;
+                       sp->cno = exc.addr2.cno;
+                       break;
+               case 1:
+                       sp->lno = exc.addr1.lno ? exc.addr1.lno : 1;
+                       sp->cno = exc.addr1.cno;
+                       break;
+               }
+               cmd = save_cmd;
+               cmdlen = save_cmdlen;
+               goto loop;
+       }
+
+       /* Reset "last" command. */
+       if (LF_ISSET(E_SETLAST))
+               exp->lastcmd = cp;
+
+       /* Final setup for the command. */
+       exc.cmd = cp;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "ex_cmd: %s", exc.cmd->name);
+       if (exc.addrcnt > 0) {
+               TRACE(sp, "\taddr1 %d", exc.addr1.lno);
+               if (exc.addrcnt > 1)
+                       TRACE(sp, " addr2: %d", exc.addr2.lno);
+               TRACE(sp, "\n");
+       }
+       if (exc.lineno)
+               TRACE(sp, "\tlineno %d", exc.lineno);
+       if (exc.flags)
+               TRACE(sp, "\tflags %0x", exc.flags);
+       if (F_ISSET(&exc, E_BUFFER))
+               TRACE(sp, "\tbuffer %c", exc.buffer);
+       TRACE(sp, "\n");
+       if (exc.argc) {
+               for (cnt = 0; cnt < exc.argc; ++cnt)
+                       TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]);
+               TRACE(sp, "\n");
+       }
+#endif
+       /* Clear autoprint flag. */
+       F_CLR(exp, EX_AUTOPRINT);
+
+       /* Increment the command count if not called from vi. */
+       if (!IN_VI_MODE(sp))
+               ++sp->ccnt;
+
+       /*
+        * If file state and not doing a global command, log the start of
+        * an action.
+        */
+       if (ep != NULL && !F_ISSET(sp, S_GLOBAL))
+               (void)log_cursor(sp, ep);
+
+       /* Save the current mode. */
+       saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+
+       /* Do the command. */
+       if ((cp->fn)(sp, ep, &exc))
+               goto err;
+
+#ifdef DEBUG
+       /* Make sure no function left the temporary space locked. */
+       if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+               F_CLR(sp->gp, G_TMP_INUSE);
+               msgq(sp, M_ERR, "Error: ex: temporary buffer not released.");
+               goto err;
+       }
+#endif
+       if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) {
+               /*
+                * Only here if the mode of the underlying file changed, e.g.
+                * the user switched files or is exiting.  There are two things
+                * that we might have to save.  First, any "+cmd" field set up
+                * for an ex/edit command will have to be saved for later, also,
+                * any not yet executed part of the current ex command.
+                *
+                *      :edit +25 file.c|s/abc/ABC/|1
+                *
+                * for example.
+                *
+                * The historic vi just hung, of course; we handle by
+                * pushing the keys onto the tty queue.  If we're
+                * switching modes to vi, since the commands are intended
+                * as ex commands, add the extra characters to make it
+                * work.
+                *
+                * For the fun of it, if you want to see if a vi clone got
+                * the ex argument parsing right, try:
+                *
+                *      echo 'foo|bar' > file1; echo 'foo/bar' > file2;
+                *      vi
+                *      :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
+                */
+               if (arg1_len == NULL && save_cmdlen == 0)
+                       return (0);
+               if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0))
+                       goto err;
+               if (save_cmdlen != 0)
+                       if (term_push(sp, save_cmd, save_cmdlen, 0, 0))
+                               goto err;
+               if (arg1 != NULL) {
+                       if (IN_VI_MODE(sp) && save_cmdlen != 0 &&
+                           term_push(sp, "|", 1, 0, 0))
+                               goto err;
+                       if (term_push(sp, arg1, arg1_len, 0, 0))
+                               goto err;
+               }
+               if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0))
+                       goto err;
+               return (0);
+       }
+
+       if (IN_EX_MODE(sp) && ep != NULL) {
+               /*
+                * The print commands have already handled the `print' flags.
+                * If so, clear them.  Don't return, autoprint may still have
+                * stuff to print out.
+                */
+                if (LF_ISSET(E_F_PRCLEAR))
+                       F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);
+
+               /*
+                * If the command was successful, and there was an explicit
+                * flag to display the new cursor line, or we're in ex mode,
+                * autoprint is set, and a change was made, display the line.
+                */
+               if (flagoff) {
+                       if (flagoff < 0) {
+                               if (sp->lno < -flagoff) {
+                                       msgq(sp, M_ERR,
+                                           "Flag offset before line 1.");
+                                       goto err;
+                               }
+                       } else {
+                               if (file_lline(sp, ep, &lno))
+                                       goto err;
+                               if (sp->lno + flagoff > lno) {
+                                       msgq(sp, M_ERR,
+                                           "Flag offset past end-of-file.");
+                                       goto err;
+                               }
+                       }
+                       sp->lno += flagoff;
+               }
+
+               if (O_ISSET(sp, O_AUTOPRINT) &&
+                   (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))
+                       LF_INIT(E_F_PRINT);
+               else
+                       LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));
+
+               memset(&exc, 0, sizeof(EXCMDARG));
+               exc.addrcnt = 2;
+               exc.addr1.lno = exc.addr2.lno = sp->lno;
+               exc.addr1.cno = exc.addr2.cno = sp->cno;
+               switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) {
+               case E_F_HASH:
+                       exc.cmd = &cmds[C_HASH];
+                       ex_number(sp, ep, &exc);
+                       break;
+               case E_F_LIST:
+                       exc.cmd = &cmds[C_LIST];
+                       ex_list(sp, ep, &exc);
+                       break;
+               case E_F_PRINT:
+                       exc.cmd = &cmds[C_PRINT];
+                       ex_pr(sp, ep, &exc);
+                       break;
+               }
+       }
+
+       cmd = save_cmd;
+       cmdlen = save_cmdlen;
+       goto loop;
+       /* NOTREACHED */
+
+       /*
+        * On error, we discard any keys we have left, as well as any keys
+        * that were mapped.  The test of save_cmdlen isn't necessarily
+        * correct.  If we fail early enough we don't know if the entire
+        * string was a single command or not.  Try and guess, it's useful
+        * to know if part of the command was discarded.
+        */
+err:   if (save_cmdlen == 0)
+               for (; cmdlen; --cmdlen)
+                       if ((ch = *cmd++) == vlit && cmdlen > 1) {
+                               --cmdlen;
+                               ++cmd;
+                       } else if (ch == '\n' || ch == '|') {
+                               if (cmdlen > 1)
+                                       save_cmdlen = 1;
+                               break;
+                       }
+       if (save_cmdlen != 0)
+               msgq(sp, M_ERR,
+                   "Ex command failed: remaining command input discarded.");
+       term_map_flush(sp, "Ex command failed");
+       return (1);
+}
+
+/*
+ * ep_range --
+ *     Get a line range for ex commands.
+ */
+static int
+ep_range(sp, ep, excp, cmdp, cmdlenp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+       char **cmdp;
+       size_t *cmdlenp;
+{
+       MARK cur, savecursor;
+       size_t cmdlen;
+       int savecursor_set, tmp;
+       char *cmd;
+
+       /* Percent character is all lines in the file. */
+       cmd = *cmdp;
+       cmdlen = *cmdlenp;
+       if (*cmd == '%') {
+               excp->addr1.lno = 1;
+               if (file_lline(sp, ep, &excp->addr2.lno))
+                       return (1);
+
+               /* If an empty file, then the first line is 0, not 1. */
+               if (excp->addr2.lno == 0)
+                       excp->addr1.lno = 0;
+               excp->addr1.cno = excp->addr2.cno = 0;
+               excp->addrcnt = 2;
+
+               ++*cmdp;
+               --*cmdlenp;
+               return (0);
+       }
+
+       /* Parse comma or semi-colon delimited line specs. */
+       for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;)
+               switch (*cmd) {
+               case ';':               /* Semi-colon delimiter. */
+                       /*
+                        * Comma delimiters delimit; semi-colon delimiters
+                        * change the current address for the 2nd address
+                        * to be the first address.  Trailing or multiple
+                        * delimiters are discarded.
+                        */
+                       if (excp->addrcnt == 0)
+                               goto done;
+                       if (!savecursor_set) {
+                               savecursor.lno = sp->lno;
+                               savecursor.cno = sp->cno;
+                               sp->lno = excp->addr1.lno;
+                               sp->cno = excp->addr1.cno;
+                               savecursor_set = 1;
+                       }
+                       ++cmd;
+                       --cmdlen;
+                       break;
+               case ',':               /* Comma delimiter. */
+                       /* If no addresses yet, defaults to ".". */
+                       if (excp->addrcnt == 0) {
+                               excp->addr1.lno = sp->lno;
+                               excp->addr1.cno = sp->cno;
+                               excp->addrcnt = 1;
+                       }
+                       /* FALLTHROUGH */
+               case ' ':               /* Whitespace. */
+               case '\t':              /* Whitespace. */
+                       ++cmd;
+                       --cmdlen;
+                       break;
+               default:
+                       if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+                               return (1);
+                       if (!tmp)
+                               goto done;
+
+                       /*
+                        * Extra addresses are discarded, starting with
+                        * the first.
+                        */
+                       switch (excp->addrcnt) {
+                       case 0:
+                               excp->addr1 = cur;
+                               excp->addrcnt = 1;
+                               break;
+                       case 1:
+                               excp->addr2 = cur;
+                               excp->addrcnt = 2;
+                               break;
+                       case 2:
+                               excp->addr1 = excp->addr2;
+                               excp->addr2 = cur;
+                               break;
+                       }
+                       break;
+               }
+
+       /*
+        * XXX
+        * This is probably not right behavior for savecursor -- need
+        * to figure out what the historical ex did for ";,;,;5p" or
+        * similar stupidity.
+        */
+done:  if (savecursor_set) {
+               sp->lno = savecursor.lno;
+               sp->cno = savecursor.cno;
+       }
+       if (excp->addrcnt == 2 &&
+           (excp->addr2.lno < excp->addr1.lno ||
+           excp->addr2.lno == excp->addr1.lno &&
+           excp->addr2.cno < excp->addr1.cno)) {
+               msgq(sp, M_ERR,
+                   "The second address is smaller than the first.");
+               return (1);
+       }
+       *cmdp = cmd;
+       *cmdlenp = cmdlen;
+       return (0);
+}
+
+/*
+ * Get a single line address specifier.
+ */
+static int
+ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found)
+       SCR *sp;
+       EXF *ep;
+       MARK *cur;
+       char **cmdp;
+       size_t *cmdlenp;
+       int *addr_found;
+{
+       MARK m, *mp;
+       long total;
+       u_int flags;
+       size_t cmdlen;
+       char *cmd, *endp;
+
+       *addr_found = 0;
+
+       cmd = *cmdp;
+       cmdlen = *cmdlenp;
+       switch (*cmd) {
+       case '$':                               /* Last line in the file. */
+               *addr_found = 1;
+               cur->cno = 0;
+               if (file_lline(sp, ep, &cur->lno))
+                       return (1);
+               ++cmd;
+               --cmdlen;
+               break;                          /* Absolute line number. */
+       case '0': case '1': case '2': case '3': case '4':
+       case '5': case '6': case '7': case '8': case '9':
+               *addr_found = 1;
+               /*
+                * The way the vi "previous context" mark worked was that
+                * "non-relative" motions set it.  While vi wasn't totally
+                * consistent about this, ANY numeric address was considered
+                * non-relative, and set the value.  Which is why we're
+                * hacking marks down here.
+                */
+               if (IN_VI_MODE(sp)) {
+                       m.lno = sp->lno;
+                       m.cno = sp->cno;
+                       if (mark_set(sp, ep, ABSMARK1, &m, 1))
+                               return (1);
+               }
+               cur->cno = 0;
+/* 8-bit XXX */        cur->lno = strtol(cmd, &endp, 10);
+               cmdlen -= (endp - cmd);
+               cmd = endp;
+               break;
+       case '\'':                              /* Use a mark. */
+               *addr_found = 1;
+               if (cmdlen == 1) {
+                       msgq(sp, M_ERR, "No mark name supplied.");
+                       return (1);
+               }
+               if ((mp = mark_get(sp, ep, cmd[1])) == NULL)
+                       return (1);
+               *cur = *mp;
+               cmd += 2;
+               cmdlen -= 2;
+               break;
+       case '\\':                              /* Search: forward/backward. */
+               /*
+                * !!!
+                * I can't find any difference between // and \/ or
+                * between ?? and \?.  Mark Horton doesn't remember
+                * there being any difference.  C'est la vie.
+                */
+               if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') {
+                       msgq(sp, M_ERR, "\\ not followed by / or ?.");
+                       return (1);
+               }
+               ++cmd;
+               --cmdlen;
+               if (cmd[0] == '/')
+                       goto forward;
+               if (cmd[0] == '?')
+                       goto backward;
+               /* NOTREACHED */
+       case '/':                               /* Search forward. */
+forward:       *addr_found = 1;
+               m.lno = sp->lno;
+               m.cno = sp->cno;
+               flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+               if (f_search(sp, ep, &m, &m, cmd, &endp, &flags))
+                       return (1);
+               cur->lno = m.lno;
+               cur->cno = m.cno;
+               cmdlen -= (endp - cmd);
+               cmd = endp;
+               break;
+       case '?':                               /* Search backward. */
+backward:      *addr_found = 1;
+               m.lno = sp->lno;
+               m.cno = sp->cno;
+               flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+               if (b_search(sp, ep, &m, &m, cmd, &endp, &flags))
+                       return (1);
+               cur->lno = m.lno;
+               cur->cno = m.cno;
+               cmdlen -= (endp - cmd);
+               cmd = endp;
+               break;
+       case '.':                               /* Current position. */
+               *addr_found = 1;
+               cur->cno = sp->cno;
+
+               /* If an empty file, then '.' is 0, not 1. */
+               if (sp->lno == 1) {
+                       if (file_lline(sp, ep, &cur->lno))
+                               return (1);
+                       if (cur->lno != 0)
+                               cur->lno = 1;
+               } else
+                       cur->lno = sp->lno;
+               ++cmd;
+               --cmdlen;
+               break;
+       }
+
+       /*
+        * Evaluate any offset.  Offsets are +/- any number, or any number
+        * of +/- signs, or any combination thereof.  If no address found
+        * yet, offset is relative to ".".
+        */
+       for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) {
+               if (!*addr_found) {
+                       cur->lno = sp->lno;
+                       cur->cno = sp->cno;
+                       *addr_found = 1;
+               }
+
+               if (cmdlen > 1 && isdigit(cmd[1])) {
+/* 8-bit XXX */                total += strtol(cmd, &endp, 10);
+                       cmdlen -= (endp - cmd);
+                       cmd = endp;
+               } else {
+                       total += cmd[0] == '-' ? -1 : 1;
+                       --cmdlen;
+                       ++cmd;
+               }
+       }
+       if (total < 0 && -total > cur->lno) {
+               msgq(sp, M_ERR, "Reference to a line number less than 0.");
+               return (1);
+       }
+       cur->lno += total;
+
+       *cmdp = cmd;
+       *cmdlenp = cmdlen;
+       return (0);
+}
+
+/*
+ * ex_is_abbrev -
+ *     The vi text input routine needs to know if ex thinks this is
+ *     an [un]abbreviate command, so it can turn off abbreviations.
+ *     Usual ranting in the vi/v_ntext:txt_abbrev() routine.
+ */
+int
+ex_is_abbrev(name, len)
+       char *name;
+       size_t len;
+{
+       EXCMDLIST const *cp;
+
+       return ((cp = ex_comm_search(name, len)) != NULL &&
+           (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
+}
+
+static inline EXCMDLIST const *
+ex_comm_search(name, len)
+       char *name;
+       size_t len;
+{
+       EXCMDLIST const *cp;
+
+       for (cp = cmds; cp->name != NULL; ++cp) {
+               if (cp->name[0] > name[0])
+                       return (NULL);
+               if (cp->name[0] != name[0])
+                       continue;
+               if (!memcmp(name, cp->name, len))
+                       return (cp);
+       }
+       return (NULL);
+}
diff --git a/usr.bin/vi/nex/ex_abbrev.c b/usr.bin/vi/nex/ex_abbrev.c
new file mode 100644 (file)
index 0000000..f28cbe4
--- /dev/null
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_abbrev.c        8.6 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * ex_abbr -- :abbreviate [key replacement]
+ *     Create an abbreviation or display abbreviations.
+ */
+int
+ex_abbr(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       switch (cmdp->argc) {
+       case 0:
+               if (seq_dump(sp, SEQ_ABBREV, 0) == 0)
+                       msgq(sp, M_INFO, "No abbreviations to display.");
+               return (0);
+       case 2:
+               break;
+       default:
+               abort();
+       }
+
+       if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+           cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, 1))
+               return (1);
+       F_SET(sp->gp, G_ABBREV);
+       return (0);
+}
+
+/*
+ * ex_unabbr -- :unabbreviate key
+ *      Delete an abbreviation.
+ */
+int
+ex_unabbr(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+        EXCMDARG *cmdp;
+{
+       ARGS *ap;
+
+       ap = cmdp->argv[0];
+       if (!F_ISSET(sp->gp, G_ABBREV) ||
+           seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) {
+               msgq(sp, M_ERR, "\"%s\" is not an abbreviation.", ap->bp);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * abbr_save --
+ *     Save the abbreviation sequences to a file.
+ */
+int
+abbr_save(sp, fp)
+       SCR *sp;
+       FILE *fp;
+{
+       return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV));
+}
diff --git a/usr.bin/vi/nex/ex_append.c b/usr.bin/vi/nex/ex_append.c
new file mode 100644 (file)
index 0000000..c6edfd5
--- /dev/null
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_append.c        8.6 (Berkeley) 11/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {APPEND, CHANGE, INSERT};
+
+static int aci __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_append -- :[line] a[ppend][!]
+ *     Append one or more lines of new text after the specified line,
+ *     or the current line if no address is specified.
+ */
+int
+ex_append(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (aci(sp, ep, cmdp, APPEND));
+}
+
+/*
+ * ex_change -- :[line[,line]] c[hange][!] [count]
+ *     Change one or more lines to the input text.
+ */
+int
+ex_change(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (aci(sp, ep, cmdp, CHANGE));
+}
+
+/*
+ * ex_insert -- :[line] i[nsert][!]
+ *     Insert one or more lines of new text before the specified line,
+ *     or the current line if no address is specified.
+ */
+int
+ex_insert(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (aci(sp, ep, cmdp, INSERT));
+}
+
+static int
+aci(sp, ep, cmdp, cmd)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       enum which cmd;
+{
+       MARK m;
+       TEXT *tp;
+       recno_t cnt;
+       int rval, aiset;
+
+       /*
+        * The ! flag turns off autoindent for append, change and
+        * insert.
+        */
+       if (F_ISSET(cmdp, E_FORCE)) {
+               aiset = O_ISSET(sp, O_AUTOINDENT);
+               O_CLR(sp, O_AUTOINDENT);
+       } else
+               aiset = 0;
+
+       /*
+        * If doing a change, replace lines as long as possible.
+        * Then, append more lines or delete remaining lines.
+        * Inserts are the same as appends to the previous line.
+        */
+       rval = 0;
+       m = cmdp->addr1;
+       if (cmd == INSERT) {
+               --m.lno;
+               cmd = APPEND;
+       }
+       if (cmd == CHANGE)
+               for (;; ++m.lno) {
+                       if (m.lno > cmdp->addr2.lno) {
+                               cmd = APPEND;
+                               --m.lno;
+                               break;
+                       }
+                       switch (sp->s_get(sp, ep, &sp->tiq, 0,
+                           TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) {
+                       case INP_OK:
+                               break;
+                       case INP_EOF:
+                       case INP_ERR:
+                               rval = 1;
+                               goto done;
+                       }
+                       tp = sp->tiq.cqh_first;
+                       if (tp->len == 1 && tp->lb[0] == '.') {
+                               for (cnt =
+                                   (cmdp->addr2.lno - m.lno) + 1; cnt--;)
+                                       if (file_dline(sp, ep, m.lno)) {
+                                               rval = 1;
+                                               goto done;
+                                       }
+                               goto done;
+                       }
+                       if (file_sline(sp, ep, m.lno, tp->lb, tp->len)) {
+                               rval = 1;
+                               goto done;
+                       }
+               }
+
+       if (cmd == APPEND)
+               for (;; ++m.lno) {
+                       switch (sp->s_get(sp, ep, &sp->tiq, 0,
+                           TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) {
+                       case INP_OK:
+                               break;
+                       case INP_EOF:
+                       case INP_ERR:
+                               rval = 1;
+                               goto done;
+                       }
+                       tp = sp->tiq.cqh_first;
+                       if (tp->len == 1 && tp->lb[0] == '.')
+                               break;
+                       if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) {
+                               rval = 1;
+                               goto done;
+                       }
+               }
+
+done:  if (rval == 0)
+               sp->lno = m.lno;
+
+       if (aiset)
+               O_SET(sp, O_AUTOINDENT);
+
+       return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_args.c b/usr.bin/vi/nex/ex_args.c
new file mode 100644 (file)
index 0000000..2f96df7
--- /dev/null
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_args.c  8.13 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_next -- :next [files]
+ *     Edit the next file, optionally setting the list of files.
+ *
+ * !!!
+ * The :next command behaved differently from the :rewind command in
+ * historic vi.  See nvi/docs/autowrite for details, but the basic
+ * idea was that it ignored the force flag if the autowrite flag was
+ * set.  This implementation handles them all identically.
+ */
+int
+ex_next(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       ARGS **argv;
+       FREF *frp;
+       char *name;
+
+       MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+       if (cmdp->argc) {
+               /* Mark all the current files as ignored. */
+               for (frp = sp->frefq.cqh_first;
+                   frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+                       F_SET(frp, FR_IGNORE);
+
+               /* Add the new files into the file list. */
+               for (argv = cmdp->argv; argv[0]->len != 0; ++argv)
+                       if (file_add(sp, NULL, argv[0]->bp, 0) == NULL)
+                               return (1);
+               
+               if ((frp = file_first(sp)) == NULL)
+                       return (1);
+       } else if ((frp = file_next(sp, sp->a_frp)) == NULL) {
+               msgq(sp, M_ERR, "No more files to edit.");
+               return (1);
+       }
+
+       /*
+        * There's a tricky sequence, where the user edits two files, e.g.
+        * "x" and "y".  While in "x", they do ":e y|:f foo", which changes
+        * the name of that FRP entry.  Then, the :n command finds the file
+        * "y" with a name change.  If the file name has been changed, get
+        * a new FREF for the original file name, and make it be the one that
+        * is displayed in the argument list, not the one with the name change.
+        */
+       if (frp->cname != NULL) {
+               F_SET(frp, FR_IGNORE);
+               name = frp->name == NULL ? frp->tname : frp->name;
+               if ((frp = file_add(sp, sp->a_frp, name, 0)) == NULL)
+                       return (1);
+       }
+       if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+               return (1);
+       sp->a_frp = frp;
+       F_SET(sp, S_FSWITCH);
+       return (0);
+}
+
+/*
+ * ex_prev -- :prev
+ *     Edit the previous file.
+ */
+int
+ex_prev(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       FREF *frp;
+       char *name;
+
+       MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+       if ((frp = file_prev(sp, sp->a_frp)) == NULL) {
+               msgq(sp, M_ERR, "No previous files to edit.");
+               return (1);
+       }
+
+       /* See comment in ex_next(). */
+       if (frp->cname != NULL) {
+               F_SET(frp, FR_IGNORE);
+               name = frp->name == NULL ? frp->tname : frp->name;
+               if ((frp = file_add(sp, frp, name, 0)) == NULL)
+                       return (1);
+       }
+       if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+               return (1);
+       sp->a_frp = frp;
+       F_SET(sp, S_FSWITCH);
+       return (0);
+}
+
+/*
+ * ex_rew -- :rew
+ *     Re-edit the list of files.
+ */
+int
+ex_rew(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       FREF *frp, *tfrp;
+
+       /*
+        * !!!
+        * Historic practice -- you can rewind to the current file.
+        */
+       if ((frp = file_first(sp)) == NULL) {
+               msgq(sp, M_ERR, "No previous files to rewind.");
+               return (1);
+       }
+
+       MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+       /*
+        * !!!
+        * Historic practice, turn off the edited bit.  The :next and :prev
+        * code will discard any name changes, so ignore them here.  Start
+        * at the beginning of the file, too.
+        */
+       for (tfrp = sp->frefq.cqh_first;
+           tfrp != (FREF *)&sp->frefq; tfrp = tfrp->q.cqe_next)
+               F_CLR(tfrp, FR_CHANGEWRITE | FR_CURSORSET | FR_EDITED);
+
+       if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+               return (1);
+       sp->a_frp = frp;
+       F_SET(sp, S_FSWITCH);
+       return (0);
+}
+
+/*
+ * ex_args -- :args
+ *     Display the list of files.
+ */
+int
+ex_args(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       FREF *frp;
+       int cnt, col, iscur, len, nlen, sep;
+       char *name;
+
+       /*
+        * !!!
+        * Ignore files that aren't in the "argument" list unless they are the
+        * one we're currently editing.  I'm not sure this is right, but the
+        * historic vi behavior of not showing the current file if it was the
+        * result of a ":e" command, or if the file name was changed was wrong.
+        * This is actually pretty tricky, don't modify it without thinking it
+        * through.  There have been a lot of problems in here.
+        *
+        * Also, historic practice was to display the original name of the file
+        * even if the user had used a file command to change the file name.
+        * Confusing, at best.  We show both names: the original as that's what
+        * the user will get in a next, prev or rewind, and the new one since
+        * that's what the user is actually editing now.
+        *
+        * When we find the "argument" FREF, i.e. the current location in the
+        * user's argument list, if it's not the same as the current FREF, we
+        * display the current FREF as following the argument in the list.
+        * This means that if the user edits three files, "x", "y" and "z", and
+        * then does a :e command in the file "x" to edit "z", "z" will appear
+        * in the list twice.
+        */
+       col = len = sep = 0;
+       for (cnt = 1, frp = sp->frefq.cqh_first;
+           frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) {
+               iscur = 0;
+               /*
+                * If the last argument FREF structure, and we're editing
+                * it, set the current bit.  Otherwise, we'll display it,
+                * then the file we're editing, and the latter will have
+                * the current bit set.
+                */
+               if (frp == sp->a_frp) {
+                       if (frp == sp->frp && frp->cname == NULL)
+                               iscur = 1;
+               } else if (F_ISSET(frp, FR_IGNORE))
+                       continue;
+               name = frp->name == NULL ? frp->tname : frp->name;
+               /*
+                * Mistake.  The user edited a temporary file (vi /tmp), then
+                * switched to another file (:e file).  The argument FREF is
+                * pointing to the temporary file, but it doesn't have a name.
+                * Gracefully recover through the creative use of goto's.
+                */
+               if (name == NULL)
+                       goto testcur;
+extra:         nlen = strlen(name);
+               col += len = nlen + sep + (iscur ? 2 : 0);
+               if (col >= sp->cols - 1) {
+                       col = len;
+                       sep = 0;
+                       (void)ex_printf(EXCOOKIE, "\n");
+               } else if (cnt != 1) {
+                       sep = 1;
+                       (void)ex_printf(EXCOOKIE, " ");
+               }
+               ++cnt;
+
+               if (iscur)
+                       (void)ex_printf(EXCOOKIE, "[%s]", name);
+               else {
+                       (void)ex_printf(EXCOOKIE, "%s", name);
+testcur:               if (frp == sp->a_frp) {
+                               if (frp != sp->frp)
+                                       name = FILENAME(sp->frp);
+                               else
+                                       name = frp->cname;
+                               iscur = 1;
+                               goto extra;
+                       }
+               }
+       }
+       /* This should never happen; left in because it's been known to. */
+       if (cnt == 1)
+               (void)ex_printf(EXCOOKIE, "No files.\n");
+       else
+               (void)ex_printf(EXCOOKIE, "\n");
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_argv.c b/usr.bin/vi/nex/ex_argv.c
new file mode 100644 (file)
index 0000000..6ad76c6
--- /dev/null
@@ -0,0 +1,575 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_argv.c  8.26 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static int argv_alloc __P((SCR *, size_t));
+static int argv_fexp __P((SCR *, EXCMDARG *,
+              char *, size_t, char *, size_t *, char **, size_t *, int));
+static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
+
+/*
+ * argv_init --
+ *     Build  a prototype arguments list.
+ */
+int
+argv_init(sp, ep, excp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+{
+       EX_PRIVATE *exp;
+
+       exp = EXP(sp);
+       exp->argsoff = 0;
+       argv_alloc(sp, 1);
+
+       excp->argv = exp->args;
+       excp->argc = exp->argsoff;
+       return (0);
+}
+
+/*
+ * argv_exp0 --
+ *     Put a string into an argv.
+ */
+int
+argv_exp0(sp, ep, excp, cmd, cmdlen)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+       char *cmd;
+       size_t cmdlen;
+{
+       EX_PRIVATE *exp;
+
+       exp = EXP(sp);
+       argv_alloc(sp, cmdlen);
+       memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen);
+       exp->args[exp->argsoff]->bp[cmdlen] = '\0';
+       exp->args[exp->argsoff]->len = cmdlen;
+       ++exp->argsoff;
+       excp->argv = exp->args;
+       excp->argc = exp->argsoff;
+       return (0);
+}
+
+/*
+ * argv_exp1 --
+ *     Do file name expansion on a string, and leave it in a string.
+ */
+int
+argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+       char *cmd;
+       size_t cmdlen;
+       int is_bang;
+{
+       EX_PRIVATE *exp;
+       size_t blen, len;
+       char *bp;
+
+       GET_SPACE_RET(sp, bp, blen, 512);
+
+       len = 0;
+       exp = EXP(sp);
+       if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
+               FREE_SPACE(sp, bp, blen);
+               return (1);
+       }
+
+       argv_alloc(sp, len);
+       memmove(exp->args[exp->argsoff]->bp, bp, len);
+       exp->args[exp->argsoff]->bp[len] = '\0';
+       exp->args[exp->argsoff]->len = len;
+       ++exp->argsoff;
+       excp->argv = exp->args;
+       excp->argc = exp->argsoff;
+
+       FREE_SPACE(sp, bp, blen);
+       return (0);
+}
+
+/*
+ * argv_exp2 --
+ *     Do file name and shell expansion on a string, and break
+ *     it up into an argv.
+ */
+int
+argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+       char *cmd;
+       size_t cmdlen;
+       int is_bang;
+{
+       size_t blen, len, n;
+       int rval;
+       char *bp, *p;
+
+       GET_SPACE_RET(sp, bp, blen, 512);
+
+#define        SHELLECHO       "echo "
+#define        SHELLOFFSET     (sizeof(SHELLECHO) - 1)
+       memmove(bp, SHELLECHO, SHELLOFFSET);
+       p = bp + SHELLOFFSET;
+       len = SHELLOFFSET;
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
+#endif
+
+       if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) {
+               rval = 1;
+               goto err;
+       }
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "before shell: %d: {%s}\n", len, bp);
+#endif
+
+       /*
+        * Do shell word expansion -- it's very, very hard to figure out
+        * what magic characters the user's shell expects.  If it's not
+        * pure vanilla, don't even try.
+        */
+       for (p = bp, n = len; n > 0; --n, ++p)
+               if (!isalnum(*p) && !isblank(*p) && *p != '/' && *p != '.')
+                       break;
+       if (n > 0) {
+               if (argv_sexp(sp, &bp, &blen, &len)) {
+                       rval = 1;
+                       goto err;
+               }
+               p = bp;
+       } else {
+               p = bp + SHELLOFFSET;
+               len -= SHELLOFFSET;
+       }
+
+#if defined(DEBUG) && 0
+       TRACE(sp, "after shell: %d: {%s}\n", len, bp);
+#endif
+
+       rval = argv_exp3(sp, ep, excp, p, len);
+
+err:   FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
+
+/*
+ * argv_exp3 --
+ *     Take a string and break it up into an argv.
+ */
+int
+argv_exp3(sp, ep, excp, cmd, cmdlen)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *excp;
+       char *cmd;
+       size_t cmdlen;
+{
+       CHAR_T vlit;
+       EX_PRIVATE *exp;
+       size_t len;
+       int ch, off;
+       char *ap, *p;
+
+       (void)term_key_ch(sp, K_VLNEXT, &vlit);
+       for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
+               /* Skip any leading whitespace. */
+               for (; cmdlen > 0; --cmdlen, ++cmd) {
+                       ch = *cmd;
+                       if (!isblank(ch))
+                               break;
+               }
+               if (cmdlen == 0)
+                       break;
+
+               /*
+                * Determine the length of this whitespace delimited
+                * argument.  
+                *
+                * QUOTING NOTE:
+                *
+                * Skip any character preceded by the user's quoting
+                * character.
+                */
+               for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len)
+                       if ((ch = *cmd) == vlit && cmdlen > 1) {
+                               ++cmd; 
+                               --cmdlen;
+                       } else if (isblank(ch))
+                               break;
+                               
+               /*
+                * Copy the argument into place.
+                *
+                * QUOTING NOTE:
+                *
+                * Lose quote chars.
+                */
+               argv_alloc(sp, len);
+               off = exp->argsoff;
+               exp->args[off]->len = len;
+               for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
+                       if (*ap == vlit) {
+                               ++ap;
+                               --exp->args[off]->len;
+                       }
+               *p = '\0';
+       }
+       excp->argv = exp->args;
+       excp->argc = exp->argsoff;
+
+#if defined(DEBUG) && 0
+       for (cnt = 0; cnt < exp->argsoff; ++cnt)
+               TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
+#endif
+       return (0);
+}
+
+/*
+ * argv_fexp --
+ *     Do file name and bang command expansion.
+ */
+static int
+argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
+       SCR *sp;
+       EXCMDARG *excp;
+       char *cmd, *p, **bpp;
+       size_t cmdlen, *lenp, *blenp;
+       int is_bang;
+{
+       EX_PRIVATE *exp;
+       char *bp, *t;
+       size_t blen, len, tlen;
+
+       /* Replace file name characters. */
+       for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
+               switch (*cmd) {
+               case '!':
+                       if (!is_bang)
+                               goto ins_ch;
+                       exp = EXP(sp);
+                       if (exp->lastbcomm == NULL) {
+                               msgq(sp, M_ERR,
+                                   "No previous command to replace \"!\".");
+                               return (1);
+                       }
+                       len += tlen = strlen(exp->lastbcomm);
+                       ADD_SPACE_RET(sp, bp, blen, len);
+                       memmove(p, exp->lastbcomm, tlen);
+                       p += tlen;
+                       F_SET(excp, E_MODIFY);
+                       break;
+               case '%':
+                       if (sp->frp->cname == NULL && sp->frp->name == NULL) {
+                               msgq(sp, M_ERR,
+                                   "No filename to substitute for %%.");
+                               return (1);
+                       }
+                       tlen = strlen(t = FILENAME(sp->frp));
+                       len += tlen;
+                       ADD_SPACE_RET(sp, bp, blen, len);
+                       memmove(p, t, tlen);
+                       p += tlen;
+                       F_SET(excp, E_MODIFY);
+                       break;
+               case '#':
+                       /*
+                        * Try the alternate file name first, then the
+                        * previously edited file.
+                        */
+                       if (sp->alt_name == NULL && (sp->p_frp == NULL ||
+                           sp->frp->cname == NULL && sp->frp->name == NULL)) {
+                               msgq(sp, M_ERR,
+                                   "No filename to substitute for #.");
+                               return (1);
+                       }
+                       if (sp->alt_name != NULL)
+                               t = sp->alt_name;
+                       else
+                               t = FILENAME(sp->frp);
+                       len += tlen = strlen(t);
+                       ADD_SPACE_RET(sp, bp, blen, len);
+                       memmove(p, t, tlen);
+                       p += tlen;
+                       F_SET(excp, E_MODIFY);
+                       break;
+               case '\\':
+                       /*
+                        * QUOTING NOTE:
+                        *
+                        * Strip any backslashes that protected the file
+                        * expansion characters.
+                        */
+                       if (cmdlen > 1 && cmd[1] == '%' || cmd[1] == '#')
+                               ++cmd;
+                       /* FALLTHROUGH */
+               default:
+ins_ch:                        ++len;
+                       ADD_SPACE_RET(sp, bp, blen, len);
+                       *p++ = *cmd;
+               }
+
+       /* Nul termination. */
+       ++len;
+       ADD_SPACE_RET(sp, bp, blen, len);
+       *p = '\0';
+
+       /* Return the new string length, buffer, buffer length. */
+       *lenp = len - 1;
+       *bpp = bp;
+       *blenp = blen;
+       return (0);
+}
+
+/*
+ * argv_alloc --
+ *     Make more space for arguments.
+ */
+static int
+argv_alloc(sp, len)
+       SCR *sp;
+       size_t len;
+{
+       ARGS *ap;                                       
+       EX_PRIVATE *exp;
+       int cnt, off;
+
+       /*
+        * Allocate room for another argument, always leaving
+        * enough room for an ARGS structure with a length of 0.
+        */
+#define        INCREMENT       20
+       exp = EXP(sp);
+       off = exp->argsoff;
+       if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
+               cnt = exp->argscnt + INCREMENT;
+               REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
+               if (exp->args == NULL) {
+                       (void)argv_free(sp);
+                       goto mem;
+               }
+               memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *));
+               exp->argscnt = cnt;
+       }
+
+       /* First argument. */
+       if (exp->args[off] == NULL) {
+               CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+               if (exp->args[off] == NULL)
+                       goto mem;                               
+       }                                               
+
+       /* First argument buffer. */
+       ap = exp->args[off];                    
+       ap->len = 0;                                    
+       if (ap->blen < len + 1) {                       
+               ap->blen = len + 1;                     
+               REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
+               if (ap->bp == NULL) {
+                       ap->bp = NULL;          
+                       ap->blen = 0;                   
+                       F_CLR(ap, A_ALLOCATED); 
+mem:                   msgq(sp, M_SYSERR, NULL);       
+                       return (1);                     
+               }                                       
+               F_SET(ap, A_ALLOCATED);         
+       }                                               
+
+       /* Second argument. */
+       if (exp->args[++off] == NULL) {
+               CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+               if (exp->args[off] == NULL)
+                       goto mem;                               
+       }                                               
+       /* 0 length serves as end-of-argument marker. */
+       exp->args[off]->len = 0;                        
+       return (0);
+}
+
+/*
+ * argv_free --
+ *     Free up argument structures.
+ */
+int
+argv_free(sp)
+       SCR *sp;
+{
+       EX_PRIVATE *exp;
+       int off;
+
+       exp = EXP(sp);
+       if (exp->args != NULL) {
+               for (off = 0; off < exp->argscnt; ++off) {
+                       if (exp->args[off] == NULL)
+                               continue;
+                       if (F_ISSET(exp->args[off], A_ALLOCATED))
+                               free(exp->args[off]->bp);
+                       FREE(exp->args[off], sizeof(ARGS));
+               }
+               FREE(exp->args, exp->argscnt * sizeof(ARGS *));
+       }
+       exp->args = NULL;
+       exp->argscnt = 0;
+       exp->argsoff = 0;
+       return (0);
+}
+
+/*
+ * argv_sexp --
+ *     Fork a shell, pipe a command through it, and read the output into
+ *     a buffer.
+ */
+static int
+argv_sexp(sp, bpp, blenp, lenp)
+       SCR *sp;
+       char **bpp;
+       size_t *blenp, *lenp;
+{
+       FILE *ifp;
+       pid_t pid;
+       size_t blen, len;
+       int ch, rval, output[2];
+       char *bp, *p, *sh, *sh_path;
+
+       bp = *bpp;
+       blen = *blenp;
+
+       sh_path = O_STR(sp, O_SHELL);
+       if ((sh = strrchr(sh_path, '/')) == NULL)
+               sh = sh_path;
+       else
+               ++sh;
+
+       /*
+        * There are two different processes running through this code.
+        * They are named the utility and the parent. The utility reads
+        * from standard input and writes to the parent.  The parent reads
+        * from the utility and writes into the buffer.  The parent reads
+        * from output[0], and the utility writes to output[1].
+        */
+       if (pipe(output) < 0) {
+               msgq(sp, M_SYSERR, "pipe");
+               return (1);
+       }
+       if ((ifp = fdopen(output[0], "r")) == NULL) {
+               msgq(sp, M_SYSERR, "fdopen");
+               goto err;
+       }
+               
+       /*
+        * Do the minimal amount of work possible, the shell is going
+        * to run briefly and then exit.  Hopefully.
+        */
+       switch (pid = vfork()) {
+       case -1:                        /* Error. */
+               msgq(sp, M_SYSERR, "vfork");
+err:           (void)close(output[0]);
+               (void)close(output[1]);
+               return (1);
+       case 0:                         /* Utility. */
+               /* Redirect stdout/stderr to the write end of the pipe. */
+               (void)dup2(output[1], STDOUT_FILENO);
+               (void)dup2(output[1], STDERR_FILENO);
+
+               /* Close the utility's file descriptors. */
+               (void)close(output[0]);
+               (void)close(output[1]);
+
+               /* Assumes that all shells have -c. */
+               execl(sh_path, sh, "-c", bp, NULL);
+               msgq(sp, M_ERR,
+                   "Error: execl: %s: %s", sh_path, strerror(errno));
+               _exit(127);
+       default:
+               /* Close the pipe end the parent won't use. */
+               (void)close(output[1]);
+               break;
+       }
+
+       rval = 0;
+
+       /*
+        * Copy process output into a buffer.
+        *
+        * !!!
+        * Historic vi apparently discarded leading \n and \r's from
+        * the shell output stream.  We don't on the grounds that any
+        * shell that does that is broken.
+        */
+       for (p = bp, len = 0, ch = EOF;
+           (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
+               if (blen < 5) {
+                       ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2);
+                       p = bp + len;
+                       blen = *blenp - len;
+               }
+
+       /* Delete the final newline, nul terminate the string. */
+       if (p > bp && p[-1] == '\n' || p[-1] == '\r') {
+               --len;
+               *--p = '\0';
+       } else
+               *p = '\0';
+       *lenp = len;
+
+       if (ferror(ifp)) {
+               msgq(sp, M_ERR, "I/O error: %s", sh);
+binc_err:      rval = 1;
+       }
+       (void)fclose(ifp);
+
+       *bpp = bp;              /* *blenp is already updated. */
+
+       /* Wait for the process. */
+       return (proc_wait(sp, (long)pid, sh, 0) | rval);
+}
diff --git a/usr.bin/vi/nex/ex_at.c b/usr.bin/vi/nex/ex_at.c
new file mode 100644 (file)
index 0000000..51d001f
--- /dev/null
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_at.c    8.16 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_at -- :@[@ | buffer]
+ *         :*[* | buffer]
+ *
+ *     Execute the contents of the buffer.
+ */
+int
+ex_at(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       CB *cbp;
+       EX_PRIVATE *exp;
+       TEXT *tp;
+       int name, lmode;
+
+       exp = EXP(sp);
+
+       /* Historically, @@ and ** execute the last buffer. */
+       name = cmdp->buffer;
+       if (name == cmdp->cmd->name[0]) {
+               if (!exp->at_lbuf_set) {
+                       msgq(sp, M_ERR, "No previous buffer to execute.");
+                       return (1);
+               }
+               name = exp->at_lbuf;
+       }
+
+       CBNAME(sp, cbp, name);
+       if (cbp == NULL) {
+               msgq(sp, M_ERR, "Buffer %s is empty.", charname(sp, name));
+               return (1);
+       }
+
+       /* Save for reuse. */
+       exp->at_lbuf = name;
+       exp->at_lbuf_set = 1;
+               
+       /*
+        * If the buffer was cut in line mode or had portions of more
+        * than one line, <newlines> are appended to each line as it
+        * is pushed onto the stack.
+        */
+       tp = cbp->textq.cqh_last;
+       lmode = F_ISSET(cbp, CB_LMODE) || tp->q.cqe_prev != (void *)&cbp->textq;
+       for (; tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+               if ((lmode || tp->q.cqe_prev != (void *)&cbp->textq) &&
+                   term_push(sp, "\n", 1, 0, 0) ||
+                   term_push(sp, tp->lb, tp->len, 0, CH_QUOTED))
+                       return (1);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_bang.c b/usr.bin/vi/nex/ex_bang.c
new file mode 100644 (file)
index 0000000..4f7222b
--- /dev/null
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_bang.c  8.22 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex/sex_screen.h"
+
+/*
+ * ex_bang -- :[line [,line]] ! command
+ *
+ * Pass the rest of the line after the ! character to the program named by
+ * the O_SHELL option.
+ *
+ * Historical vi did NOT do shell expansion on the arguments before passing
+ * them, only file name expansion.  This means that the O_SHELL program got
+ * "$t" as an argument if that is what the user entered.  Also, there's a
+ * special expansion done for the bang command.  Any exclamation points in
+ * the user's argument are replaced by the last, expanded ! command.
+ *
+ * There's some fairly amazing slop in this routine to make the different
+ * ways of getting here display the right things.  It took a long time to
+ * get get right (wrong?), so be careful.
+ */
+int
+ex_bang(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       enum filtertype ftype;
+       ARGS *ap;
+       EX_PRIVATE *exp;
+       MARK rm;
+       recno_t lno;
+       size_t blen;
+       int rval;
+       char *bp, *msg;
+
+       
+       ap = cmdp->argv[0];
+       if (ap->len == 0) {
+               msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+               return (1);
+       }
+
+       /* Swap commands. */
+       exp = EXP(sp);
+       if (exp->lastbcomm != NULL)
+               FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
+       if ((exp->lastbcomm = strdup(ap->bp)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+
+       /*
+        * If the command was modified by the expansion, we redisplay it.
+        * Redisplaying it in vi mode is tricky, and handled separately
+        * in each case below.  If we're in ex mode, it's easy, so we just
+        * do it here.
+        */
+       bp = NULL;
+       if (F_ISSET(cmdp, E_MODIFY)) {
+               if (IN_EX_MODE(sp)) {
+                       (void)ex_printf(EXCOOKIE, "!%s\n", ap->bp);
+                       (void)ex_fflush(EXCOOKIE);
+               }
+               /*
+                * Vi: Display the command if modified.  Historic vi displayed
+                * the command if it was modified due to file name and/or bang
+                * expansion.  If piping lines, it was immediately overwritten
+                * by any error or line change reporting.  We don't the user to
+                * have to page through the responses, so we only post it until
+                * it's erased by something else.  Otherwise, pass it on to the
+                * ex_exec_proc routine to display after the screen has been
+                * cleaned up.
+                */
+               if (IN_VI_MODE(sp)) {
+                       GET_SPACE_RET(sp, bp, blen, ap->len + 2);
+                       bp[0] = '!';
+                       memmove(bp + 1, ap->bp, ap->len + 1);
+               }
+       }
+
+       /*
+        * If addresses were specified, pipe lines from the file through
+        * the command.
+        */
+       if (cmdp->addrcnt != 0) {
+               if (bp != NULL) {
+                       (void)sp->s_busy(sp, bp);
+                       FREE_SPACE(sp, bp, blen);
+               }
+               /*
+                * !!!
+                * Historical vi permitted "!!" in an empty file.  When it
+                * happens, we get called with two addresses of 1,1 and a
+                * bad attitude.
+                */
+               ftype = FILTER;
+               if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
+                       if (file_lline(sp, ep, &lno))
+                               return (1);
+                       if (lno == 0) {
+                               cmdp->addr1.lno = cmdp->addr2.lno = 0;
+                               ftype = FILTER_READ;
+                       }
+               }
+               if (filtercmd(sp, ep,
+                   &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype))
+                       return (1);
+               sp->lno = rm.lno;
+               F_SET(exp, EX_AUTOPRINT);
+               return (0);
+       }
+
+       /*
+        * If no addresses were specified, run the command.  If the file
+        * has been modified and autowrite is set, write the file back.
+        * If the file has been modified, autowrite is not set and the
+        * warn option is set, tell the user about the file.
+        */
+       msg = "\n";
+       if (F_ISSET(ep, F_MODIFIED))
+               if (O_ISSET(sp, O_AUTOWRITE)) {
+                       if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) {
+                               rval = 1;
+                               goto ret;
+                       }
+               } else if (O_ISSET(sp, O_WARN))
+                       if (IN_VI_MODE(sp) && F_ISSET(cmdp, E_MODIFY))
+                               msg = "\nFile modified since last write.\n";
+                       else
+                               msg = "File modified since last write.\n";
+
+       /* Run the command. */
+       rval = ex_exec_proc(sp, ap->bp, bp, msg);
+
+       /* Ex terminates with a bang. */
+       if (IN_EX_MODE(sp))
+               (void)write(STDOUT_FILENO, "!\n", 2);
+
+       /* Vi requires user permission to continue. */
+       if (IN_VI_MODE(sp))
+               F_SET(sp, S_CONTINUE);
+
+       /* Free the extra space. */
+ret:   if (bp != NULL)
+               FREE_SPACE(sp, bp, blen);
+
+       return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_cd.c b/usr.bin/vi/nex/ex_cd.c
new file mode 100644 (file)
index 0000000..6b51bc5
--- /dev/null
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_cd.c    8.3 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_cd -- :cd[!] [directory]
+ *     Change directories.
+ */
+int
+ex_cd(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       char *dir, buf[MAXPATHLEN];
+
+       switch (cmdp->argc) {
+       case 0:
+               if ((dir = getenv("HOME")) == NULL) {
+                       msgq(sp, M_ERR,
+                           "Environment variable HOME not set.");
+                       return (1);
+               }
+               break;
+       case 1:
+               dir = cmdp->argv[0]->bp;
+               break;
+       default:
+               abort();
+       }
+
+       if (chdir(dir) < 0) {
+               msgq(sp, M_SYSERR, dir);
+               return (1);
+       }
+       if (getcwd(buf, sizeof(buf)) != NULL)
+               msgq(sp, M_INFO, "New directory: %s", buf);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_delete.c b/usr.bin/vi/nex/ex_delete.c
new file mode 100644 (file)
index 0000000..5b737e8
--- /dev/null
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_delete.c        8.6 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
+ *
+ *     Delete lines from the file.
+ */
+int
+ex_delete(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       recno_t lno;
+
+       /* Yank the lines. */
+       if (cut(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+           &cmdp->addr1, &cmdp->addr2, CUT_DELETE | CUT_LINEMODE))
+               return (1);
+
+       /* Delete the lines. */
+       if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1))
+               return (1);
+
+       /* Set the cursor to the line after the last line deleted. */
+       sp->lno = cmdp->addr1.lno;
+
+       /* Or the last line in the file if deleted to the end of the file. */
+       if (file_lline(sp, ep, &lno))
+               return (1);
+       if (sp->lno > lno)
+               sp->lno = lno;
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_digraph.c b/usr.bin/vi/nex/ex_digraph.c
new file mode 100644 (file)
index 0000000..20f1bf3
--- /dev/null
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_digraph.c       8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#ifndef NO_DIGRAPH
+#include <sys/types.h>
+
+#include <curses.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static void do_digraph __P((SCR *, EXF *, int, u_char *));
+
+/* This stuff is used to build the default digraphs table. */
+static u_char digtable[][4] = {
+# ifdef CS_IBMPC
+       "C,\200",       "u\"\1",        "e'\2",         "a^\3",
+       "a\"\4",        "a`\5",         "a@\6",         "c,\7",
+       "e^\10",        "e\"\211",      "e`\12",        "i\"\13",
+       "i^\14",        "i`\15",        "A\"\16",       "A@\17",
+       "E'\20",        "ae\21",        "AE\22",        "o^\23",
+       "o\"\24",       "o`\25",        "u^\26",        "u`\27",
+       "y\"\30",       "O\"\31",       "U\"\32",       "a'\240",
+       "i'!",          "o'\"",         "u'#",          "n~$",
+       "N~%",          "a-&",          "o-'",          "~?(",
+       "~!-",          "\"<.",         "\">/",
+#  ifdef CS_SPECIAL
+       "2/+",          "4/,",          "^+;",          "^q<",
+       "^c=",          "^r>",          "^t?",          "pp]",
+       "^^^",          "oo_",          "*a`",          "*ba",
+       "*pc",          "*Sd",          "*se",          "*uf",
+       "*tg",          "*Ph",          "*Ti",          "*Oj",
+       "*dk",          "*Hl",          "*hm",          "*En",
+       "*No",          "eqp",          "pmq",          "ger",
+       "les",          "*It",          "*iu",          "*/v",
+       "*=w",          "sq{",          "^n|",          "^2}",
+       "^3~",          "^_\377",
+#  endif /* CS_SPECIAL */
+# endif /* CS_IBMPC */
+# ifdef CS_LATIN1
+       "~!!",          "a-*",          "\">+",         "o-:",
+       "\"<>",         "~??",
+
+       "A`@",          "A'A",          "A^B",          "A~C",
+       "A\"D",         "A@E",          "AEF",          "C,G",
+       "E`H",          "E'I",          "E^J",          "E\"K",
+       "I`L",          "I'M",          "I^N",          "I\"O",
+       "-DP",          "N~Q",          "O`R",          "O'S",
+       "O^T",          "O~U",          "O\"V",         "O/X",
+       "U`Y",          "U'Z",          "U^[",          "U\"\\",
+       "Y'_",
+
+       "a``",          "a'a",          "a^b",          "a~c",
+       "a\"d",         "a@e",          "aef",          "c,g",
+       "e`h",          "e'i",          "e^j",          "e\"k",
+       "i`l",          "i'm",          "i^n",          "i\"o",
+       "-dp",          "n~q",          "o`r",          "o's",
+       "o^t",          "o~u",          "o\"v",         "o/x",
+       "u`y",          "u'z",          "u^{",          "u\"|",
+       "y'~",
+# endif /* CS_LATIN1 */
+       ""
+};
+
+int
+digraph_init(sp)
+       SCR *sp;
+{
+       int     i;
+
+       for (i = 0; *digtable[i]; i++)
+               do_digraph(sp, NULL, 0, digtable[i]);
+       do_digraph(sp, NULL, 0, NULL);
+       return (0);
+}
+
+int
+ex_digraph(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp);
+       return (0);
+}
+
+static struct _DIG
+{
+       struct _DIG     *next;
+       char            key1;
+       char            key2;
+       char            dig;
+       char            save;
+} *digs;
+
+int
+digraph(sp, key1, key2)
+       SCR *sp;
+       char    key1;   /* the underlying character */
+       char    key2;   /* the second character */
+{
+       int             new_key;
+       register struct _DIG    *dp;
+
+       /* if digraphs are disabled, then just return the new char */
+       if (O_ISSET(sp, O_DIGRAPH))
+       {
+               return key2;
+       }
+
+       /* remember the new key, so we can return it if this isn't a digraph */
+       new_key = key2;
+
+       /* sort key1 and key2, so that their original order won't matter */
+       if (key1 > key2)
+       {
+               key2 = key1;
+               key1 = new_key;
+       }
+
+       /* scan through the digraph chart */
+       for (dp = digs;
+            dp && (dp->key1 != key1 || dp->key2 != key2);
+            dp = dp->next)
+       {
+       }
+
+       /* if this combination isn't in there, just use the new key */
+       if (!dp)
+       {
+               return new_key;
+       }
+
+       /* else use the digraph key */
+       return dp->dig;
+}
+
+/* this function lists or defines digraphs */
+static void
+do_digraph(sp, ep, bang, extra)
+       SCR *sp;
+       EXF *ep;
+       int     bang;
+       u_char  *extra;
+{
+       int             dig;
+       register struct _DIG    *dp;
+       struct _DIG     *prev;
+       static int      user_defined = 0; /* boolean: are all later digraphs user-defined? */
+       char            listbuf[8];
+
+       /* if "extra" is NULL, then we've reached the end of the built-ins */
+       if (!extra)
+       {
+               user_defined = 1;
+               return;
+       }
+
+       /* if no args, then display the existing digraphs */
+       if (*extra < ' ')
+       {
+               listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
+               listbuf[7] = '\0';
+               for (dig = 0, dp = digs; dp; dp = dp->next)
+               {
+                       if (dp->save || bang)
+                       {
+                               dig += 7;
+                               if (dig >= sp->cno)
+                               {
+                                       addch('\n');
+                                       refresh();
+                                       dig = 7;
+                               }
+                               listbuf[3] = dp->key1;
+                               listbuf[4] = dp->key2;
+                               listbuf[6] = dp->dig;
+                               addstr(listbuf);
+                       }
+               }
+               addch('\n');
+               refresh();
+               return;
+       }
+
+       /* make sure we have at least two characters */
+       if (!extra[1])
+       {
+               msgq(sp, M_ERR,
+                   "Digraphs must be composed of two characters");
+               return;
+       }
+
+       /* sort key1 and key2, so that their original order won't matter */
+       if (extra[0] > extra[1])
+       {
+               dig = extra[0];
+               extra[0] = extra[1];
+               extra[1] = dig;
+       }
+
+       /* locate the new digraph character */
+       for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
+       {
+       }
+       dig = extra[dig];
+       if (!bang && dig)
+       {
+               dig |= 0x80;
+       }
+
+       /* search for the digraph */
+       for (prev = (struct _DIG *)0, dp = digs;
+            dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
+            prev = dp, dp = dp->next)
+       {
+       }
+
+       /* deleting the digraph? */
+       if (!dig)
+       {
+               if (!dp)
+               {
+#ifndef CRUNCH
+                       msgq(sp, M_ERR,
+                           "%c%c not a digraph", extra[0], extra[1]);
+#endif
+                       return;
+               }
+               if (prev)
+                       prev->next = dp->next;
+               else
+                       digs = dp->next;
+               free(dp);
+               return;
+       }
+
+       /* if necessary, create a new digraph struct for the new digraph */
+       if (dig && !dp)
+       {
+               MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG));
+               if (dp == NULL)
+                       return;
+               if (prev)
+                       prev->next = dp;
+               else
+                       digs = dp;
+               dp->next = (struct _DIG *)0;
+       }
+
+       /* assign it the new digraph value */
+       dp->key1 = extra[0];
+       dp->key2 = extra[1];
+       dp->dig = dig;
+       dp->save = user_defined;
+}
+
+void
+digraph_save(sp, fd)
+       SCR *sp;
+       int fd;
+{
+       static char     buf[] = "digraph! XX Y\n";
+       register struct _DIG    *dp;
+
+       for (dp = digs; dp; dp = dp->next)
+       {
+               if (dp->save)
+               {
+                       buf[9] = dp->key1;
+                       buf[10] = dp->key2;
+                       buf[12] = dp->dig;
+                       write(fd, buf, (unsigned)14);
+               }
+       }
+}
+#endif
diff --git a/usr.bin/vi/nex/ex_display.c b/usr.bin/vi/nex/ex_display.c
new file mode 100644 (file)
index 0000000..0cfef42
--- /dev/null
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_display.c       8.14 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "tag.h"
+#include "excmd.h"
+
+static int     bdisplay __P((SCR *, EXF *));
+static void    db __P((SCR *, CB *, char *));
+
+/*
+ * ex_display -- :display b[uffers] | s[creens] | t[ags]
+ *
+ *     Display buffers, tags or screens.
+ */
+int
+ex_display(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       switch (cmdp->argv[0]->bp[0]) {
+       case 'b':
+               return (bdisplay(sp, ep));
+       case 's':
+               return (ex_sdisplay(sp, ep));
+       case 't':
+               return (ex_tagdisplay(sp, ep));
+       }
+       msgq(sp, M_ERR,
+           "Unknown display argument %s, use b[uffers], s[creens], or t[ags].",
+           cmdp->argv[0]);
+       return (1);
+}
+
+/*
+ * bdisplay --
+ *
+ *     Display buffers.
+ */
+static int
+bdisplay(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       CB *cbp;
+
+       if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) {
+               (void)ex_printf(EXCOOKIE, "No cut buffers to display.");
+               return (0);
+       }
+
+       /* Buffers can be infinitely long, make it interruptible. */
+       F_SET(sp, S_INTERRUPTIBLE);
+
+       /* Display regular cut buffers. */
+       for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+               if (isdigit(cbp->name))
+                       continue;
+               if (cbp->textq.cqh_first != (void *)&cbp->textq)
+                       db(sp, cbp, NULL);
+               if (F_ISSET(sp, S_INTERRUPTED))
+                       return (0);
+       }
+       /* Display numbered buffers. */
+       for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+               if (!isdigit(cbp->name))
+                       continue;
+               if (cbp->textq.cqh_first != (void *)&cbp->textq)
+                       db(sp, cbp, NULL);
+               if (F_ISSET(sp, S_INTERRUPTED))
+                       return (0);
+       }
+       /* Display default buffer. */
+       if ((cbp = sp->gp->dcbp) != NULL)
+               db(sp, cbp, "default buffer");
+       return (0);
+}
+
+/*
+ * db --
+ *     Display a buffer.
+ */
+static void
+db(sp, cbp, name)
+       SCR *sp;
+       CB *cbp;
+       char *name;
+{
+       TEXT *tp;
+       size_t len;
+       char *p;
+
+       (void)ex_printf(EXCOOKIE, "********** %s%s\n",
+           name == NULL ? charname(sp, cbp->name) : name,
+           F_ISSET(cbp, CB_LMODE) ? " (line mode)" : "");
+       for (tp = cbp->textq.cqh_first;
+           tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
+               for (len = tp->len, p = tp->lb; len--;) {
+                       (void)ex_printf(EXCOOKIE, "%s", charname(sp, *p++));
+                       if (F_ISSET(sp, S_INTERRUPTED))
+                               return;
+               }
+               (void)ex_printf(EXCOOKIE, "\n");
+       }
+}
diff --git a/usr.bin/vi/nex/ex_edit.c b/usr.bin/vi/nex/ex_edit.c
new file mode 100644 (file)
index 0000000..eeafeda
--- /dev/null
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_edit.c  8.14 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_edit --  :e[dit][!] [+cmd] [file]
+ *             :vi[sual][!] [+cmd] [file]
+ *
+ * Edit a file; if none specified, re-edit the current file.  The second
+ * form of the command can only be executed while in vi mode.  See the
+ * hack in ex.c:ex_cmd().
+ *
+ * !!!
+ * Historic vi didn't permit the '+' command form without specifying
+ * a file name as well.
+ */
+int
+ex_edit(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       ARGS *ap;
+       FREF *frp;
+
+       frp = sp->frp;
+       switch (cmdp->argc) {
+       case 0:
+               /*
+                * If the name has been changed, we edit that file, not the
+                * original name.  If the user was editing a temporary file,
+                * create another one.  The reason for this is that we do
+                * special exit processing of temporary files, and reusing
+                * them is tricky.
+                */
+               if (frp->cname != NULL) {
+                       if ((frp = file_add(sp, frp, frp->cname, 1)) == NULL)
+                               return (1);
+                       set_alt_name(sp, sp->frp->cname);
+               } else if (frp->name == NULL)
+                       if ((frp = file_add(sp, frp, NULL, 1)) == NULL)
+                               return (1);
+               break;
+       case 1:
+               ap = cmdp->argv[0];
+               if ((frp = file_add(sp, sp->frp, ap->bp, 1)) == NULL)
+                       return (1);
+               set_alt_name(sp, ap->bp);
+               break;
+       default:
+               abort();
+       }
+
+       /*
+        * Check for modifications.
+        *
+        * !!!
+        * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit.
+        */
+       if (F_ISSET(ep, F_MODIFIED) &&
+           ep->refcnt <= 1 && !F_ISSET(cmdp, E_FORCE)) {
+               msgq(sp, M_ERR,
+                   "Modified since last write; write or use ! to override.");
+               return (1);
+       }
+
+       /* Switch files. */
+       if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+               return (1);
+       F_SET(sp, S_FSWITCH);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_equal.c b/usr.bin/vi/nex/ex_equal.c
new file mode 100644 (file)
index 0000000..f0a18be
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_equal.c 8.4 (Berkeley) 11/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_equal -- :address =
+ *     Print out the line number matching the specified address, or the
+ *     last line number in the file if no address specified.
+ */
+int
+ex_equal(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       recno_t lno;
+
+       if (F_ISSET(cmdp, E_ADDRDEF)) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               (void)ex_printf(EXCOOKIE, "%ld\n", lno);
+       } else
+               (void)ex_printf(EXCOOKIE, "%ld\n", cmdp->addr1.lno);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_exit.c b/usr.bin/vi/nex/ex_exit.c
new file mode 100644 (file)
index 0000000..b0f929a
--- /dev/null
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_exit.c  8.7 (Berkeley) 12/10/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_quit -- :quit[!]
+ *     Quit.
+ */
+int
+ex_quit(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       int force;
+
+       force = F_ISSET(cmdp, E_FORCE);
+
+       /* Check for modifications. */
+       if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
+               msgq(sp, M_ERR,
+                   "Modified since last write; write or use ! to override.");
+               return (1);
+       }
+
+       /*
+        * !!!
+        * Historic practice: quit! or two quit's done in succession
+        * (where ZZ counts as a quit) didn't check for other files.
+        *
+        * Also check for related screens; if they exist, quit, the
+        * user will get the message on the last screen.
+        */
+       if (!force && sp->ccnt != sp->q_ccnt + 1 &&
+           ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+               sp->q_ccnt = sp->ccnt;
+               msgq(sp, M_ERR,
+       "More files; use \":n\" to go to the next file, \":q!\" to quit.");
+               return (1);
+       }
+
+       F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_file.c b/usr.bin/vi/nex/ex_file.c
new file mode 100644 (file)
index 0000000..d556d52
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_file.c  8.7 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_file -- :f[ile] [name]
+ *     Status line and change the file's name.
+ */
+int
+ex_file(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       FREF *frp;
+       char *p, *t;
+
+       switch (cmdp->argc) {
+       case 0:
+               break;
+       case 1:
+               frp = sp->frp;
+
+               /* Make sure can allocate enough space. */
+               if ((p = strdup(cmdp->argv[0]->bp)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       return (1);
+               }
+
+               /* If already have a file name, it becomes the alternate. */
+               if ((t = FILENAME(frp)) != NULL)
+                       set_alt_name(sp, t);
+
+               /* Free any previously changed name. */
+               if (frp->cname != NULL)
+                       free(frp->cname);
+               frp->cname = p;
+
+               /* The read-only bit follows the file name; clear it. */
+               F_CLR(frp, FR_RDONLY);
+
+               /* Have to force a write if the file exists, next time. */
+               F_CLR(frp, FR_CHANGEWRITE);
+               break;
+       default:
+               abort();
+       }
+       status(sp, ep, sp->lno, 1);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_global.c b/usr.bin/vi/nex/ex_global.c
new file mode 100644 (file)
index 0000000..4f942a3
--- /dev/null
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_global.c        8.29 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "interrupt.h"
+
+enum which {GLOBAL, VGLOBAL};
+
+static int     global __P((SCR *, EXF *, EXCMDARG *, enum which));
+static void    global_intr __P((int));
+
+/*
+ * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
+ *     Exec on lines matching a pattern.
+ */
+int
+ex_global(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (global(sp, ep,
+           cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL));
+}
+
+/*
+ * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands]
+ *     Exec on lines not matching a pattern.
+ */
+int
+ex_vglobal(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (global(sp, ep, cmdp, VGLOBAL));
+}
+
+static int
+global(sp, ep, cmdp, cmd)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       enum which cmd;
+{
+       DECLARE_INTERRUPTS;
+       RANGE *rp;
+       EX_PRIVATE *exp;
+       recno_t elno, lno;
+       regmatch_t match[1];
+       regex_t *re, lre;
+       size_t clen, len;
+       int delim, eval, reflags, replaced, rval;
+       char *cb, *ptrn, *p, *t;
+
+       /*
+        * Skip leading white space.  Historic vi allowed any non-
+        * alphanumeric to serve as the global command delimiter.
+        */
+       for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
+       if (*p == '\0' || isalnum(*p)) {
+               msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+               return (1);
+       }
+       delim = *p++;
+
+       /*
+        * Get the pattern string, toss escaped characters.
+        *
+        * QUOTING NOTE:
+        * Only toss an escaped character if it escapes a delimiter.
+        */
+       for (ptrn = t = p;;) {
+               if (p[0] == '\0' || p[0] == delim) {
+                       if (p[0] == delim)
+                               ++p;
+                       /*
+                        * !!!
+                        * Nul terminate the pattern string -- it's passed
+                        * to regcomp which doesn't understand anything else.
+                        */
+                       *t = '\0';
+                       break;
+               }
+               if (p[0] == '\\' && p[1] == delim)
+                       ++p;
+               *t++ = *p++;
+       }
+
+       /* If the pattern string is empty, use the last one. */
+       if (*ptrn == '\0') {
+               if (!F_ISSET(sp, S_SRE_SET)) {
+                       msgq(sp, M_ERR, "No previous regular expression.");
+                       return (1);
+               }
+               re = &sp->sre;
+       } else {
+               /* Set RE flags. */
+               reflags = 0;
+               if (O_ISSET(sp, O_EXTENDED))
+                       reflags |= REG_EXTENDED;
+               if (O_ISSET(sp, O_IGNORECASE))
+                       reflags |= REG_ICASE;
+
+               /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+               if (re_conv(sp, &ptrn, &replaced))
+                       return (1);
+
+               /* Compile the RE. */
+               re = &lre;
+               eval = regcomp(re, ptrn, reflags);
+
+               /* Free up any allocated memory. */
+               if (replaced)
+                       free(ptrn);
+
+               if (eval) {
+                       re_error(sp, eval, re);
+                       return (1);
+               }
+
+               /*
+                * Set saved RE.  Historic practice is that
+                * globals set direction as well as the RE.
+                */
+               sp->sre = lre;
+               sp->searchdir = FORWARD;
+               F_SET(sp, S_SRE_SET);
+       }
+
+       /* Get a copy of the command string. */
+       if ((clen = strlen(p)) == 0) {
+               msgq(sp, M_ERR, "No command string specified.");
+               return (1);
+       }
+       MALLOC_RET(sp, cb, char *, clen);
+       memmove(cb, p, clen);
+
+       /*
+        * The global commands sets the substitute RE as well as
+        * the everything-else RE.
+        */
+       sp->subre = sp->sre;
+       F_SET(sp, S_SUBRE_SET);
+
+       /* Set the global flag, and set up interrupts. */
+       F_SET(sp, S_GLOBAL);
+       SET_UP_INTERRUPTS(global_intr);
+
+       /*
+        * For each line...  The semantics of global matching are that we first
+        * have to decide which lines are going to get passed to the command,
+        * and then pass them to the command, ignoring other changes.  There's
+        * really no way to do this in a single pass, since arbitrary line
+        * creation, deletion and movement can be done in the ex command.  For
+        * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
+        * What we do is create linked list of lines that are tracked through
+        * each ex command.  There's a callback routine which the DB interface
+        * routines call when a line is created or deleted.  This doesn't help
+        * the layering much. 
+        */
+       exp = EXP(sp);
+       for (rval = 0, lno = cmdp->addr1.lno,
+           elno = cmdp->addr2.lno; lno <= elno; ++lno) {
+               /* Someone's unhappy, time to stop. */
+               if (F_ISSET(sp, S_INTERRUPTED))
+                       goto interrupted;
+
+               /* Get the line and search for a match. */
+               if ((t = file_gline(sp, ep, lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, lno);
+                       goto err;
+               }
+               match[0].rm_so = 0;
+               match[0].rm_eo = len;
+               switch(eval = regexec(re, t, 1, match, REG_STARTEND)) {
+               case 0:
+                       if (cmd == VGLOBAL)
+                               continue;
+                       break;
+               case REG_NOMATCH:
+                       if (cmd == GLOBAL)
+                               continue;
+                       break;
+               default:
+                       re_error(sp, eval, re);
+                       goto err;
+               }
+
+               /* If follows the last entry, extend the last entry's range. */
+               if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq &&
+                   rp->stop == lno - 1) {
+                       ++rp->stop;
+                       continue;
+               }
+
+               /* Allocate a new range, and append it to the list. */
+               CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
+               if (rp == NULL)
+                       goto err;
+               rp->start = rp->stop = lno;
+               CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q);
+       }
+
+       exp = EXP(sp);
+       exp->range_lno = OOBLNO;
+       for (;;) {
+               /*
+                * Start at the beginning of the range each time, it may have
+                * been changed (or exhausted) if lines were inserted/deleted.
+                */
+               if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq)
+                       break;
+               if (rp->start > rp->stop) {
+                       CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+                       free(rp);
+                       continue;
+               }
+
+               /*
+                * Execute the command, setting the cursor to the line so that
+                * relative addressing works.  This means that the cursor moves
+                * to the last line sent to the command, by default, even if
+                * the command fails.
+                */
+               exp->range_lno = sp->lno = rp->start++;
+               if (ex_cmd(sp, ep, cb, clen))
+                       goto err;
+
+               /* Someone's unhappy, time to stop. */
+               if (F_ISSET(sp, S_INTERRUPTED)) {
+interrupted:           msgq(sp, M_INFO, "Interrupted.");
+                       break;
+               }
+       }
+
+       /* Set the cursor to the new value, making sure it exists. */
+       if (exp->range_lno != OOBLNO) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               sp->lno =
+                   lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno;
+       }
+       if (0) {
+err:           rval = 1;
+       }
+
+interrupt_err:
+       F_CLR(sp, S_GLOBAL);
+       TEAR_DOWN_INTERRUPTS;
+
+       /* Free any remaining ranges and the command buffer. */
+       while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) {
+               CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+               free(rp);
+       }
+       free(cb);
+       return (rval);
+}
+
+/*
+ * global_insdel --
+ *     Update the ranges based on an insertion or deletion.
+ */
+void
+global_insdel(sp, ep, op, lno)
+       SCR *sp;
+       EXF *ep;
+       enum operation op;
+       recno_t lno;
+{
+       EX_PRIVATE *exp;
+       RANGE *nrp, *rp;
+
+       exp = EXP(sp);
+
+       switch (op) {
+       case LINE_APPEND:
+               return;
+       case LINE_DELETE:
+               for (rp = exp->rangeq.cqh_first;
+                   rp != (void *)&exp->rangeq; rp = nrp) {
+                       nrp = rp->q.cqe_next;
+                       /* If range less than the line, ignore it. */
+                       if (rp->stop < lno)
+                               continue;
+                       /* If range greater than the line, decrement range. */
+                       if (rp->start > lno) {
+                               --rp->start;
+                               --rp->stop;
+                               continue;
+                       }
+                       /* Lno is inside the range, decrement the end point. */
+                       if (rp->start > --rp->stop) {
+                               CIRCLEQ_REMOVE(&exp->rangeq, rp, q);
+                               free(rp);
+                       }
+               }
+               break;
+       case LINE_INSERT:
+               for (rp = exp->rangeq.cqh_first;
+                   rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) {
+                       /* If range less than the line, ignore it. */
+                       if (rp->stop < lno)
+                               continue;
+                       /* If range greater than the line, increment range. */
+                       if (rp->start >= lno) {
+                               ++rp->start;
+                               ++rp->stop;
+                               continue;
+                       }
+                       /*
+                        * Lno is inside the range, so the range must be split.
+                        * Since we're inserting a new element, neither range
+                        * can be exhausted.
+                        */
+                       CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE));
+                       if (nrp == NULL) {
+                               F_SET(sp, S_INTERRUPTED);
+                               return;
+                       }
+                       nrp->start = lno + 1;
+                       nrp->stop = rp->stop + 1;
+                       rp->stop = lno - 1;
+                       CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q);
+                       rp = nrp;
+               }
+               break;
+       case LINE_RESET:
+               return;
+       }
+       /*
+        * If the command deleted/inserted lines, the cursor moves to
+        * the line after the deleted/inserted line.
+        */
+       exp->range_lno = lno;
+}
+
+/*
+ * global_intr --
+ *     Set the interrupt bit in any screen that is running an interruptible
+ *     global.
+ *
+ * XXX
+ * In the future this may be a problem.  The user should be able to move to
+ * another screen and keep typing while this runs.  If so, and the user has
+ * more than one global running, it will be hard to decide which one to
+ * stop.
+ */
+static void
+global_intr(signo)
+       int signo;
+{
+       SCR *sp;
+
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               if (F_ISSET(sp, S_GLOBAL) && F_ISSET(sp, S_INTERRUPTIBLE))
+                       F_SET(sp, S_INTERRUPTED);
+}
diff --git a/usr.bin/vi/nex/ex_init.c b/usr.bin/vi/nex/ex_init.c
new file mode 100644 (file)
index 0000000..26acf2f
--- /dev/null
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_init.c  8.11 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+/*
+ * ex_screen_copy --
+ *     Copy ex screen.
+ */
+int
+ex_screen_copy(orig, sp)
+       SCR *orig, *sp;
+{
+       EX_PRIVATE *oexp, *nexp;
+
+       /* Create the private ex structure. */
+       CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE));
+       sp->ex_private = nexp;
+
+       /* Initialize queues. */
+       TAILQ_INIT(&nexp->tagq);
+       TAILQ_INIT(&nexp->tagfq);
+       CIRCLEQ_INIT(&nexp->rangeq);
+
+       if (orig == NULL) {
+               nexp->at_lbuf_set = 0;
+       } else {
+               oexp = EXP(orig);
+
+               nexp->at_lbuf = oexp->at_lbuf;
+               nexp->at_lbuf_set = oexp->at_lbuf_set;
+
+               if (oexp->lastbcomm != NULL &&
+                   (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       return(1);
+               }
+
+               if (ex_tagcopy(orig, sp))
+                       return (1);
+       }
+
+       nexp->lastcmd = &cmds[C_PRINT];
+       return (0);
+}
+
+/*
+ * ex_screen_end --
+ *     End a vi screen.
+ */
+int
+ex_screen_end(sp)
+       SCR *sp;
+{
+       EX_PRIVATE *exp;
+       int rval;
+
+       rval = 0;
+       exp = EXP(sp);
+
+       if (argv_free(sp))
+               rval = 1;
+
+       if (exp->ibp != NULL)
+               FREE(exp->ibp, exp->ibp_len);
+
+       if (exp->lastbcomm != NULL)
+               FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
+
+       /* Free private memory. */
+       FREE(exp, sizeof(EX_PRIVATE));
+       sp->ex_private = NULL;
+
+       return (rval);
+}
+
+/*
+ * ex_init --
+ *     Initialize ex.
+ */
+int
+ex_init(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       size_t len;
+
+       /*
+        * The default address is the last line of the file.  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 (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 {
+               if (file_lline(sp, ep, &sp->lno))
+                       return (1);
+               if (sp->lno == 0)
+                       sp->lno = 1;
+               sp->cno = 0;
+       }
+
+       /* Display the status line. */
+       return (status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * ex_end --
+ *     End ex session.
+ */
+int
+ex_end(sp)
+       SCR *sp;
+{
+       /* Save the cursor location. */
+       sp->frp->lno = sp->lno;
+       sp->frp->cno = sp->cno;
+       F_SET(sp->frp, FR_CURSORSET);
+
+       return (0);
+}
+
+/*
+ * ex_optchange --
+ *     Handle change of options for vi.
+ */
+int
+ex_optchange(sp, opt)
+       SCR *sp;
+       int opt;
+{
+       switch (opt) {
+       case O_TAGS:
+               return (ex_tagalloc(sp, O_STR(sp, O_TAGS)));
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_join.c b/usr.bin/vi/nex/ex_join.c
new file mode 100644 (file)
index 0000000..33bace9
--- /dev/null
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_join.c  8.8 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
+ *     Join lines.
+ */
+int
+ex_join(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       recno_t from, to;
+       size_t blen, clen, len, tlen;
+       int echar, extra, first;
+       char *bp, *p, *tbp;
+
+       from = cmdp->addr1.lno;
+       to = cmdp->addr2.lno;
+
+       /* Check for no lines to join. */
+       if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) {
+               msgq(sp, M_ERR, "No following lines to join.");
+               return (1);
+       }
+
+       GET_SPACE_RET(sp, bp, blen, 256);
+
+       /*
+        * The count for the join command was off-by-one,
+        * historically, to other counts for other commands.
+        */
+       if (F_ISSET(cmdp, E_COUNT))
+               ++cmdp->addr2.lno;
+
+       /*
+        * If only a single address specified, or, the same address
+        * specified twice, the from/two addresses will be the same.
+        */
+       if (cmdp->addr1.lno == cmdp->addr2.lno)
+               ++cmdp->addr2.lno;
+
+       clen = tlen = 0;
+        for (first = 1, from = cmdp->addr1.lno,
+           to = cmdp->addr2.lno; from <= to; ++from) {
+               /*
+                * Get next line.  Historic versions of vi allowed "10J" while
+                * less than 10 lines from the end-of-file, so we do too.
+                */
+               if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+                       cmdp->addr2.lno = from - 1;
+                       break;
+               }
+
+               /* Empty lines just go away. */
+               if (len == 0)
+                       continue;
+
+               /*
+                * Get more space if necessary.  Note, tlen isn't the length
+                * of the new line, it's roughly the amount of space needed.
+                * tbp - bp is the length of the new line.
+                */
+               tlen += len + 2;
+               ADD_SPACE_RET(sp, bp, blen, tlen);
+               tbp = bp + clen;
+
+               /*
+                * Historic practice:
+                *
+                * If force specified, join without modification.
+                * If the current line ends with whitespace, strip leading
+                *    whitespace from the joined line.
+                * If the next line starts with a ), do nothing.
+                * If the current line ends with ., ? or !, insert two spaces.
+                * Else, insert one space.
+                *
+                * Echar is the last character in the last line joined.
+                */
+               extra = 0;
+               if (!first && !F_ISSET(cmdp, E_FORCE)) {
+                       if (isblank(echar))
+                               for (; len && isblank(*p); --len, ++p);
+                       else if (p[0] != ')') {
+                               if (strchr(".?!", echar)) {
+                                       *tbp++ = ' ';
+                                       ++clen;
+                                       extra = 1;
+                               }
+                               *tbp++ = ' ';
+                               ++clen;
+                               for (; len && isblank(*p); --len, ++p);
+                       }
+               }
+                       
+               if (len != 0) {
+                       memmove(tbp, p, len);
+                       tbp += len;
+                       clen += len;
+                       echar = p[len - 1];
+               } else
+                       echar = ' ';
+
+               /*
+                * Historic practice for vi was to put the cursor at the first
+                * inserted whitespace character, if there was one, or the
+                * first character of the joined line, if there wasn't, or the
+                * last character of the line if joined to an empty line.  If
+                * a count was specified, the cursor was moved as described
+                * for the first line joined, ignoring subsequent lines.  If
+                * the join was a ':' command, the cursor was placed at the
+                * first non-blank character of the line unless the cursor was
+                * "attracted" to the end of line when the command was executed
+                * in which case it moved to the new end of line.  There are
+                * probably several more special cases, but frankly, my dear,
+                * I don't give a damn.  This implementation puts the cursor
+                * on the first inserted whitespace character, the first
+                * character of the joined line, or the last character of the
+                * line regardless.  Note, if the cursor isn't on the joined
+                * line (possible with : commands), it is reset to the starting
+                * line.
+                */
+               if (first) {
+                       sp->cno = (tbp - bp) - (1 + extra);
+                       first = 0;
+               } else
+                       sp->cno = (tbp - bp) - len - (1 + extra);
+       }
+       sp->lno = cmdp->addr1.lno;
+
+       /* Delete the joined lines. */
+        for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
+               if (file_dline(sp, ep, to))
+                       goto err;
+               
+       /* Reset the original line. */
+       if (file_sline(sp, ep, from, bp, tbp - bp)) {
+err:           FREE_SPACE(sp, bp, blen);
+               return (1);
+       }
+       FREE_SPACE(sp, bp, blen);
+
+       sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_map.c b/usr.bin/vi/nex/ex_map.c
new file mode 100644 (file)
index 0000000..0154c34
--- /dev/null
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_map.c   8.6 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "excmd.h"
+
+/*
+ * ex_map -- :map[!] [input] [replacement]
+ *     Map a key/string or display mapped keys.
+ *
+ * Historical note:
+ *     Historic vi maps were fairly bizarre, and likely to differ in
+ *     very subtle and strange ways from this implementation.  Two
+ *     things worth noting are that vi would often hang or drop core
+ *     if the map was strange enough (ex: map X "xy$@x^V), or, simply
+ *     not work.  One trick worth remembering is that if you put a
+ *     mark at the start of the map, e.g. map X mx"xy ...), or if you
+ *     put the map in a .exrc file, things would often work much better.
+ *     No clue why.
+ */
+int
+ex_map(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       enum seqtype stype;
+       CHAR_T *input;
+       size_t nlen;
+       int key;
+       char *name, buf[10];
+
+       stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND;
+
+       switch (cmdp->argc) {
+       case 0:
+               if (seq_dump(sp, stype, 1) == 0)
+                       msgq(sp, M_INFO, "No %s map entries.",
+                           stype == SEQ_INPUT ? "input" : "command");
+               return (0);
+       case 2:
+               input = cmdp->argv[0]->bp;
+               break;
+       default:
+               abort();
+       }
+
+       /*
+        * If the mapped string is #[0-9] (and wasn't quoted in any
+        * way, then map to a function key.
+        */
+       nlen = 0;
+       if (input[0] == '#' && isdigit(input[1]) && !input[2]) {
+               key = atoi(input + 1);
+               nlen = snprintf(buf, sizeof(buf), "f%d", key);
+#ifdef notdef
+               if (FKEY[key]) {                /* CCC */
+                       input = FKEY[key];
+                       name = buf;
+               } else {
+                       msgq(sp, M_ERR, "This terminal has no %s key.", buf);
+                       return (1);
+               }
+#else
+               name = NULL;
+#endif
+       } else {
+               name = NULL;
+
+               /* Some single keys may not be remapped in command mode. */
+               if (stype == SEQ_COMMAND && input[1] == '\0')
+                       switch (term_key_val(sp, input[0])) {
+                       case K_COLON:
+                       case K_CR:
+                       case K_ESCAPE:
+                       case K_NL:
+                               msgq(sp, M_ERR,
+                                   "The %s character may not be remapped.",
+                                   charname(sp, input[0]));
+                               return (1);
+                       }
+       }
+       return (seq_set(sp, name, nlen, input, cmdp->argv[0]->len,
+           cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, 1));
+}
+
+/*
+ * ex_unmap -- (:unmap[!] key)
+ *     Unmap a key.
+ */
+int
+ex_unmap(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+           F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) {
+               msgq(sp, M_INFO, "\"%s\" isn't mapped.", cmdp->argv[0]->bp);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * map_save --
+ *     Save the mapped sequences to a file.
+ */
+int
+map_save(sp, fp)
+       SCR *sp;
+       FILE *fp;
+{
+       if (seq_save(sp, fp, "map ", SEQ_COMMAND))
+               return (1);
+       return (seq_save(sp, fp, "map! ", SEQ_INPUT));
+}
diff --git a/usr.bin/vi/nex/ex_mark.c b/usr.bin/vi/nex/ex_mark.c
new file mode 100644 (file)
index 0000000..34efe21
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mark.c  8.4 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_mark(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (cmdp->argv[0]->len != 1) {
+               msgq(sp, M_ERR, "Mark names must be a single character.");
+               return (1);
+       }
+       return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
+}
diff --git a/usr.bin/vi/nex/ex_mkexrc.c b/usr.bin/vi/nex/ex_mkexrc.c
new file mode 100644 (file)
index 0000000..b26c7f8
--- /dev/null
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mkexrc.c        8.8 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "seq.h"
+#include "pathnames.h"
+
+/*
+ * ex_mkexrc -- :mkexrc[!] [file]
+ *
+ * Create (or overwrite) a .exrc file with the current info.
+ */
+int
+ex_mkexrc(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       struct stat sb;
+       FILE *fp;
+       int fd, sverrno;
+       char *fname;
+
+       switch (cmdp->argc) {
+       case 0:
+               fname = _PATH_EXRC;
+               break;
+       case 1:
+               fname = cmdp->argv[0]->bp;
+               set_alt_name(sp, fname);
+               break;
+       default:
+               abort();
+       }
+
+       if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) {
+               msgq(sp, M_ERR,
+                   "%s exists, not written; use ! to override.", fname);
+               return (1);
+       }
+
+       /* Create with max permissions of rw-r--r--. */
+       if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+               msgq(sp, M_SYSERR, fname);
+               return (1);
+       }
+
+       if ((fp = fdopen(fd, "w")) == NULL) {
+               sverrno = errno;
+               (void)close(fd);
+               errno = sverrno;
+               goto e2;
+       }
+
+       if (abbr_save(sp, fp) || ferror(fp))
+               goto e1;
+       if (map_save(sp, fp) || ferror(fp))
+               goto e1;
+       if (opts_save(sp, fp) || ferror(fp))
+               goto e1;
+#ifndef NO_DIGRAPH
+       digraph_save(sp, fd);
+#endif
+       if (fclose(fp))
+               goto e2;
+
+       msgq(sp, M_INFO, "New .exrc file: %s. ", fname);
+       return (0);
+
+e1:    sverrno = errno;
+       (void)fclose(fp);
+       errno = sverrno;
+e2:    msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno));
+       return (1);
+}
diff --git a/usr.bin/vi/nex/ex_move.c b/usr.bin/vi/nex/ex_move.c
new file mode 100644 (file)
index 0000000..965618b
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_move.c  8.6 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {COPY, MOVE};
+static int cm __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_copy -- :[line [,line]] co[py] line [flags]
+ *     Copy selected lines.
+ */
+int
+ex_copy(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (cm(sp, ep, cmdp, COPY));
+}
+
+/*
+ * ex_move -- :[line [,line]] co[py] line
+ *     Move selected lines.
+ */
+int
+ex_move(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (cm(sp, ep, cmdp, MOVE));
+}
+
+static int
+cm(sp, ep, cmdp, cmd)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       enum which cmd;
+{
+       CB cb;
+       MARK fm1, fm2, m, tm;
+       recno_t diff;
+       int rval;
+
+       fm1 = cmdp->addr1;
+       fm2 = cmdp->addr2;
+       tm.lno = cmdp->lineno;
+       tm.cno = 0;
+
+       /* Make sure the destination is valid. */
+       if (cmd == MOVE && tm.lno >= fm1.lno && tm.lno < fm2.lno) {
+               msgq(sp, M_ERR, "Destination line is inside move range.");
+               return (1);
+       }
+
+       /* Save the text to a cut buffer. */
+       memset(&cb, 0, sizeof(cb));
+       CIRCLEQ_INIT(&cb.textq);
+       if (cut(sp, ep, &cb, NULL, &fm1, &fm2, CUT_LINEMODE))
+               return (1);
+
+       /* If we're not copying, delete the old text and adjust tm. */
+       if (cmd == MOVE) {
+               if (delete(sp, ep, &fm1, &fm2, 1)) {
+                       rval = 1;
+                       goto err;
+               }
+               if (tm.lno >= fm1.lno)
+                       tm.lno -= (fm2.lno - fm1.lno) + 1;
+       }
+
+       /* Add the new text. */
+       if (put(sp, ep, &cb, NULL, &tm, &m, 1)) {
+               rval = 1;
+               goto err;
+       }
+
+       /*
+        * Move and copy put the cursor on the last line moved or copied.
+        * The returned cursor from the put routine is the first line put,
+        * not the last, because that's the semantics of vi.
+        */
+       diff = (fm2.lno - fm1.lno) + 1;
+       sp->lno = m.lno + (diff - 1);
+       sp->cno = 0;
+
+       sp->rptlines[cmd == COPY ? L_COPIED : L_MOVED] += diff;
+       rval = 0;
+
+err:   (void)text_lfree(&cb.textq);
+       return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_open.c b/usr.bin/vi/nex/ex_open.c
new file mode 100644 (file)
index 0000000..77b418a
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_open.c  8.2 (Berkeley) 10/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_open -- :[line] o[pen] [/pattern/] [flags]
+ *
+ *     Switch to single line "open" mode.
+ */
+int
+ex_open(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       /* If open option off, disallow open command. */
+       if (!O_ISSET(sp, O_OPEN)) {
+               msgq(sp, M_ERR,
+                   "The open command requires that the open option be set.");
+               return (1);
+       }
+
+       msgq(sp, M_ERR, "The open command is not yet implemented.");
+       return (1);
+}
diff --git a/usr.bin/vi/nex/ex_preserve.c b/usr.bin/vi/nex/ex_preserve.c
new file mode 100644 (file)
index 0000000..a7f3548
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_preserve.c      8.4 (Berkeley) 11/8/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_preserve -- :pre[serve]
+ *     Push the file to recovery.
+ */
+int
+ex_preserve(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       recno_t lno;
+
+       if (!F_ISSET(ep, F_RCV_ON)) {
+               msgq(sp, M_ERR, "Preservation of this file not possible.");
+               return (1);
+       }
+
+       /* If recovery not initialized, do so. */
+       if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep))
+               return (1);
+
+       /* Force the file to be read in, in case it hasn't yet. */
+       if (file_lline(sp, ep, &lno))
+               return (1);
+
+       /* Sync to disk. */
+       if (rcv_sync(sp, ep))
+               return (1);
+
+       /* Preserve the recovery files. */
+       F_SET(ep, F_RCV_NORM);
+
+       msgq(sp, M_INFO, "File preserved.");
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_print.c b/usr.bin/vi/nex/ex_print.c
new file mode 100644 (file)
index 0000000..d25e9ff
--- /dev/null
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_print.c 8.6 (Berkeley) 11/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_list -- :[line [,line]] l[ist] [count] [flags]
+ *
+ *     Display the addressed lines such that the output is unambiguous.
+ */
+int
+ex_list(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (ex_print(sp, ep,
+           &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST))
+               return (1);
+       sp->lno = cmdp->addr2.lno;
+       sp->cno = cmdp->addr2.cno;
+       return (0);
+}
+
+/*
+ * ex_number -- :[line [,line]] nu[mber] [count] [flags]
+ *
+ *     Display the addressed lines with a leading line number.
+ */
+int
+ex_number(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (ex_print(sp, ep,
+           &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH))
+               return (1);
+       sp->lno = cmdp->addr2.lno;
+       sp->cno = cmdp->addr2.cno;
+       return (0);
+}
+
+/*
+ * ex_pr -- :[line [,line]] p[rint] [count] [flags]
+ *
+ *     Display the addressed lines.
+ */
+int
+ex_pr(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags))
+               return (1);
+       sp->lno = cmdp->addr2.lno;
+       sp->cno = cmdp->addr2.cno;
+       return (0);
+}
+
+/*
+ * ex_print --
+ *     Print the selected lines.
+ */
+int
+ex_print(sp, ep, fp, tp, flags)
+       SCR *sp;
+       EXF *ep;
+       MARK *fp, *tp;
+       register int flags;
+{
+       register int ch, col, rlen;
+       recno_t from, to;
+       size_t len;
+       int cnt;
+       char *p, buf[10];
+
+       F_SET(sp, S_INTERRUPTIBLE);
+       for (from = fp->lno, to = tp->lno; from <= to; ++from) {
+               /* Display the line number. */
+               if (LF_ISSET(E_F_HASH))
+                       col = ex_printf(EXCOOKIE, "%7ld ", from);
+               else
+                       col = 0;
+       
+               /*
+                * Display the line.  The format for E_F_PRINT isn't very good,
+                * especially in handling end-of-line tabs, but they're almost
+                * backward compatible.
+                */
+               if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+                       GETLINE_ERR(sp, from);
+                       return (1);
+               }
+
+#define        WCHECK(ch) {                                                    \
+       if (col == sp->cols) {                                          \
+               (void)ex_printf(EXCOOKIE, "\n");                        \
+               col = 0;                                                \
+       }                                                               \
+       (void)ex_printf(EXCOOKIE, "%c", ch);                            \
+       ++col;                                                          \
+}
+               for (rlen = len; rlen--;) {
+                       ch = *p++;
+                       if (LF_ISSET(E_F_LIST))
+                               if (ch != '\t' && isprint(ch)) {
+                                       WCHECK(ch);
+                               } else if (ch & 0x80) {
+                                       (void)snprintf(buf,
+                                           sizeof(buf), "\\%03o", ch);
+                                       len = strlen(buf);
+                                       for (cnt = 0; cnt < len; ++cnt)
+                                               WCHECK(buf[cnt]);
+                               } else {
+                                       WCHECK('^');
+                                       WCHECK(ch + 0x40);
+                               }
+                       else {
+                               ch &= 0x7f;
+                               if (ch == '\t') {
+                                       while (col < sp->cols &&
+                                           ++col % O_VAL(sp, O_TABSTOP))
+                                               (void)ex_printf(EXCOOKIE, " ");
+                                       if (col == sp->cols) {
+                                               col = 0;
+                                               (void)ex_printf(EXCOOKIE, "\n");
+                                       }
+                               } else if (isprint(ch)) {
+                                       WCHECK(ch);
+                               } else if (ch == '\n') {
+                                       col = 0;
+                                       (void)ex_printf(EXCOOKIE, "\n");
+                               } else {
+                                       WCHECK('^');
+                                       WCHECK(ch + 0x40);
+                               }
+                       }
+               }
+               if (LF_ISSET(E_F_LIST)) {
+                       WCHECK('$');
+               } else if (len == 0) {
+                       /*
+                        * If the line is empty, output a space
+                        * to overwrite the colon prompt.
+                        */
+                       WCHECK(' ');
+               }
+               (void)ex_printf(EXCOOKIE, "\n");
+
+               if (F_ISSET(sp, S_INTERRUPTED))
+                       break;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_put.c b/usr.bin/vi/nex/ex_put.c
new file mode 100644 (file)
index 0000000..3bc687f
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_put.c   8.4 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_put -- [line] pu[t] [buffer]
+ *
+ *     Append a cut buffer into the file.
+ */
+int
+ex_put(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       MARK m;
+
+       m.lno = sp->lno;
+       m.cno = sp->cno;
+       if (put(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+           &cmdp->addr1, &m, 1))
+               return (1);
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_read.c b/usr.bin/vi/nex/ex_read.c
new file mode 100644 (file)
index 0000000..9328b93
--- /dev/null
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_read.c  8.21 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_read --  :read [file]
+ *             :read [! cmd]
+ *     Read from a file or utility.
+ */
+int
+ex_read(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       struct stat sb;
+       FILE *fp;
+       MARK rm;
+       recno_t nlines;
+       size_t blen, len;
+       int rval;
+       char *bp, *name;
+
+       /*
+        * If "read !", it's a pipe from a utility.
+        *
+        * !!!
+        * Historical vi wouldn't undo a filter read, for no apparent
+        * reason.
+        */
+       if (F_ISSET(cmdp, E_FORCE)) {
+               /* Expand the user's argument. */
+               if (argv_exp1(sp, ep,
+                   cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0))
+                       return (1);
+
+               /* If argc still 1, there wasn't anything to expand. */
+               if (cmdp->argc == 1) {
+                       msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+                       return (1);
+               }
+
+               /* Redisplay the user's argument if it's changed. */
+               if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) {
+                       len = cmdp->argv[1]->len;
+                       GET_SPACE_RET(sp, bp, blen, len + 2);
+                       bp[0] = '!';
+                       memmove(bp + 1, cmdp->argv[1], cmdp->argv[1]->len + 1);
+                       (void)sp->s_busy(sp, bp);
+                       FREE_SPACE(sp, bp, blen);
+               }
+
+               if (filtercmd(sp, ep,
+                   &cmdp->addr1, NULL, &rm, cmdp->argv[1]->bp, FILTER_READ))
+                       return (1);
+               sp->lno = rm.lno;
+               return (0);
+       }
+
+       /* Expand the user's argument. */
+       if (argv_exp2(sp, ep,
+           cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0))
+               return (1);
+
+       switch (cmdp->argc) {
+       case 1:
+               /*
+                * No arguments, read the current file.
+                * Doesn't set the alternate file name.
+                */
+               name = FILENAME(sp->frp);
+               break;
+       case 2:
+               /*
+                * One argument, read it.
+                * Sets the alternate file name.
+                */
+               name = cmdp->argv[1]->bp;
+               set_alt_name(sp, name);
+               break;
+       default:
+               /* If expanded to more than one argument, object. */
+               msgq(sp, M_ERR,
+                   "%s expanded into too many file names", cmdp->argv[0]->bp);
+               msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+               return (1);
+       }
+
+       /*
+        * !!!
+        * Historically, vi did not permit reads from non-regular files,
+        * nor did it distinguish between "read !" and "read!", so there
+        * was no way to "force" it.
+        */
+       if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
+               msgq(sp, M_SYSERR, name);
+               return (1);
+       }
+       if (!S_ISREG(sb.st_mode)) {
+               (void)fclose(fp);
+               msgq(sp, M_ERR, "Only regular files may be read.");
+               return (1);
+       }
+
+       rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1);
+
+       /*
+        * Set the cursor to the first line read in, if anything read
+        * in, otherwise, the address.  (Historic vi set it to the
+        * line after the address regardless, but since that line may
+        * not exist we don't bother.)
+        */
+       sp->lno = cmdp->addr1.lno;
+       if (nlines)
+               ++sp->lno;
+       
+       F_SET(EXP(sp), EX_AUTOPRINT);
+       return (rval);
+}
+
+/*
+ * ex_readfp --
+ *     Read lines into the file.
+ */
+int
+ex_readfp(sp, ep, name, fp, fm, nlinesp, success_msg)
+       SCR *sp;
+       EXF *ep;
+       char *name;
+       FILE *fp;
+       MARK *fm;
+       recno_t *nlinesp;
+       int success_msg;
+{
+       EX_PRIVATE *exp;
+       u_long ccnt;
+       size_t len;
+       recno_t lno, nlines;
+       int rval;
+
+       /*
+        * Add in the lines from the output.  Insertion starts at the line
+        * following the address.
+        */
+       ccnt = 0;
+       rval = 0;
+       exp = EXP(sp);
+       for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno) {
+               if (file_aline(sp, ep, 1, lno, exp->ibp, len)) {
+                       rval = 1;
+                       break;
+               }
+               ccnt += len;
+       }
+
+       if (ferror(fp)) {
+               msgq(sp, M_SYSERR, name);
+               rval = 1;
+       }
+
+       if (fclose(fp)) {
+               msgq(sp, M_SYSERR, name);
+               return (1);
+       }
+
+       if (rval)
+               return (1);
+
+       /* Return the number of lines read in. */
+       nlines = lno - fm->lno;
+       if (nlinesp != NULL)
+               *nlinesp = nlines;
+
+       if (success_msg)
+               msgq(sp, M_INFO, "%s: %lu line%s, %lu characters.",
+                   name, nlines, nlines == 1 ? "" : "s", ccnt);
+
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_screen.c b/usr.bin/vi/nex/ex_screen.c
new file mode 100644 (file)
index 0000000..0fd000d
--- /dev/null
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_screen.c        8.10 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_split -- :s[plit] [file ...]
+ *     Split the screen, optionally setting the file list.
+ */
+int
+ex_split(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (sp->s_split(sp, cmdp->argc ? cmdp->argv : NULL));
+}
+
+/*
+ * ex_bg --    :bg
+ *     Hide the screen.
+ */
+int
+ex_bg(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (sp->s_bg(sp));
+}
+
+/*
+ * ex_fg --    :fg [file]
+ *     Show the screen.
+ */
+int
+ex_fg(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (sp->s_fg(sp, cmdp->argc ? cmdp->argv[0]->bp : NULL));
+}
+
+/*
+ * ex_resize --        :resize [change]
+ *     Change the screen size.
+ */
+int
+ex_resize(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (!F_ISSET(cmdp, E_COUNT))
+               cmdp->count = 1;
+       return (sp->s_rabs(sp, cmdp->count));
+}
+
+/*
+ * ex_sdisplay --
+ *     Display the list of screens.
+ */
+int
+ex_sdisplay(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       SCR *tsp;
+       int cnt, col, len, sep;
+
+       if ((tsp = sp->gp->hq.cqh_first) == (void *)&sp->gp->hq) {
+               (void)ex_printf(EXCOOKIE,
+                   "No backgrounded screens to display.\n");
+               return (0);
+       }
+
+       col = len = sep = 0;
+       for (cnt = 1; tsp != (void *)&sp->gp->hq; tsp = tsp->q.cqe_next) {
+               col += len = strlen(FILENAME(tsp->frp)) + sep;
+               if (col >= sp->cols - 1) {
+                       col = len;
+                       sep = 0;
+                       (void)ex_printf(EXCOOKIE, "\n");
+               } else if (cnt != 1) {
+                       sep = 1;
+                       (void)ex_printf(EXCOOKIE, " ");
+               }
+               (void)ex_printf(EXCOOKIE, "%s", FILENAME(tsp->frp));
+               ++cnt;
+       }
+       (void)ex_printf(EXCOOKIE, "\n");
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_script.c b/usr.bin/vi/nex/ex_script.c
new file mode 100644 (file)
index 0000000..606e479
--- /dev/null
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_script.c        8.10 (Berkeley) 12/22/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "script.h"
+
+/*
+ * XXX
+ */
+int openpty __P((int *, int *, char *, struct termios *, struct winsize *));
+
+static int sscr_getprompt __P((SCR *, EXF *));
+static int sscr_init __P((SCR *, EXF *));
+static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
+static int sscr_setprompt __P((SCR *, char *, size_t));
+
+/*
+ * ex_script -- : sc[ript][!] [file]
+ *
+ *     Switch to script mode.
+ */
+int
+ex_script(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       /* Vi only command. */
+       if (!IN_VI_MODE(sp)) {
+               msgq(sp, M_ERR,
+                   "The script command is only available in vi mode.");
+               return (1);
+       }
+
+       /* Switch to the new file. */
+       if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp))
+               return (1);
+
+       /*
+        * Create the shell, figure out the prompt.
+        *
+        * !!!
+        * The files just switched, use sp->ep.
+        */
+       if (sscr_init(sp, sp->ep))
+               return (1);
+
+       return (0);
+}
+
+/*
+ * sscr_init --
+ *     Create a pty setup for a shell.
+ */
+static int
+sscr_init(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       SCRIPT *sc;
+       char *sh, *sh_path;
+
+       MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
+       sp->script = sc;
+       sc->sh_prompt = NULL;
+       sc->sh_prompt_len = 0;
+
+       /*
+        * There are two different processes running through this code.
+        * They are the shell and the parent.
+        */
+       sc->sh_master = sc->sh_slave = -1;
+
+       if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
+               msgq(sp, M_SYSERR, "tcgetattr");
+               goto err;
+       }
+
+       /*
+        * Turn off output postprocessing and echo.
+        */
+       sc->sh_term.c_oflag &= ~OPOST;
+       sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);
+
+       if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
+               msgq(sp, M_SYSERR, "tcgetattr");
+               goto err;
+       }
+
+       if (openpty(&sc->sh_master,
+           &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
+               msgq(sp, M_SYSERR, "openpty");
+               goto err;
+       }
+
+       /*
+        * Don't use vfork() here, because the signal semantics
+        * differ from implementation to implementation.
+        */
+       switch (sc->sh_pid = fork()) {
+       case -1:                        /* Error. */
+               msgq(sp, M_SYSERR, "fork");
+err:           if (sc->sh_master != -1)
+                       (void)close(sc->sh_master);
+               if (sc->sh_slave != -1)
+                       (void)close(sc->sh_slave);
+               return (1);
+       case 0:                         /* Utility. */
+               /*
+                * The utility has default signal behavior.  Don't bother
+                * using sigaction(2) 'cause we want the default behavior.
+                */
+               (void)signal(SIGINT, SIG_DFL);
+               (void)signal(SIGQUIT, SIG_DFL);
+
+               /*
+                * XXX
+                * So that shells that do command line editing turn it off.
+                */
+               (void)putenv("TERM=emacs");
+               (void)putenv("TERMCAP=emacs:");
+               (void)putenv("EMACS=t");
+
+               (void)setsid();
+#ifdef TIOCSCTTY
+               /*
+                * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
+                * ioctl, not by opening a terminal device file.  POSIX 1003.1
+                * doesn't define a portable way to do this.  If TIOCSCTTY is
+                * not available, hope that the open does it.
+                */
+               (void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
+#endif
+               (void)close(sc->sh_master);
+               (void)dup2(sc->sh_slave, STDIN_FILENO);
+               (void)dup2(sc->sh_slave, STDOUT_FILENO);
+               (void)dup2(sc->sh_slave, STDERR_FILENO);
+               (void)close(sc->sh_slave);
+
+               /* Assumes that all shells have -i. */
+               sh_path = O_STR(sp, O_SHELL);
+               if ((sh = strrchr(sh_path, '/')) == NULL)
+                       sh = sh_path;
+               else
+                       ++sh;
+               execl(sh_path, sh, "-i", NULL);
+               msgq(sp, M_ERR,
+                   "Error: execl: %s: %s", sh_path, strerror(errno));
+               _exit(127);
+       default:
+               break;
+       }
+
+       if (sscr_getprompt(sp, ep))
+               return (1);
+
+       F_SET(sp, S_REDRAW | S_SCRIPT);
+       return (0);
+
+}
+
+/*
+ * sscr_getprompt --
+ *     Eat lines printed by the shell until a line with no trailing
+ *     carriage return comes; set the prompt from that line.
+ */
+static int
+sscr_getprompt(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       struct timeval tv;
+       SCRIPT *sc;
+       fd_set fdset;
+       recno_t lline;
+       size_t llen, len;
+       u_int value;
+       int nr;
+       char *endp, *p, *t, buf[1024];
+
+       FD_ZERO(&fdset);
+       endp = buf;
+       len = sizeof(buf);
+
+       /* Wait up to a second for characters to read. */
+       tv.tv_sec = 5;
+       tv.tv_usec = 0;
+       sc = sp->script;
+       FD_SET(sc->sh_master, &fdset);
+       switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+       case -1:                /* Error or interrupt. */
+               msgq(sp, M_SYSERR, "select");
+               goto prompterr;
+       case  0:                /* Timeout */
+               msgq(sp, M_ERR, "Error: timed out.");
+               goto prompterr;
+       case  1:                /* Characters to read. */
+               break;
+       }
+
+       /* Read the characters. */
+more:  len = sizeof(buf) - (endp - buf);
+       switch (nr = read(sc->sh_master, endp, len)) {
+       case  0:                        /* EOF. */
+               msgq(sp, M_ERR, "Error: shell: EOF");
+               goto prompterr;
+       case -1:                        /* Error or interrupt. */
+               msgq(sp, M_SYSERR, "shell");
+               goto prompterr;
+       default:
+               endp += nr;
+               break;
+       }
+
+       /* If any complete lines, push them into the file. */
+       for (p = t = buf; p < endp; ++p) {
+               value = term_key_val(sp, *p);
+               if (value == K_CR || value == K_NL) {
+                       if (file_lline(sp, ep, &lline) ||
+                           file_aline(sp, ep, 0, lline, t, p - t))
+                               goto prompterr;
+                       t = p + 1;
+               }
+       }
+       if (p > buf) {
+               memmove(buf, t, endp - t);
+               endp = buf + (endp - t);
+       }
+       if (endp == buf)
+               goto more;
+
+       /* Wait up 1/10 of a second to make sure that we got it all. */
+       tv.tv_sec = 0;
+       tv.tv_usec = 100000;
+       switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+       case -1:                /* Error or interrupt. */
+               msgq(sp, M_SYSERR, "select");
+               goto prompterr;
+       case  0:                /* Timeout */
+               break;
+       case  1:                /* Characters to read. */
+               goto more;
+       }
+
+       /* Timed out, so theoretically we have a prompt. */
+       llen = endp - buf;
+       endp = buf;
+
+       /* Append the line into the file. */
+       if (file_lline(sp, ep, &lline) ||
+           file_aline(sp, ep, 0, lline, buf, llen)) {
+prompterr:     sscr_end(sp);
+               return (1);
+       }
+
+       return (sscr_setprompt(sp, buf, llen));
+}
+
+/*
+ * sscr_exec --
+ *     Take a line and hand it off to the shell.
+ */
+int
+sscr_exec(sp, ep, lno)
+       SCR *sp;
+       EXF *ep;
+       recno_t lno;
+{
+       SCRIPT *sc;
+       recno_t last_lno;
+       size_t blen, len, last_len, tlen;
+       int matchprompt, nw, rval;
+       char *bp, *p;
+
+       /* If there's a prompt on the last line, append the command. */
+       if (file_lline(sp, ep, &last_lno))
+               return (1);
+       if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) {
+               GETLINE_ERR(sp, last_lno);
+               return (1);
+       }
+       if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
+               matchprompt = 1;
+               GET_SPACE_RET(sp, bp, blen, last_len + 128);
+               memmove(bp, p, last_len);
+       } else
+               matchprompt = 0;
+
+       /* Get something to execute. */
+       if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       goto err1;
+               if (lno == 0)
+                       goto empty;
+               else
+                       GETLINE_ERR(sp, lno);
+               goto err1;
+       }
+
+       /* Empty lines aren't interesting. */
+       if (len == 0)
+               goto empty;
+
+       /* Delete any prompt. */
+       if (sscr_matchprompt(sp, p, len, &tlen)) {
+               if (tlen == len) {
+empty:                 msgq(sp, M_BERR, "Nothing to execute.");
+                       goto err1;
+               }
+               p += (len - tlen);
+               len = tlen;
+       }
+
+       /* Push the line to the shell. */
+       sc = sp->script;
+       if ((nw = write(sc->sh_master, p, len)) != len)
+               goto err2;
+       rval = 0;
+       if (write(sc->sh_master, "\n", 1) != 1) {
+err2:          if (nw == 0)
+                       errno = EIO;
+               msgq(sp, M_SYSERR, "shell");
+               goto err1;
+       }
+
+       if (matchprompt) {
+               ADD_SPACE_RET(sp, bp, blen, last_len + len);
+               memmove(bp + last_len, p, len);
+               if (file_sline(sp, ep, last_lno, bp, last_len + len))
+err1:                  rval = 1;
+       }
+       if (matchprompt)
+               FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
+
+/*
+ * sscr_input --
+ *     Take a line from the shell and insert it into the file.
+ */
+int
+sscr_input(sp)
+       SCR *sp;
+{
+       struct timeval tv;
+       SCRIPT *sc;
+       EXF *ep;
+       recno_t lno;
+       size_t blen, len, tlen;
+       u_int value;
+       int nr, rval;
+       char *bp, *endp, *p, *t;
+
+       /* Find out where the end of the file is. */
+       ep = sp->ep;
+       if (file_lline(sp, ep, &lno))
+               return (1);
+
+#define        MINREAD 1024
+       GET_SPACE_RET(sp, bp, blen, MINREAD);
+       endp = bp;
+
+       /* Read the characters. */
+       rval = 1;
+       sc = sp->script;
+more:  switch (nr = read(sc->sh_master, endp, MINREAD)) {
+       case  0:                        /* EOF; shell just exited. */
+               sscr_end(sp);
+               F_CLR(sp, S_SCRIPT);
+               rval = 0;
+               goto ret;
+       case -1:                        /* Error or interrupt. */
+               msgq(sp, M_SYSERR, "shell");
+               goto ret;
+       default:
+               endp += nr;
+               break;
+       }
+
+       /* Append the lines into the file. */
+       for (p = t = bp; p < endp; ++p) {
+               value = term_key_val(sp, *p);
+               if (value == K_CR || value == K_NL) {
+                       len = p - t;
+                       if (file_aline(sp, ep, 1, lno++, t, len))
+                               goto ret;
+                       t = p + 1;
+               }
+       }
+       if (p > t) {
+               len = p - t;
+               /*
+                * If the last thing from the shell isn't another prompt, wait
+                * up to 1/10 of a second for more stuff to show up, so that
+                * we don't break the output into two separate lines.  Don't
+                * want to hang indefinitely because some program is hanging,
+                * confused the shell, or whatever.
+                */
+               if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 100000;
+                       FD_SET(sc->sh_master, &sp->rdfd);
+                       FD_CLR(STDIN_FILENO, &sp->rdfd);
+                       if (select(sc->sh_master + 1,
+                           &sp->rdfd, NULL, NULL, &tv) == 1) {
+                               memmove(bp, t, len);
+                               endp = bp + len;
+                               goto more;
+                       }
+               }
+               if (sscr_setprompt(sp, t, len))
+                       return (1);
+               if (file_aline(sp, ep, 1, lno++, t, len))
+                       goto ret;
+       }
+
+       /* The cursor moves to EOF. */
+       sp->lno = lno;
+       sp->cno = len ? len - 1 : 0;
+       rval = sp->s_refresh(sp, ep);
+
+ret:   FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
+
+/*
+ * sscr_setprompt --
+ *
+ * Set the prompt to the last line we got from the shell.
+ * 
+ */
+static int
+sscr_setprompt(sp, buf, len)
+       SCR *sp;
+       char* buf;
+       size_t len;
+{
+       SCRIPT *sc;
+
+       sc = sp->script;
+       if (sc->sh_prompt)
+               FREE(sc->sh_prompt, sc->sh_prompt_len);
+       MALLOC(sp, sc->sh_prompt, char *, len + 1);
+       if (sc->sh_prompt == NULL) {
+               sscr_end(sp);
+               return (1);
+       }
+       memmove(sc->sh_prompt, buf, len);
+       sc->sh_prompt_len = len;
+       sc->sh_prompt[len] = '\0';
+       return (0);
+}
+
+/*
+ * sscr_matchprompt --
+ *     Check to see if a line matches the prompt.  Nul's indicate
+ *     parts that can change, in both content and size.
+ */
+static int
+sscr_matchprompt(sp, lp, line_len, lenp)
+       SCR *sp;
+       char *lp;
+       size_t line_len, *lenp;
+{
+       SCRIPT *sc;
+       size_t prompt_len;
+       char *pp;
+
+       sc = sp->script;
+       if (line_len < (prompt_len = sc->sh_prompt_len))
+               return (0);
+
+       for (pp = sc->sh_prompt;
+           prompt_len && line_len; --prompt_len, --line_len) {
+               if (*pp == '\0') {
+                       for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
+                       if (!prompt_len)
+                               return (0);
+                       for (; line_len && *lp != *pp; --line_len, ++lp);
+                       if (!line_len)
+                               return (0);
+               }
+               if (*pp++ != *lp++)
+                       break;
+       }
+
+       if (prompt_len)
+               return (0);
+       if (lenp != NULL)
+               *lenp = line_len;
+       return (1);
+}
+
+/*
+ * sscr_end --
+ *     End the pipe to a shell.
+ */
+int
+sscr_end(sp)
+       SCR *sp;
+{
+       SCRIPT *sc;
+       int rval;
+
+       if ((sc = sp->script) == NULL)
+               return (0);
+
+       /* Turn off the script flag. */
+       F_CLR(sp, S_SCRIPT);
+       
+       /* Close down the parent's file descriptors. */
+       if (sc->sh_master != -1)
+           (void)close(sc->sh_master);
+       if (sc->sh_slave != -1)
+           (void)close(sc->sh_slave);
+
+       /* This should have killed the child. */
+       rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0);
+
+       /* Free memory. */
+       FREE(sc->sh_prompt, sc->sh_prompt_len);
+       FREE(sc, sizeof(SCRIPT));
+       sp->script = NULL;
+
+       return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_set.c b/usr.bin/vi/nex/ex_set.c
new file mode 100644 (file)
index 0000000..a463524
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_set.c   8.2 (Berkeley) 10/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_set(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       switch(cmdp->argc) {
+       case 0:
+               opts_dump(sp, CHANGED_DISPLAY);
+               break;
+       default:
+               opts_set(sp, cmdp->argv);
+               break;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_shell.c b/usr.bin/vi/nex/ex_shell.c
new file mode 100644 (file)
index 0000000..282cc8f
--- /dev/null
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shell.c 8.17 (Berkeley) 12/23/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <curses.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../svi/svi_screen.h"
+
+/*
+ * ex_shell -- :sh[ell]
+ *     Invoke the program named in the SHELL environment variable
+ *     with the argument -i.
+ */
+int
+ex_shell(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       char buf[MAXPATHLEN];
+
+       (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
+       return (ex_exec_proc(sp, buf, "\n", NULL));
+}
+
+/*
+ * ex_exec_proc --
+ *     Run a separate process.
+ */
+int
+ex_exec_proc(sp, cmd, p1, p2)
+       SCR *sp;
+       char *cmd, *p1, *p2;
+{
+       struct sigaction act, oact;
+       struct stat osb, sb;
+       struct termios term;
+       const char *name;
+       pid_t pid;
+       int isig, rval;
+
+       /* Clear the rest of the screen. */
+       if (sp->s_clear(sp))
+               return (1);
+
+       /* Save ex/vi terminal settings, and restore the original ones. */
+       EX_LEAVE(sp, isig, act, oact, sb, osb, term);
+
+       /* Put out various messages. */
+       if (p1 != NULL)
+               (void)write(STDOUT_FILENO, p1, strlen(p1));
+       if (p2 != NULL)
+               (void)write(STDOUT_FILENO, p2, strlen(p2));
+
+
+       switch (pid = vfork()) {
+       case -1:                        /* Error. */
+               msgq(sp, M_SYSERR, "vfork");
+               rval = 1;
+               goto err;
+       case 0:                         /* Utility. */
+               /*
+                * The utility has default signal behavior.  Don't bother
+                * using sigaction(2) 'cause we want the default behavior.
+                */
+               (void)signal(SIGINT, SIG_DFL);
+               (void)signal(SIGQUIT, SIG_DFL);
+
+               if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+                       name = O_STR(sp, O_SHELL);
+               else
+                       ++name;
+               execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+               msgq(sp, M_ERR, "Error: execl: %s: %s",
+                   O_STR(sp, O_SHELL), strerror(errno));
+               _exit(127);
+               /* NOTREACHED */
+       }
+
+       rval = proc_wait(sp, (long)pid, cmd, 0);
+
+       /* Restore ex/vi terminal settings. */
+err:   EX_RETURN(sp, isig, act, oact, sb, osb, term);
+
+       /*
+        * XXX
+        * EX_LEAVE/EX_RETURN only give us 1-second resolution on the tty
+        * changes.  A fast '!' command, e.g. ":!pwd" can beat us to the
+        * refresh.  When there's better resolution from the stat(2) timers,
+        * this can go away.
+        */
+       F_SET(sp, S_REFRESH);
+
+       return (rval);
+}
diff --git a/usr.bin/vi/nex/ex_shift.c b/usr.bin/vi/nex/ex_shift.c
new file mode 100644 (file)
index 0000000..8a0b858
--- /dev/null
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shift.c 8.12 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {LEFT, RIGHT};
+static int shift __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+int
+ex_shiftl(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (shift(sp, ep, cmdp, LEFT));
+}
+       
+int
+ex_shiftr(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (shift(sp, ep, cmdp, RIGHT));
+}
+
+static int
+shift(sp, ep, cmdp, rl)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       enum which rl;
+{
+       recno_t from, to;
+       size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
+       int curset;
+       char *p, *bp, *tbp;
+
+       if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
+               msgq(sp, M_INFO, "shiftwidth option set to 0");
+               return (0);
+       }
+
+       /*
+        * The historic version of vi permitted the user to string any number
+        * of '>' or '<' characters together, resulting in an indent of the
+        * appropriate levels.  There's a special hack in ex_cmd() so that
+        * cmdp->argv[0] points to the string of '>' or '<' characters.
+        *
+        * Q: What's the difference between the people adding features
+        *    to vi and the Girl Scouts?
+        * A: The Girl Scouts have mint cookies and adult supervision.
+        */
+       for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
+               sw += O_VAL(sp, O_SHIFTWIDTH);
+
+       GET_SPACE_RET(sp, bp, blen, 256);
+
+       curset = 0;
+       for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
+               if ((p = file_gline(sp, ep, from, &len)) == NULL)
+                       goto err;
+               if (!len) {
+                       if (sp->lno == from)
+                               curset = 1;
+                       continue;
+               }
+
+               /*
+                * Calculate the old indent amount and the number of
+                * characters it used.
+                */
+               for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
+                       if (p[oldidx] == ' ')
+                               ++oldcol;
+                       else if (p[oldidx] == '\t')
+                               oldcol += O_VAL(sp, O_TABSTOP) -
+                                   oldcol % O_VAL(sp, O_TABSTOP);
+                       else
+                               break;
+
+               /* Calculate the new indent amount. */
+               if (rl == RIGHT)
+                       newcol = oldcol + sw;
+               else {
+                       newcol = oldcol < sw ? 0 : oldcol - sw;
+                       if (newcol == oldcol) {
+                               if (sp->lno == from)
+                                       curset = 1;
+                               continue;
+                       }
+               }
+
+               /* Get a buffer that will hold the new line. */
+               ADD_SPACE_RET(sp, bp, blen, newcol + len);
+
+               /*
+                * Build a new indent string and count the number of
+                * characters it uses.
+                */
+               for (tbp = bp, newidx = 0;
+                   newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
+                       *tbp++ = '\t';
+                       newcol -= O_VAL(sp, O_TABSTOP);
+               }
+               for (; newcol > 0; --newcol, ++newidx)
+                       *tbp++ = ' ';
+
+               /* Add the original line. */
+               memmove(tbp, p + oldidx, len - oldidx);
+
+               /* Set the replacement line. */
+               if (file_sline(sp, ep, from, bp, (tbp + (len - oldidx)) - bp)) {
+err:                   FREE_SPACE(sp, bp, blen);
+                       return (1);
+               }
+
+               /*
+                * !!!
+                * The shift command in historic vi had the usual bizarre
+                * collection of cursor semantics.  If called from vi, the
+                * cursor was repositioned to the first non-blank character
+                * of the lowest numbered line shifted.  If called from ex,
+                * the cursor was repositioned to the first non-blank of the
+                * highest numbered line shifted.  Here, if the cursor isn't
+                * part of the set of lines that are moved, move it to the
+                * first non-blank of the last line shifted.  (This makes
+                * ":3>>" in vi work reasonably.)  If the cursor is part of
+                * the shifted lines, it doesn't get moved at all.  This
+                * permits shifting of marked areas, i.e. ">'a." shifts the
+                * marked area twice, something that couldn't be done with
+                * historic vi.
+                */
+               if (sp->lno == from) {
+                       curset = 1;
+                       if (newidx > oldidx)
+                               sp->cno += newidx - oldidx;
+                       else if (sp->cno >= oldidx - newidx)
+                               sp->cno -= oldidx - newidx;
+               }
+       }
+       if (!curset) {
+               sp->lno = to;
+               sp->cno = 0;
+               (void)nonblank(sp, ep, to, &sp->cno);
+       }
+
+       FREE_SPACE(sp, bp, blen);
+
+       sp->rptlines[rl == RIGHT ? L_RSHIFT : L_LSHIFT] +=
+           cmdp->addr2.lno - cmdp->addr1.lno + 1;
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_source.c b/usr.bin/vi/nex/ex_source.c
new file mode 100644 (file)
index 0000000..bf2a9f1
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_source.c        8.3 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_source -- :source file
+ *     Execute ex commands from a file.
+ */
+int
+ex_source(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (ex_cfile(sp, ep, cmdp->argv[0]->bp));
+}
diff --git a/usr.bin/vi/nex/ex_stop.c b/usr.bin/vi/nex/ex_stop.c
new file mode 100644 (file)
index 0000000..26ec067
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_stop.c  8.4 (Berkeley) 10/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex/sex_screen.h"
+
+/*
+ * ex_stop -- :stop[!]
+ *           :suspend[!]
+ *     Suspend execution.
+ */
+int
+ex_stop(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       /* For some strange reason, the force flag turns off autowrite. */
+       if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) &&
+           !F_ISSET(cmdp, E_FORCE)) {
+               if (file_write((sp), (ep), NULL, NULL, NULL, FS_ALL))
+                       return (1);
+               if (sex_refresh(sp, ep))
+                       return (1);
+       }
+       return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/nex/ex_subst.c b/usr.bin/vi/nex/ex_subst.c
new file mode 100644 (file)
index 0000000..6d132c5
--- /dev/null
@@ -0,0 +1,984 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_substitute.c    8.33 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "interrupt.h"
+
+#define        SUB_FIRST       0x01            /* The 'r' flag isn't reasonable. */
+#define        SUB_MUSTSETR    0x02            /* The 'r' flag is required. */
+
+static int             checkmatchsize __P((SCR *, regex_t *));
+static inline int      regsub __P((SCR *,
+                           char *, char **, size_t *, size_t *));
+static void            subst_intr __P((int));
+static int             substitute __P((SCR *, EXF *,
+                           EXCMDARG *, char *, regex_t *, u_int));
+
+/*
+ * ex_substitute --
+ *     [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
+ *
+ *     Substitute on lines matching a pattern.
+ */
+int
+ex_substitute(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       regex_t *re, lre;
+       size_t blen, len;
+       u_int flags;
+       int delim, eval, reflags, replaced;
+       char *bp, *ptrn, *rep, *p, *t;
+
+       /*
+        * Skip leading white space.
+        *
+        * !!!
+        * Historic vi allowed any non-alphanumeric to serve as the
+        * substitution command delimiter.
+        *
+        * !!!
+        * If the arguments are empty, it's the same as &, i.e. we
+        * repeat the last substitution.
+        */
+       for (p = cmdp->argv[0]->bp,
+           len = cmdp->argv[0]->len; len > 0; --len, ++p) {
+               if (!isblank(*p))
+                       break;
+       }
+       if (len == 0)
+               return (ex_subagain(sp, ep, cmdp));
+       delim = *p++;
+       if (isalnum(delim))
+               return (substitute(sp, ep,
+                   cmdp, p, &sp->subre, SUB_MUSTSETR));
+
+       /*
+        * Get the pattern string, toss escaped characters.
+        *
+        * !!!
+        * Historic vi accepted any of the following forms:
+        *
+        *      :s/abc/def/             change "abc" to "def"
+        *      :s/abc/def              change "abc" to "def"
+        *      :s/abc/                 delete "abc"
+        *      :s/abc                  delete "abc"
+        *
+        * QUOTING NOTE:
+        *
+        * Only toss an escape character if it escapes a delimiter.
+        * This means that "s/A/\\\\f" replaces "A" with "\\f".  It
+        * would be nice to be more regular, i.e. for each layer of
+        * escaping a single escape character is removed, but that's
+        * not how the historic vi worked.
+        */
+       for (ptrn = t = p;;) {
+               if (p[0] == '\0' || p[0] == delim) {
+                       if (p[0] == delim)
+                               ++p;
+                       /*
+                        * !!!
+                        * Nul terminate the pattern string -- it's passed
+                        * to regcomp which doesn't understand anything else.
+                        */
+                       *t = '\0';
+                       break;
+               }
+               if (p[0] == '\\' && p[1] == delim)
+                       ++p;
+               *t++ = *p++;
+       }
+
+       /* If the pattern string is empty, use the last one. */
+       if (*ptrn == NULL) {
+               if (!F_ISSET(sp, S_SUBRE_SET)) {
+                       msgq(sp, M_ERR,
+                           "No previous regular expression.");
+                       return (1);
+               }
+               re = &sp->subre;
+               flags = 0;
+       } else {
+               /* Set RE flags. */
+               reflags = 0;
+               if (O_ISSET(sp, O_EXTENDED))
+                       reflags |= REG_EXTENDED;
+               if (O_ISSET(sp, O_IGNORECASE))
+                       reflags |= REG_ICASE;
+
+               /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+               if (re_conv(sp, &ptrn, &replaced))
+                       return (1);
+
+               /* Compile the RE. */
+               eval = regcomp(&lre, (char *)ptrn, reflags);
+
+               /* Free up any allocated memory. */
+               if (replaced)
+                       FREE_SPACE(sp, ptrn, 0);
+
+               if (eval) {
+                       re_error(sp, eval, &lre);
+                       return (1);
+               }
+
+               /*
+                * Set saved RE.
+                *
+                * !!!
+                * Historic practice is that substitutes set the search
+                * direction as well as both substitute and search RE's.
+                */
+               sp->searchdir = FORWARD;
+               sp->sre = lre;
+               F_SET(sp, S_SRE_SET);
+               sp->subre = lre;
+               F_SET(sp, S_SUBRE_SET);
+
+               re = &lre;
+               flags = SUB_FIRST;
+       }
+
+       /*
+        * Get the replacement string.
+        *
+        * The special character ~ (\~ if O_MAGIC not set) inserts the
+        * previous replacement string into this replacement string.
+        *
+        * The special character & (\& if O_MAGIC not set) matches the
+        * entire RE.  No handling of & is required here, it's done by
+        * regsub().
+        *
+        * QUOTING NOTE:
+        *
+        * Only toss an escape character if it escapes a delimiter or
+        * an escape character, or if O_MAGIC is set and it escapes a
+        * tilde.
+        */
+       if (*p == '\0') {
+               if (sp->repl != NULL)
+                       FREE(sp->repl, sp->repl_len);
+               sp->repl = NULL;
+               sp->repl_len = 0;
+       } else {
+               /*
+                * Count ~'s to figure out how much space we need.  We could
+                * special case nonexistent last patterns or whether or not
+                * O_MAGIC is set, but it's probably not worth the effort.
+                */
+               for (rep = p, len = 0;
+                   p[0] != '\0' && p[0] != delim; ++p, ++len)
+                       if (p[0] == '~')
+                               len += sp->repl_len;
+               GET_SPACE_RET(sp, bp, blen, len);
+               for (t = bp, len = 0, p = rep;;) {
+                       if (p[0] == '\0' || p[0] == delim) {
+                               if (p[0] == delim)
+                                       ++p;
+                               break;
+                       }
+                       if (p[0] == '\\') {
+                               if (p[1] == '\\' || p[1] == delim)
+                                       ++p;
+                               else if (p[1] == '~') {
+                                       ++p;
+                                       if (!O_ISSET(sp, O_MAGIC))
+                                               goto tilde;
+                               }
+                       } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
+tilde:                         ++p;
+                               memmove(t, sp->repl, sp->repl_len);
+                               t += sp->repl_len;
+                               len += sp->repl_len;
+                               continue;
+                       }
+                       *t++ = *p++;
+                       ++len;
+               }
+               if (sp->repl != NULL)
+                       FREE(sp->repl, sp->repl_len);
+               if ((sp->repl = malloc(len)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       FREE_SPACE(sp, bp, blen);
+                       return (1);
+               }
+               memmove(sp->repl, bp, len);
+               sp->repl_len = len;
+               FREE_SPACE(sp, bp, blen);
+       }
+
+       if (checkmatchsize(sp, &sp->subre))
+               return (1);
+       return (substitute(sp, ep, cmdp, p, re, flags));
+}
+
+/*
+ * ex_subagain --
+ *     [line [,line]] & [cgr] [count] [#lp]]
+ *
+ *     Substitute using the last substitute RE and replacement pattern.
+ */
+int
+ex_subagain(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (!F_ISSET(sp, S_SUBRE_SET)) {
+               msgq(sp, M_ERR, "No previous regular expression.");
+               return (1);
+       }
+       return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0));
+}
+
+/*
+ * ex_subtilde --
+ *     [line [,line]] ~ [cgr] [count] [#lp]]
+ *
+ *     Substitute using the last RE and last substitute replacement pattern.
+ */
+int
+ex_subtilde(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (!F_ISSET(sp, S_SRE_SET)) {
+               msgq(sp, M_ERR, "No previous regular expression.");
+               return (1);
+       }
+       return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0));
+}
+
+/* 
+ * The nasty part of the substitution is what happens when the replacement
+ * string contains newlines.  It's a bit tricky -- consider the information
+ * that has to be retained for "s/f\(o\)o/^M\1^M\1/".  The solution here is
+ * to build a set of newline offsets which we use to break the line up later,
+ * when the replacement is done.  Don't change it unless you're pretty damned
+ * confident.
+ */
+#define        NEEDNEWLINE(sp) {                                               \
+       if (sp->newl_len == sp->newl_cnt) {                             \
+               sp->newl_len += 25;                                     \
+               REALLOC(sp, sp->newl, size_t *,                         \
+                   sp->newl_len * sizeof(size_t));                     \
+               if (sp->newl == NULL) {                                 \
+                       sp->newl_len = 0;                               \
+                       return (1);                                     \
+               }                                                       \
+       }                                                               \
+}
+
+#define        BUILD(sp, l, len) {                                             \
+       if (lbclen + (len) > lblen) {                                   \
+               lblen += MAX(lbclen + (len), 256);                      \
+               REALLOC(sp, lb, char *, lblen);                         \
+               if (lb == NULL) {                                       \
+                       lbclen = 0;                                     \
+                       return (1);                                     \
+               }                                                       \
+       }                                                               \
+       memmove(lb + lbclen, l, len);                                   \
+       lbclen += len;                                                  \
+}
+
+#define        NEEDSP(sp, len, pnt) {                                          \
+       if (lbclen + (len) > lblen) {                                   \
+               lblen += MAX(lbclen + (len), 256);                      \
+               REALLOC(sp, lb, char *, lblen);                         \
+               if (lb == NULL) {                                       \
+                       lbclen = 0;                                     \
+                       return (1);                                     \
+               }                                                       \
+               pnt = lb + lbclen;                                      \
+       }                                                               \
+}
+
+/*
+ * substitute --
+ *     Do the substitution.  This stuff is *really* tricky.  There are
+ *     lots of special cases, and general nastiness.  Don't mess with it
+ *     unless you're pretty confident.
+ */
+static int
+substitute(sp, ep, cmdp, s, re, flags)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       char *s;
+       regex_t *re;
+       u_int flags;
+{
+       DECLARE_INTERRUPTS;
+       MARK from, to;
+       recno_t elno, lno;
+       size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset;
+       int didsub, do_eol_match, eflags, empty_ok, eval;
+       int linechanged, matched, quit, rval;
+       int cflag, gflag, lflag, nflag, pflag, rflag;
+       char *bp, *lb;
+
+       /*
+        * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
+        * it only displayed the last change.  I'd disallow them, but they are
+        * useful in combination with the [v]global commands.  In the current
+        * model the problem is combining them with the 'c' flag -- the screen
+        * would have to flip back and forth between the confirm screen and the
+        * ex print screen, which would be pretty awful.  We do display all
+        * changes, though, for what that's worth.
+        *
+        * !!!
+        * Historic vi was fairly strict about the order of "options", the
+        * count, and "flags".  I'm somewhat fuzzy on the difference between
+        * options and flags, anyway, so this is a simpler approach, and we
+        * just take it them in whatever order the user gives them.  (The ex
+        * usage statement doesn't reflect this.)
+        */
+       cflag = gflag = lflag = nflag = pflag = rflag = 0;
+       for (lno = OOBLNO; *s != '\0'; ++s)
+               switch (*s) {
+               case ' ':
+               case '\t':
+                       break;
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       if (lno != OOBLNO)
+                               goto usage;
+                       errno = 0;
+                       lno = strtoul(s, &s, 10);
+                       if (*s == '\0')         /* Loop increment correction. */
+                               --s;
+                       if (errno == ERANGE) {
+                               if (lno == LONG_MAX)
+                                       msgq(sp, M_ERR, "Count overflow.");
+                               else if (lno == LONG_MIN)
+                                       msgq(sp, M_ERR, "Count underflow.");
+                               else
+                                       msgq(sp, M_SYSERR, NULL);
+                               return (1);
+                       }
+                       /*
+                        * In historic vi, the count was inclusive from the
+                        * second address.
+                        */
+                       cmdp->addr1.lno = cmdp->addr2.lno;
+                       cmdp->addr2.lno += lno - 1;
+                       break;
+               case '#':
+                       nflag = 1;
+                       break;
+               case 'c':
+                       cflag = 1;
+                       break;
+               case 'g':
+                       gflag = 1;
+                       break;
+               case 'l':
+                       lflag = 1;
+                       break;
+               case 'p':
+                       pflag = 1;
+                       break;
+               case 'r':
+                       if (LF_ISSET(SUB_FIRST)) {
+                               msgq(sp, M_ERR,
+                   "Regular expression specified; r flag meaningless.");
+                               return (1);
+                       }
+                       if (!F_ISSET(sp, S_SUBRE_SET)) {
+                               msgq(sp, M_ERR,
+                                   "No previous regular expression.");
+                               return (1);
+                       }
+                       rflag = 1;
+                       break;
+               default:
+                       goto usage;
+               }
+
+       if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) {
+usage:         msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+               return (1);
+       }
+
+       if (IN_VI_MODE(sp) && cflag && (lflag || nflag || pflag)) {
+               msgq(sp, M_ERR,
+       "The #, l and p flags may not be combined with the c flag in vi mode.");
+               return (1);
+       }
+
+       if (!F_ISSET(sp, S_GLOBAL))
+               SET_UP_INTERRUPTS(subst_intr);
+
+       /*
+        * bp:          if interactive, line cache
+        * blen:        if interactive, line cache length
+        * lb:          build buffer pointer.
+        * lbclen:      current length of built buffer.
+        * lblen;       length of build buffer.
+        */
+       bp = lb = NULL;
+       blen = lbclen = lblen = 0;
+
+       /* For each line... */
+       for (matched = quit = 0, lno = cmdp->addr1.lno,
+           elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {
+
+               /* Someone's unhappy, time to stop. */
+               if (F_ISSET(sp, S_INTERRUPTED)) {
+                       if (!F_ISSET(sp, S_GLOBAL))
+                               msgq(sp, M_INFO, "Interrupted.");
+                       break;
+               }
+
+               /* Get the line. */
+               if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+                       GETLINE_ERR(sp, lno);
+                       return (1);
+               }
+
+               /*
+                * Make a local copy if doing confirmation -- when calling
+                * the confirm routine we're likely to lose the cached copy.
+                */
+               if (cflag) {
+                       if (bp == NULL) {
+                               GET_SPACE_RET(sp, bp, blen, llen);
+                       } else
+                               ADD_SPACE_RET(sp, bp, blen, llen);
+                       memmove(bp, s, llen);
+                       s = bp;
+               }
+
+               /* Start searching from the beginning. */
+               offset = 0;
+               len = llen;
+
+               /* Reset the build buffer offset. */
+               lbclen = 0;
+
+               /* Reset empty match flag. */
+               empty_ok = 1;
+
+               /*
+                * We don't want to have to do a setline if the line didn't
+                * change -- keep track of whether or not this line changed.
+                * If doing confirmations, don't want to keep setting the
+                * line if change is refused -- keep track of substitutions.
+                */
+               didsub = linechanged = 0;
+
+               /* New line, do an EOL match. */
+               do_eol_match = 1;
+
+               /* It's not nul terminated, but we pretend it is. */
+               eflags = REG_STARTEND;
+
+               /*
+                * The search area is from s + offset to the EOL.
+                *
+                * Generally, sp->match[0].rm_so is the offset of the start
+                * of the match from the start of the search, and offset is
+                * the offset of the start of the last search.
+                */
+nextmatch:     sp->match[0].rm_so = 0;
+               sp->match[0].rm_eo = len;
+
+               /* Get the next match. */
+               eval = regexec(re,
+                   (char *)s + offset, re->re_nsub + 1, sp->match, eflags);
+
+               /*
+                * There wasn't a match or if there was an error, deal with
+                * it.  If there was a previous match in this line, resolve
+                * the changes into the database.  Otherwise, just move on.
+                */
+               if (eval == REG_NOMATCH)
+                       goto endmatch;
+               if (eval != 0) {
+                       re_error(sp, eval, re);
+                       goto ret1;
+               }
+               matched = 1;
+
+               /* Only the first search can match an anchored expression. */
+               eflags |= REG_NOTBOL;
+
+               /*
+                * !!!
+                * It's possible to match 0-length strings -- for example, the
+                * command s;a*;X;, when matched against the string "aabb" will
+                * result in "XbXbX", i.e. the matches are "aa", the space
+                * between the b's and the space between the b's and the end of
+                * the string.  There is a similar space between the beginning
+                * of the string and the a's.  The rule that we use (because vi
+                * historically used it) is that any 0-length match, occurring
+                * immediately after a match, is ignored.  Otherwise, the above
+                * example would have resulted in "XXbXbX".  Another example is
+                * incorrectly using " *" to replace groups of spaces with one
+                * space.
+                *
+                * The way we do this is that if we just had a successful match,
+                * the starting offset does not skip characters, and the match
+                * is empty, ignore the match and move forward.  If there's no
+                * more characters in the string, we were attempting to match
+                * after the last character, so quit.
+                */
+               if (!empty_ok &&
+                   sp->match[0].rm_so == 0 && sp->match[0].rm_eo == 0) {
+                       empty_ok = 1;
+                       if (len == 0)
+                               goto endmatch;
+                       BUILD(sp, s + offset, 1)
+                       ++offset;
+                       --len;
+                       goto nextmatch;
+               }
+
+               /* Confirm change. */
+               if (cflag) {
+                       /*
+                        * Set the cursor position for confirmation.  Note,
+                        * if we matched on a '$', the cursor may be past
+                        * the end of line.
+                        *
+                        * XXX
+                        * We may want to "fix" this in the confirm routine,
+                        * if the confirm routine should be able to display
+                        * a cursor past EOL.
+                        */
+                       from.lno = to.lno = lno;
+                       from.cno = sp->match[0].rm_so + offset;
+                       to.cno = sp->match[0].rm_eo;
+                       if (llen == 0)
+                               from.cno = to.cno = 0;
+                       else {
+                               if (to.cno >= llen)
+                                       to.cno = llen - 1;
+                               if (from.cno >= llen)
+                                       from.cno = llen - 1;
+                       }
+                       switch (sp->s_confirm(sp, ep, &from, &to)) {
+                       case CONF_YES:
+                               break;
+                       case CONF_NO:
+                               didsub = 0;
+                               BUILD(sp, s +offset, sp->match[0].rm_eo);
+                               goto skip;
+                       case CONF_QUIT:
+                               /* Set the quit flag. */
+                               quit = 1;
+
+                               /* If interruptible, pass the info back. */
+                               if (F_ISSET(sp, S_INTERRUPTIBLE))
+                                       F_SET(sp, S_INTERRUPTED);
+                               
+                               /*
+                                * If any changes, resolve them, otherwise
+                                * return to the main loop.
+                                */
+                               goto endmatch;
+                       }
+               }
+
+               /* Copy the bytes before the match into the build buffer. */
+               BUILD(sp, s + offset, sp->match[0].rm_so);
+
+               /*
+                * Cursor moves to last line changed, unless doing confirm,
+                * in which case don't move it.
+                *
+                * !!!
+                * Historic vi just put the cursor on the first non-blank
+                * of the last line changed.  This might be better.
+                */
+               if (!cflag) {
+                       sp->lno = lno;
+                       sp->cno = sp->match[0].rm_so + offset;
+               }
+
+               /* Substitute the matching bytes. */
+               didsub = 1;
+               if (regsub(sp, s + offset, &lb, &lbclen, &lblen))
+                       goto ret1;
+
+               /* Set the change flag so we know this line was modified. */
+               linechanged = 1;
+
+               /* Move past the matched bytes. */
+skip:          offset += sp->match[0].rm_eo;
+               len -= sp->match[0].rm_eo;
+
+               /* A match cannot be followed by an empty pattern. */
+               empty_ok = 0;
+
+               /*
+                * If doing a global change with confirmation, we have to
+                * update the screen.  The basic idea is to store the line
+                * so the screen update routines can find it, and restart.
+                */
+               if (didsub && cflag && gflag) {
+                       /*
+                        * The new search offset will be the end of the
+                        * modified line.
+                        */
+                       saved_offset = lbclen;
+
+                       /* Copy the rest of the line. */
+                       if (len)
+                               BUILD(sp, s + offset, len)
+
+                       /* Set the new offset. */
+                       offset = saved_offset;
+
+                       /* Store inserted lines, adjusting the build buffer. */
+                       last = 0;
+                       if (sp->newl_cnt) {
+                               for (cnt = 0;
+                                   cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+                                       if (file_iline(sp, ep, lno,
+                                           lb + last, sp->newl[cnt] - last))
+                                               goto ret1;
+                                       last = sp->newl[cnt] + 1;
+                                       ++sp->rptlines[L_ADDED];
+                               }
+                               lbclen -= last;
+                               offset -= last;
+                               sp->newl_cnt = 0;
+                       }
+
+                       /* Store and retrieve the line. */
+                       if (file_sline(sp, ep, lno, lb + last, lbclen))
+                               goto ret1;
+                       if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+                               GETLINE_ERR(sp, lno);
+                               goto ret1;
+                       }
+                       ADD_SPACE_RET(sp, bp, blen, llen)
+                       memmove(bp, s, llen);
+                       s = bp;
+                       len = llen - offset;
+
+                       /* Restart the build. */
+                       lbclen = 0;
+                       BUILD(sp, s, offset);
+
+                       /*
+                        * If we haven't already done the after-the-string
+                        * match, do one.  Set REG_NOTEOL so the '$' pattern
+                        * only matches once.
+                        */
+                       if (!do_eol_match)
+                               goto endmatch;
+                       if (offset == len) {
+                               do_eol_match = 0;
+                               eflags |= REG_NOTEOL;
+                       }
+                       goto nextmatch;
+               }
+
+               /*
+                * If it's a global:
+                *
+                * If at the end of the string, do a test for the after
+                * the string match.  Set REG_NOTEOL so the '$' pattern
+                * only matches once.
+                */
+               if (gflag && do_eol_match) {
+                       if (len == 0) {
+                               do_eol_match = 0;
+                               eflags |= REG_NOTEOL;
+                       }
+                       goto nextmatch;
+               }
+
+endmatch:      if (!linechanged)
+                       continue;
+
+               /* Copy any remaining bytes into the build buffer. */
+               if (len)
+                       BUILD(sp, s + offset, len)
+
+               /* Store inserted lines, adjusting the build buffer. */
+               last = 0;
+               if (sp->newl_cnt) {
+                       for (cnt = 0;
+                           cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+                               if (file_iline(sp, ep,
+                                   lno, lb + last, sp->newl[cnt] - last))
+                                       goto ret1;
+                               last = sp->newl[cnt] + 1;
+                               ++sp->rptlines[L_ADDED];
+                       }
+                       lbclen -= last;
+                       sp->newl_cnt = 0;
+               }
+
+               /* Store the changed line. */
+               if (file_sline(sp, ep, lno, lb + last, lbclen))
+                       goto ret1;
+
+               /* Update changed line counter. */
+               ++sp->rptlines[L_CHANGED];
+
+               /* Display as necessary. */
+               if (lflag || nflag || pflag) {
+                       from.lno = to.lno = lno;
+                       from.cno = to.cno = 0;
+                       if (lflag)
+                               ex_print(sp, ep, &from, &to, E_F_LIST);
+                       if (nflag)
+                               ex_print(sp, ep, &from, &to, E_F_HASH);
+                       if (pflag)
+                               ex_print(sp, ep, &from, &to, E_F_PRINT);
+               }
+       }
+
+       /*
+        * If not in a global command, and nothing matched, say so.
+        * Else, if none of the lines displayed, put something up.
+        */
+       if (!matched) {
+               if (!F_ISSET(sp, S_GLOBAL))
+                       msgq(sp, M_INFO, "No match found.");
+       } else if (!lflag && !nflag && !pflag)
+               F_SET(EXP(sp), EX_AUTOPRINT);
+
+       rval = 0;
+       if (0) {
+ret1:          rval = 1;
+       }
+
+interrupt_err:
+       if (!F_ISSET(sp, S_GLOBAL))
+               TEAR_DOWN_INTERRUPTS;
+
+       if (bp != NULL)
+               FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
+
+/*
+ * regsub --
+ *     Do the substitution for a regular expression.
+ */
+static inline int
+regsub(sp, ip, lbp, lbclenp, lblenp)
+       SCR *sp;
+       char *ip;                       /* Input line. */
+       char **lbp;
+       size_t *lbclenp, *lblenp;
+{
+       enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;
+       size_t lbclen, lblen;           /* Local copies. */
+       size_t mlen;                    /* Match length. */
+       size_t rpl;                     /* Remaining replacement length. */
+       char *rp;                       /* Replacement pointer. */
+       int ch;
+       int no;                         /* Match replacement offset. */
+       char *p, *t;                    /* Buffer pointers. */
+       char *lb;                       /* Local copies. */
+
+       lb = *lbp;                      /* Get local copies. */
+       lbclen = *lbclenp;
+       lblen = *lblenp;
+
+       /*
+        * QUOTING NOTE:
+        *
+        * There are some special sequences that vi provides in the
+        * replacement patterns.
+        *       & string the RE matched (\& if nomagic set)
+        *      \# n-th regular subexpression   
+        *      \E end \U, \L conversion
+        *      \e end \U, \L conversion
+        *      \l convert the next character to lower-case
+        *      \L convert to lower-case, until \E, \e, or end of replacement
+        *      \u convert the next character to upper-case
+        *      \U convert to upper-case, until \E, \e, or end of replacement
+        *
+        * Otherwise, since this is the lowest level of replacement, discard
+        * all escape characters.  This (hopefully) follows historic practice.
+        */
+#define        ADDCH(ch) {                                                     \
+       CHAR_T __ch = (ch);                                             \
+       u_int __value = term_key_val(sp, __ch);                         \
+       if (__value == K_CR || __value == K_NL) {                       \
+               NEEDNEWLINE(sp);                                        \
+               sp->newl[sp->newl_cnt++] = lbclen;                      \
+       } else if (conv != C_NOTSET) {                                  \
+               switch (conv) {                                         \
+               case C_ONELOWER:                                        \
+                       conv = C_NOTSET;                                \
+                       /* FALLTHROUGH */                               \
+               case C_LOWER:                                           \
+                       if (isupper(__ch))                              \
+                               __ch = tolower(__ch);                   \
+                       break;                                          \
+               case C_ONEUPPER:                                        \
+                       conv = C_NOTSET;                                \
+                       /* FALLTHROUGH */                               \
+               case C_UPPER:                                           \
+                       if (islower(__ch))                              \
+                               __ch = toupper(__ch);                   \
+                       break;                                          \
+               default:                                                \
+                       abort();                                        \
+               }                                                       \
+       }                                                               \
+       NEEDSP(sp, 1, p);                                               \
+       *p++ = __ch;                                                    \
+       ++lbclen;                                                       \
+}
+       conv = C_NOTSET;
+       for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {
+               switch (ch = *rp++) {
+               case '&':
+                       if (O_ISSET(sp, O_MAGIC)) {
+                               no = 0;
+                               goto subzero;
+                       }
+                       break;
+               case '\\':
+                       if (rpl == 0)
+                               break;
+                       --rpl;
+                       switch (ch = *rp) {
+                       case '&':
+                               if (!O_ISSET(sp, O_MAGIC)) {
+                                       ++rp;
+                                       no = 0;
+                                       goto subzero;
+                               }
+                               break;
+                       case '0': case '1': case '2': case '3': case '4':
+                       case '5': case '6': case '7': case '8': case '9':
+                               no = *rp++ - '0';
+subzero:                       if (sp->match[no].rm_so == -1 ||
+                                   sp->match[no].rm_eo == -1)
+                                       continue;
+                               mlen =
+                                   sp->match[no].rm_eo - sp->match[no].rm_so;
+                               for (t = ip + sp->match[no].rm_so; mlen--; ++t)
+                                       ADDCH(*t);
+                               continue;
+                       case 'e':
+                       case 'E':
+                               ++rp;
+                               conv = C_NOTSET;
+                               continue;
+                       case 'l':
+                               ++rp;
+                               conv = C_ONELOWER;
+                               continue;
+                       case 'L':
+                               ++rp;
+                               conv = C_LOWER;
+                               continue;
+                       case 'u':
+                               ++rp;
+                               conv = C_ONEUPPER;
+                               continue;
+                       case 'U':
+                               ++rp;
+                               conv = C_UPPER;
+                               continue;
+                       default:
+                               ++rp;
+                               break;
+                       }
+               }
+               ADDCH(ch);
+       }
+
+       *lbp = lb;                      /* Update caller's information. */
+       *lbclenp = lbclen;
+       *lblenp = lblen;
+       return (0);
+}
+
+static int
+checkmatchsize(sp, re)
+       SCR *sp;
+       regex_t *re;
+{
+       /* Build nsub array as necessary. */
+       if (sp->matchsize < re->re_nsub + 1) {
+               sp->matchsize = re->re_nsub + 1;
+               REALLOC(sp, sp->match,
+                   regmatch_t *, sp->matchsize * sizeof(regmatch_t));
+               if (sp->match == NULL) {
+                       sp->matchsize = 0;
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * subst_intr --
+ *     Set the interrupt bit in any screen that is interruptible.
+ *
+ * XXX
+ * In the future this may be a problem.  The user should be able to move to
+ * another screen and keep typing while this runs.  If so, and the user has
+ * more than one substitute running, it will be hard to decide which one to
+ * stop.
+ */
+static void
+subst_intr(signo)
+       int signo;
+{
+       SCR *sp;
+
+       for (sp = __global_list->dq.cqh_first;
+           sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+               if (F_ISSET(sp, S_INTERRUPTIBLE))
+                       F_SET(sp, S_INTERRUPTED);
+}
diff --git a/usr.bin/vi/nex/ex_tag.c b/usr.bin/vi/nex/ex_tag.c
new file mode 100644 (file)
index 0000000..c47ab0d
--- /dev/null
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * 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[] = "@(#)ex_tag.c   8.31 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+static char    *binary_search __P((char *, char *, char *));
+static int      compare __P((char *, char *, char *));
+static char    *linear_search __P((char *, char *, char *));
+static int      search __P((SCR *, char *, char *, char **));
+static int      tag_get __P((SCR *, char *, char **, char **, char **));
+
+/*
+ * ex_tagfirst --
+ *     The tag code can be entered from main, i.e. "vi -t tag".
+ */
+int
+ex_tagfirst(sp, tagarg)
+       SCR *sp;
+       char *tagarg;
+{
+       FREF *frp;
+       MARK m;
+       long tl;
+       u_int flags;
+       int sval;
+       char *p, *tag, *name, *search;
+
+       /* Taglength may limit the number of characters. */
+       if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl)
+               tagarg[tl] = '\0';
+
+       /* Get the tag information. */
+       if (tag_get(sp, tagarg, &tag, &name, &search))
+               return (1);
+
+       /* Create the file entry. */
+       if ((frp = file_add(sp, NULL, name, 0)) == NULL)
+               return (1);
+       if (file_init(sp, frp, NULL, 0))
+               return (1);
+
+       /*
+        * !!!
+        * Historic vi accepted a line number as well as a search
+        * string, and people are apparently still using the format.
+        */
+       if (isdigit(search[0])) {
+               m.lno = atoi(search);
+               m.cno = 0;
+       } else {
+               /*
+                * Search for the tag; cheap fallback for C functions if
+                * the name is the same but the arguments have changed.
+                */
+               m.lno = 1;
+               m.cno = 0;
+               flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+               sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+               if (sval && (p = strrchr(search, '(')) != NULL) {
+                       p[1] = '\0';
+                       sval = f_search(sp, sp->ep,
+                           &m, &m, search, NULL, &flags);
+               }
+               if (sval)
+                       msgq(sp, M_ERR, "%s: search pattern not found.", tag);
+       }
+
+       /* Set up the screen. */
+       frp->lno = m.lno;
+       frp->cno = m.cno;
+       F_SET(frp, FR_CURSORSET);
+
+       /* Might as well make this the default tag. */
+       if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * ex_tagpush -- :tag [file]
+ *     Move to a new tag.
+ *
+ * The tags stacks in nvi are a bit tricky.  Each tag contains a file name,
+ * search string, and line/column numbers.  The search string is only used
+ * for the first access and for user display.  The first record on the stack
+ * is the place where we first did a tag, so it has no search string.  The
+ * second record is the first tag, and so on.  Note, this means that the
+ * "current" tag is always on the stack.  Each tag has a line/column which is
+ * the location from which the user tagged the following TAG entry, and which
+ * is used as the return location.
+ */
+int
+ex_tagpush(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       enum {TC_CHANGE, TC_CURRENT} which;
+       EX_PRIVATE *exp;
+       FREF *frp;
+       MARK m;
+       TAG *tp;
+       u_int flags;
+       int sval;
+       long tl;
+       char *name, *p, *search, *tag;
+       
+       exp = EXP(sp);
+       switch (cmdp->argc) {
+       case 1:
+               if (exp->tlast != NULL)
+                       FREE(exp->tlast, strlen(exp->tlast) + 1);
+               if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) {
+                       msgq(sp, M_SYSERR, NULL);
+                       return (1);
+               }
+               break;
+       case 0:
+               if (exp->tlast == NULL) {
+                       msgq(sp, M_ERR, "No previous tag entered.");
+                       return (1);
+               }
+               break;
+       default:
+               abort();
+       }
+
+       /* Taglength may limit the number of characters. */
+       if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl)
+               exp->tlast[tl] = '\0';
+
+       /* Get the tag information. */
+       if (tag_get(sp, exp->tlast, &tag, &name, &search))
+               return (1);
+
+       /* Get a new FREF structure. */
+       if ((frp = file_add(sp, sp->frp, name, 1)) == NULL) {
+               FREE(tag, strlen(tag));
+               return (1);
+       }
+
+       /*
+        * Get a tag structure -- if this is the first tag, push it on the
+        * stack as a placeholder and get another tag structure.  Set the
+        * line/column of the most recent element on the stack to be the
+        * current values, including the file pointer.  Then push the new
+        * TAG onto the stack with the new file and search string for user
+        * display.
+        */
+       CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+       if (tp != NULL && exp->tagq.tqh_first == NULL) {
+               TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+               CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+       }
+       if (exp->tagq.tqh_first != NULL) {
+               exp->tagq.tqh_first->frp = sp->frp;
+               exp->tagq.tqh_first->lno = sp->lno;
+               exp->tagq.tqh_first->cno = sp->cno;
+       }
+       if (tp != NULL) {
+               if ((tp->search = strdup(search)) == NULL)
+                       msgq(sp, M_SYSERR, NULL);
+               else
+                       tp->slen = strlen(search);
+               tp->frp = frp;
+               TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+       }
+
+       /* Switch to the new file. */
+       if (sp->frp == frp)
+               which = TC_CURRENT;
+       else {
+               MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE));
+
+               if (file_init(sp, frp, NULL, 0)) {
+                       if (tp != NULL)
+                               FREE(tp, sizeof(TAG));
+                       FREE(tag, strlen(tag));
+                       return (1);
+               }
+               which = TC_CHANGE;
+       }
+
+       /*
+        * !!!
+        * Historic vi accepted a line number as well as a search
+        * string, and people are apparently still using the format.
+        */
+       if (isdigit(search[0])) {
+               m.lno = atoi(search);
+               m.cno = 0;
+               sval = 0;
+       } else {
+               /*
+                * Search for the tag; cheap fallback for C functions
+                * if the name is the same but the arguments have changed.
+                */
+               m.lno = 1;
+               m.cno = 0;
+               flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+               sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+               if (sval && (p = strrchr(search, '(')) != NULL) {
+                       p[1] = '\0';
+                       sval = f_search(sp, sp->ep,
+                            &m, &m, search, NULL, &flags);
+                       p[1] = '(';
+               }
+               if (sval)
+                       msgq(sp, M_ERR, "%s: search pattern not found.", tag);
+       }
+       free(tag);
+
+       switch (which) {
+       case TC_CHANGE:
+               frp->lno = m.lno;
+               frp->cno = m.cno;
+               F_SET(frp, FR_CURSORSET);
+               F_SET(sp, S_FSWITCH);
+               break;
+       case TC_CURRENT:
+               if (sval)
+                       return (1);
+               sp->lno = m.lno;
+               sp->cno = m.cno;
+               break;
+       }
+       return (0);
+}
+
+/* Free a tag or tagf structure from a queue. */
+#define        FREETAG(tp) {                                                   \
+       TAILQ_REMOVE(&exp->tagq, (tp), q);                              \
+       if ((tp)->search != NULL)                                       \
+               free((tp)->search);                                     \
+       FREE((tp), sizeof(TAGF));                                       \
+}
+#define        FREETAGF(tfp) {                                                 \
+       TAILQ_REMOVE(&exp->tagfq, (tfp), q);                            \
+       free((tfp)->name);                                              \
+       FREE((tfp), sizeof(TAGF));                                      \
+}
+
+/*
+ * ex_tagpop -- :tagp[op][!] [number | file]
+ *     Pop the tag stack.
+ */
+int
+ex_tagpop(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       EX_PRIVATE *exp;
+       TAG *ntp, *tp;
+       recno_t lno;
+       long off, saved_off;
+       size_t arglen, cno;
+       char *arg, *p, *t;
+
+       /* Check for an empty stack. */
+       exp = EXP(sp);
+       if (exp->tagq.tqh_first == NULL) {
+               msgq(sp, M_INFO, "The tags stack is empty.");
+               return (1);
+       }
+
+       switch (cmdp->argc) {
+       case 0:                         /* Pop one tag. */
+               tp = exp->tagq.tqh_first;
+               FREETAG(tp);
+               break;
+       case 1:                         /* Name or number. */
+               arg = cmdp->argv[0]->bp;
+               saved_off = strtol(arg, &p, 10);
+               if (*p == '\0') {
+                       if (saved_off < 1)
+                               return (0);
+                       for (tp = exp->tagq.tqh_first, off = saved_off;
+                           tp != NULL && off-- > 1; tp = tp->q.tqe_next);
+                       if (tp == NULL) {
+                               msgq(sp, M_ERR,
+"Less than %d entries on the tags stack; use :display to see the tags stack.",
+                                   saved_off);
+                               return (1);
+                       }
+                       for (off = saved_off; off-- > 1;) {
+                               tp = exp->tagq.tqh_first;
+                               FREETAG(tp);
+                       }
+               } else {
+                       arglen = strlen(arg);
+                       for (tp = exp->tagq.tqh_first;
+                           tp != NULL; tp = tp->q.tqe_next) {
+                               /* Use the user's original file name. */
+                               p = tp->frp->name;
+                               if ((t = strrchr(p, '/')) == NULL)
+                                       t = p;
+                               else
+                                       ++t;
+                               if (!strncmp(arg, t, arglen)) {
+                                       ntp = tp;
+                                       break;
+                               }
+                       }
+                       if (tp == NULL) {
+                               msgq(sp, M_ERR,
+"No file named %s on the tags stack; use :display to see the tags stack.",
+                                   arg);
+                               return (1);
+                       }
+                       for (;;) {
+                               tp = exp->tagq.tqh_first;
+                               if (tp == ntp)
+                                       break;
+                               FREETAG(tp);
+                       }
+               }
+               break;
+       default:
+               abort();
+       }
+
+       /* Update the cursor from the saved TAG information. */
+       tp = exp->tagq.tqh_first;
+       if (tp->frp == sp->frp) {
+               sp->lno = tp->lno;
+               sp->cno = tp->cno;
+       } else {
+               MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE));
+
+               if (file_init(sp, tp->frp, NULL, 0))
+                       return (1);
+
+               tp->frp->lno = tp->lno;
+               tp->frp->cno = tp->cno;
+               F_SET(sp->frp, FR_CURSORSET);
+
+               F_SET(sp, S_FSWITCH);
+       }
+
+       /* If returning to the first tag, the stack is now empty. */
+       if (tp->q.tqe_next == NULL)
+               FREETAG(tp);
+       return (0);
+}
+
+/*
+ * ex_tagtop -- :tagt[op][!]
+ *     Clear the tag stack.
+ */    
+int
+ex_tagtop(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       EX_PRIVATE *exp;
+       TAG *tp, tmp;
+       int found;
+
+       /* Pop to oldest saved information. */
+       exp = EXP(sp);
+       for (found = 0; (tp = exp->tagq.tqh_first) != NULL; found = 1) {
+               if (exp->tagq.tqh_first == NULL)
+                       tmp = *tp;
+               FREETAG(tp);
+       }
+
+       if (!found) {
+               msgq(sp, M_INFO, "The tags stack is empty.");
+               return (1);
+       }
+
+       /* If not switching files, it's easy; else do the work. */
+       if (tmp.frp == sp->frp) {
+               sp->lno = tmp.lno;
+               sp->cno = tmp.cno;
+       } else {
+               MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE));
+
+               if (file_init(sp, tmp.frp, NULL, 0))
+                       return (1);
+
+               tmp.frp->lno = tmp.lno;
+               tmp.frp->cno = tmp.cno;
+
+               F_SET(sp->frp, FR_CURSORSET);
+
+               F_SET(sp, S_FSWITCH);
+       }
+       return (0);
+}
+
+/*
+ * ex_tagdisplay --
+ *     Display the list of tags.
+ */
+int
+ex_tagdisplay(sp, ep)
+       SCR *sp;
+       EXF *ep;
+{
+       EX_PRIVATE *exp;
+       TAG *tp;
+       size_t len, maxlen;
+       int cnt;
+       char *name;
+
+       exp = EXP(sp);
+       if ((tp = exp->tagq.tqh_first) == NULL) {
+               (void)ex_printf(EXCOOKIE, "No tags to display.\n");
+               return (0);
+       }
+
+       /*
+        * Figure out the formatting.  MNOC is the maximum
+        * number of file name columns before we split the line.
+        */
+#define        MNOC    15
+       for (maxlen = 0,
+           tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) {
+               len = strlen(name = tp->frp->name);     /* The original name. */
+               if (maxlen < len && len < MNOC)
+                       maxlen = len;
+       }
+
+       for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL;
+           ++cnt, tp = tp->q.tqe_next) {
+               len = strlen(name = tp->frp->name);     /* The original name. */
+               if (len > maxlen || len + tp->slen > sp->cols)
+                       if (tp == NULL || tp->search == NULL)
+                               (void)ex_printf(EXCOOKIE,
+                                   "%2d %s\n", cnt, name);
+                       else
+                               (void)ex_printf(EXCOOKIE,
+                                    "%2d %s\n** %*.*s %s\n", cnt, name,
+                                    (int)maxlen, (int)maxlen, "", tp->search);
+               else
+                       if (tp == NULL || tp->search == NULL)
+                               (void)ex_printf(EXCOOKIE, "%2d %*.*s\n",
+                                   cnt, (int)maxlen, (int)len, name);
+                       else
+                               (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n",
+                                   cnt, (int)maxlen, (int)len, name,
+                                   tp->search);
+       }
+       return (0);
+}
+
+/*
+ * ex_tagalloc --
+ *     Create a new list of tag files.
+ */
+int
+ex_tagalloc(sp, str)
+       SCR *sp;
+       char *str;
+{
+       EX_PRIVATE *exp;
+       TAGF *tp;
+       size_t len;
+       char *p, *t;
+
+       /* Free current queue. */
+       exp = EXP(sp);
+       while ((tp = exp->tagfq.tqh_first) != NULL)
+               FREETAGF(tp);
+
+       /* Create new queue. */
+       for (p = t = str;; ++p) {
+               if (*p == '\0' || isblank(*p)) {
+                       if ((len = p - t) > 1) {
+                               MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF));
+                               MALLOC(sp, tp->name, char *, len + 1);
+                               if (tp->name == NULL) {
+                                       FREE(tp, sizeof(TAGF));
+                                       return (1);
+                               }
+                               memmove(tp->name, t, len);
+                               tp->name[len] = '\0';
+                               tp->flags = 0;
+                               TAILQ_INSERT_TAIL(&exp->tagfq, tp, q);
+                       }
+                       t = p + 1;
+               }
+               if (*p == '\0')
+                        break;
+       }
+       return (0);
+}
+                                               /* Free previous queue. */
+/*
+ * ex_tagfree --
+ *     Free the tags file list.
+ */
+int
+ex_tagfree(sp)
+       SCR *sp;
+{
+       EX_PRIVATE *exp;
+       TAG *tp;
+       TAGF *tfp;
+
+       /* Free up tag information. */
+       exp = EXP(sp);
+       while ((tp = exp->tagq.tqh_first) != NULL)
+               FREETAG(tp);
+       while ((tfp = exp->tagfq.tqh_first) != NULL)
+               FREETAGF(tfp);
+       FREE(exp->tlast, strlen(exp->tlast) + 1);
+       return (0);
+}
+
+/*
+ * ex_tagcopy --
+ *     Copy a screen's tag structures.
+ */
+int
+ex_tagcopy(orig, sp)
+       SCR *orig, *sp;
+{
+       EX_PRIVATE *oexp, *nexp;
+       TAG *ap, *tp;
+       TAGF *atfp, *tfp;
+
+       /* Copy tag stack. */
+       oexp = EXP(orig);
+       nexp = EXP(sp);
+       for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) {
+               MALLOC(sp, tp, TAG *, sizeof(TAG));
+               if (tp == NULL)
+                       goto nomem;
+               *tp = *ap;
+               if (ap->search != NULL &&
+                   (tp->search = strdup(ap->search)) == NULL)
+                       goto nomem;
+               TAILQ_INSERT_TAIL(&nexp->tagq, tp, q);
+       }
+
+       /* Copy list of tag files. */
+       for (atfp = oexp->tagfq.tqh_first;
+           atfp != NULL; atfp = atfp->q.tqe_next) {
+               MALLOC(sp, tfp, TAGF *, sizeof(TAGF));
+               if (tfp == NULL)
+                       goto nomem;
+               *tfp = *atfp;
+               if ((tfp->name = strdup(atfp->name)) == NULL)
+                       goto nomem;
+               TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
+       }
+               
+       /* Copy the last tag. */
+       if (oexp->tlast != NULL &&
+           (nexp->tlast = strdup(oexp->tlast)) == NULL) {
+nomem:         msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * tag_get --
+ *     Get a tag from the tags files.
+ */
+static int
+tag_get(sp, tag, tagp, filep, searchp)
+       SCR *sp;
+       char *tag, **tagp, **filep, **searchp;
+{
+       EX_PRIVATE *exp;
+       TAGF *tfp;
+       int dne;
+       char *p;
+
+       /*
+        * Find the tag, only display missing file messages once, and
+        * then only if we didn't find the tag.
+        */
+       dne = 0;
+       exp = EXP(sp);
+       for (p = NULL, tfp = exp->tagfq.tqh_first;
+           tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) {
+               errno = 0;
+               F_CLR(tfp, TAGF_DNE);
+               if (search(sp, tfp->name, tag, &p))
+                       if (errno == ENOENT) {
+                               if (!F_ISSET(tfp, TAGF_DNE_WARN)) {
+                                       dne = 1;
+                                       F_SET(tfp, TAGF_DNE);
+                               }
+                       } else
+                               msgq(sp, M_SYSERR, tfp->name);
+       }
+       
+       if (p == NULL) {
+               msgq(sp, M_ERR, "%s: tag not found.", tag);
+               if (dne)
+                       for (tfp = exp->tagfq.tqh_first;
+                           tfp != NULL; tfp = tfp->q.tqe_next)
+                               if (F_ISSET(tfp, TAGF_DNE)) {
+                                       errno = ENOENT;
+                                       msgq(sp, M_SYSERR, tfp->name);
+                                       F_SET(tfp, TAGF_DNE_WARN);
+                               }
+               return (1);
+       }
+
+       /*
+        * Set the return pointers; tagp points to the tag, and, incidentally
+        * the allocated string, filep points to the nul-terminated file name,
+        * searchp points to the nul-terminated search string.
+        */
+       for (*tagp = p; *p && !isblank(*p); ++p);
+       if (*p == '\0')
+               goto malformed;
+       for (*p++ = '\0'; isblank(*p); ++p);
+       for (*filep = p; *p && !isblank(*p); ++p);
+       if (*p == '\0')
+               goto malformed;
+       for (*p++ = '\0'; isblank(*p); ++p);
+       *searchp = p;
+       if (*p == '\0') {
+malformed:     free(*tagp);
+               msgq(sp, M_ERR, "%s: corrupted tag in %s.", tag, tfp->name);
+               return (1);
+       }
+       return (0);
+}
+
+#define        EQUAL           0
+#define        GREATER         1
+#define        LESS            (-1)
+
+/*
+ * search --
+ *     Search a file for a tag.
+ */
+static int
+search(sp, name, tname, tag)
+       SCR *sp;
+       char *name, *tname, **tag;
+{
+       struct stat sb;
+       int fd, len;
+       char *endp, *back, *front, *map, *p;
+
+       if ((fd = open(name, O_RDONLY, 0)) < 0)
+               return (1);
+
+       /*
+        * XXX
+        * We'd like to test if the file is too big to mmap.  Since we don't
+        * know what size or type off_t's or size_t's are, what the largest
+        * unsigned integral type is, or what random insanity the local C
+        * compiler will perpetrate, doing the comparison in a portable way
+        * is flatly impossible.  Hope that malloc fails if the file is too
+        * large.
+        */
+       if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size,
+           PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
+               (void)close(fd);
+               return (1);
+       }
+       front = map;
+       back = front + sb.st_size;
+
+       front = binary_search(tname, front, back);
+       front = linear_search(tname, front, back);
+
+       if (front == NULL || (endp = strchr(front, '\n')) == NULL) {
+               *tag = NULL;
+               goto done;
+       }
+
+       len = endp - front;
+       MALLOC(sp, p, char *, len + 1);
+       if (p == NULL) {
+               *tag = NULL;
+               goto done;
+       }
+       memmove(p, front, len);
+       p[len] = '\0';
+       *tag = p;
+
+done:  if (munmap(map, (size_t)sb.st_size))
+               msgq(sp, M_SYSERR, "munmap");
+       if (close(fd))
+               msgq(sp, M_SYSERR, "close");
+       return (0);
+}
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ * 
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string".  Relaxing the constraint
+ * this way simplifies the algorithm.
+ * 
+ * Invariants:
+ *     front points to the beginning of a line at or before the first 
+ *     matching string.
+ * 
+ *     back points to the beginning of a line at or after the first 
+ *     matching line.
+ * 
+ * Base of the Invariants.
+ *     front = NULL; 
+ *     back = EOF;
+ * 
+ * Advancing the Invariants:
+ * 
+ *     p = first newline after halfway point from front to back.
+ * 
+ *     If the string at "p" is not greater than the string to match, 
+ *     p is the new front.  Otherwise it is the new back.
+ * 
+ * Termination:
+ * 
+ *     The definition of the routine allows it return at any point, 
+ *     since front is always at or before the line to print.
+ * 
+ *     In fact, it returns when the chosen "p" equals "back".  This 
+ *     implies that there exists a string is least half as long as 
+ *     (back - front), which in turn implies that a linear search will 
+ *     be no more expensive than the cost of simply printing a string or two.
+ * 
+ *     Trying to continue with binary search at this point would be 
+ *     more trouble than it's worth.
+ */
+#define        SKIP_PAST_NEWLINE(p, back)      while (p < back && *p++ != '\n');
+
+static char *
+binary_search(string, front, back)
+       register char *string, *front, *back;
+{
+       register char *p;
+
+       p = front + (back - front) / 2;
+       SKIP_PAST_NEWLINE(p, back);
+
+       while (p != back) {
+               if (compare(string, p, back) == GREATER)
+                       front = p;
+               else
+                       back = p;
+               p = front + (back - front) / 2;
+               SKIP_PAST_NEWLINE(p, back);
+       }
+       return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ * 
+ * Return NULL for no such line.
+ * 
+ * This routine assumes:
+ * 
+ *     o front points at the first character in a line. 
+ *     o front is before or at the first line to be printed.
+ */
+static char *
+linear_search(string, front, back)
+       char *string, *front, *back;
+{
+       while (front < back) {
+               switch (compare(string, front, back)) {
+               case EQUAL:             /* Found it. */
+                       return (front);
+                       break;
+               case LESS:              /* No such string. */
+                       return (NULL);
+                       break;
+               case GREATER:           /* Keep going. */
+                       break;
+               }
+               SKIP_PAST_NEWLINE(front, back);
+       }
+       return (NULL);
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares
+ * with string2 (s1 ??? s2).
+ * 
+ *     o Matches up to len(s1) are EQUAL. 
+ *     o Matches up to len(s2) are GREATER.
+ * 
+ * The string "s1" is null terminated.  The string s2 is '\t', space, (or
+ * "back") terminated.
+ *
+ * !!!
+ * Reasonably modern ctags programs use tabs as separators, not spaces.
+ * However, historic programs did use spaces, and, I got complaints.
+ */
+static int
+compare(s1, s2, back)
+       register char *s1, *s2, *back;
+{
+       for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
+               if (*s1 != *s2)
+                       return (*s1 < *s2 ? LESS : GREATER);
+       return (*s1 ? GREATER : s2 < back &&
+           (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);
+}
diff --git a/usr.bin/vi/nex/ex_undo.c b/usr.bin/vi/nex/ex_undo.c
new file mode 100644 (file)
index 0000000..c8dedd8
--- /dev/null
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_undo.c  8.4 (Berkeley) 12/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_undol -- U
+ *     Undo changes to this line.
+ */
+int
+ex_undol(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       if (log_setline(sp, ep))
+               return (1);
+
+       sp->cno = 0;
+       return (0);
+}
+
+/*
+ * ex_undo -- u
+ *     Undo the last change.
+ */
+int
+ex_undo(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       MARK m;
+
+       /*
+        * !!!
+        * Multiple undo isn't available in ex, as there's no '.' command.
+        * Whether 'u' is undo or redo is toggled each time, unless there
+        * was a change since the last undo, in which case it's an undo.
+        */
+       if (!F_ISSET(ep, F_UNDO)) {
+               F_SET(ep, F_UNDO);
+               ep->lundo = FORWARD;
+       }
+       switch (ep->lundo) {
+       case BACKWARD:
+               if (log_forward(sp, ep, &m))
+                       return (1);
+               ep->lundo = FORWARD;
+               break;
+       case FORWARD:
+               if (log_backward(sp, ep, &m))
+                       return (1);
+               ep->lundo = BACKWARD;
+               break;
+       case NOTSET:
+               abort();
+       }
+       sp->lno = m.lno;
+       sp->cno = m.cno;
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_usage.c b/usr.bin/vi/nex/ex_usage.c
new file mode 100644 (file)
index 0000000..da84995
--- /dev/null
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_usage.c 8.11 (Berkeley) 12/17/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * ex_help -- :help
+ *     Display help message.
+ */
+int
+ex_help(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       (void)ex_printf(EXCOOKIE,
+           "To see the list of vi commands, enter \":viusage<CR>\"\n");
+       (void)ex_printf(EXCOOKIE,
+           "To see the list of ex commands, enter \":exusage<CR>\"\n");
+       (void)ex_printf(EXCOOKIE,
+           "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n");
+       (void)ex_printf(EXCOOKIE,
+           "For a vi key usage statement enter \":viusage [key]<CR>\"\n");
+       (void)ex_printf(EXCOOKIE, "To exit, enter \":q!\"\n");
+       return (0);
+}
+
+/*
+ * ex_usage -- :exusage [cmd]
+ *     Display ex usage strings.
+ */
+int
+ex_usage(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       ARGS *ap;
+       EXCMDLIST const *cp;
+       
+       switch (cmdp->argc) {
+       case 1:
+               ap = cmdp->argv[0];
+               for (cp = cmds; cp->name != NULL &&
+                   memcmp(ap->bp, cp->name, ap->len); ++cp);
+               if (cp->name == NULL)
+                       (void)ex_printf(EXCOOKIE,
+                           "The %.*s command is unknown.",
+                           (int)ap->len, ap->bp);
+               else {
+                       (void)ex_printf(EXCOOKIE,
+                           "Command: %s\n  Usage: %s\n", cp->help, cp->usage);
+                       /*
+                        * !!!
+                        * The "visual" command has two modes, one from ex,
+                        * one from the vi colon line.  Don't ask.
+                        */
+                       if (cp != &cmds[C_VISUAL_EX] &&
+                           cp != &cmds[C_VISUAL_VI])
+                               break;
+                       if (cp == &cmds[C_VISUAL_EX])
+                               cp = &cmds[C_VISUAL_VI];
+                       else
+                               cp = &cmds[C_VISUAL_EX];
+                       (void)ex_printf(EXCOOKIE,
+                           "Command: %s\n  Usage: %s\n", cp->help, cp->usage);
+               }
+               break;
+       case 0:
+               for (cp = cmds; cp->name != NULL; ++cp)
+                       (void)ex_printf(EXCOOKIE,
+                           "%*s: %s\n", MAXCMDNAMELEN, cp->name, cp->help);
+               break;
+       default:
+               abort();
+       }
+       return (0);
+}
+
+/*
+ * ex_viusage -- :viusage [key]
+ *     Display vi usage strings.
+ */
+int
+ex_viusage(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       VIKEYS const *kp;
+       int key;
+
+       switch (cmdp->argc) {
+       case 1:
+               key = cmdp->argv[0]->bp[0];
+               if (key > MAXVIKEY)
+                       goto nokey;
+
+               /* Special case: '[' and ']' commands. */
+               if ((key == '[' || key == ']') && cmdp->argv[0]->bp[1] != key)
+                       goto nokey;
+
+               kp = &vikeys[key];
+               if (kp->func == NULL)
+nokey:                 (void)ex_printf(EXCOOKIE,
+                           "The %s key has no current meaning",
+                           charname(sp, key));
+               else
+                       (void)ex_printf(EXCOOKIE,
+                           "  Key:%s%s\nUsage: %s\n",
+                           isblank(*kp->help) ? "" : " ", kp->help, kp->usage);
+               break;
+       case 0:
+               for (key = 0; key <= MAXVIKEY; ++key) {
+                       kp = &vikeys[key];
+                       if (kp->help != NULL)
+                               (void)ex_printf(EXCOOKIE, "%s\n", kp->help);
+               }
+               break;
+       default:
+               abort();
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_util.c b/usr.bin/vi/nex/ex_util.c
new file mode 100644 (file)
index 0000000..fa7e70b
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_util.c  8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_getline --
+ *     Return a line from the terminal.
+ */
+int
+ex_getline(sp, fp, lenp)
+       SCR *sp;
+       FILE *fp;
+       size_t *lenp;
+{
+       EX_PRIVATE *exp;
+       size_t off;
+       int ch;
+       char *p;
+
+       exp = EXP(sp);
+       for (off = 0, p = exp->ibp;; ++off) {
+               ch = getc(fp);
+               if (off >= exp->ibp_len) {
+                       BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1);
+                       p = exp->ibp + off;
+               }
+               if (ch == EOF || ch == '\n') {
+                       if (ch == EOF && !off)
+                               return (1);
+                       *lenp = off;
+                       return (0);
+               }
+               *p++ = ch;
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/nex/ex_version.c b/usr.bin/vi/nex/ex_version.c
new file mode 100644 (file)
index 0000000..230632d
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_version.c       8.32 (Berkeley) 1/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_version -- :version
+ *     Display the program version.
+ */
+int
+ex_version(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       static time_t then = 759365451;
+
+       (void)ex_printf(EXCOOKIE,
+"Version 1.03, %sThe CSRG, University of California, Berkeley.\n",
+           ctime(&then));
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_visual.c b/usr.bin/vi/nex/ex_visual.c
new file mode 100644 (file)
index 0000000..d53152b
--- /dev/null
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_visual.c        8.7 (Berkeley) 11/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags]
+ *
+ *     Switch to visual mode.
+ */
+int
+ex_visual(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       size_t len;
+       int pos;
+       char buf[256];
+
+       /* If open option off, disallow visual command. */
+       if (!O_ISSET(sp, O_OPEN)) {
+               msgq(sp, M_ERR,
+                   "The visual command requires that the open option be set.");
+               return (1);
+       }
+
+       /* If a line specified, move to that line. */
+       if (cmdp->addrcnt)
+               sp->lno = cmdp->addr1.lno;
+
+       /*
+        * Push a command based on the line position flags.  If no
+        * flag specified, the line goes at the top of the screen.
+        */
+       switch (F_ISSET(cmdp, E_F_CARAT | E_F_DASH | E_F_DOT | E_F_PLUS)) {
+       case E_F_CARAT:
+               pos = '^';
+               break;
+       case E_F_DASH:
+               pos = '-';
+               break;
+       case E_F_DOT:
+               pos = '.';
+               break;
+       case E_F_PLUS:
+       default:
+               pos = '+';
+               break;
+       }
+
+       if (F_ISSET(cmdp, E_COUNT))
+               len = snprintf(buf, sizeof(buf),
+                    "%luz%c%lu", sp->lno, pos, cmdp->count);
+       else
+               len = snprintf(buf, sizeof(buf), "%luz%c", sp->lno, pos);
+       (void)term_push(sp, buf, len, 0, CH_NOMAP | CH_QUOTED);
+
+       /*
+        * !!!
+        * Historically, if no line address was specified, the [p#l] flags
+        * caused the cursor to be moved to the last line of the file, which
+        * was then positioned as described above.  This seems useless, so
+        * I haven't implemented it.
+        */
+       switch (F_ISSET(cmdp, E_F_HASH | E_F_LIST | E_F_PRINT)) {
+       case E_F_HASH:
+               O_SET(sp, O_NUMBER);
+               break;
+       case E_F_LIST:
+               O_SET(sp, O_LIST);
+               break;
+       case E_F_PRINT:
+               break;
+       }
+
+       /* Switch modes. */
+       F_CLR(sp, S_SCREENS);
+       F_SET(sp, sp->saved_vi_mode);
+
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_write.c b/usr.bin/vi/nex/ex_write.c
new file mode 100644 (file)
index 0000000..5a51b6d
--- /dev/null
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_write.c 8.19 (Berkeley) 12/18/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {WQ, WRITE, XIT};
+
+static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_wq --    :wq[!] [>>] [file]
+ *     Write to a file.
+ */
+int
+ex_wq(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       int force;
+
+       if (exwr(sp, ep, cmdp, WQ))
+               return (1);
+
+       force = F_ISSET(cmdp, E_FORCE);
+       if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+               msgq(sp, M_ERR,
+                   "More files to edit; use \":n\" to go to the next file");
+               return (1);
+       }
+
+       F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+       return (0);
+}
+
+/*
+ * ex_write -- :write[!] [>>] [file]
+ *             :write [!] [cmd]
+ *     Write to a file.
+ */
+int
+ex_write(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (exwr(sp, ep, cmdp, WRITE));
+}
+
+
+/*
+ * ex_xit -- :x[it]! [file]
+ *
+ *     Write out any modifications and quit.
+ */
+int
+ex_xit(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       int force;
+
+       if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT))
+               return (1);
+
+       force = F_ISSET(cmdp, E_FORCE);
+       if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) {
+               msgq(sp, M_ERR,
+                   "More files to edit; use \":n\" to go to the next file");
+               return (1);
+       }
+
+       F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+       return (0);
+}
+
+/*
+ * exwr --
+ *     The guts of the ex write commands.
+ */
+static int
+exwr(sp, ep, cmdp, cmd)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+       enum which cmd;
+{
+       EX_PRIVATE *exp;
+       MARK rm;
+       int flags;
+       char *name, *p;
+
+       /* All write commands can have an associated '!'. */
+       LF_INIT(FS_POSSIBLE);
+       if (F_ISSET(cmdp, E_FORCE))
+               LF_SET(FS_FORCE);
+
+       /* Skip any leading whitespace. */
+       if (cmdp->argc != 0)
+               for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p);
+
+       /* If no arguments, just write the file back. */
+       if (cmdp->argc == 0 || *p == '\0') {
+               if (F_ISSET(cmdp, E_ADDR2_ALL))
+                       LF_SET(FS_ALL);
+               return (file_write(sp, ep,
+                   &cmdp->addr1, &cmdp->addr2, NULL, flags));
+       }
+
+       /* If "write !" it's a pipe to a utility. */
+       exp = EXP(sp);
+       if (cmd == WRITE && *p == '!') {
+               for (++p; *p && isblank(*p); ++p);
+               if (*p == '\0') {
+                       msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+                       return (1);
+               }
+               /* Expand the argument. */
+               if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0))
+                       return (1);
+               if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2,
+                   &rm, cmdp->argv[1]->bp, FILTER_WRITE))
+                       return (1);
+               sp->lno = rm.lno;
+               return (0);
+       }
+
+       /* If "write >>" it's an append to a file. */
+       if (cmd != XIT && p[0] == '>' && p[1] == '>') {
+               LF_SET(FS_APPEND);
+
+               /* Skip ">>" and whitespace. */
+               for (p += 2; *p && isblank(*p); ++p);
+       }
+
+       /* Build an argv so we get an argument count and file expansion. */
+       if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0))
+               return (1);
+
+       switch (cmdp->argc) {
+       case 1:
+               /*
+                * Nothing to expand, write the current file. 
+                * XXX
+                * Should never happen, already checked this case.
+                */
+               name = NULL;
+               break;
+       case 2:
+               /* One new argument, write it. */
+               name = cmdp->argv[exp->argsoff - 1]->bp;
+               set_alt_name(sp, name);
+               break;
+       default:
+               /* If expanded to more than one argument, object. */
+               msgq(sp, M_ERR, "%s expanded into too many file names",
+                   cmdp->argv[0]->bp);
+               msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
+               return (1);
+       }
+
+       if (F_ISSET(cmdp, E_ADDR2_ALL))
+               LF_SET(FS_ALL);
+       return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags));
+}
+
+/*
+ * ex_writefp --
+ *     Write a range of lines to a FILE *.
+ */
+int
+ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch)
+       SCR *sp;
+       EXF *ep;
+       char *name;
+       FILE *fp;
+       MARK *fm, *tm;
+       u_long *nlno, *nch;
+{
+       register u_long ccnt, fline, tline;
+       size_t len;
+       char *p;
+
+       fline = fm->lno;
+       tline = tm->lno;
+
+       if (nlno != NULL) {
+               *nch = 0;
+               *nlno = 0;
+       }
+       ccnt = 0;
+
+       /*
+        * The vi filter code has multiple processes running simultaneously,
+        * and one of them calls ex_writefp().  The "unsafe" function calls
+        * in this code are to file_gline() and msgq().  File_gline() is safe,
+        * see the comment in filter.c:filtercmd() for details.  We don't call
+        * msgq if the multiple process bit in the EXF is set.
+        *
+        * !!!
+        * Historic vi permitted files of 0 length to be written.  However,
+        * since the way vi got around dealing with "empty" files was to
+        * always have a line in the file no matter what, it wrote them as
+        * files of a single, empty line.  We write empty files.
+        *
+        * "Alex, I'll take vi trivia for $1000."
+        */
+       if (tline != 0)
+               for (; fline <= tline; ++fline) {
+                       if ((p = file_gline(sp, ep, fline, &len)) == NULL)
+                               break;
+                       if (fwrite(p, 1, len, fp) != len) {
+                               msgq(sp, M_SYSERR, name);
+                               (void)fclose(fp);
+                               return (1);
+                       }
+                       ccnt += len;
+                       if (putc('\n', fp) != '\n')
+                               break;
+                       ++ccnt;
+               }
+       if (fclose(fp)) {
+               if (!F_ISSET(ep, F_MULTILOCK))
+                       msgq(sp, M_SYSERR, name);
+               return (1);
+       }
+       if (nlno != NULL) {
+               *nch = ccnt;
+               *nlno = tm->lno == 0 ? 0 : tm->lno - fm->lno + 1;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/ex_yank.c b/usr.bin/vi/nex/ex_yank.c
new file mode 100644 (file)
index 0000000..0897034
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_yank.c  8.3 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_yank -- :[line [,line]] ya[nk] [buffer] [count]
+ *
+ *     Yank the lines into a buffer.
+ */
+int
+ex_yank(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       return (cut(sp, ep, NULL,
+           F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+           &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE));
+}
diff --git a/usr.bin/vi/nex/ex_z.c b/usr.bin/vi/nex/ex_z.c
new file mode 100644 (file)
index 0000000..2842a22
--- /dev/null
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_z.c     8.4 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_z -- :[line] z [^-.+=] [count] [flags]
+ *
+ *     Adjust window.
+ */
+int
+ex_z(sp, ep, cmdp)
+       SCR *sp;
+       EXF *ep;
+       EXCMDARG *cmdp;
+{
+       recno_t cnt, equals, lno;
+       int eofcheck;
+
+       /*
+        * !!!
+        * If no count specified, use either two times the size of the
+        * scrolling region, or the size of the window option.  POSIX
+        * 1003.2 claims that the latter is correct, but historic ex/vi
+        * documentation and practice appear to use the scrolling region.
+        * I'm using the window size as it means that the entire screen
+        * is used instead of losing a line to roundoff.  Note, we drop
+        * a line from the cnt if using the window size to leave room for
+        * the next ex prompt.
+        */
+       if (F_ISSET(cmdp, E_COUNT))
+               cnt = cmdp->count;
+       else
+#ifdef HISTORIC_PRACTICE
+               cnt = O_VAL(sp, O_SCROLL) * 2;
+#else
+               cnt = O_VAL(sp, O_WINDOW) - 1;
+#endif
+
+       equals = 0;
+       eofcheck = 0;
+       lno = cmdp->addr1.lno;
+
+       switch (F_ISSET(cmdp,
+           E_F_CARAT | E_F_DASH | E_F_DOT | E_F_EQUAL | E_F_PLUS)) {
+       case E_F_CARAT:         /* Display cnt * 2 before the line. */
+               eofcheck = 1;
+               if (lno > cnt * 2)
+                       cmdp->addr1.lno = (lno - cnt * 2) + 1;
+               else
+                       cmdp->addr1.lno = 1;
+               cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
+               break;
+       case E_F_DASH:          /* Line goes at the bottom of the screen. */
+               cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
+               cmdp->addr2.lno = lno;
+               break;
+       case E_F_DOT:           /* Line goes in the middle of the screen. */
+               /*
+                * !!!
+                * Historically, the "middleness" of the line overrode the
+                * count, so that "3z.19" or "3z.20" would display the first
+                * 12 lines of the file, i.e. (N - 1) / 2 lines before and
+                * after the specified line.
+                */
+               eofcheck = 1;
+               cnt = (cnt - 1) / 2;
+               cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+               cmdp->addr2.lno = lno + cnt;
+               break;
+       case E_F_EQUAL:         /* Center with hyphens. */
+               /*
+                * !!!
+                * Strangeness.  The '=' flag is like the '.' flag (see the
+                * above comment, it applies here as well) but with a special
+                * little hack.  Print out lines of hyphens before and after
+                * the specified line.  Additionally, the cursor remains set
+                * on that line.
+                */
+               eofcheck = 1;
+               cnt = (cnt - 1) / 2;
+               cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+               cmdp->addr2.lno = lno - 1;
+               if (ex_pr(sp, ep, cmdp))
+                       return (1);
+               (void)ex_printf(EXCOOKIE,
+                   "%s", "----------------------------------------\n");
+               cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
+               if (ex_pr(sp, ep, cmdp))
+                       return (1);
+               (void)ex_printf(EXCOOKIE,
+                   "%s", "----------------------------------------\n");
+               cmdp->addr1.lno = lno + 1;
+               cmdp->addr2.lno = (lno + cnt) - 1;
+               break;
+       default:
+               /* If no line specified, move to the next one. */
+               if (F_ISSET(cmdp, E_ADDRDEF))
+                       ++lno;
+               /* FALLTHROUGH */
+       case E_F_PLUS:          /* Line goes at the top of the screen. */
+               eofcheck = 1;
+               cmdp->addr1.lno = lno;
+               cmdp->addr2.lno = (lno + cnt) - 1;
+               break;
+       }
+
+       if (eofcheck) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (cmdp->addr2.lno > lno)
+                       cmdp->addr2.lno = lno;
+       }
+
+       if (ex_pr(sp, ep, cmdp))
+               return (1);
+       if (equals)
+               sp->lno = equals;
+       return (0);
+}
diff --git a/usr.bin/vi/nex/excmd.c b/usr.bin/vi/nex/excmd.c
new file mode 100644 (file)
index 0000000..27ebc39
--- /dev/null
@@ -0,0 +1,431 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)excmd.c    8.36 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * This array maps ex command names to command functions.
+ *
+ * The order in which command names are listed below is important --
+ * ambiguous abbreviations are resolved to be the first possible match,
+ * e.g. "r" means "read", not "rewind", because "read" is listed before
+ * "rewind".
+ *
+ * The syntax of the ex commands is unbelievably irregular, and a special 
+ * case from beginning to end.  Each command has an associated "syntax
+ * script" which describes the "arguments" that are possible.  The script
+ * syntax is as follows:
+ *
+ *     !               -- ! flag
+ *     1               -- flags: [+-]*[pl#][+-]*
+ *     2               -- flags: [-.+^]
+ *     3               -- flags: [-.+^=]
+ *     b               -- buffer
+ *     c[01+a]         -- count (0-N, 1-N, signed 1-N, address offset)
+ *     f[N#][or]       -- file (a number or N, optional or required)
+ *     l               -- line
+ *     S               -- string with file name expansion
+ *     s               -- string
+ *     W               -- word string
+ *     w[N#][or]       -- word (a number or N, optional or required)
+ */
+EXCMDLIST const cmds[] = {
+/* C_BANG */
+       {"!",           ex_bang,        E_ADDR2_NONE|E_NORC,
+           "S",
+           "[line [,line]] ! command",
+           "filter lines through commands or run commands"},
+/* C_HASH */
+       {"#",           ex_number,      E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+           "ca1",
+           "[line [,line]] # [count] [l]",
+           "display numbered lines"},
+/* C_SUBAGAIN */
+       {"&",           ex_subagain,    E_ADDR2|E_NORC,
+           "s",
+           "[line [,line]] & [cgr] [count] [#lp]",
+           "repeat the last subsitution"},
+/* C_STAR */
+       {"*",           ex_at,          0,
+           "b",
+           "* [buffer]",
+           "execute a buffer"},
+/* C_SHIFTL */
+       {"<",           ex_shiftl,      E_ADDR2|E_AUTOPRINT|E_NORC,
+           "ca1",
+           "[line [,line]] <[<...] [count] [flags]",
+           "shift lines left"},
+/* C_EQUAL */
+       {"=",           ex_equal,       E_ADDR1|E_NORC,
+           "1",
+           "[line] = [flags]",
+           "display line number"},
+/* C_SHIFTR */
+       {">",           ex_shiftr,      E_ADDR2|E_AUTOPRINT|E_NORC,
+           "ca1",
+           "[line [,line]] >[>...] [count] [flags]",
+           "shift lines right"},
+/* C_AT */
+       {"@",           ex_at,          0,
+           "b",
+           "@ [buffer]",
+           "execute a buffer"},
+/* C_APPEND */
+       {"append",      ex_append,      E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+           "!",
+           "[line] a[ppend][!]",
+           "append input to a line"},
+/* C_ABBR */
+       {"abbreviate",  ex_abbr,        E_NOGLOBAL,
+           "W",
+           "ab[brev] word replace",
+           "specify an input abbreviation"},
+/* C_ARGS */
+       {"args",        ex_args,        E_NOGLOBAL|E_NORC,
+           "", 
+           "ar[gs]",
+           "display file argument list"},
+/* C_BG */
+       {"bg",          ex_bg,          E_NOGLOBAL|E_NORC,
+           "",
+           "bg",
+           "background the current screen"},
+/* C_CHANGE */
+       {"change",      ex_change,      E_ADDR2|E_NORC|E_ZERODEF,
+           "!ca",
+           "[line [,line]] c[hange][!] [count]",
+           "change lines to input"},
+/* C_CD */
+       {"cd",          ex_cd,          E_NOGLOBAL,
+           "!f1o",
+           "cd[!] [directory]",
+           "change the current directory"},
+/* C_CHDIR */
+       {"chdir",       ex_cd,          E_NOGLOBAL,
+           "!f1o",
+           "chd[ir][!] [directory]",
+           "change the current directory"},
+/* C_COPY */
+       {"copy",        ex_copy,        E_ADDR2|E_AUTOPRINT|E_NORC,
+           "l1",
+           "[line [,line]] co[py] line [flags]",
+           "copy lines elsewhere in the file"},
+/* C_DELETE */
+       {"delete",      ex_delete,      E_ADDR2|E_AUTOPRINT|E_NORC,
+           "bca1",
+           "[line [,line]] d[elete] [buffer] [count] [flags]",
+           "delete lines from the file"},
+/* C_DISPLAY */
+       {"display",     ex_display,     E_NOGLOBAL|E_NORC,
+           "w1r",      
+           "display b[uffers] | s[creens] | t[ags]",
+           "display buffers, screens or tags"},
+/* C_DIGRAPH */
+       {"digraph",     ex_digraph,     E_NOGLOBAL|E_NOPERM|E_NORC,
+           "", 
+           "digraph",
+           "specify digraphs (not implemented)"},
+/* C_EDIT */
+       {"edit",        ex_edit,        E_NOGLOBAL|E_NORC,
+           "!f1o",
+           "e[dit][!] [+cmd] [file]",
+           "begin editing another file"},
+/* C_EX */
+       {"ex",          ex_edit,        E_NOGLOBAL|E_NORC,
+           "!f1o",
+           "ex[!] [+cmd] [file]",
+           "begin editing another file"},
+/* C_EXUSAGE */
+       {"exusage",     ex_usage,       E_NOGLOBAL|E_NORC,
+           "w1o",
+           "[exu]sage [command]",
+           "display ex command usage statement"},
+/* C_FILE */
+       {"file",        ex_file,        E_NOGLOBAL|E_NORC,
+           "f1o",
+           "f[ile] [name]",
+           "display (and optionally set) file name"},
+/* C_FG */
+       {"fg",          ex_fg,          E_NOGLOBAL|E_NORC,
+           "f1o",
+           "fg [file]",
+           "switch the current screen and a backgrounded screen"},
+/* C_GLOBAL */
+       {"global",      ex_global,      E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+           "!s",
+           "[line [,line]] g[lobal][!] [;/]pattern[;/] [commands]",
+           "execute a global command on lines matching a pattern"},
+/* C_HELP */
+       {"help",        ex_help,        E_NOGLOBAL|E_NORC,
+           "",
+           "he[lp]",
+           "display help statement"},
+/* C_INSERT */
+       {"insert",      ex_insert,      E_ADDR1|E_NORC,
+           "!",
+           "[line] i[nsert][!]",
+           "insert input before a line"},
+/* C_JOIN */
+       {"join",        ex_join,        E_ADDR2|E_AUTOPRINT|E_NORC,
+           "!ca1",
+           "[line [,line]] j[oin][!] [count] [flags]",
+           "join lines into a single line"},
+/* C_K */
+       {"k",           ex_mark,        E_ADDR1|E_NORC,
+           "w1r",
+           "[line] k key",
+           "mark a line position"},
+/* C_LIST */
+       {"list",        ex_list,        E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+           "ca1",
+           "[line [,line]] l[ist] [count] [#]",
+           "display lines in an unambiguous form"},
+/* C_MOVE */
+       {"move",        ex_move,        E_ADDR2|E_AUTOPRINT|E_NORC,
+           "l",
+           "[line [,line]] m[ove] line",
+           "move lines elsewhere in the file"},
+/* C_MARK */
+       {"mark",        ex_mark,        E_ADDR1|E_NORC,
+           "w1r",
+           "[line] ma[rk] key",
+           "mark a line position"},
+/* C_MAP */
+       {"map",         ex_map,         0,
+           "!W",
+           "map[!] [keys replace]",
+           "map input or commands to one or more keys"},
+/* C_MKEXRC */
+       {"mkexrc",      ex_mkexrc,      E_NOGLOBAL|E_NORC,
+           "!f1r",
+           "mkexrc[!] file",
+           "write a .exrc file"},
+/* C_NEXT */
+       {"next",        ex_next,        E_NOGLOBAL|E_NORC,
+           "!fN",
+           "n[ext][!] [file ...]",
+           "edit (and optionally specify) the next file"},
+/* C_NUMBER */
+       {"number",      ex_number,      E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+           "ca1",
+           "[line [,line]] nu[mber] [count] [l]",
+           "change display to number lines"},
+/* C_OPEN */
+       {"open",        ex_open,        E_ADDR1,
+           "s",
+           "[line] o[pen] [/pattern/] [flags]",
+           "enter \"open\" mode (not implemented)"},
+/* C_PRINT */
+       {"print",       ex_pr,          E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST,
+           "ca1",
+           "[line [,line]] p[rint] [count] [#l]",
+           "display lines"},
+/* C_PRESERVE */
+       {"preserve",    ex_preserve,    E_NOGLOBAL|E_NORC,
+           "", 
+           "pre[serve]",
+           "preserve an edit session for recovery"},
+/* C_PREVIOUS */
+       {"previous",    ex_prev,        E_NOGLOBAL|E_NORC,
+           "!",
+           "prev[ious][!]",
+           "edit the previous file in the file argument list"},
+/* C_PUT */
+       {"put",         ex_put,         E_ADDR1|E_AUTOPRINT|E_NORC|E_ZERO,
+           "b",
+           "[line] pu[t] [buffer]",
+           "append a cut buffer to the line"},
+/* C_QUIT */
+       {"quit",        ex_quit,        E_NOGLOBAL,
+           "!",
+           "q[uit][!]",
+           "exit ex/vi"},
+/* C_READ */
+       {"read",        ex_read,        E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+           "!s",
+           "[line] r[ead] [!cmd | [file]]",
+           "append input from a command or file to the line"},
+/* C_RESIZE */
+       {"resize",      ex_resize,      E_NOGLOBAL|E_NORC,
+           "c+",
+           "resize [change]",
+           "grow or shrink the current screen"},
+/* C_REWIND */
+       {"rewind",      ex_rew,         E_NOGLOBAL|E_NORC,
+           "!",
+           "rew[ind][!]",
+           "re-edit all the files in the file argument list"},
+/* C_SUBSTITUTE */
+       {"substitute",  ex_substitute,  E_ADDR2|E_NORC,
+           "s",
+"[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]",
+           "substitute on lines matching a pattern"},
+/* C_SCRIPT */
+       {"script",      ex_script,      E_NOGLOBAL|E_NORC,
+           "!f1o",
+           "sc[ript][!] [file]",
+           "run a shell in a screen"},
+/* C_SET */
+       {"set",         ex_set,         E_NOGLOBAL,
+           "wN",
+           "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
+           "set options (use \":set all\" to see all options)"},
+/* C_SHELL */
+       {"shell",       ex_shell,       E_NOGLOBAL|E_NORC,
+           "", 
+           "sh[ell]",
+           "suspend editing and run a shell"},
+/* C_SOURCE */
+       {"source",      ex_source,      E_NOGLOBAL,
+           "f1r", 
+           "so[urce] file",
+           "read a file of ex commands"},
+/* C_SPLIT */
+       {"split",       ex_split,       E_NOGLOBAL|E_NORC,
+           "fNo",
+           "sp[lit] [file ...]",
+           "split the current screen into two screens"},
+/* C_STOP */
+       {"stop",        ex_stop,        E_NOGLOBAL|E_NORC,
+           "!",
+           "st[op][!]",
+           "suspend the edit session"},
+/* C_SUSPEND */
+       {"suspend",     ex_stop,        E_NOGLOBAL|E_NORC,
+           "!",
+           "su[spend][!]",
+           "suspend the edit session"},
+/* C_T */
+       {"t",           ex_copy,        E_ADDR2|E_AUTOPRINT|E_NORC,
+           "l1", 
+           "[line [,line]] t line [flags]",
+           "move lines elsewhere in the file"},
+/* C_TAG */
+       {"tag",         ex_tagpush,     E_NOGLOBAL,
+           "!w1o", 
+           "ta[g][!] [string]",
+           "edit the file containing the tag"},
+/* C_TAGPOP */
+       {"tagpop",      ex_tagpop,      E_NOGLOBAL|E_NORC,
+           "!w1o", 
+           "tagp[op][!] [number | file]",
+           "return to a previous tag"},
+/* C_TAGTOP */
+       {"tagtop",      ex_tagtop,      E_NOGLOBAL|E_NORC,
+           "!", 
+           "tagt[op][!]",
+           "return to the first tag"},
+/* C_UNDOL */
+       {"Undo",        ex_undol,       E_AUTOPRINT|E_NOGLOBAL|E_NORC,
+           "", 
+           "U[ndo]",
+           "undo all the changes to this line"},
+/* C_UNDO */
+       {"undo",        ex_undo,        E_AUTOPRINT|E_NOGLOBAL|E_NORC,
+           "", 
+           "u[ndo]",
+           "undo the most recent change"},
+/* C_UNABBREVIATE */
+       {"unabbreviate",ex_unabbr,      E_NOGLOBAL,
+           "w1r", 
+           "una[bbrev] word",
+           "delete an abbreviation"},
+/* C_UNMAP */
+       {"unmap",       ex_unmap,       E_NOGLOBAL,
+           "!w1r", 
+           "unm[ap][!] word",
+           "delete an input or command map"},
+/* C_VGLOBAL */
+       {"vglobal",     ex_vglobal,     E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+           "s", 
+           "[line [,line]] v[global] [;/]pattern[;/] [commands]",
+           "execute a global command on lines NOT matching a pattern"},
+/* C_VERSION */
+       {"version",     ex_version,     E_NOGLOBAL|E_NORC,
+           "", 
+           "version",
+           "display the program version information"},
+/* C_VISUAL_EX */
+       {"visual",      ex_visual,      E_ADDR1|E_NOGLOBAL|E_NORC|E_ZERODEF,
+           "2c11", 
+           "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
+           "enter visual (vi) mode from ex mode"},
+/* C_VISUAL_VI */
+       {"visual",      ex_edit,        E_NOGLOBAL|E_NORC,
+           "!f1o",
+           "vi[sual][!] [+cmd] [file]",
+           "edit another file (from vi mode only)"},
+/* C_VIUSAGE */
+       {"viusage",     ex_viusage,     E_NOGLOBAL|E_NORC,
+           "w1o",
+           "[viu]sage [key]",
+           "display vi key usage statement"},
+/* C_WRITE */
+       {"write",       ex_write,       E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+           "!s",
+           "[line [,line]] w[rite][!] [!cmd | [>>] [file]]",
+           "write the file"},
+/* C_WQ */
+       {"wq",          ex_wq,          E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+           "!s",
+           "[line [,line]] wq[!] [>>] [file]",
+           "write the file and exit"},
+/* C_XIT */
+       {"xit",         ex_xit,         E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+           "!f1o",
+           "[line [,line]] x[it][!] [file]",
+           "exit"},
+/* C_YANK */
+       {"yank",        ex_yank,        E_ADDR2|E_NORC,
+           "bca",
+           "[line [,line]] ya[nk] [buffer] [count]",
+           "copy lines to a cut buffer"},
+/* C_Z */
+       {"z",           ex_z,           E_ADDR1|E_NOGLOBAL|E_NORC,
+           "3c01",
+           "[line] z [-|.|+|^|=] [count] [flags]",
+           "display different screens of the file"},
+/* C_SUBTILDE */
+       {"~",           ex_subtilde,    E_ADDR2|E_NORC,
+           "s",
+           "[line [,line]] ~ [cgr] [count] [#lp]",
+           "replace previous RE with previous replacement string,"},
+       {NULL},
+};
diff --git a/usr.bin/vi/nex/excmd.h.stub b/usr.bin/vi/nex/excmd.h.stub
new file mode 100644 (file)
index 0000000..ce2715c
--- /dev/null
@@ -0,0 +1,340 @@
+/*-
+ * 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.
+ *
+ *     @(#)excmd.h.stub        8.44 (Berkeley) 12/29/93
+ */
+
+/* Ex command structure. */
+typedef struct _excmdlist {
+       char    *name;                  /* Command name. */
+                                       /* Underlying function. */
+       int (*fn) __P((SCR *, EXF *, EXCMDARG *));
+
+#define        E_ADDR1         0x0000001       /* One address. */
+#define        E_ADDR2         0x0000002       /* Two address. */
+#define        E_ADDR2_ALL     0x0000004       /* Zero/two addresses; zero == all. */
+#define        E_ADDR2_NONE    0x0000008       /* Zero/two addresses; zero == none. */
+#define        E_ADDRDEF       0x0000010       /* Default addresses used. */
+#define        E_AUTOPRINT     0x0000020       /* Command always sets autoprint. */
+#define        E_BUFFER        0x0000040       /* Buffer name supplied. */
+#define        E_COUNT         0x0000080       /* Count supplied. */
+#define        E_FORCE         0x0000100       /*  ! */
+
+#define        E_F_CARAT       0x0000200       /*  ^ flag. */
+#define        E_F_DASH        0x0000400       /*  - flag. */
+#define        E_F_DOT         0x0000800       /*  . flag. */
+#define        E_F_EQUAL       0x0001000       /*  = flag. */
+#define        E_F_HASH        0x0002000       /*  # flag. */
+#define        E_F_LIST        0x0004000       /*  l flag. */
+#define        E_F_PLUS        0x0008000       /*  + flag. */
+#define        E_F_PRINT       0x0010000       /*  p flag. */
+
+#define        E_F_PRCLEAR     0x0020000       /* Clear the print (#, l, p) flags. */
+#define        E_MODIFY        0x0040000       /* File name expansion modified arg. */
+#define        E_NOGLOBAL      0x0080000       /* Not in a global. */
+#define        E_NOPERM        0x0100000       /* Permission denied for now. */
+#define        E_NORC          0x0200000       /* Not from a .exrc or EXINIT. */
+#define        E_SETLAST       0x0400000       /* Reset last command. */
+#define        E_ZERO          0x0800000       /* 0 is a legal addr1. */
+#define        E_ZERODEF       0x1000000       /* 0 is default addr1 of empty files. */
+       u_long   flags;
+       char    *syntax;                /* Syntax script. */
+       char    *usage;                 /* Usage line. */
+       char    *help;                  /* Help line. */
+} EXCMDLIST;
+#define        MAXCMDNAMELEN   12              /* Longest command name. */
+extern EXCMDLIST const cmds[];         /* List of ex commands. */
+
+/* Structure passed around to functions implementing ex commands. */
+struct _excmdarg {
+       EXCMDLIST const *cmd;   /* Command entry in command table. */
+       CHAR_T  buffer;         /* Named buffer. */
+       recno_t lineno;         /* Line number. */
+       long    count;          /* Signed, specified count. */
+       int     addrcnt;        /* Number of addresses (0, 1 or 2). */
+       MARK    addr1;          /* 1st address. */
+       MARK    addr2;          /* 2nd address. */
+       ARGS  **argv;           /* Array of arguments. */
+       int     argc;           /* Count of arguments. */
+       u_int   flags;          /* Selected flags from EXCMDLIST. */
+};
+
+/* Global ranges. */
+typedef struct _range  RANGE;
+struct _range {
+       CIRCLEQ_ENTRY(_range) q;        /* Linked list of ranges. */
+       recno_t start, stop;            /* Start/stop of the range. */
+};
+
+/* Ex private, per-screen memory. */
+typedef struct _ex_private {
+       ARGS   **args;                  /* Arguments. */
+       int      argscnt;               /* Argument count. */
+       int      argsoff;               /* Offset into arguments. */
+
+       CHAR_T   at_lbuf;               /* Last executed at buffer's name. */
+       int      at_lbuf_set;           /* If at_lbuf is set. */
+
+       char    *ibp;                   /* Line input buffer. */
+       size_t   ibp_len;               /* Line input buffer length. */
+
+       EXCMDLIST const *lastcmd;       /* Last command. */
+
+       CHAR_T  *lastbcomm;             /* Last bang command. */
+
+       TAILQ_HEAD(_tagh, _tag) tagq;   /* Tag stack. */
+       TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag stack. */
+       char    *tlast;                 /* Saved last tag. */
+
+                                       /* Linked list of ranges. */
+       CIRCLEQ_HEAD(_rangeh, _range) rangeq;
+       recno_t range_lno;              /* Range set line number. */
+
+#define        EX_AUTOPRINT    0x01            /* Autoprint flag. */
+       u_int   flags;
+} EX_PRIVATE;
+#define        EXP(sp) ((EX_PRIVATE *)((sp)->ex_private))
+       
+/* Macro to set up a command structure. */
+#define        SETCMDARG(s, cmd_id, naddr, lno1, lno2, force, arg) {           \
+       ARGS *__ap[2], __a;                                             \
+       memset(&s, 0, sizeof(EXCMDARG));                                \
+       s.cmd = &cmds[cmd_id];                                          \
+       s.addrcnt = (naddr);                                            \
+       s.addr1.lno = (lno1);                                           \
+       s.addr2.lno = (lno2);                                           \
+       s.addr1.cno = s.addr2.cno = 1;                                  \
+       if (force)                                                      \
+               s.flags |= E_FORCE;                                     \
+       if ((__a.bp = arg) == NULL) {                                   \
+               s.argc = 0;                                             \
+               __a.len = 0;                                            \
+       } else {                                                        \
+               s.argc = 1;                                             \
+               __a.len = strlen(arg);                                  \
+       }                                                               \
+       __ap[0] = &__a;                                                 \
+       __ap[1] = NULL;                                                 \
+       s.argv = __ap;                                                  \
+}
+
+/*
+ * :next, :prev, :rewind, :tag, :tagpush, :tagpop modifications check.
+ * If force is set, the autowrite is skipped.
+ */
+#define        MODIFY_CHECK(sp, ep, force) {                                   \
+       if (F_ISSET((ep), F_MODIFIED))                                  \
+               if (O_ISSET((sp), O_AUTOWRITE)) {                       \
+                       if (!(force) &&                                 \
+                           file_write((sp), (ep), NULL, NULL, NULL,    \
+                           FS_ALL | FS_POSSIBLE))                      \
+                               return (1);                             \
+               } else if (ep->refcnt <= 1 && !(force)) {               \
+                       msgq(sp, M_ERR,                                 \
+       "Modified since last write; write or use ! to override.");      \
+                       return (1);                                     \
+               }                                                       \
+}
+
+/*
+ * Macros to set and restore the terminal values, and note if the screen
+ * was modified.  Specific to their uses in ex/filter.c and ex/ex_shell.c.
+ *
+ * The old terminal values almost certainly turn on VINTR, VQUIT and VSUSP.
+ * We don't want to interrupt the parent(s), so we ignore VINTR.  VQUIT is
+ * ignored by main() because nvi never wants to catch it.  A VSUSP handler
+ * have been installed by the screen code.
+ */
+#define        EX_LEAVE(sp, isig, act, oact, sb, osb, term)                    \
+       if (F_ISSET(sp->gp, G_ISFROMTTY)) {                             \
+               (act).sa_handler = SIG_IGN;                             \
+               sigemptyset(&(act).sa_mask);                            \
+               (act).sa_flags = 0;                                     \
+               if ((isig) = !sigaction(SIGINT, &(act), &(oact))) {     \
+                       if (tcgetattr(STDIN_FILENO, &(term))) {         \
+                               msgq(sp, M_SYSERR, "tcgetattr");        \
+                               rval = 1;                               \
+                               goto err;                               \
+                       }                                               \
+                       if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, \
+                           &sp->gp->original_termios)) {               \
+                               msgq(sp, M_SYSERR, "tcsetattr");        \
+                               rval = 1;                               \
+                               goto err;                               \
+                       }                                               \
+               }                                                       \
+               /*                                                      \
+                * The process may write to the terminal.  Save the     \
+                * access time (read) and modification time (write)     \
+                * of the tty; if they have changed when we restore     \
+                * the modes, will have to refresh the screen.          \
+                */                                                     \
+               sb.st_mtime = 1;                                        \
+               osb.st_mtime = 0;                                       \
+               (void)fstat(STDIN_FILENO, &osb);                        \
+       }
+
+#define        EX_RETURN(sp, isig, act, oact, sb, osb, term)                   \
+       if (F_ISSET(sp->gp, G_ISFROMTTY) && (isig)) {                   \
+               if (sigaction(SIGINT, &(oact), NULL)) {                 \
+                       msgq(sp, M_SYSERR, "signal");                   \
+                       rval = 1;                                       \
+               }                                                       \
+               if (tcsetattr(STDIN_FILENO,                             \
+                   TCSANOW | TCSASOFT, &(term))) {                     \
+                       msgq(sp, M_SYSERR, "tcsetattr");                \
+                       rval = 1;                                       \
+               }                                                       \
+               /* If the terminal was used, refresh the screen. */     \
+               (void)fstat(STDIN_FILENO, &(sb));                       \
+               if ((sb).st_mtime != (osb).st_mtime ||                  \
+                   (sb).st_atime != (osb).st_atime)                    \
+                       F_SET(sp, S_REFRESH);                           \
+       }
+
+/*
+ * Filter actions:
+ *
+ *     FILTER          Filter text through the utility.
+ *     FILTER_READ     Read from the utility into the file.
+ *     FILTER_WRITE    Write to the utility, display its output.
+ */
+enum filtertype { FILTER, FILTER_READ, FILTER_WRITE };
+int    filtercmd __P((SCR *, EXF *,
+           MARK *, MARK *, MARK *, char *, enum filtertype));
+
+/* Argument expansion routines. */
+int    argv_init __P((SCR *, EXF *, EXCMDARG *));
+int    argv_exp0 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int    argv_exp1 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int    argv_exp2 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int    argv_exp3 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int    argv_free __P((SCR *));
+
+/* Ex function prototypes. */
+int    ex __P((SCR *, EXF *));
+int    ex_cfile __P((SCR *, EXF *, char *));
+int    ex_cmd __P((SCR *, EXF *, char *, size_t));
+int    ex_end __P((SCR *));
+int    ex_exec_proc __P((SCR *, char *, char *, char *));
+int    ex_gb __P((SCR *, EXF *, TEXTH *, int, u_int));
+int    ex_getline __P((SCR *, FILE *, size_t *));
+int    ex_icmd __P((SCR *, EXF *, char *, size_t));
+int    ex_init __P((SCR *, EXF *));
+int    ex_is_abbrev __P((char *, size_t));
+int    ex_optchange __P((SCR *, int));
+int    ex_print __P((SCR *, EXF *, MARK *, MARK *, int));
+int    ex_readfp __P((SCR *, EXF *, char *, FILE *, MARK *, recno_t *, int));
+void   ex_refresh __P((SCR *, EXF *));
+int    ex_screen_copy __P((SCR *, SCR *));
+int    ex_screen_end __P((SCR *));
+int    ex_sdisplay __P((SCR *, EXF *));
+int    ex_suspend __P((SCR *));
+int    ex_tdisplay __P((SCR *, EXF *));
+int    ex_writefp __P((SCR *, EXF *,
+           char *, FILE *, MARK *, MARK *, u_long *, u_long *));
+void   global_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int    proc_wait __P((SCR *, long, const char *, int));
+int    sscr_end __P((SCR *));
+int    sscr_exec __P((SCR *, EXF *, recno_t));
+int    sscr_input __P((SCR *));
+
+#define        EXPROTO(type, name)                                             \
+       type name __P((SCR *, EXF *, EXCMDARG *))
+
+EXPROTO(int, ex_abbr);
+EXPROTO(int, ex_append);
+EXPROTO(int, ex_args);
+EXPROTO(int, ex_at);
+EXPROTO(int, ex_bang);
+EXPROTO(int, ex_bg);
+EXPROTO(int, ex_cd);
+EXPROTO(int, ex_change);
+EXPROTO(int, ex_color);
+EXPROTO(int, ex_copy);
+EXPROTO(int, ex_debug);
+EXPROTO(int, ex_delete);
+EXPROTO(int, ex_digraph);
+EXPROTO(int, ex_display);
+EXPROTO(int, ex_edit);
+EXPROTO(int, ex_equal);
+EXPROTO(int, ex_fg);
+EXPROTO(int, ex_file);
+EXPROTO(int, ex_global);
+EXPROTO(int, ex_help);
+EXPROTO(int, ex_insert);
+EXPROTO(int, ex_join);
+EXPROTO(int, ex_list);
+EXPROTO(int, ex_map);
+EXPROTO(int, ex_mark);
+EXPROTO(int, ex_mkexrc);
+EXPROTO(int, ex_move);
+EXPROTO(int, ex_next);
+EXPROTO(int, ex_number);
+EXPROTO(int, ex_open);
+EXPROTO(int, ex_pr);
+EXPROTO(int, ex_preserve);
+EXPROTO(int, ex_prev);
+EXPROTO(int, ex_put);
+EXPROTO(int, ex_quit);
+EXPROTO(int, ex_read);
+EXPROTO(int, ex_resize);
+EXPROTO(int, ex_rew);
+EXPROTO(int, ex_script);
+EXPROTO(int, ex_set);
+EXPROTO(int, ex_shell);
+EXPROTO(int, ex_shiftl);
+EXPROTO(int, ex_shiftr);
+EXPROTO(int, ex_source);
+EXPROTO(int, ex_split);
+EXPROTO(int, ex_stop);
+EXPROTO(int, ex_subagain);
+EXPROTO(int, ex_substitute);
+EXPROTO(int, ex_subtilde);
+EXPROTO(int, ex_tagpop);
+EXPROTO(int, ex_tagpush);
+EXPROTO(int, ex_tagtop);
+EXPROTO(int, ex_unabbr);
+EXPROTO(int, ex_undo);
+EXPROTO(int, ex_undol);
+EXPROTO(int, ex_unmap);
+EXPROTO(int, ex_usage);
+EXPROTO(int, ex_validate);
+EXPROTO(int, ex_version);
+EXPROTO(int, ex_vglobal);
+EXPROTO(int, ex_visual);
+EXPROTO(int, ex_viusage);
+EXPROTO(int, ex_wq);
+EXPROTO(int, ex_write);
+EXPROTO(int, ex_xit);
+EXPROTO(int, ex_yank);
+EXPROTO(int, ex_z);
diff --git a/usr.bin/vi/nex/filter.c b/usr.bin/vi/nex/filter.c
new file mode 100644 (file)
index 0000000..d50e1e6
--- /dev/null
@@ -0,0 +1,393 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)filter.c   8.26 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "pathnames.h"
+
+static int     filter_ldisplay __P((SCR *, FILE *));
+
+/*
+ * filtercmd --
+ *     Run a range of lines through a filter utility and optionally
+ *     replace the original text with the stdout/stderr output of
+ *     the utility.
+ */
+int
+filtercmd(sp, ep, fm, tm, rp, cmd, ftype)
+       SCR *sp;
+       EXF *ep;
+       MARK *fm, *tm, *rp;
+       char *cmd;
+       enum filtertype ftype;
+{
+       struct sigaction act, oact;
+       struct stat osb, sb;
+       struct termios term;
+       FILE *ifp, *ofp;                /* GCC: can't be uninitialized. */
+       pid_t parent_writer_pid, utility_pid;
+       recno_t lno, nread;
+       int input[2], isig, output[2], rval;
+       char *name;
+
+       /* Set return cursor position; guard against a line number of zero. */
+       *rp = *fm;
+       if (fm->lno == 0)
+               rp->lno = 1;
+
+       /*
+        * There are three different processes running through this code.
+        * They are the utility, the parent-writer and the parent-reader.
+        * The parent-writer is the process that writes from the file to
+        * the utility, the parent reader is the process that reads from
+        * the utility.
+        *
+        * Input and output are named from the utility's point of view.
+        * The utility reads from input[0] and the parent(s) write to
+        * input[1].  The parent(s) read from output[0] and the utility
+        * writes to output[1].
+        *
+        * In the FILTER_READ case, the utility isn't expected to want
+        * input.  Redirect its input from /dev/null.  Otherwise open
+        * up utility input pipe.
+        */
+       ifp = ofp = NULL;
+       input[0] = input[1] = output[0] = output[1] = -1;
+       if (ftype == FILTER_READ) {
+               if ((input[0] = open(_PATH_DEVNULL, O_RDONLY, 0)) < 0) {
+                       msgq(sp, M_ERR,
+                           "filter: %s: %s", _PATH_DEVNULL, strerror(errno));
+                       return (1);
+               }
+       } else {
+               if (pipe(input) < 0) {
+                       msgq(sp, M_SYSERR, "pipe");
+                       goto err;
+               }
+               if ((ifp = fdopen(input[1], "w")) == NULL) {
+                       msgq(sp, M_SYSERR, "fdopen");
+                       goto err;
+               }
+       }
+
+       /* Open up utility output pipe. */
+       if (pipe(output) < 0) {
+               msgq(sp, M_SYSERR, "pipe");
+               goto err;
+       }
+       if ((ofp = fdopen(output[0], "r")) == NULL) {
+               msgq(sp, M_SYSERR, "fdopen");
+               goto err;
+       }
+
+       /*
+        * Save ex/vi terminal settings, and restore the original ones.
+        * Restoration so that users can do things like ":r! cat /dev/tty".
+        */
+       EX_LEAVE(sp, isig, act, oact, sb, osb, term);
+
+       /* Fork off the utility process. */
+       switch (utility_pid = vfork()) {
+       case -1:                        /* Error. */
+               msgq(sp, M_SYSERR, "vfork");
+err:           if (input[0] != -1)
+                       (void)close(input[0]);
+               if (ifp != NULL)
+                       (void)fclose(ifp);
+               else if (input[1] != -1)
+                       (void)close(input[1]);
+               if (ofp != NULL)
+                       (void)fclose(ofp);
+               else if (output[0] != -1)
+                       (void)close(output[0]);
+               if (output[1] != -1)
+                       (void)close(output[1]);
+               rval = 1;
+               goto ret;
+       case 0:                         /* Utility. */
+               /*
+                * The utility has default signal behavior.  Don't bother
+                * using sigaction(2) 'cause we want the default behavior.
+                */
+               (void)signal(SIGINT, SIG_DFL);
+               (void)signal(SIGQUIT, SIG_DFL);
+
+               /*
+                * Redirect stdin from the read end of the input pipe,
+                * and redirect stdout/stderr to the write end of the
+                * output pipe.
+                */
+               (void)dup2(input[0], STDIN_FILENO);
+               (void)dup2(output[1], STDOUT_FILENO);
+               (void)dup2(output[1], STDERR_FILENO);
+
+               /* Close the utility's file descriptors. */
+               (void)close(input[0]);
+               (void)close(input[1]);
+               (void)close(output[0]);
+               (void)close(output[1]);
+
+               if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+                       name = O_STR(sp, O_SHELL);
+               else
+                       ++name;
+
+               execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+               msgq(sp, M_ERR, "Error: execl: %s: %s",
+                   O_STR(sp, O_SHELL), strerror(errno));
+               _exit (127);
+               /* NOTREACHED */
+       default:                        /* Parent-reader, parent-writer. */
+               /* Close the pipe ends neither parent will use. */
+               (void)close(input[0]);
+               (void)close(output[1]);
+               break;
+       }
+
+       /*
+        * FILTER_READ:
+        *
+        * Reading is the simple case -- we don't need a parent writer,
+        * so the parent reads the output from the read end of the output
+        * pipe until it finishes, then waits for the child.  Ex_readfp
+        * appends to the MARK, and closes ofp.
+        *
+        * !!!
+        * Set the return cursor to the last line read in.  Historically,
+        * this behaves differently from ":r file" command, which leaves
+        * the cursor at the first line read in.  Check to make sure that
+        * it's not past EOF because we were reading into an empty file.
+        */
+       if (ftype == FILTER_READ) {
+               rval = ex_readfp(sp, ep, "filter", ofp, fm, &nread, 0);
+               sp->rptlines[L_ADDED] += nread;
+               if (fm->lno == 0)
+                       rp->lno = nread;
+               else
+                       rp->lno += nread;
+               goto uwait;
+       }
+
+       /*
+        * FILTER, FILTER_WRITE
+        *
+        * Here we need both a reader and a writer.  Temporary files are
+        * expensive and we'd like to avoid disk I/O.  Using pipes has the
+        * obvious starvation conditions.  It's done as follows:
+        *
+        *      fork
+        *      child
+        *              write lines out
+        *              exit
+        *      parent
+        *              FILTER:
+        *                      read lines into the file
+        *                      delete old lines
+        *              FILTER_WRITE
+        *                      read and display lines
+        *              wait for child
+        *
+        * XXX
+        * We get away without locking the underlying database because we know
+        * that none of the records that we're reading will be modified until
+        * after we've read them.  This depends on the fact that the current
+        * B+tree implementation doesn't balance pages or similar things when
+        * it inserts new records.  When the DB code has locking, we should
+        * treat vi as if it were multiple applications sharing a database, and
+        * do the required locking.  If necessary a work-around would be to do
+        * explicit locking in the line.c:file_gline() code, based on the flag
+        * set here.
+        */
+       rval = 0;
+       F_SET(ep, F_MULTILOCK);
+       switch (parent_writer_pid = fork()) {
+       case -1:                        /* Error. */
+               rval = 1;
+               msgq(sp, M_SYSERR, "fork");
+               (void)close(input[1]);
+               (void)close(output[0]);
+               break;
+       case 0:                         /* Parent-writer. */
+               /*
+                * Write the selected lines to the write end of the
+                * input pipe.  Ifp is closed by ex_writefp.
+                */
+               (void)close(output[0]);
+               _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL));
+
+               /* NOTREACHED */
+       default:                        /* Parent-reader. */
+               (void)close(input[1]);
+               if (ftype == FILTER_WRITE)
+                       /*
+                        * Read the output from the read end of the output
+                        * pipe and display it.  Filter_ldisplay closes ofp.
+                        */
+                       rval = filter_ldisplay(sp, ofp);
+               else {
+                       /*
+                        * Read the output from the read end of the output
+                        * pipe.  Ex_readfp appends to the MARK and closes
+                        * ofp.
+                        */
+                       rval = ex_readfp(sp, ep, "filter", ofp, tm, &nread, 0);
+                       sp->rptlines[L_ADDED] += nread;
+               }
+
+               /* Wait for the parent-writer. */
+               rval |= proc_wait(sp,
+                   (long)parent_writer_pid, "parent-writer", 1);
+
+               /* Delete any lines written to the utility. */
+               if (ftype == FILTER && rval == 0) {
+                       for (lno = tm->lno; lno >= fm->lno; --lno)
+                               if (file_dline(sp, ep, lno)) {
+                                       rval = 1;
+                                       break;
+                               }
+                       if (rval == 0)
+                               sp->rptlines[L_DELETED] +=
+                                   (tm->lno - fm->lno) + 1;
+               }
+               /*
+                * If the filter had no output, we may have just deleted
+                * the cursor.  Don't do any real error correction, we'll
+                * try and recover later.
+                */
+                if (rp->lno > 1 && file_gline(sp, ep, rp->lno, NULL) == NULL)
+                       --rp->lno;
+               break;
+       }
+       F_CLR(ep, F_MULTILOCK);
+
+uwait: rval |= proc_wait(sp, (long)utility_pid, cmd, 0);
+
+       /* Restore ex/vi terminal settings. */
+ret:   EX_RETURN(sp, isig, act, oact, sb, osb, term);
+
+       return (rval);
+}
+
+/*
+ * proc_wait --
+ *     Wait for one of the processes.
+ *
+ * !!!
+ * The pid_t type varies in size from a short to a long depending on the
+ * system.  It has to be cast into something or the standard promotion
+ * rules get you.  I'm using a long based on the belief that nobody is
+ * going to make it unsigned and it's unlikely to be a quad.
+ */
+int
+proc_wait(sp, pid, cmd, okpipe)
+       SCR *sp;
+       long pid;
+       const char *cmd;
+       int okpipe;
+{
+       extern const char *const sys_siglist[];
+       size_t len;
+       int pstat;
+
+       /* Wait for the utility to finish. */
+       (void)waitpid((pid_t)pid, &pstat, 0);
+
+       /*
+        * Display the utility's exit status.  Ignore SIGPIPE from the
+        * parent-writer, as that only means that the utility chose to
+        * exit before reading all of its input.
+        */
+       if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
+               for (; isblank(*cmd); ++cmd);
+               len = strlen(cmd);
+               msgq(sp, M_ERR, "%.*s%s: received signal: %s%s.",
+                   MIN(len, 20), cmd, len > 20 ? "..." : "",
+                   sys_siglist[WTERMSIG(pstat)],
+                   WCOREDUMP(pstat) ? "; core dumped" : "");
+               return (1);
+       }
+       if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
+               for (; isblank(*cmd); ++cmd);
+               len = strlen(cmd);
+               msgq(sp, M_ERR, "%.*s%s: exited with status %d",
+                   MIN(len, 20), cmd, len > 20 ? "..." : "",
+                   WEXITSTATUS(pstat));
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * filter_ldisplay --
+ *     Display a line output from a utility.
+ *
+ * XXX
+ * This should probably be combined with some of the ex_print()
+ * routines into a single display routine.
+ */
+static int
+filter_ldisplay(sp, fp)
+       SCR *sp;
+       FILE *fp;
+{
+       EX_PRIVATE *exp;
+       size_t len;
+
+       exp = EXP(sp);
+       while (!ex_getline(sp, fp, &len)) {
+               (void)ex_printf(EXCOOKIE, "%.*s\n", (int)len, exp->ibp);
+               if (ferror(sp->stdfp)) {
+                       msgq(sp, M_SYSERR, NULL);
+                       (void)fclose(fp);
+                       return (1);
+               }
+       }
+       if (fclose(fp)) {
+               msgq(sp, M_SYSERR, NULL);
+               return (1);
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nex/script.h b/usr.bin/vi/nex/script.h
new file mode 100644 (file)
index 0000000..a04f144
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)script.h    8.1 (Berkeley) 12/19/93
+ */
+
+struct _script {
+       pid_t    sh_pid;                /* Shell pid. */
+       int      sh_master;             /* Master pty fd. */
+       int      sh_slave;              /* Slave pty fd. */
+       char    *sh_prompt;             /* Prompt. */
+       size_t   sh_prompt_len;         /* Prompt length. */
+       char     sh_name[64];           /* Pty name */
+       struct winsize sh_win;          /* Window size. */
+       struct termios sh_term;         /* Terminal information. */
+};
diff --git a/usr.bin/vi/nex/tag.h b/usr.bin/vi/nex/tag.h
new file mode 100644 (file)
index 0000000..f75d9b3
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * 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.
+ *
+ *     @(#)tag.h       8.11 (Berkeley) 11/22/93
+ */
+
+struct _tagf {                         /* Tag file. */
+       TAILQ_ENTRY(_tagf) q;           /* Linked list of tag files. */
+       char    *name;                  /* Tag file name. */
+
+#define        TAGF_DNE        0x01            /* Didn't exist. */
+#define        TAGF_DNE_WARN   0x02            /* DNE error reported. */
+       u_char   flags;
+};
+
+struct _tag {                          /* Tag stack. */
+       TAILQ_ENTRY(_tag) q;            /* Linked list of tags. */
+       FREF    *frp;                   /* Saved file name. */
+       recno_t  lno;                   /* Saved line number. */
+       size_t   cno;                   /* Saved column number. */
+       char    *search;                /* Search string. */
+       size_t   slen;                  /* Search string length. */
+};
+
+int    ex_tagalloc __P((SCR *, char *));
+int    ex_tagcopy __P((SCR *, SCR *));
+int    ex_tagdisplay __P((SCR *, EXF *));
+int    ex_tagfirst __P((SCR *, char *));
+int    ex_tagfree __P((SCR *));
diff --git a/usr.bin/vi/nvi/getc.c b/usr.bin/vi/nvi/getc.c
new file mode 100644 (file)
index 0000000..d4ac230
--- /dev/null
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getc.c     8.6 (Berkeley) 10/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.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; only returned once. */
+       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)
+                       csp->cs_flags = CS_EOL;
+               else
+                       csp->cs_ch = csp->cs_bp[--csp->cs_cno];
+               break;
+       case CS_SOF:                            /* SOF; only returned once. */
+       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/nvi/v_again.c b/usr.bin/vi/nvi/v_again.c
new file mode 100644 (file)
index 0000000..7e9ea56
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_again.c  8.2 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_again -- &
+ *     Repeat the previous substitution.
+ */
+int
+v_again(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+       SETCMDARG(cmd, C_SUBAGAIN, 2, fm->lno, fm->lno, 1, "");
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_at.c b/usr.bin/vi/nvi/v_at.c
new file mode 100644 (file)
index 0000000..3b21fc3
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_at.c     8.3 (Berkeley) 8/25/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+int
+v_at(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+        SETCMDARG(cmd, C_AT, 0, OOBLNO, OOBLNO, 0, NULL);
+       cmd.buffer = vp->buffer;
+        return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_ch.c b/usr.bin/vi/nvi/v_ch.c
new file mode 100644 (file)
index 0000000..40807d1
--- /dev/null
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ch.c     8.2 (Berkeley) 12/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+#define        NOPREV {                                                        \
+       msgq(sp, M_BERR, "No previous F, f, T or t search.");           \
+       return (1);                                                     \
+}
+
+#define        NOTFOUND(ch) {                                                  \
+       msgq(sp, M_BERR, "%s not found.", charname(sp, ch));            \
+       return (1);                                                     \
+}
+
+/*
+ * v_chrepeat -- [count];
+ *     Repeat the last F, f, T or t search.
+ */
+int
+v_chrepeat(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       vp->character = sp->lastckey;
+
+       switch (sp->csearchdir) {
+       case CNOTSET:
+               NOPREV;
+       case FSEARCH:
+               return (v_chF(sp, ep, vp, fm, tm, rp));
+       case fSEARCH:
+               return (v_chf(sp, ep, vp, fm, tm, rp));
+       case TSEARCH:
+               return (v_chT(sp, ep, vp, fm, tm, rp));
+       case tSEARCH:
+               return (v_cht(sp, ep, vp, fm, tm, rp));
+       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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int rval;
+       enum cdirection savedir;
+
+       vp->character = sp->lastckey;
+       savedir = sp->csearchdir;
+
+       switch (sp->csearchdir) {
+       case CNOTSET:
+               NOPREV;
+       case FSEARCH:
+               rval = v_chf(sp, ep, vp, fm, tm, rp);
+               break;
+       case fSEARCH:
+               rval = v_chF(sp, ep, vp, fm, tm, rp);
+               break;
+       case TSEARCH:
+               rval = v_cht(sp, ep, vp, fm, tm, rp);
+               break;
+       case tSEARCH:
+               rval = v_chT(sp, ep, vp, fm, tm, rp);
+               break;
+       default:
+               abort();
+       }
+       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 a motion command, to its left if its not.
+ */
+int
+v_cht(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int rval;
+
+       rval = v_chf(sp, ep, vp, fm, tm, rp);
+       if (!rval)
+               --rp->cno;      /* XXX: Motion interaction with v_chf. */
+       sp->csearchdir = tSEARCH;
+       return (rval);
+}
+       
+/*
+ * v_chf -- [count]fc
+ *     Search forward in the line for the next occurrence of the character.
+ *     Place the cursor to its right if a motion command, on it if its not.
+ */
+int
+v_chf(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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))
+               sp->lastckey = key;
+       sp->csearchdir = fSEARCH;
+
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       NOTFOUND(key);
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       if (len == 0)
+               NOTFOUND(key);
+
+       startp = p;
+       endp = p + len;
+       p += fm->cno;
+       for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+               while (++p < endp && *p != key);
+               if (p == endp)
+                       NOTFOUND(key);
+       }
+       rp->lno = fm->lno;
+       rp->cno = p - startp;
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               ++rp->cno;
+       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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int rval;
+
+       rval = v_chF(sp, ep, vp, fm, tm, rp);
+       if (!rval)
+               ++rp->cno;
+       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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+       size_t len;
+       u_long cnt;
+       int key;
+       char *p, *endp;
+
+       /*
+        * !!!
+        * 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))
+               sp->lastckey = key;
+       sp->csearchdir = FSEARCH;
+
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       NOTFOUND(key);
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       if (len == 0)
+               NOTFOUND(key);
+
+       endp = p - 1;
+       p += fm->cno;
+       for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+               while (--p > endp && *p != key);
+               if (p == endp)
+                       NOTFOUND(key);
+       }
+       rp->lno = fm->lno;
+       rp->cno = (p - endp) - 1;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_delete.c b/usr.bin/vi/nvi/v_delete.c
new file mode 100644 (file)
index 0000000..241ddf6
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_delete.c 8.7 (Berkeley) 1/11/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Delete -- [buffer][count]D
+ *     Delete line command.
+ */
+int
+v_Delete(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+       size_t len;
+
+       if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       return (0);
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       if (len == 0)
+               return (0);
+
+       tm->lno = fm->lno;
+       tm->cno = len;
+
+       /* Yank the lines. */
+       if (cut(sp, ep, NULL,
+           F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, CUT_DELETE))
+               return (1);
+       if (delete(sp, ep, fm, tm, 0))
+               return (1);
+
+       rp->lno = fm->lno;
+       rp->cno = fm->cno ? fm->cno - 1 : 0;
+       return (0);
+}
+
+/*
+ * v_delete -- [buffer][count]d[count]motion
+ *     Delete a range of text.
+ */
+int
+v_delete(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t nlines;
+       size_t len;
+       int lmode;
+       
+       /* Yank the lines. */
+       lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0;
+       if (cut(sp, ep, NULL,
+           F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+           fm, tm, lmode | CUT_DELETE))
+               return (1);
+       if (delete(sp, ep, fm, tm, lmode))
+               return (1);
+
+       /* Check for deleting the file. */
+       if (file_lline(sp, ep, &nlines))
+               return (1);
+       if (nlines == 0) {
+               rp->lno = 1;
+               rp->cno = 0;
+               return (0);
+       }
+
+       /*
+        * If deleting lines, leave the cursor at the lowest line deleted,
+        * else, leave the cursor where it started.  Always correct for EOL.
+        *
+        * The historic vi would delete the line the cursor was on (even if
+        * not in line mode) if the motion from the cursor was past the EOF
+        * and the cursor didn't originate on the last line of the file.  A
+        * strange special case.  We never delete the line the cursor is on.
+        * We'd have to pass a flag down to the delete() routine which would
+        * have to special case it.
+        */
+       if (lmode) {
+               rp->lno = MIN(fm->lno, tm->lno);
+               if (rp->lno > nlines)
+                       rp->lno = nlines;
+               rp->cno = 0;
+               (void)nonblank(sp, ep, rp->lno, &rp->cno);
+               return (0);
+       }
+
+       rp->lno = fm->lno;
+       if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+               GETLINE_ERR(sp, rp->lno);
+               return (1);
+       }
+       if (fm->cno >= len)
+               rp->cno = len ? len - 1 : 0;
+       else
+               rp->cno = fm->cno;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_ex.c b/usr.bin/vi/nvi/v_ex.c
new file mode 100644 (file)
index 0000000..631cd34
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ex.c     8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_ex --
+ *     Run ex.
+ */
+int
+v_ex(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (sp->s_ex_run(sp, ep, rp));
+}
diff --git a/usr.bin/vi/nvi/v_exit.c b/usr.bin/vi/nvi/v_exit.c
new file mode 100644 (file)
index 0000000..f308c48
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_exit.c   8.5 (Berkeley) 12/10/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_exit -- ZZ
+ *     Save the file and exit.
+ */
+int
+v_exit(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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.
+        *
+        * Also check for related screens; if they exist, quit, the
+        * user will get the 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/nvi/v_exmode.c b/usr.bin/vi/nvi/v_exmode.c
new file mode 100644 (file)
index 0000000..150ec52
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_exmode.c 8.3 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_exmode --
+ *     Put the editor in EX mode.
+ */
+int
+v_exmode(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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/nvi/v_filter.c b/usr.bin/vi/nvi/v_filter.c
new file mode 100644 (file)
index 0000000..52ff2ae
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_filter.c 8.10 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, fm->lno, tm->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, rp));
+}
diff --git a/usr.bin/vi/nvi/v_increment.c b/usr.bin/vi/nvi/v_increment.c
new file mode 100644 (file)
index 0000000..dfae29e
--- /dev/null
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_increment.c      8.6 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, fm->lno, &len)) == NULL) {
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       GET_SPACE_RET(sp, bp, blen, len + nlen);
+       memmove(bp, p, fm->cno);
+       memmove(bp + fm->cno, nbuf, nlen);
+       memmove(bp + fm->cno + nlen,
+           p + fm->cno + vp->klen, len - fm->cno - vp->klen);
+       len = len - vp->klen + nlen;
+
+       rval = file_sline(sp, ep, fm->lno, bp, len);
+       FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_init.c b/usr.bin/vi/nvi/v_init.c
new file mode 100644 (file)
index 0000000..f0d2fac
--- /dev/null
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_init.c   8.18 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.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;
+       } 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);
+               }
+       }
+       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/nvi/v_join.c b/usr.bin/vi/nvi/v_join.c
new file mode 100644 (file)
index 0000000..c3f81d6
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_join.c   8.3 (Berkeley) 8/29/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_join -- [count]J
+ *     Join lines together.
+ */
+int
+v_join(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->lno + 1;
+       if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
+               lno = fm->lno + (vp->count - 1);
+
+       SETCMDARG(cmd, C_JOIN, 2, fm->lno, lno, 0, NULL);
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_left.c b/usr.bin/vi/nvi/v_left.c
new file mode 100644 (file)
index 0000000..cc80b37
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_left.c   8.3 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_left -- [count]^H, [count]h
+ *     Move left by columns.
+ */
+int
+v_left(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t cnt;
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       if (fm->cno == 0) {
+               msgq(sp, M_BERR, "Already in the first column.");
+               return (1);
+       }
+
+       rp->lno = fm->lno;
+       if (fm->cno > cnt)
+               rp->cno = fm->cno - cnt;
+       else
+               rp->cno = 0;
+       return (0);
+}
+
+/*
+ * v_cfirst -- [count]_
+ *
+ *     Move to the first non-blank column on a line.
+ */
+int
+v_cfirst(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t cnt;
+
+       /*
+        * A count moves down count - 1 rows, so, "3_" is the same as "2j_".
+        *
+        * !!!
+        * Historically, if the _ is a motion, it is always a line motion,
+        * and the line motion flag is set.
+        */
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       if (cnt != 1) {
+               --vp->count;
+               if (v_down(sp, ep, vp, fm, tm, rp))
+                       return (1);
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+                       F_SET(vp, VC_LMODE);
+       } else
+               rp->lno = fm->lno;
+       rp->cno = 0;
+       if (nonblank(sp, ep, rp->lno, &rp->cno))
+               return (1);
+       return (0);
+}
+
+/*
+ * v_first -- ^
+ *     Move to the first non-blank column on this line.
+ */
+int
+v_first(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * 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.
+        */
+       rp->cno = 0;
+       if (nonblank(sp, ep, fm->lno, &rp->cno))
+               return (1);
+       rp->lno = fm->lno;
+       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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       if (F_ISSET(vp, VC_C1SET) && vp->count > 1)
+               rp->cno =
+                   sp->s_chposition(sp, ep, fm->lno, (size_t)--vp->count);
+       else
+               rp->cno = 0;
+       rp->lno = fm->lno;
+       return (0);
+}
+
+/*
+ * v_zero -- 0
+ *     Move to the first column on this line.
+ */
+int
+v_zero(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       rp->lno = fm->lno;
+       rp->cno = 0;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_mark.c b/usr.bin/vi/nvi/v_mark.c
new file mode 100644 (file)
index 0000000..714242d
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_mark.c   8.3 (Berkeley) 10/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_mark -- m[a-z]
+ *     Set a mark.
+ */
+int
+v_mark(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       rp->lno = fm->lno;
+       rp->cno = fm->cno;
+       return (mark_set(sp, ep, vp->character, fm, 1));
+}
+
+/*
+ * v_gomark -- '['`a-z], or `['`a-z]
+ *     Move to a mark.
+ *
+ *     The single quote form moves to the first nonblank character of a line
+ *     containing a mark.  The back quote form moves to a mark, setting both
+ *     row and column.  We use a single routine for both forms, taking care
+ *     of the nonblank behavior using a flag for the command.
+ *
+ *     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_gomark(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       MARK *mp;
+
+       if ((mp = mark_get(sp, ep, vp->character)) == NULL)
+               return (1);
+       *rp = *mp;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_match.c b/usr.bin/vi/nvi/v_match.c
new file mode 100644 (file)
index 0000000..963cca5
--- /dev/null
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_match.c  8.7 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_match -- %
+ *     Search to matching character.
+ */
+int
+v_match(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       register int cnt, matchc, startc;
+       VCS cs;
+       recno_t lno;
+       size_t len, off;
+       int (*gc)__P((SCR *, EXF *, VCS *));
+       char *p;
+
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       goto nomatch;
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       /*
+        * !!!
+        * Historical practice was to search in the forward direction only.
+        */
+       for (off = fm->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 = fm->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);
+       }
+       rp->lno = cs.cs_lno;
+       rp->cno = cs.cs_cno;
+
+       /*
+        * Movement commands go one space further.  Increment the return
+        * MARK or from MARK depending on the direction of the search.
+        */
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+               if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+                       GETLINE_ERR(sp, rp->lno);
+                       return (1);
+               }
+               if (len)
+                       if (gc == cs_next)
+                               ++rp->cno;
+                       else
+                               ++fm->cno;
+       }
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_ntext.c b/usr.bin/vi/nvi/v_ntext.c
new file mode 100644 (file)
index 0000000..1c17ffc
--- /dev/null
@@ -0,0 +1,1728 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ntext.c  8.80 (Berkeley) 1/13/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vi.h"
+#include "seq.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int      txt_abbrev __P((SCR *, TEXT *, ARG_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 *, ARG_CHAR_T));
+static int      txt_indent __P((SCR *, TEXT *));
+static int      txt_margin __P((SCR *, TEXT *, int *, ARG_CHAR_T));
+static int      txt_outdent __P((SCR *, TEXT *));
+static void     txt_showmatch __P((SCR *, EXF *));
+static int      txt_resolve __P((SCR *, EXF *, TEXTH *));
+
+/* 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;                                               \
+}
+
+/*
+ * newtext --
+ *     Read in text from the user.
+ *
+ * !!!
+ * Historic vi always used:
+ *
+ *     ^D: autoindent deletion
+ *     ^H: last character deletion
+ *     ^W: last word deletion
+ *     ^V: quote the next character
+ *
+ * regardless of the user's choices for these characters.  The user's erase
+ * and kill characters worked in addition to these characters.  Ex was not
+ * completely consistent with this, as it did map the scroll command to the
+ * user's EOF character.
+ *
+ * This implementation does not use fixed characters, but uses whatever the
+ * user specified as described by the termios structure.  I'm getting away
+ * with something here, but I think I'm unlikely to get caught.
+ *
+ * !!!
+ * 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_SPACE, A_NOTSPACE } 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. */
+       GS *gp;                 /* Global pointer. */
+       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;
+       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;
+       gp = sp->gp;
+
+       /*
+        * 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;
+                       tp->insert = len - tm->cno;
+               } else
+                       tp->insert = len - sp->cno;
+
+               if (LF_ISSET(TXT_EMARK))
+                       tp->lb[tm->cno - 1] = 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 wasn't a very
+        * good idea.  Note also that "^^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(gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) {
+               abb = A_NOTSPACE;
+               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;
+
+       iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+       for (gp, 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, 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 ins_ch;
+               if (quoted == Q_THISCHAR) {
+                       --sp->cno;
+                       ++tp->owrite;
+                       quoted = Q_NOTSET;
+
+                       if (ch == HEX_CH)
+                               hex = H_NEXTCHAR;
+                       goto ins_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_NOTSPACE && !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_SPACE;                          \
+                       /* Handle hex numbers. */                       \
+                       if (hex == H_INHEX) {                           \
+                               if (txt_hex(sp, tp, &tmp, ch))          \
+                                       goto err;                       \
+                               if (tmp) {                              \
+                                       hex = H_NOTSET;                 \
+                                       goto next_ch;                   \
+                               }                                       \
+                       }                                               \
+                       /*                                              \
+                        * The 'R' command returns any overwriteable    \
+                        * characters in the first line to the original \
+                        * characters.
+                        */                                             \
+                       if (LF_ISSET(TXT_REPLACE) && tp->owrite &&      \
+                           tp == tiqh->cqh_first) {                    \
+                               memmove(tp->lb + sp->cno,               \
+                                   lp + sp->cno, tp->owrite);          \
+                               tp->insert += tp->owrite;               \
+                               tp->owrite = 0;                         \
+                       }                                               \
+                       /* 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, tp->lb, tp->lb_len, tp->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_VEOF:                    /* 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;
+                       /* FALLTHROUGH */
+               default:                        /* Insert the character. */
+ins_ch:                        /*
+                        * If entering a space character after a word, check
+                        * for abbreviations.  If there was one, discard the
+                        * replay characters.
+                        */
+                       if (isblank(ch) && abb == A_NOTSPACE && !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 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 = isblank(ch) ? A_SPACE : A_NOTSPACE;
+
+                       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, pushc, isinfoline, didsubp, turnoffp)
+       SCR *sp;
+       TEXT *tp;
+       ARG_CHAR_T pushc;
+       int isinfoline, *didsubp, *turnoffp;
+{
+       CHAR_T ch;
+       SEQ *qp;
+       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 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.  Not 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 = pushc;
+       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);
+}
+
+/* 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 ((p = t = file_gline(sp, ep, lno, &len)) == NULL)
+                       return (0);
+       } else
+               p = t = aitp->lb;
+       for (nlen = 0; len; ++p) {
+               if (!isblank(*p))
+                       break;
+               /* If last character is a space, it counts. */
+               if (--len == 0) {
+                       ++p;
+                       break;
+               }
+       }
+
+       /* No indentation. */
+       if (p == t)
+               return (0);
+
+       /* Set count. */
+       nlen = p - t;
+
+       /* Make sure the buffer's big enough. */
+       BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+
+       /* Copy the indentation into the new buffer. */
+       memmove(tp->lb + nlen, tp->lb, tp->len);
+       memmove(tp->lb, t, nlen);
+       tp->len += nlen;
+
+       /* Return the additional length. */
+       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, pushc)
+       SCR *sp;
+       TEXT *tp;
+       int *was_hex;
+       ARG_CHAR_T pushc;
+{
+       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 = pushc;
+       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, pushc)
+       SCR *sp;
+       TEXT *tp;
+       int *didbreak;
+       ARG_CHAR_T pushc;
+{
+       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 = pushc;
+       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);
+}
diff --git a/usr.bin/vi/nvi/v_paragraph.c b/usr.bin/vi/nvi/v_paragraph.c
new file mode 100644 (file)
index 0000000..00591cb
--- /dev/null
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_paragraph.c      8.4 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       enum { P_INTEXT, P_INBLANK } pstate;
+       size_t lastlen, len;
+       recno_t cnt, lastlno, lno;
+       char *p, *lp;
+
+       /* Figure out what state we're currently in. */
+       lno = fm->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;
+                       }
+                       /*
+                        * Historically, a motion command was up to the end
+                        * of the previous line, whereas the movement command
+                        * was to the start of the new "paragraph".
+                        */
+found:                 if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+                               rp->lno = lastlno;
+                               rp->cno = lastlen ? lastlen + 1 : 0;
+                       } else {
+                               rp->lno = lno;
+                               rp->cno = 0;
+                       }
+                       return (0);
+               default:
+                       abort();
+               }
+       }
+
+       /*
+        * EOF is a movement sink, however, the } command historically
+        * moved to the end of the last line if repeatedly invoked.
+        */
+eof:   if (fm->lno != lno - 1) {
+               rp->lno = lno - 1;
+               rp->cno = len ? len - 1 : 0;
+               return (0);
+       }
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL)
+               GETLINE_ERR(sp, fm->lno);
+       if (fm->cno != (len ? len - 1 : 0)) {
+               rp->lno = lno - 1;
+               rp->cno = len ? len - 1 : 0;
+               return (0);
+       }
+       v_eof(sp, ep, NULL);
+       return (1);
+}
+
+/*
+ * v_paragraphb -- [count]{
+ *     Move forward count paragraph.
+ */
+int
+v_paragraphb(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       enum { P_INTEXT, P_INBLANK } pstate;
+       size_t len;
+       recno_t cnt, lno;
+       char *p, *lp;
+
+       /*
+        * The { command historically moved to the beginning of the first
+        * line if invoked on the first line.
+        *
+        * Check for SOF.
+        */
+       if (fm->lno <= 1) {
+               if (fm->cno == 0) {
+                       v_sof(sp, NULL);
+                       return (1);
+               }
+               rp->lno = 1;
+               rp->cno = 0;
+               return (0);
+       }
+
+       /* Figure out what state we're currently in. */
+       lno = fm->lno;
+       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 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)) {
+                               if (!--cnt) {
+found:                                 rp->lno = lno;
+                                       rp->cno = 0;
+                                       return (0);
+                               }
+                               pstate = P_INTEXT;
+                       }
+                       break;
+               default:
+                       abort();
+               }
+       }
+
+       /* SOF is a movement sink. */
+sof:   rp->lno = 1;
+       rp->cno = 0;
+       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/nvi/v_put.c b/usr.bin/vi/nvi/v_put.c
new file mode 100644 (file)
index 0000000..60227ea
--- /dev/null
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_put.c    8.6 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       if (F_ISSET(vp, VC_ISDOT))
+               inc_buf(sp, vp);
+       return (put(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 0));
+}
+
+/*
+ * v_put -- [buffer]p
+ *     Insert the contents of the buffer after the cursor.
+ */
+int
+v_put(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       if (F_ISSET(vp, VC_ISDOT))
+               inc_buf(sp, vp);
+
+       return (put(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 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/nvi/v_redraw.c b/usr.bin/vi/nvi/v_redraw.c
new file mode 100644 (file)
index 0000000..fca5ba3
--- /dev/null
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_redraw.c 8.2 (Berkeley) 8/25/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_redraw --
+ *     Redraw the screen.
+ */
+int
+v_redraw(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       F_SET(sp, S_REFRESH);
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_replace.c b/usr.bin/vi/nvi/v_replace.c
new file mode 100644 (file)
index 0000000..f9378e7
--- /dev/null
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_replace.c        8.12 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno != 0) {
+                       GETLINE_ERR(sp, fm->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 simply append the replacement
+        * characters to the line, but I see no compelling reason to do so.
+        */
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       rp->cno = fm->cno + cnt - 1;
+       if (rp->cno > len - 1) {
+               v_eol(sp, ep, fm);
+               return (1);
+       }
+
+       /* Get the character, literal escapes, escape terminates. */
+       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:
+                       *rp = *fm;
+                       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. */
+               rp->lno = fm->lno + cnt;
+
+               /* The first part of the current line. */
+               if (file_sline(sp, ep, fm->lno, p, fm->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 += fm->cno + cnt, len -= fm->cno + cnt;
+                   len && isblank(*p); --len, ++p);
+
+               if ((tp = text_init(sp, p, len, len)) == NULL)
+                       goto err_ret;
+               if (txt_auto(sp, ep, fm->lno, NULL, 0, tp))
+                       goto err_ret;
+               rp->cno = tp->ai ? tp->ai - 1 : 0;
+               if (file_aline(sp, ep, 1, fm->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, fm->lno, "", 0)) {
+err_ret:                       rval = 1;
+                               break;
+                       }
+       } else {
+               memset(bp + fm->cno, ikey.ch, cnt);
+               rval = file_sline(sp, ep, fm->lno, bp, len);
+
+               rp->lno = fm->lno;
+               rp->cno = fm->cno + cnt - 1;
+       }
+       FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_right.c b/usr.bin/vi/nvi/v_right.c
new file mode 100644 (file)
index 0000000..54b7be9
--- /dev/null
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_right.c  8.3 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_right -- [count]' ', [count]l
+ *     Move right by columns.
+ *
+ * Special case: the 'c' and 'd' commands can move past the end of line.
+ */
+int
+v_right(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+       u_long cnt;
+       size_t len;
+
+       if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       v_eol(sp, ep, NULL);
+               else
+                       GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+               
+       rp->lno = fm->lno;
+       if (len == 0 || fm->cno == len - 1) {
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+                       rp->cno = len;
+                       return (0);
+               }
+               v_eol(sp, ep, NULL);
+               return (1);
+       }
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       rp->cno = fm->cno + cnt;
+       if (rp->cno > len - 1)
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+                       rp->cno = len;
+               else
+                       rp->cno = len - 1;
+       return (0);
+}
+
+/*
+ * v_dollar -- [count]$
+ *     Move to the last column.
+ */
+int
+v_dollar(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+       size_t len;
+       u_long cnt;
+
+       /*
+        * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
+        *
+        * !!!
+        * 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.
+        */
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       if (cnt != 1) {
+               --vp->count;
+               if (v_down(sp, ep, vp, fm, tm, rp))
+                       return (1);
+               rp->cno = 0;
+               if (nonblank(sp, ep, rp->lno, &rp->cno))
+                       return (1);
+               if (fm->cno <= rp->cno && F_ISSET(vp, VC_C | VC_D | VC_Y))
+                       F_SET(vp, VC_LMODE);
+       } else
+               rp->lno = fm->lno;
+
+       if (file_gline(sp, ep, rp->lno, &len) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       v_eol(sp, ep, NULL);
+               else
+                       GETLINE_ERR(sp, rp->lno);
+               return (1);
+       }
+               
+       if (len == 0) {
+               v_eol(sp, ep, NULL);
+               return (1);
+       }
+
+       /* If it's a motion component, move one past the end of the line. */
+       rp->cno = F_ISSET(vp, VC_C | VC_D | VC_Y) ? len : len - 1;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_screen.c b/usr.bin/vi/nvi/v_screen.c
new file mode 100644 (file)
index 0000000..986bfa4
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_screen.c 8.8 (Berkeley) 11/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_screen --
+ *     Switch screens.
+ */
+int
+v_screen(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * 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, fm->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/nvi/v_scroll.c b/usr.bin/vi/nvi/v_scroll.c
new file mode 100644 (file)
index 0000000..f69ea0f
--- /dev/null
@@ -0,0 +1,354 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_scroll.c 8.8 (Berkeley) 12/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * 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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t last;
+
+       if (file_lline(sp, ep, &last))
+               return (1);
+       if (F_ISSET(vp, VC_C1SET)) {
+               if (last < vp->count) {
+                       v_eof(sp, ep, fm);
+                       return (1);
+               }
+               rp->lno = vp->count;
+       } else
+               rp->lno = last ? last : 1;
+       return (0);
+}
+
+/* 
+ * v_home -- [count]H
+ *     Move to the first non-blank character of the logical line
+ *     count from the top of the screen, 1 by default.
+ */
+int
+v_home(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (sp->s_position(sp, ep, rp,
+           F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_TOP));
+}
+
+/*
+ * 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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * 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.
+        */
+       return (sp->s_position(sp, ep, rp, 0, P_MIDDLE));
+}
+
+/*
+ * v_bottom -- [count]L
+ *     Move to the first non-blank character of the logical line
+ *     count from the bottom of the screen, 1 by default.
+ */
+int
+v_bottom(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (sp->s_position(sp, ep,
+           rp, F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_BOTTOM));
+}
+
+/*
+ * v_up -- [count]^P, [count]k, [count]-
+ *     Move up by lines.
+ */
+int
+v_up(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+
+       lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       if (fm->lno <= lno) {
+               v_sof(sp, fm);
+               return (1);
+       }
+       rp->lno = fm->lno - lno;
+       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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * 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, fm->lno) : v_down(sp, ep, vp, fm, tm, rp));
+}
+
+/*
+ * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
+ *     Move down by lines.
+ */
+int
+v_down(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+
+       lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+
+       if (file_gline(sp, ep, lno, NULL) == NULL) {
+               v_eof(sp, ep, fm);
+               return (1);
+       }
+       rp->lno = lno;
+       return (0);
+}
+
+/*
+ * v_hpageup -- [count]^U
+ *     Page up half screens.
+ */
+int
+v_hpageup(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /* 
+        * Half screens always succeed unless already at SOF.  Half screens
+        * set the scroll value, even if the command ultimately failed, in
+        * historic vi.  It's 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);
+
+       return (sp->s_down(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1));
+}
+
+/*
+ * v_hpagedown -- [count]^D
+ *     Page down half screens.
+ */
+int
+v_hpagedown(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /* 
+        * Half screens always succeed unless already at EOF.  Half screens
+        * set the scroll value, even if the command ultimately failed, in
+        * historic vi.  It's 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);
+
+       return (sp->s_up(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1));
+}
+
+/*
+ * 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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t count;
+
+       /* Calculation from POSIX 1003.2/D8. */
+       count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1);
+
+       return (sp->s_down(sp, ep, rp, count, 1));
+}
+
+/*
+ * 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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t count;
+
+       /* Calculation from POSIX 1003.2/D8. */
+       count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1);
+
+       return (sp->s_up(sp, ep, rp, count, 1));
+}
+
+/*
+ * v_lineup -- [count]^Y
+ *     Page up by lines.
+ */
+int
+v_lineup(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * The cursor moves down, staying with its original line, unless it
+        * reaches the bottom of the screen.
+        */
+       return (sp->s_down(sp, ep,
+           rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0));
+}
+
+/*
+ * v_linedown -- [count]^E
+ *     Page down by lines.
+ */
+int
+v_linedown(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * The cursor moves up, staying with its original line, unless it
+        * reaches the top of the screen.
+        */
+       return (sp->s_up(sp, ep,
+           rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0));
+}
diff --git a/usr.bin/vi/nvi/v_search.c b/usr.bin/vi/nvi/v_search.c
new file mode 100644 (file)
index 0000000..25dcaf5
--- /dev/null
@@ -0,0 +1,364 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_search.c 8.16 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int bcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int));
+static int fcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int));
+static int getptrn __P((SCR *, EXF *, int, char **));
+
+/*
+ * v_searchn -- n
+ *     Repeat last search.
+ */
+int
+v_searchn(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int flags;
+
+       flags = SEARCH_MSG;
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               flags |= SEARCH_EOL;
+       switch (sp->searchdir) {
+       case BACKWARD:
+               if (b_search(sp, ep, fm, rp, NULL, NULL, &flags))
+                       return (1);
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+                   bcorrect(sp, ep, vp, fm, rp, flags))
+                       return (1);
+               break;
+       case FORWARD:
+               if (f_search(sp, ep, fm, rp, NULL, NULL, &flags))
+                       return (1);
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y| VC_SH) &&
+                   fcorrect(sp, ep, vp, fm, rp, flags))
+                       return (1);
+               break;
+       case NOTSET:
+               msgq(sp, M_ERR, "No previous search pattern.");
+               return (1);
+       default:
+               abort();
+       }
+       return (0);
+}
+
+/*
+ * v_searchN -- N
+ *     Reverse last search.
+ */
+int
+v_searchN(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int flags;
+
+       flags = SEARCH_MSG;
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               flags |= SEARCH_EOL;
+       switch (sp->searchdir) {
+       case BACKWARD:
+               if (f_search(sp, ep, fm, rp, NULL, NULL, &flags))
+                       return (1);
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+                   fcorrect(sp, ep, vp, fm, rp, flags))
+                       return (1);
+               break;
+       case FORWARD:
+               if (b_search(sp, ep, fm, rp, NULL, NULL, &flags))
+                       return (1);
+               if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+                   bcorrect(sp, ep, vp, fm, rp, flags))
+                       return (1);
+               break;
+       case NOTSET:
+               msgq(sp, M_ERR, "No previous search pattern.");
+               return (1);
+       default:
+               abort();
+       }
+       return (0);
+}
+
+/*
+ * v_searchw -- [count]^A
+ *     Search for the word under the cursor.
+ */
+int
+v_searchw(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       size_t blen, len;
+       u_int flags;
+       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);
+               
+       flags = SEARCH_MSG;
+       rval = f_search(sp, ep, fm, rp, bp, NULL, &flags);
+
+       FREE_SPACE(sp, bp, blen);
+       if (rval)
+               return (1);
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+           fcorrect(sp, ep, vp, fm, rp, flags))
+               return (1);
+       return (0);
+}
+
+/*
+ * v_searchb -- [count]?RE[? offset]
+ *     Search backward.
+ */
+int
+v_searchb(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int flags;
+       char *ptrn;
+
+       if (F_ISSET(vp, VC_ISDOT))
+               ptrn = NULL;
+       else {
+               if (getptrn(sp, ep, '?', &ptrn))
+                       return (1);
+               if (ptrn == NULL)
+                       return (0);
+       }
+
+       flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM;
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               flags |= SEARCH_EOL;
+       if (b_search(sp, ep, fm, rp, ptrn, NULL, &flags))
+               return (1);
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+           bcorrect(sp, ep, vp, fm, rp, flags))
+               return (1);
+       return (0);
+}
+
+/*
+ * v_searchf -- [count]/RE[/ offset]
+ *     Search forward.
+ */
+int
+v_searchf(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       int flags;
+       char *ptrn;
+
+       if (F_ISSET(vp, VC_ISDOT))
+               ptrn = NULL;
+       else {
+               if (getptrn(sp, ep, '/', &ptrn))
+                       return (1);
+               if (ptrn == NULL)
+                       return (0);
+       }
+
+       flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM;
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               flags |= SEARCH_EOL;
+       if (f_search(sp, ep, fm, rp, ptrn, NULL, &flags))
+               return (1);
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) &&
+           fcorrect(sp, ep, vp, fm, rp, flags))
+               return (1);
+       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 likely exactly match historic practice.
+ */
+
+/*
+ * bcorrect --
+ *     Handle command with a backward search as the motion.
+ */
+static int
+bcorrect(sp, ep, vp, fm, rp, flags)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *rp;
+       u_int flags;
+{
+       size_t len;
+       char *p;
+
+       /*
+        * !!!
+        * Correct backward searches which start at column 0 to be one
+        * past the last column of the previous line.
+        *
+        * Backward searches become line mode operations if they start
+        * at column 0 and end at column 0 of another line.
+        */
+       if (fm->lno > rp->lno && fm->cno == 0) {
+               if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, rp->lno);
+                       return (1);
+               }
+               if (rp->cno == 0)
+                       F_SET(vp, VC_LMODE);
+               fm->cno = len;
+       }
+
+       /*
+        * !!!
+        * Commands would become line mode operations if there was a delta
+        * specified to the search pattern.
+        */
+       if (LF_ISSET(SEARCH_DELTA)) {
+               F_SET(vp, VC_LMODE);
+               return (0);
+       }
+       return (0);
+}
+
+/*
+ * fcorrect --
+ *     Handle command with a forward search as the motion.
+ */
+static int
+fcorrect(sp, ep, vp, fm, rp, flags)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *rp;
+       u_int flags;
+{
+       size_t len;
+       char *p;
+
+       /*
+        * !!!
+        * Correct forward searches which end at column 0 to be one
+        * past the last column of the previous line.
+        *
+        * Forward searches become line mode operations if they start
+        * at column 0 and end at column 0 of another line.
+        */
+       if (fm->lno < rp->lno && rp->cno == 0) {
+               if ((p = file_gline(sp, ep, --rp->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, rp->lno);
+                       return (1);
+               }
+               if (fm->cno == 0)
+                       F_SET(vp, VC_LMODE);
+               rp->cno = len;
+       }
+
+       /*
+        * !!!
+        * Commands would become line mode operations if there was a delta
+        * specified to the search pattern.
+        */
+       if (LF_ISSET(SEARCH_DELTA)) {
+               F_SET(vp, VC_LMODE);
+               return (0);
+       }
+
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_section.c b/usr.bin/vi/nvi/v_section.c
new file mode 100644 (file)
index 0000000..8d97e2f
--- /dev/null
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_section.c        8.4 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.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 behaviore to match the paragraphs command.
+ *
+ * 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.
+ */
+
+/* Macro to do a check on each line. */
+#define        CHECK {                                                         \
+       if (len == 0)                                                   \
+               continue;                                               \
+       if (p[0] == '{') {                                              \
+               if (!--cnt) {                                           \
+                       rp->cno = 0;                                    \
+                       rp->lno = lno;                                  \
+                       return (0);                                     \
+               }                                                       \
+               continue;                                               \
+       }                                                               \
+       if (p[0] != '.' || len < 3)                                     \
+               continue;                                               \
+       for (lp = list; *lp; lp += 2)                                   \
+               if (lp[0] == p[1] &&                                    \
+                   (lp[1] == ' ' || lp[1] == p[2]) && !--cnt) {        \
+                       rp->cno = 0;                                    \
+                       rp->lno = lno;                                  \
+                       return (0);                                     \
+               }                                                       \
+}
+
+/*
+ * v_sectionf -- [count]]]
+ *     Move forward count sections/functions.
+ */
+int
+v_sectionf(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       size_t len;
+       recno_t cnt, lno;
+       char *p, *list, *lp;
+
+       /* Get macro list. */
+       if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+               return (1);
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       for (lno = fm->lno; (p = file_gline(sp, ep, ++lno, &len)) != NULL;)
+               CHECK;
+
+       /* EOF is a movement sink. */
+       if (fm->lno != lno - 1) {
+               rp->lno = lno - 1;
+               rp->cno = len ? len - 1 : 0;
+               return (0);
+       }
+       v_eof(sp, ep, NULL);
+       return (1);
+}
+
+/*
+ * v_sectionb -- [count][[
+ *     Move backward count sections/functions.
+ */
+int
+v_sectionb(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       size_t len;
+       recno_t cnt, lno;
+       char *p, *list, *lp;
+
+       /* Check for SOF. */
+       if (fm->lno <= 1) {
+               v_sof(sp, NULL);
+               return (1);
+       }
+
+       /* Get macro list. */
+       if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+               return (1);
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       for (lno = fm->lno; (p = file_gline(sp, ep, --lno, &len)) != NULL;)
+               CHECK;
+
+       /* SOF is a movement sink. */
+       rp->lno = 1;
+       rp->cno = 0;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_sentence.c b/usr.bin/vi/nvi/v_sentence.c
new file mode 100644 (file)
index 0000000..6849521
--- /dev/null
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_sentence.c       8.7 (Berkeley) 8/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.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.
+ * And, tabs are eaten (along with spaces) when skipping to the start of the
+ * text follow a "sentence".
+ */
+
+/*
+ * v_sentencef -- [count])
+ *     Move forward count sentences.
+ */
+int
+v_sentencef(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       enum { BLANK, NONE, PERIOD } state;
+       VCS cs;
+       u_long cnt;
+
+       cs.cs_lno = fm->lno;
+       cs.cs_cno = fm->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 (fm->lno != cs.cs_lno || fm->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. */
+       if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
+               goto okret;
+       v_eof(sp, ep, NULL);
+       return (1);
+
+okret: rp->lno = cs.cs_lno;
+       rp->cno = cs.cs_cno;
+
+       /*
+        * Historic, uh, features, yeah, that's right, call 'em features.
+        * If the sentence movement is cutting an entire line, the buffer
+        * is in line mode.  Reach up into the caller's VICMDARG structure,
+        * and whack the flags.
+        */
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y) &&
+           fm->cno == 0 && (rp->cno == 0 || cs.cs_flags != 0)) {
+               if (rp->cno == 0)
+                       --rp->lno;
+               F_SET(vp, VC_LMODE);
+       }
+       return (0);
+}
+
+/*
+ * v_sentenceb -- [count](
+ *     Move backward count sentences.
+ */
+int
+v_sentenceb(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       VCS cs;
+       recno_t slno;
+       size_t len, scno;
+       u_long cnt;
+       int last1, last2;
+
+       if (fm->lno == 1 && fm->cno == 0) {
+               v_sof(sp, NULL);
+               return (1);
+       }
+
+       cs.cs_lno = fm->lno;
+       cs.cs_cno = fm->cno;
+       if (cs_init(sp, ep, &cs))
+               return (1);
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       /*
+        * If on an empty line, skip to the next previous
+        * non-white-space character.
+        */
+       if (cs.cs_flags == CS_EMP) {
+               if (cs_bblank(sp, ep, &cs))
+                       return (1);
+               for (;;) {
+                       if (cs_next(sp, ep, &cs))
+                               return (1);
+                       if (cs.cs_flags != CS_EOL)
+                               break;
+               }
+       }
+
+       for (last1 = last2 = 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) {
+                       last2 = last1;
+                       last1 = 1;
+                       continue;
+               }
+               if (cs.cs_flags == CS_EMP) {
+                       if (--cnt == 0)
+                               goto ret;
+                       if (cs_bblank(sp, ep, &cs))
+                               return (1);
+                       last1 = last2 = 0;
+                       continue;
+               }
+               switch (cs.cs_ch) {
+               case '.':
+               case '?':
+               case '!':
+                       if (!last1 || --cnt != 0) {
+                               last2 = last1 = 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 (fm->lno != cs.cs_lno || fm->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;
+                       last2 = last1 = 0;
+                       break;
+               case '\t':
+                       last1 = last2 = 1;
+                       break;
+               default:
+                       last2 = last1;
+                       last1 =
+                           cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
+                           cs.cs_ch == ')' || cs.cs_ch == ']' ||
+                           cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
+               }
+       }
+
+okret: rp->lno = cs.cs_lno;
+       rp->cno = cs.cs_cno;
+
+       /*
+        * See comment in v_sentencef().  Ignore errors, they should
+        * never occur, and they'll get caught later.
+        */
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y) && rp->cno == 0 &&
+           file_gline(sp, ep, fm->lno, &len) != NULL && (len == 0 ||
+           fm->cno == len - 1))
+               F_SET(vp, VC_LMODE);
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_shift.c b/usr.bin/vi/nvi/v_shift.c
new file mode 100644 (file)
index 0000000..2e98414
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_shift.c  8.3 (Berkeley) 11/13/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_shiftl -- [count]<motion
+ *     Shift lines left.
+ */
+int
+v_shiftl(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+       SETCMDARG(cmd, C_SHIFTL, 2, fm->lno, tm->lno, 0, "<");
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
+
+/*
+ * v_shiftr -- [count]>motion
+ *     Shift lines right.
+ */
+int
+v_shiftr(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+       SETCMDARG(cmd, C_SHIFTR, 2, fm->lno, tm->lno, 0, ">");
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_status.c b/usr.bin/vi/nvi/v_status.c
new file mode 100644 (file)
index 0000000..9109c01
--- /dev/null
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_status.c 8.10 (Berkeley) 11/20/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_status -- ^G
+ *     Show the file status.
+ */
+int
+v_status(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+
+       /*
+        * ^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, fm->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/nvi/v_stop.c b/usr.bin/vi/nvi/v_stop.c
new file mode 100644 (file)
index 0000000..2f64bfe
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_stop.c   8.5 (Berkeley) 10/28/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+int
+v_stop(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /* 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/nvi/v_switch.c b/usr.bin/vi/nvi/v_switch.c
new file mode 100644 (file)
index 0000000..d0d000f
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_switch.c 8.5 (Berkeley) 11/23/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+/*
+ * v_switch -- ^^
+ *     Switch to the previous file.
+ */
+int
+v_switch(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, rp));
+}
diff --git a/usr.bin/vi/nvi/v_tag.c b/usr.bin/vi/nvi/v_tag.c
new file mode 100644 (file)
index 0000000..530e352
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_tag.c    8.2 (Berkeley) 12/3/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <string.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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+       SETCMDARG(cmd, C_TAG, 0, OOBLNO, 0, 0, vp->keyword);
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
+
+/*
+ * v_tagpop -- ^T
+ *     Pop the tags stack.
+ */
+/* ARGSUSED */
+int
+v_tagpop(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       EXCMDARG cmd;
+
+       SETCMDARG(cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL);
+       return (sp->s_ex_cmd(sp, ep, &cmd, rp));
+}
diff --git a/usr.bin/vi/nvi/v_text.c b/usr.bin/vi/nvi/v_text.c
new file mode 100644 (file)
index 0000000..083b5b2
--- /dev/null
@@ -0,0 +1,827 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_text.c   8.23 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.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_BEAUTIFY | 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_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.)
+ */
+
+static int v_CS __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *, u_int));
+
+/*
+ * v_iA -- [count]A
+ *     Append text to the end of the line.
+ */
+int
+v_iA(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->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. */
+                       sp->cno = len == 0 ? 0 : len - 1;
+                       if (first == 1) {
+                               log_cursor(sp, ep);
+                               first = 0;
+                       }
+                       /* Start the change after the line. */
+                       sp->cno = len;
+               }
+
+               if (v_ntext(sp, ep,
+                   &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+                       return (1);
+
+               SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+               sp->lno = lno = rp->lno;
+               sp->cno = rp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_ia -- [count]a
+ *     Append text to the cursor position.
+ */
+int
+v_ia(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->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, rp, 0, OOBLNO, flags))
+                       return (1);
+
+               SET_TXT_STD(sp, TXT_REPLAY);
+               sp->lno = lno = rp->lno;
+               sp->cno = rp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_iI -- [count]I
+ *     Insert text at the first non-blank character in the line.
+ */
+int
+v_iI(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->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. */
+                       if (first == 1) {
+                               log_cursor(sp, ep);
+                               first = 0;
+                       }
+               }
+               if (len == 0)
+                       LF_SET(TXT_APPENDEOL);
+
+               if (v_ntext(sp, ep,
+                   &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
+                       return (1);
+
+               SET_TXT_STD(sp, TXT_REPLAY);
+               sp->lno = lno = rp->lno;
+               sp->cno = rp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_ii -- [count]i
+ *     Insert text at the cursor position.
+ */
+int
+v_ii(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->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, fm->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, rp, 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 = rp->lno;
+               if ((sp->cno = rp->cno) != 0)
+                       ++sp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_iO -- [count]O
+ *     Insert text above this line.
+ */
+int
+v_iO(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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. */
+                       if (first == 1) {
+                               log_cursor(sp, ep);
+                               first = 0;
+                       }
+                       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, rp, 0, ai_line, flags))
+                       return (1);
+
+               SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+               sp->lno = lno = rp->lno;
+               sp->cno = rp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_io -- [count]o
+ *     Insert text after this line.
+ */
+int
+v_io(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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. */
+                       if (first == 1) {
+                               log_cursor(sp, ep);
+                               first = 0;
+                       }
+                       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, rp, 0, ai_line, flags))
+                       return (1);
+
+               SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
+               sp->lno = lno = rp->lno;
+               sp->cno = rp->cno;
+       }
+       return (0);
+}
+
+/*
+ * v_Change -- [buffer][count]C
+ *     Change line command.
+ */
+int
+v_Change(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (v_CS(sp, ep, vp, fm, tm, rp, 0));
+}
+
+/*
+ * v_Subst -- [buffer][count]S
+ *     Line substitute command.
+ */
+int
+v_Subst(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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)) {
+               fm->cno = 0;
+               if (nonblank(sp, ep, fm->lno, &fm->cno))
+                       return (1);
+               LF_SET(TXT_AICHARS);
+       } else
+               fm->cno = 0;
+       sp->cno = fm->cno;
+       return (v_CS(sp, ep, vp, fm, tm, rp, flags));
+}
+
+/*
+ * v_CS --
+ *     C and S commands.
+ */
+static int
+v_CS(sp, ep, vp, fm, tm, rp, iflags)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+       u_int iflags;
+{
+       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.
+        */
+       tm->lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+       if (fm->lno != tm->lno) {
+               /* Make sure that the to line is real. */
+               if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+                       v_eof(sp, ep, fm);
+                       return (1);
+               }
+
+               /* Cut the lines. */
+               if (cut(sp, ep,
+                   NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+                   fm, tm, CUT_LINEMODE))
+                       return (1);
+
+               /* Insert a line while we still can... */
+               if (file_iline(sp, ep, fm->lno, "", 0))
+                       return (1);
+               ++fm->lno;
+               ++tm->lno;
+
+               /* Delete the lines. */
+               if (delete(sp, ep, fm, tm, 1))
+                       return (1);
+
+               /* Get the inserted line. */
+               if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               tm = NULL;
+               sp->lno = fm->lno;
+               sp->cno = 0;
+               LF_SET(TXT_APPENDEOL);
+       } else { 
+               /* The line may be empty, but that's okay. */
+               if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                       if (file_lline(sp, ep, &lno))
+                               return (1);
+                       if (lno != 0) {
+                               GETLINE_ERR(sp, tm->lno);
+                               return (1);
+                       }
+                       len = 0;
+                       LF_SET(TXT_APPENDEOL);
+               } else {
+                       if (cut(sp, ep,
+                           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+                           fm, tm, CUT_LINEMODE))
+                               return (1);
+                       tm->cno = len;
+                       if (len == 0)
+                               LF_SET(TXT_APPENDEOL);
+                       LF_SET(TXT_EMARK | TXT_OVERWRITE);
+               }
+       }
+       /* Correct logging for implied cursor motion. */
+       log_cursor(sp, ep);
+       return (v_ntext(sp, ep,
+           &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
+}
+
+/*
+ * v_change -- [buffer][count]c[count]motion
+ *     Change command.
+ */
+int
+v_change(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, VC_LMODE) ? CUT_LINEMODE : 0;
+       if (lmode) {
+               fm->cno = 0;
+               if (O_ISSET(sp, O_AUTOINDENT)) {
+                       if (nonblank(sp, ep, fm->lno, &fm->cno))
+                               return (1);
+                       LF_SET(TXT_AICHARS);
+               }
+       }
+       sp->lno = fm->lno;
+       sp->cno = fm->cno;
+
+       /* Correct logging for implied cursor motion. */
+       log_cursor(sp, ep);
+
+       /*
+        * 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 (fm->lno == tm->lno) {
+               if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                       if (p == NULL) {
+                               if (file_lline(sp, ep, &lno))
+                                       return (1);
+                               if (lno != 0) {
+                                       GETLINE_ERR(sp, fm->lno);
+                                       return (1);
+                               }
+                       }
+                       len = 0;
+                       LF_SET(TXT_APPENDEOL);
+               } else {
+                       if (cut(sp, ep,
+                           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+                           fm, tm, lmode))
+                               return (1);
+                       if (len == 0)
+                               LF_SET(TXT_APPENDEOL);
+                       LF_SET(TXT_EMARK | TXT_OVERWRITE);
+               }
+               return (v_ntext(sp, ep,
+                   &sp->tiq, tm, p, len, rp, 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, fm, tm, lmode))
+               return (1);
+
+       /* If replacing entire lines and there's leading text. */
+       if (lmode && fm->cno) {
+               /* Get a copy of the first line changed. */
+               if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               /* Copy the leading text elsewhere. */
+               GET_SPACE_RET(sp, bp, blen, fm->cno);
+               memmove(bp, p, fm->cno);
+       } else
+               bp = NULL;
+
+       /* Delete the text. */
+       if (delete(sp, ep, fm, tm, lmode))
+               return (1);
+
+       /* If replacing entire lines, insert a replacement line. */
+       if (lmode) {
+               if (file_iline(sp, ep, fm->lno, bp, fm->cno))
+                       return (1);
+               sp->lno = fm->lno;
+               len = sp->cno = fm->cno;
+       }
+
+       /* Get the line we're editing. */
+       if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno != 0) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               len = 0;
+       }
+
+       /* Check to see if we're appending to the line. */
+       if (fm->cno >= len)
+               LF_SET(TXT_APPENDEOL);
+
+       /* No to mark. */
+       tm = NULL;
+
+       rval = v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, rp->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno != 0) {
+                       GETLINE_ERR(sp, rp->lno);
+                       return (1);
+               }
+               len = 0;
+               LF_SET(TXT_APPENDEOL);
+       } else {
+               if (len == 0)
+                       LF_SET(TXT_APPENDEOL);
+               LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+       }
+       tm->lno = rp->lno;
+       tm->cno = len ? len : 0;
+       if (v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 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 rp->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, rp->lno, &len)) == NULL)
+                       GETLINE_ERR(sp, rp->lno);
+               SET_TXT_STD(sp, TXT_REPLAY);
+
+               sp->lno = rp->lno;
+
+               if (len == 0 || rp->cno == len - 1) {
+                       sp->cno = len;
+                       LF_SET(TXT_APPENDEOL);
+               } else {
+                       sp->cno = rp->cno;
+                       if (rp->cno != 0)
+                               ++sp->cno;
+                       LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+               }
+
+               if (v_ntext(sp, ep,
+                   &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags))
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * v_subst -- [buffer][count]s
+ *     Substitute characters.
+ */
+int
+v_subst(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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, fm->lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno != 0) {
+                       GETLINE_ERR(sp, fm->lno);
+                       return (1);
+               }
+               len = 0;
+               LF_SET(TXT_APPENDEOL);
+       } else {
+               if (len == 0)
+                       LF_SET(TXT_APPENDEOL);
+               LF_SET(TXT_EMARK | TXT_OVERWRITE);
+       }
+
+       tm->lno = fm->lno;
+       tm->cno = fm->cno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+       if (tm->cno > len)
+               tm->cno = len;
+
+       if (p != NULL && cut(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+               return (1);
+
+       return (v_ntext(sp, ep,
+           &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
+}
diff --git a/usr.bin/vi/nvi/v_ulcase.c b/usr.bin/vi/nvi/v_ulcase.c
new file mode 100644 (file)
index 0000000..12fd1c6
--- /dev/null
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ulcase.c 8.3 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       recno_t lno;
+       size_t blen, lcnt, len;
+       u_long cnt;
+       int ch, change, rval;
+       char *bp, *p;
+
+       /* Figure out what memory to use. */
+       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 you 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, fm->lno, &len)) == NULL) {
+                       if (file_lline(sp, ep, &lno))
+                               return (1);
+                       if (lno >= fm->lno) {
+                               GETLINE_ERR(sp, fm->lno);
+                               rval = 1;
+                               break;
+                       }
+                       if (change == -1) {
+                               v_eof(sp, ep, NULL);
+                               return (1);
+                       }
+                       break;
+               }
+
+               /* Set current line number. */
+               lno = fm->lno;
+
+               /* Empty lines just decrement the count. */
+               if (len == 0) {
+                       --cnt;
+                       ++fm->lno;
+                       fm->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 + fm->cno;
+               else
+                       p = bp;
+
+               /*
+                * Figure out how many characters get changed in this
+                * line.  Set the final cursor column.
+                */
+               if (fm->cno + cnt >= len) {
+                       lcnt = len - fm->cno;
+                       ++fm->lno;
+                       fm->cno = 0;
+               } else
+                       fm->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 (fm->lno != lno && file_gline(sp, ep, fm->lno, &len) == NULL) {
+               --fm->lno;
+               fm->cno = len ? len - 1 : 0;
+       }
+       *rp = *fm;
+
+       FREE_SPACE(sp, bp, blen);
+       return (rval);
+}
diff --git a/usr.bin/vi/nvi/v_undo.c b/usr.bin/vi/nvi/v_undo.c
new file mode 100644 (file)
index 0000000..87c749b
--- /dev/null
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_undo.c   8.6 (Berkeley) 1/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Undo -- U
+ *     Undo changes to this line.
+ */
+int
+v_Undo(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /*
+        * 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.
+        */
+       rp->lno = fm->lno;
+       rp->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, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       /* 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, rp));
+       case FORWARD:
+               return (log_forward(sp, ep, rp));
+       default:
+               abort();
+       }
+       /* NOTREACHED */
+}
diff --git a/usr.bin/vi/nvi/v_util.c b/usr.bin/vi/nvi/v_util.c
new file mode 100644 (file)
index 0000000..83c4fb4
--- /dev/null
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_util.c   8.5 (Berkeley) 11/15/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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_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_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/nvi/v_word.c b/usr.bin/vi/nvi/v_word.c
new file mode 100644 (file)
index 0000000..8d917d6
--- /dev/null
@@ -0,0 +1,560 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_word.c   8.10 (Berkeley) 10/26/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.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".  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.  If the lines contained only white-space characters, the 'w' and 'W'
+ * commands will just beep at you, and the 'B', 'b', 'E' and 'e' commands
+ * will treat the group as a single word, and the 'B' and 'b' commands will
+ * treat the lines as individual words.  This implementation treats both
+ * cases as a single white-space word.
+ */
+
+#define        FW(test)        for (; len && (test); --len, ++p)
+#define        BW(test)        for (; len && (test); --len, --p)
+
+enum which {BIGWORD, LITTLEWORD};
+
+static int bword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int));
+static int eword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int));
+static int fword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, enum which));
+
+/*
+ * v_wordw -- [count]w
+ *     Move forward a word at a time.
+ */
+int
+v_wordw(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (fword(sp, ep, vp, fm, rp, LITTLEWORD));
+}
+
+/*
+ * v_wordW -- [count]W
+ *     Move forward a bigword at a time.
+ */
+int
+v_wordW(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (fword(sp, ep, vp, fm, rp, BIGWORD));
+}
+
+/*
+ * fword --
+ *     Move forward by words.
+ */
+static int
+fword(sp, ep, vp, fm, rp, type)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *rp;
+       enum which type;
+{
+       enum { INWORD, NOTWORD } state;
+       VCS cs;
+       u_long cnt;
+
+       cs.cs_lno = fm->lno;
+       cs.cs_cno = fm->cno;
+       if (cs_init(sp, ep, &cs)) 
+               return (1);
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 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)) {
+                               ++cs.cs_cno;
+                               goto ret3;
+                       }
+                       if (F_ISSET(vp, VC_D | VC_Y)) {
+                               if (cs_fspace(sp, ep, &cs))
+                                       return (1);
+                               goto ret1;
+                       }
+               }
+               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 ret2;
+                               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 && F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+                               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 ret2;
+               }
+       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 ret2;
+                               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 && F_ISSET(vp, VC_C | VC_D | VC_Y)) {
+                               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 ret2;
+               }
+
+       /*
+        * If a motion command, and eating the trailing non-word would
+        * move us off this line, don't do it.  Move the return cursor
+        * to one past the EOL instead.
+        */
+ret1:  if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOL)
+               ++cs.cs_cno;
+
+       /* If we didn't move, we must be at EOF. */
+ret2:  if (cs.cs_lno == fm->lno && cs.cs_cno == fm->cno) {
+               v_eof(sp, ep, fm);
+               return (1);
+       }
+       /*
+        * If at EOF, and it's a motion command, move the return cursor
+        * one past the EOF.
+        */
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOF)
+               ++cs.cs_cno;
+ret3:  rp->lno = cs.cs_lno;
+       rp->cno = cs.cs_cno;
+       return (0);
+}
+
+/*
+ * v_wordb -- [count]b
+ *     Move backward a word at a time.
+ */
+int
+v_wordb(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (bword(sp, ep, vp, fm, rp, 0));
+}
+
+/*
+ * v_WordB -- [count]B
+ *     Move backward a bigword at a time.
+ */
+int
+v_wordB(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (bword(sp, ep, vp, fm, rp, 1));
+}
+
+/*
+ * bword --
+ *     Move backward by words.
+ */
+static int
+bword(sp, ep, vp, fm, rp, spaceonly)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *rp;
+       int spaceonly;
+{
+       register char *p;
+       recno_t lno;
+       size_t len;
+       u_long cno, cnt;
+       char *startp;
+
+       lno = fm->lno;
+       cno = fm->cno;
+
+       /* Check for start of file. */
+       if (lno == 1 && cno == 0) {
+               v_sof(sp, NULL);
+               return (1);
+       }
+
+       if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       v_sof(sp, NULL);
+               else
+                       GETLINE_ERR(sp, lno);
+               return (1);
+       }
+               
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       /*
+        * Reset the length to the number of characters in the line; the
+        * first character is the current cursor position.
+        */
+       len = cno ? cno + 1 : 0;
+       if (len == 0)
+               goto line;
+       for (startp = p, p += cno; cnt--;) {
+               if (spaceonly) {
+                       if (!isblank(*p)) {
+                               if (len < 2)
+                                       goto line;
+                               --p;
+                               --len;
+                       }
+                       BW(isblank(*p));
+                       if (len)
+                               BW(!isblank(*p));
+                       else
+                               goto line;
+               } else {
+                       if (!isblank(*p)) {
+                               if (len < 2)
+                                       goto line;
+                               --p;
+                               --len;
+                       }
+                       BW(isblank(*p));
+                       if (len)
+                               if (inword(*p))
+                                       BW(inword(*p));
+                               else
+                                       BW(!isblank(*p) && !inword(*p));
+                       else
+                               goto line;
+               }
+
+               if (cnt && len == 0) {
+                       /* If we hit SOF, stay there (historic practice). */
+line:                  if (lno == 1) {
+                               rp->lno = 1;
+                               rp->cno = 0;
+                               return (0);
+                       }
+
+                       /*
+                        * Get the line.  If the line is empty, decrement
+                        * count and get another one.
+                        */
+                       if ((p = file_gline(sp, ep, --lno, &len)) == NULL) {
+                               GETLINE_ERR(sp, lno);
+                               return (1);
+                       }
+                       if (len == 0) {
+                               if (cnt == 0 || --cnt == 0) {
+                                       rp->lno = lno;
+                                       rp->cno = 0;
+                                       return (0);
+                               }
+                               goto line;
+                       }
+
+                       /*
+                        * Set the cursor to the end of the line.  If the word
+                        * at the end of this line has only a single character,
+                        * we've already skipped over it.
+                        */
+                       startp = p;
+                       if (len) {
+                               p += len - 1;
+                               if (cnt && len > 1 && !isblank(p[0]))
+                                       if (inword(p[0])) {
+                                               if (!inword(p[-1]))
+                                                       --cnt;
+                                       } else if (!isblank(p[-1]) &&
+                                           !inword(p[-1]))
+                                                       --cnt;
+                       }
+               } else {
+                       ++p;
+                       ++len;
+               }
+       }
+       rp->lno = lno;
+       rp->cno = p - startp;
+       return (0);
+}
+
+/*
+ * v_worde -- [count]e
+ *     Move forward to the end of the word.
+ */
+int
+v_worde(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (eword(sp, ep, vp, fm, rp, 0));
+}
+
+/*
+ * v_wordE -- [count]E
+ *     Move forward to the end of the bigword.
+ */
+int
+v_wordE(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       return (eword(sp, ep, vp, fm, rp, 1));
+}
+
+/*
+ * eword --
+ *     Move forward to the end of the word.
+ */
+static int
+eword(sp, ep, vp, fm, rp, spaceonly)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *rp;
+       int spaceonly;
+{
+       register char *p;
+       recno_t lno;
+       size_t len, llen;
+       u_long cno, cnt;
+       int empty;
+       char *startp;
+
+       lno = fm->lno;
+       cno = fm->cno;
+
+       if ((p = file_gline(sp, ep, lno, &llen)) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       v_eof(sp, ep, NULL);
+               else
+                       GETLINE_ERR(sp, lno);
+               return (1);
+       }
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       /*
+        * Reset the length; the first character is the current cursor
+        * position.  If no more characters in this line, may already
+        * be at EOF.
+        */
+       len = llen - cno;
+       if (empty = llen == 0 || llen == cno + 1)
+               goto line;
+
+       for (startp = p += cno; cnt--; empty = 0) {
+               if (spaceonly) {
+                       if (!isblank(*p)) {
+                               if (len < 2)
+                                       goto line;
+                               ++p;
+                               --len;
+                       }
+                       FW(isblank(*p));
+                       if (len)
+                               FW(!isblank(*p));
+                       else
+                               ++cnt;
+               } else {
+                       if (!isblank(*p)) {
+                               if (len < 2)
+                                       goto line;
+                               ++p;
+                               --len;
+                       }
+                       FW(isblank(*p));
+                       if (len)
+                               if (inword(*p))
+                                       FW(inword(*p));
+                               else
+                                       FW(!isblank(*p) && !inword(*p));
+                       else
+                               ++cnt;
+               }
+
+               if (cnt && len == 0) {
+                       /* If we hit EOF, stay there (historic practice). */
+line:                  if ((p = file_gline(sp, ep, ++lno, &llen)) == NULL) {
+                               /*
+                                * If already at eof, complain, unless it's
+                                * a change command or a delete command and
+                                * there's something to delete.
+                                */
+                               if (empty) {
+                                       if (F_ISSET(vp, VC_C) ||
+                                           F_ISSET(vp, VC_D) && llen != 0) {
+                                               rp->lno = lno - 1;
+                                               rp->cno = llen ? llen : 1;
+                                               return (0);
+                                       }
+                                       v_eof(sp, ep, NULL);
+                                       return (1);
+                               }
+                               if ((p =
+                                   file_gline(sp, ep, --lno, &llen)) == NULL) {
+                                       GETLINE_ERR(sp, lno);
+                                       return (1);
+                               }
+                               rp->lno = lno;
+                               rp->cno = llen ? llen - 1 : 0;
+                               /* The 'c', 'd' and 'y' need one more space. */
+                               if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+                                       ++rp->cno;
+                               return (0);
+                       }
+                       len = llen;
+                       cno = 0;
+                       startp = p;
+               } else {
+                       --p;
+                       ++len;
+               }
+       }
+       rp->lno = lno;
+       rp->cno = cno + (p - startp);
+
+       /* The 'c', 'd' and 'y' need one more space. */
+       if (F_ISSET(vp, VC_C | VC_D | VC_Y))
+               ++rp->cno;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_xchar.c b/usr.bin/vi/nvi/v_xchar.c
new file mode 100644 (file)
index 0000000..0198622
--- /dev/null
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_xchar.c  8.4 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+#define        NODEL(sp) {                                                     \
+       msgq(sp, M_BERR, "No characters to delete.");                   \
+       return (1);                                                     \
+}
+
+/*
+ * v_xchar --
+ *     Deletes the character(s) on which the cursor sits.
+ */
+int
+v_xchar(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       MARK m;
+       recno_t lno;
+       u_long cnt;
+       size_t len;
+
+       if (file_gline(sp, ep, fm->lno, &len) == NULL) {
+               if (file_lline(sp, ep, &lno))
+                       return (1);
+               if (lno == 0)
+                       NODEL(sp);
+               GETLINE_ERR(sp, fm->lno);
+               return (1);
+       }
+
+       if (len == 0)
+               NODEL(sp);
+
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+       /*
+        * Deleting 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.
+        */
+       tm->lno = fm->lno;
+       if (cnt < len - fm->cno) {
+               tm->cno = fm->cno + cnt;
+               m = *fm;
+       } else {
+               tm->cno = len;
+               m.lno = fm->lno;
+               m.cno = fm->cno ? fm->cno - 1 : 0;
+       }
+
+       if (cut(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+               return (1);
+       if (delete(sp, ep, fm, tm, 0))
+               return (1);
+
+       *rp = m;
+       return (0);
+}
+
+/*
+ * v_Xchar --
+ *     Deletes the character(s) immediately before the current cursor
+ *     position.
+ */
+int
+v_Xchar(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       u_long cnt;
+
+       if (fm->cno == 0) {
+               msgq(sp, M_BERR, "Already at the left-hand margin.");
+               return (1);
+       }
+
+       *tm = *fm;
+       cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+       fm->cno = cnt >= tm->cno ? 0 : tm->cno - cnt;
+
+       if (cut(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+               return (1);
+       if (delete(sp, ep, fm, tm, 0))
+               return (1);
+
+       *rp = *fm;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_yank.c b/usr.bin/vi/nvi/v_yank.c
new file mode 100644 (file)
index 0000000..7b2718e
--- /dev/null
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_yank.c   8.11 (Berkeley) 1/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Yank --   [buffer][count]Y
+ *     Yank lines of text into a cut buffer.
+ */
+int
+v_Yank(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+               v_eof(sp, ep, fm);
+               return (1);
+       }
+       if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+           fm, tm, CUT_LINEMODE))
+               return (1);
+
+       sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1;
+       return (0);
+}
+
+/*
+ * v_yank --   [buffer][count]y[count][motion]
+ *     Yank text (or lines of text) into a cut buffer.
+ */
+int
+v_yank(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       if (F_ISSET(vp, VC_LMODE)) {
+               if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
+                       v_eof(sp, ep, fm);
+                       return (1);
+               }
+               if (cut(sp, ep,
+                   NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+                   fm, tm, CUT_LINEMODE))
+                       return (1);
+       } else if (cut(sp, ep,
+           NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
+               return (1);
+
+       /*
+        * !!!
+        * Historic vi moved the cursor to the from MARK if it was before the
+        * current cursor.  This makes no sense.  For example, "yj" moves the
+        * cursor but "yk" does 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.
+        */
+       if (F_ISSET(vp, VC_REVMOVE)) {
+               rp->lno = fm->lno;
+               if (vp->mkp == &vikeys['\'']) {
+                       rp->cno = 0;
+                       (void)nonblank(sp, ep, rp->lno, &rp->cno);
+               } else if (vp->mkp == &vikeys['`'])
+                       rp->cno = fm->cno;
+               else
+                       rp->cno = sp->s_relative(sp, ep, rp->lno);
+       }
+
+       sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1;
+       return (0);
+}
diff --git a/usr.bin/vi/nvi/v_z.c b/usr.bin/vi/nvi/v_z.c
new file mode 100644 (file)
index 0000000..31937ff
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_z.c      8.8 (Berkeley) 12/2/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_z -- [count]z[count][-.+^<CR>]
+ *     Move the screen.
+ */
+int
+v_z(sp, ep, vp, fm, tm, rp)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *vp;
+       MARK *fm, *tm, *rp;
+{
+       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 = fm->lno;
+
+       /* Set return cursor values. */
+       rp->lno = lno;
+       rp->cno = fm->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_rrel(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, rp, sp->t_maxrows - 1, 1))
+                       return (1);
+               if (sp->s_position(sp, ep, rp, 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/nvi/vcmd.c b/usr.bin/vi/nvi/vcmd.c
new file mode 100644 (file)
index 0000000..d7f1caf
--- /dev/null
@@ -0,0 +1,522 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vcmd.c     8.22 (Berkeley) 1/8/94";
+#endif /* not lint */
+
+#include <sys/types.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|V_RCM_SET,
+           "[count]^A",
+           "^A search forward for cursor word"},
+/* 002  ^B */
+       {v_pageup,      V_ABS|V_CNT|V_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|V_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|V_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|V_RCM_SET,
+           "[count]^H",
+           "^H move left by columns"},
+/* 011  ^I */
+       {NULL},
+/* 012  ^J */
+       {v_down,        V_CNT|V_LMODE|V_MOVE|V_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_LMODE|V_MOVE|V_RCM_SETFNB,
+           "[count]^M",
+           "^M move down by lines (to first non-blank)"},
+/* 016  ^N */
+       {v_down,        V_CNT|V_LMODE|V_MOVE|V_RCM,
+           "[count]^N",
+           "^N move down by lines"},
+/* 017  ^O */
+       {NULL},
+/* 020  ^P */
+       {v_up,          V_CNT|V_LMODE|V_MOVE|V_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,      V_RCM_SET,
+           "^T",
+           "^T tag pop"},
+/* 025  ^U */
+       {v_hpageup,     V_ABS|V_CNT|V_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|V_RCM_SET,
+           "^]",
+           "^] tag push cursor word"},
+/* 036  ^^ */
+       {v_switch,      0,
+           "^^",
+           "^^ switch to previous file"},
+/* 037  ^_ */
+       {NULL},
+/* 040 ' ' */
+       {v_right,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]' '",
+           "   <space> move right by columns"},
+/* 041   ! */
+       {v_filter,      V_CNT|V_DOT|V_MOTION|V_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|V_RCM_SET,
+           "[count]#[#+-]",
+           " # number increment/decrement"},
+/* 044   $ */
+       {v_dollar,      V_CNT|V_MOVE|V_RCM_SETLAST,
+           " [count]$",
+           " $ move to last column"},
+/* 045   % */
+       {v_match,       V_ABS|V_MOVE|V_RCM_SET,
+           "%",
+           " % move to match"},
+/* 046   & */
+       {v_again,       0,
+           "&",
+           " & repeat substitution"},
+/* 047   ' */
+       {v_gomark,      V_ABS|V_CHAR|V_LMODE|V_MOVE|V_RCM_SETFNB,
+           "'['a-z]",
+           " ' move to mark (to first non-blank)"},
+/* 050   ( */
+       {v_sentenceb,   V_CNT|V_MOVE|V_RCM_SET,
+           "[count](",
+           " ( move back sentence"},
+/* 051   ) */
+       {v_sentencef,   V_ABS|V_CNT|V_MOVE|V_RCM_SET,
+           "[count])",
+           " ) move forward sentence"},
+/* 052   * */
+       {NULL},
+/* 053   + */
+       {v_down,        V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+           "[count]+",
+           " + move down by lines (to first non-blank)"},
+/* 054   , */
+       {v_chrrepeat,   V_CNT|V_MOVE|V_RCM_SET,
+           "[count],",
+           " , reverse last F, f, T or t search"},
+/* 055   - */
+       {v_up,          V_CNT|V_LMODE|V_MOVE|V_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|V_RCM_SET,
+           "/RE[/ offset]",
+           " / search forward"},
+/* 060   0 */
+       {v_zero,        V_MOVE|V_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|V_RCM_SET,
+           "[count];",
+           " ; repeat last F, f, T or t search"},
+/* 074   < */
+       {v_shiftl,      V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH,
+           "[count]<[count]motion",
+           " < shift lines left to motion"},
+/* 075   = */
+       {NULL},
+/* 076   > */
+       {v_shiftr,      V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH,
+           "[count]>[count]motion",
+           " > shift lines right to motion"},
+/* 077   ? */
+       {v_searchb,     V_ABS|V_MOVE|V_RCM_SET,
+           "?RE[? offset]",
+           " ? search backward"},
+/* 100   @ */
+       {v_at,          V_RBUF|V_RCM_SET,
+           "@buffer",
+           " @ execute buffer"},
+/* 101   A */
+       {v_iA,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]A",
+           " A append to the line"},
+/* 102   B */
+       {v_wordB,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]B",
+           " B move back bigword"},
+/* 103   C */
+       {v_Change,      V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer][count]C",
+           " C change to end-of-line"},
+/* 104   D */
+       {v_Delete,      V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer][count]D",
+           " D delete to end-of-line"},
+/* 105   E */
+       {v_wordE,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]E",
+           " E move to end of bigword"},
+/* 106   F */
+       {v_chF,         V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+           "[count]F character",
+           " F character in line backward search"},
+/* 107   G */
+       {v_lgoto,       V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB,
+           "[count]G",
+           " G move to line"},
+/* 110   H */
+       {v_home,        V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+           "[count]H",
+           " H move to count lines from screen top"},
+/* 111   I */
+       {v_iI,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]I",
+           " I insert at line beginning"},
+/* 112   J */
+       {v_join,        V_CNT|V_DOT|V_RCM_SET,
+           "[count]J",
+           " J join lines"},
+/* 113   K */
+       {NULL},
+/* 114   L */
+       {v_bottom,      V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+           "[count]L",
+           " L move to screen bottom"},
+/* 115   M */
+       {v_middle,      V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB,
+           "M",
+           " M move to screen middle"},
+/* 116   N */
+       {v_searchN,     V_ABS|V_MOVE|V_RCM_SET,
+           "n",
+           " N reverse last search"},
+/* 117   O */
+       {v_iO,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]O",
+           " O insert above line"},
+/* 120   P */
+       {v_Put,         V_CNT|V_DOT|V_OBUF|V_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|V_RCM_SET,
+           "[count]R",
+           " R replace characters"},
+/* 123   S */
+       {v_Subst,       V_CNT|V_DOT|V_LMODE|V_OBUF|V_RCM_SET,
+           "[buffer][count]S",
+           " S substitute for the line(s)"},
+/* 124   T */
+       {v_chT,         V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+           "[count]T character",
+           " T before character in line backward search"},
+/* 125   U */
+       {v_Undo,        V_RCM_SET,
+           "U",
+           " U Restore the current line"},
+/* 126   V */
+       {NULL},
+/* 127   W */
+       {v_wordW,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]W",
+           " W move to next bigword"},
+/* 130   X */
+       {v_Xchar,       V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer][count]X",
+           " X delete character before cursor"},
+/* 131   Y */
+       {v_Yank,        V_CNT|V_LMODE|V_OBUF,
+           "[buffer][count]Y",
+           " Y copy line"},
+/* 132   Z */
+       {v_exit,        0,
+           "ZZ",
+           "ZZ save file and exit"},
+/* 133   [ */
+       {v_sectionb,    V_ABS|V_LMODE|V_MOVE|V_RCM_SET,
+           "[[",
+           "[[ move back section"},
+/* 134   \ */
+       {NULL},
+/* 135   ] */
+       {v_sectionf,    V_ABS|V_LMODE|V_MOVE|V_RCM_SET,
+           "]]",
+           "]] move forward section"},
+/* 136   ^ */
+       /*
+        * DON'T set the V_RCM_SETFNB flag, the function has to do
+        * the work anyway, in case it's a motion component.  DO set
+        * V_RCM_SET, so that any motion that's part of a command is
+        * preserved.
+        */
+       {v_first,       V_CNT|V_MOVE|V_RCM_SET,
+           "^",
+           " ^ move to first non-blank"},
+/* 137   _ */
+       /*
+        * DON'T set the V_RCM_SETFNB flag, the function has to do
+        * the work anyway, in case it's a motion component.  DO set
+        * V_RCM_SET, so that any motion that's part of a command is
+        * preserved.
+        */
+       {v_cfirst,      V_CNT|V_MOVE|V_RCM_SET,
+           "_",
+           " _ move to first non-blank"},
+/* 140   ` */
+       {v_gomark,      V_ABS|V_CHAR|V_MOVE|V_RCM_SET,
+           "`[`a-z]",
+           " ` move to mark"},
+/* 141   a */
+       {v_ia,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]a",
+           " a append after cursor"},
+/* 142   b */
+       {v_wordb,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]b",
+           " b move back word"},
+/* 143   c */
+       {v_change,      V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_C,
+           "[buffer][count]c[count]motion",
+           " c change to motion"},
+/* 144   d */
+       {v_delete,      V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_D,
+           "[buffer][count]d[count]motion",
+           " d delete to motion"},
+/* 145   e */
+       {v_worde,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]e",
+           " e move to end of word"},
+/* 146   f */
+       {v_chf,         V_CHAR|V_CNT|V_MOVE|V_RCM_SET,
+           "[count]f character",
+           " f character in line forward search"},
+/* 147   g */
+       {NULL},
+/* 150   h */
+       {v_left,        V_CNT|V_MOVE|V_RCM_SET,
+           "[count]h",
+           " h move left by columns"},
+/* 151   i */
+       {v_ii,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]i",
+           " i insert before cursor"},
+/* 152   j */
+       {v_down,        V_CNT|V_LMODE|V_MOVE|V_RCM,
+           "[count]j",
+           " j move down by lines"},
+/* 153   k */
+       {v_up,          V_CNT|V_LMODE|V_MOVE|V_RCM,
+           "[count]k",
+           " k move up by lines"},
+/* 154   l */
+       {v_right,       V_CNT|V_MOVE|V_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|V_RCM_SET,
+           "n",
+           " n repeat last search"},
+/* 157   o */
+       {v_io,          V_CNT|V_DOT|V_RCM_SET,
+           "[count]o",
+           " o append after line"},
+/* 160   p */
+       {v_put,         V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer]p",
+           " p insert after cursor from buffer"},
+/* 161   q */
+       {NULL},
+/* 162   r */
+       {v_replace,     V_CNT|V_DOT|V_RCM_SET,
+           "[count]r character",
+           " r replace character"},
+/* 163   s */
+       {v_subst,       V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer][count]s",
+           " s substitute character"},
+/* 164   t */
+       {v_cht,         V_CHAR|V_CNT|V_MOVE|V_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,        V_RCM_SET,
+           "u",
+           " u undo last change"},
+/* 166   v */
+       {NULL},
+/* 167   w */
+       {v_wordw,       V_CNT|V_MOVE|V_RCM_SET,
+           "[count]w",
+           " w move to next word"},
+/* 170   x */
+       {v_xchar,       V_CNT|V_DOT|V_OBUF|V_RCM_SET,
+           "[buffer][count]x",
+           " x delete character"},
+/* 171   y */
+       {v_yank,        V_CNT|V_MOTION|V_OBUF|V_RCM_SET|VC_Y,
+           "[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|V_RCM_SETFNB,
+           "[line]z[window_size][-|.|+|^|<CR>]",
+           " z redraw window"},
+/* 173   { */
+       {v_paragraphb,  V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET,
+           "[count]{",
+           " { move back paragraph"},
+/* 174   | */
+       {v_ncol,        V_ABS|V_CNT|V_MOVE|V_RCM_SET,
+           "[count]|",
+           " | move to column"},
+/* 175   } */
+       {v_paragraphf,  V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET,
+           "[count]}",
+           " } move forward paragraph"},
+/* 176   ~ */
+       {v_ulcase,      V_CNT|V_DOT|V_RCM_SET,
+           "[count]~",
+           " ~ reverse case"},
+};
diff --git a/usr.bin/vi/nvi/vcmd.h b/usr.bin/vi/nvi/vcmd.h
new file mode 100644 (file)
index 0000000..d48fa26
--- /dev/null
@@ -0,0 +1,274 @@
+/*-
+ * 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.23 (Berkeley) 1/8/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  buffer;         /* Buffer. */
+       CHAR_T  character;      /* Character. */
+       u_long  count;          /* Count. */
+       u_long  count2;         /* Second count (only used by z). */
+       int     key;            /* Command key. */
+       VIKEYS const *kp;       /* VIKEYS key. */
+       VIKEYS const *mkp;      /* VIKEYS motion 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, but "d/b" only 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.  This is implemented by setting special flags per command so that
+ * the motion routines know what's really going on.
+ *
+ * Note, the VC_COMMASK flags are set in the vikeys array, and therefore
+ * must have values not used in the set of flags declared in the VIKEYS
+ * structure below.
+ */
+#define        VC_C            0x0001  /* The 'c' command. */
+#define        VC_D            0x0002  /* The 'd' command. */
+#define        VC_SH           0x0004  /* The '>' command. */
+#define        VC_Y            0x0008  /* The 'y' command. */
+#define        VC_COMMASK      0x000f  /* Mask for special flags. */
+
+#define        VC_BUFFER       0x0010  /* Buffer set. */
+#define        VC_C1SET        0x0020  /* Count 1 set. */
+#define        VC_C1RESET      0x0040  /* Reset the C1SET flag for dot commands. */
+#define        VC_C2SET        0x0080  /* Count 2 set. */
+#define        VC_LMODE        0x0100  /* Motion is line oriented. */
+#define        VC_ISDOT        0x0200  /* Command was the dot command. */
+#define        VC_REVMOVE      0x0400  /* Movement was before the cursor. */
+
+       u_int flags;
+
+#define        vp_endzero      keyword /* END ZERO OUT. */
+       char *keyword;          /* Keyword. */
+       size_t kbuflen;         /* Keyword buffer length. */
+} VICMDARG;
+
+/* Vi command structure. */
+struct _vikeys {                       /* Underlying function. */
+       int (*func) __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *));
+
+#define        V_DONTUSE1      0x000001        /* VC_C */
+#define        V_DONTUSE2      0x000002        /* VC_D */
+#define        V_DONTUSE3      0x000004        /* VC_SH */
+#define        V_DONTUSE4      0x000008        /* VC_Y */
+#define        V_ABS           0x000010        /* Absolute movement, set '' mark. */
+#define        V_CHAR          0x000020        /* Character (required, trailing). */
+#define        V_CNT           0x000040        /* Count (optional, leading). */
+#define        V_DOT           0x000080        /* On success, sets dot command. */
+#define        V_KEYNUM        0x000100        /* Cursor referenced number. */
+#define        V_KEYW          0x000200        /* Cursor referenced word. */
+#define        V_LMODE         0x000400        /* Motion is line oriented. */
+#define        V_MOTION        0x000800        /* Motion (required, trailing). */
+#define        V_MOVE          0x001000        /* Command defines movement. */
+#define        V_OBUF          0x002000        /* Buffer (optional, leading). */
+#define        V_RBUF          0x004000        /* Buffer (required, trailing). */
+#define        V_RCM           0x008000        /* Use relative cursor movment (RCM). */
+#define        V_RCM_SET       0x010000        /* RCM: set to current position. */
+#define        V_RCM_SETFNB    0x020000        /* RCM: set to first non-blank (FNB). */
+#define        V_RCM_SETLAST   0x040000        /* RCM: set to last character. */
+#define        V_RCM_SETLFNB   0x080000        /* RCM: set to FNB if line moved. */
+#define        V_RCM_SETNNB    0x100000        /* RCM: set to next non-blank. */
+       u_long 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 "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 *));
+
+/* 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. */
+} 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 *));
+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 *));
+int    vi __P((SCR *, EXF *));
+
+#define        VIPROTO(type, name)                                             \
+       type name __P((SCR *, EXF *,    VICMDARG *, MARK *, MARK *, MARK *))
+
+VIPROTO(int, v_again);
+VIPROTO(int, v_at);
+VIPROTO(int, v_bottom);
+VIPROTO(int, v_cfirst);
+VIPROTO(int, v_Change);
+VIPROTO(int, v_change);
+VIPROTO(int, v_chF);
+VIPROTO(int, v_chf);
+VIPROTO(int, v_chrepeat);
+VIPROTO(int, v_chrrepeat);
+VIPROTO(int, v_chT);
+VIPROTO(int, v_cht);
+VIPROTO(int, v_cr);
+VIPROTO(int, v_Delete);
+VIPROTO(int, v_delete);
+VIPROTO(int, v_dollar);
+VIPROTO(int, v_down);
+VIPROTO(int, v_ex);
+VIPROTO(int, v_exit);
+VIPROTO(int, v_exmode);
+VIPROTO(int, v_filter);
+VIPROTO(int, v_first);
+VIPROTO(int, v_gomark);
+VIPROTO(int, v_home);
+VIPROTO(int, v_hpagedown);
+VIPROTO(int, v_hpageup);
+VIPROTO(int, v_iA);
+VIPROTO(int, v_ia);
+VIPROTO(int, v_iI);
+VIPROTO(int, v_ii);
+VIPROTO(int, v_increment);
+VIPROTO(int, v_iO);
+VIPROTO(int, v_io);
+VIPROTO(int, v_join);
+VIPROTO(int, v_left);
+VIPROTO(int, v_lgoto);
+VIPROTO(int, v_linedown);
+VIPROTO(int, v_lineup);
+VIPROTO(int, v_mark);
+VIPROTO(int, v_match);
+VIPROTO(int, v_middle);
+VIPROTO(int, v_ncol);
+VIPROTO(int, v_pagedown);
+VIPROTO(int, v_pageup);
+VIPROTO(int, v_paragraphb);
+VIPROTO(int, v_paragraphf);
+VIPROTO(int, v_Put);
+VIPROTO(int, v_put);
+VIPROTO(int, v_redraw);
+VIPROTO(int, v_Replace);
+VIPROTO(int, v_replace);
+VIPROTO(int, v_right);
+VIPROTO(int, v_screen);
+VIPROTO(int, v_searchb);
+VIPROTO(int, v_searchf);
+VIPROTO(int, v_searchN);
+VIPROTO(int, v_searchn);
+VIPROTO(int, v_searchw);
+VIPROTO(int, v_sectionb);
+VIPROTO(int, v_sectionf);
+VIPROTO(int, v_sentenceb);
+VIPROTO(int, v_sentencef);
+VIPROTO(int, v_shiftl);
+VIPROTO(int, v_shiftr);
+VIPROTO(int, v_status);
+VIPROTO(int, v_stop);
+VIPROTO(int, v_Subst);
+VIPROTO(int, v_subst);
+VIPROTO(int, v_switch);
+VIPROTO(int, v_tagpop);
+VIPROTO(int, v_tagpush);
+VIPROTO(int, v_ulcase);
+VIPROTO(int, v_Undo);
+VIPROTO(int, v_undo);
+VIPROTO(int, v_up);
+VIPROTO(int, v_wordB);
+VIPROTO(int, v_wordb);
+VIPROTO(int, v_wordE);
+VIPROTO(int, v_worde);
+VIPROTO(int, v_wordW);
+VIPROTO(int, v_wordw);
+VIPROTO(int, v_Xchar);
+VIPROTO(int, v_xchar);
+VIPROTO(int, v_Yank);
+VIPROTO(int, v_yank);
+VIPROTO(int, v_z);
+VIPROTO(int, v_zero);
diff --git a/usr.bin/vi/nvi/vi.c b/usr.bin/vi/nvi/vi.c
new file mode 100644 (file)
index 0000000..f969d78
--- /dev/null
@@ -0,0 +1,780 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vi.c       8.45 (Berkeley) 1/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.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 *, MARK *, MARK *));
+
+/*
+ * 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, fm, tm, m;
+       VICMDARG cmd, *vp;
+       u_int flags, saved_mode;
+       int comcount, eval;
+
+       /* Start vi. */
+       if (v_init(sp, ep))
+               return (1);
+
+       /* Paint the screen. */
+       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;
+
+               /* Get any associated keyword. */
+               flags = vp->kp->flags;
+               if (LF_ISSET(V_KEYNUM | V_KEYW) &&
+                   getkeyword(sp, ep, vp, flags))
+                       goto err;
+
+               /* If a non-relative movement, copy the future absolute mark. */
+               if (LF_ISSET(V_ABS)) {
+                       abs.lno = sp->lno;
+                       abs.cno = sp->cno;
+               }
+
+               /*
+                * Do any required motion; getmotion sets the from MARK
+                * and the line mode flag.
+                */
+               if (LF_ISSET(V_MOTION)) {
+                       if (getmotion(sp, ep, DOTMOTION, vp, &fm, &tm))
+                               goto err;
+               } else {
+                       /*
+                        * Set everything to the current cursor position.
+                        * Line commands (ex: Y) default to the current line.
+                        */
+                       tm.lno = fm.lno = sp->lno;
+                       tm.cno = fm.cno = sp->cno;
+
+                       /*
+                        * Set line mode flag, for example, "yy".
+                        *
+                        * If a count is set, we set the to MARK here relative
+                        * to the cursor/from MARK.  This is done for commands
+                        * that take both counts and motions, i.e. "4yy" and
+                        * "y%" -- there's no way the command can known which
+                        * the user did, so we have to do it here.  There are
+                        * other commands that are line mode commands and take
+                        * counts ("#G", "#H") and for which this calculation
+                        * is either meaningless or wrong.  Each command must
+                        * do its own validity checking of the value.
+                        */
+                       if (F_ISSET(vp->kp, V_LMODE)) {
+                               F_SET(vp, VC_LMODE);
+                               if (F_ISSET(vp, VC_C1SET)) {
+                                       tm.lno = sp->lno + vp->count - 1;
+                                       tm.cno = sp->cno;
+                               }
+                       }
+               }
+
+               /* Increment the command count. */
+               ++sp->ccnt;
+
+               /*
+                * Call the function.  Set the return cursor to the current
+                * cursor position first -- the underlying routines don't
+                * bother to do the work if it doesn't move.
+                */
+               m.lno = sp->lno;
+               m.cno = sp->cno;
+               saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+               if ((vp->kp->func)(sp, ep, vp, &fm, &tm, &m))
+                       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 (LF_ISSET(V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1))
+                       goto err;
+
+               /* Set the dot command structure. */
+               if (LF_ISSET(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 V_RCM commands are moths to the V_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 V_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 (LF_ISSET(V_RCM | V_RCM_SETFNB |
+                   V_RCM_SETLAST | V_RCM_SETLFNB | V_RCM_SETNNB)) {
+               case 0:
+                       break;
+               case V_RCM:
+                       m.cno = sp->s_relative(sp, ep, m.lno);
+                       break;
+               case V_RCM_SETLAST:
+                       sp->rcmflags = RCM_LAST;
+                       break;
+               case V_RCM_SETLFNB:
+                       if (fm.lno != m.lno) {
+                               if (nonblank(sp, ep, m.lno, &m.cno))
+                                       goto err;
+                               sp->rcmflags = RCM_FNB;
+                       }
+                       break;
+               case V_RCM_SETFNB:
+                       m.cno = 0;
+                       /* FALLTHROUGH */
+               case V_RCM_SETNNB:
+                       if (nonblank(sp, ep, m.lno, &m.cno))
+                               goto err;
+                       sp->rcmflags = RCM_FNB;
+                       break;
+               default:
+                       abort();
+               }
+                       
+               /* Update the cursor. */
+               sp->lno = m.lno;
+               sp->cno = m.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 (LF_ISSET(V_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)
+                               goto usage;
+               }
+               /* 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.
+        */
+       else if (ismotion->key != key && !LF_ISSET(V_MOVE)) {
+usage:         msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ?
+                   vikeys[ismotion->key].usage : kp->usage);
+               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, fm, tm)
+       SCR *sp;
+       EXF *ep;
+       VICMDARG *dm, *vp;
+       MARK *fm, *tm;
+{
+       MARK m;
+       VICMDARG motion;
+       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, VC_LMODE);
+
+               /*
+                * Set the end of the command; the column is after the line.
+                *
+                * If the current line is missing, i.e. the file is empty,
+                * historic vi permitted a "cc" or "!!" command to insert
+                * text.
+                */
+               tm->lno = sp->lno + motion.count - 1;
+               if (file_gline(sp, ep, tm->lno, &tm->cno) == NULL) {
+                       if (tm->lno != 1 || vp->key != 'c' && vp->key != '!') {
+                               m.lno = sp->lno;
+                               m.cno = sp->cno;
+                               v_eof(sp, ep, &m);
+                               return (1);
+                       }
+                       tm->cno = 0;
+               }
+
+               /* Set the origin of the command. */
+               fm->lno = sp->lno;
+               fm->cno = 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);
+
+               /*
+                * Everything starts at the current position.  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.
+                */
+               fm->lno = tm->lno = sp->lno;
+               fm->cno = tm->cno = sp->cno;
+               if ((motion.kp->func)(sp, ep, &motion, fm, NULL, tm))
+                       return (1);
+
+               /*
+                * If the underlying motion was a line motion, set the flag
+                * in the command structure.  Underlying commands can also
+                * flag the movement as a line motion (see v_sentence).
+                */
+               if (F_ISSET(motion.kp, V_LMODE) || F_ISSET(&motion, VC_LMODE))
+                       F_SET(vp, VC_LMODE);
+
+               /*
+                * If the motion is in the reverse direction, switch the from
+                * and to MARK's so that it's always in a forward direction.
+                * Because the motion is always from the from MARK to, but not
+                * including, the to MARK, the function may have modified the
+                * from MARK, so that it gets the one-past-the-place semantics
+                * we use; see v_match() for an example.  Also set a flag so
+                * that the underlying function knows that we did this; v_yank,
+                * for example, has to know so it gets the return cursor right.
+                */
+               if (tm->lno < fm->lno ||
+                   tm->lno == fm->lno && tm->cno < fm->cno) {
+                       m = *fm;
+                       *fm = *tm;
+                       *tm = m;
+                       F_SET(vp, VC_REVMOVE);
+               }
+       }
+
+       /*
+        * 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;
+       }
+
+       /* Let the underlying function know what motion command was used. */
+       vp->mkp = motion.kp;
+       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:
+               F_SET(sp, S_EXIT_FORCE);
+               /* FALLTHROUGH */
+       case INP_ERR:
+               return (1);
+       }
+       return (ikeyp->value == K_ESCAPE);
+}
index 3a751b5..0da531d 100644 (file)
@@ -44,7 +44,7 @@ static char sccsid[] = "@(#)svi_util.c        8.26 (Berkeley) 12/9/93";
 #include <unistd.h>
 
 #include "vi.h"
 #include <unistd.h>
 
 #include "vi.h"
-#include "../vi/vcmd.h"
+#include "vcmd.h"
 #include "excmd.h"
 #include "svi_screen.h"
 
 #include "excmd.h"
 #include "svi_screen.h"