date and time created 80/07/31 23:00:53 by mark
authorMark Horton <mark@ucbvax.Berkeley.EDU>
Fri, 1 Aug 1980 14:00:53 +0000 (06:00 -0800)
committerMark Horton <mark@ucbvax.Berkeley.EDU>
Fri, 1 Aug 1980 14:00:53 +0000 (06:00 -0800)
SCCS-vsn: usr.bin/ex/ex_vops.c 1.1

usr/src/usr.bin/ex/ex_vops.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/ex/ex_vops.c b/usr/src/usr.bin/ex/ex_vops.c
new file mode 100644 (file)
index 0000000..92a5f68
--- /dev/null
@@ -0,0 +1,812 @@
+/* Copyright (c) 1979 Regents of the University of California */
+#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;
+       }
+       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();
+       vfixcurs();
+}
+
+vundo()
+{
+       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();
+                       return;
+               }
+               /*
+                * Undo() call below basically replaces undap1 to undap2-1
+                * with dol through unddol-1.  Hack screen image to
+                * reflect this replacement.
+                */
+               vreplace(undap1 - addr, undap2 - undap1,
+                   undkind == UNDPUT ? 0 : unddol - dol);
+               savenote = notecnt;
+               undo(1);
+               if (vundkind != VMCHNG || addr != dot)
+                       killU();
+               vundkind = VMANY;
+               cnt = dot - addr;
+               if (cnt < 0 || cnt > vcnt || state != VISUAL) {
+                       vjumpto(dot, NOSTR, '.');
+                       return;
+               }
+               if (!savenote)
+                       notecnt = 0;
+               vcline = cnt;
+               vrepaint(vmcurs);
+               vmcurs = 0;
+               return;
+
+       case VCHNG:
+       case VCAPU:
+               vundkind = VCHNG;
+               strcpy(temp, vutmp);
+               strcpy(vutmp, linebuf);
+               doomed = column(vUA2 - 1) - column(vUA1 - 1);
+               strcLIN(temp);
+               cp = vUA1; vUA1 = vUD1; vUD1 = cp;
+               cp = vUA2; vUA2 = vUD2; vUD2 = cp;
+               cursor = vUD1;
+               if (state == HARDOPEN) {
+                       doomed = 0;
+                       vsave();
+                       vopen(dot, WBOT);
+                       vnline(cursor);
+                       return;
+               }
+               /*
+                * 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();
+               return;
+
+       case VNONE:
+               beep();
+               return;
+       }
+}
+
+/*
+ * 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;) {
+                               register int c = *cp++ & 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.)
+ */
+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)
+       char c;
+       register int cnt;
+{
+       register int ind = 0, i;
+       short oldhold = hold;
+
+       if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
+               cnt = 1;
+       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();
+       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);
+}
+
+/*
+ * > < 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, 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 = "";
+       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) {
+               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);
+       }
+       CP(vutmp, linebuf);
+       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";
+               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;
+       }
+}