Subversion Repositories planix.SVN

Rev

Rev 107 | Rev 113 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#if     !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vops.c    7.7 (Berkeley) 6/7/85";
#endif

#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;

vUndo()
{

        /*
         * Avoid UU which clobbers ability to do u.
         */
        if (vundkind == VCAPU || vUNDdot != dot) {
                beep();
                return;
        }
#ifndef BIT8
        CP(vutmp, linebuf);
#else
        sc_strcpy(vutmp, linebuf);
#endif
        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();
}

vundo(show)
bool show;      /* if true update the screen */
{
        register int cnt;
        register line *addr;
        register char *cp;
        char temp[LBSIZE];
        bool savenote;
        int (*OO)();
        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, LINES);
                        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;
#ifndef BIT8
                strcpy(temp, vutmp);
                strcpy(vutmp, linebuf);
#else
                cs_strcpy(temp, vutmp);
                sc_strcpy(vutmp, linebuf);
#endif
                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;)
                        putchar(*cp++);
                Outchar = OO; hold = oldhold;
                endim();
                physdc(cindent(), cindent() + doomed);
                doomed = 0;
                vdirty(vcline, 1);
                vsyncCL();
                if (cursor > linebuf && cursor >= strend(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.
 */
vmacchng(fromvis)
bool fromvis;
{
        line *savedot, *savedol;
        char *savecursor;
        char savelb[LBSIZE];
        int nlines, more;
        register line *a1, *a2;
        char ch;        /* DEBUG */
        int copyw(), copywR();

        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.
 */
vnoapp()
{

        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.
 */
vmove()
{
        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;) {
#ifndef ISO
                                register int c = *cp++ & TRIM;
#else
                                register int c;

                                if (niso(*cp))
                                        c = *cp++ & TRIM;
                                else
                                        c = *cp++;
#endif

                                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.)
 */
vdelete(c)
        char c;
{
        register char *cp;
        register int i;

        if (wdot) {
                if (wcursor) {
                        vchange('d');
                        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 - 1), 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).
 */
vchange(c)
        char 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 == 'd') {
                                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 == 'd' && *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 == 'd' && 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 != 'd')
                        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 != 'd' && 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 != 'd') {
                        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--;
                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 {
                vcursbef(wcursor);
                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.
 */
voOpen(c, cnt)
        int c;  /* mjm: char --> int */
        register int cnt;
{
        register int ind = 0, i;
        short oldhold = hold;
        long oldmask;

        if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
                cnt = 1;
/* XXX  oldmask = sigblock(sigmask(SIGWINCH)); */
        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);
/*XXX   (void)sigsetmask(oldmask); XXX*/
}

/*
 * > < 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 };

vshftop()
{
        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.
 */
vfilter()
{
        register line *addr;
        register int cnt;
        char *oglobp;
        short d;

        if ((cnt = xdw()) < 0)
                return;
        if (vglobp)
                vglobp = uxb;
        if (readecho('!'))
                return;
        oglobp = globp; globp = genbuf + 1;
        d = peekc; ungetchar(0);
        CATCH
                fixech();
                unix0(0);
        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, LINES);
        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
 */
xdw()
{
        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.
 */
vshift()
{

        shift(op, 1);
}

/*
 * Replace a single character with the next input character.
 * A funny kind of insert.
 */
vrep(cnt)
        register int cnt;
{
        register int i, c;

        if (cnt > strlen(cursor)) {
                beep();
                return;
        }
        i = column(cursor + cnt - 1);
        vcursat(cursor);
        doomed = i - cindent();
        if (!vglobp) {
                c = getesc();
                if (c == 0) {
                        vfixcurs();
                        return;
                }
                ungetkey(c);
        }
#ifndef BIT8
        CP(vutmp, linebuf);
#else
        sc_strcpy(vutmp, linebuf);
#endif
        if (FIXUNDO)
                vundkind = VCHNG;
        wcursor = cursor + cnt;
        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().
 */
vyankit()
{
        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.
 */
setpk()
{

        if (wcursor) {
                pkill[0] = cursor;
                pkill[1] = wcursor;
        }
}