/* * Save the current contents of linebuf, if it has changed. */ void vsave(void) { char temp[LBSIZE]; CP(temp, linebuf); if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) { /* * If the undo state is saved in the temporary buffer * vutmp, then we sync this into the temp file so that * we will be able to undo even after we have moved off * the line. It would be possible to associate a line * with vutmp but we assume that vutmp is only associated * with line dot (e.g. in case ':') above, so beware. */ prepapp(); CP(linebuf, vutmp); putmark(dot); vremote(1, yank, 0); vundkind = VMCHNG; notecnt = 0; undkind = UNDCHANGE; } /* * Get the line out of the temp file and do nothing if it hasn't * changed. This may seem like a loss, but the line will * almost always be in a read buffer so this may well avoid disk i/o. */ getDOT(); if (strcmp(linebuf, temp) == 0) return; strcLIN(temp); putmark(dot); }
void shift(int c, int cnt) { register int *addr; char *cp = genbuf; char *dp; register int i; if (!inglobal) save12(), undkind = UNDCHANGE; cnt *= value(SHIFTWIDTH); for (addr = addr1; addr <= addr2; addr++) { ex_getline(*addr); i = whitecnt(linebuf); switch (c) { case '>': if (linebuf[0] == 0) continue; cp = genindent(i + cnt); break; case '<': if (i == 0) continue; i -= cnt; cp = i > 0 ? genindent(i) : genbuf; break; } dot = addr; if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) error("Line too long|Result line after shift would be too long"); strcpy(cp, dp); strcLIN(genbuf); putmark(addr); } }
/* * Subroutine for vdoappend to pass to append. */ static int vgetsplit(void) { if (vsplitpt == 0) return (EOF); strcLIN(vsplitpt); vsplitpt = 0; return (0); }
int getsub(void) { register char *p; if ((p = linebp) == 0) return (EOF); strcLIN(p); linebp = 0; return (0); }
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); }
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(); }
static void dosub(void) { register char *lp, *sp, *rp; int c; lp = linebuf; sp = genbuf; rp = rhsbuf; while (lp < loc1) *sp++ = *lp++; casecnt = 0; while ((c = *rp++)) { if (c & RE_QUOTE) switch (c & TRIM) { case '&': sp = place(sp, loc1, loc2); if (sp == 0) goto ovflo; continue; case 'l': casecnt = 1; destuc = 0; continue; case 'L': casecnt = LBSIZE; destuc = 0; continue; case 'u': casecnt = 1; destuc = 1; continue; case 'U': casecnt = LBSIZE; destuc = 1; continue; case 'E': case 'e': casecnt = 0; continue; } if (c < 0 && (c &= TRIM) >= '1' && c < nbra + '1') { sp = place(sp, braslist[c - '1'], braelist[c - '1']); if (sp == 0) goto ovflo; continue; } if (casecnt) *sp++ = fixcase(c & TRIM); else *sp++ = c & TRIM; if (sp >= &genbuf[LBSIZE]) ovflo: error("Line overflow@in substitute - limit 512 chars"); } lp = loc2; loc2 = sp + (linebuf - genbuf) + (loc1 == loc2); while ((*sp++ = *lp++)) if (sp >= &genbuf[LBSIZE]) goto ovflo; strcLIN(genbuf); }
/* * The guts of a sync. Similar to redraw but * just less ambitous. */ void vsync1(register int p) { register int l; char temp[LBSIZE]; register struct vlinfo *vp = &vlinfo[0]; short oldhold = hold; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny(); #endif if (holdupd) { if (holdupd < 3) holdupd = 2; return; } if (state == HARDOPEN || splitw) return; vscrap(); CP(temp, linebuf); if (vcnt == 0) LINE(0) = WTOP; l = 0; while (l < vcnt && vp->vliny < p) l++, vp++; heldech = 0; hold |= HOLDECH; while (p <= WBOT && Peekkey != ATTN) { /* * Want to put a line here if not in visual and first line * or if there are lies left and this line starts before * the current line, or if this line is piled under the * next line (vreplace does this and we undo it). */ if (l == 0 && state != VISUAL || (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) { if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) { if (l == vcline) strcLIN(temp); else getline(dot[l - vcline]); /* * Be careful that a long line doesn't cause the * screen to shoot up. */ if (l != vcline && (vp->vflags & VDIRT)) { vp->vdepth = vdepth(); vp->vflags &= ~VDIRT; if (p + vp->vdepth - 1 > WBOT) break; } vreopen(p, lineDOT() + (l - vcline), l); } p = vp->vliny + vp->vdepth; vp++; l++; } else /* * A physical line between logical lines, * so we settle for an @ at the beginning. */ vclrlin(p, dot + (l - vcline)), p++; } strcLIN(temp); hold = oldhold; if (heldech) vclrech(0); }
/* * Fully cleanup the screen, leaving no @ lines except at end when * line after last won't completely fit. The routine vsync is * more conservative and much less work on dumb terminals. */ void vredraw(register int p) { register int l; register line *tp; char temp[LBSIZE]; bool anydl = 0; short oldhold = hold; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny(); #endif if (holdupd) { holdupd = 3; return; } if (state == HARDOPEN || splitw) return; if (p < 0 /* || p > WECHO */) error(catgets(catd, 1, 221, "Internal error: vredraw")); /* * Trim the ragged edges (lines which are off the screen but * not yet logically discarded), save the current line, and * search for first logical line affected by the redraw. */ vscrap(); CP(temp, linebuf); l = 0; tp = dot - vcline; if (vcnt == 0) LINE(0) = WTOP; while (l < vcnt && LINE(l) < p) l++, tp++; /* * We hold off echo area clearing during the redraw in deference * to a final clear of the echo area at the end if appropriate. */ heldech = 0; hold |= HOLDECH; for (; l < vcnt && Peekkey != ATTN; l++) { if (l == vcline) strcLIN(temp); else getline(*tp); /* * Delete junk between displayed lines. */ if (LINE(l) != LINE(l + 1) && LINE(l) != p) { if (anydl == 0 && DB && CD) { hold = oldhold; vclrech(0); anydl = 1; hold |= HOLDECH; heldech = 0; } vdellin(p, LINE(l) - p, l); } /* * If line image is not know to be up to date, then * redisplay it; else just skip onward. */ LINE(l) = p; if (FLAGS(l) & VDIRT) { DEPTH(l) = vdepth(); if (l != vcline && p + DEPTH(l) - 1 > WBOT) { vscrap(); break; } FLAGS(l) &= ~VDIRT; vreopen(p, lineno(tp), l); p = LINE(l) + DEPTH(l); } else p += DEPTH(l); tp++; } /* * That takes care of lines which were already partially displayed. * Now try to fill the rest of the screen with text. */ if (state == VISUAL && p <= WBOT) { int ovcline = vcline; vcline = l; for (; tp <= dol && Peekkey != ATTN; tp++) { getline(*tp); if (p + vdepth() - 1 > WBOT) break; vopen(tp, p); p += DEPTH(vcline); vcline++; } vcline = ovcline; } /* * Thats all the text we can get on. * Now rest of lines (if any) get either a ~ if they * are past end of file, or an @ if the next line won't fit. */ for (; p <= WBOT && Peekkey != ATTN; p++) vclrlin(p, tp); strcLIN(temp); hold = oldhold; if (heldech) vclrech(0); #ifdef ADEBUG if (trace) tvliny(); #endif }
/* * Change operator. * * In a single line we mark the end of the changed area with '$'. * On multiple whole lines, we clear the lines first. * Across lines with both wcursor and wdot given, we delete * and sync then append (but one operation for undo). */ void vchange(int c) { register char *cp; register int i, ind, cnt; line *addr; if (wdot) { /* * Change/delete of lines or across line boundaries. */ if ((cnt = xdw()) < 0) return; getDOT(); if (wcursor && cnt == 1) { /* * Not really. */ wdot = 0; if (c == EOF) { vdelete(c); return; } goto smallchange; } if (cursor && wcursor) { /* * Across line boundaries, but not * necessarily whole lines. * Construct what will be left. */ *cursor = 0; strcpy(genbuf, linebuf); getline(*wdot); if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) { getDOT(); beep(); return; } strcat(genbuf, wcursor); if (c == EOF && *vpastwh(genbuf) == 0) { /* * Although this is a delete * spanning line boundaries, what * would be left is all white space, * so take it all away. */ wcursor = 0; getDOT(); op = 0; notpart(lastreg); notpart('1'); vdelete(c); return; } ind = -1; } else if (c == EOF && wcursor == 0) { vdelete(c); return; } else #ifdef LISPCODE /* * We are just substituting text for whole lines, * so determine the first autoindent. */ if (value(LISP) && value(AUTOINDENT)) ind = lindent(dot); else #endif ind = whitecnt(linebuf); i = vcline >= 0 ? LINE(vcline) : WTOP; /* * Delete the lines from the buffer, * and remember how the partial stuff came about in * case we are told to put. */ addr = dot; vremote(cnt, delete, 0); setpk(); notenam = "delete"; if (c != EOF) notenam = "change"; /* * If DEL[0] were nonzero, put would put it back * rather than the deleted lines. */ DEL[0] = 0; if (cnt > 1) killU(); /* * Now hack the screen image coordination. */ vreplace(vcline, cnt, 0); wdot = NOLINE; noteit(0); vcline--; if (addr <= dol) dot--; /* * If this is a across line delete/change, * cursor stays where it is; just splice together the pieces * of the new line. Otherwise generate a autoindent * after a S command. */ if (ind >= 0) { *genindent(ind) = 0; vdoappend(genbuf); } else { vmcurs = cursor; strcLIN(genbuf); vdoappend(linebuf); } /* * Indicate a change on hardcopies by * erasing the current line. */ if (c != EOF && state != VISUAL && state != HARDOPEN) { int oldhold = hold; hold |= HOLDAT, vclrlin(i, dot), hold = oldhold; } /* * Open the line (logically) on the screen, and * update the screen tail. Unless we are really a delete * go off and gather up inserted characters. */ vcline++; if (vcline < 0) vcline = 0; vopen(dot, i); vsyncCL(); noteit(1); if (c != EOF) { if (ind >= 0) { cursor = linebuf; linebuf[0] = 0; vfixcurs(); } else { ind = 0; vcursat(cursor); } vappend('x', 1, ind); return; } if (*cursor == 0 && cursor > linebuf) cursor += skipleft(linebuf, cursor); vrepaint(cursor); return; } smallchange: /* * The rest of this is just low level hacking on changes * of small numbers of characters. */ if (wcursor < linebuf) wcursor = linebuf; if (cursor == wcursor) { beep(); return; } i = vdcMID(); cp = cursor; if (state != HARDOPEN) vfixcurs(); /* * Put out the \\'s indicating changed text in hardcopy, * or mark the end of the change with $ if not hardcopy. */ if (state == HARDOPEN) bleep(i, cp); else { int c, d, n; vcursbef(wcursor); d = skipleft(linebuf, wcursor); nextc(c, &wcursor[d], n); if (colsc(c) > 1) putchar(' '); putchar('$'); i = cindent(); } /* * Remember the deleted text for possible put, * and then prepare and execute the input portion of the change. */ cursor = cp; setDEL(); CP(cursor, wcursor); if (state != HARDOPEN) { vcursaft(cursor - 1); doomed = i - cindent(); } else { /* sethard(); wcursor = cursor; cursor = linebuf; vgoto(outline, value(NUMBER) << 3); vmove(); */ doomed = 0; } prepapp(); vappend('c', 1, 0); }
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); }
/* * Find over structure, repeated count times. * Don't go past line limit. F is the operation to * be performed eventually. If pastatom then the user said {} * rather than (), implying past atoms in a list (or a paragraph * rather than a sentence. */ int llfind(bool pastatom, int cnt, void (*f)(int), line *limit) { #ifdef LISPCODE register int c; #endif register int rc = 0; char *save = smalloc(LBSIZE); /* * Initialize, saving the current line buffer state * and computing the limit; a 0 argument means * directional end of file. */ wasend = 0; lf = f; lcpy(save, linebuf, LBSIZE); if (limit == 0) limit = dir < 0 ? one : dol; llimit = limit; wdot = dot; wcursor = cursor; if (pastatom >= 2) { while (cnt > 0 && word(f, cnt)) cnt--; if (pastatom == 3) eend(f); if (dot == wdot) { wdot = 0; if (cursor == wcursor) rc = -1; } } #ifdef LISPCODE else if (!value(LISP)) { #else else { #endif char *icurs; line *idot; if (linebuf[0] == 0) { do if (!lnext()) goto ret; while (linebuf[0] == 0); if (dir > 0) { wdot--; linebuf[0] = 0; wcursor = linebuf; /* * If looking for sentence, next line * starts one. */ if (!pastatom) { icurs = wcursor; idot = wdot; goto begin; } } } icurs = wcursor; idot = wdot; /* * Advance so as to not find same thing again. */ if (dir > 0) { if (!lnext()) { rc = -1; goto ret; } } else ignore(lskipa1("")); /* * Count times find end of sentence/paragraph. */ begin: for (;;) { while (!endsent(pastatom)) if (!lnext()) goto ret; if (!pastatom || (wcursor == linebuf && endPS())) if (--cnt <= 0) break; if (linebuf[0] == 0) { do if (!lnext()) goto ret; while (linebuf[0] == 0); } else if (!lnext()) goto ret; } /* * If going backwards, and didn't hit the end of the buffer, * then reverse direction. */ if (dir < 0 && (wdot != llimit || wcursor != linebuf)) { dir = 1; llimit = dot; /* * Empty line needs special treatement. * If moved to it from other than begining of next line, * then a sentence starts on next line. */ if (linebuf[0] == 0 && !pastatom && (wdot != dot - 1 || cursor != linebuf)) { lnext(); goto ret; } } /* * If we are not at a section/paragraph division, * advance to next. */ if ((wcursor == icurs && wdot == idot) || wcursor != linebuf || !endPS()) ignore(lskipa1("")); } #ifdef LISPCODE else { c = *wcursor; /* * Startup by skipping if at a ( going left or a ) going * right to keep from getting stuck immediately. */ if ((dir < 0 && c == '(') || (dir > 0 && c == ')')) { if (!lnext()) { rc = -1; goto ret; } } /* * Now chew up repitition count. Each time around * if at the beginning of an s-exp (going forwards) * or the end of an s-exp (going backwards) * skip the s-exp. If not at beg/end resp, then stop * if we hit a higher level paren, else skip an atom, * counting it unless pastatom. */ while (cnt > 0) { c = *wcursor; if ((dir < 0 && c == ')') || (dir > 0 && c == '(')) { if (!lskipbal("()")) goto ret; /* * Unless this is the last time going * backwards, skip past the matching paren * so we don't think it is a higher level paren. */ if (dir < 0 && cnt == 1) goto ret; if (!lnext() || !ltosolid()) goto ret; --cnt; } else if ((dir < 0 && c == '(') || (dir > 0 && c == ')')) /* Found a higher level paren */ goto ret; else { if (!lskipatom()) goto ret; if (!pastatom) --cnt; } } } #endif ret: strcLIN(save); free(save); return (rc); }
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(); }