summaryrefslogtreecommitdiffstats
path: root/usr.bin/vi/ex
diff options
context:
space:
mode:
authorsef <sef@FreeBSD.org>1994-08-16 23:36:45 +0000
committersef <sef@FreeBSD.org>1994-08-16 23:36:45 +0000
commita6a4f07746b676691400c41fa4f116251050ba7f (patch)
treef7f1cc01b7d9f04f19bfc16853d3e1be207a8ffc /usr.bin/vi/ex
downloadFreeBSD-src-a6a4f07746b676691400c41fa4f116251050ba7f.zip
FreeBSD-src-a6a4f07746b676691400c41fa4f116251050ba7f.tar.gz
Latest public release of nvi, from Keith Bostic. I hope I got this
right ;). Reviewed by: Sean Eric Fagan
Diffstat (limited to 'usr.bin/vi/ex')
-rw-r--r--usr.bin/vi/ex/ex.c1866
-rw-r--r--usr.bin/vi/ex/ex_abbrev.c129
-rw-r--r--usr.bin/vi/ex/ex_append.c220
-rw-r--r--usr.bin/vi/ex/ex_args.c263
-rw-r--r--usr.bin/vi/ex/ex_argv.c609
-rw-r--r--usr.bin/vi/ex/ex_at.c118
-rw-r--r--usr.bin/vi/ex/ex_bang.c242
-rw-r--r--usr.bin/vi/ex/ex_cd.c223
-rw-r--r--usr.bin/vi/ex/ex_delete.c92
-rw-r--r--usr.bin/vi/ex/ex_digraph.c324
-rw-r--r--usr.bin/vi/ex/ex_display.c169
-rw-r--r--usr.bin/vi/ex/ex_edit.c122
-rw-r--r--usr.bin/vi/ex/ex_equal.c86
-rw-r--r--usr.bin/vi/ex/ex_exit.c79
-rw-r--r--usr.bin/vi/ex/ex_file.c103
-rw-r--r--usr.bin/vi/ex/ex_global.c400
-rw-r--r--usr.bin/vi/ex/ex_init.c202
-rw-r--r--usr.bin/vi/ex/ex_join.c200
-rw-r--r--usr.bin/vi/ex/ex_map.c160
-rw-r--r--usr.bin/vi/ex/ex_mark.c66
-rw-r--r--usr.bin/vi/ex/ex_mkexrc.c130
-rw-r--r--usr.bin/vi/ex/ex_move.c222
-rw-r--r--usr.bin/vi/ex/ex_open.c75
-rw-r--r--usr.bin/vi/ex/ex_preserve.c128
-rw-r--r--usr.bin/vi/ex/ex_print.c212
-rw-r--r--usr.bin/vi/ex/ex_put.c78
-rw-r--r--usr.bin/vi/ex/ex_read.c300
-rw-r--r--usr.bin/vi/ex/ex_screen.c155
-rw-r--r--usr.bin/vi/ex/ex_script.c582
-rw-r--r--usr.bin/vi/ex/ex_set.c70
-rw-r--r--usr.bin/vi/ex/ex_shell.c150
-rw-r--r--usr.bin/vi/ex/ex_shift.c204
-rw-r--r--usr.bin/vi/ex/ex_source.c66
-rw-r--r--usr.bin/vi/ex/ex_stop.c76
-rw-r--r--usr.bin/vi/ex/ex_subst.c1001
-rw-r--r--usr.bin/vi/ex/ex_tag.c905
-rw-r--r--usr.bin/vi/ex/ex_undo.c103
-rw-r--r--usr.bin/vi/ex/ex_usage.c197
-rw-r--r--usr.bin/vi/ex/ex_util.c189
-rw-r--r--usr.bin/vi/ex/ex_version.c71
-rw-r--r--usr.bin/vi/ex/ex_visual.c137
-rw-r--r--usr.bin/vi/ex/ex_write.c327
-rw-r--r--usr.bin/vi/ex/ex_yank.c69
-rw-r--r--usr.bin/vi/ex/ex_z.c180
-rw-r--r--usr.bin/vi/ex/excmd.awk6
-rw-r--r--usr.bin/vi/ex/excmd.c458
-rw-r--r--usr.bin/vi/ex/excmd.h.stub285
-rw-r--r--usr.bin/vi/ex/filter.c414
-rw-r--r--usr.bin/vi/ex/script.h45
-rw-r--r--usr.bin/vi/ex/tag.h58
50 files changed, 12566 insertions, 0 deletions
diff --git a/usr.bin/vi/ex/ex.c b/usr.bin/vi/ex/ex.c
new file mode 100644
index 0000000..c0455a1
--- /dev/null
+++ b/usr.bin/vi/ex/ex.c
@@ -0,0 +1,1866 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex.c 8.155 (Berkeley) 8/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static void badlno __P((SCR *, recno_t));
+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 *));
+
+/*
+ * ex --
+ * Read an ex command and execute it.
+ */
+int
+ex(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ enum input irval;
+ TEXT *tp;
+ u_int flags, saved_mode;
+ int eval;
+
+ 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_STDIN_TTY)) {
+ sp->if_lno = 1;
+ sp->if_name = strdup("input");
+ }
+
+ /*
+ * !!!
+ * Historically, the beautify option applies to ex command input read
+ * from a file. In addition, the first time a ^H was discarded from
+ * the input, a message "^H discarded" was displayed. We don't bother.
+ */
+ LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR | TXT_EXSUSPEND);
+
+ for (eval = 0;; ++sp->if_lno) {
+ /* Set the flags that the user can change. */
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+ else
+ LF_CLR(TXT_BEAUTIFY);
+ if (O_ISSET(sp, O_PROMPT))
+ LF_SET(TXT_PROMPT);
+ else
+ LF_CLR(TXT_PROMPT);
+
+ /*
+ * Get the next command. Interrupt flag manipulation is
+ * safe because ex_icmd clears them all.
+ */
+ CLR_INTERRUPT(sp);
+ F_SET(sp, S_INTERRUPTIBLE);
+ irval = sp->s_get(sp, ep, sp->tiqp, ':', flags);
+ if (INTERRUPTED(sp)) {
+ (void)fputc('\n', stdout);
+ (void)fflush(stdout);
+ goto refresh;
+ }
+ switch (irval) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ case INP_ERR:
+ F_SET(sp, S_EXIT_FORCE);
+ /* FALLTHROUGH */
+ case INP_INTR:
+ goto ret;
+ }
+
+ /*
+ * If the user entered a carriage return, send ex_cmd()
+ * a separator -- it discards single newlines.
+ */
+ tp = sp->tiqp->cqh_first;
+ if (tp->len == 0) {
+ tp->len = 1;
+ tp->lb[0] = ' ';
+ }
+
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ if (ex_icmd(sp, ep,
+ tp->lb, tp->len, 1) && !F_ISSET(sp->gp, G_STDIN_TTY))
+ F_SET(sp, S_EXIT_FORCE);
+ (void)msg_rpt(sp, 0);
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+refresh: 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, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *filename;
+ int needsep;
+{
+ 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,
+ * but we don't care if we can't get space.
+ */
+ sp->if_lno = 1;
+ sp->if_name = strdup(filename);
+ F_SET(sp, S_VLITONLY);
+ rval = ex_icmd(sp, ep, bp, len, needsep);
+ F_CLR(sp, S_VLITONLY);
+ free(sp->if_name);
+ 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, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t len;
+ int needsep;
+{
+ /*
+ * 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.
+ *
+ * !!!
+ * Previous versions of nvi cleared mapped characters on error. This
+ * feature was removed when users complained that it wasn't historic
+ * practice.
+ */
+ CLR_INTERRUPT(sp);
+ return (ex_cmd(sp, ep, cmd, len, needsep));
+}
+
+/* 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"};
+
+/* Special command structure for :d[flags]. */
+static EXCMDLIST const cmd_del2 =
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "1bca1",
+ "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
+ "delete lines from the file"};
+
+/*
+ * ex_cmd --
+ * Parse and execute a string containing ex commands.
+ */
+int
+ex_cmd(sp, ep, cmd, cmdlen, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t cmdlen;
+ int needsep;
+{
+ enum { NOTSET, NEEDSEP_N, NEEDSEP_NR, NONE } sep;
+ 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 blank, ch, cnt, delim, flags, namelen, nl;
+ int optnum, uselastcmd, tmp, vi_address;
+ char *arg1, *save_cmd, *p, *s, *t;
+
+ /* Init. */
+ nl = 0;
+ sep = needsep ? NOTSET : NONE;
+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 (INTERRUPTED(sp))
+ return (0);
+
+ /* Skip <blank>s, empty lines. */
+ for (blank = 0; cmdlen > 0; ++cmd, --cmdlen)
+ if ((ch = *cmd) == '\n')
+ ++sp->if_lno;
+ else if (isblank(ch))
+ blank = 1;
+ else
+ break;
+
+ /*
+ * !!!
+ * Permit extra colons at the start of the line. Historically,
+ * ex/vi allowed a single extra one. It's simpler not to count.
+ * The stripping is done here because, historically, any command
+ * could have preceding colons, e.g. ":g/pattern/:p" worked.
+ */
+ if (cmdlen != 0 && ch == ':') {
+ if (sep == NOTSET)
+ sep = NEEDSEP_N;
+ while (--cmdlen > 0 && (ch = *++cmd) == ':');
+ }
+
+ /*
+ * Command lines that start with a double-quote are comments.
+ *
+ * !!!
+ * Historically, there was no escape or delimiter for a comment,
+ * e.g. :"foo|set was a single comment and nothing was output.
+ * Since nvi permits users to escape <newline> characters into
+ * command lines, we have to check for that case.
+ */
+ if (cmdlen != 0 && ch == '"') {
+ while (--cmdlen > 0 && *++cmd != '\n');
+ if (*cmd == '\n') {
+ nl = 1;
+ ++cmd;
+ --cmdlen;
+ }
+ goto loop;
+ }
+
+ /* Skip whitespace. */
+ for (; cmdlen > 0; ++cmd, --cmdlen) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * The last point at which an empty line can mean do nothing.
+ *
+ * !!!
+ * Historically, in ex mode, lines containing only <blank> characters
+ * were the same as a single <carriage-return>, i.e. a default command.
+ * In vi mode, they were ignored.
+ *
+ * In .exrc files this was a serious annoyance, as vi kept trying to
+ * treat them as print commands. We ignore backward compatibility in
+ * this case, and discard lines containing only <blank> characters from
+ * .exrc files.
+ */
+ if (cmdlen == 0 && (!IN_EX_MODE(sp) || ep == NULL || !blank))
+ 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;
+
+ /*
+ * Check to see if this is a command for which we may want to output
+ * a \r separator instead of a \n. (The command :1<CR> puts out a \n,
+ * but the command :<CR> puts out a \r.) If the line is empty except
+ * for <blank>s, <carriage-return> or <eof>, we'll probably want to
+ * output \r. I don't think there's any way to get <blank> characters
+ * *after* the command character, but this is the ex parser, and I've
+ * been wrong before.
+ */
+ if (sep == NOTSET)
+ sep = cmdlen == 0 || cmdlen == 1 && cmd[0] == '\004' ?
+ NEEDSEP_NR : NEEDSEP_N;
+
+ /* 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.
+ *
+ * !!!
+ * Historically, lines with multiple adjacent (or <blank> separated)
+ * command separators were very strange. For example, the command
+ * |||<carriage-return>, when the cursor was on line 1, displayed
+ * lines 2, 3 and 5 of the file. In addition, the command " | "
+ * would only display the line after the next line, instead of the
+ * next two lines. No ideas why. It worked reasonably when executed
+ * from vi mode, and displayed lines 2, 3, and 4, so we do a default
+ * command for each separator.
+ */
+#define SINGLE_CHAR_COMMANDS "\004!#&*<=>@~"
+ 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;
+ }
+ }
+
+ /*
+ * !!!
+ * Historic vi permitted flags to immediately follow any
+ * subset of the 'delete' command, but then did not permit
+ * further arguments (flag, buffer, count). Make it work.
+ * Permit further arguments for the few shreds of dignity
+ * it offers.
+ *
+ * !!!
+ * Note, adding commands that start with 'd', and match
+ * "delete" up to a l, p, +, - or # character can break
+ * this code.
+ */
+ if (p[0] == 'd') {
+ for (s = p,
+ t = cmds[C_DELETE].name; *s == *t; ++s, ++t);
+ if (s[0] == 'l' || s[0] == 'p' ||
+ s[0] == '+' || s[0] == '-' || s[0] == '#') {
+ len = (cmd - p) - (s - p);
+ cmd -= len;
+ cmdlen += len;
+ cp = &cmd_del2;
+ goto skip;
+ }
+ }
+
+ /*
+ * 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
+ * the command "sgc" work.
+ */
+ if ((cp = ex_comm_search(p, namelen)) == NULL)
+ switch (p[0]) {
+ case 's':
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmd_subagain;
+ break;
+ case 'k':
+ if (p[1] && !p[2]) {
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmds[C_K];
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ msgq(sp, M_ERR,
+ "The %.*s command is unknown", namelen, p);
+ goto err;
+ }
+
+ /* Some commands are either not implemented or turned off. */
+skip: 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];
+
+ /* Set the format style flags for the next command. */
+ if (cp == &cmds[C_HASH])
+ exp->fdef = E_F_HASH;
+ else if (cp == &cmds[C_LIST])
+ exp->fdef = E_F_LIST;
+ else if (cp == &cmds[C_PRINT])
+ exp->fdef = E_F_PRINT;
+ uselastcmd = 0;
+ } else {
+ /* Print is the default command. */
+ cp = &cmds[C_PRINT];
+
+ /* Set the saved format flags. */
+ F_SET(&exc, exp->fdef);
+
+ /*
+ * !!!
+ * If no address was specified, and it's not a global command,
+ * we up the address by one. (I have not an idea why global
+ * commands are exempted, but it's (ahem) historic practice.
+ */
+ if (exc.addrcnt == 0 && !F_ISSET(sp, S_GLOBAL)) {
+ exc.addrcnt = 1;
+ exc.addr1.lno = sp->lno + 1;
+ exc.addr1.cno = sp->cno;
+ }
+
+ uselastcmd = 1;
+ }
+
+ /*
+ * !!!
+ * Historically, the number option applied to both ex and vi. One
+ * strangeness was that ex didn't switch display formats until a
+ * command was entered, e.g. <CR>'s after the set didn't change to
+ * the new format, but :1p would.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ optnum = 1;
+ F_SET(&exc, E_F_HASH);
+ } else
+ optnum = 0;
+
+ /* 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. We
+ * don't have a "reading an rc" bit, because we want the commands
+ * to work when the user source's the rc file later. 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 have already 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, next and visual in vi mode commands all 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,
+ * next, vi visual, and substitute commands didn't 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(s). Then, we
+ * do normal command processing on whatever is left. Barf-O-Rama.
+ */
+ arg1_len = 0;
+ save_cmd = cmd;
+ if (cp == &cmds[C_EDIT] || cp == &cmds[C_EX] ||
+ cp == &cmds[C_NEXT] || cp == &cmds[C_VISUAL_VI]) {
+ /*
+ * Move to the next non-whitespace character. A '!'
+ * immediately following the command is eaten as a
+ * force flag.
+ */
+ if (cmdlen > 0 && *cmd == '!') {
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_FORCE);
+
+ /* Reset, don't reparse. */
+ save_cmd = cmd;
+ }
+ for (tmp = 0; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(*cmd))
+ 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 && *cmd == '+') {
+ ++cmd;
+ --cmdlen;
+ for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && 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.
+ */
+ vi_address = cmdlen != 0 && cmd[0] != '\n';
+ for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = cmd[0];
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ tmp = cmd[1];
+ if (tmp == '\n' || tmp == '|') {
+ if (tmp == '\n')
+ ++sp->if_lno;
+ --cmdlen;
+ ++cmd;
+ ++cnt;
+ ch = tmp;
+ }
+ } 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 = CH_LITERAL;
+
+ /*
+ * Set the default addresses. It's an error to specify an address for
+ * a command that doesn't take them. If two addresses are specified
+ * for a command that only takes one, lose the first one. Two special
+ * cases here, some commands take 0 or 2 addresses. For most of them
+ * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one
+ * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
+ *
+ * Also, if the file is empty, some commands want to use an address of
+ * 0, i.e. the entire file is 0 to 0, and the default first address is
+ * 0. Otherwise, an entire file is 1 to N and the default line is 1.
+ * Note, we also add the E_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;
+ }
+
+ /*
+ * !!!
+ * The ^D scroll command historically scrolled the value of the scroll
+ * option or to EOF. It was an error if the cursor was already at EOF.
+ * (Leading addresses were permitted, but were then ignored.)
+ */
+ if (cp == &cmds[C_SCROLL]) {
+ exc.addrcnt = 2;
+ exc.addr1.lno = sp->lno + 1;
+ exc.addr2.lno = sp->lno + O_VAL(sp, O_SCROLL);
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno != 0 && lno > sp->lno && exc.addr2.lno > lno)
+ exc.addr2.lno = lno;
+ }
+
+ flagoff = 0;
+ for (p = cp->syntax; *p != '\0'; ++p) {
+ /*
+ * The force flag is sensitive to leading whitespace, i.e.
+ * "next !" is different from "next!". Handle it before
+ * skipping leading <blank>s.
+ */
+ if (*p == '!') {
+ if (cmdlen > 0 && *cmd == '!') {
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_FORCE);
+ }
+ continue;
+ }
+
+ /* Skip leading <blank>s. */
+ for (; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(*cmd))
+ 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. Since '!' can precede commands that do their
+ * own parsing, we have to have already handled it.
+ */
+ if (cmdlen == 0 && *p != 'S' && *p != 's')
+ break;
+
+ switch (*p) {
+ 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 '#':
+ optnum = 0;
+ F_SET(&exc, E_F_HASH);
+ exp->fdef |= E_F_HASH;
+ break;
+ case 'l':
+ F_SET(&exc, E_F_LIST);
+ exp->fdef |= E_F_LIST;
+ break;
+ case 'p':
+ F_SET(&exc, E_F_PRINT);
+ exp->fdef |= 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 */
+ /*
+ * !!!
+ * Historically, "d #" was a delete with a flag, not a
+ * delete into the '#' buffer. If the current command
+ * permits a flag, don't use one as a buffer. However,
+ * the 'l' and 'p' flags were legal buffer names in the
+ * historic ex, and were used as buffers, not flags.
+ */
+ if ((cmd[0] == '+' || cmd[0] == '-' || cmd[0] == '#') &&
+ strchr(p, '1') != NULL)
+ break;
+ /*
+ * !!!
+ * Digits can't be buffer names in ex commands, or the
+ * command "d2" would be a delete into buffer '2', and
+ * not a two-line deletion.
+ */
+ if (!isdigit(cmd[0])) {
+ exc.buffer = *cmd;
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_BUFFER);
+ }
+ break;
+ case 'c': /* count [01+a] */
+ ++p;
+ /* Validate any signed value. */
+ if (!isdigit(*cmd) &&
+ (*p != '+' || (*cmd != '+' && *cmd != '-')))
+ break;
+ /* If a signed value, set appropriate flags. */
+ if (*cmd == '-')
+ F_SET(&exc, E_COUNT_NEG);
+ else if (*cmd == '+')
+ F_SET(&exc, E_COUNT_POS);
+/* 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;
+ }
+ /* The line must exist for these commands. */
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (cur.lno > lno) {
+ badlno(sp, lno);
+ 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) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && 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) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && 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 {
+ badlno(sp, lno);
+ 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 +100 nonexistent_file" to work.
+ */
+ if (num == 0 && (IN_EX_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) {
+ badlno(sp, lno);
+ goto err;
+ }
+ break;
+ }
+
+ /*
+ * If doing a default command and there's nothing left on the line,
+ * vi just moves to the line. For example, ":3" and ":'a,'b" just
+ * move to line 3 and line 'b, respectively, but ":3|" prints line 3.
+ *
+ * !!!
+ * This is done before the absolute mark gets set; historically,
+ * "/a/,/b/" did NOT set vi's absolute mark, but "/a/,/b/d" did.
+ */
+ if (IN_VI_MODE(sp) && uselastcmd && vi_address == 0) {
+ 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;
+ }
+
+ /*
+ * Set the absolute mark -- we have to set it for vi here, in case
+ * it's a compound command, e.g. ":5p|6" should set the absolute
+ * mark for vi.
+ */
+ if (F_ISSET(exp, EX_ABSMARK)) {
+ cur.lno = sp->lno;
+ cur.cno = sp->cno;
+ F_CLR(exp, EX_ABSMARK);
+ if (mark_set(sp, ep, ABSMARK1, &cur, 1))
+ goto err;
+ }
+
+ /* 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_EX_MODE(sp))
+ ++sp->ccnt;
+
+ /*
+ * If file state available, 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);
+
+ /*
+ * !!!
+ * There are two special commands for the purposes of this code: the
+ * default command (<carriage-return>) or the scrolling commands (^D
+ * and <EOF>) as the first non-<blank> characters in the line.
+ *
+ * If this is the first command in the command line, we received the
+ * command from the ex command loop and we're talking to a tty, and
+ * and there's nothing else on the command line, and it's one of the
+ * special commands, we erase the prompt character with a '\r'. Else,
+ * we put out a newline character to separate the command from the
+ * output from the command. It's OK if vi calls us -- we won't be in
+ * ex mode so we'll do nothing.
+ *
+ * !!!
+ * Historically, ex only put out a \r, so, if the displayed line was
+ * only a single character long, and <eof> was represented as ^D, the
+ * output wouldn't overwrite the user's input. Sex currently doesn't
+ * display the <eof> character if it's going to be the scroll command,
+ * i.e. if it's the first non-<blank> character in the line. If sex
+ * is changed to run in cooked mode, i.e. <eof> is displayed, this code
+ * will have to overwrite it. We also don't treat lines with extra
+ * prompt characters as empty -- it's not worth the effort since we'd
+ * have to overwrite some indeterminate number of columns with spaces
+ * to clean up. For now, put out enough spaces to overwrite the prompt.
+ */
+ if (sep != NONE) {
+ if (ep != NULL &&
+ IN_EX_MODE(sp) && F_ISSET(sp->gp, G_STDIN_TTY))
+ if (sep == NEEDSEP_NR &&
+ (uselastcmd || cp == &cmds[C_SCROLL])) {
+ (void)putchar('\r');
+ for (len = KEY_LEN(sp, PROMPTCHAR); len--;)
+ (void)putchar(' ');
+ (void)putchar('\r');
+ } else
+ (void)putchar('\n');
+ sep = NONE;
+ }
+
+ /* 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. 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
+ * part of the current ex command that hasn't been executed
+ * yet. For example:
+ *
+ * :edit +25 file.c|s/abc/ABC/|1
+ *
+ * !!!
+ * The historic vi just hung, of course; nvi handles it by
+ * pushing the keys onto the tty queue. Since the commands
+ * are intended as ex commands, add additional characters
+ * to make it all work if we're switching modes to vi. Also,
+ * + commands were oriented to the last line in the file,
+ * historically, make the cursor start out there.
+ *
+ * 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 == 0 && save_cmdlen == 0)
+ return (0);
+ if (term_push(sp, "\n", 1, 0))
+ goto err;
+ if (save_cmdlen != 0)
+ if (term_push(sp, save_cmd, save_cmdlen, 0))
+ goto err;
+ if (arg1 != NULL) {
+ if (IN_VI_MODE(sp) && save_cmdlen != 0 &&
+ term_push(sp, "|", 1, 0))
+ goto err;
+ if (term_push(sp, arg1, arg1_len, 0))
+ goto err;
+ if (file_lline(sp, ep, &sp->frp->lno))
+ goto err;
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+ if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0))
+ goto err;
+ return (0);
+ }
+
+ /*
+ * Integrate any offset parsed by the underlying command, and make
+ * sure the referenced line exists.
+ *
+ * XXX
+ * May not match historic practice (I've never been able to completely
+ * figure it out.) For example, the '=' command from vi mode often
+ * got the offset wrong, and complained it was too large, but didn't
+ * seem to have a problem with the cursor. If anyone complains, ask
+ * them how it's supposed to work, they probably know.
+ */
+ if (ep != NULL && (flagoff += exc.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 the command was successful and we're in ex command mode, we
+ * may want to display a line. Make sure there's a line to display.
+ */
+ if (ep != NULL &&
+ IN_EX_MODE(sp) && !F_ISSET(sp, S_GLOBAL) && sp->lno != 0) {
+ /*
+ * The print commands have already handled the `print' flags.
+ * If so, clear them.
+ */
+ if (LF_ISSET(E_F_PRCLEAR))
+ F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);
+
+ /* If hash only set because of the number option, discard it. */
+ if (optnum)
+ F_CLR(&exc, E_F_HASH);
+
+ /*
+ * If 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 any print flags set use
+ * them, otherwise default to print.
+ */
+ LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));
+ if (!LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT) &&
+ O_ISSET(sp, O_AUTOPRINT) &&
+ (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))
+ LF_INIT(E_F_PRINT);
+
+ if (LF_ISSET(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;
+ (void)ex_print(sp, ep, &exc.addr1, &exc.addr2, flags);
+ }
+ }
+
+ cmd = save_cmd;
+ cmdlen = save_cmdlen;
+ goto loop;
+ /* NOTREACHED */
+
+ /*
+ * If we haven't put out a separator line, do it now. For more
+ * detailed comments, see above.
+ */
+err: if (sep != NONE &&
+ ep != NULL && IN_EX_MODE(sp) && F_ISSET(sp->gp, G_STDIN_TTY))
+ (void)fputc('\n', stdout);
+ /*
+ * 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.
+ */
+ if (save_cmdlen == 0)
+ for (; cmdlen; --cmdlen) {
+ ch = *cmd++;
+ if (IS_ESCAPE(sp, ch) && 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");
+ /*
+ * !!!
+ * Previous versions of nvi cleared mapped characters on error. This
+ * feature was removed when users complained that it wasn't historic
+ * practice.
+ */
+ 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 the 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) {
+ 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.
+ *
+ * The way the "previous context" mark worked was that any "non-relative"
+ * motion set it. While ex/vi wasn't totally consistent about this, ANY
+ * numeric address, search pattern, '$', or mark reference in an address
+ * was considered non-relative, and set the value. Which should explain
+ * why we're hacking marks down here. The problem was that the mark was
+ * only set if the command was called, i.e. we have to set a flag and test
+ * it later.
+ *
+ * XXX
+ * This is not exactly historic practice, although it's fairly close.
+ */
+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;
+{
+ EX_PRIVATE *exp;
+ MARK m;
+ long total;
+ u_int flags;
+ size_t cmdlen;
+ int (*sf) __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+ char *cmd, *endp;
+
+ exp = EXP(sp);
+ *addr_found = 0;
+
+ cmd = *cmdp;
+ cmdlen = *cmdlenp;
+ switch (*cmd) {
+ case '$': /* Last line in the file. */
+ *addr_found = 1;
+ F_SET(exp, EX_ABSMARK);
+
+ 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;
+ F_SET(exp, EX_ABSMARK);
+
+ 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;
+ F_SET(exp, EX_ABSMARK);
+
+ if (cmdlen == 1) {
+ msgq(sp, M_ERR, "No mark name supplied");
+ return (1);
+ }
+ if (mark_get(sp, ep, cmd[1], cur))
+ return (1);
+ 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;
+ sf = cmd[0] == '/' ? f_search : b_search;
+ goto search;
+ case '/': /* Search forward. */
+ sf = f_search;
+ goto search;
+ case '?': /* Search backward. */
+ sf = b_search;
+search: F_SET(exp, EX_ABSMARK);
+
+ if (ep == NULL) {
+ msgq(sp, M_ERR,
+ "A search address requires that a file have already been read in");
+ return (1);
+ }
+ *addr_found = 1;
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+ if (sf(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 (*addr_found) {
+ 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]));
+}
+
+/*
+ * ex_is_unmap -
+ * The vi text input routine needs to know if ex thinks this is
+ * an unmap command, so it can turn off input mapping. Usual
+ * ranting in the vi/v_ntext:txt_unmap() routine.
+ */
+int
+ex_is_unmap(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ /*
+ * The command the vi input routines are really interested in
+ * is "unmap!", not just unmap.
+ */
+ if (name[len - 1] != '!')
+ return (0);
+ --len;
+ return ((cp = ex_comm_search(name, len)) != NULL &&
+ cp == &cmds[C_UNMAP]);
+}
+
+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);
+}
+
+static void
+badlno(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ if (lno == 0)
+ msgq(sp, M_ERR, "Illegal address: the file is empty");
+ else
+ msgq(sp, M_ERR, "Illegal address: only %lu line%s in the file",
+ lno, lno > 1 ? "s" : "");
+}
diff --git a/usr.bin/vi/ex/ex_abbrev.c b/usr.bin/vi/ex/ex_abbrev.c
new file mode 100644
index 0000000..a0af8cd
--- /dev/null
+++ b/usr.bin/vi/ex/ex_abbrev.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_abbrev.c 8.12 (Berkeley) 8/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../vi/vcmd.h"
+
+/*
+ * ex_abbr -- :abbreviate [key replacement]
+ * Create an abbreviation or display abbreviations.
+ */
+int
+ex_abbr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CHAR_T *p;
+ size_t len;
+
+ 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();
+ }
+
+ /* Check for illegal characters. */
+ for (p = cmdp->argv[0]->bp, len = cmdp->argv[0]->len; len--; ++p)
+ if (!inword(*p)) {
+ msgq(sp, M_ERR,
+ "%s may not be part of an abbreviated word",
+ KEY_NAME(sp, *p));
+ return (1);
+ }
+
+ if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, SEQ_USERDEF))
+ 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/ex/ex_append.c b/usr.bin/vi/ex/ex_append.c
new file mode 100644
index 0000000..97172dc
--- /dev/null
+++ b/usr.bin/vi/ex/ex_append.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_append.c 8.22 (Berkeley) 8/7/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../sex/sex_screen.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;
+ TEXTH *sv_tiqp, tiq;
+ TEXT *tp;
+ struct termios t;
+ u_int flags;
+ int rval;
+
+ rval = 0;
+
+ /*
+ * Set input flags; the ! flag turns off autoindent for append,
+ * change and insert.
+ */
+ LF_INIT(TXT_DOTTERM | TXT_NLECHO);
+ if (!F_ISSET(cmdp, E_FORCE) && O_ISSET(sp, O_AUTOINDENT))
+ LF_SET(TXT_AUTOINDENT);
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+
+ /* Input is interruptible. */
+ F_SET(sp, S_INTERRUPTIBLE);
+
+ /*
+ * If this code is called by vi, the screen TEXTH structure (sp->tiqp)
+ * may already be in use, e.g. ":append|s/abc/ABC/" would fail as we're
+ * only halfway through the line when the append code fires. Use the
+ * local structure instead.
+ *
+ * If this code is called by vi, we want to reset the terminal and use
+ * ex's s_get() routine. It actually works fine if we use vi's s_get()
+ * routine, but it doesn't look as nice. Maybe if we had a separate
+ * window or something, but getting a line at a time looks awkward.
+ */
+ if (IN_VI_MODE(sp)) {
+ memset(&tiq, 0, sizeof(TEXTH));
+ CIRCLEQ_INIT(&tiq);
+ sv_tiqp = sp->tiqp;
+ sp->tiqp = &tiq;
+
+ if (F_ISSET(sp->gp, G_STDIN_TTY))
+ SEX_RAW(t);
+ (void)write(STDOUT_FILENO, "\n", 1);
+ LF_SET(TXT_NLECHO);
+
+ }
+
+ /* Set the line number, so that autoindent works correctly. */
+ sp->lno = cmdp->addr1.lno;
+
+ if (sex_get(sp, ep, sp->tiqp, 0, flags) != INP_OK)
+ goto err;
+
+ /*
+ * If doing a change, replace lines for as long as possible. Then,
+ * append more lines or delete remaining lines. Changes to an empty
+ * file are just appends, and inserts are the same as appends to the
+ * previous line.
+ *
+ * !!!
+ * Adjust the current line number for the commands to match historic
+ * practice if the user doesn't enter anything, and set the address
+ * to which we'll append. This is safe because an address of 0 is
+ * illegal for change and insert.
+ */
+ m = cmdp->addr1;
+ switch (cmd) {
+ case INSERT:
+ --m.lno;
+ /* FALLTHROUGH */
+ case APPEND:
+ if (sp->lno == 0)
+ sp->lno = 1;
+ break;
+ case CHANGE:
+ --m.lno;
+ if (sp->lno != 1)
+ --sp->lno;
+ break;
+ }
+
+ /*
+ * !!!
+ * Cut into the unnamed buffer.
+ */
+ if (cmd == CHANGE &&
+ (cut(sp, ep, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
+ delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1)))
+ goto err;
+
+ for (tp = sp->tiqp->cqh_first;
+ tp != (TEXT *)sp->tiqp; tp = tp->q.cqe_next) {
+ if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) {
+err: rval = 1;
+ break;
+ }
+ sp->lno = ++m.lno;
+ }
+
+ if (IN_VI_MODE(sp)) {
+ sp->tiqp = sv_tiqp;
+ text_lfree(&tiq);
+
+ /* Reset the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY)) {
+ if (SEX_NORAW(t))
+ rval = 1;
+ F_SET(sp, S_REFRESH);
+ }
+ }
+ return (rval);
+}
diff --git a/usr.bin/vi/ex/ex_args.c b/usr.bin/vi/ex/ex_args.c
new file mode 100644
index 0000000..9213e84
--- /dev/null
+++ b/usr.bin/vi/ex/ex_args.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_args.c 8.27 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_next -- :next [+cmd] [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, **pc;
+ FREF *frp;
+ int noargs;
+ char **ap;
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /*
+ * If the first argument is a plus sign, '+', it's an initial
+ * ex command.
+ */
+ argv = cmdp->argv;
+ if (cmdp->argc && argv[0]->bp[0] == '+') {
+ --cmdp->argc;
+ pc = argv++;
+ } else
+ pc = NULL;
+
+ /* Any other arguments are a replacement file list. */
+ if (cmdp->argc) {
+ /* Free the current list. */
+ if (!F_ISSET(sp, S_ARGNOFREE) && sp->argv != NULL) {
+ for (ap = sp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(sp->argv);
+ }
+ F_CLR(sp, S_ARGNOFREE | S_ARGRECOVER);
+ sp->cargv = NULL;
+
+ /* Create a new list. */
+ CALLOC_RET(sp,
+ sp->argv, char **, cmdp->argc + 1, sizeof(char *));
+ for (ap = sp->argv,
+ argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ return (1);
+ *ap = NULL;
+
+ /* Switch to the first one. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ noargs = 0;
+ } else {
+ if (sp->cargv == NULL || sp->cargv[1] == NULL) {
+ msgq(sp, M_ERR, "No more files to edit");
+ return (1);
+ }
+ if ((frp = file_add(sp, sp->cargv[1])) == NULL)
+ return (1);
+ if (F_ISSET(sp, S_ARGRECOVER))
+ F_SET(frp, FR_RECOVER);
+ noargs = 1;
+ }
+
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ if (noargs)
+ ++sp->cargv;
+
+ /* Push the initial command onto the stack. */
+ if (pc != NULL)
+ if (IN_EX_MODE(sp))
+ (void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
+ else if (IN_VI_MODE(sp)) {
+ (void)term_push(sp, "\n", 1, 0);
+ (void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
+ (void)term_push(sp, ":", 1, 0);
+ (void)file_lline(sp, sp->ep, &sp->frp->lno);
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+
+ 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;
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ if (sp->cargv == sp->argv) {
+ msgq(sp, M_ERR, "No previous files to edit");
+ return (1);
+ }
+ if ((frp = file_add(sp, sp->cargv[-1])) == NULL)
+ return (1);
+
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ --sp->cargv;
+ 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;
+
+ /*
+ * !!!
+ * Historic practice -- you can rewind to the current file.
+ */
+ if (sp->argv == NULL) {
+ msgq(sp, M_ERR, "No previous files to rewind");
+ return (1);
+ }
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /*
+ * !!!
+ * Historic practice, start at the beginning of the file.
+ */
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ F_CLR(frp, FR_CURSORSET | FR_FNONBLANK);
+
+ /* Switch to the first one. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ 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;
+{
+ int cnt, col, len, sep;
+ char **ap;
+
+ if (sp->argv == NULL) {
+ (void)ex_printf(EXCOOKIE, "No file list to display.\n");
+ return (0);
+ }
+
+ col = len = sep = 0;
+ for (cnt = 1, ap = sp->argv; *ap != NULL; ++ap) {
+ col += len = strlen(*ap) + sep + (ap == sp->cargv ? 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 (ap == sp->cargv)
+ (void)ex_printf(EXCOOKIE, "[%s]", *ap);
+ else
+ (void)ex_printf(EXCOOKIE, "%s", *ap);
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_argv.c b/usr.bin/vi/ex/ex_argv.c
new file mode 100644
index 0000000..f93aa76
--- /dev/null
+++ b/usr.bin/vi/ex/ex_argv.c
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_argv.c 8.36 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 --
+ * Append a string to the argument list.
+ */
+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 append it to the
+ * argument list.
+ */
+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, *p, *t;
+
+ 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);
+ }
+
+ /* If it's empty, we're done. */
+ if (len != 0) {
+ for (p = bp, t = bp + len; p < t; ++p)
+ if (!isblank(*p))
+ break;
+ if (p == t)
+ goto ret;
+ } else
+ goto ret;
+
+ (void)argv_exp0(sp, ep, excp, bp, len);
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (0);
+}
+
+/*
+ * argv_exp2 --
+ * Do file name and shell expansion on a string, and append it to
+ * the argument list.
+ */
+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, *mp, *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. Historically, it was a
+ * union of v7 shell and csh meta characters. We match that practice
+ * by default, so ":read \%" tries to read a file named '%'. It would
+ * make more sense to pass any special characters through the shell,
+ * but then, if your shell was csh, the above example will behave
+ * differently in nvi than in vi. If you want to get other characters
+ * passed through to your shell, change the "meta" option.
+ *
+ * To avoid a function call per character, we do a first pass through
+ * the meta characters looking for characters that aren't expected
+ * to be there.
+ */
+ for (p = mp = O_STR(sp, O_META); *p != '\0'; ++p)
+ if (isblank(*p) || isalnum(*p))
+ break;
+ if (*p != '\0') {
+ for (p = bp, n = len; n > 0; --n, ++p)
+ if (strchr(mp, *p) != NULL)
+ break;
+ } else
+ for (p = bp, n = len; n > 0; --n, ++p)
+ if (!isblank(*p) &&
+ !isalnum(*p) && strchr(mp, *p) != NULL)
+ 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, which is appended
+ * to the argument list.
+ */
+int
+argv_exp3(sp, ep, excp, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+ size_t len;
+ int ch, off;
+ char *ap, *p;
+
+ 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) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && 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 (IS_ESCAPE(sp, *ap))
+ ++ap;
+ *p = '\0';
+ }
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+
+#if defined(DEBUG) && 0
+ for (cnt = 0; cnt < exp->argsoff; ++cnt)
+ TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
+#endif
+ return (0);
+}
+
+/*
+ * argv_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 ((t = sp->frp->name) == NULL) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for %%");
+ return (1);
+ }
+ tlen = strlen(t);
+ len += tlen;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '#':
+ if ((t = sp->alt_name) == NULL) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for #");
+ return (1);
+ }
+ 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;
+ --cmdlen;
+ }
+ /* 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.
+ */
+ SIGBLOCK(sp->gp);
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+err: (void)close(output[0]);
+ (void)close(output[1]);
+ return (1);
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ /* 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: /* Parent. */
+ SIGUNBLOCK(sp->gp);
+
+ /* 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/ex/ex_at.c b/usr.bin/vi/ex/ex_at.c
new file mode 100644
index 0000000..fc5922f
--- /dev/null
+++ b/usr.bin/vi/ex/ex_at.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_at.c 8.25 (Berkeley) 8/1/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+
+ exp = EXP(sp);
+
+ /*
+ * !!!
+ * Historically, [@*]<carriage-return> and [@*][@*] executed the most
+ * recently executed buffer in ex mode. In vi mode, only @@ repeated
+ * the last buffer. We change historic practice and make @* work from
+ * vi mode as well, it's simpler and more consistent.
+ */
+ name = F_ISSET(cmdp, E_BUFFER) ? cmdp->buffer : '@';
+ if (name == '@' || name == '*') {
+ 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", KEY_NAME(sp, name));
+ return (1);
+ }
+
+ /* Save for reuse. */
+ exp->at_lbuf = name;
+ exp->at_lbuf_set = 1;
+
+ /*
+ * !!!
+ * Historic practice is that if the buffer was cut in line mode,
+ * <newlines> were appended to each line as it was pushed onto
+ * the stack. If the buffer was cut in character mode, <newlines>
+ * were appended to all lines but the last one.
+ */
+ for (tp = cbp->textq.cqh_last;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+ if ((F_ISSET(cbp, CB_LMODE) ||
+ tp->q.cqe_next != (void *)&cbp->textq) &&
+ term_push(sp, "\n", 1, 0) ||
+ term_push(sp, tp->lb, tp->len, 0))
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_bang.c b/usr.bin/vi/ex/ex_bang.c
new file mode 100644
index 0000000..70bbb57
--- /dev/null
+++ b/usr.bin/vi/ex/ex_bang.c
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_bang.c 8.33 (Berkeley) 8/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 it 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);
+ }
+
+ /* Set the last bang command. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ 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) && !F_ISSET(sp, S_EXSILENT)) {
+ 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 + 3);
+ bp[0] = '!';
+ memmove(bp + 1, ap->bp, ap->len);
+ bp[ap->len + 1] = '\n';
+ bp[ap->len + 2] = '\0';
+ }
+ }
+
+ /*
+ * If addresses were specified, pipe lines from the file through the
+ * command.
+ *
+ * Historically, vi lines were replaced by both the stdout and stderr
+ * lines of the command, but ex by only the stdout lines. This makes
+ * no sense to me, so nvi makes it consistent for both, and matches
+ * vi's historic behavior.
+ */
+ if (cmdp->addrcnt != 0) {
+ /* Autoprint is set historically, even if the command fails. */
+ F_SET(exp, EX_AUTOPRINT);
+
+ /* Vi gets a busy message. */
+ if (bp != NULL)
+ (void)sp->s_busy(sp, bp);
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. When it
+ * happens, we get called with two addresses of 1,1 and a
+ * bad attitude. The simple solution is to turn it into a
+ * FILTER_READ operation, but that means that we don't put
+ * an empty line into the default cut buffer as did historic
+ * vi. Tough.
+ */
+ 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;
+ }
+ }
+ rval = filtercmd(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
+
+ /*
+ * If in vi mode, move to the first nonblank.
+ *
+ * !!!
+ * Historic vi wasn't consistent in this area -- if you used
+ * a forward motion it moved to the first nonblank, but if you
+ * did a backward motion it didn't. And, if you followed a
+ * backward motion with a forward motion, it wouldn't move to
+ * the nonblank for either. Going to the nonblank generally
+ * seems more useful, so we do it.
+ */
+ if (rval == 0) {
+ sp->lno = rm.lno;
+ if (IN_VI_MODE(sp)) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+ }
+ goto ret2;
+ }
+
+ /*
+ * 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 = NULL;
+ 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 ret1;
+ }
+ } else if (O_ISSET(sp, O_WARN) && !F_ISSET(sp, S_EXSILENT))
+ msg = "File modified since last write.\n";
+
+ /* Run the command. */
+ rval = ex_exec_proc(sp, ap->bp, bp, msg);
+
+ /* Vi requires user permission to continue. */
+ if (IN_VI_MODE(sp))
+ F_SET(sp, S_CONTINUE);
+
+ret2: if (IN_EX_MODE(sp)) {
+ /*
+ * Put ex error messages out so they aren't confused with
+ * the autoprint output.
+ */
+ if (rval)
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Ex terminates with a bang, even if the command fails. */
+ if (!F_ISSET(sp, S_EXSILENT))
+ (void)write(STDOUT_FILENO, "!\n", 2);
+ }
+
+ /* Free the extra space. */
+ret1: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+
+ /*
+ * XXX
+ * The ! commands never return an error, so that autoprint always
+ * happens in the ex parser.
+ */
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_cd.c b/usr.bin/vi/ex/ex_cd.c
new file mode 100644
index 0000000..3732f7b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_cd.c
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_cd.c 8.16 (Berkeley) 8/8/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+{
+ struct passwd *pw;
+ ARGS *ap;
+ CDPATH *cdp;
+ char *dir; /* XXX END OF THE STACK, DON'T TRUST GETCWD. */
+ char buf[MAXPATHLEN * 2];
+
+ /*
+ * !!!
+ * Historic practice is that the cd isn't attempted if the file has
+ * been modified, unless its name begins with a leading '/' or the
+ * force flag is set.
+ */
+ if (F_ISSET(ep, F_MODIFIED) &&
+ !F_ISSET(cmdp, E_FORCE) && sp->frp->name[0] != '/') {
+ msgq(sp, M_ERR,
+ "File modified since last complete write; write or use ! to override");
+ return (1);
+ }
+
+ switch (cmdp->argc) {
+ case 0:
+ /* If no argument, change to the user's home directory. */
+ if ((dir = getenv("HOME")) == NULL) {
+ if ((pw = getpwuid(getuid())) == NULL ||
+ pw->pw_dir == NULL || pw->pw_dir[0] == '\0') {
+ msgq(sp, M_ERR,
+ "Unable to find home directory location");
+ return (1);
+ }
+ dir = pw->pw_dir;
+ }
+ break;
+ case 1:
+ dir = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /* Try the current directory first. */
+ if (!chdir(dir))
+ goto ret;
+
+ /*
+ * If moving to the user's home directory, or, the path begins with
+ * "/", "./" or "../", it's the only place we try.
+ */
+ if (cmdp->argc == 0 ||
+ (ap = cmdp->argv[0])->bp[0] == '/' ||
+ ap->len == 1 && ap->bp[0] == '.' ||
+ ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' &&
+ (ap->bp[2] == '/' || ap->bp[2] == '\0'))
+ goto err;
+
+ /* If the user has a CDPATH variable, try its elements. */
+ for (cdp = EXP(sp)->cdq.tqh_first; cdp != NULL; cdp = cdp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s/%s", cdp->path, dir);
+ if (!chdir(buf)) {
+ret: if (getcwd(buf, sizeof(buf)) != NULL)
+ msgq(sp, M_INFO, "New directory: %s", buf);
+ return (0);
+ }
+ }
+err: msgq(sp, M_SYSERR, "%s", dir);
+ return (1);
+}
+
+#define FREE_CDPATH(cdp) { \
+ TAILQ_REMOVE(&exp->cdq, (cdp), q); \
+ free((cdp)->path); \
+ FREE((cdp), sizeof(CDPATH)); \
+}
+/*
+ * ex_cdalloc --
+ * Create a new list of cd paths.
+ */
+int
+ex_cdalloc(sp, str)
+ SCR *sp;
+ char *str;
+{
+ EX_PRIVATE *exp;
+ CDPATH *cdp;
+ size_t len;
+ int founddot;
+ char *p, *t;
+
+ /* Free current queue. */
+ exp = EXP(sp);
+ while ((cdp = exp->cdq.tqh_first) != NULL)
+ FREE_CDPATH(cdp);
+
+ /*
+ * Create new queue. The CDPATH environmental variable (and the
+ * user's manual entry) are delimited by colon characters.
+ */
+ for (p = t = str, founddot = 0;; ++p) {
+ if (*p == '\0' || *p == ':') {
+ /*
+ * Empty strings specify ".". The only way to get an
+ * empty string is a leading colon, colons in a row,
+ * or a trailing colon. Or, to put it the other way,
+ * if the the length is zero, then it's either ":XXX",
+ * "XXX::XXXX" , "XXX:", or "", and the only failure
+ * mode is the last one. Note, the string ":" gives
+ * us two entries of '.', so we only include one of
+ * them.
+ */
+ if ((len = p - t) == 0) {
+ if (p == str && *p == '\0')
+ break;
+ if (founddot) {
+ if (*p == '\0')
+ break;
+ continue;
+ }
+ len = 1;
+ t = ".";
+ founddot = 1;
+ }
+ MALLOC_RET(sp, cdp, CDPATH *, sizeof(CDPATH));
+ MALLOC(sp, cdp->path, char *, len + 1);
+ if (cdp->path == NULL) {
+ free(cdp);
+ return (1);
+ }
+ memmove(cdp->path, t, len);
+ cdp->path[len] = '\0';
+ TAILQ_INSERT_TAIL(&exp->cdq, cdp, q);
+ t = p + 1;
+ }
+ if (*p == '\0')
+ break;
+ }
+ return (0);
+}
+ /* Free previous queue. */
+/*
+ * ex_cdfree --
+ * Free the cd path list.
+ */
+int
+ex_cdfree(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ CDPATH *cdp;
+
+ /* Free up cd path information. */
+ exp = EXP(sp);
+ while ((cdp = exp->cdq.tqh_first) != NULL)
+ FREE_CDPATH(cdp);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_delete.c b/usr.bin/vi/ex/ex_delete.c
new file mode 100644
index 0000000..08c4250
--- /dev/null
+++ b/usr.bin/vi/ex/ex_delete.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_delete.c 8.12 (Berkeley) 8/5/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+
+ /*
+ * !!!
+ * Historically, lines deleted in ex were not placed in the numeric
+ * buffers. We follow historic practice so that we don't overwrite
+ * vi buffers accidentally.
+ */
+ if (cut(sp, ep,
+ F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, 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/ex/ex_digraph.c b/usr.bin/vi/ex/ex_digraph.c
new file mode 100644
index 0000000..0739923
--- /dev/null
+++ b/usr.bin/vi/ex/ex_digraph.c
@@ -0,0 +1,324 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_digraph.c 8.6 (Berkeley) 3/25/94";
+#endif /* not lint */
+
+#ifndef NO_DIGRAPH
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "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/ex/ex_display.c b/usr.bin/vi/ex/ex_display.c
new file mode 100644
index 0000000..139127e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_display.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_display.c 8.21 (Berkeley) 8/3/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "tag.h"
+#include "excmd.h"
+
+static int bdisplay __P((SCR *, EXF *));
+static void db __P((SCR *, CB *, CHAR_T *));
+
+/*
+ * 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':
+#undef ARG
+#define ARG "buffers"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (bdisplay(sp, ep));
+ case 's':
+#undef ARG
+#define ARG "screens"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_sdisplay(sp, ep));
+ case 't':
+#undef ARG
+#define ARG "tags"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_tagdisplay(sp, ep));
+ }
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ 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.\n");
+ 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 (INTERRUPTED(sp))
+ 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 (INTERRUPTED(sp))
+ 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_T *name;
+{
+ CHAR_T *p;
+ TEXT *tp;
+ size_t len;
+
+ (void)ex_printf(EXCOOKIE, "********** %s%s\n",
+ name == NULL ? KEY_NAME(sp, cbp->name) : name,
+ F_ISSET(cbp, CB_LMODE) ? " (line mode)" : " (character mode)");
+ for (tp = cbp->textq.cqh_first;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
+ for (len = tp->len, p = tp->lb; len--; ++p) {
+ (void)ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
+ if (INTERRUPTED(sp))
+ return;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+}
diff --git a/usr.bin/vi/ex/ex_edit.c b/usr.bin/vi/ex/ex_edit.c
new file mode 100644
index 0000000..bfe0a2a
--- /dev/null
+++ b/usr.bin/vi/ex/ex_edit.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_edit.c 8.18 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 (F_ISSET(frp, FR_TMPFILE)) {
+ if ((frp = file_add(sp, NULL)) == NULL)
+ return (1);
+ } else {
+ if ((frp = file_add(sp, frp->name)) == NULL)
+ return (1);
+ set_alt_name(sp, sp->frp->name);
+ }
+ break;
+ case 1:
+ ap = cmdp->argv[0];
+ if ((frp = file_add(sp, ap->bp)) == 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 (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
+ 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/ex/ex_equal.c b/usr.bin/vi/ex/ex_equal.c
new file mode 100644
index 0000000..a80d723
--- /dev/null
+++ b/usr.bin/vi/ex/ex_equal.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_equal.c 8.6 (Berkeley) 4/26/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_equal -- :address =
+ */
+int
+ex_equal(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ /*
+ * Print out the line number matching the specified address,
+ * or the number of the last line in the file if no address
+ * specified.
+ *
+ * !!!
+ * Historically, ":0=" displayed 0, and ":=" or ":1=" in an
+ * empty file displayed 1. Until somebody complains loudly,
+ * we're going to do it right. The tables in excmd.c permit
+ * lno to get away with any address from 0 to the end of the
+ * file, which, in an empty file, is 0.
+ */
+ if (F_ISSET(cmdp, E_ADDRDEF)) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ } else
+ lno = cmdp->addr1.lno;
+
+ (void)ex_printf(EXCOOKIE, "%ld\n", lno);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_exit.c b/usr.bin/vi/ex/ex_exit.c
new file mode 100644
index 0000000..3f0a362
--- /dev/null
+++ b/usr.bin/vi/ex/ex_exit.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_exit.c 8.13 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 (file_m2(sp, ep, force))
+ return (1);
+
+ /* Check for more files to edit. */
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_file.c b/usr.bin/vi/ex/ex_file.c
new file mode 100644
index 0000000..b2c68e7
--- /dev/null
+++ b/usr.bin/vi/ex/ex_file.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_file.c 8.10 (Berkeley) 8/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_file -- :f[ile] [name]
+ * Change the file's name and display the status line.
+ */
+int
+ex_file(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CHAR_T *p;
+ FREF *frp;
+
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ frp = sp->frp;
+
+ /* Make sure can allocate enough space. */
+ if ((p = v_strdup(sp,
+ cmdp->argv[0]->bp, cmdp->argv[0]->len)) == NULL)
+ return (1);
+
+ /* If already have a file name, it becomes the alternate. */
+ if (!F_ISSET(frp, FR_TMPFILE))
+ set_alt_name(sp, frp->name);
+
+ /* Free the previous name. */
+ free(frp->name);
+ frp->name = p;
+
+ /*
+ * The read-only bit follows the file name; clear it.
+ * The file has a real name, it's no longer a temporary.
+ */
+ F_CLR(frp, FR_RDONLY | FR_TMPFILE);
+
+ /* Have to force a write if the file exists, next time. */
+ F_SET(frp, FR_NAMECHANGE);
+ break;
+ default:
+ abort();
+ }
+ return (msg_status(sp, ep, sp->lno, 1));
+}
diff --git a/usr.bin/vi/ex/ex_global.c b/usr.bin/vi/ex/ex_global.c
new file mode 100644
index 0000000..624bf8e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_global.c
@@ -0,0 +1,400 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_global.c 8.41 (Berkeley) 8/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {GLOBAL, VGLOBAL};
+
+static int global __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * 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;
+{
+ MARK abs;
+ 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_SPACE(sp, ptrn, 0);
+
+ 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; the default command is print.
+ * Don't worry about a set of <blank>s with no command, that will
+ * default to print in the ex parser.
+ */
+ if ((clen = strlen(p)) == 0) {
+ p = "p";
+ clen = 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. */
+ F_SET(sp, S_GLOBAL);
+
+ /* The global commands always set the previous context mark. */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ if (mark_set(sp, ep, ABSMARK1, &abs, 1))
+ goto err;
+
+ /*
+ * 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 (INTERRUPTED(sp))
+ 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, 0))
+ goto err;
+
+ /* Someone's unhappy, time to stop. */
+ if (INTERRUPTED(sp)) {
+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;
+ }
+
+ /* Command we ran may have set the autoprint flag, clear it. */
+ F_CLR(exp, EX_AUTOPRINT);
+
+ /* Clear the global flag. */
+ F_CLR(sp, S_GLOBAL);
+
+ /* 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;
+}
diff --git a/usr.bin/vi/ex/ex_init.c b/usr.bin/vi/ex/ex_init.c
new file mode 100644
index 0000000..79b0e4b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_init.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_init.c 8.16 (Berkeley) 8/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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);
+ TAILQ_INIT(&nexp->cdq);
+ 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);
+ }
+ 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);
+
+ if (ex_tagfree(sp))
+ rval = 1;
+
+ if (ex_cdfree(sp))
+ rval = 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 (msg_status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * ex_end --
+ * End ex session.
+ */
+int
+ex_end(sp)
+ SCR *sp;
+{
+ return (0);
+}
+
+/*
+ * ex_optchange --
+ * Handle change of options for vi.
+ */
+int
+ex_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_CDPATH:
+ return (ex_cdalloc(sp, O_STR(sp, O_CDPATH)));
+ case O_TAGS:
+ return (ex_tagalloc(sp, O_STR(sp, O_TAGS)));
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_join.c b/usr.bin/vi/ex/ex_join.c
new file mode 100644
index 0000000..d561f0a
--- /dev/null
+++ b/usr.bin/vi/ex/ex_join.c
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_join.c 8.12 (Berkeley) 5/21/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+
+ /* If the original line changed, reset it. */
+ if (!first && 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/ex/ex_map.c b/usr.bin/vi/ex/ex_map.c
new file mode 100644
index 0000000..a1f1915
--- /dev/null
+++ b/usr.bin/vi/ex/ex_map.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_map.c 8.17 (Berkeley) 7/16/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "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, *p;
+
+ 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) then store
+ * the function key mapping, and call the screen specific routine.
+ * Note, if the screen specific routine is able to create the
+ * mapping, the SEQ_FUNCMAP type stays around, maybe the next screen
+ * type can get it right.
+ */
+ if (input[0] == '#') {
+ for (p = input + 1; isdigit(*p); ++p);
+ if (p[0] != '\0')
+ goto nofunc;
+
+ if (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_FUNCMAP))
+ return (1);
+ return (sp->s_fmap(sp, stype, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len));
+ }
+
+ /* Some single keys may not be remapped in command mode. */
+nofunc: if (stype == SEQ_COMMAND && input[1] == '\0')
+ switch (KEY_VAL(sp, input[0])) {
+ case K_COLON:
+ case K_ESCAPE:
+ case K_NL:
+ msgq(sp, M_ERR, "The %s character may not be remapped",
+ KEY_NAME(sp, input[0]));
+ return (1);
+ }
+ return (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_USERDEF));
+}
+
+/*
+ * 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/ex/ex_mark.c b/usr.bin/vi/ex/ex_mark.c
new file mode 100644
index 0000000..bd1e0fd
--- /dev/null
+++ b/usr.bin/vi/ex/ex_mark.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mark.c 8.6 (Berkeley) 5/21/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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/ex/ex_mkexrc.c b/usr.bin/vi/ex/ex_mkexrc.c
new file mode 100644
index 0000000..095d51e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_mkexrc.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mkexrc.c 8.12 (Berkeley) 7/15/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.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/ex/ex_move.c b/usr.bin/vi/ex/ex_move.c
new file mode 100644
index 0000000..9822508
--- /dev/null
+++ b/usr.bin/vi/ex/ex_move.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_move.c 8.17 (Berkeley) 8/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_copy -- :[line [,line]] co[py] line [flags]
+ * Copy selected lines.
+ */
+int
+ex_copy(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CB cb;
+ MARK fm1, fm2, m, tm;
+ recno_t cnt;
+ int rval;
+
+ rval = 0;
+
+ /*
+ * It's possible to copy things into the area that's being
+ * copied, e.g. "2,5copy3" is legitimate. Save the text to
+ * a cut buffer.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ memset(&cb, 0, sizeof(cb));
+ CIRCLEQ_INIT(&cb.textq);
+ for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
+ if (cut_line(sp, ep, cnt, 0, 0, &cb)) {
+ rval = 1;
+ goto err;
+ }
+ cb.flags |= CB_LMODE;
+
+ /* Put the text into place. */
+ tm.lno = cmdp->lineno;
+ tm.cno = 0;
+ if (put(sp, ep, &cb, NULL, &tm, &m, 1))
+ rval = 1;
+ else {
+ /*
+ * Copy puts the cursor on the last line copied. The cursor
+ * returned by the put routine is the first line put, not the
+ * last, because that's the historic semantic of vi.
+ */
+ cnt = (fm2.lno - fm1.lno) + 1;
+ sp->lno = m.lno + (cnt - 1);
+ sp->cno = 0;
+ }
+err: text_lfree(&cb.textq);
+ return (rval);
+}
+
+/*
+ * ex_move -- :[line [,line]] mo[ve] line
+ * Move selected lines.
+ */
+int
+ex_move(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ LMARK *lmp;
+ MARK fm1, fm2;
+ recno_t cnt, diff, fl, tl, mfl, mtl;
+ size_t blen, len;
+ int mark_reset;
+ char *bp, *p;
+
+ /*
+ * It's not possible to move things into the area that's being
+ * moved.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
+ msgq(sp, M_ERR, "Destination line is inside move range");
+ return (1);
+ }
+
+ /*
+ * Log the positions of any marks in the to-be-deleted lines. This
+ * has to work with the logging code. What happens is that we log
+ * the old mark positions, make the changes, then log the new mark
+ * positions. Then the marks end up in the right positions no matter
+ * which way the log is traversed.
+ *
+ * XXX
+ * Reset the MARK_USERSET flag so that the log can undo the mark.
+ * This isn't very clean, and should probably be fixed.
+ */
+ fl = fm1.lno;
+ tl = cmdp->lineno;
+
+ /* Log the old positions of the marks. */
+ mark_reset = 0;
+ for (lmp = ep->marks.lh_first; lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= fl && lmp->lno <= tl) {
+ mark_reset = 1;
+ F_CLR(lmp, MARK_USERSET);
+ (void)log_mark(sp, ep, lmp);
+ }
+
+ /* Get memory for the copy. */
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /* Move the lines. */
+ diff = (fm2.lno - fm1.lno) + 1;
+ if (tl > fl) { /* Destination > source. */
+ mfl = tl - diff;
+ mtl = tl;
+ for (cnt = diff; cnt--;) {
+ if ((p = file_gline(sp, ep, fl, &len)) == NULL)
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ if (file_aline(sp, ep, 1, tl, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl + 1;
+ if (file_dline(sp, ep, fl))
+ return (1);
+ }
+ } else { /* Destination < source. */
+ mfl = tl;
+ mtl = tl + diff;
+ for (cnt = diff; cnt--;) {
+ if ((p = file_gline(sp, ep, fl, &len)) == NULL)
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ if (file_aline(sp, ep, 1, tl++, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl;
+ ++fl;
+ if (file_dline(sp, ep, fl))
+ return (1);
+ }
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ sp->lno = tl; /* Last line moved. */
+ sp->cno = 0;
+
+ /* Log the new positions of the marks. */
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= mfl && lmp->lno <= mtl)
+ (void)log_mark(sp, ep, lmp);
+
+
+ sp->rptlines[L_MOVED] += diff;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_open.c b/usr.bin/vi/ex/ex_open.c
new file mode 100644
index 0000000..385fdd3
--- /dev/null
+++ b/usr.bin/vi/ex/ex_open.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_open.c 8.4 (Berkeley) 5/21/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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/ex/ex_preserve.c b/usr.bin/vi/ex/ex_preserve.c
new file mode 100644
index 0000000..8960f98
--- /dev/null
+++ b/usr.bin/vi/ex/ex_preserve.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_preserve.c 8.12 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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, RCV_SNAPSHOT))
+ return (1);
+
+ msgq(sp, M_INFO, "File preserved");
+ return (0);
+}
+
+/*
+ * ex_recover -- :rec[over][!] file
+ *
+ * Recover the file.
+ */
+int
+ex_recover(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ FREF *frp;
+
+ ap = cmdp->argv[0];
+
+ /* Set the alternate file name. */
+ set_alt_name(sp, ap->bp);
+
+ /*
+ * Check for modifications. Autowrite did not historically
+ * affect :recover.
+ */
+ if (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ /* Get a file structure for the file. */
+ if ((frp = file_add(sp, ap->bp)) == NULL)
+ return (1);
+
+ /* Set the recover bit. */
+ F_SET(frp, FR_RECOVER);
+
+ /* 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/ex/ex_print.c b/usr.bin/vi/ex/ex_print.c
new file mode 100644
index 0000000..c3aa22f
--- /dev/null
+++ b/usr.bin/vi/ex/ex_print.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_print.c 8.14 (Berkeley) 8/7/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+{
+ recno_t from, to;
+ size_t col, len;
+ char *p;
+
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (from = fp->lno, to = tp->lno; from <= to; ++from) {
+ /*
+ * Display the line number. The %6 format is specified
+ * by POSIX 1003.2, and is almost certainly large enough.
+ * Check, though, just in case.
+ */
+ if (LF_ISSET(E_F_HASH))
+ if (from <= 999999)
+ col = ex_printf(EXCOOKIE, "%6ld ", from);
+ else
+ col = ex_printf(EXCOOKIE, "TOOBIG ");
+ 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);
+ }
+
+ if (len == 0 && !LF_ISSET(E_F_LIST))
+ (void)ex_printf(EXCOOKIE, "\n");
+ else if (ex_ldisplay(sp, p, len, col, flags))
+ return (1);
+
+ if (INTERRUPTED(sp))
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * ex_ldisplay --
+ * Display a line.
+ */
+int
+ex_ldisplay(sp, lp, len, col, flags)
+ SCR *sp;
+ CHAR_T *lp;
+ size_t len, col;
+ u_int flags;
+{
+ CHAR_T ch, *kp;
+ u_long ts;
+ size_t tlen;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ for (;; --len) {
+ if (len > 0)
+ ch = *lp++;
+ else if (LF_ISSET(E_F_LIST))
+ ch = '$';
+ else
+ break;
+ if (ch == '\t' && !LF_ISSET(E_F_LIST))
+ for (tlen = ts - col % ts;
+ col < sp->cols && tlen--; ++col)
+ (void)ex_printf(EXCOOKIE, " ");
+ else {
+ kp = KEY_NAME(sp, ch);
+ tlen = KEY_LEN(sp, ch);
+ if (col + tlen < sp->cols) {
+ (void)ex_printf(EXCOOKIE, "%s", kp);
+ col += tlen;
+ } else
+ for (; tlen--; ++kp, ++col) {
+ if (col == sp->cols) {
+ col = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ (void)ex_printf(EXCOOKIE, "%c", *kp);
+ }
+ }
+ if (len == 0)
+ break;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_put.c b/usr.bin/vi/ex/ex_put.c
new file mode 100644
index 0000000..ce44b5f
--- /dev/null
+++ b/usr.bin/vi/ex/ex_put.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_put.c 8.6 (Berkeley) 7/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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);
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_read.c b/usr.bin/vi/ex/ex_read.c
new file mode 100644
index 0000000..dafc4ad
--- /dev/null
+++ b/usr.bin/vi/ex/ex_read.c
@@ -0,0 +1,300 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_read.c 8.39 (Berkeley) 8/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_read -- :read [file]
+ * :read [!cmd]
+ * Read from a file or utility.
+ *
+ * !!!
+ * Historical vi wouldn't undo a filter read, for no apparent reason.
+ */
+int
+ex_read(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct stat sb;
+ CHAR_T *arg, *name;
+ EX_PRIVATE *exp;
+ FILE *fp;
+ MARK rm;
+ recno_t nlines;
+ size_t arglen, blen, len;
+ int btear, farg, rval;
+ char *p;
+
+ /*
+ * 0 args: we're done.
+ * 1 args: check for "read !arg".
+ * 2 args: check for "read ! arg".
+ * >2 args: object, too many args.
+ */
+ farg = 0;
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ arg = cmdp->argv[0]->bp;
+ arglen = cmdp->argv[0]->len;
+ if (*arg == '!') {
+ ++arg;
+ --arglen;
+ farg = 1;
+ }
+ break;
+ case 2:
+ if (cmdp->argv[0]->len == 1 && cmdp->argv[0]->bp[0] == '!') {
+ arg = cmdp->argv[1]->bp;
+ arglen = cmdp->argv[1]->len;
+ farg = 2;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ goto badarg;
+ }
+
+ if (farg != 0) {
+ /* File name and bang expand the user's argument. */
+ if (argv_exp1(sp, ep, cmdp, arg, arglen, 1))
+ return (1);
+
+ /* If argc unchanged, there wasn't anything to expand. */
+ if (cmdp->argc == farg)
+ goto usage;
+
+ /* Set the last bang command. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ if ((exp->lastbcomm = strdup(cmdp->argv[farg]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* Redisplay the user's argument if it's changed. */
+ if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) {
+ len = cmdp->argv[farg]->len;
+ GET_SPACE_RET(sp, p, blen, len + 2);
+ p[0] = '!';
+ memmove(p + 1,
+ cmdp->argv[farg]->bp, cmdp->argv[farg]->len + 1);
+ (void)sp->s_busy(sp, p);
+ FREE_SPACE(sp, p, blen);
+ }
+
+ if (filtercmd(sp, ep, &cmdp->addr1,
+ NULL, &rm, cmdp->argv[farg]->bp, FILTER_READ))
+ return (1);
+
+ /* The filter version of read set the autoprint flag. */
+ F_SET(EXP(sp), EX_AUTOPRINT);
+
+ /* If in vi mode, move to the first nonblank. */
+ sp->lno = rm.lno;
+ if (IN_VI_MODE(sp)) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+ return (0);
+ }
+
+ /* Shell and file name expand the user's argument. */
+ if (argv_exp2(sp, ep, cmdp, arg, arglen, 0))
+ return (1);
+
+ /*
+ * 0 args: no arguments, read the current file, don't set the
+ * alternate file name.
+ * 1 args: read it, switching to it or settgin the alternate file
+ * name.
+ * >1 args: object, too many args.
+ */
+ switch (cmdp->argc) {
+ case 1:
+ name = sp->frp->name;
+ break;
+ case 2:
+ name = cmdp->argv[1]->bp;
+ /*
+ * !!!
+ * Historically, if you had an "unnamed" file, the read command
+ * renamed the file.
+ */
+ if (F_ISSET(sp->frp, FR_TMPFILE) &&
+ !F_ISSET(sp->frp, FR_READNAMED)) {
+ if ((p = v_strdup(sp,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len)) != NULL) {
+ free(sp->frp->name);
+ sp->frp->name = p;
+ }
+ F_SET(sp->frp, FR_NAMECHANGE | FR_READNAMED);
+ } else
+ set_alt_name(sp, name);
+ break;
+ default:
+badarg: msgq(sp, M_ERR,
+ "%s expanded into too many file names", cmdp->argv[0]->bp);
+usage: 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, "%s", name);
+ return (1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ (void)fclose(fp);
+ msgq(sp, M_ERR, "Only regular files may be read");
+ return (1);
+ }
+
+ /* Turn on busy message. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Reading...");
+ rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1);
+ if (btear)
+ busy_off(sp);
+
+ /*
+ * 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;
+
+ 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;
+ recno_t lcnt, lno;
+ size_t len;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ int rval;
+
+ rval = 0;
+ exp = EXP(sp);
+
+ /*
+ * Add in the lines from the output. Insertion starts at the line
+ * following the address.
+ */
+ ccnt = 0;
+ lcnt = 0;
+ for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) {
+ if (INTERRUPTED(sp)) {
+ if (!success_msg)
+ msgq(sp, M_INFO, "Interrupted");
+ break;
+ }
+ if (file_aline(sp, ep, 1, lno, exp->ibp, len)) {
+ rval = 1;
+ break;
+ }
+ ccnt += len;
+ }
+
+ if (ferror(fp)) {
+ msgq(sp, M_SYSERR, "%s", name);
+ rval = 1;
+ }
+
+ if (fclose(fp)) {
+ msgq(sp, M_SYSERR, "%s", name);
+ return (1);
+ }
+
+ if (rval)
+ return (1);
+
+ /* Return the number of lines read in. */
+ if (nlinesp != NULL)
+ *nlinesp = lcnt;
+
+ if (success_msg)
+ msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters",
+ INTERRUPTED(sp) ? "Interrupted read: " : "",
+ name, lcnt, lcnt == 1 ? "" : "s", ccnt);
+
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_screen.c b/usr.bin/vi/ex/ex_screen.c
new file mode 100644
index 0000000..179ed07
--- /dev/null
+++ b/usr.bin/vi/ex/ex_screen.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_screen.c 8.13 (Berkeley) 6/27/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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, cmdp->argc));
+}
+
+/*
+ * 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 [+-]rows
+ * Change the screen size.
+ */
+int
+ex_resize(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum adjust adj;
+
+ if (!F_ISSET(cmdp, E_COUNT)) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+ if (F_ISSET(cmdp, E_COUNT_NEG))
+ adj = A_DECREASE;
+ else if (F_ISSET(cmdp, E_COUNT_POS))
+ adj = A_INCREASE;
+ else
+ adj = A_SET;
+ return (sp->s_rabs(sp, cmdp->count, adj));
+}
+
+/*
+ * 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(tsp->frp->name) + 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", tsp->frp->name);
+ ++cnt;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_script.c b/usr.bin/vi/ex/ex_script.c
new file mode 100644
index 0000000..59586f1
--- /dev/null
+++ b/usr.bin/vi/ex/ex_script.c
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_script.c 8.17 (Berkeley) 7/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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.
+ */
+ SIGBLOCK(sp->gp);
+ switch (sc->sh_pid = fork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ 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. */
+ sig_end();
+
+ /*
+ * 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: /* Parent. */
+ SIGUNBLOCK(sp->gp);
+ 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;
+ CHAR_T *endp, *p, *t, buf[1024];
+ SCRIPT *sc;
+ fd_set fdset;
+ recno_t lline;
+ size_t llen, len;
+ u_int value;
+ int nr;
+
+ 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 = 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;
+ CHAR_T *endp, *p, *t;
+ EXF *ep;
+ SCRIPT *sc;
+ recno_t lno;
+ size_t blen, len, tlen;
+ u_int value;
+ int nr, rval;
+ char *bp;
+
+ /* 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 = 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/ex/ex_set.c b/usr.bin/vi/ex/ex_set.c
new file mode 100644
index 0000000..1ad19f7
--- /dev/null
+++ b/usr.bin/vi/ex/ex_set.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_set.c 8.4 (Berkeley) 7/22/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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->cmd->usage, cmdp->argv);
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_shell.c b/usr.bin/vi/ex/ex_shell.c
new file mode 100644
index 0000000..bcbb97d
--- /dev/null
+++ b/usr.bin/vi/ex/ex_shell.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shell.c 8.24 (Berkeley) 8/5/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+{
+ const char *name;
+ pid_t pid;
+ int rval, teardown;
+
+ /* Clear the rest of the screen. */
+ if (sp->s_clear(sp))
+ return (1);
+
+ /* Save ex/vi terminal settings, and restore the original ones. */
+ teardown = !ex_sleave(sp);
+
+ /*
+ * Flush waiting messages (autowrite, for example) so the output
+ * matches historic practice.
+ */
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Put out various messages. */
+ if (p1 != NULL)
+ (void)write(STDOUT_FILENO, p1, strlen(p1));
+ if (p2 != NULL)
+ (void)write(STDOUT_FILENO, p2, strlen(p2));
+
+ SIGBLOCK(sp->gp);
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+ rval = 1;
+ break;
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ 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. */
+ SIGUNBLOCK(sp->gp);
+
+ rval = proc_wait(sp, (long)pid, cmd, 0);
+ break;
+ }
+
+ /* Restore ex/vi terminal settings. */
+ if (teardown)
+ ex_rleave(sp);
+
+ /*
+ * XXX
+ * Stat of the tty structures (see ex_sleave, ex_rleave) 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/ex/ex_shift.c b/usr.bin/vi/ex/ex_shift.c
new file mode 100644
index 0000000..41cb557
--- /dev/null
+++ b/usr.bin/vi/ex/ex_shift.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shift.c 8.14 (Berkeley) 3/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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/ex/ex_source.c b/usr.bin/vi/ex/ex_source.c
new file mode 100644
index 0000000..70b85b9
--- /dev/null
+++ b/usr.bin/vi/ex/ex_source.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_source.c 8.5 (Berkeley) 8/8/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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, 0));
+}
diff --git a/usr.bin/vi/ex/ex_stop.c b/usr.bin/vi/ex/ex_stop.c
new file mode 100644
index 0000000..3487bbb
--- /dev/null
+++ b/usr.bin/vi/ex/ex_stop.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_stop.c 8.7 (Berkeley) 8/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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(cmdp, E_FORCE) &&
+ F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) &&
+ file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/ex/ex_subst.c b/usr.bin/vi/ex/ex_subst.c
new file mode 100644
index 0000000..54b6ee8
--- /dev/null
+++ b/usr.bin/vi/ex/ex_subst.c
@@ -0,0 +1,1001 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_subst.c 8.57 (Berkeley) 8/7/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */
+#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */
+
+static __inline int regsub __P((SCR *, char *,
+ char **, size_t *, size_t *, regmatch_t [10]));
+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));
+
+ /*
+ * !!!
+ * The full-blown substitute command reset the remembered
+ * state of the 'c' and 'g' suffices.
+ */
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * Get the pattern string, toss 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] == '\\')
+ if (p[1] == delim)
+ ++p;
+ else if (p[1] == '\\')
+ *t++ = *p++;
+ *t++ = *p++;
+ }
+
+ /*
+ * If the pattern string is empty, use the last RE (not just the
+ * last substitution RE).
+ */
+ if (*ptrn == '\0') {
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression");
+ return (1);
+ }
+ re = &sp->sre;
+ 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) matches the
+ * entire RE. No handling of & is required here, it's done by
+ * regsub().
+ *
+ * The special character ~ (\~ if O_MAGIC not set) inserts the
+ * previous replacement string into this replacement string.
+ * Count ~'s to figure out how much space we need. We could
+ * special case nonexistent last patterns or whether or not
+ * O_MAGIC is set, but it's probably not worth the effort.
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escape character if it escapes a delimiter or
+ * if O_MAGIC is set and it escapes a tilde.
+ *
+ * !!!
+ * If the entire replacement pattern is "%", then use the last
+ * replacement pattern. This semantic was added to vi in System
+ * V and then percolated elsewhere, presumably around the time
+ * that it was added to their version of ed(1).
+ */
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ sp->repl = NULL;
+ sp->repl_len = 0;
+ } else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim))
+ p += p[1] == delim ? 2 : 1;
+ else {
+ for (rep = p, len = 0;
+ p[0] != '\0' && p[0] != delim; ++p, ++len)
+ if (p[0] == '~')
+ len += sp->repl_len;
+ GET_SPACE_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] == delim)
+ ++p;
+ else if (p[1] == '\\') {
+ *t++ = *p++;
+ ++len;
+ } else if (p[1] == '~') {
+ ++p;
+ if (!O_ISSET(sp, O_MAGIC))
+ goto tilde;
+ }
+ } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
+tilde: ++p;
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ len += sp->repl_len;
+ continue;
+ }
+ *t++ = *p++;
+ ++len;
+ }
+ if ((sp->repl_len = len) != 0) {
+ if (sp->repl != NULL)
+ free(sp->repl);
+ if ((sp->repl = malloc(len)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ memmove(sp->repl, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+ }
+ 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;
+{
+ MARK from, to;
+ recno_t elno, lno;
+ regmatch_t match[10];
+ size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset;
+ int cflag, lflag, nflag, pflag, rflag;
+ int didsub, do_eol_match, eflags, empty_ok, eval;
+ int linechanged, matched, quit, rval;
+ char *bp, *lb;
+
+ /*
+ * !!!
+ * Historically, the 'g' and 'c' suffices were always toggled as flags,
+ * so ":s/A/B/" was the same as ":s/A/B/ccgg". If O_EDCOMPATIBLE was
+ * not set, they were initialized to 0 for all substitute commands. If
+ * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
+ * specified substitute/replacement patterns (see ex_substitute()).
+ */
+ if (!O_ISSET(sp, O_EDCOMPATIBLE))
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
+ * it only displayed the last change. I'd disallow them, but they are
+ * useful in combination with the [v]global commands. In the current
+ * model the problem is combining them with the 'c' flag -- the screen
+ * would have to flip back and forth between the confirm screen and the
+ * ex print screen, which would be pretty awful. We do display all
+ * changes, though, for what that's worth.
+ *
+ * !!!
+ * Historic vi was fairly strict about the order of "options", the
+ * count, and "flags". I'm somewhat fuzzy on the difference between
+ * options and flags, anyway, so this is a simpler approach, and we
+ * just take it them in whatever order the user gives them. (The ex
+ * usage statement doesn't reflect this.)
+ */
+ cflag = lflag = nflag = pflag = rflag = 0;
+ for (lno = OOBLNO; *s != '\0'; ++s)
+ switch (*s) {
+ case ' ':
+ case '\t':
+ continue;
+ case '+':
+ ++cmdp->flagoff;
+ break;
+ case '-':
+ --cmdp->flagoff;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (lno != OOBLNO)
+ goto usage;
+ errno = 0;
+ 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':
+ sp->c_suffix = !sp->c_suffix;
+ break;
+ case 'g':
+ sp->g_suffix = !sp->g_suffix;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ if (LF_ISSET(SUB_FIRST)) {
+ msgq(sp, M_ERR,
+ "Regular expression specified; r flag meaningless");
+ return (1);
+ }
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR,
+ "No previous regular expression");
+ return (1);
+ }
+ rflag = 1;
+ re = &sp->sre;
+ 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) && sp->c_suffix && (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);
+ }
+
+ /*
+ * 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 (INTERRUPTED(sp)) {
+ 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);
+ goto ret1;
+ }
+
+ /*
+ * Make a local copy if doing confirmation -- when calling
+ * the confirm routine we're likely to lose the cached copy.
+ */
+ if (sp->c_suffix) {
+ if (bp == NULL) {
+ GET_SPACE_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, match[0].rm_so is the offset of the start
+ * of the match from the start of the search, and offset
+ * is the offset of the start of the last search.
+ */
+nextmatch: match[0].rm_so = 0;
+ match[0].rm_eo = len;
+
+ /* Get the next match. */
+ eval = regexec(re, (char *)s + offset, 10, match, eflags);
+
+ /*
+ * There wasn't a match or if there was an error, deal with
+ * it. If there was a previous match in this line, resolve
+ * the changes into the database. Otherwise, just move on.
+ */
+ if (eval == REG_NOMATCH)
+ goto endmatch;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ goto 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 && match[0].rm_so == 0 && 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 (sp->c_suffix) {
+ /*
+ * 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 = match[0].rm_so + offset;
+ to.cno = 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, 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, match[0].rm_so);
+
+ /* Substitute the matching bytes. */
+ didsub = 1;
+ if (regsub(sp, s + offset, &lb, &lbclen, &lblen, match))
+ goto ret1;
+
+ /* Set the change flag so we know this line was modified. */
+ linechanged = 1;
+
+ /* Move past the matched bytes. */
+skip: offset += match[0].rm_eo;
+ len -= match[0].rm_eo;
+
+ /* A match cannot be followed by an empty pattern. */
+ empty_ok = 0;
+
+ /*
+ * If doing a global change with confirmation, we have to
+ * update the screen. The basic idea is to store the line
+ * so the screen update routines can find it, and restart.
+ */
+ if (didsub && sp->c_suffix && sp->g_suffix) {
+ /*
+ * The new search offset will be the end of the
+ * modified line.
+ */
+ saved_offset = lbclen;
+
+ /* Copy the rest of the line. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* 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 (sp->g_suffix && do_eol_match) {
+ if (len == 0) {
+ do_eol_match = 0;
+ eflags |= REG_NOTEOL;
+ }
+ goto nextmatch;
+ }
+
+endmatch: if (!linechanged)
+ continue;
+
+ /* Copy any remaining bytes into the build buffer. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* 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. */
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+
+ /*
+ * !!!
+ * Display as necessary. Historic practice is to only
+ * display the last line of a line split into multiple
+ * lines.
+ */
+ if (lflag || nflag || pflag) {
+ from.lno = to.lno = lno;
+ from.cno = to.cno = 0;
+ if (lflag)
+ 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 (!sp->c_suffix)
+ sp->lno = lno;
+
+ /*
+ * !!!
+ * Move the cursor to the last line changed.
+ */
+ if (!sp->c_suffix)
+ sp->lno = lno;
+ }
+
+ /*
+ * !!!
+ * Move the cursor to the first non-blank of the last line change.
+ *
+ * XXX
+ * This is NOT backward compatible with historic vi, which always
+ * moved to the last line actually changed.
+ */
+ if (!sp->c_suffix) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+
+ /*
+ * If not in a global command, and nothing matched, say so.
+ * Else, if none of the lines displayed, put something up.
+ */
+ 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;
+ }
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ if (lb != NULL)
+ free(lb);
+ return (rval);
+}
+
+/*
+ * regsub --
+ * Do the substitution for a regular expression.
+ */
+static __inline int
+regsub(sp, ip, lbp, lbclenp, lblenp, match)
+ SCR *sp;
+ char *ip; /* Input line. */
+ char **lbp;
+ size_t *lbclenp, *lblenp;
+ regmatch_t match[10];
+{
+ enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;
+ size_t lbclen, lblen; /* Local copies. */
+ size_t mlen; /* Match length. */
+ size_t rpl; /* Remaining replacement length. */
+ char *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 = 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 '&':
+ ++rp;
+ if (!O_ISSET(sp, O_MAGIC)) {
+ no = 0;
+ goto subzero;
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ no = *rp++ - '0';
+subzero: if (match[no].rm_so == -1 ||
+ match[no].rm_eo == -1)
+ break;
+ mlen = match[no].rm_eo - match[no].rm_so;
+ for (t = ip + match[no].rm_so; mlen--; ++t)
+ 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);
+}
diff --git a/usr.bin/vi/ex/ex_tag.c b/usr.bin/vi/ex/ex_tag.c
new file mode 100644
index 0000000..0bb8c3e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_tag.c
@@ -0,0 +1,905 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * 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.43 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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, name)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, 0))
+ return (1);
+
+ /*
+ * !!!
+ * The historic tags file format (from a long, long time ago...)
+ * used a line number, not a search string. I got complaints, so
+ * people are 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);
+}
+
+/* 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_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 the (possibly new) FREF structure. */
+ if ((frp = file_add(sp, name)) == NULL)
+ goto err;
+
+ if (sp->frp == frp)
+ which = TC_CURRENT;
+ else {
+ if (file_m1(sp, sp->ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ goto err;
+ which = TC_CHANGE;
+ }
+
+ /*
+ * 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 files. */
+ if (which == TC_CHANGE && file_init(sp, frp, NULL, 0)) {
+ if (tp != NULL)
+ FREETAG(tp);
+ /* Handle special, first-tag case. */
+ if (exp->tagq.tqh_first->q.tqe_next == NULL)
+ TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q);
+err: free(tag);
+ 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;
+ 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);
+}
+
+/*
+ * 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;
+ long off;
+ size_t arglen;
+ 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. */
+ ntp = exp->tagq.tqh_first;
+ break;
+ case 1: /* Name or number. */
+ arg = cmdp->argv[0]->bp;
+ off = strtol(arg, &p, 10);
+ if (*p == '\0') {
+ if (off < 1)
+ return (0);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL && --off > 1; tp = tp->q.tqe_next);
+ if (tp == NULL) {
+ msgq(sp, M_ERR,
+"Less than %s entries on the tags stack; use :display to see the tags stack",
+ arg);
+ return (1);
+ }
+ ntp = tp;
+ } else {
+ arglen = strlen(arg);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL; ntp = tp, 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))
+ 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);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor from the saved TAG information. */
+ tp = ntp->q.tqe_next;
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ if (file_m1(sp, ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+ 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);
+ }
+
+ /* Pop entries off the queue up to ntp. */
+ for (;;) {
+ tp = exp->tagq.tqh_first;
+ FREETAG(tp);
+ if (tp == ntp)
+ break;
+ }
+
+ /* If returning to the first tag, the stack is now empty. */
+ if (exp->tagq.tqh_first->q.tqe_next == NULL)
+ TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q);
+ 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;
+
+ /* Find oldest saved information. */
+ exp = EXP(sp);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL && tp->q.tqe_next != NULL; tp = tp->q.tqe_next);
+ if (tp == NULL) {
+ msgq(sp, M_INFO, "The tags stack is empty");
+ return (1);
+ }
+
+ /* If not switching files, it's easy; else do the work. */
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ if (file_m1(sp, sp->ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+ 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);
+ }
+
+ /* Empty out the queue. */
+ while ((tp = exp->tagq.tqh_first) != NULL)
+ FREETAG(tp);
+ 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);
+ if (exp->tlast != NULL)
+ free(exp->tlast);
+ 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;
+{
+ struct stat sb;
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ size_t plen, slen, tlen;
+ int dne;
+ char *p, pbuf[MAXPATHLEN];
+
+ /*
+ * 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);
+ else
+ if (p != NULL)
+ break;
+ }
+
+ 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 file name, and searchp
+ * points to the search string. All three are nul-terminated.
+ */
+ 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);
+ }
+
+ /*
+ * !!!
+ * If the tag file path is a relative path, see if it exists. If it
+ * doesn't, look relative to the tags file path. It's okay for a tag
+ * file to not exist, and, historically, vi simply displayed a "new"
+ * file. However, if the path exists relative to the tag file, it's
+ * pretty clear what's happening, so we may as well do it right.
+ */
+ if ((*filep)[0] != '/'
+ && stat(*filep, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
+ *p = '\0';
+ plen = snprintf(pbuf, sizeof(pbuf), "%s/%s", tfp->name, *filep);
+ *p = '/';
+ if (stat(pbuf, &sb) == 0) {
+ slen = strlen(*searchp);
+ tlen = strlen(*tagp);
+ MALLOC(sp, p, char *, plen + slen + tlen + 5);
+ if (p != NULL) {
+ memmove(p, *tagp, tlen);
+ free(*tagp);
+ *tagp = p;
+ *(p += tlen) = '\0';
+ memmove(++p, pbuf, plen);
+ *filep = p;
+ *(p += plen) = '\0';
+ memmove(++p, *searchp, slen);
+ *searchp = p;
+ *(p += slen) = '\0';
+ }
+ }
+ }
+ 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);
+ case LESS: /* No such string. */
+ return (NULL);
+ 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/ex/ex_undo.c b/usr.bin/vi/ex/ex_undo.c
new file mode 100644
index 0000000..4331696
--- /dev/null
+++ b/usr.bin/vi/ex/ex_undo.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_undo.c 8.7 (Berkeley) 8/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_undo -- u
+ * Undo the last change.
+ */
+int
+ex_undo(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK m;
+
+ /*
+ * !!!
+ * Historic undo always set the previous context mark.
+ */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (mark_set(sp, ep, ABSMARK1, &m, 1))
+ return (1);
+
+ /*
+ * !!!
+ * 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/ex/ex_usage.c b/usr.bin/vi/ex/ex_usage.c
new file mode 100644
index 0000000..b6031b5
--- /dev/null
+++ b/usr.bin/vi/ex/ex_usage.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_usage.c 8.19 (Berkeley) 8/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../vi/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;
+ char *name;
+
+ 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:
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (cp = cmds; cp->name != NULL; ++cp) {
+ /* The ^D command has an unprintable name. */
+ if (cp == &cmds[C_SCROLL])
+ name = "^D";
+ else
+ name = cp->name;
+ (void)ex_printf(EXCOOKIE,
+ "%*s: %s\n", MAXCMDNAMELEN, 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:
+ if (cmdp->argv[0]->len != 1) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (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;
+
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ kp = &vikeys[key];
+
+ if (kp->func == NULL)
+nokey: (void)ex_printf(EXCOOKIE,
+ "The %s key has no current meaning",
+ KEY_NAME(sp, key));
+ else
+ (void)ex_printf(EXCOOKIE,
+ " Key:%s%s\nUsage: %s\n",
+ isblank(*kp->help) ? "" : " ", kp->help, kp->usage);
+ break;
+ case 0:
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (key = 0; key <= MAXVIKEY; ++key) {
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ 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/ex/ex_util.c b/usr.bin/vi/ex/ex_util.c
new file mode 100644
index 0000000..e88c28f
--- /dev/null
+++ b/usr.bin/vi/ex/ex_util.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_util.c 8.12 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 (errno = 0, off = 0, p = exp->ibp;;) {
+ if (off >= exp->ibp_len) {
+ BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1);
+ p = exp->ibp + off;
+ }
+ if ((ch = getc(fp)) == EOF && !feof(fp)) {
+ if (errno == EINTR) {
+ errno = 0;
+ clearerr(fp);
+ continue;
+ }
+ return (1);
+ }
+ if (ch == EOF || ch == '\n') {
+ if (ch == EOF && !off)
+ return (1);
+ *lenp = off;
+ return (0);
+ }
+ *p++ = ch;
+ ++off;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * ex_sleave --
+ * Save the terminal/signal state, screen modification time.
+ * Specific to ex/filter.c and ex/ex_shell.c.
+ */
+int
+ex_sleave(sp)
+ SCR *sp;
+{
+ struct stat sb;
+ EX_PRIVATE *exp;
+
+ /* Ignore sessions not using tty's. */
+ if (!F_ISSET(sp->gp, G_STDIN_TTY))
+ return (1);
+
+ exp = EXP(sp);
+ if (tcgetattr(STDIN_FILENO, &exp->leave_term)) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ return (1);
+ }
+ if (tcsetattr(STDIN_FILENO,
+ TCSANOW | TCSASOFT, &sp->gp->original_termios)) {
+ msgq(sp, M_SYSERR, "tcsetattr");
+ return (1);
+ }
+ /*
+ * 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.
+ */
+ if (fstat(STDIN_FILENO, &sb)) {
+ msgq(sp, M_SYSERR, "stat: stdin");
+ exp->leave_atime = exp->leave_mtime = 0;
+ } else {
+ exp->leave_atime = sb.st_atime;
+ exp->leave_mtime = sb.st_mtime;
+ }
+ return (0);
+}
+
+/*
+ * ex_rleave --
+ * Return the terminal/signal state, not screen modification time.
+ * Specific to ex/filter.c and ex/ex_shell.c.
+ */
+void
+ex_rleave(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ struct stat sb;
+
+ exp = EXP(sp);
+
+ /* Restore the terminal modes. */
+ if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &exp->leave_term))
+ msgq(sp, M_SYSERR, "tcsetattr");
+
+ /* If the terminal was used, refresh the screen. */
+ if (fstat(STDIN_FILENO, &sb) || exp->leave_atime == 0 ||
+ exp->leave_atime != sb.st_atime || exp->leave_mtime != sb.st_mtime)
+ F_SET(sp, S_REFRESH);
+}
+
+/*
+ * ex_ncheck --
+ * Check for more files to edit.
+ */
+int
+ex_ncheck(sp, force)
+ SCR *sp;
+ int force;
+{
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ */
+ if (!force && sp->ccnt != sp->q_ccnt + 1 &&
+ sp->cargv != NULL && sp->cargv[1] != NULL) {
+ sp->q_ccnt = sp->ccnt;
+ msgq(sp, M_ERR,
+ "More files to edit; use n[ext] to go to the next file, q[uit]! to quit");
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_version.c b/usr.bin/vi/ex/ex_version.c
new file mode 100644
index 0000000..31c2cb7
--- /dev/null
+++ b/usr.bin/vi/ex/ex_version.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_version.c 8.62 (Berkeley) 8/15/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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 = 776975285;
+
+ (void)ex_printf(EXCOOKIE,
+"Version 1.32, %sThe CSRG, University of California, Berkeley.\n",
+ ctime(&then));
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_visual.c b/usr.bin/vi/ex/ex_visual.c
new file mode 100644
index 0000000..cfb0c13
--- /dev/null
+++ b/usr.bin/vi/ex/ex_visual.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_visual.c 8.13 (Berkeley) 7/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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:
+ pos = '+';
+ break;
+ default:
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = 0;
+ F_SET(sp->frp, FR_CURSORSET | FR_FNONBLANK);
+ goto nopush;
+ }
+
+ 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, 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. */
+nopush: F_CLR(sp, S_SCREENS);
+ F_SET(sp, sp->saved_vi_mode);
+
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_write.c b/usr.bin/vi/ex/ex_write.c
new file mode 100644
index 0000000..3dbb6e8
--- /dev/null
+++ b/usr.bin/vi/ex/ex_write.c
@@ -0,0 +1,327 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_write.c 8.36 (Berkeley) 8/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {WN, WQ, WRITE, XIT};
+
+static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_wn -- :wn[!] [>>] [file]
+ * Write to a file and switch to the next one.
+ */
+int
+ex_wn(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (exwr(sp, ep, cmdp, WN))
+ return (1);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ /* The file name isn't a new file to edit. */
+ cmdp->argc = 0;
+
+ return (ex_next(sp, ep, cmdp));
+}
+
+/*
+ * ex_wq -- :wq[!] [>>] [file]
+ * Write to a file and quit.
+ */
+int
+ex_wq(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ if (exwr(sp, ep, cmdp, WQ))
+ return (1);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ if (ex_ncheck(sp, force))
+ 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);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ if (ex_ncheck(sp, force))
+ 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;
+{
+ struct stat sb;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ recno_t fline, tline, lcnt;
+ size_t len;
+ int sv_errno;
+ char *p;
+
+ fline = fm->lno;
+ tline = tm->lno;
+
+ if (nlno != NULL) {
+ *nch = 0;
+ *nlno = 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."
+ */
+ ccnt = 0;
+ lcnt = 0;
+ if (tline != 0) {
+ for (; fline <= tline; ++fline, ++lcnt) {
+ /* Caller has to provide any interrupt message. */
+ if (INTERRUPTED(sp))
+ break;
+ 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 it's a regular file, sync it so that NFS is forced to flush. */
+ if (!fstat(fileno(fp), &sb) &&
+ S_ISREG(sb.st_mode) && fsync(fileno(fp))) {
+ sv_errno = errno;
+ (void)fclose(fp);
+ errno = sv_errno;
+ goto err;
+ }
+ if (fclose(fp))
+ goto err;
+ if (nlno != NULL) {
+ *nch = ccnt;
+ *nlno = lcnt;
+ }
+ return (0);
+
+err: if (!F_ISSET(ep, F_MULTILOCK))
+ msgq(sp, M_SYSERR, name);
+ return (1);
+}
diff --git a/usr.bin/vi/ex/ex_yank.c b/usr.bin/vi/ex/ex_yank.c
new file mode 100644
index 0000000..7f5d280
--- /dev/null
+++ b/usr.bin/vi/ex/ex_yank.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_yank.c 8.5 (Berkeley) 5/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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,
+ F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE));
+}
diff --git a/usr.bin/vi/ex/ex_z.c b/usr.bin/vi/ex/ex_z.c
new file mode 100644
index 0000000..a5917b3
--- /dev/null
+++ b/usr.bin/vi/ex/ex_z.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_z.c 8.6 (Berkeley) 7/23/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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;
+{
+ MARK abs;
+ 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;
+
+ /*
+ * !!!
+ * Historically, z. set the absolute cursor mark.
+ */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ (void)mark_set(sp, ep, ABSMARK1, &abs, 1);
+ 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/ex/excmd.awk b/usr.bin/vi/ex/excmd.awk
new file mode 100644
index 0000000..2890220
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.awk
@@ -0,0 +1,6 @@
+# @(#)excmd.awk 8.1 (Berkeley) 4/17/94
+
+/^\/\* C_[0-9A-Z_]* \*\/$/ {
+ printf("#define %s %d\n", $2, cnt++);
+ next;
+}
diff --git a/usr.bin/vi/ex/excmd.c b/usr.bin/vi/ex/excmd.c
new file mode 100644
index 0000000..3845768
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)excmd.c 8.58 (Berkeley) 8/9/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.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_SCROLL */
+ {"\004", ex_pr, E_ADDR2|E_NORC,
+ "",
+ "^D",
+ "scroll lines"},
+/* 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,
+ "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|E_ZERO|E_ZERODEF,
+ "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"},
+/*
+ * !!!
+ * Adding new commands starting with 'd' may break the delete command code
+ * in ex_cmd() (the ex parser). Read through the comments there, first.
+ */
+/* C_DELETE */
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "bca1",
+ "[line [,line]] d[elete][flags] [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][!] [;/]RE[;/] [commands]",
+ "execute a global command on lines matching an RE"},
+/* 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,
+ "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][!] [+cmd] [file ...]",
+ "edit (and optionally specify) the next file"},
+/* C_NUMBER */
+ {"number", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "ca1",
+ "[line [,line]] nu[mber] [count] [l]",
+ "change display to number lines"},
+/* C_OPEN */
+ {"open", ex_open, E_ADDR1,
+ "s",
+ "[line] o[pen] [/RE/] [flags]",
+ "enter \"open\" mode (not implemented)"},
+/* C_PRINT */
+ {"print", ex_pr, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "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|E_NORC,
+ "!",
+ "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_RECOVER */
+ {"recover", ex_recover, E_NOGLOBAL|E_NORC,
+ "!f1r",
+ "recover[!] file",
+ "recover a saved file"},
+/* C_RESIZE */
+ {"resize", ex_resize, E_NOGLOBAL|E_NORC,
+ "c+",
+ "resize [+-]rows",
+ "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] [[/;]RE[/;]/repl[/;] [cgr] [count] [#lp]]",
+ "substitute on lines matching an RE"},
+/* 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]",
+ "copy 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_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] [;/]RE[;/] [commands]",
+ "execute a global command on lines NOT matching an RE"},
+/* 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_WN */
+ {"wn", ex_wn, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] wn[!] [>>] [file]",
+ "write the file and switch to the next 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/ex/excmd.h.stub b/usr.bin/vi/ex/excmd.h.stub
new file mode 100644
index 0000000..3b4051b
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.h.stub
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)excmd.h.stub 8.73 (Berkeley) 8/9/94
+ */
+
+#define PROMPTCHAR ':' /* Prompt character. */
+
+/* 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_COUNT_NEG 0x0000100 /* Count was signed negative. */
+#define E_COUNT_POS 0x0000200 /* Count was signed positive. */
+#define E_FORCE 0x0000400 /* ! */
+
+#define E_F_CARAT 0x0000800 /* ^ flag. */
+#define E_F_DASH 0x0001000 /* - flag. */
+#define E_F_DOT 0x0002000 /* . flag. */
+#define E_F_EQUAL 0x0004000 /* = flag. */
+#define E_F_HASH 0x0008000 /* # flag. */
+#define E_F_LIST 0x0010000 /* l flag. */
+#define E_F_PLUS 0x0020000 /* + flag. */
+#define E_F_PRINT 0x0040000 /* p flag. */
+
+#define E_F_PRCLEAR 0x0080000 /* Clear the print (#, l, p) flags. */
+#define E_MODIFY 0x0100000 /* File name expansion modified arg. */
+#define E_NOGLOBAL 0x0200000 /* Not in a global. */
+#define E_NOPERM 0x0400000 /* Permission denied for now. */
+#define E_NORC 0x0800000 /* Not from a .exrc or EXINIT. */
+#define E_ZERO 0x1000000 /* 0 is a legal addr1. */
+#define E_ZERODEF 0x2000000 /* 0 is default addr1 of empty files. */
+ u_int32_t flags;
+ char *syntax; /* Syntax script. */
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+} EXCMDLIST;
+#define MAXCMDNAMELEN 12 /* Longest command name. */
+extern EXCMDLIST const cmds[]; /* List of ex commands. */
+
+/*
+ * Structure passed around to functions implementing ex commands.
+ * There are several commands in vi that build one of these and
+ * call ex directly. See vi/v_ex.c for details.
+ */
+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. */
+ long flagoff; /* Signed, flag offset parsed by command. */
+ 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_int32_t 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. */
+};
+
+/* Cd paths. */
+typedef struct _cdpath CDPATH;
+struct _cdpath {
+ TAILQ_ENTRY(_cdpath) q; /* Linked list of cd paths. */
+ char *path; /* Path. */
+};
+
+/* 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. */
+
+ u_int32_t fdef; /* Default command flags. */
+
+ CHAR_T *lastbcomm; /* Last bang command. */
+
+ struct termios leave_term; /* ex_[sr]leave tty state. */
+ /* XXX: Should be struct timespec's, but time_t is more portable. */
+ time_t leave_atime; /* ex_[sr]leave old access time. */
+ time_t leave_mtime; /* ex_[sr]leave old mod time. */
+
+ TAILQ_HEAD(_tagh, _tag) tagq; /* Tag list (stack). */
+ TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag file list. */
+ char *tlast; /* Saved last tag. */
+
+ TAILQ_HEAD(_cdh, _cdpath) cdq; /* Cd path list. */
+
+ /* Linked list of ranges. */
+ CIRCLEQ_HEAD(_rangeh, _range) rangeq;
+ recno_t range_lno; /* Range set line number. */
+
+#define EX_ABSMARK 0x01 /* Set the absolute mark. */
+#define EX_AUTOPRINT 0x02 /* Autoprint flag. */
+ u_int8_t flags;
+} EX_PRIVATE;
+#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private))
+
+/*
+ * !!!
+ * Historically, .exrc files and EXINIT variables could only use ^V
+ * as an escape character, neither ^Q or a user specified character
+ * worked. We enforce that here, just in case someone depends on it.
+ */
+#define IS_ESCAPE(sp, ch) \
+ (F_ISSET(sp, S_VLITONLY) ? \
+ (ch) == CH_LITERAL : KEY_VAL(sp, ch) == K_VLNEXT)
+
+/*
+ * 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));
+int ex_cmd __P((SCR *, EXF *, char *, size_t, int));
+int ex_cdalloc __P((SCR *, char *));
+int ex_cdfree __P((SCR *));
+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));
+int ex_init __P((SCR *, EXF *));
+int ex_is_abbrev __P((char *, size_t));
+int ex_is_unmap __P((char *, size_t));
+int ex_ldisplay __P((SCR *, CHAR_T *, size_t, size_t, u_int));
+int ex_ncheck __P((SCR *, int));
+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 *));
+void ex_rleave __P((SCR *));
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_sdisplay __P((SCR *, EXF *));
+int ex_sleave __P((SCR *));
+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 *));
+
+int abbr_save __P((SCR *, FILE *));
+int map_save __P((SCR *, FILE *));
+
+#define EXPROTO(name) int name __P((SCR *, EXF *, EXCMDARG *))
+EXPROTO(ex_abbr);
+EXPROTO(ex_append);
+EXPROTO(ex_args);
+EXPROTO(ex_at);
+EXPROTO(ex_bang);
+EXPROTO(ex_bg);
+EXPROTO(ex_cd);
+EXPROTO(ex_change);
+EXPROTO(ex_color);
+EXPROTO(ex_copy);
+EXPROTO(ex_debug);
+EXPROTO(ex_delete);
+EXPROTO(ex_digraph);
+EXPROTO(ex_display);
+EXPROTO(ex_edit);
+EXPROTO(ex_equal);
+EXPROTO(ex_fg);
+EXPROTO(ex_file);
+EXPROTO(ex_global);
+EXPROTO(ex_help);
+EXPROTO(ex_insert);
+EXPROTO(ex_join);
+EXPROTO(ex_list);
+EXPROTO(ex_map);
+EXPROTO(ex_mark);
+EXPROTO(ex_mkexrc);
+EXPROTO(ex_move);
+EXPROTO(ex_next);
+EXPROTO(ex_number);
+EXPROTO(ex_open);
+EXPROTO(ex_pr);
+EXPROTO(ex_preserve);
+EXPROTO(ex_prev);
+EXPROTO(ex_put);
+EXPROTO(ex_quit);
+EXPROTO(ex_read);
+EXPROTO(ex_recover);
+EXPROTO(ex_resize);
+EXPROTO(ex_rew);
+EXPROTO(ex_script);
+EXPROTO(ex_set);
+EXPROTO(ex_shell);
+EXPROTO(ex_shiftl);
+EXPROTO(ex_shiftr);
+EXPROTO(ex_source);
+EXPROTO(ex_split);
+EXPROTO(ex_stop);
+EXPROTO(ex_subagain);
+EXPROTO(ex_substitute);
+EXPROTO(ex_subtilde);
+EXPROTO(ex_tagpop);
+EXPROTO(ex_tagpush);
+EXPROTO(ex_tagtop);
+EXPROTO(ex_unabbr);
+EXPROTO(ex_undo);
+EXPROTO(ex_unmap);
+EXPROTO(ex_usage);
+EXPROTO(ex_validate);
+EXPROTO(ex_version);
+EXPROTO(ex_vglobal);
+EXPROTO(ex_visual);
+EXPROTO(ex_viusage);
+EXPROTO(ex_wn);
+EXPROTO(ex_wq);
+EXPROTO(ex_write);
+EXPROTO(ex_xit);
+EXPROTO(ex_yank);
+EXPROTO(ex_z);
diff --git a/usr.bin/vi/ex/filter.c b/usr.bin/vi/ex/filter.c
new file mode 100644
index 0000000..59a221c
--- /dev/null
+++ b/usr.bin/vi/ex/filter.c
@@ -0,0 +1,414 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)filter.c 8.43 (Berkeley) 8/7/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.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;
+{
+ FILE *ifp, *ofp;
+ pid_t parent_writer_pid, utility_pid;
+ recno_t nread;
+ int input[2], output[2], rval, teardown;
+ 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.
+ */
+ teardown = 0;
+ 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;
+ }
+
+ /* 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".
+ */
+ teardown = ftype != FILTER_WRITE && !ex_sleave(sp);
+
+ /* Fork off the utility process. */
+ SIGBLOCK(sp->gp);
+ switch (utility_pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+err: if (input[0] != -1)
+ (void)close(input[0]);
+ if (input[1] != -1)
+ (void)close(input[1]);
+ if (ofp != NULL)
+ (void)fclose(ofp);
+ else if (output[0] != -1)
+ (void)close(output[0]);
+ if (output[1] != -1)
+ (void)close(output[1]);
+ rval = 1;
+ goto ret;
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ /*
+ * Redirect stdin from the read end of the input pipe, and
+ * redirect stdout/stderr to the write end of the output pipe.
+ *
+ * !!!
+ * Historically, ex only directed stdout into the input pipe,
+ * letting stderr come out on the terminal as usual. Vi did
+ * not, directing both stdout and stderr into the input pipe.
+ * We match that practice for both ex and vi for consistency.
+ */
+ (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. */
+ SIGUNBLOCK(sp->gp);
+
+ /* 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);
+
+ SIGBLOCK(sp->gp);
+ switch (parent_writer_pid = fork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "fork");
+ (void)close(input[1]);
+ (void)close(output[0]);
+ rval = 1;
+ break;
+ case 0: /* Parent-writer. */
+ /*
+ * Write the selected lines to the write end of the input
+ * pipe. This instance of ifp is closed by ex_writefp.
+ */
+ (void)close(output[0]);
+ if ((ifp = fdopen(input[1], "w")) == NULL)
+ _exit (1);
+ _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL));
+
+ /* NOTREACHED */
+ default: /* Parent-reader. */
+ SIGUNBLOCK(sp->gp);
+
+ (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 (rval == 0 && ftype == FILTER &&
+ (cut(sp, ep, NULL, fm, tm, CUT_LINEMODE) ||
+ delete(sp, ep, fm, tm, 1))) {
+ rval = 1;
+ break;
+ }
+
+ /*
+ * If the filter had no output, we may have just deleted
+ * the cursor. Don't do any real error correction, we'll
+ * try and recover later.
+ */
+ if (rp->lno > 1 && 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: if (teardown)
+ ex_rleave(sp);
+ 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. We can get interrupted
+ * by SIGALRM, just ignore it.
+ */
+ for (;;) {
+ errno = 0;
+ if (waitpid((pid_t)pid, &pstat, 0) != -1)
+ break;
+ if (errno != EINTR) {
+ msgq(sp, M_SYSERR, "wait error");
+ return (1);
+ }
+ }
+
+ /*
+ * Display the utility's exit status. Ignore SIGPIPE from the
+ * parent-writer, as that only means that the utility chose to
+ * exit before reading all of its input.
+ */
+ if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
+ for (; 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 output from a utility.
+ *
+ * !!!
+ * Historically, the characters were passed unmodified to the terminal.
+ * We use the ex print routines to make sure they're printable.
+ */
+static int
+filter_ldisplay(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ size_t len;
+
+ EX_PRIVATE *exp;
+
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (exp = EXP(sp); !ex_getline(sp, fp, &len);) {
+ if (ex_ldisplay(sp, exp->ibp, len, 0, 0))
+ break;
+ if (INTERRUPTED(sp))
+ break;
+ }
+ if (ferror(fp))
+ msgq(sp, M_SYSERR, "filter input");
+ (void)fclose(fp);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/script.h b/usr.bin/vi/ex/script.h
new file mode 100644
index 0000000..b21c63a
--- /dev/null
+++ b/usr.bin/vi/ex/script.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)script.h 8.2 (Berkeley) 4/17/94
+ */
+
+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/ex/tag.h b/usr.bin/vi/ex/tag.h
new file mode 100644
index 0000000..a9fd59d
--- /dev/null
+++ b/usr.bin/vi/ex/tag.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tag.h 8.13 (Berkeley) 7/17/94
+ */
+
+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_int8_t 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 *));
OpenPOWER on IntegriCloud