ed.refresh.c revision 131962
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
35RCSID("$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
44Char   *litptr[256];
45static int vcursor_h, vcursor_v;
46static int rprompt_h, rprompt_v;
47
48static	void	Draw 			__P((int));
49static	void	Vdraw 			__P((int));
50static	void	RefreshPromptpart	__P((Char *));
51static	void	update_line 		__P((Char *, Char *, int));
52static	void	str_insert		__P((Char *, int, int, Char *, int));
53static	void	str_delete		__P((Char *, int, int, int));
54static	void	str_cp			__P((Char *, Char *, int));
55#ifndef WINNT_NATIVE
56static
57#else
58extern
59#endif
60	void    PutPlusOne      __P((int));
61static	void	cpy_pad_spaces		__P((Char *, Char *, int));
62#if defined(DSPMBYTE)
63static	Char 	*update_line_fix_mbyte_point __P((Char *, Char *, int));
64#endif
65#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
66static	void	dprintf			__P((char *, ...));
67#ifdef DEBUG_UPDATE
68static	void	dprintstr		__P((char *, Char *, Char *));
69
70static void
71dprintstr(str, f, t)
72char *str;
73Char *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 */
87static void
88#ifdef FUNCPROTO
89dprintf(char *fmt, ...)
90#else
91dprintf(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
122static void
123Draw(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
203static void
204Vdraw(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 */
235static void
236RefreshPromptpart(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
281static
282#endif
283int OldvcV = 0;
284
285void
286Refresh()
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
402GotoBottom()
403{				/* used to go to last used screen line */
404    MoveToLine(OldvcV);
405}
406
407#endif
408
409void
410PastBottom()
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 */
422static void
423str_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 */
468static void
469str_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
501static void
502str_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 */
512static Char *
513update_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
539old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
540new:	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
556static void			/* could be changed to make it smarter */
557update_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
1095static void
1096cpy_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
1115void
1116RefCursor()
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
1210static void
1211PutPlusOne(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
1234void
1235RefPlusOne()
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
1297void
1298ClearDisp()
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
1309void
1310ClearLines()
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