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