From: Mark Horton Date: Fri, 1 Aug 1980 14:01:00 +0000 (-0800) Subject: date and time created 80/07/31 23:01:00 by mark X-Git-Tag: BSD-4^3~951 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/98fb7faaf85ad67ddc8ebb9ad4a6430049b56722?hp=1c1e118c3dd625e05c8f366a9703c49d3990d84b date and time created 80/07/31 23:01:00 by mark SCCS-vsn: usr.bin/ex/ex_vput.c 1.1 --- diff --git a/usr/src/usr.bin/ex/ex_vput.c b/usr/src/usr.bin/ex/ex_vput.c new file mode 100644 index 0000000000..cadd33ab3c --- /dev/null +++ b/usr/src/usr.bin/ex/ex_vput.c @@ -0,0 +1,1331 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_tty.h" +#include "ex_vis.h" + +/* + * Deal with the screen, clearing, cursor positioning, putting characters + * into the screen image, and deleting characters. + * Really hard stuff here is utilizing insert character operations + * on intelligent terminals which differs widely from terminal to terminal. + */ +vclear() +{ + +#ifdef ADEBUG + if (trace) + tfixnl(), fprintf(trace, "------\nvclear\n"); +#endif + tputs(CL, LINES, putch); + destcol = 0; + outcol = 0; + destline = 0; + outline = 0; + if (inopen) + vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1)); +} + +/* + * Clear memory. + */ +vclrbyte(cp, i) + register char *cp; + register int i; +{ + + if (i > 0) + do + *cp++ = 0; + while (--i != 0); +} + +/* + * Clear a physical display line, high level. + */ +vclrlin(l, tp) + int l; + line *tp; +{ + + vigoto(l, 0); + if ((hold & HOLDAT) == 0) + putchar(tp > dol ? ((UPPERCASE || HZ) ? '^' : '~') : '@'); + if (state == HARDOPEN) + sethard(); + vclreol(); +} + +/* + * Clear to the end of the current physical line + */ +vclreol() +{ + register int i, j; + register char *tp; + + if (destcol == WCOLS) + return; + destline += destcol / WCOLS; + destcol %= WCOLS; + if (destline < 0 || destline > WECHO) + error("Internal error: vclreol"); + i = WCOLS - destcol; + tp = vtube[destline] + destcol; + if (CE) { + if (IN && *tp || !ateopr()) { + vcsync(); + vputp(CE, 1); + } + vclrbyte(tp, i); + return; + } + if (*tp == 0) + return; + while (i > 0 && (j = *tp & (QUOTE|TRIM))) { + if (j != ' ' && (j & QUOTE) == 0) { + destcol = WCOLS - i; + vputchar(' '); + } + --i, *tp++ = 0; + } +} + +/* + * Clear the echo line. + * If didphys then its been cleared physically (as + * a side effect of a clear to end of display, e.g.) + * so just do it logically. + * If work here is being held off, just remember, in + * heldech, if work needs to be done, don't do anything. + */ +vclrech(didphys) + bool didphys; +{ + + if (Peekkey == ATTN) + return; + if (hold & HOLDECH) { + heldech = !didphys; + return; + } + if (!didphys && (CD || CE)) { + splitw++; + /* + * If display is retained below, then MUST use CD or CE + * since we don't really know whats out there. + * Vigoto might decide (incorrectly) to do nothing. + */ + if (DB) + vgoto(WECHO, 0), vputp(CD ? CD : CE, 1); + else + vigoto(WECHO, 0), vclreol(); + splitw = 0; + didphys = 1; + } + if (didphys) + vclrbyte(vtube[WECHO], WCOLS); + heldech = 0; +} + +/* + * Fix the echo area for use, setting + * the state variable splitw so we wont rollup + * when we move the cursor there. + */ +fixech() +{ + + splitw++; + if (state != VISUAL && state != CRTOPEN) { + vclean(); + vcnt = 0; + } + vgoto(WECHO, 0); flusho(); +} + +/* + * Put the cursor ``before'' cp. + */ +vcursbef(cp) + register char *cp; +{ + + if (cp <= linebuf) + vgotoCL(value(NUMBER) << 3); + else + vgotoCL(column(cp - 1) - 1); +} + +/* + * Put the cursor ``at'' cp. + */ +vcursat(cp) + register char *cp; +{ + + if (cp <= linebuf && linebuf[0] == 0) + vgotoCL(value(NUMBER) << 3); + else + vgotoCL(column(cp - 1)); +} + +/* + * Put the cursor ``after'' cp. + */ +vcursaft(cp) + register char *cp; +{ + + vgotoCL(column(cp)); +} + +/* + * Fix the cursor to be positioned in the correct place + * to accept a command. + */ +vfixcurs() +{ + + vsetcurs(cursor); +} + +/* + * Compute the column position implied by the cursor at ``nc'', + * and move the cursor there. + */ +vsetcurs(nc) + register char *nc; +{ + register int col; + + col = column(nc); + if (linebuf[0]) + col--; + vgotoCL(col); + cursor = nc; +} + +/* + * Move the cursor invisibly, i.e. only remember to do it. + */ +vigoto(y, x) + int y, x; +{ + + destline = y; + destcol = x; +} + +/* + * Move the cursor to the position implied by any previous + * vigoto (or low level hacking with destcol/destline as in readecho). + */ +vcsync() +{ + + vgoto(destline, destcol); +} + +/* + * Goto column x of the current line. + */ +vgotoCL(x) + register int x; +{ + + if (splitw) + vgoto(WECHO, x); + else + vgoto(LINE(vcline), x); +} + +/* + * Invisible goto column x of current line. + */ +vigotoCL(x) + register int x; +{ + + if (splitw) + vigoto(WECHO, x); + else + vigoto(LINE(vcline), x); +} + +/* + * Move cursor to line y, column x, handling wraparound and scrolling. + */ +vgoto(y, x) + register int y, x; +{ + register char *tp; + register int c; + + /* + * Fold the possibly too large value of x. + */ + if (x >= WCOLS) { + y += x / WCOLS; + x %= WCOLS; + } + if (y < 0) + error("Internal error: vgoto"); + if (outcol >= WCOLS) { + if (AM) { + outline += outcol / WCOLS; + outcol %= WCOLS; + } else + outcol = WCOLS - 1; + } + + /* + * In a hardcopy or glass crt open, print the stuff + * implied by a motion, or backspace. + */ + if (state == HARDOPEN || state == ONEOPEN) { + if (y != outline) + error("Line too long for open"); + if (x + 1 < outcol - x || (outcol > x && !BS)) + destcol = 0, fgoto(); + tp = vtube[WBOT] + outcol; + while (outcol != x) + if (outcol < x) { + if (*tp == 0) + *tp = ' '; + c = *tp++ & TRIM; + vputc(c && (!OS || EO) ? c : ' '), outcol++; + } else { + if (BC) + vputp(BC, 0); + else + vputc('\b'); + outcol--; + } + destcol = outcol = x; + destline = outline; + return; + } + + /* + * If the destination position implies a scroll, do it. + */ + destline = y; + if (destline > WBOT && (!splitw || destline > WECHO)) { + endim(); + vrollup(destline); + } + + /* + * If there really is a motion involved, do it. + * The check here is an optimization based on profiling. + */ + destcol = x; + if ((destline - outline) * WCOLS != destcol - outcol) { + if (!MI) + endim(); + fgoto(); + } +} + +/* + * This is the hardest code in the editor, and deals with insert modes + * on different kinds of intelligent terminals. The complexity is due + * to the cross product of three factors: + * + * 1. Lines may display as more than one segment on the screen. + * 2. There are 2 kinds of intelligent terminal insert modes. + * 3. Tabs squash when you insert characters in front of them, + * in a way in which current intelligent terminals don't handle. + * + * The two kinds of terminals are typified by the DM2500 or HP2645 for + * one and the CONCEPT-100 or the FOX for the other. + * + * The first (HP2645) kind has an insert mode where the characters + * fall off the end of the line and the screen is shifted rigidly + * no matter how the display came about. + * + * The second (CONCEPT-100) kind comes from terminals which are designed + * for forms editing and which distinguish between blanks and ``spaces'' + * on the screen, spaces being like blank, but never having had + * and data typed into that screen position (since, e.g. a clear operation + * like clear screen). On these terminals, when you insert a character, + * the characters from where you are to the end of the screen shift + * over till a ``space'' is found, and the null character there gets + * eaten up. + * + * + * The code here considers the line as consisting of several parts + * the first part is the ``doomed'' part, i.e. a part of the line + * which is being typed over. Next comes some text up to the first + * following tab. The tab is the next segment of the line, and finally + * text after the tab. + * + * We have to consider each of these segments and the effect of the + * insertion of a character on them. On terminals like HP2645's we + * must simulate a multi-line insert mode using the primitive one + * line insert mode. If we are inserting in front of a tab, we have + * to either delete characters from the tab or insert white space + * (when the tab reaches a new spot where it gets larger) before we + * insert the new character. + * + * On a terminal like a CONCEPT our strategy is to make all + * blanks be displayed, while trying to keep the screen having ``spaces'' + * for portions of tabs. In this way the terminal hardward does some + * of the hacking for compression of tabs, although this tends to + * disappear as you work on the line and spaces change into blanks. + * + * There are a number of boundary conditions (like typing just before + * the first following tab) where we can avoid a lot of work. Most + * of them have to be dealt with explicitly because performance is + * much, much worse if we don't. + * + * A final thing which is hacked here is two flavors of insert mode. + * Datamedia's do this by an insert mode which you enter and leave + * and by having normal motion character operate differently in this + * mode, notably by having a newline insert a line on the screen in + * this mode. This generally means it is unsafe to move around + * the screen ignoring the fact that we are in this mode. + * This is possible on some terminals, and wins big (e.g. HP), so + * we encode this as a ``can move in insert capability'' mi, + * and terminals which have it can do insert mode with much less + * work when tabs are present following the cursor on the current line. + */ + +/* + * Routine to expand a tab, calling the normal Outchar routine + * to put out each implied character. Note that we call outchar + * with a QUOTE. We use QUOTE internally to represent a position + * which is part of the expansion of a tab. + */ +vgotab() +{ + register int i = (LINE(vcline) - destline) * WCOLS + destcol; + + do + (*Outchar)(QUOTE); + while (++i % value(TABSTOP)); +} + +/* + * Variables for insert mode. + */ +int linend; /* The column position of end of line */ +int tabstart; /* Column of start of first following tab */ +int tabend; /* Column of end of following tabs */ +int tabsize; /* Size of the following tabs */ +int tabslack; /* Number of ``spaces'' in following tabs */ +int inssiz; /* Number of characters to be inserted */ +int inscol; /* Column where insertion is taking place */ +int shft; /* Amount tab expansion shifted rest of line */ +int slakused; /* This much of tabslack will be used up */ + +/* + * This routine MUST be called before insert mode is run, + * and brings all segments of the current line to the top + * of the screen image buffer so it is easier for us to + * maniuplate them. + */ +vprepins() +{ + register int i; + register char *cp = vtube0; + + for (i = 0; i < DEPTH(vcline); i++) { + vmaktop(LINE(vcline) + i, cp); + cp += WCOLS; + } +} + +vmaktop(p, cp) + register int p; + char *cp; +{ + register int i; + char temp[TUBECOLS]; + + if (vtube[p] == cp) + return; + for (i = ZERO; i <= WECHO; i++) + if (vtube[i] == cp) { + copy(temp, vtube[i], WCOLS); + copy(vtube[i], vtube[p], WCOLS); + copy(vtube[p], temp, WCOLS); + vtube[i] = vtube[p]; + vtube[p] = cp; + return; + } + error("Line too long"); +} + +/* + * Insert character c at current cursor position. + * Multi-character inserts occur only as a result + * of expansion of tabs (i.e. inssize == 1 except + * for tabs) and code assumes this in several place + * to make life simpler. + */ +vinschar(c) + char c; +{ + register int i; + register char *tp; + + if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) { + /* + * Don't want to try to use terminal + * insert mode, or to try to fake it. + * Just put the character out; the screen + * will probably be wrong but we will fix it later. + */ + if (c == '\t') { + vgotab(); + return; + } + vputchar(c); + if (DEPTH(vcline) * WCOLS + !value(REDRAW) > + (destline - LINE(vcline)) * WCOLS + destcol) + return; + /* + * The next line is about to be clobbered + * make space for another segment of this line + * (on an intelligent terminal) or just remember + * that next line was clobbered (on a dumb one + * if we don't care to redraw the tail. + */ + if (AL) { + vnpins(0); + } else { + c = LINE(vcline) + DEPTH(vcline); + if (c < LINE(vcline + 1) || c > WBOT) + return; + i = destcol; + vinslin(c, 1, vcline); + DEPTH(vcline)++; + vigoto(c, i); + vprepins(); + } + return; + } + /* + * Compute the number of positions in the line image of the + * current line. This is done from the physical image + * since that is faster. Note that we have no memory + * from insertion to insertion so that routines which use + * us don't have to worry about moving the cursor around. + */ + if (*vtube0 == 0) + linend = 0; + else { + /* + * Search backwards for a non-null character + * from the end of the displayed line. + */ + i = WCOLS * DEPTH(vcline); + if (i == 0) + i = WCOLS; + tp = vtube0 + i; + while (*--tp == 0) + if (--i == 0) + break; + linend = i; + } + + /* + * We insert at a position based on the physical location + * of the output cursor. + */ + inscol = destcol + (destline - LINE(vcline)) * WCOLS; + if (c == '\t') { + /* + * Characters inserted from a tab must be + * remembered as being part of a tab, but we can't + * use QUOTE here since we really need to print blanks. + * QUOTE|' ' is the representation of this. + */ + inssiz = value(TABSTOP) - inscol % value(TABSTOP); + c = ' ' | QUOTE; + } else + inssiz = 1; + + /* + * If the text to be inserted is less than the number + * of doomed positions, then we don't need insert mode, + * rather we can just typeover. + */ + if (inssiz <= doomed) { + endim(); + if (inscol != linend) + doomed -= inssiz; + do + vputchar(c); + while (--inssiz); + return; + } + + /* + * Have to really do some insertion, thus + * stake out the bounds of the first following + * group of tabs, computing starting position, + * ending position, and the number of ``spaces'' therein + * so we can tell how much it will squish. + */ + tp = vtube0 + inscol; + for (i = inscol; i < linend; i++) + if (*tp++ & QUOTE) { + --tp; + break; + } + tabstart = tabend = i; + tabslack = 0; + while (tabend < linend) { + i = *tp++; + if ((i & QUOTE) == 0) + break; + if ((i & TRIM) == 0) + tabslack++; + tabsize++; + tabend++; + } + tabsize = tabend - tabstart; + + /* + * For HP's and DM's, e.g. tabslack has no meaning. + */ + if (!IN) + tabslack = 0; +#ifdef IDEBUG + if (trace) { + fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ", + inscol, inssiz, tabstart); + fprintf(trace, "tabend %d, tabslack %d, linend %d\n", + tabend, tabslack, linend); + } +#endif + + /* + * The real work begins. + */ + slakused = 0; + shft = 0; + if (tabsize) { + /* + * There are tabs on this line. + * If they need to expand, then the rest of the line + * will have to be shifted over. In this case, + * we will need to make sure there are no ``spaces'' + * in the rest of the line (on e.g. CONCEPT-100) + * and then grab another segment on the screen if this + * line is now deeper. We then do the shift + * implied by the insertion. + */ + if (inssiz >= doomed + value(TABSTOP) - tabstart % value(TABSTOP)) { + if (IN) + vrigid(); + vneedpos(value(TABSTOP)); + vishft(); + } + } else if (inssiz > doomed) + /* + * No tabs, but line may still get deeper. + */ + vneedpos(inssiz - doomed); + /* + * Now put in the inserted characters. + */ + viin(c); + + /* + * Now put the cursor in its final resting place. + */ + destline = LINE(vcline); + destcol = inscol + inssiz; + vcsync(); +} + +/* + * Rigidify the rest of the line after the first + * group of following tabs, typing blanks over ``spaces''. + */ +vrigid() +{ + register int col; + register char *tp = vtube0 + tabend; + + for (col = tabend; col < linend; col++) + if ((*tp++ & TRIM) == 0) { + endim(); + vgotoCL(col); + vputchar(' ' | QUOTE); + } +} + +/* + * We need cnt more positions on this line. + * Open up new space on the screen (this may in fact be a + * screen rollup). + * + * On a dumb terminal we may infact redisplay the rest of the + * screen here brute force to keep it pretty. + */ +vneedpos(cnt) + int cnt; +{ + register int d = DEPTH(vcline); + register int rmdr = d * WCOLS - linend; + + if (cnt <= rmdr - IN) + return; + endim(); + vnpins(1); +} + +vnpins(dosync) + int dosync; +{ + register int d = DEPTH(vcline); + register int e; + + e = LINE(vcline) + DEPTH(vcline); + if (e < LINE(vcline + 1)) { + vigoto(e, 0); + vclreol(); + return; + } + DEPTH(vcline)++; + if (e < WECHO) { + e = vglitchup(vcline, d); + vigoto(e, 0); vclreol(); + if (dosync) { + Outchar = vputchar; + vsync(e + 1); + Outchar = vinschar; + } + } else { + vup1(); + vigoto(WBOT, 0); + vclreol(); + } + vprepins(); +} + +/* + * Do the shift of the next tabstop implied by + * insertion so it expands. + */ +vishft() +{ + int tshft = 0; + int j; + register int i; + register char *tp = vtube0; + register char *up; + short oldhold = hold; + + shft = value(TABSTOP); + hold |= HOLDPUPD; + if (!IM && !EI) { + /* + * Dumb terminals are easy, we just have + * to retype the text. + */ + vigotoCL(tabend + shft); + up = tp + tabend; + for (i = tabend; i < linend; i++) + vputchar(*up++); + } else if (IN) { + /* + * CONCEPT-like terminals do most of the work for us, + * we don't have to muck with simulation of multi-line + * insert mode. Some of the shifting may come for free + * also if the tabs don't have enough slack to take up + * all the inserted characters. + */ + i = shft; + slakused = inssiz - doomed; + if (slakused > tabslack) { + i -= slakused - tabslack; + slakused -= tabslack; + } + if (i > 0 && tabend != linend) { + tshft = i; + vgotoCL(tabend); + goim(); + do + vputchar(' ' | QUOTE); + while (--i); + } + } else { + /* + * HP and Datamedia type terminals have to have multi-line + * insert faked. Hack each segment after where we are + * (going backwards to where we are.) We then can + * hack the segment where the end of the first following + * tab group is. + */ + for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) { + vgotoCL(j * WCOLS); + goim(); + up = tp + j * WCOLS - shft; + i = shft; + do + vputchar(*up++); + while (--i); + } + vigotoCL(tabstart); + i = shft - (inssiz - doomed); + if (i > 0) { + tabslack = inssiz - doomed; + vcsync(); + goim(); + do + vputchar(' '); + while (--i); + } + } + /* + * Now do the data moving in the internal screen + * image which is common to all three cases. + */ + tp += linend; + up = tp + shft; + i = linend - tabend; + if (i > 0) + do + *--up = *--tp; + while (--i); + if (IN && tshft) { + i = tshft; + do + *--up = ' ' | QUOTE; + while (--i); + } + hold = oldhold; +} + +/* + * Now do the insert of the characters (finally). + */ +viin(c) + char c; +{ + register char *tp, *up; + register int i, j; + register bool noim = 0; + int remdoom; + short oldhold = hold; + + hold |= HOLDPUPD; + if (tabsize && (IM && EI) && inssiz - doomed > tabslack) + /* + * There is a tab out there which will be affected + * by the insertion since there aren't enough doomed + * characters to take up all the insertion and we do + * have insert mode capability. + */ + if (inscol + doomed == tabstart) { + /* + * The end of the doomed characters sits right at the + * start of the tabs, then we don't need to use insert + * mode; unless the tab has already been expanded + * in which case we MUST use insert mode. + */ + slakused = 0; + noim = !shft; + } else { + /* + * The last really special case to handle is case + * where the tab is just sitting there and doesn't + * have enough slack to let the insertion take + * place without shifting the rest of the line + * over. In this case we have to go out and + * delete some characters of the tab before we start + * or the answer will be wrong, as the rest of the + * line will have been shifted. This code means + * that terminals with only insert chracter (no + * delete character) won't work correctly. + */ + i = inssiz - doomed - tabslack - slakused; + i %= value(TABSTOP); + if (i > 0) { + vgotoCL(tabstart); + godm(); + for (i = inssiz - doomed - tabslack; i > 0; i--) + vputp(DC, DEPTH(vcline)); + enddm(); + } + } + + /* + * Now put out the characters of the actual insertion. + */ + vigotoCL(inscol); + remdoom = doomed; + for (i = inssiz; i > 0; i--) { + if (remdoom > 0) { + remdoom--; + endim(); + } else if (noim) + endim(); + else if (IM && EI) { + vcsync(); + goim(); + } + vputchar(c); + } + + if (!IM || !EI) { + /* + * We are a dumb terminal; brute force update + * the rest of the line; this is very much an n^^2 process, + * and totally unreasonable at low speed. + * + * You asked for it, you get it. + */ + tp = vtube0 + inscol + doomed; + for (i = inscol + doomed; i < tabstart; i++) + vputchar(*tp++); + hold = oldhold; + vigotoCL(tabstart + inssiz - doomed); + for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) + vputchar(' ' | QUOTE); + } else { + if (!IN) { + /* + * On terminals without multi-line + * insert in the hardware, we must go fix the segments + * between the inserted text and the following + * tabs, if they are on different lines. + * + * Aaargh. + */ + tp = vtube0; + for (j = (inscol + inssiz - 1) / WCOLS + 1; + j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) { + vgotoCL(j * WCOLS); + i = inssiz - doomed; + up = tp + j * WCOLS - i; + goim(); + do + vputchar(*up++); + while (--i && *up); + } + } else { + /* + * On terminals with multi line inserts, + * life is simpler, just reflect eating of + * the slack. + */ + tp = vtube0 + tabend; + for (i = tabsize - (inssiz - doomed); i >= 0; i--) { + if ((*--tp & (QUOTE|TRIM)) == QUOTE) { + --tabslack; + if (tabslack >= slakused) + continue; + } + *tp = ' ' | QUOTE; + } + } + /* + * Blank out the shifted positions to be tab positions. + */ + if (shft) { + tp = vtube0 + tabend + shft; + for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) + if ((*--tp & QUOTE) == 0) + *tp = ' ' | QUOTE; + } + } + + /* + * Finally, complete the screen image update + * to reflect the insertion. + */ + hold = oldhold; + tp = vtube0 + tabstart; up = tp + inssiz - doomed; + for (i = tabstart; i > inscol + doomed; i--) + *--up = *--tp; + for (i = inssiz; i > 0; i--) + *--up = c; + doomed = 0; +} + +/* + * Go into ``delete mode''. If the + * sequence which goes into delete mode + * is the same as that which goes into insert + * mode, then we are in delete mode already. + */ +godm() +{ + + if (insmode) { + if (eq(DM, IM)) + return; + endim(); + } + vputp(DM, 0); +} + +/* + * If we are coming out of delete mode, but + * delete and insert mode end with the same sequence, + * it wins to pretend we are now in insert mode, + * since we will likely want to be there again soon + * if we just moved over to delete space from part of + * a tab (above). + */ +enddm() +{ + + if (eq(DM, IM)) { + insmode = 1; + return; + } + vputp(ED, 0); +} + +/* + * In and out of insert mode. + * Note that the code here demands that there be + * a string for insert mode (the null string) even + * if the terminal does all insertions a single character + * at a time, since it branches based on whether IM is null. + */ +goim() +{ + + if (!insmode) + vputp(IM, 0); + insmode = 1; +} + +endim() +{ + + if (insmode) { + vputp(EI, 0); + insmode = 0; + } +} + +/* + * Put the character c on the screen at the current cursor position. + * This routine handles wraparound and scrolling and understands not + * to roll when splitw is set, i.e. we are working in the echo area. + * There is a bunch of hacking here dealing with the difference between + * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also + * code to deal with terminals which overstrike, including CRT's where + * you can erase overstrikes with some work. CRT's which do underlining + * implicitly which has to be erased (like CONCEPTS) are also handled. + */ +vputchar(c) + register int c; +{ + register char *tp; + register int d; + + c &= (QUOTE|TRIM); +#ifdef TRACE + if (trace) + tracec(c); +#endif + /* Patch to fix problem of >79 chars on echo line: don't echo extras */ + if (destcol >= WCOLS-1 && splitw && destline == WECHO) + return; + if (destcol >= WCOLS) { + destline += destcol / WCOLS; + destcol %= WCOLS; + } + if (destline > WBOT && (!splitw || destline > WECHO)) + vrollup(destline); + tp = vtube[destline] + destcol; + switch (c) { + + case '\t': + vgotab(); + return; + + case ' ': + /* + * We can get away without printing a space in a number + * of cases, but not always. We get away with doing nothing + * if we are not in insert mode, and not on a CONCEPT-100 + * like terminal, and either not in hardcopy open or in hardcopy + * open on a terminal with no overstriking, provided, + * in all cases, that nothing has ever been displayed + * at this position. Ugh. + */ + if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) { + *tp = ' '; + destcol++; + return; + } + goto def; + + case QUOTE: + if (insmode) { + /* + * When in insert mode, tabs have to expand + * to real, printed blanks. + */ + c = ' ' | QUOTE; + goto def; + } + if (*tp == 0) { + /* + * A ``space''. + */ + if ((hold & HOLDPUPD) == 0) + *tp = QUOTE; + destcol++; + return; + } + /* + * A ``space'' ontop of a part of a tab. + */ + if (*tp & QUOTE) { + destcol++; + return; + } + c = ' ' | QUOTE; + /* fall into ... */ + +def: + default: + d = *tp & TRIM; + /* + * Now get away with doing nothing if the characters + * are the same, provided we are not in insert mode + * and if we are in hardopen, that the terminal has overstrike. + */ + if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) { + if ((hold & HOLDPUPD) == 0) + *tp = c; + destcol++; + return; + } + /* + * Backwards looking optimization. + * The low level cursor motion routines will use + * a cursor motion right sequence to step 1 character + * right. On, e.g., a DM3025A this is 2 characters + * and printing is noticeably slower at 300 baud. + * Since the low level routines are not allowed to use + * spaces for positioning, we discover the common + * case of a single space here and force a space + * to be printed. + */ + if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) { + vputc(' '); + outcol++; + } + + /* + * This is an inline expansion a call to vcsync() dictated + * by high frequency in a profile. + */ + if (outcol != destcol || outline != destline) + vgoto(destline, destcol); + + /* + * Deal with terminals which have overstrike. + * We handle erasing general overstrikes, erasing + * underlines on terminals (such as CONCEPTS) which + * do underlining correctly automatically (e.g. on nroff + * output), and remembering, in hardcopy mode, + * that we have overstruct something. + */ + if (!insmode && d && d != ' ' && d != (c & TRIM)) { + if (EO && (OS || UL && (c == '_' || d == '_'))) { + vputc(' '); + outcol++, destcol++; + back1(); + } else + rubble = 1; + } + + /* + * Unless we are just bashing characters around for + * inner working of insert mode, update the display. + */ + if ((hold & HOLDPUPD) == 0) + *tp = c; + + /* + * In insert mode, put out the IC sequence, padded + * based on the depth of the current line. + * A terminal which had no real insert mode, rather + * opening a character position at a time could do this. + * Actually should use depth to end of current line + * but this rarely matters. + */ + if (insmode) + vputp(IC, DEPTH(vcline)); + vputc(c & TRIM); + + /* + * In insert mode, IP is a post insert pad. + */ + if (insmode) + vputp(IP, DEPTH(vcline)); + destcol++, outcol++; + + /* + * CONCEPT braindamage in early models: after a wraparound + * the next newline is eaten. It's hungry so we just + * feed it now rather than worrying about it. + */ + if (XN && outcol % WCOLS == 0) + vputc('\n'); + } +} + +/* + * Delete display positions stcol through endcol. + * Amount of use of special terminal features here is limited. + */ +physdc(stcol, endcol) + int stcol, endcol; +{ + register char *tp, *up; + char *tpe; + register int i; + register int nc = endcol - stcol; + +#ifdef IDEBUG + if (trace) + tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol); +#endif + if (!DC || nc <= 0) + return; + if (IN) { + /* + * CONCEPT-100 like terminal. + * If there are any ``spaces'' in the material to be + * deleted, then this is too hard, just retype. + */ + vprepins(); + up = vtube0 + stcol; + i = nc; + do + if ((*up++ & (QUOTE|TRIM)) == QUOTE) + return; + while (--i); + i = 2 * nc; + do + if (*up == 0 || (*up++ & QUOTE) == QUOTE) + return; + while (--i); + vgotoCL(stcol); + } else { + /* + * HP like delete mode. + * Compute how much text we are moving over by deleting. + * If it appears to be faster to just retype + * the line, do nothing and that will be done later. + * We are assuming 2 output characters per deleted + * characters and that clear to end of line is available. + */ + i = stcol / WCOLS; + if (i != endcol / WCOLS) + return; + i += LINE(vcline); + stcol %= WCOLS; + endcol %= WCOLS; + up = vtube[i]; tp = up + endcol; tpe = up + WCOLS; + while (tp < tpe && *tp) + tp++; + if (tp - (up + stcol) < 2 * nc) + return; + vgoto(i, stcol); + } + + /* + * Go into delete mode and do the actual delete. + * Padding is on DC itself. + */ + godm(); + for (i = nc; i > 0; i--) + vputp(DC, DEPTH(vcline)); + vputp(ED, 0); + + /* + * Straighten up. + * With CONCEPT like terminals, characters are pulled left + * from first following null. HP like terminals shift rest of + * this (single physical) line rigidly. + */ + if (IN) { + up = vtube0 + stcol; + tp = vtube0 + endcol; + while (i = *tp++) { + if ((i & (QUOTE|TRIM)) == QUOTE) + break; + *up++ = i; + } + do + *up++ = i; + while (--nc); + } else { + copy(up + stcol, up + endcol, WCOLS - endcol); + vclrbyte(tpe - nc, nc); + } +} + +#ifdef TRACE +tfixnl() +{ + + if (trubble || techoin) + fprintf(trace, "\n"); + trubble = 0, techoin = 0; +} + +tvliny() +{ + register int i; + + if (!trace) + return; + tfixnl(); + fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline); + for (i = 0; i <= vcnt; i++) { + fprintf(trace, "%d", LINE(i)); + if (FLAGS(i) & VDIRT) + fprintf(trace, "*"); + if (DEPTH(i) != 1) + fprintf(trace, "<%d>", DEPTH(i)); + if (i < vcnt) + fprintf(trace, " "); + } + fprintf(trace, "\n"); +} + +tracec(c) + char c; +{ + + if (!techoin) + trubble = 1; + if (c == ESCAPE) + fprintf(trace, "$"); + else if (c < ' ' || c == DELETE) + fprintf(trace, "^%c", ctlof(c)); + else + fprintf(trace, "%c", c); +} +#endif + +/* + * Put a character with possible tracing. + */ +vputch(c) + int c; +{ + +#ifdef TRACE + if (trace) + tracec(c); +#endif + vputc(c); +}