From 79597adf529eb9d4d135a0c007a9aea785230dcf Mon Sep 17 00:00:00 2001 From: Mark Horton Date: Fri, 1 Aug 1980 06:00:53 -0800 Subject: [PATCH] date and time created 80/07/31 23:00:53 by mark SCCS-vsn: usr.bin/ex/ex_vops.c 1.1 --- usr/src/usr.bin/ex/ex_vops.c | 812 +++++++++++++++++++++++++++++++++++ 1 file changed, 812 insertions(+) create mode 100644 usr/src/usr.bin/ex/ex_vops.c diff --git a/usr/src/usr.bin/ex/ex_vops.c b/usr/src/usr.bin/ex/ex_vops.c new file mode 100644 index 0000000000..92a5f6829a --- /dev/null +++ b/usr/src/usr.bin/ex/ex_vops.c @@ -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; + } +} -- 2.20.1