159243Sobrien/*
259243Sobrien * ed.refresh.c: Lower level screen refreshing functions
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "ed.h"
3459243Sobrien/* #define DEBUG_UPDATE */
3559243Sobrien/* #define DEBUG_REFRESH */
3659243Sobrien/* #define DEBUG_LITERAL */
3759243Sobrien
3859243Sobrien/* refresh.c -- refresh the current set of lines on the screen */
3959243Sobrien
40145479SmpChar   *litptr;
4159243Sobrienstatic int vcursor_h, vcursor_v;
4259243Sobrienstatic int rprompt_h, rprompt_v;
4359243Sobrien
44167465Smpstatic	int	MakeLiteral		(Char *, int, Char);
45316957Sdchaginstatic	int	Draw 			(Char *, int, int);
46167465Smpstatic	void	Vdraw 			(Char, int);
47167465Smpstatic	void	RefreshPromptpart	(Char *);
48167465Smpstatic	void	update_line 		(Char *, Char *, int);
49167465Smpstatic	void	str_insert		(Char *, int, int, Char *, int);
50167465Smpstatic	void	str_delete		(Char *, int, int, int);
51167465Smpstatic	void	str_cp			(Char *, Char *, int);
52131962Smp#ifndef WINNT_NATIVE
53131962Smpstatic
54131962Smp#else
55131962Smpextern
56131962Smp#endif
57167465Smp	void    PutPlusOne      (Char, int);
58167465Smpstatic	void	cpy_pad_spaces		(Char *, Char *, int);
5959243Sobrien#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
60231990Smpstatic	void	reprintf			(char *, ...);
6159243Sobrien#ifdef DEBUG_UPDATE
62167465Smpstatic	void	dprintstr		(char *, const Char *, const Char *);
6359243Sobrien
6459243Sobrienstatic void
65167465Smpdprintstr(char *str, const Char *f, const Char *t)
6659243Sobrien{
67231990Smp    reprintf("%s:\"", str);
68145479Smp    while (f < t) {
69167465Smp	if (ASC(*f) & ~ASCII)
70231990Smp	  reprintf("[%x]", *f++);
71145479Smp	else
72231990Smp	  reprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
73145479Smp    }
74231990Smp    reprintf("\"\r\n");
75145479Smp}
7659243Sobrien#endif /* DEBUG_UPDATE */
7759243Sobrien
78231990Smp/* reprintf():
7959243Sobrien *	Print to $DEBUGTTY, so that we can test editing on one pty, and
8059243Sobrien *      print debugging stuff on another. Don't interrupt the shell while
8159243Sobrien *	debugging cause you'll mangle up the file descriptors!
8259243Sobrien */
8359243Sobrienstatic void
84231990Smpreprintf(char *fmt, ...)
8559243Sobrien{
8659243Sobrien    static int fd = -1;
8759243Sobrien    char *dtty;
8859243Sobrien
8959243Sobrien    if ((dtty = getenv("DEBUGTTY"))) {
9059243Sobrien	int o;
9159243Sobrien	va_list va;
9259243Sobrien	va_start(va, fmt);
9359243Sobrien
9459243Sobrien	if (fd == -1)
95167465Smp	    fd = xopen(dtty, O_RDWR);
9659243Sobrien	o = SHOUT;
9759243Sobrien	flush();
9859243Sobrien	SHOUT = fd;
9959243Sobrien	xvprintf(fmt, va);
10059243Sobrien	va_end(va);
10159243Sobrien	flush();
10259243Sobrien	SHOUT = o;
10359243Sobrien    }
10459243Sobrien}
10559243Sobrien#endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
10659243Sobrien
107145479Smpstatic int litlen = 0, litalloc = 0;
108145479Smp
109167465Smpstatic int MakeLiteral(Char *str, int len, Char addlit)
11059243Sobrien{
111145479Smp    int i, addlitlen = 0;
112145479Smp    Char *addlitptr = 0;
113145479Smp    if (addlit) {
114145479Smp	if ((addlit & LITERAL) != 0) {
115145479Smp	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
116145479Smp	    addlitlen = Strlen(addlitptr);
117145479Smp	} else {
118145479Smp	    addlitptr = &addlit;
119145479Smp	    addlitlen = 1;
120145479Smp	}
121145479Smp	for (i = 0; i < litlen; i += LIT_FACTOR)
122145479Smp	    if (!Strncmp(addlitptr, litptr + i, addlitlen) && !Strncmp(str, litptr + i + addlitlen, len) && litptr[i + addlitlen + len] == 0)
123145479Smp		return (i / LIT_FACTOR) | LITERAL;
124145479Smp    } else {
125145479Smp	addlitlen = 0;
126145479Smp	for (i = 0; i < litlen; i += LIT_FACTOR)
127145479Smp	    if (!Strncmp(str, litptr + i, len) && litptr[i + len] == 0)
128145479Smp		return (i / LIT_FACTOR) | LITERAL;
12959243Sobrien    }
130145479Smp    if (litlen + addlitlen + len + 1 + (LIT_FACTOR - 1) > litalloc) {
131145479Smp	Char *newlitptr;
132145479Smp	int add = 256;
133145479Smp	while (len + addlitlen + 1 + (LIT_FACTOR - 1) > add)
134145479Smp	    add *= 2;
135167465Smp	newlitptr = xrealloc(litptr, (litalloc + add) * sizeof(Char));
136145479Smp	if (!newlitptr)
137145479Smp	    return '?';
138145479Smp	litptr = newlitptr;
139145479Smp	litalloc += add;
140145479Smp	if (addlitptr && addlitptr != &addlit)
141145479Smp	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
142145479Smp    }
143145479Smp    i = litlen / LIT_FACTOR;
144145479Smp    if (i >= LITERAL || i == CHAR_DBWIDTH)
145145479Smp	return '?';
146145479Smp    if (addlitptr) {
147145479Smp	Strncpy(litptr + litlen, addlitptr, addlitlen);
148145479Smp	litlen += addlitlen;
149145479Smp    }
150145479Smp    Strncpy(litptr + litlen, str, len);
151145479Smp    litlen += len;
152145479Smp    do
153145479Smp	litptr[litlen++] = 0;
154145479Smp    while (litlen % LIT_FACTOR);
155145479Smp    return i | LITERAL;
156145479Smp}
15759243Sobrien
158316957Sdchagin/* draw char at cp, expand tabs, ctl chars */
159145479Smpstatic int
160316957SdchaginDraw(Char *cp, int nocomb, int drawPrompt)
161145479Smp{
162167465Smp    int w, i, lv, lh;
163167465Smp    Char c, attr;
164145479Smp
165316957Sdchagin#ifdef WIDE_STRINGS
166316957Sdchagin    if (!drawPrompt) {			/* draw command-line */
167316957Sdchagin	attr = 0;
168316957Sdchagin	c = *cp;
169316957Sdchagin    } else {				/* draw prompt */
170316957Sdchagin	/* prompt with attributes(UNDER,BOLD,STANDOUT) */
171316957Sdchagin	if (*cp & (UNDER | BOLD | STANDOUT)) {		/* *cp >= STANDOUT */
172316957Sdchagin
173316957Sdchagin	    /* example)
174316957Sdchagin	     * We can't distinguish whether (*cp=)0x02ffffff is
175316957Sdchagin	     * U+02FFFFFF or U+00FFFFFF|STANDOUT.
176316957Sdchagin	     * We handle as U+00FFFFFF|STANDOUT, only when drawing prompt. */
177316957Sdchagin	    attr = (*cp & ATTRIBUTES);
178316957Sdchagin	    /* ~(UNDER | BOLD | STANDOUT) = 0xf1ffffff */
179316957Sdchagin	    c = *cp & ~(UNDER | BOLD | STANDOUT);
180316957Sdchagin
181316957Sdchagin	    /* if c is ctrl code, we handle *cp as havnig no attributes */
182316957Sdchagin	    if ((c < 0x20 && c >= 0) || c == 0x7f) {
183316957Sdchagin		attr = 0;
184316957Sdchagin		c = *cp;
185316957Sdchagin	    }
186316957Sdchagin	} else {			/* prompt without attributes */
187316957Sdchagin	    attr = 0;
188316957Sdchagin	    c = *cp;
189316957Sdchagin	}
190316957Sdchagin    }
191316957Sdchagin#else
192145479Smp    attr = *cp & ~CHAR;
193167465Smp    c = *cp & CHAR;
194316957Sdchagin#endif
195316957Sdchagin    w = NLSClassify(c, nocomb, drawPrompt);
196145479Smp    switch (w) {
197145479Smp	case NLSCLASS_NL:
198145479Smp	    Vdraw('\0', 0);		/* assure end of line	 */
199145479Smp	    vcursor_h = 0;		/* reset cursor pos	 */
20059243Sobrien	    vcursor_v++;
201145479Smp	    break;
202145479Smp	case NLSCLASS_TAB:
203145479Smp	    do {
204145479Smp		Vdraw(' ', 1);
205145479Smp	    } while ((vcursor_h & 07) != 0);
206145479Smp	    break;
207145479Smp	case NLSCLASS_CTRL:
208145479Smp	    Vdraw('^' | attr, 1);
209145479Smp	    if (c == CTL_ESC('\177')) {
210145479Smp		Vdraw('?' | attr, 1);
211145479Smp	    } else {
21269408Sache#ifdef IS_ASCII
213145479Smp		/* uncontrolify it; works only for iso8859-1 like sets */
214145479Smp		Vdraw(c | 0100 | attr, 1);
21569408Sache#else
216145479Smp		Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
217145479Smp#endif
21859243Sobrien	    }
219145479Smp	    break;
220145479Smp	case NLSCLASS_ILLEGAL:
221145479Smp	    Vdraw('\\' | attr, 1);
222167465Smp	    Vdraw((((c >> 6) & 7) + '0') | attr, 1);
223167465Smp	    Vdraw((((c >> 3) & 7) + '0') | attr, 1);
224167465Smp	    Vdraw(((c & 7) + '0') | attr, 1);
225145479Smp	    break;
226145479Smp	case NLSCLASS_ILLEGAL2:
227145479Smp	case NLSCLASS_ILLEGAL3:
228145479Smp	case NLSCLASS_ILLEGAL4:
229316957Sdchagin	case NLSCLASS_ILLEGAL5:
230316957Sdchagin	    Vdraw('\\', 1);
231316957Sdchagin	    Vdraw('U', 1);
232316957Sdchagin	    Vdraw('+', 1);
233316957Sdchagin	    for (i = 16 + 4 * (-w-5); i >= 0; i -= 4)
234145479Smp		Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
235145479Smp	    break;
236145479Smp	case 0:
237145479Smp	    lv = vcursor_v;
238145479Smp	    lh = vcursor_h;
239145479Smp	    for (;;) {
240145479Smp		lh--;
241145479Smp		if (lh < 0) {
242145479Smp		    lv--;
243145479Smp		    if (lv < 0)
244145479Smp			break;
245145479Smp		    lh = Strlen(Vdisplay[lv]) - 1;
246145479Smp		}
247145479Smp		if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
248145479Smp		    break;
249145479Smp	    }
250145479Smp	    if (lv < 0) {
251167465Smp		Vdraw('\\' | attr, 1);
252167465Smp		Vdraw((((c >> 6) & 7) + '0') | attr, 1);
253167465Smp		Vdraw((((c >> 3) & 7) + '0') | attr, 1);
254167465Smp		Vdraw(((c & 7) + '0') | attr, 1);
255167465Smp		break;
256145479Smp	    }
257167465Smp	    Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
258145479Smp	    break;
259145479Smp	default:
260167465Smp	    Vdraw(*cp, w);
261145479Smp	    break;
26259243Sobrien    }
263167465Smp    return 1;
26459243Sobrien}
26559243Sobrien
26659243Sobrienstatic void
267167465SmpVdraw(Char c, int width)	/* draw char c onto V lines */
26859243Sobrien{
26959243Sobrien#ifdef DEBUG_REFRESH
27059243Sobrien# ifdef SHORT_STRINGS
271231990Smp    reprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
27259243Sobrien# else
273231990Smp    reprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
27459243Sobrien# endif /* SHORT_STRNGS */
27559243Sobrien#endif  /* DEBUG_REFRESH */
27659243Sobrien
277145479Smp    /* Hopefully this is what all the terminals do with multi-column characters
278145479Smp       that "span line breaks". */
279145479Smp    while (vcursor_h + width > TermH)
280145479Smp	Vdraw(' ', 1);
281167465Smp    Vdisplay[vcursor_v][vcursor_h] = c;
282145479Smp    if (width)
283145479Smp	vcursor_h++;		/* advance to next place */
284145479Smp    while (--width > 0)
285145479Smp	Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
28659243Sobrien    if (vcursor_h >= TermH) {
28759243Sobrien	Vdisplay[vcursor_v][TermH] = '\0';	/* assure end of line */
28859243Sobrien	vcursor_h = 0;		/* reset it. */
28959243Sobrien	vcursor_v++;
29059243Sobrien#ifdef DEBUG_REFRESH
29159243Sobrien	if (vcursor_v >= TermV) {	/* should NEVER happen. */
292231990Smp	    reprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
29359243Sobrien		    vcursor_v, TermV);
29459243Sobrien	    abort();
29559243Sobrien	}
29659243Sobrien#endif /* DEBUG_REFRESH */
29759243Sobrien    }
29859243Sobrien}
29959243Sobrien
30059243Sobrien/*
30159243Sobrien *  RefreshPromptpart()
30259243Sobrien *	draws a prompt element, expanding literals (we know it's ASCIZ)
30359243Sobrien */
30459243Sobrienstatic void
305167465SmpRefreshPromptpart(Char *buf)
30659243Sobrien{
307145479Smp    Char *cp;
308167465Smp    int w;
30959243Sobrien
310167465Smp    if (buf == NULL)
311167465Smp	return;
312145479Smp    for (cp = buf; *cp; ) {
31359243Sobrien	if (*cp & LITERAL) {
314145479Smp	    Char *litstart = cp;
31559243Sobrien	    while (*cp & LITERAL)
31659243Sobrien		cp++;
317145479Smp	    if (*cp) {
318167465Smp		w = NLSWidth(*cp & CHAR);
319167465Smp		Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
320167465Smp		cp++;
321145479Smp	    }
32259243Sobrien	    else {
32359243Sobrien		/*
32459243Sobrien		 * XXX: This is a bug, we lose the last literal, if it is not
32559243Sobrien		 * followed by a normal character, but it is too hard to fix
32659243Sobrien		 */
32759243Sobrien		break;
32859243Sobrien	    }
32959243Sobrien	}
33059243Sobrien	else
331316957Sdchagin	    cp += Draw(cp, cp == buf, 1);
33259243Sobrien    }
33359243Sobrien}
33459243Sobrien
33559243Sobrien/*
33659243Sobrien *  Refresh()
33759243Sobrien *	draws the new virtual screen image from the current input
33859243Sobrien *  	line, then goes line-by-line changing the real image to the new
33959243Sobrien *	virtual image. The routine to re-draw a line can be replaced
34059243Sobrien *	easily in hopes of a smarter one being placed there.
34159243Sobrien */
342131962Smp#ifndef WINNT_NATIVE
343131962Smpstatic
344131962Smp#endif
345131962Smpint OldvcV = 0;
346131962Smp
34759243Sobrienvoid
348167465SmpRefresh(void)
34959243Sobrien{
350145479Smp    int cur_line;
351145479Smp    Char *cp;
35259243Sobrien    int     cur_h, cur_v = 0, new_vcv;
35359243Sobrien    int     rhdiff;
35459243Sobrien    Char    oldgetting;
35559243Sobrien
35659243Sobrien#ifdef DEBUG_REFRESH
357231990Smp    reprintf("Prompt = :%s:\r\n", short2str(Prompt));
358231990Smp    reprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
35959243Sobrien#endif /* DEBUG_REFRESH */
36059243Sobrien    oldgetting = GettingInput;
36159243Sobrien    GettingInput = 0;		/* avoid re-entrance via SIGWINCH */
36259243Sobrien
36359243Sobrien    /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
36459243Sobrien    vcursor_h = 0;
36559243Sobrien    vcursor_v = 0;
366167465Smp    RefreshPromptpart(RPrompt);
36759243Sobrien    rprompt_h = vcursor_h;
36859243Sobrien    rprompt_v = vcursor_v;
36959243Sobrien
37059243Sobrien    /* reset the Vdraw cursor, draw prompt */
37159243Sobrien    vcursor_h = 0;
37259243Sobrien    vcursor_v = 0;
373167465Smp    RefreshPromptpart(Prompt);
37459243Sobrien    cur_h = -1;			/* set flag in case I'm not set */
37559243Sobrien
37659243Sobrien    /* draw the current input buffer */
377145479Smp    for (cp = InputBuf; (cp < LastChar); ) {
378145479Smp	if (cp >= Cursor && cur_h == -1) {
37959243Sobrien	    cur_h = vcursor_h;	/* save for later */
38059243Sobrien	    cur_v = vcursor_v;
381145479Smp	    Cursor = cp;
38259243Sobrien	}
383316957Sdchagin	cp += Draw(cp, cp == InputBuf, 0);
38459243Sobrien    }
38559243Sobrien
38659243Sobrien    if (cur_h == -1) {		/* if I haven't been set yet, I'm at the end */
38759243Sobrien	cur_h = vcursor_h;
38859243Sobrien	cur_v = vcursor_v;
38959243Sobrien    }
39059243Sobrien
39159243Sobrien    rhdiff = TermH - vcursor_h - rprompt_h;
39259243Sobrien    if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
39359243Sobrien			/*
39459243Sobrien			 * have a right-hand side prompt that will fit on
39559243Sobrien			 * the end of the first line with at least one
39659243Sobrien			 * character gap to the input buffer.
39759243Sobrien			 */
39859243Sobrien	while (--rhdiff > 0)		/* pad out with spaces */
399145479Smp	    Vdraw(' ', 1);
400167465Smp	RefreshPromptpart(RPrompt);
40159243Sobrien    }
40259243Sobrien    else {
40359243Sobrien	rprompt_h = 0;			/* flag "not using rprompt" */
40459243Sobrien	rprompt_v = 0;
40559243Sobrien    }
40659243Sobrien
40759243Sobrien    new_vcv = vcursor_v;	/* must be done BEFORE the NUL is written */
408145479Smp    Vdraw('\0', 1);		/* put NUL on end */
40959243Sobrien
410145479Smp#if defined (DEBUG_REFRESH)
411231990Smp    reprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
41259243Sobrien	    TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
41359243Sobrien#endif /* DEBUG_REFRESH */
41459243Sobrien
41559243Sobrien#ifdef DEBUG_UPDATE
416231990Smp    reprintf("updating %d lines.\r\n", new_vcv);
41759243Sobrien#endif  /* DEBUG_UPDATE */
41859243Sobrien    for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
41959243Sobrien	/* NOTE THAT update_line MAY CHANGE Display[cur_line] */
42059243Sobrien	update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
42169408Sache#ifdef WINNT_NATIVE
42259243Sobrien	flush();
42369408Sache#endif /* WINNT_NATIVE */
42459243Sobrien
42559243Sobrien	/*
42659243Sobrien	 * Copy the new line to be the current one, and pad out with spaces
42759243Sobrien	 * to the full width of the terminal so that if we try moving the
42859243Sobrien	 * cursor by writing the character that is at the end of the
42959243Sobrien	 * screen line, it won't be a NUL or some old leftover stuff.
43059243Sobrien	 */
43159243Sobrien	cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
43259243Sobrien    }
43359243Sobrien#ifdef DEBUG_REFRESH
434231990Smp    reprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
43559243Sobrien	    vcursor_v, OldvcV, cur_line);
43659243Sobrien#endif /* DEBUG_REFRESH */
43759243Sobrien    if (OldvcV > new_vcv) {
43859243Sobrien	for (; cur_line <= OldvcV; cur_line++) {
43959243Sobrien	    update_line(Display[cur_line], STRNULL, cur_line);
44059243Sobrien	    *Display[cur_line] = '\0';
44159243Sobrien	}
44259243Sobrien    }
44359243Sobrien    OldvcV = new_vcv;		/* set for next time */
44459243Sobrien#ifdef DEBUG_REFRESH
445231990Smp    reprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
44659243Sobrien	    CursorH, CursorV, cur_h, cur_v);
44759243Sobrien#endif /* DEBUG_REFRESH */
44869408Sache#ifdef WINNT_NATIVE
44959243Sobrien    flush();
45069408Sache#endif /* WINNT_NATIVE */
45159243Sobrien    MoveToLine(cur_v);		/* go to where the cursor is */
45259243Sobrien    MoveToChar(cur_h);
45359243Sobrien    SetAttributes(0);		/* Clear all attributes */
45459243Sobrien    flush();			/* send the output... */
45559243Sobrien    GettingInput = oldgetting;	/* reset to old value */
45659243Sobrien}
45759243Sobrien
45859243Sobrien#ifdef notdef
459167465SmpGotoBottom(void)
46059243Sobrien{				/* used to go to last used screen line */
46159243Sobrien    MoveToLine(OldvcV);
46259243Sobrien}
46359243Sobrien
46459243Sobrien#endif
46559243Sobrien
46659243Sobrienvoid
467167465SmpPastBottom(void)
46859243Sobrien{				/* used to go to last used screen line */
46959243Sobrien    MoveToLine(OldvcV);
47059243Sobrien    (void) putraw('\r');
47159243Sobrien    (void) putraw('\n');
47259243Sobrien    ClearDisp();
47359243Sobrien    flush();
47459243Sobrien}
47559243Sobrien
47659243Sobrien
47759243Sobrien/* insert num characters of s into d (in front of the character) at dat,
47859243Sobrien   maximum length of d is dlen */
47959243Sobrienstatic void
480167465Smpstr_insert(Char *d, int dat, int dlen, Char *s, int num)
48159243Sobrien{
482145479Smp    Char *a, *b;
48359243Sobrien
48459243Sobrien    if (num <= 0)
48559243Sobrien	return;
48659243Sobrien    if (num > dlen - dat)
48759243Sobrien	num = dlen - dat;
48859243Sobrien
48959243Sobrien#ifdef DEBUG_REFRESH
490231990Smp    reprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
49159243Sobrien	    num, dat, dlen, short2str(d));
492231990Smp    reprintf("s == \"%s\"n", short2str(s));
49359243Sobrien#endif /* DEBUG_REFRESH */
49459243Sobrien
49559243Sobrien    /* open up the space for num chars */
49659243Sobrien    if (num > 0) {
49759243Sobrien	b = d + dlen - 1;
49859243Sobrien	a = b - num;
49959243Sobrien	while (a >= &d[dat])
50059243Sobrien	    *b-- = *a--;
50159243Sobrien	d[dlen] = '\0';		/* just in case */
50259243Sobrien    }
50359243Sobrien#ifdef DEBUG_REFRESH
504231990Smp    reprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
50559243Sobrien	    num, dat, dlen, short2str(d));
506231990Smp    reprintf("s == \"%s\"n", short2str(s));
50759243Sobrien#endif /* DEBUG_REFRESH */
50859243Sobrien
50959243Sobrien    /* copy the characters */
51059243Sobrien    for (a = d + dat; (a < d + dlen) && (num > 0); num--)
51159243Sobrien	*a++ = *s++;
51259243Sobrien
51359243Sobrien#ifdef DEBUG_REFRESH
514231990Smp    reprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
51559243Sobrien	    num, dat, dlen, d, short2str(s));
516231990Smp    reprintf("s == \"%s\"n", short2str(s));
51759243Sobrien#endif /* DEBUG_REFRESH */
51859243Sobrien}
51959243Sobrien
52059243Sobrien/* delete num characters d at dat, maximum length of d is dlen */
52159243Sobrienstatic void
522167465Smpstr_delete(Char *d, int dat, int dlen, int num)
52359243Sobrien{
524145479Smp    Char *a, *b;
52559243Sobrien
52659243Sobrien    if (num <= 0)
52759243Sobrien	return;
52859243Sobrien    if (dat + num >= dlen) {
52959243Sobrien	d[dat] = '\0';
53059243Sobrien	return;
53159243Sobrien    }
53259243Sobrien
53359243Sobrien#ifdef DEBUG_REFRESH
534231990Smp    reprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
53559243Sobrien	    num, dat, dlen, short2str(d));
53659243Sobrien#endif /* DEBUG_REFRESH */
53759243Sobrien
53859243Sobrien    /* open up the space for num chars */
53959243Sobrien    if (num > 0) {
54059243Sobrien	b = d + dat;
54159243Sobrien	a = b + num;
54259243Sobrien	while (a < &d[dlen])
54359243Sobrien	    *b++ = *a++;
54459243Sobrien	d[dlen] = '\0';		/* just in case */
54559243Sobrien    }
54659243Sobrien#ifdef DEBUG_REFRESH
547231990Smp    reprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
54859243Sobrien	    num, dat, dlen, short2str(d));
54959243Sobrien#endif /* DEBUG_REFRESH */
55059243Sobrien}
55159243Sobrien
55259243Sobrienstatic void
553167465Smpstr_cp(Char *a, Char *b, int n)
55459243Sobrien{
55559243Sobrien    while (n-- && *b)
55659243Sobrien	*a++ = *b++;
55759243Sobrien}
55859243Sobrien
55959243Sobrien
56059243Sobrien/* ****************************************************************
56159243Sobrien    update_line() is based on finding the middle difference of each line
56259243Sobrien    on the screen; vis:
56359243Sobrien
56459243Sobrien			     /old first difference
56559243Sobrien	/beginning of line   |              /old last same       /old EOL
56659243Sobrien	v		     v              v                    v
56759243Sobrienold:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
56859243Sobriennew:	eddie> Oh, my little buggy says to me, as lurgid as
56959243Sobrien	^		     ^        ^			   ^
57059243Sobrien	\beginning of line   |        \new last same	   \new end of line
57159243Sobrien			     \new first difference
57259243Sobrien
57359243Sobrien    all are character pointers for the sake of speed.  Special cases for
57459243Sobrien    no differences, as well as for end of line additions must be handled.
57559243Sobrien**************************************************************** */
57659243Sobrien
57759243Sobrien/* Minimum at which doing an insert it "worth it".  This should be about
57859243Sobrien * half the "cost" of going into insert mode, inserting a character, and
57959243Sobrien * going back out.  This should really be calculated from the termcap
58059243Sobrien * data...  For the moment, a good number for ANSI terminals.
58159243Sobrien */
58259243Sobrien#define MIN_END_KEEP	4
58359243Sobrien
58459243Sobrienstatic void			/* could be changed to make it smarter */
585167465Smpupdate_line(Char *old, Char *new, int cur_line)
58659243Sobrien{
587145479Smp    Char *o, *n, *p, c;
588145479Smp    Char  *ofd, *ols, *oe, *nfd, *nls, *ne;
589145479Smp    Char  *osb, *ose, *nsb, *nse;
59059243Sobrien    int     fx, sx;
59159243Sobrien
59259243Sobrien    /*
593145479Smp     * find first diff (won't be CHAR_DBWIDTH in either line)
59459243Sobrien     */
59559243Sobrien    for (o = old, n = new; *o && (*o == *n); o++, n++)
59659243Sobrien	continue;
59759243Sobrien    ofd = o;
59859243Sobrien    nfd = n;
59959243Sobrien
60059243Sobrien    /*
60159243Sobrien     * Find the end of both old and new
60259243Sobrien     */
603167465Smp    o = Strend(o);
604167465Smp
60559243Sobrien    /*
60659243Sobrien     * Remove any trailing blanks off of the end, being careful not to
60759243Sobrien     * back up past the beginning.
60859243Sobrien     */
609167465Smp    if (!(adrof(STRhighlight) && MarkIsSet)) {
61059243Sobrien    while (ofd < o) {
61159243Sobrien	if (o[-1] != ' ')
61259243Sobrien	    break;
61359243Sobrien	o--;
61459243Sobrien    }
615167465Smp    }
61659243Sobrien    oe = o;
61759243Sobrien    *oe = (Char) 0;
61859243Sobrien
619167465Smp    n = Strend(n);
620167465Smp
62159243Sobrien    /* remove blanks from end of new */
622167465Smp    if (!(adrof(STRhighlight) && MarkIsSet)) {
62359243Sobrien    while (nfd < n) {
62459243Sobrien	if (n[-1] != ' ')
62559243Sobrien	    break;
62659243Sobrien	n--;
62759243Sobrien    }
628167465Smp    }
62959243Sobrien    ne = n;
63059243Sobrien    *ne = (Char) 0;
63159243Sobrien
63259243Sobrien    /*
63359243Sobrien     * if no diff, continue to next line of redraw
63459243Sobrien     */
63559243Sobrien    if (*ofd == '\0' && *nfd == '\0') {
63659243Sobrien#ifdef DEBUG_UPDATE
637231990Smp	reprintf("no difference.\r\n");
63859243Sobrien#endif /* DEBUG_UPDATE */
63959243Sobrien	return;
64059243Sobrien    }
64159243Sobrien
64259243Sobrien    /*
64359243Sobrien     * find last same pointer
64459243Sobrien     */
64559243Sobrien    while ((o > ofd) && (n > nfd) && (*--o == *--n))
64659243Sobrien	continue;
647145479Smp    if (*o != *n) {
648145479Smp	o++;
649145479Smp	n++;
650145479Smp    }
651145479Smp    while (*o == CHAR_DBWIDTH) {
652145479Smp	o++;
653145479Smp	n++;
654145479Smp    }
655145479Smp    ols = o;
656145479Smp    nls = n;
65759243Sobrien
65859243Sobrien    /*
65959243Sobrien     * find same begining and same end
66059243Sobrien     */
66159243Sobrien    osb = ols;
66259243Sobrien    nsb = nls;
66359243Sobrien    ose = ols;
66459243Sobrien    nse = nls;
66559243Sobrien
66659243Sobrien    /*
66759243Sobrien     * case 1: insert: scan from nfd to nls looking for *ofd
66859243Sobrien     */
66959243Sobrien    if (*ofd) {
67059243Sobrien	for (c = *ofd, n = nfd; n < nls; n++) {
67159243Sobrien	    if (c == *n) {
67259243Sobrien		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
67359243Sobrien		    continue;
67459243Sobrien		/*
67559243Sobrien		 * if the new match is longer and it's worth keeping, then we
67659243Sobrien		 * take it
67759243Sobrien		 */
67859243Sobrien		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
67959243Sobrien		    nsb = n;
68059243Sobrien		    nse = p;
68159243Sobrien		    osb = ofd;
68259243Sobrien		    ose = o;
68359243Sobrien		}
68459243Sobrien	    }
68559243Sobrien	}
68659243Sobrien    }
68759243Sobrien
68859243Sobrien    /*
68959243Sobrien     * case 2: delete: scan from ofd to ols looking for *nfd
69059243Sobrien     */
69159243Sobrien    if (*nfd) {
69259243Sobrien	for (c = *nfd, o = ofd; o < ols; o++) {
69359243Sobrien	    if (c == *o) {
69459243Sobrien		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
69559243Sobrien		    continue;
69659243Sobrien		/*
69759243Sobrien		 * if the new match is longer and it's worth keeping, then we
69859243Sobrien		 * take it
69959243Sobrien		 */
70059243Sobrien		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
70159243Sobrien		    nsb = nfd;
70259243Sobrien		    nse = n;
70359243Sobrien		    osb = o;
70459243Sobrien		    ose = p;
70559243Sobrien		}
70659243Sobrien	    }
70759243Sobrien	}
70859243Sobrien    }
70959243Sobrien#ifdef notdef
71059243Sobrien    /*
71159243Sobrien     * If `last same' is before `same end' re-adjust
71259243Sobrien     */
71359243Sobrien    if (ols < ose)
71459243Sobrien	ols = ose;
71559243Sobrien    if (nls < nse)
71659243Sobrien	nls = nse;
71759243Sobrien#endif
71859243Sobrien
71959243Sobrien    /*
72059243Sobrien     * Pragmatics I: If old trailing whitespace or not enough characters to
72159243Sobrien     * save to be worth it, then don't save the last same info.
72259243Sobrien     */
72359243Sobrien    if ((oe - ols) < MIN_END_KEEP) {
72459243Sobrien	ols = oe;
72559243Sobrien	nls = ne;
72659243Sobrien    }
72759243Sobrien
72859243Sobrien    /*
72959243Sobrien     * Pragmatics II: if the terminal isn't smart enough, make the data dumber
73059243Sobrien     * so the smart update doesn't try anything fancy
73159243Sobrien     */
73259243Sobrien
73359243Sobrien    /*
73459243Sobrien     * fx is the number of characters we need to insert/delete: in the
73559243Sobrien     * beginning to bring the two same begins together
73659243Sobrien     */
73759243Sobrien    fx = (int) ((nsb - nfd) - (osb - ofd));
73859243Sobrien    /*
73959243Sobrien     * sx is the number of characters we need to insert/delete: in the end to
74059243Sobrien     * bring the two same last parts together
74159243Sobrien     */
74259243Sobrien    sx = (int) ((nls - nse) - (ols - ose));
74359243Sobrien
74459243Sobrien    if (!T_CanIns) {
74559243Sobrien	if (fx > 0) {
74659243Sobrien	    osb = ols;
74759243Sobrien	    ose = ols;
74859243Sobrien	    nsb = nls;
74959243Sobrien	    nse = nls;
75059243Sobrien	}
75159243Sobrien	if (sx > 0) {
75259243Sobrien	    ols = oe;
75359243Sobrien	    nls = ne;
75459243Sobrien	}
75559243Sobrien	if ((ols - ofd) < (nls - nfd)) {
75659243Sobrien	    ols = oe;
75759243Sobrien	    nls = ne;
75859243Sobrien	}
75959243Sobrien    }
76059243Sobrien    if (!T_CanDel) {
76159243Sobrien	if (fx < 0) {
76259243Sobrien	    osb = ols;
76359243Sobrien	    ose = ols;
76459243Sobrien	    nsb = nls;
76559243Sobrien	    nse = nls;
76659243Sobrien	}
76759243Sobrien	if (sx < 0) {
76859243Sobrien	    ols = oe;
76959243Sobrien	    nls = ne;
77059243Sobrien	}
77159243Sobrien	if ((ols - ofd) > (nls - nfd)) {
77259243Sobrien	    ols = oe;
77359243Sobrien	    nls = ne;
77459243Sobrien	}
77559243Sobrien    }
77659243Sobrien
77759243Sobrien    /*
77859243Sobrien     * Pragmatics III: make sure the middle shifted pointers are correct if
77959243Sobrien     * they don't point to anything (we may have moved ols or nls).
78059243Sobrien     */
78159243Sobrien    /* if the change isn't worth it, don't bother */
78259243Sobrien    /* was: if (osb == ose) */
78359243Sobrien    if ((ose - osb) < MIN_END_KEEP) {
78459243Sobrien	osb = ols;
78559243Sobrien	ose = ols;
78659243Sobrien	nsb = nls;
78759243Sobrien	nse = nls;
78859243Sobrien    }
78959243Sobrien
79059243Sobrien    /*
79159243Sobrien     * Now that we are done with pragmatics we recompute fx, sx
79259243Sobrien     */
79359243Sobrien    fx = (int) ((nsb - nfd) - (osb - ofd));
79459243Sobrien    sx = (int) ((nls - nse) - (ols - ose));
79559243Sobrien
79659243Sobrien#ifdef DEBUG_UPDATE
797231990Smp    reprintf("\n");
798231990Smp    reprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
79959243Sobrien	    ofd - old, osb - old, ose - old, ols - old, oe - old);
800231990Smp    reprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
80159243Sobrien	    nfd - new, nsb - new, nse - new, nls - new, ne - new);
802231990Smp    reprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
803231990Smp    reprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
80459243Sobrien    dprintstr("old- oe", old, oe);
80559243Sobrien    dprintstr("new- ne", new, ne);
80659243Sobrien    dprintstr("old-ofd", old, ofd);
80759243Sobrien    dprintstr("new-nfd", new, nfd);
80859243Sobrien    dprintstr("ofd-osb", ofd, osb);
80959243Sobrien    dprintstr("nfd-nsb", nfd, nsb);
81059243Sobrien    dprintstr("osb-ose", osb, ose);
81159243Sobrien    dprintstr("nsb-nse", nsb, nse);
81259243Sobrien    dprintstr("ose-ols", ose, ols);
81359243Sobrien    dprintstr("nse-nls", nse, nls);
81459243Sobrien    dprintstr("ols- oe", ols, oe);
81559243Sobrien    dprintstr("nls- ne", nls, ne);
81659243Sobrien#endif /* DEBUG_UPDATE */
81759243Sobrien
81859243Sobrien    /*
81959243Sobrien     * CursorV to this line cur_line MUST be in this routine so that if we
82059243Sobrien     * don't have to change the line, we don't move to it. CursorH to first
82159243Sobrien     * diff char
82259243Sobrien     */
82359243Sobrien    MoveToLine(cur_line);
82459243Sobrien
82559243Sobrien    /*
82659243Sobrien     * at this point we have something like this:
82759243Sobrien     *
82859243Sobrien     * /old                  /ofd    /osb               /ose    /ols     /oe
82959243Sobrien     * v.....................v       v..................v       v........v
83059243Sobrien     * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
83159243Sobrien     * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
83259243Sobrien     * ^.....................^     ^..................^       ^........^
83359243Sobrien     * \new                  \nfd  \nsb               \nse     \nls    \ne
83459243Sobrien     *
83559243Sobrien     * fx is the difference in length between the the chars between nfd and
83659243Sobrien     * nsb, and the chars between ofd and osb, and is thus the number of
83759243Sobrien     * characters to delete if < 0 (new is shorter than old, as above),
83859243Sobrien     * or insert (new is longer than short).
83959243Sobrien     *
84059243Sobrien     * sx is the same for the second differences.
84159243Sobrien     */
84259243Sobrien
84359243Sobrien    /*
84459243Sobrien     * if we have a net insert on the first difference, AND inserting the net
84559243Sobrien     * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
84659243Sobrien     * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
84759243Sobrien     * (TermH - 1) else we do the deletes first so that we keep everything we
84859243Sobrien     * need to.
84959243Sobrien     */
85059243Sobrien
85159243Sobrien    /*
85259243Sobrien     * if the last same is the same like the end, there is no last same part,
85359243Sobrien     * otherwise we want to keep the last same part set p to the last useful
85459243Sobrien     * old character
85559243Sobrien     */
85659243Sobrien    p = (ols != oe) ? oe : ose;
85759243Sobrien
85859243Sobrien    /*
85959243Sobrien     * if (There is a diffence in the beginning) && (we need to insert
86059243Sobrien     * characters) && (the number of characters to insert is less than the term
86159243Sobrien     * width) We need to do an insert! else if (we need to delete characters)
86259243Sobrien     * We need to delete characters! else No insert or delete
86359243Sobrien     */
86459243Sobrien    if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
86559243Sobrien#ifdef DEBUG_UPDATE
866231990Smp	reprintf("first diff insert at %d...\r\n", nfd - new);
86759243Sobrien#endif  /* DEBUG_UPDATE */
86859243Sobrien	/*
86959243Sobrien	 * Move to the first char to insert, where the first diff is.
87059243Sobrien	 */
87159243Sobrien	MoveToChar(nfd - new);
87259243Sobrien	/*
87359243Sobrien	 * Check if we have stuff to keep at end
87459243Sobrien	 */
87559243Sobrien	if (nsb != ne) {
87659243Sobrien#ifdef DEBUG_UPDATE
877231990Smp	    reprintf("with stuff to keep at end\r\n");
87859243Sobrien#endif  /* DEBUG_UPDATE */
87959243Sobrien	    /*
88059243Sobrien	     * insert fx chars of new starting at nfd
88159243Sobrien	     */
88259243Sobrien	    if (fx > 0) {
88359243Sobrien#ifdef DEBUG_UPDATE
88459243Sobrien		if (!T_CanIns)
885231990Smp		    reprintf("   ERROR: cannot insert in early first diff\n");
88659243Sobrien#endif  /* DEBUG_UPDATE */
88759243Sobrien		Insert_write(nfd, fx);
88859243Sobrien		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
88959243Sobrien	    }
89059243Sobrien	    /*
89159243Sobrien	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
89259243Sobrien	     */
89359243Sobrien	    so_write(nfd + fx, (nsb - nfd) - fx);
89459243Sobrien	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
89559243Sobrien	}
89659243Sobrien	else {
89759243Sobrien#ifdef DEBUG_UPDATE
898231990Smp	    reprintf("without anything to save\r\n");
89959243Sobrien#endif  /* DEBUG_UPDATE */
90059243Sobrien	    so_write(nfd, (nsb - nfd));
90159243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
90259243Sobrien	    /*
90359243Sobrien	     * Done
90459243Sobrien	     */
90559243Sobrien	    return;
90659243Sobrien	}
90759243Sobrien    }
90859243Sobrien    else if (fx < 0) {
90959243Sobrien#ifdef DEBUG_UPDATE
910231990Smp	reprintf("first diff delete at %d...\r\n", ofd - old);
91159243Sobrien#endif  /* DEBUG_UPDATE */
91259243Sobrien	/*
91359243Sobrien	 * move to the first char to delete where the first diff is
91459243Sobrien	 */
91559243Sobrien	MoveToChar(ofd - old);
91659243Sobrien	/*
91759243Sobrien	 * Check if we have stuff to save
91859243Sobrien	 */
91959243Sobrien	if (osb != oe) {
92059243Sobrien#ifdef DEBUG_UPDATE
921231990Smp	    reprintf("with stuff to save at end\r\n");
92259243Sobrien#endif  /* DEBUG_UPDATE */
92359243Sobrien	    /*
92459243Sobrien	     * fx is less than zero *always* here but we check for code
92559243Sobrien	     * symmetry
92659243Sobrien	     */
92759243Sobrien	    if (fx < 0) {
92859243Sobrien#ifdef DEBUG_UPDATE
92959243Sobrien		if (!T_CanDel)
930231990Smp		    reprintf("   ERROR: cannot delete in first diff\n");
93159243Sobrien#endif /* DEBUG_UPDATE */
93259243Sobrien		DeleteChars(-fx);
93359243Sobrien		str_delete(old, (int) (ofd - old), TermH, -fx);
93459243Sobrien	    }
93559243Sobrien	    /*
93659243Sobrien	     * write (nsb-nfd) chars of new starting at nfd
93759243Sobrien	     */
93859243Sobrien	    so_write(nfd, (nsb - nfd));
93959243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
94059243Sobrien
94159243Sobrien	}
94259243Sobrien	else {
94359243Sobrien#ifdef DEBUG_UPDATE
944231990Smp	    reprintf("but with nothing left to save\r\n");
94559243Sobrien#endif  /* DEBUG_UPDATE */
94659243Sobrien	    /*
94759243Sobrien	     * write (nsb-nfd) chars of new starting at nfd
94859243Sobrien	     */
94959243Sobrien	    so_write(nfd, (nsb - nfd));
95059243Sobrien#ifdef DEBUG_REFRESH
951231990Smp	    reprintf("cleareol %d\n", (oe - old) - (ne - new));
95259243Sobrien#endif  /* DEBUG_UPDATE */
95369408Sache#ifndef WINNT_NATIVE
95459243Sobrien	    ClearEOL((oe - old) - (ne - new));
95559243Sobrien#else
95659243Sobrien	    /*
95759243Sobrien	     * The calculation above does not work too well on NT
95859243Sobrien	     */
95959243Sobrien	    ClearEOL(TermH - CursorH);
96069408Sache#endif /*WINNT_NATIVE*/
96159243Sobrien	    /*
96259243Sobrien	     * Done
96359243Sobrien	     */
96459243Sobrien	    return;
96559243Sobrien	}
96659243Sobrien    }
96759243Sobrien    else
96859243Sobrien	fx = 0;
96959243Sobrien
97059243Sobrien    if (sx < 0) {
97159243Sobrien#ifdef DEBUG_UPDATE
972231990Smp	reprintf("second diff delete at %d...\r\n", (ose - old) + fx);
97359243Sobrien#endif  /* DEBUG_UPDATE */
97459243Sobrien	/*
97559243Sobrien	 * Check if we have stuff to delete
97659243Sobrien	 */
97759243Sobrien	/*
97859243Sobrien	 * fx is the number of characters inserted (+) or deleted (-)
97959243Sobrien	 */
98059243Sobrien
98159243Sobrien	MoveToChar((ose - old) + fx);
98259243Sobrien	/*
98359243Sobrien	 * Check if we have stuff to save
98459243Sobrien	 */
98559243Sobrien	if (ols != oe) {
98659243Sobrien#ifdef DEBUG_UPDATE
987231990Smp	    reprintf("with stuff to save at end\r\n");
98859243Sobrien#endif  /* DEBUG_UPDATE */
98959243Sobrien	    /*
99059243Sobrien	     * Again a duplicate test.
99159243Sobrien	     */
99259243Sobrien	    if (sx < 0) {
99359243Sobrien#ifdef DEBUG_UPDATE
99459243Sobrien		if (!T_CanDel)
995231990Smp		    reprintf("   ERROR: cannot delete in second diff\n");
99659243Sobrien#endif  /* DEBUG_UPDATE */
99759243Sobrien		DeleteChars(-sx);
99859243Sobrien	    }
99959243Sobrien
100059243Sobrien	    /*
100159243Sobrien	     * write (nls-nse) chars of new starting at nse
100259243Sobrien	     */
100359243Sobrien	    so_write(nse, (nls - nse));
100459243Sobrien	}
100559243Sobrien	else {
100659243Sobrien	    int olen = (int) (oe - old + fx);
100759243Sobrien	    if (olen > TermH)
100859243Sobrien		olen = TermH;
100959243Sobrien#ifdef DEBUG_UPDATE
1010231990Smp	    reprintf("but with nothing left to save\r\n");
101159243Sobrien#endif /* DEBUG_UPDATE */
101259243Sobrien	    so_write(nse, (nls - nse));
101359243Sobrien#ifdef DEBUG_REFRESH
1014231990Smp	    reprintf("cleareol %d\n", olen - (ne - new));
101559243Sobrien#endif /* DEBUG_UPDATE */
101669408Sache#ifndef WINNT_NATIVE
101759243Sobrien	    ClearEOL(olen - (ne - new));
101859243Sobrien#else
101959243Sobrien	    /*
102059243Sobrien	     * The calculation above does not work too well on NT
102159243Sobrien	     */
102259243Sobrien	    ClearEOL(TermH - CursorH);
102369408Sache#endif /*WINNT_NATIVE*/
102459243Sobrien	}
102559243Sobrien    }
102659243Sobrien
102759243Sobrien    /*
102859243Sobrien     * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
102959243Sobrien     */
103059243Sobrien    if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
103159243Sobrien#ifdef DEBUG_UPDATE
1032231990Smp	reprintf("late first diff insert at %d...\r\n", nfd - new);
103359243Sobrien#endif /* DEBUG_UPDATE */
103459243Sobrien
103559243Sobrien	MoveToChar(nfd - new);
103659243Sobrien	/*
103759243Sobrien	 * Check if we have stuff to keep at the end
103859243Sobrien	 */
103959243Sobrien	if (nsb != ne) {
104059243Sobrien#ifdef DEBUG_UPDATE
1041231990Smp	    reprintf("with stuff to keep at end\r\n");
104259243Sobrien#endif /* DEBUG_UPDATE */
104359243Sobrien	    /*
104459243Sobrien	     * We have to recalculate fx here because we set it
104559243Sobrien	     * to zero above as a flag saying that we hadn't done
104659243Sobrien	     * an early first insert.
104759243Sobrien	     */
104859243Sobrien	    fx = (int) ((nsb - nfd) - (osb - ofd));
104959243Sobrien	    if (fx > 0) {
105059243Sobrien		/*
105159243Sobrien		 * insert fx chars of new starting at nfd
105259243Sobrien		 */
105359243Sobrien#ifdef DEBUG_UPDATE
105459243Sobrien		if (!T_CanIns)
1055231990Smp		    reprintf("   ERROR: cannot insert in late first diff\n");
105659243Sobrien#endif /* DEBUG_UPDATE */
105759243Sobrien		Insert_write(nfd, fx);
105859243Sobrien		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
105959243Sobrien	    }
106059243Sobrien
106159243Sobrien	    /*
106259243Sobrien	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
106359243Sobrien	     */
106459243Sobrien	    so_write(nfd + fx, (nsb - nfd) - fx);
106559243Sobrien	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
106659243Sobrien	}
106759243Sobrien	else {
106859243Sobrien#ifdef DEBUG_UPDATE
1069231990Smp	    reprintf("without anything to save\r\n");
107059243Sobrien#endif /* DEBUG_UPDATE */
107159243Sobrien	    so_write(nfd, (nsb - nfd));
107259243Sobrien	    str_cp(ofd, nfd, (int) (nsb - nfd));
107359243Sobrien	}
107459243Sobrien    }
107559243Sobrien
107659243Sobrien    /*
107759243Sobrien     * line is now NEW up to nse
107859243Sobrien     */
107959243Sobrien    if (sx >= 0) {
108059243Sobrien#ifdef DEBUG_UPDATE
1081231990Smp	reprintf("second diff insert at %d...\r\n", nse - new);
108259243Sobrien#endif /* DEBUG_UPDATE */
108359243Sobrien	MoveToChar(nse - new);
108459243Sobrien	if (ols != oe) {
108559243Sobrien#ifdef DEBUG_UPDATE
1086231990Smp	    reprintf("with stuff to keep at end\r\n");
108759243Sobrien#endif /* DEBUG_UPDATE */
108859243Sobrien	    if (sx > 0) {
108959243Sobrien		/* insert sx chars of new starting at nse */
109059243Sobrien#ifdef DEBUG_UPDATE
109159243Sobrien		if (!T_CanIns)
1092231990Smp		    reprintf("   ERROR: cannot insert in second diff\n");
109359243Sobrien#endif /* DEBUG_UPDATE */
109459243Sobrien		Insert_write(nse, sx);
109559243Sobrien	    }
109659243Sobrien
109759243Sobrien	    /*
109859243Sobrien	     * write (nls-nse) - sx chars of new starting at (nse + sx)
109959243Sobrien	     */
110059243Sobrien	    so_write(nse + sx, (nls - nse) - sx);
110159243Sobrien	}
110259243Sobrien	else {
110359243Sobrien#ifdef DEBUG_UPDATE
1104231990Smp	    reprintf("without anything to save\r\n");
110559243Sobrien#endif /* DEBUG_UPDATE */
110659243Sobrien	    so_write(nse, (nls - nse));
110759243Sobrien
110859243Sobrien	    /*
110959243Sobrien             * No need to do a clear-to-end here because we were doing
111059243Sobrien	     * a second insert, so we will have over written all of the
111159243Sobrien	     * old string.
111259243Sobrien	     */
111359243Sobrien	}
111459243Sobrien    }
111559243Sobrien#ifdef DEBUG_UPDATE
1116231990Smp    reprintf("done.\r\n");
111759243Sobrien#endif /* DEBUG_UPDATE */
111859243Sobrien}
111959243Sobrien
112059243Sobrien
112159243Sobrienstatic void
1122167465Smpcpy_pad_spaces(Char *dst, Char *src, int width)
112359243Sobrien{
1124145479Smp    int i;
112559243Sobrien
112659243Sobrien    for (i = 0; i < width; i++) {
112759243Sobrien	if (*src == (Char) 0)
112859243Sobrien	    break;
112959243Sobrien	*dst++ = *src++;
113059243Sobrien    }
113159243Sobrien
113259243Sobrien    while (i < width) {
113359243Sobrien	*dst++ = ' ';
113459243Sobrien	i++;
113559243Sobrien    }
113659243Sobrien    *dst = (Char) 0;
113759243Sobrien}
113859243Sobrien
113959243Sobrienvoid
1140167465SmpRefCursor(void)
114159243Sobrien{				/* only move to new cursor pos */
1142145479Smp    Char *cp;
1143167465Smp    int w, h, th, v;
114459243Sobrien
114559243Sobrien    /* first we must find where the cursor is... */
114659243Sobrien    h = 0;
114759243Sobrien    v = 0;
114859243Sobrien    th = TermH;			/* optimize for speed */
114959243Sobrien
1150167465Smp    for (cp = Prompt; cp != NULL && *cp; ) {	/* do prompt */
1151145479Smp	if (*cp & LITERAL) {
1152145479Smp	    cp++;
115359243Sobrien	    continue;
115459243Sobrien	}
1155316957Sdchagin	w = NLSClassify(*cp & CHAR, cp == Prompt, 0);
1156167465Smp	cp++;
1157145479Smp	switch(w) {
1158145479Smp	    case NLSCLASS_NL:
1159145479Smp		h = 0;
1160145479Smp		v++;
1161145479Smp		break;
1162145479Smp	    case NLSCLASS_TAB:
1163145479Smp		while (++h & 07)
1164145479Smp		    ;
1165145479Smp		break;
1166145479Smp	    case NLSCLASS_CTRL:
1167145479Smp		h += 2;
1168145479Smp		break;
1169145479Smp	    case NLSCLASS_ILLEGAL:
1170145479Smp		h += 4;
1171145479Smp		break;
1172145479Smp	    case NLSCLASS_ILLEGAL2:
1173145479Smp	    case NLSCLASS_ILLEGAL3:
1174145479Smp	    case NLSCLASS_ILLEGAL4:
1175145479Smp		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1176145479Smp		break;
1177145479Smp	    default:
1178145479Smp		h += w;
117959243Sobrien	}
118059243Sobrien	if (h >= th) {		/* check, extra long tabs picked up here also */
1181145479Smp	    h -= th;
118259243Sobrien	    v++;
118359243Sobrien	}
118459243Sobrien    }
118559243Sobrien
1186145479Smp    for (cp = InputBuf; cp < Cursor;) {	/* do input buffer to Cursor */
1187316957Sdchagin	w = NLSClassify(*cp & CHAR, cp == InputBuf, 0);
1188167465Smp	cp++;
1189145479Smp	switch(w) {
1190145479Smp	    case NLSCLASS_NL:
1191145479Smp		h = 0;
1192145479Smp		v++;
1193145479Smp		break;
1194145479Smp	    case NLSCLASS_TAB:
1195145479Smp		while (++h & 07)
1196145479Smp		    ;
1197145479Smp		break;
1198145479Smp	    case NLSCLASS_CTRL:
1199145479Smp		h += 2;
1200145479Smp		break;
1201145479Smp	    case NLSCLASS_ILLEGAL:
1202145479Smp		h += 4;
1203145479Smp		break;
1204145479Smp	    case NLSCLASS_ILLEGAL2:
1205145479Smp	    case NLSCLASS_ILLEGAL3:
1206145479Smp	    case NLSCLASS_ILLEGAL4:
1207145479Smp		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1208145479Smp		break;
1209145479Smp	    default:
1210145479Smp		h += w;
121159243Sobrien	}
121259243Sobrien	if (h >= th) {		/* check, extra long tabs picked up here also */
1213145479Smp	    h -= th;
121459243Sobrien	    v++;
121559243Sobrien	}
121659243Sobrien    }
121759243Sobrien
121859243Sobrien    /* now go there */
121959243Sobrien    MoveToLine(v);
122059243Sobrien    MoveToChar(h);
1221167465Smp    if (adrof(STRhighlight) && MarkIsSet) {
1222167465Smp	ClearLines();
1223167465Smp	ClearDisp();
1224167465Smp	Refresh();
1225167465Smp    }
122659243Sobrien    flush();
122759243Sobrien}
122859243Sobrien
1229131962Smp#ifndef WINTT_NATIVE
123059243Sobrienstatic void
1231167465SmpPutPlusOne(Char c, int width)
123259243Sobrien{
1233145479Smp    while (width > 1 && CursorH + width > TermH)
1234145479Smp	PutPlusOne(' ', 1);
1235145479Smp    if ((c & LITERAL) != 0) {
1236145479Smp	Char *d;
1237145479Smp	for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1238145479Smp	    (void) putwraw(*d);
1239145479Smp    } else {
1240145479Smp	(void) putwraw(c);
1241145479Smp    }
124259243Sobrien    Display[CursorV][CursorH++] = (Char) c;
1243145479Smp    while (--width > 0)
1244145479Smp	Display[CursorV][CursorH++] = CHAR_DBWIDTH;
124559243Sobrien    if (CursorH >= TermH) {	/* if we must overflow */
124659243Sobrien	CursorH = 0;
124759243Sobrien	CursorV++;
124859243Sobrien	OldvcV++;
124959243Sobrien	if (T_Margin & MARGIN_AUTO) {
125059243Sobrien	    if (T_Margin & MARGIN_MAGIC) {
125159243Sobrien		(void) putraw(' ');
125259243Sobrien		(void) putraw('\b');
125359243Sobrien	    }
125459243Sobrien	}
125559243Sobrien	else {
125659243Sobrien	    (void) putraw('\r');
125759243Sobrien	    (void) putraw('\n');
125859243Sobrien	}
125959243Sobrien    }
126059243Sobrien}
1261131962Smp#endif
126259243Sobrien
126359243Sobrienvoid
1264145479SmpRefPlusOne(int l)
126559243Sobrien{				/* we added just one char, handle it fast.
126659243Sobrien				 * assumes that screen cursor == real cursor */
1267167465Smp    Char *cp, c;
1268145479Smp    int w;
126959243Sobrien
1270145479Smp    if (Cursor != LastChar) {
127159243Sobrien	Refresh();		/* too hard to handle */
127259243Sobrien	return;
127359243Sobrien    }
127459243Sobrien    if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
127559243Sobrien	Refresh();		/* clear out rprompt if less than one char gap*/
127659243Sobrien	return;
1277145479Smp    }
1278145479Smp    cp = Cursor - l;
1279167465Smp    c = *cp & CHAR;
1280316957Sdchagin    w = NLSClassify(c, cp == InputBuf, 0);
1281145479Smp    switch(w) {
1282145479Smp	case NLSCLASS_CTRL:
1283145479Smp	    PutPlusOne('^', 1);
1284145479Smp	    if (c == CTL_ESC('\177')) {
1285145479Smp		PutPlusOne('?', 1);
1286145479Smp		break;
1287145479Smp	    }
128869408Sache#ifdef IS_ASCII
1289145479Smp	    /* uncontrolify it; works only for iso8859-1 like sets */
1290145479Smp	    PutPlusOne((c | 0100), 1);
129169408Sache#else
1292145479Smp	    PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
129369408Sache#endif
1294145479Smp	    break;
1295145479Smp	case NLSCLASS_ILLEGAL:
1296145479Smp	    PutPlusOne('\\', 1);
1297145479Smp	    PutPlusOne(((c >> 6) & 7) + '0', 1);
1298145479Smp	    PutPlusOne(((c >> 3) & 7) + '0', 1);
1299145479Smp	    PutPlusOne((c & 7) + '0', 1);
1300145479Smp	    break;
1301145479Smp	case 1:
1302167465Smp	    if (adrof(STRhighlight) && MarkIsSet)
1303167465Smp		StartHighlight();
1304145479Smp	    if (l > 1)
1305145479Smp		PutPlusOne(MakeLiteral(cp, l, 0), 1);
1306145479Smp	    else
1307145479Smp		PutPlusOne(*cp, 1);
1308167465Smp	    if (adrof(STRhighlight) && MarkIsSet)
1309167465Smp		StopHighlight();
1310145479Smp	    break;
1311145479Smp	default:
1312145479Smp	    Refresh();		/* too hard to handle */
1313145479Smp	    return;
131459243Sobrien    }
131559243Sobrien    flush();
131659243Sobrien}
131759243Sobrien
131859243Sobrien/* clear the screen buffers so that new new prompt starts fresh. */
131959243Sobrien
132059243Sobrienvoid
1321167465SmpClearDisp(void)
132259243Sobrien{
1323145479Smp    int i;
132459243Sobrien
132559243Sobrien    CursorV = 0;		/* clear the display buffer */
132659243Sobrien    CursorH = 0;
132759243Sobrien    for (i = 0; i < TermV; i++)
1328316957Sdchagin	(void) memset(Display[i], 0, (TermH + 1) * sizeof(Display[0][0]));
132959243Sobrien    OldvcV = 0;
1330145479Smp    litlen = 0;
133159243Sobrien}
133259243Sobrien
133359243Sobrienvoid
1334167465SmpClearLines(void)
133559243Sobrien{				/* Make sure all lines are *really* blank */
1336145479Smp    int i;
133759243Sobrien
133859243Sobrien    if (T_CanCEOL) {
133959243Sobrien	/*
134059243Sobrien	 * Clear the lines from the bottom up so that if we try moving
134159243Sobrien	 * the cursor down by writing the character that is at the end
134259243Sobrien	 * of the screen line, we won't rewrite a character that shouldn't
134359243Sobrien	 * be there.
134459243Sobrien	 */
134559243Sobrien	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
134659243Sobrien	    MoveToLine(i);
134759243Sobrien	    MoveToChar(0);
134859243Sobrien	    ClearEOL(TermH);
134959243Sobrien	}
135059243Sobrien    }
135159243Sobrien    else {
135259243Sobrien	MoveToLine(OldvcV);	/* go to last line */
135359243Sobrien	(void) putraw('\r');	/* go to BOL */
135459243Sobrien	(void) putraw('\n');	/* go to new line */
135559243Sobrien    }
135659243Sobrien}
1357