summaryrefslogtreecommitdiffstats
path: root/contrib/nvi/ex/ex_append.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/ex/ex_append.c')
-rw-r--r--contrib/nvi/ex/ex_append.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/contrib/nvi/ex/ex_append.c b/contrib/nvi/ex/ex_append.c
new file mode 100644
index 0000000..8d89e12
--- /dev/null
+++ b/contrib/nvi/ex/ex_append.c
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_append.c 10.30 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+enum which {APPEND, CHANGE, INSERT};
+
+static int ex_aci __P((SCR *, EXCMD *, 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.
+ *
+ * PUBLIC: int ex_append __P((SCR *, EXCMD *));
+ */
+int
+ex_append(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, cmdp, APPEND));
+}
+
+/*
+ * ex_change -- :[line[,line]] c[hange][!] [count]
+ * Change one or more lines to the input text.
+ *
+ * PUBLIC: int ex_change __P((SCR *, EXCMD *));
+ */
+int
+ex_change(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, 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.
+ *
+ * PUBLIC: int ex_insert __P((SCR *, EXCMD *));
+ */
+int
+ex_insert(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, cmdp, INSERT));
+}
+
+/*
+ * ex_aci --
+ * Append, change, insert in ex.
+ */
+static int
+ex_aci(sp, cmdp, cmd)
+ SCR *sp;
+ EXCMD *cmdp;
+ enum which cmd;
+{
+ CHAR_T *p, *t;
+ GS *gp;
+ TEXT *tp;
+ TEXTH tiq;
+ recno_t cnt, lno;
+ size_t len;
+ u_int32_t flags;
+ int need_newline;
+
+ gp = sp->gp;
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * 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 appends, inserts are the same as appends to the previous
+ * line.
+ *
+ * !!!
+ * Set the address to which we'll append. We set sp->lno to this
+ * address as well so that autoindent works correctly when get text
+ * from the user.
+ */
+ lno = cmdp->addr1.lno;
+ sp->lno = lno;
+ if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
+ --lno;
+
+ /*
+ * !!!
+ * If the file isn't empty, cut changes into the unnamed buffer.
+ */
+ if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
+ (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
+ del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
+ return (1);
+
+ /*
+ * !!!
+ * Anything that was left after the command separator becomes part
+ * of the inserted text. Apparently, it was common usage to enter:
+ *
+ * :g/pattern/append|stuff1
+ *
+ * and append the line of text "stuff1" to the lines containing the
+ * pattern. It was also historically legal to enter:
+ *
+ * :append|stuff1
+ * stuff2
+ * .
+ *
+ * and the text on the ex command line would be appended as well as
+ * the text inserted after it. There was an historic bug however,
+ * that the user had to enter *two* terminating lines (the '.' lines)
+ * to terminate text input mode, in this case. This whole thing
+ * could be taken too far, however. Entering:
+ *
+ * :append|stuff1\
+ * stuff2
+ * stuff3
+ * .
+ *
+ * i.e. mixing and matching the forms confused the historic vi, and,
+ * not only did it take two terminating lines to terminate text input
+ * mode, but the trailing backslashes were retained on the input. We
+ * match historic practice except that we discard the backslashes.
+ *
+ * Input lines specified on the ex command line lines are separated by
+ * <newline>s. If there is a trailing delimiter an empty line was
+ * inserted. There may also be a leading delimiter, which is ignored
+ * unless it's also a trailing delimiter. It is possible to encounter
+ * a termination line, i.e. a single '.', in a global command, but not
+ * necessary if the text insert command was the last of the global
+ * commands.
+ */
+ if (cmdp->save_cmdlen != 0) {
+ for (p = cmdp->save_cmd,
+ len = cmdp->save_cmdlen; len > 0; p = t) {
+ for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
+ if (t != p || len == 0) {
+ if (F_ISSET(sp, SC_EX_GLOBAL) &&
+ t - p == 1 && p[0] == '.') {
+ ++t;
+ if (len > 0)
+ --len;
+ break;
+ }
+ if (db_append(sp, 1, lno++, p, t - p))
+ return (1);
+ }
+ if (len != 0) {
+ ++t;
+ if (--len == 0 &&
+ db_append(sp, 1, lno++, "", 0))
+ return (1);
+ }
+ }
+ /*
+ * If there's any remaining text, we're in a global, and
+ * there's more command to parse.
+ *
+ * !!!
+ * We depend on the fact that non-global commands will eat the
+ * rest of the command line as text input, and before getting
+ * any text input from the user. Otherwise, we'd have to save
+ * off the command text before or during the call to the text
+ * input function below.
+ */
+ if (len != 0)
+ cmdp->save_cmd = t;
+ cmdp->save_cmdlen = len;
+ }
+
+ if (F_ISSET(sp, SC_EX_GLOBAL)) {
+ if ((sp->lno = lno) == 0 && db_exist(sp, 1))
+ sp->lno = 1;
+ return (0);
+ }
+
+ /*
+ * If not in a global command, read from the terminal.
+ *
+ * If this code is called by vi, we want to reset the terminal and use
+ * ex's line get routine. It actually works fine if we use vi'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.
+ * However, depending on the screen that we're using, that may not
+ * be possible.
+ */
+ if (F_ISSET(sp, SC_VI)) {
+ if (gp->scr_screen(sp, SC_EX)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
+ return (1);
+ }
+
+ /* If we're still in the vi screen, move out explicitly. */
+ need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
+ F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
+ if (need_newline)
+ (void)ex_puts(sp, "\n");
+
+ /*
+ * !!!
+ * Users of historical versions of vi sometimes get confused
+ * when they enter append mode, and can't seem to get out of
+ * it. Give them an informational message.
+ */
+ (void)ex_puts(sp,
+ msg_cat(sp, "273|Entering ex input mode.", NULL));
+ (void)ex_puts(sp, "\n");
+ (void)ex_fflush(sp);
+ }
+
+ /*
+ * Set input flags; the ! flag turns off autoindent for append,
+ * change and insert.
+ */
+ LF_INIT(TXT_DOTTERM | TXT_NUMBER);
+ if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
+ LF_SET(TXT_AUTOINDENT);
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+
+ /*
+ * This code can't use the common screen TEXTH structure (sp->tiq),
+ * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
+ * as we are only halfway through the text when the append code fires.
+ * Use a local structure instead. (The ex code would have to use a
+ * local structure except that we're guaranteed to finish remaining
+ * characters in the common TEXTH structure when they were inserted
+ * into the file, above.)
+ */
+ memset(&tiq, 0, sizeof(TEXTH));
+ CIRCLEQ_INIT(&tiq);
+
+ if (ex_txt(sp, &tiq, 0, flags))
+ return (1);
+
+ for (cnt = 0, tp = tiq.cqh_first;
+ tp != (TEXT *)&tiq; ++cnt, tp = tp->q.cqe_next)
+ if (db_append(sp, 1, lno++, tp->lb, tp->len))
+ return (1);
+
+ /*
+ * Set sp->lno to the final line number value (correcting for a
+ * possible 0 value) as that's historically correct for the final
+ * line value, whether or not the user entered any text.
+ */
+ if ((sp->lno = lno) == 0 && db_exist(sp, 1))
+ sp->lno = 1;
+
+ return (0);
+}
OpenPOWER on IntegriCloud