/* * Clear to the end of the current physical line */ void vclreol(void) { register int i; register char *tp; if (destcol == VCOLUMNS) return; destline += destcol / VCOLUMNS; destcol %= VCOLUMNS; #ifdef DEBUG if (destline < 0 || destline > TUBELINES) error("Internal error: vclreol@- please tell someone"); #endif i = VCOLUMNS - destcol; tp = vtube[destline] + destcol; if (*tp == 0) return; if (CE) { vcsync(); vputs(CE); vclrbyte(tp, i); return; } while (i > 0 && *tp != 0) { vputchar(' '); --i, tp++; } }
/* * 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 to the end of the current physical line */ void vclreol(void) { int i; wchar_t *tp, j; #ifdef TRACE if (trace) fprintf(trace, "vclreol(), destcol %d, ateopr() %d\n", destcol, ateopr()); #endif if (destcol == WCOLS) return; destline += destcol / WCOLS; destcol %= WCOLS; if (destline < 0 || destline > WECHO) error(gettext("Internal error: vclreol")); i = WCOLS - destcol; tp = vtube[destline] + destcol; if (clr_eol) { if (insert_null_glitch && *tp || !ateopr()) { vcsync(); vputp(clr_eol, 1); } vclrbyte(tp, i); return; } if (*tp == 0) return; while (i > 0 && (j = *tp & (QUOTE|TRIM))) { if (j != ' ' && (j & QUOTE) == 0) { destcol = WCOLS - i; (void) vputchar(' '); } --i, *tp++ = 0; } }
/* * Get a line into genbuf after gcursor. * Cnt limits the number of input characters * accepted and is used for handling the replace * single character command. Aescaped is the location * where we stick a termination indicator (whether we * ended with an ESCAPE or a newline/return. * * We do erase-kill type processing here and also * are careful about the way we do this so that it is * repeatable. (I.e. so that your kill doesn't happen, * when you repeat an insert if it was escaped with \ the * first time you did it. */ char * vgetline(int cnt, char *gcursor, bool *aescaped) { register int c, ch; register char *cp; int x, y, iwhite; char *iglobp; void (*OO)() = Outchar; /* * Clear the output state and counters * for autoindent backwards motion (counts of ^D, etc.) * Remember how much white space at beginning of line so * as not to allow backspace over autoindent. */ *aescaped = 0; ogcursor = gcursor; flusho(); CDCNT = 0; HADUP = 0; HADZERO = 0; gobbled = 0; iwhite = whitecnt(genbuf); iglobp = vglobp; /* * Carefully avoid using vinschar in the echo area. */ if (splitw) Outchar = vputchar; else { Outchar = vinschar; vprepins(); } for (;;) { if (gobblebl) gobblebl--; if (cnt != 0) { cnt--; if (cnt == 0) goto vadone; } ch = c = getkey() & (QUOTE|TRIM); if (!iglobp) { /* * Erase-kill type processing. * Only happens if we were not reading * from untyped input when we started. * Map users erase to ^H, kill to -1 for switch. */ if (c == tty.c_cc[VERASE]) c = CTRL('h'); else if (c == tty.c_cc[VKILL]) c = -1; switch (c) { /* * ^? Interrupt drops you back to visual * command mode with an unread interrupt * still in the input buffer. * * ^\ Quit does the same as interrupt. * If you are a ex command rather than * a vi command this will drop you * back to command mode for sure. */ case ATTN: case QUIT: ungetkey(c); goto vadone; /* * ^H Backs up a character in the input. * * BUG: Can't back around line boundaries. * This is hard because stuff has * already been saved for repeat. */ case CTRL('h'): bakchar: cp = gcursor - 1; if (cp < ogcursor) { beep(); continue; } goto vbackup; /* * ^W Back up a white/non-white word. */ case CTRL('w'): wdkind = 1; for (cp = gcursor; cp > ogcursor && isspace((int)cp[-1]); cp--) continue; for (c = wordch(cp - 1); cp > ogcursor && wordof(c, cp - 1); cp--) continue; goto vbackup; /* * users kill Kill input on this line, back to * the autoindent. */ case -1: cp = ogcursor; vbackup: if (cp == gcursor) { beep(); continue; } endim(); *cp = 0; c = cindent(); vgotoCL(qcolumn(cursor - 1, genbuf)); if (doomed >= 0) doomed += c - cindent(); gcursor = cp; continue; /* * \ Followed by erase or kill * maps to just the erase or kill. */ case '\\': x = destcol, y = destline; ex_putchar('\\'); vcsync(); c = getkey(); if (c == tty.c_cc[VERASE] || c == tty.c_cc[VKILL]) { vgoto(y, x); if (doomed >= 0) doomed++; goto def; } ungetkey(c), c = '\\'; goto noput; /* * ^Q Super quote following character * Only ^@ is verboten (trapped at * a lower level) and \n forces a line * split so doesn't really go in. * * ^V Synonym for ^Q */ case CTRL('q'): case CTRL('v'): x = destcol, y = destline; ex_putchar('^'); vgoto(y, x); c = getkey(); if (c != NL) { if (doomed >= 0) doomed++; goto def; } break; } } /* * If we get a blank not in the echo area * consider splitting the window in the wrapmargin. */ if (c == ' ' && !splitw) { if (gobblebl) { gobbled = 1; continue; } if (value(WRAPMARGIN) && outcol >= WCOLS - value(WRAPMARGIN)) { c = NL; gobblebl = 2; } } switch (c) { /* * ^M Except in repeat maps to \n. */ case CR: if (vglobp) goto def; c = '\n'; /* presto chango ... */ /* * \n Start new line. */ case NL: *aescaped = c; goto vadone; /* * escape End insert unless repeat and more to repeat. */ case ESCAPE: if (vglobp && *vglobp) goto def; goto vadone; /* * ^D Backtab. * ^T Software forward tab. * * Unless in repeat where this means these * were superquoted in. */ case CTRL('d'): case CTRL('t'): if (vglobp) goto def; /* fall into ... */ /* * ^D|QUOTE Is a backtab (in a repeated command). */ case CTRL('d') | QUOTE: *gcursor = 0; cp = vpastwh(genbuf); c = whitecnt(genbuf); if (ch == CTRL('t')) { /* * ^t just generates new indent replacing * current white space rounded up to soft * tab stop increment. */ if (cp != gcursor) /* * BUG: Don't hack ^T except * right after initial * white space. */ continue; cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); ogcursor = cp; goto vbackup; } /* * ^D works only if we are at the (end of) the * generated autoindent. We count the ^D for repeat * purposes. */ if (c == iwhite && c != 0) { if (cp == gcursor) { iwhite = backtab(c); CDCNT++; ogcursor = cp = genindent(iwhite); goto vbackup; } else if (&cp[1] == gcursor && (*cp == '^' || *cp == '0')) { /* * ^^D moves to margin, then back * to current indent on next line. * * 0^D moves to margin and then * stays there. */ HADZERO = *cp == '0'; ogcursor = cp = genbuf; HADUP = 1 - HADZERO; CDCNT = 1; endim(); back1(); vputc(' '); goto vbackup; } } if (vglobp && vglobp - iglobp >= 2 && (vglobp[-2] == '^' || vglobp[-2] == '0') && gcursor == ogcursor + 1) goto bakchar; continue; default: /* * Possibly discard control inputs. */ if (!vglobp && junk(c)) { beep(); continue; } def: ex_putchar(c); noput: if (gcursor > &genbuf[LBSIZE - 2]) error("Line too long"); *gcursor++ = c & TRIM; vcsync(); #ifdef LISP if (value(SHOWMATCH) && !iglobp) if (c == ')' || c == '}') lsmatch(gcursor); #endif continue; } } vadone: *gcursor = 0; Outchar = OO; endim(); return (gcursor); }
/* * Get a line into genbuf after gcursor. * Cnt limits the number of input characters * accepted and is used for handling the replace * single character command. Aescaped is the location * where we stick a termination indicator (whether we * ended with an ESCAPE or a newline/return. * * We do erase-kill type processing here and also * are careful about the way we do this so that it is * repeatable. (I.e. so that your kill doesn't happen, * when you repeat an insert if it was escaped with \ the * first time you did it. commch is the command character * involved, including the prompt for readline. */ char * vgetline(int cnt, char *gcursor, bool *aescaped, int commch) { register int c, ch; register char *cp; int x, y, iwhite, backsl=0; cell *iglobp; char cstr[2]; int (*OO)(int) = Outchar; /* * Clear the output state and counters * for autoindent backwards motion (counts of ^D, etc.) * Remember how much white space at beginning of line so * as not to allow backspace over autoindent. */ *aescaped = 0; ogcursor = gcursor; flusho(); CDCNT = 0; HADUP = 0; HADZERO = 0; gobbled = 0; iwhite = whitecnt(genbuf); iglobp = vglobp; /* * Carefully avoid using vinschar in the echo area. */ if (splitw) Outchar = vputchar; else { Outchar = vinschar; vprepins(); } for (;;) { backsl = 0; if (gobblebl) gobblebl--; if (cnt != 0) { cnt--; if (cnt == 0) goto vadone; } c = getkey(); if (c != ATTN) c &= (QUOTE|TRIM); ch = c; maphopcnt = 0; if (vglobp == 0 && Peekkey == 0 && commch != 'r') while ((ch = map(c, immacs)) != c) { c = ch; if (!value(REMAP)) break; if (++maphopcnt > 256) error(catgets(catd, 1, 234, "Infinite macro loop")); } if (!iglobp) { /* * Erase-kill type processing. * Only happens if we were not reading * from untyped input when we started. * Map users erase to ^H, kill to -1 for switch. */ if (c == tty.c_cc[VERASE]) c = CTRL('h'); else if (c == tty.c_cc[VKILL]) c = -1; if (c == ATTN) goto case_ATTN; switch (c) { /* * ^? Interrupt drops you back to visual * command mode with an unread interrupt * still in the input buffer. * * ^\ Quit does the same as interrupt. * If you are a ex command rather than * a vi command this will drop you * back to command mode for sure. */ case QUIT: case_ATTN: ungetkey(c); goto vadone; /* * ^H Backs up a character in the input. * * BUG: Can't back around line boundaries. * This is hard because stuff has * already been saved for repeat. */ case CTRL('h'): bakchar: cp = gcursor + skipleft(ogcursor, gcursor); if (cp < ogcursor) { if (splitw) { /* * Backspacing over readecho * prompt. Pretend delete but * don't beep. */ ungetkey(c); goto vadone; } beep(); continue; } goto vbackup; /* * ^W Back up a white/non-white word. */ case CTRL('w'): wdkind = 1; for (cp = gcursor; cp > ogcursor && isspace(cp[-1]&0377); cp--) continue; for (c = wordch(cp - 1); cp > ogcursor && wordof(c, cp - 1); cp--) continue; goto vbackup; /* * users kill Kill input on this line, back to * the autoindent. */ case -1: cp = ogcursor; vbackup: if (cp == gcursor) { beep(); continue; } endim(); *cp = 0; c = cindent(); vgotoCL(qcolumn(cursor + skipleft(linebuf, cursor), genbuf)); if (doomed >= 0) doomed += c - cindent(); gcursor = cp; continue; /* * \ Followed by erase or kill * maps to just the erase or kill. */ case '\\': x = destcol, y = destline; putchar('\\'); vcsync(); c = getkey(); if (c == tty.c_cc[VERASE] || c == tty.c_cc[VKILL]) { vgoto(y, x); if (doomed >= 0) doomed++; goto def; } ungetkey(c), c = '\\'; backsl = 1; break; /* * ^Q Super quote following character * Only ^@ is verboten (trapped at * a lower level) and \n forces a line * split so doesn't really go in. * * ^V Synonym for ^Q */ case CTRL('q'): case CTRL('v'): x = destcol, y = destline; putchar('^'); vgoto(y, x); c = getkey(); if (c != NL) { if (doomed >= 0) doomed++; goto def; } break; } } /* * If we get a blank not in the echo area * consider splitting the window in the wrapmargin. */ if (c != NL && !splitw) { if (c == ' ' && gobblebl) { gobbled = 1; continue; } if (value(WRAPMARGIN) && (outcol >= OCOLUMNS - value(WRAPMARGIN) || (backsl && outcol == 0)) && commch != 'r') { /* * At end of word and hit wrapmargin. * Move the word to next line and keep going. */ wdkind = 1; gappend(c); if (backsl) gappend(getkey()); *gcursor = 0; /* * Find end of previous word if we are past it. */ for (cp=gcursor; cp>ogcursor && isspace(cp[-1]&0377); cp--) ; if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) { /* * Find beginning of previous word. */ for (; cp>ogcursor && !isspace(cp[-1]&0377); cp--) ; if (cp <= ogcursor) { /* * There is a single word that * is too long to fit. Just * let it pass, but beep for * each new letter to warn * the luser. */ c = *--gcursor; *gcursor = 0; beep(); goto dontbreak; } /* * Save it for next line. */ macpush(cp, 0); cp--; } macpush("\n", 0); /* * Erase white space before the word. */ while (cp > ogcursor && isspace(cp[-1]&0377)) cp--; /* skip blank */ gobblebl = 3; goto vbackup; } dontbreak:; } /* * Word abbreviation mode. */ cstr[0] = c; if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) { int wdtype, abno; cstr[1] = 0; wdkind = 1; cp = gcursor + skipleft(ogcursor, gcursor); for (wdtype = wordch(cp - 1); cp > ogcursor && wordof(wdtype, cp - 1); cp--) ; *gcursor = 0; for (abno=0; abbrevs[abno].mapto; abno++) { if (!abbrevs[abno].hadthis && eq(cp, abbrevs[abno].cap)) { abbrevs[abno].hadthis++; macpush(cstr, 0); macpush(abbrevs[abno].mapto, 0); goto vbackup; } } } #ifdef BIT8 if (c == OVERBUF) goto btrp; #endif switch (c) { /* * ^M Except in repeat maps to \n. */ case CR: if (vglobp) goto def; c = '\n'; /* presto chango ... */ /* * \n Start new line. */ case NL: *aescaped = c; goto vadone; /* * escape End insert unless repeat and more to repeat. */ case ESCAPE: if (lastvgk) goto def; goto vadone; /* * ^D Backtab. * ^T Software forward tab. * * Unless in repeat where this means these * were superquoted in. */ case CTRL('d'): case CTRL('t'): if (vglobp) goto def; /* fall into ... */ /* * ^D|QUOTE Is a backtab (in a repeated command). */ #ifndef BIT8 case CTRL('d') | QUOTE: #else btrp: #endif *gcursor = 0; cp = vpastwh(genbuf); c = whitecnt(genbuf); if (ch == CTRL('t')) { /* * ^t just generates new indent replacing * current white space rounded up to soft * tab stop increment. */ if (cp != gcursor) /* * BUG: Don't hack ^T except * right after initial * white space. */ continue; cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); ogcursor = cp; goto vbackup; } /* * ^D works only if we are at the (end of) the * generated autoindent. We count the ^D for repeat * purposes. */ if (c == iwhite && c != 0) { if (cp == gcursor) { iwhite = backtab(c); CDCNT++; ogcursor = cp = genindent(iwhite); goto vbackup; } else if (&cp[1] == gcursor && (*cp == '^' || *cp == '0')) { /* * ^^D moves to margin, then back * to current indent on next line. * * 0^D moves to margin and then * stays there. */ HADZERO = *cp == '0'; ogcursor = cp = genbuf; HADUP = 1 - HADZERO; CDCNT = 1; endim(); back1(); vputchar(' '); goto vbackup; } } if (vglobp && vglobp - iglobp >= 2 && (vglobp[-2] == '^' || vglobp[-2] == '0') && gcursor == ogcursor + 1) goto bakchar; continue; default: /* * Possibly discard control inputs. */ if (!vglobp && junk(c)) { beep(); continue; } def: if (!backsl) { /* int cnt; */ putchar(c); flush(); } if (gcursor > &genbuf[LBSIZE - 2]) error(catgets(catd, 1, 235, "Line too long")); gappend(c & TRIM); vcsync(); if (value(SHOWMATCH) && !iglobp) if (c == ')' || c == '}') lsmatch(gcursor); continue; } } vadone: *gcursor = 0; if (Outchar != termchar) Outchar = OO; endim(); return (gcursor); }
/* * Redisplay logical line l at physical line p with line number lineno. */ int vreopen(int p, int lineno, int l) { register int d; register struct vlinfo *vp = &vlinfo[l]; if (p < 0) error("Line too long to fit on screen"); d = vp->vdepth; if (d == 0 || (vp->vflags & VDIRT)) vp->vdepth = d = vdepth(); vp->vliny = p, vp->vflags &= ~VDIRT; /* * Try to win by making the screen larger rather than inserting * a line and driving text off the bottom. */ p = vglitchup(l, 0); /* * BUG: Should consider using CE here to clear to end of line. * As it stands we always strike over the current text. * Since often the current text is the same as what * we are overstriking with, it tends not to show. * On the other hand if it is different and we end up * spacing out a lot of text, we could have won with * a CE. This is probably worthwhile at low speed * only however, since clearly computation will be * necessary to determine which way to go. */ vigoto(p, 0); pline(lineno); /* * When we are typing part of a line for hardcopy open, don't * want to type the '$' marking an end of line if in list mode. */ if (hold & HOLDDOL) return (d); if (Putchar == listchar) putchar('$'); /* * Optimization of cursor motion may prevent screen rollup if the * line has blanks/tabs at the end unless we force the cursor to appear * on the last line segment. */ if (vp->vliny + d - 1 > WBOT) vcsync(); /* * Switch into hardcopy open mode if we are in one line (adm3) * open mode and this line is now too long. If in hardcopy * open mode, then call sethard to move onto the next line * with appropriate positioning. */ if (state == ONEOPEN) { WCOLS = OCOLUMNS; if (vdepth() > 1) { WCOLS = TUBECOLS; sethard(); } else WCOLS = TUBECOLS; } else if (state == HARDOPEN) sethard(); /* * Unless we filled (completely) the last line we typed on, * we have to clear to the end of the line * in case stuff is left from before. */ if (vp->vliny + d > destline) { if (IN && destcol == WCOLS) vigoto(vp->vliny + d - 1, 0); vclreol(); } return (d); }
/* * 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; }
/* * 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; }
/* * 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; }