xref: /freebsd/contrib/tcsh/ed.refresh.c (revision ed620727e2f8c40f85e894795ed40b39c2ea0624)
1 /* $Header: /src/pub/tcsh/ed.refresh.c,v 3.30 2003/02/08 20:03:25 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("$Id: ed.refresh.c,v 3.30 2003/02/08 20:03:25 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[256];
45 static int vcursor_h, vcursor_v;
46 static int rprompt_h, rprompt_v;
47 
48 static	void	Draw 			__P((int));
49 static	void	Vdraw 			__P((int));
50 static	void	RefreshPromptpart	__P((Char *));
51 static	void	update_line 		__P((Char *, Char *, int));
52 static	void	str_insert		__P((Char *, int, int, Char *, int));
53 static	void	str_delete		__P((Char *, int, int, int));
54 static	void	str_cp			__P((Char *, Char *, int));
55 #ifndef WINNT_NATIVE
56 static
57 #else
58 extern
59 #endif
60 	void    PutPlusOne      __P((int));
61 static	void	cpy_pad_spaces		__P((Char *, Char *, int));
62 #if defined(DSPMBYTE)
63 static	Char 	*update_line_fix_mbyte_point __P((Char *, Char *, int));
64 #endif
65 #if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
66 static	void	dprintf			__P((char *, ...));
67 #ifdef DEBUG_UPDATE
68 static	void	dprintstr		__P((char *, Char *, Char *));
69 
70 static void
71 dprintstr(str, f, t)
72 char *str;
73 Char *f, *t;
74 {
75     dprintf("%s:\"", str);
76     while (f < t)
77 	dprintf("%c", *f++ & ASCII);
78     dprintf("\"\r\n");
79 }
80 #endif /* DEBUG_UPDATE */
81 
82 /* dprintf():
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 #ifdef FUNCPROTO
89 dprintf(char *fmt, ...)
90 #else
91 dprintf(va_list)
92     va_dcl
93 #endif /* __STDC__ */
94 {
95     static int fd = -1;
96     char *dtty;
97 
98     if ((dtty = getenv("DEBUGTTY"))) {
99 	int o;
100 	va_list va;
101 #ifdef FUNCPROTO
102 	va_start(va, fmt);
103 #else
104 	char *fmt;
105 	va_start(va);
106 	fmt = va_arg(va, char *);
107 #endif /* __STDC__ */
108 
109 	if (fd == -1)
110 	    fd = open(dtty, O_RDWR);
111 	o = SHOUT;
112 	flush();
113 	SHOUT = fd;
114 	xvprintf(fmt, va);
115 	va_end(va);
116 	flush();
117 	SHOUT = o;
118     }
119 }
120 #endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
121 
122 static void
123 Draw(c)				/* draw c, expand tabs, ctl chars */
124     register int c;
125 {
126     register Char ch = c & CHAR;
127 
128     if (Isprint(ch)) {
129 	Vdraw(c);
130 	return;
131     }
132     /* from wolman%crltrx.DEC@decwrl.dec.com (Alec Wolman) */
133     if (ch == '\n') {		/* expand the newline	 */
134 	/*
135 	 * Don't force a newline if Vdraw does it (i.e. we're at end of line)
136 	 * - or we will get two newlines and possibly garbage in between
137 	 */
138 	int oldv = vcursor_v;
139 
140 	Vdraw('\0');		/* assure end of line	 */
141 	if (oldv == vcursor_v) {
142 	    vcursor_h = 0;	/* reset cursor pos	 */
143 	    vcursor_v++;
144 	}
145 	return;
146     }
147     if (ch == '\t') {		/* expand the tab 	 */
148 	for (;;) {
149 	    Vdraw(' ');
150 	    if ((vcursor_h & 07) == 0)
151 		break;		/* go until tab stop	 */
152 	}
153     }
154     else if (Iscntrl(ch)) {
155 #ifdef IS_ASCII
156 	Vdraw('^');
157 	if (ch == CTL_ESC('\177')) {
158 	    Vdraw('?');
159 	}
160 	else {
161 	    /* uncontrolify it; works only for iso8859-1 like sets */
162 	    Vdraw((c | 0100));
163 #else
164 	if (ch == CTL_ESC('\177')) {
165 	    Vdraw('^');
166 	    Vdraw('?');
167 	}
168 	else {
169 	    if (Isupper(_toebcdic[_toascii[c]|0100])
170 		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL)
171 	    {
172 		Vdraw('^');
173 		Vdraw(_toebcdic[_toascii[c]|0100]);
174 	    }
175 	    else
176 	    {
177 		Vdraw('\\');
178 		Vdraw(((c >> 6) & 7) + '0');
179 		Vdraw(((c >> 3) & 7) + '0');
180 		Vdraw((c & 7) + '0');
181 	    }
182 #endif
183 	}
184     }
185 #ifdef KANJI
186     else if (
187 #ifdef DSPMBYTE
188 	     _enable_mbdisp &&
189 #endif
190 	     !adrof(STRnokanji)) {
191 	Vdraw(c);
192 	return;
193     }
194 #endif
195     else {
196 	Vdraw('\\');
197 	Vdraw(((c >> 6) & 7) + '0');
198 	Vdraw(((c >> 3) & 7) + '0');
199 	Vdraw((c & 7) + '0');
200     }
201 }
202 
203 static void
204 Vdraw(c)			/* draw char c onto V lines */
205     register int c;
206 {
207 #ifdef DEBUG_REFRESH
208 # ifdef SHORT_STRINGS
209     dprintf("Vdrawing %6.6o '%c'\r\n", c, c & ASCII);
210 # else
211     dprintf("Vdrawing %3.3o '%c'\r\n", c, c);
212 # endif /* SHORT_STRNGS */
213 #endif  /* DEBUG_REFRESH */
214 
215     Vdisplay[vcursor_v][vcursor_h] = (Char) c;
216     vcursor_h++;		/* advance to next place */
217     if (vcursor_h >= TermH) {
218 	Vdisplay[vcursor_v][TermH] = '\0';	/* assure end of line */
219 	vcursor_h = 0;		/* reset it. */
220 	vcursor_v++;
221 #ifdef DEBUG_REFRESH
222 	if (vcursor_v >= TermV) {	/* should NEVER happen. */
223 	    dprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
224 		    vcursor_v, TermV);
225 	    abort();
226 	}
227 #endif /* DEBUG_REFRESH */
228     }
229 }
230 
231 /*
232  *  RefreshPromptpart()
233  *	draws a prompt element, expanding literals (we know it's ASCIZ)
234  */
235 static void
236 RefreshPromptpart(buf)
237     Char *buf;
238 {
239     register Char *cp;
240     static unsigned int litnum = 0;
241     if (buf == NULL)
242     {
243       litnum = 0;
244       return;
245     }
246 
247     for (cp = buf; *cp; cp++) {
248 	if (*cp & LITERAL) {
249 	    if (litnum < (sizeof(litptr) / sizeof(litptr[0]))) {
250 		litptr[litnum] = cp;
251 #ifdef DEBUG_LITERAL
252 		dprintf("litnum = %d, litptr = %x:\r\n",
253 			litnum, litptr[litnum]);
254 #endif /* DEBUG_LITERAL */
255 	    }
256 	    while (*cp & LITERAL)
257 		cp++;
258 	    if (*cp)
259 		Vdraw((int) (litnum++ | LITERAL));
260 	    else {
261 		/*
262 		 * XXX: This is a bug, we lose the last literal, if it is not
263 		 * followed by a normal character, but it is too hard to fix
264 		 */
265 		break;
266 	    }
267 	}
268 	else
269 	    Draw(*cp);
270     }
271 }
272 
273 /*
274  *  Refresh()
275  *	draws the new virtual screen image from the current input
276  *  	line, then goes line-by-line changing the real image to the new
277  *	virtual image. The routine to re-draw a line can be replaced
278  *	easily in hopes of a smarter one being placed there.
279  */
280 #ifndef WINNT_NATIVE
281 static
282 #endif
283 int OldvcV = 0;
284 
285 void
286 Refresh()
287 {
288     register int cur_line;
289     register Char *cp;
290     int     cur_h, cur_v = 0, new_vcv;
291     int     rhdiff;
292     Char    oldgetting;
293 
294 #ifdef DEBUG_REFRESH
295     dprintf("PromptBuf = :%s:\r\n", short2str(PromptBuf));
296     dprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
297 #endif /* DEBUG_REFRESH */
298     oldgetting = GettingInput;
299     GettingInput = 0;		/* avoid re-entrance via SIGWINCH */
300 
301     /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
302     vcursor_h = 0;
303     vcursor_v = 0;
304     RefreshPromptpart(NULL);
305     RefreshPromptpart(RPromptBuf);
306     rprompt_h = vcursor_h;
307     rprompt_v = vcursor_v;
308 
309     /* reset the Vdraw cursor, draw prompt */
310     vcursor_h = 0;
311     vcursor_v = 0;
312     RefreshPromptpart(NULL);
313     RefreshPromptpart(PromptBuf);
314     cur_h = -1;			/* set flag in case I'm not set */
315 
316     /* draw the current input buffer */
317     for (cp = InputBuf; (cp < LastChar); cp++) {
318 	if (cp == Cursor) {
319 	    cur_h = vcursor_h;	/* save for later */
320 	    cur_v = vcursor_v;
321 	}
322 	Draw(*cp);
323     }
324 
325     if (cur_h == -1) {		/* if I haven't been set yet, I'm at the end */
326 	cur_h = vcursor_h;
327 	cur_v = vcursor_v;
328     }
329 
330     rhdiff = TermH - vcursor_h - rprompt_h;
331     if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
332 			/*
333 			 * have a right-hand side prompt that will fit on
334 			 * the end of the first line with at least one
335 			 * character gap to the input buffer.
336 			 */
337 	while (--rhdiff > 0)		/* pad out with spaces */
338 	    Draw(' ');
339 	RefreshPromptpart(RPromptBuf);
340     }
341     else {
342 	rprompt_h = 0;			/* flag "not using rprompt" */
343 	rprompt_v = 0;
344     }
345 
346     new_vcv = vcursor_v;	/* must be done BEFORE the NUL is written */
347     Vdraw('\0');		/* put NUL on end */
348 
349 #ifdef DEBUG_REFRESH
350     dprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
351 	    TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
352 #endif /* DEBUG_REFRESH */
353 
354 #ifdef DEBUG_UPDATE
355     dprintf("updating %d lines.\r\n", new_vcv);
356 #endif  /* DEBUG_UPDATE */
357     for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
358 	/* NOTE THAT update_line MAY CHANGE Display[cur_line] */
359 	update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
360 #ifdef WINNT_NATIVE
361 	flush();
362 #endif /* WINNT_NATIVE */
363 
364 	/*
365 	 * Copy the new line to be the current one, and pad out with spaces
366 	 * to the full width of the terminal so that if we try moving the
367 	 * cursor by writing the character that is at the end of the
368 	 * screen line, it won't be a NUL or some old leftover stuff.
369 	 */
370 	cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
371 #ifdef notdef
372 	(void) Strncpy(Display[cur_line], Vdisplay[cur_line], (size_t) TermH);
373 	Display[cur_line][TermH] = '\0';	/* just in case */
374 #endif
375     }
376 #ifdef DEBUG_REFRESH
377     dprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
378 	    vcursor_v, OldvcV, cur_line);
379 #endif /* DEBUG_REFRESH */
380     if (OldvcV > new_vcv) {
381 	for (; cur_line <= OldvcV; cur_line++) {
382 	    update_line(Display[cur_line], STRNULL, cur_line);
383 	    *Display[cur_line] = '\0';
384 	}
385     }
386     OldvcV = new_vcv;		/* set for next time */
387 #ifdef DEBUG_REFRESH
388     dprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
389 	    CursorH, CursorV, cur_h, cur_v);
390 #endif /* DEBUG_REFRESH */
391 #ifdef WINNT_NATIVE
392     flush();
393 #endif /* WINNT_NATIVE */
394     MoveToLine(cur_v);		/* go to where the cursor is */
395     MoveToChar(cur_h);
396     SetAttributes(0);		/* Clear all attributes */
397     flush();			/* send the output... */
398     GettingInput = oldgetting;	/* reset to old value */
399 }
400 
401 #ifdef notdef
402 GotoBottom()
403 {				/* used to go to last used screen line */
404     MoveToLine(OldvcV);
405 }
406 
407 #endif
408 
409 void
410 PastBottom()
411 {				/* used to go to last used screen line */
412     MoveToLine(OldvcV);
413     (void) putraw('\r');
414     (void) putraw('\n');
415     ClearDisp();
416     flush();
417 }
418 
419 
420 /* insert num characters of s into d (in front of the character) at dat,
421    maximum length of d is dlen */
422 static void
423 str_insert(d, dat, dlen, s, num)
424     register Char *d;
425     register int dat, dlen;
426     register Char *s;
427     register int num;
428 {
429     register Char *a, *b;
430 
431     if (num <= 0)
432 	return;
433     if (num > dlen - dat)
434 	num = dlen - dat;
435 
436 #ifdef DEBUG_REFRESH
437     dprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
438 	    num, dat, dlen, short2str(d));
439     dprintf("s == \"%s\"n", short2str(s));
440 #endif /* DEBUG_REFRESH */
441 
442     /* open up the space for num chars */
443     if (num > 0) {
444 	b = d + dlen - 1;
445 	a = b - num;
446 	while (a >= &d[dat])
447 	    *b-- = *a--;
448 	d[dlen] = '\0';		/* just in case */
449     }
450 #ifdef DEBUG_REFRESH
451     dprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
452 	    num, dat, dlen, short2str(d));
453     dprintf("s == \"%s\"n", short2str(s));
454 #endif /* DEBUG_REFRESH */
455 
456     /* copy the characters */
457     for (a = d + dat; (a < d + dlen) && (num > 0); num--)
458 	*a++ = *s++;
459 
460 #ifdef DEBUG_REFRESH
461     dprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
462 	    num, dat, dlen, d, short2str(s));
463     dprintf("s == \"%s\"n", short2str(s));
464 #endif /* DEBUG_REFRESH */
465 }
466 
467 /* delete num characters d at dat, maximum length of d is dlen */
468 static void
469 str_delete(d, dat, dlen, num)
470     register Char *d;
471     register int dat, dlen, num;
472 {
473     register Char *a, *b;
474 
475     if (num <= 0)
476 	return;
477     if (dat + num >= dlen) {
478 	d[dat] = '\0';
479 	return;
480     }
481 
482 #ifdef DEBUG_REFRESH
483     dprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
484 	    num, dat, dlen, short2str(d));
485 #endif /* DEBUG_REFRESH */
486 
487     /* open up the space for num chars */
488     if (num > 0) {
489 	b = d + dat;
490 	a = b + num;
491 	while (a < &d[dlen])
492 	    *b++ = *a++;
493 	d[dlen] = '\0';		/* just in case */
494     }
495 #ifdef DEBUG_REFRESH
496     dprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
497 	    num, dat, dlen, short2str(d));
498 #endif /* DEBUG_REFRESH */
499 }
500 
501 static void
502 str_cp(a, b, n)
503     register Char *a, *b;
504     register int n;
505 {
506     while (n-- && *b)
507 	*a++ = *b++;
508 }
509 
510 
511 #if defined(DSPMBYTE) /* BY TAGA Nayuta VERY THANKS */
512 static Char *
513 update_line_fix_mbyte_point(start, target, d)
514      Char *start, *target;
515      int d;
516 {
517     if (_enable_mbdisp) {
518 	while (*start) {
519 	    if (target == start)
520 		break;
521 	    if (target < start)
522 		return target + d;
523 	    if (Ismbyte1(*start) && Ismbyte2(*(start + 1)))
524 		start++;
525 	    start++;
526 	}
527     }
528     return target;
529 }
530 #endif
531 
532 /* ****************************************************************
533     update_line() is based on finding the middle difference of each line
534     on the screen; vis:
535 
536 			     /old first difference
537 	/beginning of line   |              /old last same       /old EOL
538 	v		     v              v                    v
539 old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
540 new:	eddie> Oh, my little buggy says to me, as lurgid as
541 	^		     ^        ^			   ^
542 	\beginning of line   |        \new last same	   \new end of line
543 			     \new first difference
544 
545     all are character pointers for the sake of speed.  Special cases for
546     no differences, as well as for end of line additions must be handled.
547 **************************************************************** */
548 
549 /* Minimum at which doing an insert it "worth it".  This should be about
550  * half the "cost" of going into insert mode, inserting a character, and
551  * going back out.  This should really be calculated from the termcap
552  * data...  For the moment, a good number for ANSI terminals.
553  */
554 #define MIN_END_KEEP	4
555 
556 static void			/* could be changed to make it smarter */
557 update_line(old, new, cur_line)
558     register Char *old, *new;
559     int     cur_line;
560 {
561     register 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
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     while (*o)
578 	o++;
579     /*
580      * Remove any trailing blanks off of the end, being careful not to
581      * back up past the beginning.
582      */
583     while (ofd < o) {
584 	if (o[-1] != ' ')
585 	    break;
586 	o--;
587     }
588     oe = o;
589     *oe = (Char) 0;
590 
591     while (*n)
592 	n++;
593 
594     /* remove blanks from end of new */
595     while (nfd < n) {
596 	if (n[-1] != ' ')
597 	    break;
598 	n--;
599     }
600     ne = n;
601     *ne = (Char) 0;
602 
603     /*
604      * if no diff, continue to next line of redraw
605      */
606     if (*ofd == '\0' && *nfd == '\0') {
607 #ifdef DEBUG_UPDATE
608 	dprintf("no difference.\r\n");
609 #endif /* DEBUG_UPDATE */
610 	return;
611     }
612 
613     /*
614      * find last same pointer
615      */
616     while ((o > ofd) && (n > nfd) && (*--o == *--n))
617 	continue;
618     ols = ++o;
619     nls = ++n;
620 
621     /*
622      * find same begining and same end
623      */
624     osb = ols;
625     nsb = nls;
626     ose = ols;
627     nse = nls;
628 
629     /*
630      * case 1: insert: scan from nfd to nls looking for *ofd
631      */
632     if (*ofd) {
633 	for (c = *ofd, n = nfd; n < nls; n++) {
634 	    if (c == *n) {
635 		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
636 		    continue;
637 		/*
638 		 * if the new match is longer and it's worth keeping, then we
639 		 * take it
640 		 */
641 		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
642 		    nsb = n;
643 		    nse = p;
644 		    osb = ofd;
645 		    ose = o;
646 		}
647 	    }
648 	}
649     }
650 
651     /*
652      * case 2: delete: scan from ofd to ols looking for *nfd
653      */
654     if (*nfd) {
655 	for (c = *nfd, o = ofd; o < ols; o++) {
656 	    if (c == *o) {
657 		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
658 		    continue;
659 		/*
660 		 * if the new match is longer and it's worth keeping, then we
661 		 * take it
662 		 */
663 		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
664 		    nsb = nfd;
665 		    nse = n;
666 		    osb = o;
667 		    ose = p;
668 		}
669 	    }
670 	}
671     }
672 #ifdef notdef
673     /*
674      * If `last same' is before `same end' re-adjust
675      */
676     if (ols < ose)
677 	ols = ose;
678     if (nls < nse)
679 	nls = nse;
680 #endif
681 
682     /*
683      * Pragmatics I: If old trailing whitespace or not enough characters to
684      * save to be worth it, then don't save the last same info.
685      */
686     if ((oe - ols) < MIN_END_KEEP) {
687 	ols = oe;
688 	nls = ne;
689     }
690 
691     /*
692      * Pragmatics II: if the terminal isn't smart enough, make the data dumber
693      * so the smart update doesn't try anything fancy
694      */
695 
696     /*
697      * fx is the number of characters we need to insert/delete: in the
698      * beginning to bring the two same begins together
699      */
700     fx = (int) ((nsb - nfd) - (osb - ofd));
701     /*
702      * sx is the number of characters we need to insert/delete: in the end to
703      * bring the two same last parts together
704      */
705     sx = (int) ((nls - nse) - (ols - ose));
706 
707     if (!T_CanIns) {
708 	if (fx > 0) {
709 	    osb = ols;
710 	    ose = ols;
711 	    nsb = nls;
712 	    nse = nls;
713 	}
714 	if (sx > 0) {
715 	    ols = oe;
716 	    nls = ne;
717 	}
718 	if ((ols - ofd) < (nls - nfd)) {
719 	    ols = oe;
720 	    nls = ne;
721 	}
722     }
723     if (!T_CanDel) {
724 	if (fx < 0) {
725 	    osb = ols;
726 	    ose = ols;
727 	    nsb = nls;
728 	    nse = nls;
729 	}
730 	if (sx < 0) {
731 	    ols = oe;
732 	    nls = ne;
733 	}
734 	if ((ols - ofd) > (nls - nfd)) {
735 	    ols = oe;
736 	    nls = ne;
737 	}
738     }
739 
740     /*
741      * Pragmatics III: make sure the middle shifted pointers are correct if
742      * they don't point to anything (we may have moved ols or nls).
743      */
744     /* if the change isn't worth it, don't bother */
745     /* was: if (osb == ose) */
746     if ((ose - osb) < MIN_END_KEEP) {
747 	osb = ols;
748 	ose = ols;
749 	nsb = nls;
750 	nse = nls;
751     }
752 
753     /*
754      * Now that we are done with pragmatics we recompute fx, sx
755      */
756     fx = (int) ((nsb - nfd) - (osb - ofd));
757     sx = (int) ((nls - nse) - (ols - ose));
758 
759 #ifdef DEBUG_UPDATE
760     dprintf("\n");
761     dprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
762 	    ofd - old, osb - old, ose - old, ols - old, oe - old);
763     dprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
764 	    nfd - new, nsb - new, nse - new, nls - new, ne - new);
765     dprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
766     dprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
767     dprintstr("old- oe", old, oe);
768     dprintstr("new- ne", new, ne);
769     dprintstr("old-ofd", old, ofd);
770     dprintstr("new-nfd", new, nfd);
771     dprintstr("ofd-osb", ofd, osb);
772     dprintstr("nfd-nsb", nfd, nsb);
773     dprintstr("osb-ose", osb, ose);
774     dprintstr("nsb-nse", nsb, nse);
775     dprintstr("ose-ols", ose, ols);
776     dprintstr("nse-nls", nse, nls);
777     dprintstr("ols- oe", ols, oe);
778     dprintstr("nls- ne", nls, ne);
779 #endif /* DEBUG_UPDATE */
780 
781     /*
782      * CursorV to this line cur_line MUST be in this routine so that if we
783      * don't have to change the line, we don't move to it. CursorH to first
784      * diff char
785      */
786     MoveToLine(cur_line);
787 
788 #if defined(DSPMBYTE) /* BY TAGA Nayuta VERY THANKS */
789     ofd = update_line_fix_mbyte_point(old, ofd, -1);
790     osb = update_line_fix_mbyte_point(old, osb,  1);
791     ose = update_line_fix_mbyte_point(old, ose, -1);
792     ols = update_line_fix_mbyte_point(old, ols,  1);
793     nfd = update_line_fix_mbyte_point(new, nfd, -1);
794     nsb = update_line_fix_mbyte_point(new, nsb,  1);
795     nse = update_line_fix_mbyte_point(new, nse, -1);
796     nls = update_line_fix_mbyte_point(new, nls,  1);
797 #endif
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 	dprintf("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 	    dprintf("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 		    dprintf("   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 	    dprintf("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 	dprintf("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 	    dprintf("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 		    dprintf("   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 	    dprintf("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 	    dprintf("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 	dprintf("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 	    dprintf("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 		    dprintf("   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 	    dprintf("but with nothing left to save\r\n");
985 #endif /* DEBUG_UPDATE */
986 	    so_write(nse, (nls - nse));
987 #ifdef DEBUG_REFRESH
988 	    dprintf("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 	dprintf("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 	    dprintf("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 		    dprintf("   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 	    dprintf("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 	dprintf("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 	    dprintf("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 		    dprintf("   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 	    dprintf("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     dprintf("done.\r\n");
1091 #endif /* DEBUG_UPDATE */
1092 }
1093 
1094 
1095 static void
1096 cpy_pad_spaces(dst, src, width)
1097     register Char *dst, *src;
1098     register int width;
1099 {
1100     register int i;
1101 
1102     for (i = 0; i < width; i++) {
1103 	if (*src == (Char) 0)
1104 	    break;
1105 	*dst++ = *src++;
1106     }
1107 
1108     while (i < width) {
1109 	*dst++ = ' ';
1110 	i++;
1111     }
1112     *dst = (Char) 0;
1113 }
1114 
1115 void
1116 RefCursor()
1117 {				/* only move to new cursor pos */
1118     register Char *cp, c;
1119     register int h, th, v;
1120 
1121     /* first we must find where the cursor is... */
1122     h = 0;
1123     v = 0;
1124     th = TermH;			/* optimize for speed */
1125 
1126     for (cp = PromptBuf; *cp; cp++) {	/* do prompt */
1127 	if (*cp & LITERAL)
1128 	    continue;
1129 	c = *cp & CHAR;		/* extra speed plus strip the inverse */
1130 	h++;			/* all chars at least this long */
1131 
1132 	/* from wolman%crltrx.DEC@decwrl.dec.com (Alec Wolman) */
1133 	/* lets handle newline as part of the prompt */
1134 
1135 	if (c == '\n') {
1136 	    h = 0;
1137 	    v++;
1138 	}
1139 	else {
1140 	    if (c == '\t') {	/* if a tab, to next tab stop */
1141 		while (h & 07) {
1142 		    h++;
1143 		}
1144 	    }
1145 	    else if (Iscntrl(c)) {	/* if control char */
1146 		h++;
1147 		if (h > th) {	/* if overflow, compensate */
1148 		    h = 1;
1149 		    v++;
1150 		}
1151 	    }
1152 	    else if (!Isprint(c)) {
1153 		h += 3;
1154 		if (h > th) {	/* if overflow, compensate */
1155 		    h = h - th;
1156 		    v++;
1157 		}
1158 	    }
1159 	}
1160 
1161 	if (h >= th) {		/* check, extra long tabs picked up here also */
1162 	    h = 0;
1163 	    v++;
1164 	}
1165     }
1166 
1167     for (cp = InputBuf; cp < Cursor; cp++) {	/* do input buffer to Cursor */
1168 	c = *cp & CHAR;		/* extra speed plus strip the inverse */
1169 	h++;			/* all chars at least this long */
1170 
1171 	if (c == '\n') {	/* handle newline in data part too */
1172 	    h = 0;
1173 	    v++;
1174 	}
1175 	else {
1176 	    if (c == '\t') {	/* if a tab, to next tab stop */
1177 		while (h & 07) {
1178 		    h++;
1179 		}
1180 	    }
1181 	    else if (Iscntrl(c)) {	/* if control char */
1182 		h++;
1183 		if (h > th) {	/* if overflow, compensate */
1184 		    h = 1;
1185 		    v++;
1186 		}
1187 	    }
1188 	    else if (!Isprint(c)) {
1189 		h += 3;
1190 		if (h > th) {	/* if overflow, compensate */
1191 		    h = h - th;
1192 		    v++;
1193 		}
1194 	    }
1195 	}
1196 
1197 	if (h >= th) {		/* check, extra long tabs picked up here also */
1198 	    h = 0;
1199 	    v++;
1200 	}
1201     }
1202 
1203     /* now go there */
1204     MoveToLine(v);
1205     MoveToChar(h);
1206     flush();
1207 }
1208 
1209 #ifndef WINTT_NATIVE
1210 static void
1211 PutPlusOne(c)
1212     int    c;
1213 {
1214     (void) putraw(c);
1215     Display[CursorV][CursorH++] = (Char) c;
1216     if (CursorH >= TermH) {	/* if we must overflow */
1217 	CursorH = 0;
1218 	CursorV++;
1219 	OldvcV++;
1220 	if (T_Margin & MARGIN_AUTO) {
1221 	    if (T_Margin & MARGIN_MAGIC) {
1222 		(void) putraw(' ');
1223 		(void) putraw('\b');
1224 	    }
1225 	}
1226 	else {
1227 	    (void) putraw('\r');
1228 	    (void) putraw('\n');
1229 	}
1230     }
1231 }
1232 #endif
1233 
1234 void
1235 RefPlusOne()
1236 {				/* we added just one char, handle it fast.
1237 				 * assumes that screen cursor == real cursor */
1238     register Char c, mc;
1239 
1240     c = Cursor[-1] & CHAR;	/* the char we just added */
1241 
1242     if (c == '\t' || Cursor != LastChar) {
1243 	Refresh();		/* too hard to handle */
1244 	return;
1245     }
1246 
1247     if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1248 	Refresh();		/* clear out rprompt if less than one char gap*/
1249 	return;
1250     }				/* else (only do at end of line, no TAB) */
1251 
1252     if (Iscntrl(c)) {		/* if control char, do caret */
1253 #ifdef IS_ASCII
1254 	mc = (c == '\177') ? '?' : (c | 0100);
1255 	PutPlusOne('^');
1256 	PutPlusOne(mc);
1257 #else
1258 	if (_toascii[c] == '\177' || Isupper(_toebcdic[_toascii[c]|0100])
1259 		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL)
1260 	{
1261 	    mc = (_toascii[c] == '\177') ? '?' : _toebcdic[_toascii[c]|0100];
1262 	    PutPlusOne('^');
1263 	    PutPlusOne(mc);
1264 	}
1265 	else
1266 	{
1267 	    PutPlusOne('\\');
1268 	    PutPlusOne(((c >> 6) & 7) + '0');
1269 	    PutPlusOne(((c >> 3) & 7) + '0');
1270 	    PutPlusOne((c & 7) + '0');
1271 	}
1272 #endif
1273     }
1274     else if (Isprint(c)) {	/* normal char */
1275 	PutPlusOne(c);
1276     }
1277 #ifdef KANJI
1278     else if (
1279 #ifdef DSPMBYTE
1280 	     _enable_mbdisp &&
1281 #endif
1282 	     !adrof(STRnokanji)) {
1283 	PutPlusOne(c);
1284     }
1285 #endif
1286     else {
1287 	PutPlusOne('\\');
1288 	PutPlusOne(((c >> 6) & 7) + '0');
1289 	PutPlusOne(((c >> 3) & 7) + '0');
1290 	PutPlusOne((c & 7) + '0');
1291     }
1292     flush();
1293 }
1294 
1295 /* clear the screen buffers so that new new prompt starts fresh. */
1296 
1297 void
1298 ClearDisp()
1299 {
1300     register int i;
1301 
1302     CursorV = 0;		/* clear the display buffer */
1303     CursorH = 0;
1304     for (i = 0; i < TermV; i++)
1305 	(void) memset(Display[i], 0, TermH * sizeof(Display[0][0]));
1306     OldvcV = 0;
1307 }
1308 
1309 void
1310 ClearLines()
1311 {				/* Make sure all lines are *really* blank */
1312     register int i;
1313 
1314     if (T_CanCEOL) {
1315 	/*
1316 	 * Clear the lines from the bottom up so that if we try moving
1317 	 * the cursor down by writing the character that is at the end
1318 	 * of the screen line, we won't rewrite a character that shouldn't
1319 	 * be there.
1320 	 */
1321 	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
1322 	    MoveToLine(i);
1323 	    MoveToChar(0);
1324 	    ClearEOL(TermH);
1325 	}
1326     }
1327     else {
1328 	MoveToLine(OldvcV);	/* go to last line */
1329 	(void) putraw('\r');	/* go to BOL */
1330 	(void) putraw('\n');	/* go to new line */
1331     }
1332 }
1333