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