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