diff options
| author | Thomas Ulmer <thomasmulmer02@gmail.com> | 2026-02-23 16:54:28 -0800 |
|---|---|---|
| committer | Thomas Ulmer <thomasmulmer02@gmail.com> | 2026-02-23 16:54:28 -0800 |
| commit | 15bd7946cc838a3151c357e4b0bc1ab85eecda62 (patch) | |
| tree | 56977cb9bfc4349f46e2c608503a298df30ca957 /ex_vput.c | |
add musl and vi
Diffstat (limited to 'ex_vput.c')
| -rw-r--r-- | ex_vput.c | 1630 |
1 files changed, 1630 insertions, 0 deletions
diff --git a/ex_vput.c b/ex_vput.c new file mode 100644 index 0000000..1851b67 --- /dev/null +++ b/ex_vput.c @@ -0,0 +1,1630 @@ +/* + * This code contains changes by + * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved. + * + * Conditions 1, 2, and 4 and the no-warranty notice below apply + * to these changes. + * + * + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * Redistributions of source code and documentation must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * Neither the name of Caldera International, Inc. nor the names of + * other contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE + * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +#ifdef DOSCCS +static char sccsid[] = "@(#)ex_vput.c 1.52 (gritter) 12/25/06"; +#endif +#endif + +/* from ex_vput.c 7.4.1 (2.11BSD GTE) 12/9/94 */ + +#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. + */ +void +vclear(void) +{ + +#ifdef ADEBUG + if (trace) + tfixnl(), fprintf(trace, "------\nvclear\n"); +#endif + tputs(CL, TLINES, putch); + destcol = 0; + outcol = 0; + destline = 0; + outline = 0; + if (inopen) + vclrcell(vtube0, WCOLS * (WECHO - ZERO + 1)); +} + +/* + * Clear memory. + */ +void +vclrcell(register cell *cp, register int i) +{ + if (i > 0) + do + *cp++ = 0; + while (--i != 0); +} + +/* + * Clear a physical display line, high level. + */ +void +vclrlin(int l, line *tp) +{ + + vigoto(l, 0); + if ((hold & HOLDAT) == 0) +#ifndef UCVISUAL + putchar(tp > dol ? '~' : '@'); +#else + putchar(tp > dol ? ((UPPERCASE || xHZ) ? '^' : '~') : '@'); +#endif + if (state == HARDOPEN) + sethard(); + vclreol(); +} + +/* + * Clear to the end of the current physical line + */ +void +vclreol(void) +{ + register int i, j; + register cell *tp; + + if (destcol == WCOLS) + return; + destline += destcol / WCOLS; + destcol %= WCOLS; + if (destline < 0 || destline > WECHO) + error(catgets(catd, 1, 237, "Internal error: vclreol")); + i = WCOLS - destcol; + tp = vtube[destline] + destcol; + if (CE) { + if (IN && *tp || !ateopr()) { + vcsync(); + vputp(CE, 1); + } + vclrcell(tp, i); + return; + } + if (*tp == 0) + return; + while (i > 0 && (j = *tp & (QUOTE|TRIM|MULTICOL))) { + 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. + */ +void +vclrech(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 { + if (XT) { + /* + * This code basically handles the t1061 + * where positioning at (0, 0) won't work + * because the terminal won't let you put + * the cursor on it's magic cookie. + * + * Should probably be XS above, or even a + * new X? glitch, but right now t1061 is the + * only terminal with XT. + */ + vgoto(WECHO, 0); + vputp(DL, 1); + } else { + vigoto(WECHO, 0); + vclreol(); + } + } + splitw = 0; + didphys = 1; + } + if (didphys && vtube) + vclrcell(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. + */ +void +fixech(void) +{ + + splitw++; + if (state != VISUAL && state != CRTOPEN) { + vclean(); + vcnt = 0; + } + vgoto(WECHO, 0); flusho(); +} + +/* + * Put the cursor ``before'' cp. + */ +void +vcursbef(register char *cp) +{ + + if (cp <= linebuf) + vgotoCL(value(NUMBER) << 3); + else + vgotoCL(column(cp - 1) - 1); +} + +/* + * Put the cursor ``at'' cp. + */ +void +vcursat(register char *cp) +{ + + if (cp <= linebuf && linebuf[0] == 0) + vgotoCL(value(NUMBER) << 3); + else + vgotoCL(column(cp + skipleft(linebuf, cp))); +} + +/* + * Put the cursor ``after'' cp. + */ +void +vcursaft(register char *cp) +{ + + vgotoCL(column(cp)); +} + +/* + * Fix the cursor to be positioned in the correct place + * to accept a command. + */ +void +vfixcurs(void) +{ + + vsetcurs(cursor); +} + +/* + * Compute the column position implied by the cursor at ``nc'', + * and move the cursor there. + */ +void +vsetcurs(register char *nc) +{ + register int col; + + col = column(nc); + if (linebuf[0]) + col--; + vgotoCL(col); + cursor = vcolbp; +} + +/* + * Move the cursor invisibly, i.e. only remember to do it. + */ +void +vigoto(int y, int 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). + */ +void +vcsync(void) +{ + + vgoto(destline, destcol); +} + +/* + * Goto column x of the current line. + */ +void +vgotoCL(register int x) +{ + + if (splitw) + vgoto(WECHO, x); + else + vgoto(LINE(vcline), x); +} + +/* + * Invisible goto column x of current line. + */ +void +vigotoCL(register int x) +{ + + if (splitw) + vigoto(WECHO, x); + else + vigoto(LINE(vcline), x); +} + +/* + * Move cursor to line y, column x, handling wraparound and scrolling. + */ +void +vgoto(register int y, register int x) +{ + register cell *tp; + register int c; + + /* + * Fold the possibly too large value of x. + */ + if (x >= WCOLS) { + y += x / WCOLS; + x %= WCOLS; + } +#ifdef MB + if (y >= 0 && y <= WLINES && mb_cur_max > 1 && !insmode) { + while (x > 0 && (vtube[y][x]&(MULTICOL|TRIM)) == MULTICOL && + vtube[y][x-1] & MULTICOL && + (vtube[y][x-1]&(MULTICOL|TRIM)) != MULTICOL) + x--; + } +#endif /* MB */ + if (y < 0) + error(catgets(catd, 1, 238, "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(catgets(catd, 1, 239, "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. + */ +void +vgotab(void) +{ + register int i = tabcol(destcol, value(TABSTOP)) - destcol; + + do + (*Outchar)(QUOTE); + while (--i); +} + +/* + * 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 insmc0; /* Multi-column character before insertion */ +int insmc1; /* Multi-column character at insertion */ +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. + */ +void +vprepins(void) +{ + register int i; + register cell *cp = vtube0; + + for (i = 0; i < DEPTH(vcline); i++) { + vmaktop(LINE(vcline) + i, cp); + cp += WCOLS; + } +} + +void +vmaktop(register int p, cell *cp) +{ + register int i; + cell temp[TUBECOLS]; + + if (p < 0 || vtube[p] == cp) + return; + for (i = ZERO; i <= WECHO; i++) + if (vtube[i] == cp) { + copy(temp, vtube[i], WCOLS * sizeof *temp); + copy(vtube[i], vtube[p], WCOLS * sizeof *temp); + copy(vtube[p], temp, WCOLS * sizeof *temp); + vtube[i] = vtube[p]; + vtube[p] = cp; + return; + } + error(catgets(catd, 1, 240, "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. + */ +int +vinschar(int c) +/* int c; /\* mjm: char --> int */ +{ + register int i; + register cell *tp; + char *OIM; + bool OXN; + int noim, filler = 0; + + insmc1 = colsc(c) - 1; + 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 c; + } + vputchar(c); +#ifdef MB + if (insmc1 == 0 && (vtube0[destcol]&(TRIM|MULTICOL))==MULTICOL) + vtube0[destcol] = INVBIT; +#endif /* MB */ + if (DEPTH(vcline) * WCOLS + !value(REDRAW) > + (destline - LINE(vcline)) * WCOLS + destcol) + return c; + /* + * 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 c; + i = destcol; + vinslin(c, 1, vcline); + DEPTH(vcline)++; + vigoto(c, i); + vprepins(); + } + return c; + } + /* + * 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 + insmc1; + } + + /* + * We insert at a position based on the physical location + * of the output cursor. + */ + inscol = destcol + (destline - LINE(vcline)) * WCOLS; + insmc0 = 0; +#ifdef MB + i = 0; + while (inscol+i < LBSIZE && vtube0[inscol+i]&MULTICOL && + (vtube0[inscol+insmc0+i]&(MULTICOL|TRIM)) != MULTICOL) + i++; + while (inscol+insmc0+i < LBSIZE && + (vtube0[inscol+insmc0+i]&(MULTICOL|TRIM)) == MULTICOL) + insmc0++; +#endif /* MB */ + 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 = tabcol(inscol+insmc0, value(TABSTOP)) - inscol - insmc0; + 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 + insmc1 <= doomed) { + endim(); + if (inscol + insmc0 != linend) + doomed -= inssiz + insmc1; +#ifdef MB + if (insmc1 == 0 && c != '\t' && + vtube0[inscol+insmc0] & MULTICOL) + vtube0[inscol+insmc0] = INVBIT; +#endif /* MB */ + do + vputchar(c); + while (--inssiz); + return c; + } + + /* + * 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 + insmc0; + for (i = inscol + insmc0; 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|MULTICOL)) == 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 + OIM = IM; + OXN = XN; + noim = 0; +#ifdef MB + if (mb_cur_max > 1) { + if (destcol + 1 + insmc1 == WCOLS + 1) { + noim = 1; + if (insmc1 == 1 && insmc0 == 0) + filler = 1; + } + for (i = inscol; vtube0[i]; i++) + if (i + 1 >= WCOLS && vtube0[i] & MULTICOL) { + noim = 1; + break; + } + } +#endif /* MB */ + if (noim) { + endim(); + IM = 0; + XN = 0; + } + + /* + * 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 + tabcol(tabstart, value(TABSTOP)) - tabstart) { + if (IN) + vrigid(); + vneedpos(value(TABSTOP)); + vishft(); + } + } else if (inssiz + insmc1 > doomed) + /* + * No tabs, but line may still get deeper. + */ + vneedpos(inssiz + insmc1 - doomed); + /* + * Now put in the inserted characters. + */ + viin(c); + + /* + * Now put the cursor in its final resting place. + */ + destline = LINE(vcline); + destcol = inscol + inssiz + insmc1 + filler; + vcsync(); + if (IM != OIM) { + IM = OIM; + XN = OXN; + } + return c; +} + +/* + * Rigidify the rest of the line after the first + * group of following tabs, typing blanks over ``spaces''. + */ +void +vrigid(void) +{ + register int col; + register cell *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. + */ +void +vneedpos(int npcnt) +{ + register int d = DEPTH(vcline); + register int rmdr = d * WCOLS - linend; + + /* + * Delete the showmode string on wraparound to last line. Cannot use + * vclrech() since the mode string is printed on the echo area, but + * not actually a part of it. + */ + if (value(SHOWMODE) && (value(REDRAW) || (IM && EI)) && + npcnt == rmdr - IN && LINE(vcline) + d == WECHO) { + int sdc, sdl; + char *ocurs; + + endim(); + sdc = destcol, sdl = destline, ocurs = cursor; + splitw++; + vgoto(WECHO, 0); + if (CD) { + vputp(CD, 1); + } else if (CE) { + vputp(CE, 1); + } else { + int i; + + for (i = 1; i < WCOLS; i++) + vputchar(' '); + } + destcol = sdc, destline = sdl; cursor = ocurs; + splitw = 0; + } + if (npcnt <= rmdr - IN) + return; + endim(); + vnpins(1); +} + +void +vnpins(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) { + int (*Ooutchar)(int) = Outchar; + Outchar = vputchar; + vsync(e + 1); + Outchar = Ooutchar; + } + } else { + vup1(); + vigoto(WBOT, 0); + vclreol(); + } + vprepins(); +} + +/* + * Do the shift of the next tabstop implied by + * insertion so it expands. + */ +void +vishft(void) +{ + int tshft = 0; + int j; + register int i; + register cell *tp = vtube0; + register cell *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 { + if (*up) + vputchar(*up++); + else + break; + } 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). + */ +void +viin(int c) +/* int c; /\* mjm: char --> int */ +{ + register cell *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 + insmc0 + 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 > insmc1) { + remdoom--; + endim(); + } else if (noim || insmc1 && remdoom == insmc1) + endim(); + else if (IM && EI) { + vcsync(); + goim(); + } + vputchar(c); + } + + if (!IM || !EI || remdoom && remdoom == insmc1) { + /* + * 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 + insmc0 - doomed); + for (i = tabsize - (inssiz - insmc0 - 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 + insmc0 + inssiz - 1) / WCOLS + 1; + j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) { + vgotoCL(j * WCOLS); + i = inssiz - doomed + insmc1; + 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 + insmc1 - 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 + insmc1 + inssiz - doomed; + for (i = tabstart; i > inscol + doomed; i--) + *--up = *--tp; +#ifdef MB + for (i = insmc1; i > 0; i--) + *--up = MULTICOL; +#endif + for (i = inssiz; i > 0; i--) + *--up = c | (insmc1 ? MULTICOL : 0); + 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. + */ +void +godm(void) +{ + + 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). + */ +void +enddm(void) +{ + + 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. + */ +void +goim(void) +{ + + if (!insmode) + vputp(IM, 0); + insmode = 1; +} + +void +endim(void) +{ + + 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. + */ +int +vputchar(register int c) +{ + register cell *tp; + register int d, m, n; + +#ifndef BIT8 + c &= (QUOTE|TRIM); +#endif +#ifdef TRACE + if (trace) + tracec(c); +#endif + /* Fix problem of >79 chars on echo line. */ + if (destcol >= WCOLS-1 && splitw && destline == WECHO) + pofix(); +#ifdef MB + if (mb_cur_max > 1) { + if (c == MULTICOL) + return c; + /* + * If a multicolumn character extends beyond the screen + * width, it must be put on the next line. A tilde is + * printed as an indicator but must disappear when the + * text is moved at a later time. + */ + if (c == ('~'|INVBIT|QUOTE)) + c = '~'|INVBIT; + else if (c == ('~'|INVBIT)) + return c; + else if (destcol < WCOLS && destcol + + colsc(c==QUOTE ? ' ' : c&TRIM&~MULTICOL) - 1 + >= WCOLS) + vputchar('~'|INVBIT|QUOTE); + } +#endif /* MB */ + if (destcol >= WCOLS) { + destline += destcol / WCOLS; + destcol %= WCOLS; + } + if (destline > WBOT && (!splitw || destline > WECHO)) + vrollup(destline); + tp = vtube[destline] + destcol; + if (c == 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 c; + } + /* + * A ``space'' ontop of a part of a tab. + */ + if (*tp & QUOTE) { + destcol++; + return c; + } + c = ' ' | QUOTE; + goto def; + } + +#ifdef notdef +#ifdef BIT8 + if (c == ' ' | QUOTE) { + c = ' '; + goto def; + } +#endif +#endif + switch (c) { + + case '\t': + vgotab(); + return c; + + 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"E)) { + *tp = ' '; + destcol++; + return c; + } + goto def; + +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 & ~MULTICOL) == (c & TRIM & ~MULTICOL) && !insmode && + (state != HARDOPEN || OS) && c != MULTICOL) { + n = colsc(d); + for (m = 1; m < n; m++) + if ((tp[m] & (MULTICOL|TRIM)) != MULTICOL) + break; + if (m == n) { + if ((hold & HOLDPUPD) == 0) + *tp = c | (n > 1 ? MULTICOL : 0); + destcol += n; + return c; + } + } + /* + * 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. + */ +#ifdef notdef + if (insmode) +#else + /* + * It seems today's termcap writers consider this + * an either-or situation; if both im and ic + * are used vi puts out additional spaces. + * + * SVR4 ex does not include this change. If it hits + * your terminal, change back to the old way and + * mail me a description. + * + * GR July 2000 + */ + if (insmode && (!IM || !*IM)) +#endif /* !notdef */ + { + n = colsc(c&TRIM); + for (m = 0; m < n; m++) + 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. + * Fixed to use return linefeed to work right + * on vt100/tab132 as well as concept. + */ + if (XN && outcol % WCOLS == 0) { + vputc('\r'); + vputc('\n'); + } + } +#ifdef MB + if (mb_cur_max > 1) { + if ((d = colsc(c&TRIM&~MULTICOL)) > 1) { + if ((hold & HOLDPUPD) == 0) + *tp |= MULTICOL; + while (--d) { + if ((hold & HOLDPUPD) == 0) + *++tp = MULTICOL; + destcol++; + outcol++; + } + } else if (d == 0) { + destcol--; + outcol--; + } + } +#endif /* MB */ + return c; +} + +/* + * Delete display positions stcol through endcol. + * Amount of use of special terminal features here is limited. + */ +void +physdc(int stcol, int endcol) +{ + register cell *tp, *up; + cell *tpe = NULL; + 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) * sizeof *up); + vclrcell(tpe - nc, nc); + } +} + +#ifdef TRACE +void +tfixnl(void) +{ + + if (trubble || techoin) + fprintf(trace, "\n"); + trubble = 0, techoin = 0; +} + +void +tvliny(void) +{ + 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"); +} + +void +tracec(int c) +/* int c; /\* mjm: char --> int */ +{ + + if (!techoin) + trubble = 1; + if (c == ESCAPE) + fprintf(trace, "$"); + else if (c & QUOTE) /* mjm: for 3B (no sign extension) */ + fprintf(trace, "~%c", ctlof(c&TRIM)); + else if (c < ' ' || c == DELETE) + fprintf(trace, "^%c", ctlof(c)); + else + fprintf(trace, "%c", c); +} +#endif + +/* + * Put a character with possible tracing. + */ +int +vputch(int c) +{ + +#ifdef TRACE + if (trace) + tracec(c); +#endif + return vputc(c); +} |
