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