xref: /dragonfly/contrib/tcsh-6/ed.refresh.c (revision f9993810)
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 
40 Char   *litptr;
41 static int vcursor_h, vcursor_v;
42 static int rprompt_h, rprompt_v;
43 
44 static	int	MakeLiteral		(Char *, int, Char);
45 static	int	Draw 			(Char *, int, int);
46 static	void	Vdraw 			(Char, int);
47 static	void	RefreshPromptpart	(Char *);
48 static	void	update_line 		(Char *, Char *, int);
49 static	void	str_insert		(Char *, int, int, Char *, int);
50 static	void	str_delete		(Char *, int, int, int);
51 static	void	str_cp			(Char *, Char *, int);
52 #ifndef WINNT_NATIVE
53 static
54 #else
55 extern
56 #endif
57 	void    PutPlusOne      (Char, int);
58 static	void	cpy_pad_spaces		(Char *, Char *, int);
59 #if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
60 static	void	reprintf			(char *, ...);
61 #ifdef DEBUG_UPDATE
62 static	void	dprintstr		(char *, const Char *, const Char *);
63 
64 static void
65 dprintstr(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  */
83 static void
84 reprintf(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 
107 static int litlen = 0, litalloc = 0;
108 
109 static 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 */
159 static int
160 Draw(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 
266 static void
267 Vdraw(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  */
304 static void
305 RefreshPromptpart(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
343 static
344 #endif
345 int OldvcV = 0;
346 
347 void
348 Refresh(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
459 GotoBottom(void)
460 {				/* used to go to last used screen line */
461     MoveToLine(OldvcV);
462 }
463 
464 #endif
465 
466 void
467 PastBottom(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 */
479 static void
480 str_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 */
521 static void
522 str_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 
552 static void
553 str_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
567 old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
568 new:	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 
584 static void			/* could be changed to make it smarter */
585 update_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 
1121 static void
1122 cpy_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 
1139 static void
1140 CalcPosition(int w, int th, int *h, int *v)
1141 {
1142     switch(w) {
1143 	case NLSCLASS_NL:
1144 	    *h = 0;
1145 	    (*v)++;
1146 	    break;
1147 	case NLSCLASS_TAB:
1148 	    while (++(*h) & 07)
1149 		;
1150 	    break;
1151 	case NLSCLASS_CTRL:
1152 	    *h += 2;
1153 	    break;
1154 	case NLSCLASS_ILLEGAL:
1155 	    *h += 4;
1156 	    break;
1157 	case NLSCLASS_ILLEGAL2:
1158 	case NLSCLASS_ILLEGAL3:
1159 	case NLSCLASS_ILLEGAL4:
1160 	case NLSCLASS_ILLEGAL5:
1161 	    *h += 4 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1162 	    break;
1163 	default:
1164 	    *h += w;
1165     }
1166     if (*h >= th) {		/* check, extra long tabs picked up here also */
1167 	*h -= th;
1168 	(*v)++;
1169     }
1170 }
1171 
1172 void
1173 RefCursor(void)
1174 {				/* only move to new cursor pos */
1175     Char *cp;
1176     int w, h, th, v;
1177 
1178     /* first we must find where the cursor is... */
1179     h = 0;
1180     v = 0;
1181     th = TermH;			/* optimize for speed */
1182 
1183     for (cp = Prompt; cp != NULL && *cp; ) {	/* do prompt */
1184 	if (*cp & LITERAL) {
1185 	    cp++;
1186 	    continue;
1187 	}
1188 	w = NLSClassify(*cp & CHAR, cp == Prompt, 0);
1189 	cp++;
1190 	CalcPosition(w, th, &h, &v);
1191     }
1192 
1193     for (cp = InputBuf; cp < Cursor;) {	/* do input buffer to Cursor */
1194 	w = NLSClassify(*cp & CHAR, cp == InputBuf, 0);
1195 	cp++;
1196 	CalcPosition(w, th, &h, &v);
1197     }
1198 
1199     /* now go there */
1200     MoveToLine(v);
1201     MoveToChar(h);
1202     if (adrof(STRhighlight) && MarkIsSet) {
1203 	ClearLines();
1204 	ClearDisp();
1205 	Refresh();
1206     }
1207     flush();
1208 }
1209 
1210 #ifndef WINTT_NATIVE
1211 static void
1212 PutPlusOne(Char c, int width)
1213 {
1214     while (width > 1 && CursorH + width > TermH)
1215 	PutPlusOne(' ', 1);
1216     if ((c & LITERAL) != 0) {
1217 	Char *d;
1218 	for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1219 	    (void) putwraw(*d);
1220     } else {
1221 	(void) putwraw(c);
1222     }
1223     Display[CursorV][CursorH++] = (Char) c;
1224     while (--width > 0)
1225 	Display[CursorV][CursorH++] = CHAR_DBWIDTH;
1226     if (CursorH >= TermH) {	/* if we must overflow */
1227 	CursorH = 0;
1228 	CursorV++;
1229 	OldvcV++;
1230 	if (T_Margin & MARGIN_AUTO) {
1231 	    if (T_Margin & MARGIN_MAGIC) {
1232 		(void) putraw(' ');
1233 		(void) putraw('\b');
1234 	    }
1235 	}
1236 	else {
1237 	    (void) putraw('\r');
1238 	    (void) putraw('\n');
1239 	}
1240     }
1241 }
1242 #endif
1243 
1244 void
1245 RefPlusOne(int l)
1246 {				/* we added just one char, handle it fast.
1247 				 * assumes that screen cursor == real cursor */
1248     Char *cp, c;
1249     int w;
1250 
1251     if (Cursor != LastChar) {
1252 	Refresh();		/* too hard to handle */
1253 	return;
1254     }
1255     if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1256 	Refresh();		/* clear out rprompt if less than one char gap*/
1257 	return;
1258     }
1259     cp = Cursor - l;
1260     c = *cp & CHAR;
1261     w = NLSClassify(c, cp == InputBuf, 0);
1262     switch(w) {
1263 	case NLSCLASS_CTRL:
1264 	    PutPlusOne('^', 1);
1265 	    if (c == CTL_ESC('\177')) {
1266 		PutPlusOne('?', 1);
1267 		break;
1268 	    }
1269 #ifdef IS_ASCII
1270 	    /* uncontrolify it; works only for iso8859-1 like sets */
1271 	    PutPlusOne((c | 0100), 1);
1272 #else
1273 	    PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
1274 #endif
1275 	    break;
1276 	case NLSCLASS_ILLEGAL:
1277 	    PutPlusOne('\\', 1);
1278 	    PutPlusOne(((c >> 6) & 7) + '0', 1);
1279 	    PutPlusOne(((c >> 3) & 7) + '0', 1);
1280 	    PutPlusOne((c & 7) + '0', 1);
1281 	    break;
1282 	case 1:
1283 	    if (adrof(STRhighlight) && MarkIsSet)
1284 		StartHighlight();
1285 	    if (l > 1)
1286 		PutPlusOne(MakeLiteral(cp, l, 0), 1);
1287 	    else
1288 		PutPlusOne(*cp, 1);
1289 	    if (adrof(STRhighlight) && MarkIsSet)
1290 		StopHighlight();
1291 	    break;
1292 	default:
1293 	    Refresh();		/* too hard to handle */
1294 	    return;
1295     }
1296     flush();
1297 }
1298 
1299 /* clear the screen buffers so that new new prompt starts fresh. */
1300 
1301 void
1302 ClearDisp(void)
1303 {
1304     int i;
1305 
1306     CursorV = 0;		/* clear the display buffer */
1307     CursorH = 0;
1308     for (i = 0; i < TermV; i++)
1309 	(void) memset(Display[i], 0, (TermH + 1) * sizeof(Display[0][0]));
1310     OldvcV = 0;
1311     litlen = 0;
1312 }
1313 
1314 void
1315 ClearLines(void)
1316 {				/* Make sure all lines are *really* blank */
1317     int i;
1318 
1319     if (T_CanCEOL) {
1320 	/*
1321 	 * Clear the lines from the bottom up so that if we try moving
1322 	 * the cursor down by writing the character that is at the end
1323 	 * of the screen line, we won't rewrite a character that shouldn't
1324 	 * be there.
1325 	 */
1326 	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
1327 	    MoveToLine(i);
1328 	    MoveToChar(0);
1329 	    ClearEOL(TermH);
1330 	}
1331     }
1332     else {
1333 	MoveToLine(OldvcV);	/* go to last line */
1334 	(void) putraw('\r');	/* go to BOL */
1335 	(void) putraw('\n');	/* go to new line */
1336     }
1337 }
1338