ed.refresh.c revision 100616
1/* $Header: /src/pub/tcsh/ed.refresh.c,v 3.29 2002/03/08 17:36:45 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.29 2002/03/08 17:36:45 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));
55static	void	PutPlusOne		__P((int));
56static	void	cpy_pad_spaces		__P((Char *, Char *, int));
57#if defined(DSPMBYTE)
58static	Char 	*update_line_fix_mbyte_point __P((Char *, Char *, int));
59#endif
60#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
61static	void	dprintf			__P((char *, ...));
62#ifdef DEBUG_UPDATE
63static	void	dprintstr		__P((char *, Char *, Char *));
64
65static void
66dprintstr(str, f, t)
67char *str;
68Char *f, *t;
69{
70    dprintf("%s:\"", str);
71    while (f < t)
72	dprintf("%c", *f++ & ASCII);
73    dprintf("\"\r\n");
74}
75#endif /* DEBUG_UPDATE */
76
77/* dprintf():
78 *	Print to $DEBUGTTY, so that we can test editing on one pty, and
79 *      print debugging stuff on another. Don't interrupt the shell while
80 *	debugging cause you'll mangle up the file descriptors!
81 */
82static void
83#ifdef FUNCPROTO
84dprintf(char *fmt, ...)
85#else
86dprintf(va_list)
87    va_dcl
88#endif /* __STDC__ */
89{
90    static int fd = -1;
91    char *dtty;
92
93    if ((dtty = getenv("DEBUGTTY"))) {
94	int o;
95	va_list va;
96#ifdef FUNCPROTO
97	va_start(va, fmt);
98#else
99	char *fmt;
100	va_start(va);
101	fmt = va_arg(va, char *);
102#endif /* __STDC__ */
103
104	if (fd == -1)
105	    fd = open(dtty, O_RDWR);
106	o = SHOUT;
107	flush();
108	SHOUT = fd;
109	xvprintf(fmt, va);
110	va_end(va);
111	flush();
112	SHOUT = o;
113    }
114}
115#endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
116
117static void
118Draw(c)				/* draw c, expand tabs, ctl chars */
119    register int c;
120{
121    register Char ch = c & CHAR;
122
123    if (Isprint(ch)) {
124	Vdraw(c);
125	return;
126    }
127    /* from wolman%crltrx.DEC@decwrl.dec.com (Alec Wolman) */
128    if (ch == '\n') {		/* expand the newline	 */
129	/*
130	 * Don't force a newline if Vdraw does it (i.e. we're at end of line)
131	 * - or we will get two newlines and possibly garbage in between
132	 */
133	int oldv = vcursor_v;
134
135	Vdraw('\0');		/* assure end of line	 */
136	if (oldv == vcursor_v) {
137	    vcursor_h = 0;	/* reset cursor pos	 */
138	    vcursor_v++;
139	}
140	return;
141    }
142    if (ch == '\t') {		/* expand the tab 	 */
143	for (;;) {
144	    Vdraw(' ');
145	    if ((vcursor_h & 07) == 0)
146		break;		/* go until tab stop	 */
147	}
148    }
149    else if (Iscntrl(ch)) {
150#ifdef IS_ASCII
151	Vdraw('^');
152	if (ch == CTL_ESC('\177')) {
153	    Vdraw('?');
154	}
155	else {
156	    /* uncontrolify it; works only for iso8859-1 like sets */
157	    Vdraw((c | 0100));
158#else
159	if (ch == CTL_ESC('\177')) {
160	    Vdraw('^');
161	    Vdraw('?');
162	}
163	else {
164	    if (Isupper(_toebcdic[_toascii[c]|0100])
165		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL)
166	    {
167		Vdraw('^');
168		Vdraw(_toebcdic[_toascii[c]|0100]);
169	    }
170	    else
171	    {
172		Vdraw('\\');
173		Vdraw(((c >> 6) & 7) + '0');
174		Vdraw(((c >> 3) & 7) + '0');
175		Vdraw((c & 7) + '0');
176	    }
177#endif
178	}
179    }
180#ifdef KANJI
181    else if (
182#ifdef DSPMBYTE
183	     _enable_mbdisp &&
184#endif
185	     !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
198static void
199Vdraw(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 */
230static void
231RefreshPromptpart(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 */
275static int OldvcV = 0;
276void
277Refresh()
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_NATIVE
352	flush();
353#endif /* WINNT_NATIVE */
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_NATIVE
383    flush();
384#endif /* WINNT_NATIVE */
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
393GotoBottom()
394{				/* used to go to last used screen line */
395    MoveToLine(OldvcV);
396}
397
398#endif
399
400void
401PastBottom()
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 */
413static void
414str_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 */
459static void
460str_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
492static void
493str_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 */
503static Char *
504update_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
530old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
531new:	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
547static void			/* could be changed to make it smarter */
548update_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_NATIVE
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_NATIVE*/
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_NATIVE
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_NATIVE*/
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
1086static void
1087cpy_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
1106void
1107RefCursor()
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
1200static void
1201PutPlusOne(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
1223void
1224RefPlusOne()
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#ifdef IS_ASCII
1243	mc = (c == '\177') ? '?' : (c | 0100);
1244	PutPlusOne('^');
1245	PutPlusOne(mc);
1246#else
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
1262    }
1263    else if (Isprint(c)) {	/* normal char */
1264	PutPlusOne(c);
1265    }
1266#ifdef KANJI
1267    else if (
1268#ifdef DSPMBYTE
1269	     _enable_mbdisp &&
1270#endif
1271	     !adrof(STRnokanji)) {
1272	PutPlusOne(c);
1273    }
1274#endif
1275    else {
1276	PutPlusOne('\\');
1277	PutPlusOne(((c >> 6) & 7) + '0');
1278	PutPlusOne(((c >> 3) & 7) + '0');
1279	PutPlusOne((c & 7) + '0');
1280    }
1281    flush();
1282}
1283
1284/* clear the screen buffers so that new new prompt starts fresh. */
1285
1286void
1287ClearDisp()
1288{
1289    register int i;
1290
1291    CursorV = 0;		/* clear the display buffer */
1292    CursorH = 0;
1293    for (i = 0; i < TermV; i++)
1294	(void) memset(Display[i], 0, TermH * sizeof(Display[0][0]));
1295    OldvcV = 0;
1296}
1297
1298void
1299ClearLines()
1300{				/* Make sure all lines are *really* blank */
1301    register int i;
1302
1303    if (T_CanCEOL) {
1304	/*
1305	 * Clear the lines from the bottom up so that if we try moving
1306	 * the cursor down by writing the character that is at the end
1307	 * of the screen line, we won't rewrite a character that shouldn't
1308	 * be there.
1309	 */
1310	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
1311	    MoveToLine(i);
1312	    MoveToChar(0);
1313	    ClearEOL(TermH);
1314	}
1315    }
1316    else {
1317	MoveToLine(OldvcV);	/* go to last line */
1318	(void) putraw('\r');	/* go to BOL */
1319	(void) putraw('\n');	/* go to new line */
1320    }
1321}
1322