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