/* * 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); }
/* * 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); }
/* * 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); } } }
/* * Something weird just happened and we * lost track of whats happening out there. * Since we cant, in general, read where we are * we just reset to some known state. * On cursor addressible terminals setting to unknown * will force a cursor address soon. */ void termreset(void) { endim(); if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */ putpad(TI); /*adb change -- emit terminal initial sequence */ destcol = 0; destline = TLINES - 1; if (CA) { outcol = UKCOL; outline = UKCOL; } else { outcol = destcol; outline = destline; } }
void lsmatch(char *cp) { char *save = smalloc(LBSIZE); register char *sp = save; register char *scurs = cursor; wcursor = cp; lcpy(sp, linebuf, LBSIZE); *wcursor = 0; strcpy(cursor, genbuf); cursor = strend(linebuf) - 1; if (lmatchp(dot - vcline)) { register int i = insmode; register int c = outcol; register int l = outline; if (!MI) endim(); vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1); flush(); sleep(1); vgoto(l, c); if (i) goim(); } else { strcLIN(sp); strcpy(scurs, genbuf); if (!lmatchp((line *) 0)) beep(); } strcLIN(sp); wdot = 0; wcursor = 0; cursor = scurs; free(save); }
/* * 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); }
void vappend(int ch, int cnt, int indent) { register int i; register char *gcursor; bool escape; int repcnt; short oldhold = hold; /* * Before a move in hardopen when the line is dirty * or we are in the middle of the printed representation, * we retype the line to the left of the cursor so the * insert looks clean. */ if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { rubble = 1; wcursor = cursor; vmove(); } vaifirst = indent == 0; /* * Handle replace character by (eventually) * limiting the number of input characters allowed * in the vgetline routine. */ if (ch == 'r') repcnt = 2; else repcnt = 0; /* * If an autoindent is specified, then * generate a mixture of blanks to tabs to implement * it and place the cursor after the indent. * Text read by the vgetline routine will be placed in genbuf, * so the indent is generated there. */ if (value(AUTOINDENT) && indent != 0) { gcursor = genindent(indent); *gcursor = 0; vgotoCL(qcolumn(cursor - 1, genbuf)); } else { gcursor = genbuf; *gcursor = 0; if (ch == 'o') vfixcurs(); } /* * Prepare for undo. Pointers delimit inserted portion of line. */ vUA1 = vUA2 = cursor; /* * If we are not in a repeated command and a ^@ comes in * then this means the previous inserted text. * If there is none or it was too long to be saved, * then beep() and also arrange to undo any damage done * so far (e.g. if we are a change.) */ if ((vglobp && *vglobp == 0) || peekbr()) { if ((INS[0] & (OVERBUF|TRIM)) == OVERBUF) { beep(); if (!splitw) ungetkey('u'); doomed = 0; hold = oldhold; return; } /* * Unread input from INS. * An escape will be generated at end of string. * Hold off n^^2 type update on dumb terminals. */ vglobp = INS; hold |= HOLDQIK; } else if (vglobp == 0) /* * Not a repeated command, get * a new inserted text for repeat. */ INS[0] = 0; /* * For wrapmargin to hack away second space after a '.' * when the first space caused a line break we keep * track that this happened in gobblebl, which says * to gobble up a blank silently. */ gobblebl = 0; /* * Text gathering loop. * New text goes into genbuf starting at gcursor. * cursor preserves place in linebuf where text will eventually go. */ if (*cursor == 0 || state == CRTOPEN) hold |= HOLDROL; for (;;) { if (ch == 'r' && repcnt == 0) escape = 0; else { gcursor = vgetline(repcnt, gcursor, &escape); /* * After an append, stick information * about the ^D's and ^^D's and 0^D's in * the repeated text buffer so repeated * inserts of stuff indented with ^D as backtab's * can work. */ if (HADUP) addtext("^"); else if (HADZERO) addtext("0"); while (CDCNT > 0) addtext("\204"), CDCNT--; if (gobbled) addtext(" "); addtext(ogcursor); } repcnt = 0; /* * Smash the generated and preexisting indents together * and generate one cleanly made out of tabs and spaces * if we are using autoindent. */ if (!vaifirst && value(AUTOINDENT)) { i = fixindent(indent); if (!HADUP) indent = i; gcursor = strend(genbuf); } /* * Limit the repetition count based on maximum * possible line length; do output implied * by further count (> 1) and cons up the new line * in linebuf. */ cnt = vmaxrep(ch, cnt); CP(gcursor + 1, cursor); do { CP(cursor, genbuf); if (cnt > 1) { int oldhold = hold; Outchar = vinschar; hold |= HOLDQIK; ex_printf("%s", genbuf); hold = oldhold; Outchar = vputchar; } cursor += gcursor - genbuf; } while (--cnt > 0); endim(); vUA2 = cursor; if (escape != '\n') CP(cursor, gcursor + 1); /* * If doomed characters remain, clobber them, * and reopen the line to get the display exact. */ if (state != HARDOPEN) { DEPTH(vcline) = 0; if (doomed > 0) { register int cind = cindent(); physdc(cind, cind + doomed); doomed = 0; } i = vreopen(LINE(vcline), lineDOT(), vcline); } /* * All done unless we are continuing on to another line. */ if (escape != '\n') break; /* * Set up for the new line. * First save the current line, then construct a new * first image for the continuation line consisting * of any new autoindent plus the pushed ahead text. */ killU(); addtext(gobblebl ? " " : "\n"); vsave(); cnt = 1; if (value(AUTOINDENT)) { #ifdef LISP if (value(LISP)) indent = lindent(dot + 1); else #endif if (!HADUP && vaifirst) indent = whitecnt(linebuf); vaifirst = 0; strcLIN(vpastwh(gcursor + 1)); gcursor = genindent(indent); *gcursor = 0; if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) gcursor = genbuf; CP(gcursor, linebuf); } else { CP(genbuf, gcursor + 1); gcursor = genbuf; } /* * If we started out as a single line operation and are now * turning into a multi-line change, then we had better yank * out dot before it changes so that undo will work * correctly later. */ if (vundkind == VCHNG) { vremote(1, (void (*)(int))yank, 0); undap1--; } /* * Now do the append of the new line in the buffer, * and update the display. If slowopen * we don't do very much. */ vdoappend(genbuf); vundkind = VMANYINS; vcline++; if (state != VISUAL) vshow(dot, NOLINE); else { i += LINE(vcline - 1); vopen(dot, i); if (value(SLOWOPEN)) vscrap(); else vsync1(LINE(vcline)); } strcLIN(gcursor); *gcursor = 0; cursor = linebuf; vgotoCL(qcolumn(cursor - 1, genbuf)); } /* * All done with insertion, position the cursor * and sync the screen. */ hold = oldhold; if (cursor > linebuf) cursor--; if (state != HARDOPEN) vsyncCL(); else if (cursor > linebuf) back1(); doomed = 0; wcursor = cursor; vmove(); }
/* * 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); }
void vundo ( int show /* if true update the screen */ ) { register int cnt; register line *addr; register char *cp; char *temp = smalloc(LBSIZE); bool savenote; int (*OO)(int); short oldhold = hold; switch (vundkind) { case VMANYINS: wcursor = 0; addr1 = undap1; addr2 = undap2 - 1; vsave(); YANKreg('1'); notecnt = 0; /* fall into ... */ case VMANY: case VMCHNG: vsave(); addr = dot - vcline; notecnt = 1; if (undkind == UNDPUT && undap1 == undap2) { beep(); break; } /* * Undo() call below basically replaces undap1 to undap2-1 * with dol through unddol-1. Hack screen image to * reflect this replacement. */ if (show) if (undkind == UNDMOVE) vdirty(0, TLINES); else vreplace(undap1 - addr, undap2 - undap1, undkind == UNDPUT ? 0 : unddol - dol); savenote = notecnt; undo(1); if (show && (vundkind != VMCHNG || addr != dot)) killU(); vundkind = VMANY; cnt = dot - addr; if (cnt < 0 || cnt > vcnt || state != VISUAL) { if (show) vjumpto(dot, NOSTR, '.'); break; } if (!savenote) notecnt = 0; if (show) { vcline = cnt; vrepaint(vmcurs); } vmcurs = 0; break; case VCHNG: case VCAPU: vundkind = VCHNG; CP(temp, vutmp); CP(vutmp, linebuf); doomed = column(vUA2 - 1) - column(vUA1 - 1); strcLIN(temp); cp = vUA1; vUA1 = vUD1; vUD1 = cp; cp = vUA2; vUA2 = vUD2; vUD2 = cp; if (!show) break; cursor = vUD1; if (state == HARDOPEN) { doomed = 0; vsave(); vopen(dot, WBOT); vnline(cursor); break; } /* * Pseudo insert command. */ vcursat(cursor); OO = Outchar; Outchar = vinschar; hold |= HOLDQIK; vprepins(); temp[vUA2 - linebuf] = 0; for (cp = &temp[vUA1 - linebuf]; *cp;) { int c, n; nextc(c, cp, n); cp += n; putchar(c); } Outchar = OO; hold = oldhold; endim(); physdc(cindent(), cindent() + doomed); doomed = 0; vdirty(vcline, 1); vsyncCL(); if (cursor > linebuf && cursor >= strend(linebuf)) cursor += skipleft(linebuf, cursor); vfixcurs(); break; case VNONE: beep(); break; } free(temp); }
void vappend(int ch, int cnt, int indent) { int i; unsigned char *gcursor; bool escape; int repcnt, savedoomed; short oldhold = hold; int savecnt = cnt; line *startsrcline; int startsrccol, endsrccol; int gotNL = 0; int imultlinecnt = 0; int omultlinecnt = 0; if ((savecnt > 1) && (ch == 'o' || ch == 'O')) { omultlinecnt = 1; } #ifdef XPG6 if ((savecnt > 1) && (ch == 'a' || ch == 'A' || ch == 'i' || ch == 'I')) imultlinecnt = 1; #endif /* XPG6 */ /* * Before a move in hardopen when the line is dirty * or we are in the middle of the printed representation, * we retype the line to the left of the cursor so the * insert looks clean. */ if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { rubble = 1; gcursor = cursor; i = *gcursor; *gcursor = ' '; wcursor = gcursor; (void) vmove(); *gcursor = i; } /* * If vrep() passed indent = 0, this is the 'r' command, * so don't autoindent until the last char. */ vaifirst = indent == 0; /* * Handle replace character by (eventually) * limiting the number of input characters allowed * in the vgetline routine. */ if (ch == 'r') repcnt = 2; else repcnt = 0; /* * If an autoindent is specified, then * generate a mixture of blanks to tabs to implement * it and place the cursor after the indent. * Text read by the vgetline routine will be placed in genbuf, * so the indent is generated there. */ if (value(vi_AUTOINDENT) && indent != 0) { unsigned char x; gcursor = genindent(indent); *gcursor = 0; vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); } else { gcursor = genbuf; *gcursor = 0; if (ch == 'o') vfixcurs(); } /* * Prepare for undo. Pointers delimit inserted portion of line. */ vUA1 = vUA2 = cursor; /* * If we are not in a repeated command and a ^@ comes in * then this means the previous inserted text. * If there is none or it was too long to be saved, * then beep() and also arrange to undo any damage done * so far (e.g. if we are a change.) */ switch (ch) { case 'r': break; case 'a': /* * TRANSLATION_NOTE * "A" is a terse mode message corresponding to * "APPEND MODE". * Translated message of "A" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("A")); } else { vshowmode(gettext("APPEND MODE")); } break; case 's': /* * TRANSLATION_NOTE * "S" is a terse mode message corresponding to * "SUBSTITUTE MODE". * Translated message of "S" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("S")); } else { vshowmode(gettext("SUBSTITUTE MODE")); } break; case 'c': /* * TRANSLATION_NOTE * "C" is a terse mode message corresponding to * "CHANGE MODE". * Translated message of "C" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("C")); } else { vshowmode(gettext("CHANGE MODE")); } break; case 'R': /* * TRANSLATION_NOTE * "R" is a terse mode message corresponding to * "REPLACE MODE". * Translated message of "R" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("R")); } else { vshowmode(gettext("REPLACE MODE")); } break; case 'o': /* * TRANSLATION_NOTE * "O" is a terse mode message corresponding to * "OPEN MODE". * Translated message of "O" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("O")); } else { vshowmode(gettext("OPEN MODE")); } break; case 'i': /* * TRANSLATION_NOTE * "I" is a terse mode message corresponding to * "INSERT MODE" and the following "INPUT MODE". * Translated message of "I" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("I")); } else { vshowmode(gettext("INSERT MODE")); } break; default: /* * TRANSLATION_NOTE * "I" is a terse mode message corresponding to * "INPUT MODE" and the previous "INSERT MODE". * Translated message of "I" must be 1 character (not byte). * Or, just leave it. */ if (value(vi_TERSE)) { vshowmode(gettext("I")); } else { vshowmode(gettext("INPUT MODE")); } } ixlatctl(1); if ((vglobp && *vglobp == 0) || peekbr()) { if (INS[128] == 0200) { (void) beep(); if (!splitw) ungetkey('u'); doomed = 0; hold = oldhold; return; } /* * Unread input from INS. * An escape will be generated at end of string. * Hold off n^^2 type update on dumb terminals. */ vglobp = INS; inscdcnt = INSCDCNT; hold |= HOLDQIK; } else if (vglobp == 0) { /* * Not a repeated command, get * a new inserted text for repeat. */ INS[0] = 0; INS[128] = 0; INSCDCNT = 0; } /* * For wrapmargin to hack away second space after a '.' * when the first space caused a line break we keep * track that this happened in gobblebl, which says * to gobble up a blank silently. */ gobblebl = 0; startsrcline = dot; startsrccol = cursor - linebuf; /* * Text gathering loop. * New text goes into genbuf starting at gcursor. * cursor preserves place in linebuf where text will eventually go. */ if (*cursor == 0 || state == CRTOPEN) hold |= HOLDROL; for (;;) { if (ch == 'r' && repcnt == 0) escape = 0; else { ixlatctl(1); /* * When vgetline() returns, gcursor is * pointing to '\0' and vgetline() has * read an ESCAPE or NL. */ gcursor = vgetline(repcnt, gcursor, &escape, ch); if (escape == '\n') { gotNL = 1; #ifdef XPG6 if (ch == 'r') { /* * XPG6 assertion 313 [count]r\n : * Arrange to set cursor correctly. */ endsrccol = gcursor - genbuf - 1; } #endif /* XPG6 */ } else { /* * Upon escape, gcursor is pointing to '\0' * terminating the string in genbuf. */ endsrccol = gcursor - genbuf - 1; } ixlatctl(0); /* * After an append, stick information * about the ^D's and ^^D's and 0^D's in * the repeated text buffer so repeated * inserts of stuff indented with ^D as backtab's * can work. */ if (HADUP) addtext("^"); else if (HADZERO) addtext("0"); if(!vglobp) INSCDCNT = CDCNT; while (CDCNT > 0) { addtext("\004"); CDCNT--; } if (gobbled) addtext(" "); addtext(ogcursor); } repcnt = 0; /* * Smash the generated and preexisting indents together * and generate one cleanly made out of tabs and spaces * if we are using autoindent and this isn't 'r' command. */ if (!vaifirst && value(vi_AUTOINDENT)) { i = fixindent(indent); if (!HADUP) indent = i; gcursor = strend(genbuf); } /* * Set cnt to 1 to avoid repeating the text on the same line. * Do this for commands 'i', 'I', 'a', and 'A', if we're * inserting anything with a newline for XPG6. Always do this * for commands 'o' and 'O'. */ if ((imultlinecnt && gotNL) || omultlinecnt) { cnt = 1; } /* * Limit the repetition count based on maximum * possible line length; do output implied * by further count (> 1) and cons up the new line * in linebuf. */ cnt = vmaxrep(ch, cnt); /* * cursor points to linebuf * Copy remaining old text (cursor) in original * line to after new text (gcursor + 1) in genbuf. */ CP(gcursor + 1, cursor); /* * For [count] r \n command, when replacing [count] chars * with '\n', this loop replaces [count] chars with "". */ do { /* cp new text (genbuf) into linebuf (cursor) */ CP(cursor, genbuf); if (cnt > 1) { int oldhold = hold; Outchar = vinschar; hold |= HOLDQIK; viprintf("%s", genbuf); hold = oldhold; Outchar = vputchar; } /* point cursor after new text in linebuf */ cursor += gcursor - genbuf; } while (--cnt > 0); endim(); vUA2 = cursor; /* add the remaining old text after the cursor */ if (escape != '\n') CP(cursor, gcursor + 1); /* * If doomed characters remain, clobber them, * and reopen the line to get the display exact. * eg. c$ to change to end of line */ if (state != HARDOPEN) { DEPTH(vcline) = 0; savedoomed = doomed; if (doomed > 0) { int cind = cindent(); physdc(cind, cind + doomed); doomed = 0; } if(MB_CUR_MAX > 1) rewrite = _ON; i = vreopen(LINE(vcline), lineDOT(), vcline); if(MB_CUR_MAX > 1) rewrite = _OFF; #ifdef TRACE if (trace) fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed); #endif if (ch == 'R') doomed = savedoomed; } /* * Unless we are continuing on to another line * (got a NL), break out of the for loop (got * an ESCAPE). */ if (escape != '\n') { vshowmode(""); break; } /* * Set up for the new line. * First save the current line, then construct a new * first image for the continuation line consisting * of any new autoindent plus the pushed ahead text. */ killU(); addtext(gobblebl ? " " : "\n"); /* save vutmp (for undo state) into temp file */ vsave(); cnt = 1; if (value(vi_AUTOINDENT)) { if (value(vi_LISP)) indent = lindent(dot + 1); else if (!HADUP && vaifirst) indent = whitecnt(linebuf); vaifirst = 0; strcLIN(vpastwh(gcursor + 1)); gcursor = genindent(indent); *gcursor = 0; if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) gcursor = genbuf; CP(gcursor, linebuf); } else { /* * Put gcursor at start of genbuf to wipe * out previous line in preparation for * the next vgetline() loop. */ CP(genbuf, gcursor + 1); gcursor = genbuf; } /* * If we started out as a single line operation and are now * turning into a multi-line change, then we had better yank * out dot before it changes so that undo will work * correctly later. */ if (FIXUNDO && vundkind == VCHNG) { vremote(1, yank, 0); undap1--; } /* * Now do the append of the new line in the buffer, * and update the display, ie: append genbuf to * the file after dot. If slowopen * we don't do very much. */ vdoappend(genbuf); vundkind = VMANYINS; vcline++; if (state != VISUAL) vshow(dot, NOLINE); else { i += LINE(vcline - 1); vopen(dot, i); if (value(vi_SLOWOPEN)) vscrap(); else vsync1(LINE(vcline)); } switch (ch) { case 'r': break; case 'a': if (value(vi_TERSE)) { vshowmode(gettext("A")); } else { vshowmode(gettext("APPEND MODE")); } break; case 's': if (value(vi_TERSE)) { vshowmode(gettext("S")); } else { vshowmode(gettext("SUBSTITUTE MODE")); } break; case 'c': if (value(vi_TERSE)) { vshowmode(gettext("C")); } else { vshowmode(gettext("CHANGE MODE")); } break; case 'R': if (value(vi_TERSE)) { vshowmode(gettext("R")); } else { vshowmode(gettext("REPLACE MODE")); } break; case 'i': if (value(vi_TERSE)) { vshowmode(gettext("I")); } else { vshowmode(gettext("INSERT MODE")); } break; case 'o': if (value(vi_TERSE)) { vshowmode(gettext("O")); } else { vshowmode(gettext("OPEN MODE")); } break; default: if (value(vi_TERSE)) { vshowmode(gettext("I")); } else { vshowmode(gettext("INPUT MODE")); } } strcLIN(gcursor); /* zero genbuf */ *gcursor = 0; cursor = linebuf; vgotoCL(nqcolumn(cursor - 1, genbuf)); } /* end for (;;) loop in vappend() */ if (imultlinecnt && gotNL) { imultlinerep(savecnt, startsrcline, startsrccol, endsrccol); } else if (omultlinecnt) { omultlinerep(savecnt, startsrcline, endsrccol); #ifdef XPG6 } else if (savecnt > 1 && ch == 'r' && gotNL) { /* * XPG6 assertion 313 & 254 : Position cursor for [count]r\n * then insert [count -1] newlines. */ endsrccol = gcursor - genbuf - 1; rmultlinerep(savecnt, endsrccol); #endif /* XPG6 */ } /* * All done with insertion, position the cursor * and sync the screen. */ hold = oldhold; if ((imultlinecnt && gotNL) || omultlinecnt) { fixdisplay(); #ifdef XPG6 } else if (savecnt > 1 && ch == 'r' && gotNL) { fixdisplay(); /* * XPG6 assertion 313 & 254 [count]r\n : Set flag to call * fixdisplay() after operate() has finished. To be sure that * the text (after the last \n followed by an indent) is always * displayed, fixdisplay() is called right before getting * the next command. */ redisplay = 1; #endif /* XPG6 */ } else if (cursor > linebuf) { cursor = lastchr(linebuf, cursor); #ifdef XPG6 /* * XPG6 assertion 313 & 254 [count]r\n : * For 'r' command, when the replacement char causes new * lines to be created, point cursor to first non-blank. * The old code, ie: cursor = lastchr(linebuf, cursor); * set cursor to the blank before the first non-blank * for r\n */ if (ch == 'r' && gotNL && isblank((int)*cursor)) ++cursor; #endif /* XPG6 */ } if (state != HARDOPEN) vsyncCL(); else if (cursor > linebuf) back1(); doomed = 0; wcursor = cursor; (void) vmove(); }
/* * 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; }
/* * 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(); } }
/* * 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; }