1/*
2 * ed.refresh.c: Lower level screen refreshing functions
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include "sh.h"
33#include "ed.h"
34/* #define DEBUG_UPDATE */
35/* #define DEBUG_REFRESH */
36/* #define DEBUG_LITERAL */
37
38/* refresh.c -- refresh the current set of lines on the screen */
39
40Char   *litptr;
41static int vcursor_h, vcursor_v;
42static int rprompt_h, rprompt_v;
43
44static	int	MakeLiteral		(Char *, int, Char);
45static	int	Draw 			(Char *, int, int);
46static	void	Vdraw 			(Char, int);
47static	void	RefreshPromptpart	(Char *);
48static	void	update_line 		(Char *, Char *, int);
49static	void	str_insert		(Char *, int, int, Char *, int);
50static	void	str_delete		(Char *, int, int, int);
51static	void	str_cp			(Char *, Char *, int);
52#ifndef WINNT_NATIVE
53static
54#else
55extern
56#endif
57	void    PutPlusOne      (Char, int);
58static	void	cpy_pad_spaces		(Char *, Char *, int);
59#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
60static	void	reprintf			(char *, ...);
61#ifdef DEBUG_UPDATE
62static	void	dprintstr		(char *, const Char *, const Char *);
63
64static void
65dprintstr(char *str, const Char *f, const Char *t)
66{
67    reprintf("%s:\"", str);
68    while (f < t) {
69	if (ASC(*f) & ~ASCII)
70	  reprintf("[%x]", *f++);
71	else
72	  reprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
73    }
74    reprintf("\"\r\n");
75}
76#endif /* DEBUG_UPDATE */
77
78/* reprintf():
79 *	Print to $DEBUGTTY, so that we can test editing on one pty, and
80 *      print debugging stuff on another. Don't interrupt the shell while
81 *	debugging cause you'll mangle up the file descriptors!
82 */
83static void
84reprintf(char *fmt, ...)
85{
86    static int fd = -1;
87    char *dtty;
88
89    if ((dtty = getenv("DEBUGTTY"))) {
90	int o;
91	va_list va;
92	va_start(va, fmt);
93
94	if (fd == -1)
95	    fd = xopen(dtty, O_RDWR);
96	o = SHOUT;
97	flush();
98	SHOUT = fd;
99	xvprintf(fmt, va);
100	va_end(va);
101	flush();
102	SHOUT = o;
103    }
104}
105#endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
106
107static int litlen = 0, litalloc = 0;
108
109static int MakeLiteral(Char *str, int len, Char addlit)
110{
111    int i, addlitlen = 0;
112    Char *addlitptr = 0;
113    if (addlit) {
114	if ((addlit & LITERAL) != 0) {
115	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
116	    addlitlen = Strlen(addlitptr);
117	} else {
118	    addlitptr = &addlit;
119	    addlitlen = 1;
120	}
121	for (i = 0; i < litlen; i += LIT_FACTOR)
122	    if (!Strncmp(addlitptr, litptr + i, addlitlen) && !Strncmp(str, litptr + i + addlitlen, len) && litptr[i + addlitlen + len] == 0)
123		return (i / LIT_FACTOR) | LITERAL;
124    } else {
125	addlitlen = 0;
126	for (i = 0; i < litlen; i += LIT_FACTOR)
127	    if (!Strncmp(str, litptr + i, len) && litptr[i + len] == 0)
128		return (i / LIT_FACTOR) | LITERAL;
129    }
130    if (litlen + addlitlen + len + 1 + (LIT_FACTOR - 1) > litalloc) {
131	Char *newlitptr;
132	int add = 256;
133	while (len + addlitlen + 1 + (LIT_FACTOR - 1) > add)
134	    add *= 2;
135	newlitptr = xrealloc(litptr, (litalloc + add) * sizeof(Char));
136	if (!newlitptr)
137	    return '?';
138	litptr = newlitptr;
139	litalloc += add;
140	if (addlitptr && addlitptr != &addlit)
141	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
142    }
143    i = litlen / LIT_FACTOR;
144    if (i >= LITERAL || i == CHAR_DBWIDTH)
145	return '?';
146    if (addlitptr) {
147	Strncpy(litptr + litlen, addlitptr, addlitlen);
148	litlen += addlitlen;
149    }
150    Strncpy(litptr + litlen, str, len);
151    litlen += len;
152    do
153	litptr[litlen++] = 0;
154    while (litlen % LIT_FACTOR);
155    return i | LITERAL;
156}
157
158/* draw char at cp, expand tabs, ctl chars */
159static int
160Draw(Char *cp, int nocomb, int drawPrompt)
161{
162    int w, i, lv, lh;
163    Char c, attr;
164
165#ifdef WIDE_STRINGS
166    if (!drawPrompt) {			/* draw command-line */
167	attr = 0;
168	c = *cp;
169    } else {				/* draw prompt */
170	/* prompt with attributes(UNDER,BOLD,STANDOUT) */
171	if (*cp & (UNDER | BOLD | STANDOUT)) {		/* *cp >= STANDOUT */
172
173	    /* example)
174	     * We can't distinguish whether (*cp=)0x02ffffff is
175	     * U+02FFFFFF or U+00FFFFFF|STANDOUT.
176	     * We handle as U+00FFFFFF|STANDOUT, only when drawing prompt. */
177	    attr = (*cp & ATTRIBUTES);
178	    /* ~(UNDER | BOLD | STANDOUT) = 0xf1ffffff */
179	    c = *cp & ~(UNDER | BOLD | STANDOUT);
180
181	    /* if c is ctrl code, we handle *cp as havnig no attributes */
182	    if ((c < 0x20 && c >= 0) || c == 0x7f) {
183		attr = 0;
184		c = *cp;
185	    }
186	} else {			/* prompt without attributes */
187	    attr = 0;
188	    c = *cp;
189	}
190    }
191#else
192    attr = *cp & ~CHAR;
193    c = *cp & CHAR;
194#endif
195    w = NLSClassify(c, nocomb, drawPrompt);
196    switch (w) {
197	case NLSCLASS_NL:
198	    Vdraw('\0', 0);		/* assure end of line	 */
199	    vcursor_h = 0;		/* reset cursor pos	 */
200	    vcursor_v++;
201	    break;
202	case NLSCLASS_TAB:
203	    do {
204		Vdraw(' ', 1);
205	    } while ((vcursor_h & 07) != 0);
206	    break;
207	case NLSCLASS_CTRL:
208	    Vdraw('^' | attr, 1);
209	    if (c == CTL_ESC('\177')) {
210		Vdraw('?' | attr, 1);
211	    } else {
212#ifdef IS_ASCII
213		/* uncontrolify it; works only for iso8859-1 like sets */
214		Vdraw(c | 0100 | attr, 1);
215#else
216		Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
217#endif
218	    }
219	    break;
220	case NLSCLASS_ILLEGAL:
221	    Vdraw('\\' | attr, 1);
222	    Vdraw((((c >> 6) & 7) + '0') | attr, 1);
223	    Vdraw((((c >> 3) & 7) + '0') | attr, 1);
224	    Vdraw(((c & 7) + '0') | attr, 1);
225	    break;
226	case NLSCLASS_ILLEGAL2:
227	case NLSCLASS_ILLEGAL3:
228	case NLSCLASS_ILLEGAL4:
229	case NLSCLASS_ILLEGAL5:
230	    Vdraw('\\', 1);
231	    Vdraw('U', 1);
232	    Vdraw('+', 1);
233	    for (i = 16 + 4 * (-w-5); i >= 0; i -= 4)
234		Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
235	    break;
236	case 0:
237	    lv = vcursor_v;
238	    lh = vcursor_h;
239	    for (;;) {
240		lh--;
241		if (lh < 0) {
242		    lv--;
243		    if (lv < 0)
244			break;
245		    lh = Strlen(Vdisplay[lv]) - 1;
246		}
247		if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
248		    break;
249	    }
250	    if (lv < 0) {
251		Vdraw('\\' | attr, 1);
252		Vdraw((((c >> 6) & 7) + '0') | attr, 1);
253		Vdraw((((c >> 3) & 7) + '0') | attr, 1);
254		Vdraw(((c & 7) + '0') | attr, 1);
255		break;
256	    }
257	    Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
258	    break;
259	default:
260	    Vdraw(*cp, w);
261	    break;
262    }
263    return 1;
264}
265
266static void
267Vdraw(Char c, int width)	/* draw char c onto V lines */
268{
269#ifdef DEBUG_REFRESH
270# ifdef SHORT_STRINGS
271    reprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
272# else
273    reprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
274# endif /* SHORT_STRNGS */
275#endif  /* DEBUG_REFRESH */
276
277    /* Hopefully this is what all the terminals do with multi-column characters
278       that "span line breaks". */
279    while (vcursor_h + width > TermH)
280	Vdraw(' ', 1);
281    Vdisplay[vcursor_v][vcursor_h] = c;
282    if (width)
283	vcursor_h++;		/* advance to next place */
284    while (--width > 0)
285	Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
286    if (vcursor_h >= TermH) {
287	Vdisplay[vcursor_v][TermH] = '\0';	/* assure end of line */
288	vcursor_h = 0;		/* reset it. */
289	vcursor_v++;
290#ifdef DEBUG_REFRESH
291	if (vcursor_v >= TermV) {	/* should NEVER happen. */
292	    reprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
293		    vcursor_v, TermV);
294	    abort();
295	}
296#endif /* DEBUG_REFRESH */
297    }
298}
299
300/*
301 *  RefreshPromptpart()
302 *	draws a prompt element, expanding literals (we know it's ASCIZ)
303 */
304static void
305RefreshPromptpart(Char *buf)
306{
307    Char *cp;
308    int w;
309
310    if (buf == NULL)
311	return;
312    for (cp = buf; *cp; ) {
313	if (*cp & LITERAL) {
314	    Char *litstart = cp;
315	    while (*cp & LITERAL)
316		cp++;
317	    if (*cp) {
318		w = NLSWidth(*cp & CHAR);
319		Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
320		cp++;
321	    }
322	    else {
323		/*
324		 * XXX: This is a bug, we lose the last literal, if it is not
325		 * followed by a normal character, but it is too hard to fix
326		 */
327		break;
328	    }
329	}
330	else
331	    cp += Draw(cp, cp == buf, 1);
332    }
333}
334
335/*
336 *  Refresh()
337 *	draws the new virtual screen image from the current input
338 *  	line, then goes line-by-line changing the real image to the new
339 *	virtual image. The routine to re-draw a line can be replaced
340 *	easily in hopes of a smarter one being placed there.
341 */
342#ifndef WINNT_NATIVE
343static
344#endif
345int OldvcV = 0;
346
347void
348Refresh(void)
349{
350    int cur_line;
351    Char *cp;
352    int     cur_h, cur_v = 0, new_vcv;
353    int     rhdiff;
354    Char    oldgetting;
355
356#ifdef DEBUG_REFRESH
357    reprintf("Prompt = :%s:\r\n", short2str(Prompt));
358    reprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
359#endif /* DEBUG_REFRESH */
360    oldgetting = GettingInput;
361    GettingInput = 0;		/* avoid re-entrance via SIGWINCH */
362
363    /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
364    vcursor_h = 0;
365    vcursor_v = 0;
366    RefreshPromptpart(RPrompt);
367    rprompt_h = vcursor_h;
368    rprompt_v = vcursor_v;
369
370    /* reset the Vdraw cursor, draw prompt */
371    vcursor_h = 0;
372    vcursor_v = 0;
373    RefreshPromptpart(Prompt);
374    cur_h = -1;			/* set flag in case I'm not set */
375
376    /* draw the current input buffer */
377    for (cp = InputBuf; (cp < LastChar); ) {
378	if (cp >= Cursor && cur_h == -1) {
379	    cur_h = vcursor_h;	/* save for later */
380	    cur_v = vcursor_v;
381	    Cursor = cp;
382	}
383	cp += Draw(cp, cp == InputBuf, 0);
384    }
385
386    if (cur_h == -1) {		/* if I haven't been set yet, I'm at the end */
387	cur_h = vcursor_h;
388	cur_v = vcursor_v;
389    }
390
391    rhdiff = TermH - vcursor_h - rprompt_h;
392    if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
393			/*
394			 * have a right-hand side prompt that will fit on
395			 * the end of the first line with at least one
396			 * character gap to the input buffer.
397			 */
398	while (--rhdiff > 0)		/* pad out with spaces */
399	    Vdraw(' ', 1);
400	RefreshPromptpart(RPrompt);
401    }
402    else {
403	rprompt_h = 0;			/* flag "not using rprompt" */
404	rprompt_v = 0;
405    }
406
407    new_vcv = vcursor_v;	/* must be done BEFORE the NUL is written */
408    Vdraw('\0', 1);		/* put NUL on end */
409
410#if defined (DEBUG_REFRESH)
411    reprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
412	    TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
413#endif /* DEBUG_REFRESH */
414
415#ifdef DEBUG_UPDATE
416    reprintf("updating %d lines.\r\n", new_vcv);
417#endif  /* DEBUG_UPDATE */
418    for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
419	/* NOTE THAT update_line MAY CHANGE Display[cur_line] */
420	update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
421#ifdef WINNT_NATIVE
422	flush();
423#endif /* WINNT_NATIVE */
424
425	/*
426	 * Copy the new line to be the current one, and pad out with spaces
427	 * to the full width of the terminal so that if we try moving the
428	 * cursor by writing the character that is at the end of the
429	 * screen line, it won't be a NUL or some old leftover stuff.
430	 */
431	cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
432    }
433#ifdef DEBUG_REFRESH
434    reprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
435	    vcursor_v, OldvcV, cur_line);
436#endif /* DEBUG_REFRESH */
437    if (OldvcV > new_vcv) {
438	for (; cur_line <= OldvcV; cur_line++) {
439	    update_line(Display[cur_line], STRNULL, cur_line);
440	    *Display[cur_line] = '\0';
441	}
442    }
443    OldvcV = new_vcv;		/* set for next time */
444#ifdef DEBUG_REFRESH
445    reprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
446	    CursorH, CursorV, cur_h, cur_v);
447#endif /* DEBUG_REFRESH */
448#ifdef WINNT_NATIVE
449    flush();
450#endif /* WINNT_NATIVE */
451    MoveToLine(cur_v);		/* go to where the cursor is */
452    MoveToChar(cur_h);
453    SetAttributes(0);		/* Clear all attributes */
454    flush();			/* send the output... */
455    GettingInput = oldgetting;	/* reset to old value */
456}
457
458#ifdef notdef
459GotoBottom(void)
460{				/* used to go to last used screen line */
461    MoveToLine(OldvcV);
462}
463
464#endif
465
466void
467PastBottom(void)
468{				/* used to go to last used screen line */
469    MoveToLine(OldvcV);
470    (void) putraw('\r');
471    (void) putraw('\n');
472    ClearDisp();
473    flush();
474}
475
476
477/* insert num characters of s into d (in front of the character) at dat,
478   maximum length of d is dlen */
479static void
480str_insert(Char *d, int dat, int dlen, Char *s, int num)
481{
482    Char *a, *b;
483
484    if (num <= 0)
485	return;
486    if (num > dlen - dat)
487	num = dlen - dat;
488
489#ifdef DEBUG_REFRESH
490    reprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
491	    num, dat, dlen, short2str(d));
492    reprintf("s == \"%s\"n", short2str(s));
493#endif /* DEBUG_REFRESH */
494
495    /* open up the space for num chars */
496    if (num > 0) {
497	b = d + dlen - 1;
498	a = b - num;
499	while (a >= &d[dat])
500	    *b-- = *a--;
501	d[dlen] = '\0';		/* just in case */
502    }
503#ifdef DEBUG_REFRESH
504    reprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
505	    num, dat, dlen, short2str(d));
506    reprintf("s == \"%s\"n", short2str(s));
507#endif /* DEBUG_REFRESH */
508
509    /* copy the characters */
510    for (a = d + dat; (a < d + dlen) && (num > 0); num--)
511	*a++ = *s++;
512
513#ifdef DEBUG_REFRESH
514    reprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
515	    num, dat, dlen, d, short2str(s));
516    reprintf("s == \"%s\"n", short2str(s));
517#endif /* DEBUG_REFRESH */
518}
519
520/* delete num characters d at dat, maximum length of d is dlen */
521static void
522str_delete(Char *d, int dat, int dlen, int num)
523{
524    Char *a, *b;
525
526    if (num <= 0)
527	return;
528    if (dat + num >= dlen) {
529	d[dat] = '\0';
530	return;
531    }
532
533#ifdef DEBUG_REFRESH
534    reprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
535	    num, dat, dlen, short2str(d));
536#endif /* DEBUG_REFRESH */
537
538    /* open up the space for num chars */
539    if (num > 0) {
540	b = d + dat;
541	a = b + num;
542	while (a < &d[dlen])
543	    *b++ = *a++;
544	d[dlen] = '\0';		/* just in case */
545    }
546#ifdef DEBUG_REFRESH
547    reprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
548	    num, dat, dlen, short2str(d));
549#endif /* DEBUG_REFRESH */
550}
551
552static void
553str_cp(Char *a, Char *b, int n)
554{
555    while (n-- && *b)
556	*a++ = *b++;
557}
558
559
560/* ****************************************************************
561    update_line() is based on finding the middle difference of each line
562    on the screen; vis:
563
564			     /old first difference
565	/beginning of line   |              /old last same       /old EOL
566	v		     v              v                    v
567old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
568new:	eddie> Oh, my little buggy says to me, as lurgid as
569	^		     ^        ^			   ^
570	\beginning of line   |        \new last same	   \new end of line
571			     \new first difference
572
573    all are character pointers for the sake of speed.  Special cases for
574    no differences, as well as for end of line additions must be handled.
575**************************************************************** */
576
577/* Minimum at which doing an insert it "worth it".  This should be about
578 * half the "cost" of going into insert mode, inserting a character, and
579 * going back out.  This should really be calculated from the termcap
580 * data...  For the moment, a good number for ANSI terminals.
581 */
582#define MIN_END_KEEP	4
583
584static void			/* could be changed to make it smarter */
585update_line(Char *old, Char *new, int cur_line)
586{
587    Char *o, *n, *p, c;
588    Char  *ofd, *ols, *oe, *nfd, *nls, *ne;
589    Char  *osb, *ose, *nsb, *nse;
590    int     fx, sx;
591
592    /*
593     * find first diff (won't be CHAR_DBWIDTH in either line)
594     */
595    for (o = old, n = new; *o && (*o == *n); o++, n++)
596	continue;
597    ofd = o;
598    nfd = n;
599
600    /*
601     * Find the end of both old and new
602     */
603    o = Strend(o);
604
605    /*
606     * Remove any trailing blanks off of the end, being careful not to
607     * back up past the beginning.
608     */
609    if (!(adrof(STRhighlight) && MarkIsSet)) {
610    while (ofd < o) {
611	if (o[-1] != ' ')
612	    break;
613	o--;
614    }
615    }
616    oe = o;
617    *oe = (Char) 0;
618
619    n = Strend(n);
620
621    /* remove blanks from end of new */
622    if (!(adrof(STRhighlight) && MarkIsSet)) {
623    while (nfd < n) {
624	if (n[-1] != ' ')
625	    break;
626	n--;
627    }
628    }
629    ne = n;
630    *ne = (Char) 0;
631
632    /*
633     * if no diff, continue to next line of redraw
634     */
635    if (*ofd == '\0' && *nfd == '\0') {
636#ifdef DEBUG_UPDATE
637	reprintf("no difference.\r\n");
638#endif /* DEBUG_UPDATE */
639	return;
640    }
641
642    /*
643     * find last same pointer
644     */
645    while ((o > ofd) && (n > nfd) && (*--o == *--n))
646	continue;
647    if (*o != *n) {
648	o++;
649	n++;
650    }
651    while (*o == CHAR_DBWIDTH) {
652	o++;
653	n++;
654    }
655    ols = o;
656    nls = n;
657
658    /*
659     * find same begining and same end
660     */
661    osb = ols;
662    nsb = nls;
663    ose = ols;
664    nse = nls;
665
666    /*
667     * case 1: insert: scan from nfd to nls looking for *ofd
668     */
669    if (*ofd) {
670	for (c = *ofd, n = nfd; n < nls; n++) {
671	    if (c == *n) {
672		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
673		    continue;
674		/*
675		 * if the new match is longer and it's worth keeping, then we
676		 * take it
677		 */
678		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
679		    nsb = n;
680		    nse = p;
681		    osb = ofd;
682		    ose = o;
683		}
684	    }
685	}
686    }
687
688    /*
689     * case 2: delete: scan from ofd to ols looking for *nfd
690     */
691    if (*nfd) {
692	for (c = *nfd, o = ofd; o < ols; o++) {
693	    if (c == *o) {
694		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
695		    continue;
696		/*
697		 * if the new match is longer and it's worth keeping, then we
698		 * take it
699		 */
700		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
701		    nsb = nfd;
702		    nse = n;
703		    osb = o;
704		    ose = p;
705		}
706	    }
707	}
708    }
709#ifdef notdef
710    /*
711     * If `last same' is before `same end' re-adjust
712     */
713    if (ols < ose)
714	ols = ose;
715    if (nls < nse)
716	nls = nse;
717#endif
718
719    /*
720     * Pragmatics I: If old trailing whitespace or not enough characters to
721     * save to be worth it, then don't save the last same info.
722     */
723    if ((oe - ols) < MIN_END_KEEP) {
724	ols = oe;
725	nls = ne;
726    }
727
728    /*
729     * Pragmatics II: if the terminal isn't smart enough, make the data dumber
730     * so the smart update doesn't try anything fancy
731     */
732
733    /*
734     * fx is the number of characters we need to insert/delete: in the
735     * beginning to bring the two same begins together
736     */
737    fx = (int) ((nsb - nfd) - (osb - ofd));
738    /*
739     * sx is the number of characters we need to insert/delete: in the end to
740     * bring the two same last parts together
741     */
742    sx = (int) ((nls - nse) - (ols - ose));
743
744    if (!T_CanIns) {
745	if (fx > 0) {
746	    osb = ols;
747	    ose = ols;
748	    nsb = nls;
749	    nse = nls;
750	}
751	if (sx > 0) {
752	    ols = oe;
753	    nls = ne;
754	}
755	if ((ols - ofd) < (nls - nfd)) {
756	    ols = oe;
757	    nls = ne;
758	}
759    }
760    if (!T_CanDel) {
761	if (fx < 0) {
762	    osb = ols;
763	    ose = ols;
764	    nsb = nls;
765	    nse = nls;
766	}
767	if (sx < 0) {
768	    ols = oe;
769	    nls = ne;
770	}
771	if ((ols - ofd) > (nls - nfd)) {
772	    ols = oe;
773	    nls = ne;
774	}
775    }
776
777    /*
778     * Pragmatics III: make sure the middle shifted pointers are correct if
779     * they don't point to anything (we may have moved ols or nls).
780     */
781    /* if the change isn't worth it, don't bother */
782    /* was: if (osb == ose) */
783    if ((ose - osb) < MIN_END_KEEP) {
784	osb = ols;
785	ose = ols;
786	nsb = nls;
787	nse = nls;
788    }
789
790    /*
791     * Now that we are done with pragmatics we recompute fx, sx
792     */
793    fx = (int) ((nsb - nfd) - (osb - ofd));
794    sx = (int) ((nls - nse) - (ols - ose));
795
796#ifdef DEBUG_UPDATE
797    reprintf("\n");
798    reprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
799	    ofd - old, osb - old, ose - old, ols - old, oe - old);
800    reprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
801	    nfd - new, nsb - new, nse - new, nls - new, ne - new);
802    reprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
803    reprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
804    dprintstr("old- oe", old, oe);
805    dprintstr("new- ne", new, ne);
806    dprintstr("old-ofd", old, ofd);
807    dprintstr("new-nfd", new, nfd);
808    dprintstr("ofd-osb", ofd, osb);
809    dprintstr("nfd-nsb", nfd, nsb);
810    dprintstr("osb-ose", osb, ose);
811    dprintstr("nsb-nse", nsb, nse);
812    dprintstr("ose-ols", ose, ols);
813    dprintstr("nse-nls", nse, nls);
814    dprintstr("ols- oe", ols, oe);
815    dprintstr("nls- ne", nls, ne);
816#endif /* DEBUG_UPDATE */
817
818    /*
819     * CursorV to this line cur_line MUST be in this routine so that if we
820     * don't have to change the line, we don't move to it. CursorH to first
821     * diff char
822     */
823    MoveToLine(cur_line);
824
825    /*
826     * at this point we have something like this:
827     *
828     * /old                  /ofd    /osb               /ose    /ols     /oe
829     * v.....................v       v..................v       v........v
830     * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
831     * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
832     * ^.....................^     ^..................^       ^........^
833     * \new                  \nfd  \nsb               \nse     \nls    \ne
834     *
835     * fx is the difference in length between the the chars between nfd and
836     * nsb, and the chars between ofd and osb, and is thus the number of
837     * characters to delete if < 0 (new is shorter than old, as above),
838     * or insert (new is longer than short).
839     *
840     * sx is the same for the second differences.
841     */
842
843    /*
844     * if we have a net insert on the first difference, AND inserting the net
845     * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
846     * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
847     * (TermH - 1) else we do the deletes first so that we keep everything we
848     * need to.
849     */
850
851    /*
852     * if the last same is the same like the end, there is no last same part,
853     * otherwise we want to keep the last same part set p to the last useful
854     * old character
855     */
856    p = (ols != oe) ? oe : ose;
857
858    /*
859     * if (There is a diffence in the beginning) && (we need to insert
860     * characters) && (the number of characters to insert is less than the term
861     * width) We need to do an insert! else if (we need to delete characters)
862     * We need to delete characters! else No insert or delete
863     */
864    if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
865#ifdef DEBUG_UPDATE
866	reprintf("first diff insert at %d...\r\n", nfd - new);
867#endif  /* DEBUG_UPDATE */
868	/*
869	 * Move to the first char to insert, where the first diff is.
870	 */
871	MoveToChar(nfd - new);
872	/*
873	 * Check if we have stuff to keep at end
874	 */
875	if (nsb != ne) {
876#ifdef DEBUG_UPDATE
877	    reprintf("with stuff to keep at end\r\n");
878#endif  /* DEBUG_UPDATE */
879	    /*
880	     * insert fx chars of new starting at nfd
881	     */
882	    if (fx > 0) {
883#ifdef DEBUG_UPDATE
884		if (!T_CanIns)
885		    reprintf("   ERROR: cannot insert in early first diff\n");
886#endif  /* DEBUG_UPDATE */
887		Insert_write(nfd, fx);
888		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
889	    }
890	    /*
891	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
892	     */
893	    so_write(nfd + fx, (nsb - nfd) - fx);
894	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
895	}
896	else {
897#ifdef DEBUG_UPDATE
898	    reprintf("without anything to save\r\n");
899#endif  /* DEBUG_UPDATE */
900	    so_write(nfd, (nsb - nfd));
901	    str_cp(ofd, nfd, (int) (nsb - nfd));
902	    /*
903	     * Done
904	     */
905	    return;
906	}
907    }
908    else if (fx < 0) {
909#ifdef DEBUG_UPDATE
910	reprintf("first diff delete at %d...\r\n", ofd - old);
911#endif  /* DEBUG_UPDATE */
912	/*
913	 * move to the first char to delete where the first diff is
914	 */
915	MoveToChar(ofd - old);
916	/*
917	 * Check if we have stuff to save
918	 */
919	if (osb != oe) {
920#ifdef DEBUG_UPDATE
921	    reprintf("with stuff to save at end\r\n");
922#endif  /* DEBUG_UPDATE */
923	    /*
924	     * fx is less than zero *always* here but we check for code
925	     * symmetry
926	     */
927	    if (fx < 0) {
928#ifdef DEBUG_UPDATE
929		if (!T_CanDel)
930		    reprintf("   ERROR: cannot delete in first diff\n");
931#endif /* DEBUG_UPDATE */
932		DeleteChars(-fx);
933		str_delete(old, (int) (ofd - old), TermH, -fx);
934	    }
935	    /*
936	     * write (nsb-nfd) chars of new starting at nfd
937	     */
938	    so_write(nfd, (nsb - nfd));
939	    str_cp(ofd, nfd, (int) (nsb - nfd));
940
941	}
942	else {
943#ifdef DEBUG_UPDATE
944	    reprintf("but with nothing left to save\r\n");
945#endif  /* DEBUG_UPDATE */
946	    /*
947	     * write (nsb-nfd) chars of new starting at nfd
948	     */
949	    so_write(nfd, (nsb - nfd));
950#ifdef DEBUG_REFRESH
951	    reprintf("cleareol %d\n", (oe - old) - (ne - new));
952#endif  /* DEBUG_UPDATE */
953#ifndef WINNT_NATIVE
954	    ClearEOL((oe - old) - (ne - new));
955#else
956	    /*
957	     * The calculation above does not work too well on NT
958	     */
959	    ClearEOL(TermH - CursorH);
960#endif /*WINNT_NATIVE*/
961	    /*
962	     * Done
963	     */
964	    return;
965	}
966    }
967    else
968	fx = 0;
969
970    if (sx < 0) {
971#ifdef DEBUG_UPDATE
972	reprintf("second diff delete at %d...\r\n", (ose - old) + fx);
973#endif  /* DEBUG_UPDATE */
974	/*
975	 * Check if we have stuff to delete
976	 */
977	/*
978	 * fx is the number of characters inserted (+) or deleted (-)
979	 */
980
981	MoveToChar((ose - old) + fx);
982	/*
983	 * Check if we have stuff to save
984	 */
985	if (ols != oe) {
986#ifdef DEBUG_UPDATE
987	    reprintf("with stuff to save at end\r\n");
988#endif  /* DEBUG_UPDATE */
989	    /*
990	     * Again a duplicate test.
991	     */
992	    if (sx < 0) {
993#ifdef DEBUG_UPDATE
994		if (!T_CanDel)
995		    reprintf("   ERROR: cannot delete in second diff\n");
996#endif  /* DEBUG_UPDATE */
997		DeleteChars(-sx);
998	    }
999
1000	    /*
1001	     * write (nls-nse) chars of new starting at nse
1002	     */
1003	    so_write(nse, (nls - nse));
1004	}
1005	else {
1006	    int olen = (int) (oe - old + fx);
1007	    if (olen > TermH)
1008		olen = TermH;
1009#ifdef DEBUG_UPDATE
1010	    reprintf("but with nothing left to save\r\n");
1011#endif /* DEBUG_UPDATE */
1012	    so_write(nse, (nls - nse));
1013#ifdef DEBUG_REFRESH
1014	    reprintf("cleareol %d\n", olen - (ne - new));
1015#endif /* DEBUG_UPDATE */
1016#ifndef WINNT_NATIVE
1017	    ClearEOL(olen - (ne - new));
1018#else
1019	    /*
1020	     * The calculation above does not work too well on NT
1021	     */
1022	    ClearEOL(TermH - CursorH);
1023#endif /*WINNT_NATIVE*/
1024	}
1025    }
1026
1027    /*
1028     * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
1029     */
1030    if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
1031#ifdef DEBUG_UPDATE
1032	reprintf("late first diff insert at %d...\r\n", nfd - new);
1033#endif /* DEBUG_UPDATE */
1034
1035	MoveToChar(nfd - new);
1036	/*
1037	 * Check if we have stuff to keep at the end
1038	 */
1039	if (nsb != ne) {
1040#ifdef DEBUG_UPDATE
1041	    reprintf("with stuff to keep at end\r\n");
1042#endif /* DEBUG_UPDATE */
1043	    /*
1044	     * We have to recalculate fx here because we set it
1045	     * to zero above as a flag saying that we hadn't done
1046	     * an early first insert.
1047	     */
1048	    fx = (int) ((nsb - nfd) - (osb - ofd));
1049	    if (fx > 0) {
1050		/*
1051		 * insert fx chars of new starting at nfd
1052		 */
1053#ifdef DEBUG_UPDATE
1054		if (!T_CanIns)
1055		    reprintf("   ERROR: cannot insert in late first diff\n");
1056#endif /* DEBUG_UPDATE */
1057		Insert_write(nfd, fx);
1058		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
1059	    }
1060
1061	    /*
1062	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
1063	     */
1064	    so_write(nfd + fx, (nsb - nfd) - fx);
1065	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
1066	}
1067	else {
1068#ifdef DEBUG_UPDATE
1069	    reprintf("without anything to save\r\n");
1070#endif /* DEBUG_UPDATE */
1071	    so_write(nfd, (nsb - nfd));
1072	    str_cp(ofd, nfd, (int) (nsb - nfd));
1073	}
1074    }
1075
1076    /*
1077     * line is now NEW up to nse
1078     */
1079    if (sx >= 0) {
1080#ifdef DEBUG_UPDATE
1081	reprintf("second diff insert at %d...\r\n", nse - new);
1082#endif /* DEBUG_UPDATE */
1083	MoveToChar(nse - new);
1084	if (ols != oe) {
1085#ifdef DEBUG_UPDATE
1086	    reprintf("with stuff to keep at end\r\n");
1087#endif /* DEBUG_UPDATE */
1088	    if (sx > 0) {
1089		/* insert sx chars of new starting at nse */
1090#ifdef DEBUG_UPDATE
1091		if (!T_CanIns)
1092		    reprintf("   ERROR: cannot insert in second diff\n");
1093#endif /* DEBUG_UPDATE */
1094		Insert_write(nse, sx);
1095	    }
1096
1097	    /*
1098	     * write (nls-nse) - sx chars of new starting at (nse + sx)
1099	     */
1100	    so_write(nse + sx, (nls - nse) - sx);
1101	}
1102	else {
1103#ifdef DEBUG_UPDATE
1104	    reprintf("without anything to save\r\n");
1105#endif /* DEBUG_UPDATE */
1106	    so_write(nse, (nls - nse));
1107
1108	    /*
1109             * No need to do a clear-to-end here because we were doing
1110	     * a second insert, so we will have over written all of the
1111	     * old string.
1112	     */
1113	}
1114    }
1115#ifdef DEBUG_UPDATE
1116    reprintf("done.\r\n");
1117#endif /* DEBUG_UPDATE */
1118}
1119
1120
1121static void
1122cpy_pad_spaces(Char *dst, Char *src, int width)
1123{
1124    int i;
1125
1126    for (i = 0; i < width; i++) {
1127	if (*src == (Char) 0)
1128	    break;
1129	*dst++ = *src++;
1130    }
1131
1132    while (i < width) {
1133	*dst++ = ' ';
1134	i++;
1135    }
1136    *dst = (Char) 0;
1137}
1138
1139void
1140RefCursor(void)
1141{				/* only move to new cursor pos */
1142    Char *cp;
1143    int w, h, th, v;
1144
1145    /* first we must find where the cursor is... */
1146    h = 0;
1147    v = 0;
1148    th = TermH;			/* optimize for speed */
1149
1150    for (cp = Prompt; cp != NULL && *cp; ) {	/* do prompt */
1151	if (*cp & LITERAL) {
1152	    cp++;
1153	    continue;
1154	}
1155	w = NLSClassify(*cp & CHAR, cp == Prompt, 0);
1156	cp++;
1157	switch(w) {
1158	    case NLSCLASS_NL:
1159		h = 0;
1160		v++;
1161		break;
1162	    case NLSCLASS_TAB:
1163		while (++h & 07)
1164		    ;
1165		break;
1166	    case NLSCLASS_CTRL:
1167		h += 2;
1168		break;
1169	    case NLSCLASS_ILLEGAL:
1170		h += 4;
1171		break;
1172	    case NLSCLASS_ILLEGAL2:
1173	    case NLSCLASS_ILLEGAL3:
1174	    case NLSCLASS_ILLEGAL4:
1175		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1176		break;
1177	    default:
1178		h += w;
1179	}
1180	if (h >= th) {		/* check, extra long tabs picked up here also */
1181	    h -= th;
1182	    v++;
1183	}
1184    }
1185
1186    for (cp = InputBuf; cp < Cursor;) {	/* do input buffer to Cursor */
1187	w = NLSClassify(*cp & CHAR, cp == InputBuf, 0);
1188	cp++;
1189	switch(w) {
1190	    case NLSCLASS_NL:
1191		h = 0;
1192		v++;
1193		break;
1194	    case NLSCLASS_TAB:
1195		while (++h & 07)
1196		    ;
1197		break;
1198	    case NLSCLASS_CTRL:
1199		h += 2;
1200		break;
1201	    case NLSCLASS_ILLEGAL:
1202		h += 4;
1203		break;
1204	    case NLSCLASS_ILLEGAL2:
1205	    case NLSCLASS_ILLEGAL3:
1206	    case NLSCLASS_ILLEGAL4:
1207		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1208		break;
1209	    default:
1210		h += w;
1211	}
1212	if (h >= th) {		/* check, extra long tabs picked up here also */
1213	    h -= th;
1214	    v++;
1215	}
1216    }
1217
1218    /* now go there */
1219    MoveToLine(v);
1220    MoveToChar(h);
1221    if (adrof(STRhighlight) && MarkIsSet) {
1222	ClearLines();
1223	ClearDisp();
1224	Refresh();
1225    }
1226    flush();
1227}
1228
1229#ifndef WINTT_NATIVE
1230static void
1231PutPlusOne(Char c, int width)
1232{
1233    while (width > 1 && CursorH + width > TermH)
1234	PutPlusOne(' ', 1);
1235    if ((c & LITERAL) != 0) {
1236	Char *d;
1237	for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1238	    (void) putwraw(*d);
1239    } else {
1240	(void) putwraw(c);
1241    }
1242    Display[CursorV][CursorH++] = (Char) c;
1243    while (--width > 0)
1244	Display[CursorV][CursorH++] = CHAR_DBWIDTH;
1245    if (CursorH >= TermH) {	/* if we must overflow */
1246	CursorH = 0;
1247	CursorV++;
1248	OldvcV++;
1249	if (T_Margin & MARGIN_AUTO) {
1250	    if (T_Margin & MARGIN_MAGIC) {
1251		(void) putraw(' ');
1252		(void) putraw('\b');
1253	    }
1254	}
1255	else {
1256	    (void) putraw('\r');
1257	    (void) putraw('\n');
1258	}
1259    }
1260}
1261#endif
1262
1263void
1264RefPlusOne(int l)
1265{				/* we added just one char, handle it fast.
1266				 * assumes that screen cursor == real cursor */
1267    Char *cp, c;
1268    int w;
1269
1270    if (Cursor != LastChar) {
1271	Refresh();		/* too hard to handle */
1272	return;
1273    }
1274    if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1275	Refresh();		/* clear out rprompt if less than one char gap*/
1276	return;
1277    }
1278    cp = Cursor - l;
1279    c = *cp & CHAR;
1280    w = NLSClassify(c, cp == InputBuf, 0);
1281    switch(w) {
1282	case NLSCLASS_CTRL:
1283	    PutPlusOne('^', 1);
1284	    if (c == CTL_ESC('\177')) {
1285		PutPlusOne('?', 1);
1286		break;
1287	    }
1288#ifdef IS_ASCII
1289	    /* uncontrolify it; works only for iso8859-1 like sets */
1290	    PutPlusOne((c | 0100), 1);
1291#else
1292	    PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
1293#endif
1294	    break;
1295	case NLSCLASS_ILLEGAL:
1296	    PutPlusOne('\\', 1);
1297	    PutPlusOne(((c >> 6) & 7) + '0', 1);
1298	    PutPlusOne(((c >> 3) & 7) + '0', 1);
1299	    PutPlusOne((c & 7) + '0', 1);
1300	    break;
1301	case 1:
1302	    if (adrof(STRhighlight) && MarkIsSet)
1303		StartHighlight();
1304	    if (l > 1)
1305		PutPlusOne(MakeLiteral(cp, l, 0), 1);
1306	    else
1307		PutPlusOne(*cp, 1);
1308	    if (adrof(STRhighlight) && MarkIsSet)
1309		StopHighlight();
1310	    break;
1311	default:
1312	    Refresh();		/* too hard to handle */
1313	    return;
1314    }
1315    flush();
1316}
1317
1318/* clear the screen buffers so that new new prompt starts fresh. */
1319
1320void
1321ClearDisp(void)
1322{
1323    int i;
1324
1325    CursorV = 0;		/* clear the display buffer */
1326    CursorH = 0;
1327    for (i = 0; i < TermV; i++)
1328	(void) memset(Display[i], 0, (TermH + 1) * sizeof(Display[0][0]));
1329    OldvcV = 0;
1330    litlen = 0;
1331}
1332
1333void
1334ClearLines(void)
1335{				/* Make sure all lines are *really* blank */
1336    int i;
1337
1338    if (T_CanCEOL) {
1339	/*
1340	 * Clear the lines from the bottom up so that if we try moving
1341	 * the cursor down by writing the character that is at the end
1342	 * of the screen line, we won't rewrite a character that shouldn't
1343	 * be there.
1344	 */
1345	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
1346	    MoveToLine(i);
1347	    MoveToChar(0);
1348	    ClearEOL(TermH);
1349	}
1350    }
1351    else {
1352	MoveToLine(OldvcV);	/* go to last line */
1353	(void) putraw('\r');	/* go to BOL */
1354	(void) putraw('\n');	/* go to new line */
1355    }
1356}
1357