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