summaryrefslogtreecommitdiff
path: root/ex_vops.c
diff options
context:
space:
mode:
authorThomas Ulmer <thomasmulmer02@gmail.com>2026-02-23 16:54:28 -0800
committerThomas Ulmer <thomasmulmer02@gmail.com>2026-02-23 16:54:28 -0800
commit15bd7946cc838a3151c357e4b0bc1ab85eecda62 (patch)
tree56977cb9bfc4349f46e2c608503a298df30ca957 /ex_vops.c
add musl and vi
Diffstat (limited to 'ex_vops.c')
-rw-r--r--ex_vops.c1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/ex_vops.c b/ex_vops.c
new file mode 100644
index 0000000..f9a7aa5
--- /dev/null
+++ b/ex_vops.c
@@ -0,0 +1,1071 @@
+/*
+ * This code contains changes by
+ * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * Redistributions of source code and documentation must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 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.
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * Neither the name of Caldera International, Inc. nor the names of
+ * other contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_vops.c 1.28 (gritter) 8/4/05";
+#endif
+#endif
+
+/* from ex_vops.c 7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * This file defines the operation sequences which interface the
+ * logical changes to the file buffer with the internal and external
+ * display representations.
+ */
+
+/*
+ * Undo.
+ *
+ * Undo is accomplished in two ways. We often for small changes in the
+ * current line know how (in terms of a change operator) how the change
+ * occurred. Thus on an intelligent terminal we can undo the operation
+ * by another such operation, using insert and delete character
+ * stuff. The pointers vU[AD][12] index the buffer vutmp when this
+ * is possible and provide the necessary information.
+ *
+ * The other case is that the change involved multiple lines or that
+ * we have moved away from the line or forgotten how the change was
+ * accomplished. In this case we do a redisplay and hope that the
+ * low level optimization routines (which don't look for winning
+ * via insert/delete character) will not lose too badly.
+ */
+char *vUA1, *vUA2;
+char *vUD1, *vUD2;
+
+void
+vUndo(void)
+{
+
+ /*
+ * Avoid UU which clobbers ability to do u.
+ */
+ if (vundkind == VCAPU || vUNDdot != dot) {
+ beep();
+ return;
+ }
+ CP(vutmp, linebuf);
+ vUD1 = linebuf; vUD2 = strend(linebuf);
+ putmk1(dot, vUNDsav);
+ getDOT();
+ vUA1 = linebuf; vUA2 = strend(linebuf);
+ vundkind = VCAPU;
+ if (state == ONEOPEN || state == HARDOPEN) {
+ vjumpto(dot, vUNDcurs, 0);
+ return;
+ }
+ vdirty(vcline, 1);
+ vsyncCL();
+ cursor = linebuf;
+ vfixcurs();
+}
+
+void
+vundo (
+ int show /* if true update the screen */
+)
+{
+ register int cnt;
+ register line *addr;
+ register char *cp;
+ char *temp = smalloc(LBSIZE);
+ bool savenote;
+ int (*OO)(int);
+ short oldhold = hold;
+
+ switch (vundkind) {
+
+ case VMANYINS:
+ wcursor = 0;
+ addr1 = undap1;
+ addr2 = undap2 - 1;
+ vsave();
+ YANKreg('1');
+ notecnt = 0;
+ /* fall into ... */
+
+ case VMANY:
+ case VMCHNG:
+ vsave();
+ addr = dot - vcline;
+ notecnt = 1;
+ if (undkind == UNDPUT && undap1 == undap2) {
+ beep();
+ break;
+ }
+ /*
+ * Undo() call below basically replaces undap1 to undap2-1
+ * with dol through unddol-1. Hack screen image to
+ * reflect this replacement.
+ */
+ if (show)
+ if (undkind == UNDMOVE)
+ vdirty(0, TLINES);
+ else
+ vreplace(undap1 - addr, undap2 - undap1,
+ undkind == UNDPUT ? 0 : unddol - dol);
+ savenote = notecnt;
+ undo(1);
+ if (show && (vundkind != VMCHNG || addr != dot))
+ killU();
+ vundkind = VMANY;
+ cnt = dot - addr;
+ if (cnt < 0 || cnt > vcnt || state != VISUAL) {
+ if (show)
+ vjumpto(dot, NOSTR, '.');
+ break;
+ }
+ if (!savenote)
+ notecnt = 0;
+ if (show) {
+ vcline = cnt;
+ vrepaint(vmcurs);
+ }
+ vmcurs = 0;
+ break;
+
+ case VCHNG:
+ case VCAPU:
+ vundkind = VCHNG;
+ CP(temp, vutmp);
+ CP(vutmp, linebuf);
+ doomed = column(vUA2 - 1) - column(vUA1 - 1);
+ strcLIN(temp);
+ cp = vUA1; vUA1 = vUD1; vUD1 = cp;
+ cp = vUA2; vUA2 = vUD2; vUD2 = cp;
+ if (!show)
+ break;
+ cursor = vUD1;
+ if (state == HARDOPEN) {
+ doomed = 0;
+ vsave();
+ vopen(dot, WBOT);
+ vnline(cursor);
+ break;
+ }
+ /*
+ * Pseudo insert command.
+ */
+ vcursat(cursor);
+ OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
+ vprepins();
+ temp[vUA2 - linebuf] = 0;
+ for (cp = &temp[vUA1 - linebuf]; *cp;) {
+ int c, n;
+ nextc(c, cp, n);
+ cp += n;
+ putchar(c);
+ }
+ Outchar = OO; hold = oldhold;
+ endim();
+ physdc(cindent(), cindent() + doomed);
+ doomed = 0;
+ vdirty(vcline, 1);
+ vsyncCL();
+ if (cursor > linebuf && cursor >= strend(linebuf))
+ cursor += skipleft(linebuf, cursor);
+ vfixcurs();
+ break;
+
+ case VNONE:
+ beep();
+ break;
+ }
+ free(temp);
+}
+
+/*
+ * Routine to handle a change inside a macro.
+ * Fromvis is true if we were called from a visual command (as
+ * opposed to an ex command). This has nothing to do with being
+ * in open/visual mode as :s/foo/bar is not fromvis.
+ */
+void
+vmacchng(int fromvis)
+{
+ line *savedot, *savedol;
+ char *savecursor;
+ char *savelb;
+ int nlines, more;
+ /* register line *a1, *a2; */
+ /* char ch; */ /* DEBUG */
+
+ if (!inopen)
+ return;
+ if (!vmacp)
+ vch_mac = VC_NOTINMAC;
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac, linebuf, *dot);
+#endif
+ if (vmacp && fromvis)
+ vsave();
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "after vsave, linebuf='%s', *dot=%o\n", linebuf, *dot);
+#endif
+ switch(vch_mac) {
+ case VC_NOCHANGE:
+ vch_mac = VC_ONECHANGE;
+ break;
+ case VC_ONECHANGE:
+ savelb = smalloc(LBSIZE);
+ /* Save current state somewhere */
+#ifdef TRACE
+ vudump("before vmacchng hairy case");
+#endif
+ savedot = dot; savedol = dol; savecursor = cursor;
+ CP(savelb, linebuf);
+ nlines = dol - zero;
+ while ((line *) endcore - truedol < nlines)
+ morelines();
+ copyw(truedol+1, zero+1, nlines);
+ truedol += nlines;
+
+#ifdef TRACE
+ visdump("before vundo");
+#endif
+ /* Restore state as it was at beginning of macro */
+ vundo(0);
+#ifdef TRACE
+ visdump("after vundo");
+ vudump("after vundo");
+#endif
+
+ /* Do the saveall we should have done then */
+ saveall();
+#ifdef TRACE
+ vudump("after saveall");
+#endif
+
+ /* Restore current state from where saved */
+ more = savedol - dol; /* amount we shift everything by */
+ if (more)
+ (*(more>0 ? copywR : copyw))(savedol+1, dol+1, truedol-dol);
+ unddol += more; truedol += more; undap2 += more;
+
+ truedol -= nlines;
+ copyw(zero+1, truedol+1, nlines);
+ dot = savedot; dol = savedol ; cursor = savecursor;
+ CP(linebuf, savelb);
+ vch_mac = VC_MANYCHANGE;
+
+ /* Arrange that no further undo saving happens within macro */
+ otchng = tchng; /* Copied this line blindly - bug? */
+ inopen = -1; /* no need to save since it had to be 1 or -1 before */
+ vundkind = VMANY;
+#ifdef TRACE
+ vudump("after vmacchng");
+#endif
+ free(savelb);
+ break;
+ case VC_NOTINMAC:
+ case VC_MANYCHANGE:
+ /* Nothing to do for various reasons. */
+ break;
+ }
+}
+
+/*
+ * Initialize undo information before an append.
+ */
+void
+vnoapp(void)
+{
+
+ vUD1 = vUD2 = cursor;
+}
+
+/*
+ * All the rest of the motion sequences have one or more
+ * cases to deal with. In the case wdot == 0, operation
+ * is totally within current line, from cursor to wcursor.
+ * If wdot is given, but wcursor is 0, then operation affects
+ * the inclusive line range. The hardest case is when both wdot
+ * and wcursor are given, then operation affects from line dot at
+ * cursor to line wdot at wcursor.
+ */
+
+/*
+ * Move is simple, except for moving onto new lines in hardcopy open mode.
+ */
+/*ARGSUSED*/
+void
+vmove(int unused)
+{
+ register int cnt;
+
+ if (wdot) {
+ if (wdot < one || wdot > dol) {
+ beep();
+ return;
+ }
+ cnt = wdot - dot;
+ wdot = NOLINE;
+ if (cnt)
+ killU();
+ vupdown(cnt, wcursor);
+ return;
+ }
+
+ /*
+ * When we move onto a new line, save information for U undo.
+ */
+ if (vUNDdot != dot) {
+ vUNDsav = *dot;
+ vUNDcurs = wcursor;
+ vUNDdot = dot;
+ }
+
+ /*
+ * In hardcopy open, type characters to left of cursor
+ * on new line, or back cursor up if its to left of where we are.
+ * In any case if the current line is ``rubbled'' i.e. has trashy
+ * looking overstrikes on it or \'s from deletes, we reprint
+ * so it is more comprehensible (and also because we can't work
+ * if we let it get more out of sync since column() won't work right.
+ */
+ if (state == HARDOPEN) {
+ register char *cp;
+ if (rubble) {
+ register int c;
+ int oldhold = hold;
+
+ sethard();
+ cp = wcursor;
+ c = *cp;
+ *cp = 0;
+ hold |= HOLDDOL;
+ vreopen(WTOP, lineDOT(), vcline);
+ hold = oldhold;
+ *cp = c;
+ } else if (wcursor > cursor) {
+ vfixcurs();
+ for (cp = cursor; *cp && cp < wcursor;) {
+ int c, n;
+ nextc(c, cp, n);
+ cp += n;
+ c &= TRIM;
+ putchar(c ? c : ' ');
+ }
+ }
+ }
+ vsetcurs(wcursor);
+}
+
+/*
+ * Delete operator.
+ *
+ * Hard case of deleting a range where both wcursor and wdot
+ * are specified is treated as a special case of change and handled
+ * by vchange (although vchange may pass it back if it degenerates
+ * to a full line range delete.)
+ */
+void
+vdelete(int c)
+{
+ register char *cp;
+ register int i;
+
+ if (wdot) {
+ if (wcursor) {
+ vchange(EOF);
+ return;
+ }
+ if ((i = xdw()) < 0)
+ return;
+ if (state != VISUAL) {
+ vgoto(LINE(0), 0);
+ vputchar('@');
+ }
+ wdot = dot;
+ vremote(i, delete, 0);
+ notenam = "delete";
+ DEL[0] = 0;
+ killU();
+ vreplace(vcline, i, 0);
+ if (wdot > dol)
+ vcline--;
+ vrepaint(NOSTR);
+ return;
+ }
+ if (wcursor < linebuf)
+ wcursor = linebuf;
+ if (cursor == wcursor) {
+ beep();
+ return;
+ }
+ i = vdcMID();
+ cp = cursor;
+ setDEL();
+ CP(cp, wcursor);
+ if (cp > linebuf && (cp[0] == 0 || c == '#'))
+ cp--;
+ if (state == HARDOPEN) {
+ bleep(i, cp);
+ cursor = cp;
+ return;
+ }
+ physdc(column(cursor + skipleft(linebuf, cursor)), i);
+ DEPTH(vcline) = 0;
+ vreopen(LINE(vcline), lineDOT(), vcline);
+ vsyncCL();
+ vsetcurs(cp);
+}
+
+/*
+ * Change operator.
+ *
+ * In a single line we mark the end of the changed area with '$'.
+ * On multiple whole lines, we clear the lines first.
+ * Across lines with both wcursor and wdot given, we delete
+ * and sync then append (but one operation for undo).
+ */
+void
+vchange(int c)
+{
+ register char *cp;
+ register int i, ind, cnt;
+ line *addr;
+
+ if (wdot) {
+ /*
+ * Change/delete of lines or across line boundaries.
+ */
+ if ((cnt = xdw()) < 0)
+ return;
+ getDOT();
+ if (wcursor && cnt == 1) {
+ /*
+ * Not really.
+ */
+ wdot = 0;
+ if (c == EOF) {
+ vdelete(c);
+ return;
+ }
+ goto smallchange;
+ }
+ if (cursor && wcursor) {
+ /*
+ * Across line boundaries, but not
+ * necessarily whole lines.
+ * Construct what will be left.
+ */
+ *cursor = 0;
+ strcpy(genbuf, linebuf);
+ getline(*wdot);
+ if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
+ getDOT();
+ beep();
+ return;
+ }
+ strcat(genbuf, wcursor);
+ if (c == EOF && *vpastwh(genbuf) == 0) {
+ /*
+ * Although this is a delete
+ * spanning line boundaries, what
+ * would be left is all white space,
+ * so take it all away.
+ */
+ wcursor = 0;
+ getDOT();
+ op = 0;
+ notpart(lastreg);
+ notpart('1');
+ vdelete(c);
+ return;
+ }
+ ind = -1;
+ } else if (c == EOF && wcursor == 0) {
+ vdelete(c);
+ return;
+ } else
+#ifdef LISPCODE
+ /*
+ * We are just substituting text for whole lines,
+ * so determine the first autoindent.
+ */
+ if (value(LISP) && value(AUTOINDENT))
+ ind = lindent(dot);
+ else
+#endif
+ ind = whitecnt(linebuf);
+ i = vcline >= 0 ? LINE(vcline) : WTOP;
+
+ /*
+ * Delete the lines from the buffer,
+ * and remember how the partial stuff came about in
+ * case we are told to put.
+ */
+ addr = dot;
+ vremote(cnt, delete, 0);
+ setpk();
+ notenam = "delete";
+ if (c != EOF)
+ notenam = "change";
+ /*
+ * If DEL[0] were nonzero, put would put it back
+ * rather than the deleted lines.
+ */
+ DEL[0] = 0;
+ if (cnt > 1)
+ killU();
+
+ /*
+ * Now hack the screen image coordination.
+ */
+ vreplace(vcline, cnt, 0);
+ wdot = NOLINE;
+ noteit(0);
+ vcline--;
+ if (addr <= dol)
+ dot--;
+
+ /*
+ * If this is a across line delete/change,
+ * cursor stays where it is; just splice together the pieces
+ * of the new line. Otherwise generate a autoindent
+ * after a S command.
+ */
+ if (ind >= 0) {
+ *genindent(ind) = 0;
+ vdoappend(genbuf);
+ } else {
+ vmcurs = cursor;
+ strcLIN(genbuf);
+ vdoappend(linebuf);
+ }
+
+ /*
+ * Indicate a change on hardcopies by
+ * erasing the current line.
+ */
+ if (c != EOF && state != VISUAL && state != HARDOPEN) {
+ int oldhold = hold;
+
+ hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
+ }
+
+ /*
+ * Open the line (logically) on the screen, and
+ * update the screen tail. Unless we are really a delete
+ * go off and gather up inserted characters.
+ */
+ vcline++;
+ if (vcline < 0)
+ vcline = 0;
+ vopen(dot, i);
+ vsyncCL();
+ noteit(1);
+ if (c != EOF) {
+ if (ind >= 0) {
+ cursor = linebuf;
+ linebuf[0] = 0;
+ vfixcurs();
+ } else {
+ ind = 0;
+ vcursat(cursor);
+ }
+ vappend('x', 1, ind);
+ return;
+ }
+ if (*cursor == 0 && cursor > linebuf)
+ cursor += skipleft(linebuf, cursor);
+ vrepaint(cursor);
+ return;
+ }
+
+smallchange:
+ /*
+ * The rest of this is just low level hacking on changes
+ * of small numbers of characters.
+ */
+ if (wcursor < linebuf)
+ wcursor = linebuf;
+ if (cursor == wcursor) {
+ beep();
+ return;
+ }
+ i = vdcMID();
+ cp = cursor;
+ if (state != HARDOPEN)
+ vfixcurs();
+
+ /*
+ * Put out the \\'s indicating changed text in hardcopy,
+ * or mark the end of the change with $ if not hardcopy.
+ */
+ if (state == HARDOPEN)
+ bleep(i, cp);
+ else {
+ int c, d, n;
+ vcursbef(wcursor);
+ d = skipleft(linebuf, wcursor);
+ nextc(c, &wcursor[d], n);
+ if (colsc(c) > 1)
+ putchar(' ');
+ putchar('$');
+ i = cindent();
+ }
+
+ /*
+ * Remember the deleted text for possible put,
+ * and then prepare and execute the input portion of the change.
+ */
+ cursor = cp;
+ setDEL();
+ CP(cursor, wcursor);
+ if (state != HARDOPEN) {
+ vcursaft(cursor - 1);
+ doomed = i - cindent();
+ } else {
+/*
+ sethard();
+ wcursor = cursor;
+ cursor = linebuf;
+ vgoto(outline, value(NUMBER) << 3);
+ vmove();
+*/
+ doomed = 0;
+ }
+ prepapp();
+ vappend('c', 1, 0);
+}
+
+/*
+ * Open new lines.
+ *
+ * Tricky thing here is slowopen. This causes display updating
+ * to be held off so that 300 baud dumb terminals don't lose badly.
+ * This also suppressed counts, which otherwise say how many blank
+ * space to open up. Counts are also suppressed on intelligent terminals.
+ * Actually counts are obsoleted, since if your terminal is slow
+ * you are better off with slowopen.
+ */
+void
+voOpen (
+ int c, /* mjm: char --> int */
+ register int cnt
+)
+{
+ register int ind = 0, i;
+ short oldhold = hold;
+#ifdef SIGWINCH
+ sigset_t set, oset;
+#endif
+
+ if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
+ cnt = 1;
+#ifdef SIGWINCH
+ sigemptyset(&set);
+ sigaddset(&set, SIGWINCH);
+ sigprocmask(SIG_BLOCK, &set, &oset);
+#endif
+ vsave();
+ setLAST();
+ if (value(AUTOINDENT))
+ ind = whitecnt(linebuf);
+ if (c == 'O') {
+ vcline--;
+ dot--;
+ if (dot > zero)
+ getDOT();
+ }
+ if (value(AUTOINDENT)) {
+#ifdef LISPCODE
+ if (value(LISP))
+ ind = lindent(dot + 1);
+#endif
+ }
+ killU();
+ prepapp();
+ if (FIXUNDO)
+ vundkind = VMANY;
+ if (state != VISUAL)
+ c = WBOT + 1;
+ else {
+ c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
+ if (c < ZERO)
+ c = ZERO;
+ i = LINE(vcline + 1) - c;
+ if (i < cnt && c <= WBOT && (!AL || !DL))
+ vinslin(c, cnt - i, vcline);
+ }
+ *genindent(ind) = 0;
+ vdoappend(genbuf);
+ vcline++;
+ oldhold = hold;
+ hold |= HOLDROL;
+ vopen(dot, c);
+ hold = oldhold;
+ if (value(SLOWOPEN))
+ /*
+ * Oh, so lazy!
+ */
+ vscrap();
+ else
+ vsync1(LINE(vcline));
+ cursor = linebuf;
+ linebuf[0] = 0;
+ vappend('o', 1, ind);
+#ifdef SIGWINCH
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+#endif
+}
+
+/*
+ * > < and = shift operators.
+ *
+ * Note that =, which aligns lisp, is just a ragged sort of shift,
+ * since it never distributes text between lines.
+ */
+char vshnam[2] = { 'x', 0 };
+
+/*ARGSUSED*/
+void
+vshftop(int unused)
+{
+ register line *addr;
+ register int cnt;
+
+ if ((cnt = xdw()) < 0)
+ return;
+ addr = dot;
+ vremote(cnt, vshift, 0);
+ vshnam[0] = op;
+ notenam = vshnam;
+ dot = addr;
+ vreplace(vcline, cnt, cnt);
+ if (state == HARDOPEN)
+ vcnt = 0;
+ vrepaint(NOSTR);
+}
+
+/*
+ * !.
+ *
+ * Filter portions of the buffer through unix commands.
+ */
+/*ARGSUSED*/
+void
+vfilter(int unused)
+{
+ register line *addr;
+ register int cnt;
+ char *oglobp;
+ short d;
+#ifdef BIT8
+ cell cuxb[UXBSIZE + 2];
+#endif
+
+ if ((cnt = xdw()) < 0)
+ return;
+ if (vglobp)
+#ifdef BIT8
+ vglobp = cuxb;
+#else
+ vglobp = uxb;
+#endif
+ if (readecho('!'))
+ return;
+ oglobp = globp; globp = genbuf + 1;
+ d = peekc; ungetchar(0);
+ CATCH
+ fixech();
+ unix0(0);
+#ifdef BIT8
+ str2cell(cuxb, uxb);
+#endif
+ ONERR
+ splitw = 0;
+ ungetchar(d);
+ vrepaint(cursor);
+ globp = oglobp;
+ return;
+ ENDCATCH
+ ungetchar(d); globp = oglobp;
+ addr = dot;
+ CATCH
+ vgoto(WECHO, 0); flusho();
+ vremote(cnt, filter, 2);
+ ONERR
+ vdirty(0, TLINES);
+ ENDCATCH
+ if (dot == zero && dol > zero)
+ dot = one;
+ splitw = 0;
+ notenam = "";
+ /*
+ * BUG: we shouldn't be depending on what undap2 and undap1 are,
+ * since we may be inside a macro. What's really wanted is the
+ * number of lines we read from the filter. However, the mistake
+ * will be an overestimate so it only results in extra work,
+ * it shouldn't cause any real screwups.
+ */
+ vreplace(vcline, cnt, undap2 - undap1);
+ dot = addr;
+ if (dot > dol) {
+ dot--;
+ vcline--;
+ }
+ vrepaint(NOSTR);
+}
+
+/*
+ * Xdw exchanges dot and wdot if appropriate and also checks
+ * that wdot is reasonable. Its name comes from
+ * xchange dotand wdot
+ */
+int
+xdw(void)
+{
+ register char *cp;
+ register int cnt;
+/*
+ register int notp = 0;
+ */
+
+ if (wdot == NOLINE || wdot < one || wdot > dol) {
+ beep();
+ return (-1);
+ }
+ vsave();
+ setLAST();
+ if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
+ register line *addr;
+
+ vcline -= dot - wdot;
+ addr = dot; dot = wdot; wdot = addr;
+ cp = cursor; cursor = wcursor; wcursor = cp;
+ }
+ /*
+ * If a region is specified but wcursor is at the begining
+ * of the last line, then we move it to be the end of the
+ * previous line (actually off the end).
+ */
+ if (cursor && wcursor == linebuf && wdot > dot) {
+ wdot--;
+ getDOT();
+ if (vpastwh(linebuf) >= cursor)
+ wcursor = 0;
+ else {
+ getline(*wdot);
+ wcursor = strend(linebuf);
+ getDOT();
+ }
+ /*
+ * Should prepare in caller for possible dot == wdot.
+ */
+ }
+ cnt = wdot - dot + 1;
+ if (vreg) {
+ vremote(cnt, YANKreg, vreg);
+/*
+ if (notp)
+ notpart(vreg);
+ */
+ }
+
+ /*
+ * Kill buffer code. If delete operator is c or d, then save
+ * the region in numbered buffers.
+ *
+ * BUG: This may be somewhat inefficient due
+ * to the way named buffer are implemented,
+ * necessitating some optimization.
+ */
+ vreg = 0;
+ if (any(op, "cd")) {
+ vremote(cnt, YANKreg, '1');
+/*
+ if (notp)
+ notpart('1');
+ */
+ }
+ return (cnt);
+}
+
+/*
+ * Routine for vremote to call to implement shifts.
+ */
+/*ARGSUSED*/
+void
+vshift(int unused)
+{
+
+ shift(op, 1);
+}
+
+/*
+ * Replace a single character with the next input character.
+ * A funny kind of insert.
+ */
+void
+vrep(register int cnt)
+{
+ register int i, c;
+
+ if (cnt > strlen(cursor)) {
+ beep();
+ return;
+ }
+ showmode('r');
+ i = column(cursor + cnt - 1);
+ vcursat(cursor);
+ doomed = i - cindent();
+ if (!vglobp) {
+ c = getesc();
+ if (c == 0) {
+ showmode(0);
+ vfixcurs();
+ return;
+ }
+ ungetkey(c);
+ }
+ CP(vutmp, linebuf);
+ if (FIXUNDO)
+ vundkind = VCHNG;
+ wcursor = cursor;
+ for (i = 0; i < cnt; i++)
+ wcursor += skipright(cursor, wcursor);
+ vUD1 = cursor; vUD2 = wcursor;
+ CP(cursor, wcursor);
+ prepapp();
+ vappend('r', cnt, 0);
+ *lastcp++ = INS[0];
+ setLAST();
+}
+
+/*
+ * Yank.
+ *
+ * Yanking to string registers occurs for free (essentially)
+ * in the routine xdw().
+ */
+/*ARGSUSED*/
+void
+vyankit(int unused)
+{
+ register int cnt;
+
+ if (wdot) {
+ if ((cnt = xdw()) < 0)
+ return;
+ vremote(cnt, yank, 0);
+ setpk();
+ notenam = "yank";
+ if (FIXUNDO)
+ vundkind = VNONE;
+ DEL[0] = 0;
+ wdot = NOLINE;
+ if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
+ notecnt = 0;
+ vrepaint(cursor);
+ return;
+ }
+ takeout(DEL);
+}
+
+/*
+ * Set pkill variables so a put can
+ * know how to put back partial text.
+ * This is necessary because undo needs the complete
+ * line images to be saved, while a put wants to trim
+ * the first and last lines. The compromise
+ * is for put to be more clever.
+ */
+void
+setpk(void)
+{
+
+ if (wcursor) {
+ pkill[0] = cursor;
+ pkill[1] = wcursor;
+ }
+}
+
+/*
+ * Kill the last deleted part of a line so that "p" does not put it back.
+ * This is to be called from ex commands that delete some text.
+ */
+void
+vkillDEL(void)
+{
+ DEL[0] = 0;
+}