1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/ed.refresh.c,v 3.47 2011/02/27 00:14:51 christos Exp $ */
259243Sobrien/*
359243Sobrien * ed.refresh.c: Lower level screen refreshing functions
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35232633SmpRCSID("$tcsh: ed.refresh.c,v 3.47 2011/02/27 00:14:51 christos Exp $")
3659243Sobrien
3759243Sobrien#include "ed.h"
3859243Sobrien/* #define DEBUG_UPDATE */
3959243Sobrien/* #define DEBUG_REFRESH */
4059243Sobrien/* #define DEBUG_LITERAL */
4159243Sobrien
4259243Sobrien/* refresh.c -- refresh the current set of lines on the screen */
4359243Sobrien
44145479SmpChar   *litptr;
4559243Sobrienstatic int vcursor_h, vcursor_v;
4659243Sobrienstatic int rprompt_h, rprompt_v;
4759243Sobrien
48167465Smpstatic	int	MakeLiteral		(Char *, int, Char);
49167465Smpstatic	int	Draw 			(Char *, int);
50167465Smpstatic	void	Vdraw 			(Char, int);
51167465Smpstatic	void	RefreshPromptpart	(Char *);
52167465Smpstatic	void	update_line 		(Char *, Char *, int);
53167465Smpstatic	void	str_insert		(Char *, int, int, Char *, int);
54167465Smpstatic	void	str_delete		(Char *, int, int, int);
55167465Smpstatic	void	str_cp			(Char *, Char *, int);
56131962Smp#ifndef WINNT_NATIVE
57131962Smpstatic
58131962Smp#else
59131962Smpextern
60131962Smp#endif
61167465Smp	void    PutPlusOne      (Char, int);
62167465Smpstatic	void	cpy_pad_spaces		(Char *, Char *, int);
6359243Sobrien#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
64232633Smpstatic	void	reprintf			(char *, ...);
6559243Sobrien#ifdef DEBUG_UPDATE
66167465Smpstatic	void	dprintstr		(char *, const Char *, const Char *);
6759243Sobrien
6859243Sobrienstatic void
69167465Smpdprintstr(char *str, const Char *f, const Char *t)
7059243Sobrien{
71232633Smp    reprintf("%s:\"", str);
72145479Smp    while (f < t) {
73167465Smp	if (ASC(*f) & ~ASCII)
74232633Smp	  reprintf("[%x]", *f++);
75145479Smp	else
76232633Smp	  reprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
77145479Smp    }
78232633Smp    reprintf("\"\r\n");
79145479Smp}
8059243Sobrien#endif /* DEBUG_UPDATE */
8159243Sobrien
82232633Smp/* reprintf():
8359243Sobrien *	Print to $DEBUGTTY, so that we can test editing on one pty, and
8459243Sobrien *      print debugging stuff on another. Don't interrupt the shell while
8559243Sobrien *	debugging cause you'll mangle up the file descriptors!
8659243Sobrien */
8759243Sobrienstatic void
88232633Smpreprintf(char *fmt, ...)
8959243Sobrien{
9059243Sobrien    static int fd = -1;
9159243Sobrien    char *dtty;
9259243Sobrien
9359243Sobrien    if ((dtty = getenv("DEBUGTTY"))) {
9459243Sobrien	int o;
9559243Sobrien	va_list va;
9659243Sobrien	va_start(va, fmt);
9759243Sobrien
9859243Sobrien	if (fd == -1)
99167465Smp	    fd = xopen(dtty, O_RDWR);
10059243Sobrien	o = SHOUT;
10159243Sobrien	flush();
10259243Sobrien	SHOUT = fd;
10359243Sobrien	xvprintf(fmt, va);
10459243Sobrien	va_end(va);
10559243Sobrien	flush();
10659243Sobrien	SHOUT = o;
10759243Sobrien    }
10859243Sobrien}
10959243Sobrien#endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
11059243Sobrien
111145479Smpstatic int litlen = 0, litalloc = 0;
112145479Smp
113167465Smpstatic int MakeLiteral(Char *str, int len, Char addlit)
11459243Sobrien{
115145479Smp    int i, addlitlen = 0;
116145479Smp    Char *addlitptr = 0;
117145479Smp    if (addlit) {
118145479Smp	if ((addlit & LITERAL) != 0) {
119145479Smp	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
120145479Smp	    addlitlen = Strlen(addlitptr);
121145479Smp	} else {
122145479Smp	    addlitptr = &addlit;
123145479Smp	    addlitlen = 1;
124145479Smp	}
125145479Smp	for (i = 0; i < litlen; i += LIT_FACTOR)
126145479Smp	    if (!Strncmp(addlitptr, litptr + i, addlitlen) && !Strncmp(str, litptr + i + addlitlen, len) && litptr[i + addlitlen + len] == 0)
127145479Smp		return (i / LIT_FACTOR) | LITERAL;
128145479Smp    } else {
129145479Smp	addlitlen = 0;
130145479Smp	for (i = 0; i < litlen; i += LIT_FACTOR)
131145479Smp	    if (!Strncmp(str, litptr + i, len) && litptr[i + len] == 0)
132145479Smp		return (i / LIT_FACTOR) | LITERAL;
13359243Sobrien    }
134145479Smp    if (litlen + addlitlen + len + 1 + (LIT_FACTOR - 1) > litalloc) {
135145479Smp	Char *newlitptr;
136145479Smp	int add = 256;
137145479Smp	while (len + addlitlen + 1 + (LIT_FACTOR - 1) > add)
138145479Smp	    add *= 2;
139167465Smp	newlitptr = xrealloc(litptr, (litalloc + add) * sizeof(Char));
140145479Smp	if (!newlitptr)
141145479Smp	    return '?';
142145479Smp	litptr = newlitptr;
143145479Smp	litalloc += add;
144145479Smp	if (addlitptr && addlitptr != &addlit)
145145479Smp	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
146145479Smp    }
147145479Smp    i = litlen / LIT_FACTOR;
148145479Smp    if (i >= LITERAL || i == CHAR_DBWIDTH)
149145479Smp	return '?';
150145479Smp    if (addlitptr) {
151145479Smp	Strncpy(litptr + litlen, addlitptr, addlitlen);
152145479Smp	litlen += addlitlen;
153145479Smp    }
154145479Smp    Strncpy(litptr + litlen, str, len);
155145479Smp    litlen += len;
156145479Smp    do
157145479Smp	litptr[litlen++] = 0;
158145479Smp    while (litlen % LIT_FACTOR);
159145479Smp    return i | LITERAL;
160145479Smp}
16159243Sobrien
162145479Smpstatic int
163167465SmpDraw(Char *cp, int nocomb)	/* draw char at cp, expand tabs, ctl chars */
164145479Smp{
165167465Smp    int w, i, lv, lh;
166167465Smp    Char c, attr;
167145479Smp
168145479Smp    attr = *cp & ~CHAR;
169167465Smp    c = *cp & CHAR;
170145479Smp    w = NLSClassify(c, nocomb);
171145479Smp    switch (w) {
172145479Smp	case NLSCLASS_NL:
173145479Smp	    Vdraw('\0', 0);		/* assure end of line	 */
174145479Smp	    vcursor_h = 0;		/* reset cursor pos	 */
17559243Sobrien	    vcursor_v++;
176145479Smp	    break;
177145479Smp	case NLSCLASS_TAB:
178145479Smp	    do {
179145479Smp		Vdraw(' ', 1);
180145479Smp	    } while ((vcursor_h & 07) != 0);
181145479Smp	    break;
182145479Smp	case NLSCLASS_CTRL:
183145479Smp	    Vdraw('^' | attr, 1);
184145479Smp	    if (c == CTL_ESC('\177')) {
185145479Smp		Vdraw('?' | attr, 1);
186145479Smp	    } else {
18769408Sache#ifdef IS_ASCII
188145479Smp		/* uncontrolify it; works only for iso8859-1 like sets */
189145479Smp		Vdraw(c | 0100 | attr, 1);
19069408Sache#else
191145479Smp		Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
192145479Smp#endif
19359243Sobrien	    }
194145479Smp	    break;
195145479Smp	case NLSCLASS_ILLEGAL:
196145479Smp	    Vdraw('\\' | attr, 1);
197167465Smp	    Vdraw((((c >> 6) & 7) + '0') | attr, 1);
198167465Smp	    Vdraw((((c >> 3) & 7) + '0') | attr, 1);
199167465Smp	    Vdraw(((c & 7) + '0') | attr, 1);
200145479Smp	    break;
201145479Smp	case NLSCLASS_ILLEGAL2:
202145479Smp	case NLSCLASS_ILLEGAL3:
203145479Smp	case NLSCLASS_ILLEGAL4:
204145479Smp	    Vdraw('\\' | attr, 1);
205145479Smp	    Vdraw('U' | attr, 1);
206145479Smp	    Vdraw('+' | attr, 1);
207145479Smp	    for (i = 8 * NLSCLASS_ILLEGAL_SIZE(w) - 4; i >= 0; i -= 4)
208145479Smp		Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
209145479Smp	    break;
210145479Smp	case 0:
211145479Smp	    lv = vcursor_v;
212145479Smp	    lh = vcursor_h;
213145479Smp	    for (;;) {
214145479Smp		lh--;
215145479Smp		if (lh < 0) {
216145479Smp		    lv--;
217145479Smp		    if (lv < 0)
218145479Smp			break;
219145479Smp		    lh = Strlen(Vdisplay[lv]) - 1;
220145479Smp		}
221145479Smp		if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
222145479Smp		    break;
223145479Smp	    }
224145479Smp	    if (lv < 0) {
225167465Smp		Vdraw('\\' | attr, 1);
226167465Smp		Vdraw((((c >> 6) & 7) + '0') | attr, 1);
227167465Smp		Vdraw((((c >> 3) & 7) + '0') | attr, 1);
228167465Smp		Vdraw(((c & 7) + '0') | attr, 1);
229167465Smp		break;
230145479Smp	    }
231167465Smp	    Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
232145479Smp	    break;
233145479Smp	default:
234167465Smp	    Vdraw(*cp, w);
235145479Smp	    break;
23659243Sobrien    }
237167465Smp    return 1;
23859243Sobrien}
23959243Sobrien
24059243Sobrienstatic void
241167465SmpVdraw(Char c, int width)	/* draw char c onto V lines */
24259243Sobrien{
24359243Sobrien#ifdef DEBUG_REFRESH
24459243Sobrien# ifdef SHORT_STRINGS
245232633Smp    reprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
24659243Sobrien# else
247232633Smp    reprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
24859243Sobrien# endif /* SHORT_STRNGS */
24959243Sobrien#endif  /* DEBUG_REFRESH */
25059243Sobrien
251145479Smp    /* Hopefully this is what all the terminals do with multi-column characters
252145479Smp       that "span line breaks". */
253145479Smp    while (vcursor_h + width > TermH)
254145479Smp	Vdraw(' ', 1);
255167465Smp    Vdisplay[vcursor_v][vcursor_h] = c;
256145479Smp    if (width)
257145479Smp	vcursor_h++;		/* advance to next place */
258145479Smp    while (--width > 0)
259145479Smp	Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
26059243Sobrien    if (vcursor_h >= TermH) {
26159243Sobrien	Vdisplay[vcursor_v][TermH] = '\0';	/* assure end of line */
26259243Sobrien	vcursor_h = 0;		/* reset it. */
26359243Sobrien	vcursor_v++;
26459243Sobrien#ifdef DEBUG_REFRESH
26559243Sobrien	if (vcursor_v >= TermV) {	/* should NEVER happen. */
266232633Smp	    reprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
26759243Sobrien		    vcursor_v, TermV);
26859243Sobrien	    abort();
26959243Sobrien	}
27059243Sobrien#endif /* DEBUG_REFRESH */
27159243Sobrien    }
27259243Sobrien}
27359243Sobrien
27459243Sobrien/*
27559243Sobrien *  RefreshPromptpart()
27659243Sobrien *	draws a prompt element, expanding literals (we know it's ASCIZ)
27759243Sobrien */
27859243Sobrienstatic void
279167465SmpRefreshPromptpart(Char *buf)
28059243Sobrien{
281145479Smp    Char *cp;
282167465Smp    int w;
28359243Sobrien
284167465Smp    if (buf == NULL)
285167465Smp	return;
286145479Smp    for (cp = buf; *cp; ) {
28759243Sobrien	if (*cp & LITERAL) {
288145479Smp	    Char *litstart = cp;
28959243Sobrien	    while (*cp & LITERAL)
29059243Sobrien		cp++;
291145479Smp	    if (*cp) {
292167465Smp		w = NLSWidth(*cp & CHAR);
293167465Smp		Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
294167465Smp		cp++;
295145479Smp	    }
29659243Sobrien	    else {
29759243Sobrien		/*
29859243Sobrien		 * XXX: This is a bug, we lose the last literal, if it is not
29959243Sobrien		 * followed by a normal character, but it is too hard to fix
30059243Sobrien		 */
30159243Sobrien		break;
30259243Sobrien	    }
30359243Sobrien	}
30459243Sobrien	else
305145479Smp	    cp += Draw(cp, cp == buf);
30659243Sobrien    }
30759243Sobrien}
30859243Sobrien
30959243Sobrien/*
31059243Sobrien *  Refresh()
31159243Sobrien *	draws the new virtual screen image from the current input
31259243Sobrien *  	line, then goes line-by-line changing the real image to the new
31359243Sobrien *	virtual image. The routine to re-draw a line can be replaced
31459243Sobrien *	easily in hopes of a smarter one being placed there.
31559243Sobrien */
316131962Smp#ifndef WINNT_NATIVE
317131962Smpstatic
318131962Smp#endif
319131962Smpint OldvcV = 0;
320131962Smp
32159243Sobrienvoid
322167465SmpRefresh(void)
32359243Sobrien{
324145479Smp    int cur_line;
325145479Smp    Char *cp;
32659243Sobrien    int     cur_h, cur_v = 0, new_vcv;
32759243Sobrien    int     rhdiff;
32859243Sobrien    Char    oldgetting;
32959243Sobrien
33059243Sobrien#ifdef DEBUG_REFRESH
331232633Smp    reprintf("Prompt = :%s:\r\n", short2str(Prompt));
332232633Smp    reprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
33359243Sobrien#endif /* DEBUG_REFRESH */
33459243Sobrien    oldgetting = GettingInput;
33559243Sobrien    GettingInput = 0;		/* avoid re-entrance via SIGWINCH */
33659243Sobrien
33759243Sobrien    /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
33859243Sobrien    vcursor_h = 0;
33959243Sobrien    vcursor_v = 0;
340167465Smp    RefreshPromptpart(RPrompt);
34159243Sobrien    rprompt_h = vcursor_h;
34259243Sobrien    rprompt_v = vcursor_v;
34359243Sobrien
34459243Sobrien    /* reset the Vdraw cursor, draw prompt */
34559243Sobrien    vcursor_h = 0;
34659243Sobrien    vcursor_v = 0;
347167465Smp    RefreshPromptpart(Prompt);
34859243Sobrien    cur_h = -1;			/* set flag in case I'm not set */
34959243Sobrien
35059243Sobrien    /* draw the current input buffer */
351145479Smp    for (cp = InputBuf; (cp < LastChar); ) {
352145479Smp	if (cp >= Cursor && cur_h == -1) {
35359243Sobrien	    cur_h = vcursor_h;	/* save for later */
35459243Sobrien	    cur_v = vcursor_v;
355145479Smp	    Cursor = cp;
35659243Sobrien	}
357145479Smp	cp += Draw(cp, cp == InputBuf);
35859243Sobrien    }
35959243Sobrien
36059243Sobrien    if (cur_h == -1) {		/* if I haven't been set yet, I'm at the end */
36159243Sobrien	cur_h = vcursor_h;
36259243Sobrien	cur_v = vcursor_v;
36359243Sobrien    }
36459243Sobrien
36559243Sobrien    rhdiff = TermH - vcursor_h - rprompt_h;
36659243Sobrien    if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
36759243Sobrien			/*
36859243Sobrien			 * have a right-hand side prompt that will fit on
36959243Sobrien			 * the end of the first line with at least one
37059243Sobrien			 * character gap to the input buffer.
37159243Sobrien			 */
37259243Sobrien	while (--rhdiff > 0)		/* pad out with spaces */
373145479Smp	    Vdraw(' ', 1);
374167465Smp	RefreshPromptpart(RPrompt);
37559243Sobrien    }
37659243Sobrien    else {
37759243Sobrien	rprompt_h = 0;			/* flag "not using rprompt" */
37859243Sobrien	rprompt_v = 0;
37959243Sobrien    }
38059243Sobrien
38159243Sobrien    new_vcv = vcursor_v;	/* must be done BEFORE the NUL is written */
382145479Smp    Vdraw('\0', 1);		/* put NUL on end */
38359243Sobrien
384145479Smp#if defined (DEBUG_REFRESH)
385232633Smp    reprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
38659243Sobrien	    TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
38759243Sobrien#endif /* DEBUG_REFRESH */
38859243Sobrien
38959243Sobrien#ifdef DEBUG_UPDATE
390232633Smp    reprintf("updating %d lines.\r\n", new_vcv);
39159243Sobrien#endif  /* DEBUG_UPDATE */
39259243Sobrien    for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
39359243Sobrien	/* NOTE THAT update_line MAY CHANGE Display[cur_line] */
39459243Sobrien	update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
39569408Sache#ifdef WINNT_NATIVE
39659243Sobrien	flush();
39769408Sache#endif /* WINNT_NATIVE */
39859243Sobrien
39959243Sobrien	/*
40059243Sobrien	 * Copy the new line to be the current one, and pad out with spaces
40159243Sobrien	 * to the full width of the terminal so that if we try moving the
40259243Sobrien	 * cursor by writing the character that is at the end of the
40359243Sobrien	 * screen line, it won't be a NUL or some old leftover stuff.
40459243Sobrien	 */
40559243Sobrien	cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
40659243Sobrien    }
40759243Sobrien#ifdef DEBUG_REFRESH
408232633Smp    reprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
40959243Sobrien	    vcursor_v, OldvcV, cur_line);
41059243Sobrien#endif /* DEBUG_REFRESH */
41159243Sobrien    if (OldvcV > new_vcv) {
41259243Sobrien	for (; cur_line <= OldvcV; cur_line++) {
41359243Sobrien	    update_line(Display[cur_line], STRNULL, cur_line);
41459243Sobrien	    *Display[cur_line] = '\0';
41559243Sobrien	}
41659243Sobrien    }
41759243Sobrien    OldvcV = new_vcv;		/* set for next time */
41859243Sobrien#ifdef DEBUG_REFRESH
419232633Smp    reprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
42059243Sobrien	    CursorH, CursorV, cur_h, cur_v);
42159243Sobrien#endif /* DEBUG_REFRESH */
42269408Sache#ifdef WINNT_NATIVE
42359243Sobrien    flush();
42469408Sache#endif /* WINNT_NATIVE */
42559243Sobrien    MoveToLine(cur_v);		/* go to where the cursor is */
42659243Sobrien    MoveToChar(cur_h);
42759243Sobrien    SetAttributes(0);		/* Clear all attributes */
42859243Sobrien    flush();			/* send the output... */
42959243Sobrien    GettingInput = oldgetting;	/* reset to old value */
43059243Sobrien}
43159243Sobrien
43259243Sobrien#ifdef notdef
433167465SmpGotoBottom(void)
43459243Sobrien{				/* used to go to last used screen line */
43559243Sobrien    MoveToLine(OldvcV);
43659243Sobrien}
43759243Sobrien
43859243Sobrien#endif
43959243Sobrien
44059243Sobrienvoid
441167465SmpPastBottom(void)
44259243Sobrien{				/* used to go to last used screen line */
44359243Sobrien    MoveToLine(OldvcV);
44459243Sobrien    (void) putraw('\r');
44559243Sobrien    (void) putraw('\n');
44659243Sobrien    ClearDisp();
44759243Sobrien    flush();
44859243Sobrien}
44959243Sobrien
45059243Sobrien
45159243Sobrien/* insert num characters of s into d (in front of the character) at dat,
45259243Sobrien   maximum length of d is dlen */
45359243Sobrienstatic void
454167465Smpstr_insert(Char *d, int dat, int dlen, Char *s, int num)
45559243Sobrien{
456145479Smp    Char *a, *b;
45759243Sobrien
45859243Sobrien    if (num <= 0)
45959243Sobrien	return;
46059243Sobrien    if (num > dlen - dat)
46159243Sobrien	num = dlen - dat;
46259243Sobrien
46359243Sobrien#ifdef DEBUG_REFRESH
464232633Smp    reprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
46559243Sobrien	    num, dat, dlen, short2str(d));
466232633Smp    reprintf("s == \"%s\"n", short2str(s));
46759243Sobrien#endif /* DEBUG_REFRESH */
46859243Sobrien
46959243Sobrien    /* open up the space for num chars */
47059243Sobrien    if (num > 0) {
47159243Sobrien	b = d + dlen - 1;
47259243Sobrien	a = b - num;
47359243Sobrien	while (a >= &d[dat])
47459243Sobrien	    *b-- = *a--;
47559243Sobrien	d[dlen] = '\0';		/* just in case */
47659243Sobrien    }
47759243Sobrien#ifdef DEBUG_REFRESH
478232633Smp    reprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
47959243Sobrien	    num, dat, dlen, short2str(d));
480232633Smp    reprintf("s == \"%s\"n", short2str(s));
48159243Sobrien#endif /* DEBUG_REFRESH */
48259243Sobrien
48359243Sobrien    /* copy the characters */
48459243Sobrien    for (a = d + dat; (a < d + dlen) && (num > 0); num--)
48559243Sobrien	*a++ = *s++;
48659243Sobrien
48759243Sobrien#ifdef DEBUG_REFRESH
488232633Smp    reprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
48959243Sobrien	    num, dat, dlen, d, short2str(s));
490232633Smp    reprintf("s == \"%s\"n", short2str(s));
49159243Sobrien#endif /* DEBUG_REFRESH */
49259243Sobrien}
49359243Sobrien
49459243Sobrien/* delete num characters d at dat, maximum length of d is dlen */
49559243Sobrienstatic void
496167465Smpstr_delete(Char *d, int dat, int dlen, int num)
49759243Sobrien{
498145479Smp    Char *a, *b;
49959243Sobrien
50059243Sobrien    if (num <= 0)
50159243Sobrien	return;
50259243Sobrien    if (dat + num >= dlen) {
50359243Sobrien	d[dat] = '\0';
50459243Sobrien	return;
50559243Sobrien    }
50659243Sobrien
50759243Sobrien#ifdef DEBUG_REFRESH
508232633Smp    reprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
50959243Sobrien	    num, dat, dlen, short2str(d));
51059243Sobrien#endif /* DEBUG_REFRESH */
51159243Sobrien
51259243Sobrien    /* open up the space for num chars */
51359243Sobrien    if (num > 0) {
51459243Sobrien	b = d + dat;
51559243Sobrien	a = b + num;
51659243Sobrien	while (a < &d[dlen])
51759243Sobrien	    *b++ = *a++;
51859243Sobrien	d[dlen] = '\0';		/* just in case */
51959243Sobrien    }
52059243Sobrien#ifdef DEBUG_REFRESH
521232633Smp    reprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
52259243Sobrien	    num, dat, dlen, short2str(d));
52359243Sobrien#endif /* DEBUG_REFRESH */
52459243Sobrien}
52559243Sobrien
52659243Sobrienstatic void
527167465Smpstr_cp(Char *a, Char *b, int n)
52859243Sobrien{
52959243Sobrien    while (n-- && *b)
53059243Sobrien	*a++ = *b++;
53159243Sobrien}
53259243Sobrien
53359243Sobrien
53459243Sobrien/* ****************************************************************
53559243Sobrien    update_line() is based on finding the middle difference of each line
53659243Sobrien    on the screen; vis:
53759243Sobrien
53859243Sobrien			     /old first difference
53959243Sobrien	/beginning of line   |              /old last same       /old EOL
54059243Sobrien	v		     v              v                    v
54159243Sobrienold:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
54259243Sobriennew:	eddie> Oh, my little buggy says to me, as lurgid as
54359243Sobrien	^		     ^        ^			   ^
54459243Sobrien	\beginning of line   |        \new last same	   \new end of line
54559243Sobrien			     \new first difference
54659243Sobrien
54759243Sobrien    all are character pointers for the sake of speed.  Special cases for
54859243Sobrien    no differences, as well as for end of line additions must be handled.
54959243Sobrien**************************************************************** */
55059243Sobrien
55159243Sobrien/* Minimum at which doing an insert it "worth it".  This should be about
55259243Sobrien * half the "cost" of going into insert mode, inserting a character, and
55359243Sobrien * going back out.  This should really be calculated from the termcap
55459243Sobrien * data...  For the moment, a good number for ANSI terminals.
55559243Sobrien */
55659243Sobrien#define MIN_END_KEEP	4
55759243Sobrien
55859243Sobrienstatic void			/* could be changed to make it smarter */
559167465Smpupdate_line(Char *old, Char *new, int cur_line)
56059243Sobrien{
561145479Smp    Char *o, *n, *p, c;
562145479Smp    Char  *ofd, *ols, *oe, *nfd, *nls, *ne;
563145479Smp    Char  *osb, *ose, *nsb, *nse;
56459243Sobrien    int     fx, sx;
56559243Sobrien
56659243Sobrien    /*
567145479Smp     * find first diff (won't be CHAR_DBWIDTH in either line)
56859243Sobrien     */
56959243Sobrien    for (o = old, n = new; *o && (*o == *n); o++, n++)
57059243Sobrien	continue;
57159243Sobrien    ofd = o;
57259243Sobrien    nfd = n;
57359243Sobrien
57459243Sobrien    /*
57559243Sobrien     * Find the end of both old and new
57659243Sobrien     */
577167465Smp    o = Strend(o);
578167465Smp
57959243Sobrien    /*
58059243Sobrien     * Remove any trailing blanks off of the end, being careful not to
58159243Sobrien     * back up past the beginning.
58259243Sobrien     */
583167465Smp    if (!(adrof(STRhighlight) && MarkIsSet)) {
58459243Sobrien    while (ofd < o) {
58559243Sobrien	if (o[-1] != ' ')
58659243Sobrien	    break;
58759243Sobrien	o--;
58859243Sobrien    }
589167465Smp    }
59059243Sobrien    oe = o;
59159243Sobrien    *oe = (Char) 0;
59259243Sobrien
593167465Smp    n = Strend(n);
594167465Smp
59559243Sobrien    /* remove blanks from end of new */
596167465Smp    if (!(adrof(STRhighlight) && MarkIsSet)) {
59759243Sobrien    while (nfd < n) {
59859243Sobrien	if (n[-1] != ' ')
59959243Sobrien	    break;
60059243Sobrien	n--;
60159243Sobrien    }
602167465Smp    }
60359243Sobrien    ne = n;
60459243Sobrien    *ne = (Char) 0;
60559243Sobrien
60659243Sobrien    /*
60759243Sobrien     * if no diff, continue to next line of redraw
60859243Sobrien     */
60959243Sobrien    if (*ofd == '\0' && *nfd == '\0') {
61059243Sobrien#ifdef DEBUG_UPDATE
611232633Smp	reprintf("no difference.\r\n");
61259243Sobrien#endif /* DEBUG_UPDATE */
61359243Sobrien	return;
61459243Sobrien    }
61559243Sobrien
61659243Sobrien    /*
61759243Sobrien     * find last same pointer
61859243Sobrien     */
61959243Sobrien    while ((o > ofd) && (n > nfd) && (*--o == *--n))
62059243Sobrien	continue;
621145479Smp    if (*o != *n) {
622145479Smp	o++;
623145479Smp	n++;
624145479Smp    }
625145479Smp    while (*o == CHAR_DBWIDTH) {
626145479Smp	o++;
627145479Smp	n++;
628145479Smp    }
629145479Smp    ols = o;
630145479Smp    nls = n;
63159243Sobrien
63259243Sobrien    /*
63359243Sobrien     * find same begining and same end
63459243Sobrien     */
63559243Sobrien    osb = ols;
63659243Sobrien    nsb = nls;
63759243Sobrien    ose = ols;
63859243Sobrien    nse = nls;
63959243Sobrien
64059243Sobrien    /*
64159243Sobrien     * case 1: insert: scan from nfd to nls looking for *ofd
64259243Sobrien     */
64359243Sobrien    if (*ofd) {
64459243Sobrien	for (c = *ofd, n = nfd; n < nls; n++) {
64559243Sobrien	    if (c == *n) {
64659243Sobrien		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
64759243Sobrien		    continue;
64859243Sobrien		/*
64959243Sobrien		 * if the new match is longer and it's worth keeping, then we
65059243Sobrien		 * take it
65159243Sobrien		 */
65259243Sobrien		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
65359243Sobrien		    nsb = n;
65459243Sobrien		    nse = p;
65559243Sobrien		    osb = ofd;
65659243Sobrien		    ose = o;
65759243Sobrien		}
65859243Sobrien	    }
65959243Sobrien	}
66059243Sobrien    }
66159243Sobrien
66259243Sobrien    /*
66359243Sobrien     * case 2: delete: scan from ofd to ols looking for *nfd
66459243Sobrien     */
66559243Sobrien    if (*nfd) {
66659243Sobrien	for (c = *nfd, o = ofd; o < ols; o++) {
66759243Sobrien	    if (c == *o) {
66859243Sobrien		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
66959243Sobrien		    continue;
67059243Sobrien		/*
67159243Sobrien		 * if the new match is longer and it's worth keeping, then we
67259243Sobrien		 * take it
67359243Sobrien		 */
67459243Sobrien		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
67559243Sobrien		    nsb = nfd;
67659243Sobrien		    nse = n;
67759243Sobrien		    osb = o;
67859243Sobrien		    ose = p;
67959243Sobrien		}
68059243Sobrien	    }
68159243Sobrien	}
68259243Sobrien    }
68359243Sobrien#ifdef notdef
68459243Sobrien    /*
68559243Sobrien     * If `last same' is before `same end' re-adjust
68659243Sobrien     */
68759243Sobrien    if (ols < ose)
68859243Sobrien	ols = ose;
68959243Sobrien    if (nls < nse)
69059243Sobrien	nls = nse;
69159243Sobrien#endif
69259243Sobrien
69359243Sobrien    /*
69459243Sobrien     * Pragmatics I: If old trailing whitespace or not enough characters to
69559243Sobrien     * save to be worth it, then don't save the last same info.
69659243Sobrien     */
69759243Sobrien    if ((oe - ols) < MIN_END_KEEP) {
69859243Sobrien	ols = oe;
69959243Sobrien	nls = ne;
70059243Sobrien    }
70159243Sobrien
70259243Sobrien    /*
70359243Sobrien     * Pragmatics II: if the terminal isn't smart enough, make the data dumber
70459243Sobrien     * so the smart update doesn't try anything fancy
70559243Sobrien     */
70659243Sobrien
70759243Sobrien    /*
70859243Sobrien     * fx is the number of characters we need to insert/delete: in the
70959243Sobrien     * beginning to bring the two same begins together
71059243Sobrien     */
71159243Sobrien    fx = (int) ((nsb - nfd) - (osb - ofd));
71259243Sobrien    /*
71359243Sobrien     * sx is the number of characters we need to insert/delete: in the end to
71459243Sobrien     * bring the two same last parts together
71559243Sobrien     */
71659243Sobrien    sx = (int) ((nls - nse) - (ols - ose));
71759243Sobrien
71859243Sobrien    if (!T_CanIns) {
71959243Sobrien	if (fx > 0) {
72059243Sobrien	    osb = ols;
72159243Sobrien	    ose = ols;
72259243Sobrien	    nsb = nls;
72359243Sobrien	    nse = nls;
72459243Sobrien	}
72559243Sobrien	if (sx > 0) {
72659243Sobrien	    ols = oe;
72759243Sobrien	    nls = ne;
72859243Sobrien	}
72959243Sobrien	if ((ols - ofd) < (nls - nfd)) {
73059243Sobrien	    ols = oe;
73159243Sobrien	    nls = ne;
73259243Sobrien	}
73359243Sobrien    }
73459243Sobrien    if (!T_CanDel) {
73559243Sobrien	if (fx < 0) {
73659243Sobrien	    osb = ols;
73759243Sobrien	    ose = ols;
73859243Sobrien	    nsb = nls;
73959243Sobrien	    nse = nls;
74059243Sobrien	}
74159243Sobrien	if (sx < 0) {
74259243Sobrien	    ols = oe;
74359243Sobrien	    nls = ne;
74459243Sobrien	}
74559243Sobrien	if ((ols - ofd) > (nls - nfd)) {
74659243Sobrien	    ols = oe;
74759243Sobrien	    nls = ne;
74859243Sobrien	}
74959243Sobrien    }
75059243Sobrien
75159243Sobrien    /*
75259243Sobrien     * Pragmatics III: make sure the middle shifted pointers are correct if
75359243Sobrien     * they don't point to anything (we may have moved ols or nls).
75459243Sobrien     */
75559243Sobrien    /* if the change isn't worth it, don't bother */
75659243Sobrien    /* was: if (osb == ose) */
75759243Sobrien    if ((ose - osb) < MIN_END_KEEP) {
75859243Sobrien	osb = ols;
75959243Sobrien	ose = ols;
76059243Sobrien	nsb = nls;
76159243Sobrien	nse = nls;
76259243Sobrien    }
76359243Sobrien
76459243Sobrien    /*
76559243Sobrien     * Now that we are done with pragmatics we recompute fx, sx
76659243Sobrien     */
76759243Sobrien    fx = (int) ((nsb - nfd) - (osb - ofd));
76859243Sobrien    sx = (int) ((nls - nse) - (ols - ose));
76959243Sobrien
77059243Sobrien#ifdef DEBUG_UPDATE
771232633Smp    reprintf("\n");
772232633Smp    reprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
77359243Sobrien	    ofd - old, osb - old, ose - old, ols - old, oe - old);
774232633Smp    reprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
77559243Sobrien	    nfd - new, nsb - new, nse - new, nls - new, ne - new);
776232633Smp    reprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
777232633Smp    reprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
77859243Sobrien    dprintstr("old- oe", old, oe);
77959243Sobrien    dprintstr("new- ne", new, ne);
78059243Sobrien    dprintstr("old-ofd", old, ofd);
78159243Sobrien    dprintstr("new-nfd", new, nfd);
78259243Sobrien    dprintstr("ofd-osb", ofd, osb);
78359243Sobrien    dprintstr("nfd-nsb", nfd, nsb);
78459243Sobrien    dprintstr("osb-ose", osb, ose);
78559243Sobrien    dprintstr("nsb-nse", nsb, nse);
78659243Sobrien    dprintstr("ose-ols", ose, ols);
78759243Sobrien    dprintstr("nse-nls", nse, nls);
78859243Sobrien    dprintstr("ols- oe", ols, oe);
78959243Sobrien    dprintstr("nls- ne", nls, ne);
79059243Sobrien#endif /* DEBUG_UPDATE */
79159243Sobrien
79259243Sobrien    /*
79359243Sobrien     * CursorV to this line cur_line MUST be in this routine so that if we
79459243Sobrien     * don't have to change the line, we don't move to it. CursorH to first
79559243Sobrien     * diff char
79659243Sobrien     */
79759243Sobrien    MoveToLine(cur_line);
79859243Sobrien
79959243Sobrien    /*
80059243Sobrien     * at this point we have something like this:
80159243Sobrien     *
80259243Sobrien     * /old                  /ofd    /osb               /ose    /ols     /oe
80359243Sobrien     * v.....................v       v..................v       v........v
80459243Sobrien     * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
80559243Sobrien     * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
80659243Sobrien     * ^.....................^     ^..................^       ^........^
80759243Sobrien     * \new                  \nfd  \nsb               \nse     \nls    \ne
80859243Sobrien     *
80959243Sobrien     * fx is the difference in length between the the chars between nfd and
81059243Sobrien     * nsb, and the chars between ofd and osb, and is thus the number of
81159243Sobrien     * characters to delete if < 0 (new is shorter than old, as above),
81259243Sobrien     * or insert (new is longer than short).
81359243Sobrien     *
81459243Sobrien     * sx is the same for the second differences.
81559243Sobrien     */
81659243Sobrien
81759243Sobrien    /*
81859243Sobrien     * if we have a net insert on the first difference, AND inserting the net
81959243Sobrien     * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
82059243Sobrien     * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
82159243Sobrien     * (TermH - 1) else we do the deletes first so that we keep everything we
82259243Sobrien     * need to.
82359243Sobrien     */
82459243Sobrien
82559243Sobrien    /*
82659243Sobrien     * if the last same is the same like the end, there is no last same part,
82759243Sobrien     * otherwise we want to keep the last same part set p to the last useful
82859243Sobrien     * old character
82959243Sobrien     */
83059243Sobrien    p = (ols != oe) ? oe : ose;
83159243Sobrien
83259243Sobrien    /*
83359243Sobrien     * if (There is a diffence in the beginning) && (we need to insert
83459243Sobrien     * characters) && (the number of characters to insert is less than the term
83559243Sobrien     * width) We need to do an insert! else if (we need to delete characters)
83659243Sobrien     * We need to delete characters! else No insert or delete
83759243Sobrien     */
83859243Sobrien    if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
83959243Sobrien#ifdef DEBUG_UPDATE
840232633Smp	reprintf("first diff insert at %d...\r\n", nfd - new);
84159243Sobrien#endif  /* DEBUG_UPDATE */
84259243Sobrien	/*
84359243Sobrien	 * Move to the first char to insert, where the first diff is.
84459243Sobrien	 */
84559243Sobrien	MoveToChar(nfd - new);
84659243Sobrien	/*
84759243Sobrien	 * Check if we have stuff to keep at end
84859243Sobrien	 */
84959243Sobrien	if (nsb != ne) {
85059243Sobrien#ifdef DEBUG_UPDATE
851232633Smp	    reprintf("with stuff to keep at end\r\n");
85259243Sobrien#endif  /* DEBUG_UPDATE */
85359243Sobrien	    /*
85459243Sobrien	     * insert fx chars of new starting at nfd
85559243Sobrien	     */
85659243Sobrien	    if (fx > 0) {
85759243Sobrien#ifdef DEBUG_UPDATE
85859243Sobrien		if (!T_CanIns)
859232633Smp		    reprintf("   ERROR: cannot insert in early first diff\n");
86059243Sobrien#endif  /* DEBUG_UPDATE */
86159243Sobrien		Insert_write(nfd, fx);
86259243Sobrien		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
86359243Sobrien	    }
86459243Sobrien	    /*
86559243Sobrien	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
86659243Sobrien	     */
86759243Sobrien	    so_write(nfd + fx, (nsb - nfd) - fx);
86859243Sobrien	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
86959243Sobrien	}
87059243Sobrien	else {
87159243Sobrien#ifdef DEBUG_UPDATE
872232633Smp	    reprintf("without anything to save\r\n");
87359243Sobrien#endif  /* DEBUG_UPDATE */
87459243Sobrien	    so_write(nfd, (nsb - nfd));
87559243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
87659243Sobrien	    /*
87759243Sobrien	     * Done
87859243Sobrien	     */
87959243Sobrien	    return;
88059243Sobrien	}
88159243Sobrien    }
88259243Sobrien    else if (fx < 0) {
88359243Sobrien#ifdef DEBUG_UPDATE
884232633Smp	reprintf("first diff delete at %d...\r\n", ofd - old);
88559243Sobrien#endif  /* DEBUG_UPDATE */
88659243Sobrien	/*
88759243Sobrien	 * move to the first char to delete where the first diff is
88859243Sobrien	 */
88959243Sobrien	MoveToChar(ofd - old);
89059243Sobrien	/*
89159243Sobrien	 * Check if we have stuff to save
89259243Sobrien	 */
89359243Sobrien	if (osb != oe) {
89459243Sobrien#ifdef DEBUG_UPDATE
895232633Smp	    reprintf("with stuff to save at end\r\n");
89659243Sobrien#endif  /* DEBUG_UPDATE */
89759243Sobrien	    /*
89859243Sobrien	     * fx is less than zero *always* here but we check for code
89959243Sobrien	     * symmetry
90059243Sobrien	     */
90159243Sobrien	    if (fx < 0) {
90259243Sobrien#ifdef DEBUG_UPDATE
90359243Sobrien		if (!T_CanDel)
904232633Smp		    reprintf("   ERROR: cannot delete in first diff\n");
90559243Sobrien#endif /* DEBUG_UPDATE */
90659243Sobrien		DeleteChars(-fx);
90759243Sobrien		str_delete(old, (int) (ofd - old), TermH, -fx);
90859243Sobrien	    }
90959243Sobrien	    /*
91059243Sobrien	     * write (nsb-nfd) chars of new starting at nfd
91159243Sobrien	     */
91259243Sobrien	    so_write(nfd, (nsb - nfd));
91359243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
91459243Sobrien
91559243Sobrien	}
91659243Sobrien	else {
91759243Sobrien#ifdef DEBUG_UPDATE
918232633Smp	    reprintf("but with nothing left to save\r\n");
91959243Sobrien#endif  /* DEBUG_UPDATE */
92059243Sobrien	    /*
92159243Sobrien	     * write (nsb-nfd) chars of new starting at nfd
92259243Sobrien	     */
92359243Sobrien	    so_write(nfd, (nsb - nfd));
92459243Sobrien#ifdef DEBUG_REFRESH
925232633Smp	    reprintf("cleareol %d\n", (oe - old) - (ne - new));
92659243Sobrien#endif  /* DEBUG_UPDATE */
92769408Sache#ifndef WINNT_NATIVE
92859243Sobrien	    ClearEOL((oe - old) - (ne - new));
92959243Sobrien#else
93059243Sobrien	    /*
93159243Sobrien	     * The calculation above does not work too well on NT
93259243Sobrien	     */
93359243Sobrien	    ClearEOL(TermH - CursorH);
93469408Sache#endif /*WINNT_NATIVE*/
93559243Sobrien	    /*
93659243Sobrien	     * Done
93759243Sobrien	     */
93859243Sobrien	    return;
93959243Sobrien	}
94059243Sobrien    }
94159243Sobrien    else
94259243Sobrien	fx = 0;
94359243Sobrien
94459243Sobrien    if (sx < 0) {
94559243Sobrien#ifdef DEBUG_UPDATE
946232633Smp	reprintf("second diff delete at %d...\r\n", (ose - old) + fx);
94759243Sobrien#endif  /* DEBUG_UPDATE */
94859243Sobrien	/*
94959243Sobrien	 * Check if we have stuff to delete
95059243Sobrien	 */
95159243Sobrien	/*
95259243Sobrien	 * fx is the number of characters inserted (+) or deleted (-)
95359243Sobrien	 */
95459243Sobrien
95559243Sobrien	MoveToChar((ose - old) + fx);
95659243Sobrien	/*
95759243Sobrien	 * Check if we have stuff to save
95859243Sobrien	 */
95959243Sobrien	if (ols != oe) {
96059243Sobrien#ifdef DEBUG_UPDATE
961232633Smp	    reprintf("with stuff to save at end\r\n");
96259243Sobrien#endif  /* DEBUG_UPDATE */
96359243Sobrien	    /*
96459243Sobrien	     * Again a duplicate test.
96559243Sobrien	     */
96659243Sobrien	    if (sx < 0) {
96759243Sobrien#ifdef DEBUG_UPDATE
96859243Sobrien		if (!T_CanDel)
969232633Smp		    reprintf("   ERROR: cannot delete in second diff\n");
97059243Sobrien#endif  /* DEBUG_UPDATE */
97159243Sobrien		DeleteChars(-sx);
97259243Sobrien	    }
97359243Sobrien
97459243Sobrien	    /*
97559243Sobrien	     * write (nls-nse) chars of new starting at nse
97659243Sobrien	     */
97759243Sobrien	    so_write(nse, (nls - nse));
97859243Sobrien	}
97959243Sobrien	else {
98059243Sobrien	    int olen = (int) (oe - old + fx);
98159243Sobrien	    if (olen > TermH)
98259243Sobrien		olen = TermH;
98359243Sobrien#ifdef DEBUG_UPDATE
984232633Smp	    reprintf("but with nothing left to save\r\n");
98559243Sobrien#endif /* DEBUG_UPDATE */
98659243Sobrien	    so_write(nse, (nls - nse));
98759243Sobrien#ifdef DEBUG_REFRESH
988232633Smp	    reprintf("cleareol %d\n", olen - (ne - new));
98959243Sobrien#endif /* DEBUG_UPDATE */
99069408Sache#ifndef WINNT_NATIVE
99159243Sobrien	    ClearEOL(olen - (ne - new));
99259243Sobrien#else
99359243Sobrien	    /*
99459243Sobrien	     * The calculation above does not work too well on NT
99559243Sobrien	     */
99659243Sobrien	    ClearEOL(TermH - CursorH);
99769408Sache#endif /*WINNT_NATIVE*/
99859243Sobrien	}
99959243Sobrien    }
100059243Sobrien
100159243Sobrien    /*
100259243Sobrien     * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
100359243Sobrien     */
100459243Sobrien    if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
100559243Sobrien#ifdef DEBUG_UPDATE
1006232633Smp	reprintf("late first diff insert at %d...\r\n", nfd - new);
100759243Sobrien#endif /* DEBUG_UPDATE */
100859243Sobrien
100959243Sobrien	MoveToChar(nfd - new);
101059243Sobrien	/*
101159243Sobrien	 * Check if we have stuff to keep at the end
101259243Sobrien	 */
101359243Sobrien	if (nsb != ne) {
101459243Sobrien#ifdef DEBUG_UPDATE
1015232633Smp	    reprintf("with stuff to keep at end\r\n");
101659243Sobrien#endif /* DEBUG_UPDATE */
101759243Sobrien	    /*
101859243Sobrien	     * We have to recalculate fx here because we set it
101959243Sobrien	     * to zero above as a flag saying that we hadn't done
102059243Sobrien	     * an early first insert.
102159243Sobrien	     */
102259243Sobrien	    fx = (int) ((nsb - nfd) - (osb - ofd));
102359243Sobrien	    if (fx > 0) {
102459243Sobrien		/*
102559243Sobrien		 * insert fx chars of new starting at nfd
102659243Sobrien		 */
102759243Sobrien#ifdef DEBUG_UPDATE
102859243Sobrien		if (!T_CanIns)
1029232633Smp		    reprintf("   ERROR: cannot insert in late first diff\n");
103059243Sobrien#endif /* DEBUG_UPDATE */
103159243Sobrien		Insert_write(nfd, fx);
103259243Sobrien		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
103359243Sobrien	    }
103459243Sobrien
103559243Sobrien	    /*
103659243Sobrien	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
103759243Sobrien	     */
103859243Sobrien	    so_write(nfd + fx, (nsb - nfd) - fx);
103959243Sobrien	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
104059243Sobrien	}
104159243Sobrien	else {
104259243Sobrien#ifdef DEBUG_UPDATE
1043232633Smp	    reprintf("without anything to save\r\n");
104459243Sobrien#endif /* DEBUG_UPDATE */
104559243Sobrien	    so_write(nfd, (nsb - nfd));
104659243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
104759243Sobrien	}
104859243Sobrien    }
104959243Sobrien
105059243Sobrien    /*
105159243Sobrien     * line is now NEW up to nse
105259243Sobrien     */
105359243Sobrien    if (sx >= 0) {
105459243Sobrien#ifdef DEBUG_UPDATE
1055232633Smp	reprintf("second diff insert at %d...\r\n", nse - new);
105659243Sobrien#endif /* DEBUG_UPDATE */
105759243Sobrien	MoveToChar(nse - new);
105859243Sobrien	if (ols != oe) {
105959243Sobrien#ifdef DEBUG_UPDATE
1060232633Smp	    reprintf("with stuff to keep at end\r\n");
106159243Sobrien#endif /* DEBUG_UPDATE */
106259243Sobrien	    if (sx > 0) {
106359243Sobrien		/* insert sx chars of new starting at nse */
106459243Sobrien#ifdef DEBUG_UPDATE
106559243Sobrien		if (!T_CanIns)
1066232633Smp		    reprintf("   ERROR: cannot insert in second diff\n");
106759243Sobrien#endif /* DEBUG_UPDATE */
106859243Sobrien		Insert_write(nse, sx);
106959243Sobrien	    }
107059243Sobrien
107159243Sobrien	    /*
107259243Sobrien	     * write (nls-nse) - sx chars of new starting at (nse + sx)
107359243Sobrien	     */
107459243Sobrien	    so_write(nse + sx, (nls - nse) - sx);
107559243Sobrien	}
107659243Sobrien	else {
107759243Sobrien#ifdef DEBUG_UPDATE
1078232633Smp	    reprintf("without anything to save\r\n");
107959243Sobrien#endif /* DEBUG_UPDATE */
108059243Sobrien	    so_write(nse, (nls - nse));
108159243Sobrien
108259243Sobrien	    /*
108359243Sobrien             * No need to do a clear-to-end here because we were doing
108459243Sobrien	     * a second insert, so we will have over written all of the
108559243Sobrien	     * old string.
108659243Sobrien	     */
108759243Sobrien	}
108859243Sobrien    }
108959243Sobrien#ifdef DEBUG_UPDATE
1090232633Smp    reprintf("done.\r\n");
109159243Sobrien#endif /* DEBUG_UPDATE */
109259243Sobrien}
109359243Sobrien
109459243Sobrien
109559243Sobrienstatic void
1096167465Smpcpy_pad_spaces(Char *dst, Char *src, int width)
109759243Sobrien{
1098145479Smp    int i;
109959243Sobrien
110059243Sobrien    for (i = 0; i < width; i++) {
110159243Sobrien	if (*src == (Char) 0)
110259243Sobrien	    break;
110359243Sobrien	*dst++ = *src++;
110459243Sobrien    }
110559243Sobrien
110659243Sobrien    while (i < width) {
110759243Sobrien	*dst++ = ' ';
110859243Sobrien	i++;
110959243Sobrien    }
111059243Sobrien    *dst = (Char) 0;
111159243Sobrien}
111259243Sobrien
111359243Sobrienvoid
1114167465SmpRefCursor(void)
111559243Sobrien{				/* only move to new cursor pos */
1116145479Smp    Char *cp;
1117167465Smp    int w, h, th, v;
111859243Sobrien
111959243Sobrien    /* first we must find where the cursor is... */
112059243Sobrien    h = 0;
112159243Sobrien    v = 0;
112259243Sobrien    th = TermH;			/* optimize for speed */
112359243Sobrien
1124167465Smp    for (cp = Prompt; cp != NULL && *cp; ) {	/* do prompt */
1125145479Smp	if (*cp & LITERAL) {
1126145479Smp	    cp++;
112759243Sobrien	    continue;
112859243Sobrien	}
1129167465Smp	w = NLSClassify(*cp & CHAR, cp == Prompt);
1130167465Smp	cp++;
1131145479Smp	switch(w) {
1132145479Smp	    case NLSCLASS_NL:
1133145479Smp		h = 0;
1134145479Smp		v++;
1135145479Smp		break;
1136145479Smp	    case NLSCLASS_TAB:
1137145479Smp		while (++h & 07)
1138145479Smp		    ;
1139145479Smp		break;
1140145479Smp	    case NLSCLASS_CTRL:
1141145479Smp		h += 2;
1142145479Smp		break;
1143145479Smp	    case NLSCLASS_ILLEGAL:
1144145479Smp		h += 4;
1145145479Smp		break;
1146145479Smp	    case NLSCLASS_ILLEGAL2:
1147145479Smp	    case NLSCLASS_ILLEGAL3:
1148145479Smp	    case NLSCLASS_ILLEGAL4:
1149145479Smp		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1150145479Smp		break;
1151145479Smp	    default:
1152145479Smp		h += w;
115359243Sobrien	}
115459243Sobrien	if (h >= th) {		/* check, extra long tabs picked up here also */
1155145479Smp	    h -= th;
115659243Sobrien	    v++;
115759243Sobrien	}
115859243Sobrien    }
115959243Sobrien
1160145479Smp    for (cp = InputBuf; cp < Cursor;) {	/* do input buffer to Cursor */
1161167465Smp	w = NLSClassify(*cp & CHAR, cp == InputBuf);
1162167465Smp	cp++;
1163145479Smp	switch(w) {
1164145479Smp	    case NLSCLASS_NL:
1165145479Smp		h = 0;
1166145479Smp		v++;
1167145479Smp		break;
1168145479Smp	    case NLSCLASS_TAB:
1169145479Smp		while (++h & 07)
1170145479Smp		    ;
1171145479Smp		break;
1172145479Smp	    case NLSCLASS_CTRL:
1173145479Smp		h += 2;
1174145479Smp		break;
1175145479Smp	    case NLSCLASS_ILLEGAL:
1176145479Smp		h += 4;
1177145479Smp		break;
1178145479Smp	    case NLSCLASS_ILLEGAL2:
1179145479Smp	    case NLSCLASS_ILLEGAL3:
1180145479Smp	    case NLSCLASS_ILLEGAL4:
1181145479Smp		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1182145479Smp		break;
1183145479Smp	    default:
1184145479Smp		h += w;
118559243Sobrien	}
118659243Sobrien	if (h >= th) {		/* check, extra long tabs picked up here also */
1187145479Smp	    h -= th;
118859243Sobrien	    v++;
118959243Sobrien	}
119059243Sobrien    }
119159243Sobrien
119259243Sobrien    /* now go there */
119359243Sobrien    MoveToLine(v);
119459243Sobrien    MoveToChar(h);
1195167465Smp    if (adrof(STRhighlight) && MarkIsSet) {
1196167465Smp	ClearLines();
1197167465Smp	ClearDisp();
1198167465Smp	Refresh();
1199167465Smp    }
120059243Sobrien    flush();
120159243Sobrien}
120259243Sobrien
1203131962Smp#ifndef WINTT_NATIVE
120459243Sobrienstatic void
1205167465SmpPutPlusOne(Char c, int width)
120659243Sobrien{
1207145479Smp    while (width > 1 && CursorH + width > TermH)
1208145479Smp	PutPlusOne(' ', 1);
1209145479Smp    if ((c & LITERAL) != 0) {
1210145479Smp	Char *d;
1211145479Smp	for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1212145479Smp	    (void) putwraw(*d);
1213145479Smp    } else {
1214145479Smp	(void) putwraw(c);
1215145479Smp    }
121659243Sobrien    Display[CursorV][CursorH++] = (Char) c;
1217145479Smp    while (--width > 0)
1218145479Smp	Display[CursorV][CursorH++] = CHAR_DBWIDTH;
121959243Sobrien    if (CursorH >= TermH) {	/* if we must overflow */
122059243Sobrien	CursorH = 0;
122159243Sobrien	CursorV++;
122259243Sobrien	OldvcV++;
122359243Sobrien	if (T_Margin & MARGIN_AUTO) {
122459243Sobrien	    if (T_Margin & MARGIN_MAGIC) {
122559243Sobrien		(void) putraw(' ');
122659243Sobrien		(void) putraw('\b');
122759243Sobrien	    }
122859243Sobrien	}
122959243Sobrien	else {
123059243Sobrien	    (void) putraw('\r');
123159243Sobrien	    (void) putraw('\n');
123259243Sobrien	}
123359243Sobrien    }
123459243Sobrien}
1235131962Smp#endif
123659243Sobrien
123759243Sobrienvoid
1238145479SmpRefPlusOne(int l)
123959243Sobrien{				/* we added just one char, handle it fast.
124059243Sobrien				 * assumes that screen cursor == real cursor */
1241167465Smp    Char *cp, c;
1242145479Smp    int w;
124359243Sobrien
1244145479Smp    if (Cursor != LastChar) {
124559243Sobrien	Refresh();		/* too hard to handle */
124659243Sobrien	return;
124759243Sobrien    }
124859243Sobrien    if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
124959243Sobrien	Refresh();		/* clear out rprompt if less than one char gap*/
125059243Sobrien	return;
1251145479Smp    }
1252145479Smp    cp = Cursor - l;
1253167465Smp    c = *cp & CHAR;
1254145479Smp    w = NLSClassify(c, cp == InputBuf);
1255145479Smp    switch(w) {
1256145479Smp	case NLSCLASS_CTRL:
1257145479Smp	    PutPlusOne('^', 1);
1258145479Smp	    if (c == CTL_ESC('\177')) {
1259145479Smp		PutPlusOne('?', 1);
1260145479Smp		break;
1261145479Smp	    }
126269408Sache#ifdef IS_ASCII
1263145479Smp	    /* uncontrolify it; works only for iso8859-1 like sets */
1264145479Smp	    PutPlusOne((c | 0100), 1);
126569408Sache#else
1266145479Smp	    PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
126769408Sache#endif
1268145479Smp	    break;
1269145479Smp	case NLSCLASS_ILLEGAL:
1270145479Smp	    PutPlusOne('\\', 1);
1271145479Smp	    PutPlusOne(((c >> 6) & 7) + '0', 1);
1272145479Smp	    PutPlusOne(((c >> 3) & 7) + '0', 1);
1273145479Smp	    PutPlusOne((c & 7) + '0', 1);
1274145479Smp	    break;
1275145479Smp	case 1:
1276167465Smp	    if (adrof(STRhighlight) && MarkIsSet)
1277167465Smp		StartHighlight();
1278145479Smp	    if (l > 1)
1279145479Smp		PutPlusOne(MakeLiteral(cp, l, 0), 1);
1280145479Smp	    else
1281145479Smp		PutPlusOne(*cp, 1);
1282167465Smp	    if (adrof(STRhighlight) && MarkIsSet)
1283167465Smp		StopHighlight();
1284145479Smp	    break;
1285145479Smp	default:
1286145479Smp	    Refresh();		/* too hard to handle */
1287145479Smp	    return;
128859243Sobrien    }
128959243Sobrien    flush();
129059243Sobrien}
129159243Sobrien
129259243Sobrien/* clear the screen buffers so that new new prompt starts fresh. */
129359243Sobrien
129459243Sobrienvoid
1295167465SmpClearDisp(void)
129659243Sobrien{
1297145479Smp    int i;
129859243Sobrien
129959243Sobrien    CursorV = 0;		/* clear the display buffer */
130059243Sobrien    CursorH = 0;
130159243Sobrien    for (i = 0; i < TermV; i++)
130259243Sobrien	(void) memset(Display[i], 0, TermH * sizeof(Display[0][0]));
130359243Sobrien    OldvcV = 0;
1304145479Smp    litlen = 0;
130559243Sobrien}
130659243Sobrien
130759243Sobrienvoid
1308167465SmpClearLines(void)
130959243Sobrien{				/* Make sure all lines are *really* blank */
1310145479Smp    int i;
131159243Sobrien
131259243Sobrien    if (T_CanCEOL) {
131359243Sobrien	/*
131459243Sobrien	 * Clear the lines from the bottom up so that if we try moving
131559243Sobrien	 * the cursor down by writing the character that is at the end
131659243Sobrien	 * of the screen line, we won't rewrite a character that shouldn't
131759243Sobrien	 * be there.
131859243Sobrien	 */
131959243Sobrien	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
132059243Sobrien	    MoveToLine(i);
132159243Sobrien	    MoveToChar(0);
132259243Sobrien	    ClearEOL(TermH);
132359243Sobrien	}
132459243Sobrien    }
132559243Sobrien    else {
132659243Sobrien	MoveToLine(OldvcV);	/* go to last line */
132759243Sobrien	(void) putraw('\r');	/* go to BOL */
132859243Sobrien	(void) putraw('\n');	/* go to new line */
132959243Sobrien    }
133059243Sobrien}
1331