/* * 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.49 (gritter) 2/15/05"; #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) 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)() = 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 && (d = colsc(c&TRIM&~MULTICOL)) > 1) { if ((hold & HOLDPUPD) == 0) *tp |= MULTICOL; while (--d) { if ((hold & HOLDPUPD) == 0) *++tp = MULTICOL; 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); }