BSD 2 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 9 May 1979 07:22:11 +0000 (23:22 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Wed, 9 May 1979 07:22:11 +0000 (23:22 -0800)
Work on file src/ex/ex_vops2.c

Synthesized-from: 2bsd

src/ex/ex_vops2.c [new file with mode: 0644]

diff --git a/src/ex/ex_vops2.c b/src/ex/ex_vops2.c
new file mode 100644 (file)
index 0000000..ac0b210
--- /dev/null
@@ -0,0 +1,761 @@
+/* Copyright (c) 1979 Regents of the University of California */
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Low level routines for operations sequences,
+ * and mostly, insert mode (and a subroutine
+ * to read an input line, including in the echo area.)
+ */
+char   *vUA1, *vUA2;
+char   *vUD1, *vUD2;
+
+/*
+ * Obleeperate characters in hardcopy
+ * open with \'s.
+ */
+bleep(i, cp)
+       register int i;
+       char *cp;
+{
+
+       i -= column(cp);
+       do
+               putchar('\\' | QUOTE);
+       while (--i >= 0);
+       rubble = 1;
+}
+
+/*
+ * Common code for middle part of delete
+ * and change operating on parts of lines.
+ */
+vdcMID()
+{
+       register char *cp;
+
+       squish();
+       setLAST();
+       vundkind = VCHNG, CP(vutmp, linebuf);
+       if (wcursor < cursor)
+               cp = wcursor, wcursor = cursor, cursor = cp;
+       vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
+       return (column(wcursor - 1));
+}
+
+/*
+ * Take text from linebuf and stick it
+ * in the VBSIZE buffer BUF.  Used to save
+ * deleted text of part of line.
+ */
+takeout(BUF)
+       char *BUF;
+{
+       register char *cp;
+
+       if (wcursor < linebuf)
+               wcursor = linebuf;
+       if (cursor == wcursor) {
+               beep();
+               return;
+       }
+       if (wcursor < cursor) {
+               cp = wcursor;
+               wcursor = cursor;
+               cursor = cp;
+       }
+       setBUF(BUF);
+       if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
+               beep();
+}
+
+/*
+ * Are we at the end of the printed representation of the
+ * line?  Used internally in hardcopy open.
+ */
+ateopr()
+{
+       register int i, c;
+       register char *cp = vtube[destline] + destcol;
+
+       for (i = WCOLS - destcol; i > 0; i--) {
+               c = *cp++;
+               if (c == 0)
+                       return (1);
+               if (c != ' ' && (c & QUOTE) == 0)
+                       return (0);
+       }
+       return (1);
+}
+
+/*
+ * Append.
+ *
+ * This routine handles the top level append, doing work
+ * as each new line comes in, and arranging repeatability.
+ * It also handles append with repeat counts, and calculation
+ * of autoindents for new lines.
+ */
+bool   vaifirst;
+bool   gobbled;
+char   *ogcursor;
+
+vappend(ch, cnt, indent)
+       char ch;
+       int cnt, indent;
+{
+       register int i;
+       register char *gcursor;
+       bool escape;
+       int repcnt;
+       short oldhold = hold;
+
+       /*
+        * Before a move in hardopen when the line is dirty
+        * or we are in the middle of the printed representation,
+        * we retype the line to the left of the cursor so the
+        * insert looks clean.
+        */
+       if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
+               rubble = 1;
+               wcursor = cursor;
+               vmove();
+       }
+       vaifirst = indent == 0;
+
+       /*
+        * Handle replace character by (eventually)
+        * limiting the number of input characters allowed
+        * in the vgetline routine.
+        */
+       if (ch == 'r')
+               repcnt = 2;
+       else
+               repcnt = 0;
+
+       /*
+        * If an autoindent is specified, then
+        * generate a mixture of blanks to tabs to implement
+        * it and place the cursor after the indent.
+        * Text read by the vgetline routine will be placed in genbuf,
+        * so the indent is generated there.
+        */
+       if (value(AUTOINDENT) && indent != 0) {
+               gcursor = genindent(indent);
+               *gcursor = 0;
+               vgotoCL(qcolumn(cursor - 1, genbuf));
+       } else {
+               gcursor = genbuf;
+               *gcursor = 0;
+               if (ch == 'o')
+                       vfixcurs();
+       }
+
+       /*
+        * Prepare for undo.  Pointers delimit inserted portion of line.
+        */
+       vUA1 = vUA2 = cursor;
+
+       /*
+        * If we are not in a repeated command and a ^@ comes in
+        * then this means the previous inserted text.
+        * If there is none or it was too long to be saved,
+        * then beep() and also arrange to undo any damage done
+        * so far (e.g. if we are a change.)
+        */
+       if ((vglobp && *vglobp == 0) || peekbr()) {
+               if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
+                       beep();
+                       if (!splitw)
+                               ungetkey('u');
+                       doomed = 0;
+                       hold = oldhold;
+                       return;
+               }
+               /*
+                * Unread input from INS.
+                * An escape will be generated at end of string.
+                * Hold off n^^2 type update on dumb terminals.
+                */
+               vglobp = INS;
+               hold |= HOLDQIK;
+       } else if (vglobp == 0)
+               /*
+                * Not a repeated command, get
+                * a new inserted text for repeat.
+                */
+               INS[0] = 0;
+
+       /*
+        * For wrapmargin to hack away second space after a '.'
+        * when the first space caused a line break we keep
+        * track that this happened in gobblebl, which says
+        * to gobble up a blank silently.
+        */
+       gobblebl = 0;
+
+       /*
+        * Text gathering loop.
+        * New text goes into genbuf starting at gcursor.
+        * cursor preserves place in linebuf where text will eventually go.
+        */
+       if (*cursor == 0 || state == CRTOPEN)
+               hold |= HOLDROL;
+       for (;;) {
+               if (ch == 'r' && repcnt == 0)
+                       escape = 0;
+               else {
+                       gcursor = vgetline(repcnt, gcursor, &escape);
+
+                       /*
+                        * After an append, stick information
+                        * about the ^D's and ^^D's and 0^D's in
+                        * the repeated text buffer so repeated
+                        * inserts of stuff indented with ^D as backtab's
+                        * can work.
+                        */
+                       if (HADUP)
+                               addtext("^");
+                       else if (HADZERO)
+                               addtext("0");
+                       while (CDCNT > 0)
+                               addtext("\204"), CDCNT--;
+                       if (gobbled)
+                               addtext(" ");
+                       addtext(ogcursor);
+               }
+               repcnt = 0;
+
+               /*
+                * Smash the generated and preexisting indents together
+                * and generate one cleanly made out of tabs and spaces
+                * if we are using autoindent.
+                */
+               if (!vaifirst && value(AUTOINDENT)) {
+                       i = fixindent(indent);
+                       if (!HADUP)
+                               indent = i;
+                       gcursor = strend(genbuf);
+               }
+
+               /*
+                * Limit the repetition count based on maximum
+                * possible line length; do output implied
+                * by further count (> 1) and cons up the new line
+                * in linebuf.
+                */
+               cnt = vmaxrep(ch, cnt);
+               CP(gcursor + 1, cursor);
+               do {
+                       CP(cursor, genbuf);
+                       if (cnt > 1) {
+                               int oldhold = hold;
+
+                               Outchar = vinschar;
+                               hold |= HOLDQIK;
+                               printf("%s", genbuf);
+                               hold = oldhold;
+                               Outchar = vputchar;
+                       }
+                       cursor += gcursor - genbuf;
+               } while (--cnt > 0);
+               endim();
+               vUA2 = cursor;
+               if (escape != '\n')
+                       CP(cursor, gcursor + 1);
+
+               /*
+                * If doomed characters remain, clobber them,
+                * and reopen the line to get the display exact.
+                */
+               if (state != HARDOPEN) {
+                       DEPTH(vcline) = 0;
+                       if (doomed > 0) {
+                               register int cind = cindent();
+
+                               physdc(cind, cind + doomed);
+                               doomed = 0;
+                       }
+                       i = vreopen(LINE(vcline), lineDOT(), vcline);
+               }
+
+               /*
+                * All done unless we are continuing on to another line.
+                */
+               if (escape != '\n')
+                       break;
+
+               /*
+                * Set up for the new line.
+                * First save the current line, then construct a new
+                * first image for the continuation line consisting
+                * of any new autoindent plus the pushed ahead text.
+                */
+               killU();
+               addtext(gobblebl ? " " : "\n");
+               vsave();
+               cnt = 1;
+               if (value(AUTOINDENT)) {
+#ifdef LISP
+                       if (value(LISP))
+                               indent = lindent(dot + 1);
+                       else
+#endif
+                            if (!HADUP && vaifirst)
+                               indent = whitecnt(linebuf);
+                       vaifirst = 0;
+                       strcLIN(vpastwh(gcursor + 1));
+                       gcursor = genindent(indent);
+                       *gcursor = 0;
+                       if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
+                               gcursor = genbuf;
+                       CP(gcursor, linebuf);
+               } else {
+                       CP(genbuf, gcursor + 1);
+                       gcursor = genbuf;
+               }
+
+               /*
+                * If we started out as a single line operation and are now
+                * turning into a multi-line change, then we had better yank
+                * out dot before it changes so that undo will work
+                * correctly later.
+                */
+               if (vundkind == VCHNG) {
+                       vremote(1, yank, 0);
+                       undap1--;
+               }
+
+               /*
+                * Now do the append of the new line in the buffer,
+                * and update the display.  If slowopen
+                * we don't do very much.
+                */
+               vdoappend(genbuf);
+               vundkind = VMANYINS;
+               vcline++;
+               if (state != VISUAL)
+                       vshow(dot, NOLINE);
+               else {
+                       i += LINE(vcline - 1);
+                       vopen(dot, i);
+                       if (value(SLOWOPEN))
+                               vscrap();
+                       else
+                               vsync1(LINE(vcline));
+               }
+               strcLIN(gcursor);
+               *gcursor = 0;
+               cursor = linebuf;
+               vgotoCL(qcolumn(cursor - 1, genbuf));
+       }
+
+       /*
+        * All done with insertion, position the cursor
+        * and sync the screen.
+        */
+       hold = oldhold;
+       if (cursor > linebuf)
+               cursor--;
+       if (state != HARDOPEN)
+               vsyncCL();
+       else if (cursor > linebuf)
+               back1();
+       doomed = 0;
+       wcursor = cursor;
+       vmove();
+}
+
+/*
+ * Subroutine for vgetline to back up a single character position,
+ * backwards around end of lines (vgoto can't hack columns which are
+ * less than 0 in general).
+ */
+back1()
+{
+
+       vgoto(destline - 1, WCOLS + destcol - 1);
+}
+
+/*
+ * Get a line into genbuf after gcursor.
+ * Cnt limits the number of input characters
+ * accepted and is used for handling the replace
+ * single character command.  Aescaped is the location
+ * where we stick a termination indicator (whether we
+ * ended with an ESCAPE or a newline/return.
+ *
+ * We do erase-kill type processing here and also
+ * are careful about the way we do this so that it is
+ * repeatable.  (I.e. so that your kill doesn't happen,
+ * when you repeat an insert if it was escaped with \ the
+ * first time you did it.
+ */
+char *
+vgetline(cnt, gcursor, aescaped)
+       int cnt;
+       register char *gcursor;
+       bool *aescaped;
+{
+       register int c, ch;
+       register char *cp;
+       int x, y, iwhite;
+       char *iglobp;
+       int (*OO)() = Outchar;
+
+       /*
+        * Clear the output state and counters
+        * for autoindent backwards motion (counts of ^D, etc.)
+        * Remember how much white space at beginning of line so
+        * as not to allow backspace over autoindent.
+        */
+       *aescaped = 0;
+       ogcursor = gcursor;
+       flusho();
+       CDCNT = 0;
+       HADUP = 0;
+       HADZERO = 0;
+       gobbled = 0;
+       iwhite = whitecnt(genbuf);
+       iglobp = vglobp;
+
+       /*
+        * Carefully avoid using vinschar in the echo area.
+        */
+       if (splitw)
+               Outchar = vputchar;
+       else {
+               Outchar = vinschar;
+               vprepins();
+       }
+       for (;;) {
+               if (gobblebl)
+                       gobblebl--;
+               if (cnt != 0) {
+                       cnt--;
+                       if (cnt == 0)
+                               goto vadone;
+               }
+               ch = c = getkey() & (QUOTE|TRIM);
+               if (!iglobp) {
+
+                       /*
+                        * Erase-kill type processing.
+                        * Only happens if we were not reading
+                        * from untyped input when we started.
+                        * Map users erase to ^H, kill to -1 for switch.
+                        */
+                       if (c == tty.sg_erase)
+                               c = CTRL(h);
+                       else if (c == tty.sg_kill)
+                               c = -1;
+                       switch (c) {
+
+                       /*
+                        * ^?           Interrupt drops you back to visual
+                        *              command mode with an unread interrupt
+                        *              still in the input buffer.
+                        *
+                        * ^\           Quit does the same as interrupt.
+                        *              If you are a ex command rather than
+                        *              a vi command this will drop you
+                        *              back to command mode for sure.
+                        */
+                       case ATTN:
+                       case QUIT:
+                               ungetkey(c);
+                               goto vadone;
+
+                       /*
+                        * ^H           Backs up a character in the input.
+                        *
+                        * BUG:         Can't back around line boundaries.
+                        *              This is hard because stuff has
+                        *              already been saved for repeat.
+                        */
+                       case CTRL(h):
+bakchar:
+                               cp = gcursor - 1;
+                               if (cp < ogcursor) {
+                                       beep();
+                                       continue;
+                               }
+                               goto vbackup;
+
+                       /*
+                        * ^W           Back up a white/non-white word.
+                        */
+                       case CTRL(w):
+                               wdkind = 1;
+                               for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
+                                       continue;
+                               for (c = wordch(cp - 1); cp > ogcursor && wordof(c, cp - 1); cp--)
+                                       continue;
+                               goto vbackup;
+
+                       /*
+                        * users kill   Kill input on this line, back to
+                        *              the autoindent.
+                        */
+                       case -1:
+                               cp = ogcursor;
+vbackup:
+                               if (cp == gcursor) {
+                                       beep();
+                                       continue;
+                               }
+                               endim();
+                               *cp = 0;
+                               c = cindent();
+                               vgotoCL(qcolumn(cursor - 1, genbuf));
+                               if (doomed >= 0)
+                                       doomed += c - cindent();
+                               gcursor = cp;
+                               continue;
+
+                       /*
+                        * \            Followed by erase or kill
+                        *              maps to just the erase or kill.
+                        */
+                       case '\\':
+                               x = destcol, y = destline;
+                               putchar('\\');
+                               vcsync();
+                               c = getkey();
+                               if (c == tty.sg_erase || c == tty.sg_kill) {
+                                       vgoto(y, x);
+                                       if (doomed >= 0)
+                                               doomed++;
+                                       goto def;
+                               }
+                               ungetkey(c), c = '\\';
+                               goto noput;
+
+                       /*
+                        * ^Q           Super quote following character
+                        *              Only ^@ is verboten (trapped at
+                        *              a lower level) and \n forces a line
+                        *              split so doesn't really go in.
+                        *
+                        * ^V           Synonym for ^Q
+                        */
+                       case CTRL(q):
+                       case CTRL(v):
+                               x = destcol, y = destline;
+                               putchar('^');
+                               vgoto(y, x);
+                               c = getkey();
+                               if (c != NL) {
+                                       if (doomed >= 0)
+                                               doomed++;
+                                       goto def;
+                               }
+                               break;
+                       }
+               }
+
+               /*
+                * If we get a blank not in the echo area
+                * consider splitting the window in the wrapmargin.
+                */
+               if (c == ' ' && !splitw) {
+                       if (gobblebl) {
+                               gobbled = 1;
+                               continue;
+                       }
+                       if (value(WRAPMARGIN) && outcol >= WCOLS - value(WRAPMARGIN)) {
+                               c = NL;
+                               gobblebl = 2;
+                       }
+               }
+               switch (c) {
+
+               /*
+                * ^M           Except in repeat maps to \n.
+                */
+               case CR:
+                       if (vglobp)
+                               goto def;
+                       c = '\n';
+                       /* presto chango ... */
+
+               /*
+                * \n           Start new line.
+                */
+               case NL:
+                       *aescaped = c;
+                       goto vadone;
+
+               /*
+                * escape       End insert unless repeat and more to repeat.
+                */
+               case ESCAPE:
+                       if (vglobp && *vglobp)
+                               goto def;
+                       goto vadone;
+
+               /*
+                * ^D           Backtab.
+                * ^T           Software forward tab.
+                *
+                *              Unless in repeat where this means these
+                *              were superquoted in.
+                */
+               case CTRL(d):
+               case CTRL(t):
+                       if (vglobp)
+                               goto def;
+                       /* fall into ... */
+
+               /*
+                * ^D|QUOTE     Is a backtab (in a repeated command).
+                */
+               case CTRL(d) | QUOTE:
+                       *gcursor = 0;
+                       cp = vpastwh(genbuf);
+                       c = whitecnt(genbuf);
+                       if (ch == CTRL(t)) {
+                               /*
+                                * ^t just generates new indent replacing
+                                * current white space rounded up to soft
+                                * tab stop increment.
+                                */
+                               if (cp != gcursor)
+                                       /*
+                                        * BUG:         Don't hack ^T except
+                                        *              right after initial
+                                        *              white space.
+                                        */
+                                       continue;
+                               cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
+                               ogcursor = cp;
+                               goto vbackup;
+                       }
+                       /*
+                        * ^D works only if we are at the (end of) the
+                        * generated autoindent.  We count the ^D for repeat
+                        * purposes.
+                        */
+                       if (c == iwhite && c != 0)
+                               if (cp == gcursor) {
+                                       iwhite = backtab(c);
+                                       CDCNT++;
+                                       ogcursor = cp = genindent(iwhite);
+                                       goto vbackup;
+                               } else if (&cp[1] == gcursor &&
+                                   (*cp == '^' || *cp == '0')) {
+                                       /*
+                                        * ^^D moves to margin, then back
+                                        * to current indent on next line.
+                                        *
+                                        * 0^D moves to margin and then
+                                        * stays there.
+                                        */
+                                       HADZERO = *cp == '0';
+                                       ogcursor = cp = genbuf;
+                                       HADUP = 1 - HADZERO;
+                                       CDCNT = 1;
+                                       endim();
+                                       back1();
+                                       vputc(' ');
+                                       goto vbackup;
+                               }
+                       if (vglobp && vglobp - iglobp >= 2 &&
+                           (vglobp[-2] == '^' || vglobp[-2] == '0')
+                           && gcursor == ogcursor + 1)
+                               goto bakchar;
+                       continue;
+
+               default:
+                       /*
+                        * Possibly discard control inputs.
+                        */
+                       if (!vglobp && junk(c)) {
+                               beep();
+                               continue;
+                       }
+def:
+                       putchar(c);
+noput:
+                       if (gcursor > &genbuf[LBSIZE - 2])
+                               error("Line too long");
+                       *gcursor++ = c & TRIM;
+                       vcsync();
+#ifdef LISP
+                       if (value(SHOWMATCH) && !iglobp)
+                               if (c == ')' || c == '}')
+                                       lsmatch(gcursor);
+#endif
+                       continue;
+               }
+       }
+vadone:
+       *gcursor = 0;
+       Outchar = OO;
+       endim();
+       return (gcursor);
+}
+
+int    vgetsplit();
+char   *vsplitpt;
+
+/*
+ * Append the line in buffer at lp
+ * to the buffer after dot.
+ */
+vdoappend(lp)
+       char *lp;
+{
+       register int oing = inglobal;
+
+       vsplitpt = lp;
+       inglobal = 1;
+       ignore(append(vgetsplit, dot));
+       inglobal = oing;
+}
+
+/*
+ * Subroutine for vdoappend to pass to append.
+ */
+vgetsplit()
+{
+
+       if (vsplitpt == 0)
+               return (EOF);
+       strcLIN(vsplitpt);
+       vsplitpt = 0;
+       return (0);
+}
+
+/*
+ * Vmaxrep determines the maximum repetitition factor
+ * allowed that will yield total line length less than
+ * 512 characters and also does hacks for the R command.
+ */
+vmaxrep(ch, cnt)
+       char ch;
+       register int cnt;
+{
+       register int len, replen;
+
+       if (cnt > LBSIZE - 2)
+               cnt = LBSIZE - 2;
+       replen = strlen(genbuf);
+       if (ch == 'R') {
+               len = strlen(cursor);
+               if (replen < len)
+                       len = replen;
+               CP(cursor, cursor + len);
+               vUD2 += len;
+       }
+       len = strlen(linebuf);
+       if (len + cnt * replen <= LBSIZE - 2)
+               return (cnt);
+       cnt = (LBSIZE - 2 - len) / replen;
+       if (cnt == 0) {
+               vsave();
+               error("Line too long");
+       }
+       return (cnt);
+}