Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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.26 (gritter) 1/13/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[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;
        }
}

/*
 * 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[LBSIZE];
        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:
                /* 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
                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;
}