refresh.c revision 1.11
1/*	$NetBSD: refresh.c,v 1.11 1999/11/13 11:32:12 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40#if !defined(lint) && !defined(SCCSID)
41#if 0
42static char sccsid[] = "@(#)refresh.c	8.1 (Berkeley) 6/4/93";
43#else
44__RCSID("$NetBSD: refresh.c,v 1.11 1999/11/13 11:32:12 lukem Exp $");
45#endif
46#endif /* not lint && not SCCSID */
47
48/*
49 * refresh.c: Lower level screen refreshing functions
50 */
51#include "sys.h"
52#include <stdio.h>
53#include <ctype.h>
54#include <unistd.h>
55#include <string.h>
56
57#include "el.h"
58
59private	void	re_addc 		__P((EditLine *, int));
60private	void	re_update_line 		__P((EditLine *, char *, char *, int));
61private	void	re_insert		__P((EditLine *, char *, int, int,
62					     char *, int));
63private	void	re_delete		__P((EditLine *, char *, int, int,
64					     int));
65private	void	re_fastputc		__P((EditLine *, int));
66
67private	void	re__strncopy		__P((char *, char *, size_t));
68private	void	re__copy_and_pad	__P((char *, char *, size_t));
69
70#ifdef DEBUG_REFRESH
71private	void	re_printstr		__P((EditLine *, char *, char *,
72					     char *));
73# define __F el->el_errfile
74# define ELRE_DEBUG(a, b, c)	do 				\
75				    if (a) {			\
76					(void) fprintf b;	\
77					c;			\
78				    }				\
79				while (0)
80/* re_printstr():
81 *	Print a string on the debugging pty
82 */
83private void
84re_printstr(el, str, f, t)
85    EditLine *el;
86    char *str;
87    char *f, *t;
88{
89    ELRE_DEBUG(1,(__F, "%s:\"", str),);
90    while (f < t)
91	ELRE_DEBUG(1,(__F, "%c", *f++ & 0177),);
92    ELRE_DEBUG(1,(__F, "\"\r\n"),);
93}
94#else
95# define ELRE_DEBUG(a, b, c)
96#endif
97
98
99/* re_addc():
100 *	Draw c, expanding tabs, control chars etc.
101 */
102private void
103re_addc(el, c)
104    EditLine *el;
105    int c;
106{
107    if (isprint(c)) {
108	re_putc(el, c);
109	return;
110    }
111    if (c == '\n') {			/* expand the newline	 */
112	re_putc(el, '\0');		/* assure end of line	 */
113	el->el_refresh.r_cursor.h = 0;	/* reset cursor pos	 */
114	el->el_refresh.r_cursor.v++;
115	return;
116    }
117    if (c == '\t') {		/* expand the tab 	 */
118	for (;;) {
119	    re_putc(el, ' ');
120	    if ((el->el_refresh.r_cursor.h & 07) == 0)
121		break;		/* go until tab stop	 */
122	}
123    }
124    else if (iscntrl(c)) {
125	re_putc(el, '^');
126	if (c == '\177')
127	    re_putc(el, '?');
128	else
129	    /* uncontrolify it; works only for iso8859-1 like sets */
130	    re_putc(el, (c | 0100));
131    }
132    else {
133	re_putc(el, '\\');
134	re_putc(el, (int)((((unsigned int)c >> 6) & 07) + '0'));
135	re_putc(el, (int)((((unsigned int)c >> 3) & 07) + '0'));
136	re_putc(el, (c & 07) + '0');
137    }
138} /* end re_addc */
139
140
141/* re_putc():
142 *	Draw the character given
143 */
144protected void
145re_putc(el, c)
146    EditLine *el;
147    int c;
148{
149    ELRE_DEBUG(1,(__F, "printing %3.3o '%c'\r\n", c, c),);
150
151    el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c;
152    el->el_refresh.r_cursor.h++;				/* advance to next place */
153    if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
154	el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0';
155						/* assure end of line */
156	el->el_refresh.r_cursor.h = 0;				/* reset it. */
157	el->el_refresh.r_cursor.v++;
158	ELRE_DEBUG(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
159		 (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
160		  el->el_refresh.r_cursor.v, el->el_term.t_size.v), abort());
161    }
162} /* end re_putc */
163
164/* re_refresh():
165 *	draws the new virtual screen image from the current input
166 *  	line, then goes line-by-line changing the real image to the new
167 *	virtual image. The routine to re-draw a line can be replaced
168 *	easily in hopes of a smarter one being placed there.
169 */
170protected void
171re_refresh(el)
172    EditLine *el;
173{
174    int		i, rhdiff;
175    char	*cp;
176    coord_t	cur;
177
178    ELRE_DEBUG(1,(__F, "el->el_line.buffer = :%s:\r\n", el->el_line.buffer),);
179
180    /* reset the Drawing cursor */
181    el->el_refresh.r_cursor.h = 0;
182    el->el_refresh.r_cursor.v = 0;
183
184    /* temporarily draw rprompt to calculate its size */
185    prompt_print(el, EL_RPROMPT);
186
187    /* reset the Drawing cursor */
188    el->el_refresh.r_cursor.h = 0;
189    el->el_refresh.r_cursor.v = 0;
190
191    cur.h = -1;			/* set flag in case I'm not set */
192    cur.v = 0;
193
194    prompt_print(el, EL_PROMPT);
195
196    /* draw the current input buffer */
197    for (cp = el->el_line.buffer; cp < el->el_line.lastchar; cp++) {
198	if (cp == el->el_line.cursor) {
199	    cur.h = el->el_refresh.r_cursor.h;	/* save for later */
200	    cur.v = el->el_refresh.r_cursor.v;
201	}
202	re_addc(el, (unsigned char) *cp);
203    }
204
205    if (cur.h == -1) {		/* if I haven't been set yet, I'm at the end */
206	cur.h = el->el_refresh.r_cursor.h;
207	cur.v = el->el_refresh.r_cursor.v;
208    }
209
210    rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h -
211	el->el_rprompt.p_pos.h;
212    if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
213	!el->el_refresh.r_cursor.v && rhdiff > 1) {
214				/*
215				 * have a right-hand side prompt that will fit
216				 * on the end of the first line with at least
217				 * one character gap to the input buffer.
218				 */
219	while (--rhdiff > 0)	/* pad out with spaces */
220	    re_putc(el, ' ');
221    	prompt_print(el, EL_RPROMPT);
222    } else {
223	el->el_rprompt.p_pos.h = 0;	/* flag "not using rprompt" */
224	el->el_rprompt.p_pos.v = 0;
225    }
226
227    /* must be done BEFORE the NUL is written */
228    el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
229    re_putc(el, '\0');		/* put NUL on end */
230
231    ELRE_DEBUG(1,(__F,
232	     "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
233	     el->el_term.t_size.h, el->el_refresh.r_cursor.h,
234	     el->el_refresh.r_cursor.v, el->el_vdisplay[0]),);
235
236    ELRE_DEBUG(1,(__F, "updating %d lines.\r\n", el->el_refresh.r_newcv),);
237    for (i = 0; i <= el->el_refresh.r_newcv; i++) {
238	/* NOTE THAT re_update_line MAY CHANGE el_display[i] */
239	re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
240
241	/*
242	 * Copy the new line to be the current one, and pad out with spaces
243	 * to the full width of the terminal so that if we try moving the
244	 * cursor by writing the character that is at the end of the
245	 * screen line, it won't be a NUL or some old leftover stuff.
246	 */
247	re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
248			(size_t)el->el_term.t_size.h);
249    }
250    ELRE_DEBUG(1,(__F,
251	 "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
252	 el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i),);
253
254    if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
255	for (; i <= el->el_refresh.r_oldcv; i++) {
256	    term_move_to_line(el, i);
257	    term_move_to_char(el, 0);
258	    term_clear_EOL(el, (int)strlen(el->el_display[i]));
259#ifdef DEBUG_REFRESH
260	    term_overwrite(el, "C\b", 2);
261#endif /* DEBUG_REFRESH */
262	    *el->el_display[i] = '\0';
263	}
264
265    el->el_refresh.r_oldcv = el->el_refresh.r_newcv;	/* set for next time */
266    ELRE_DEBUG(1,(__F,
267		"\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
268		el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
269		cur.h, cur.v),);
270    term_move_to_line(el, cur.v);		/* go to where the cursor is */
271    term_move_to_char(el, cur.h);
272} /* end re_refresh */
273
274
275/* re_goto_bottom():
276 *	 used to go to last used screen line
277 */
278protected void
279re_goto_bottom(el)
280    EditLine *el;
281{
282    term_move_to_line(el, el->el_refresh.r_oldcv);
283    term__putc('\r');
284    term__putc('\n');
285    re_clear_display(el);
286    term__flush();
287} /* end re_goto_bottom */
288
289
290/* re_insert():
291 *	insert num characters of s into d (in front of the character)
292 *	at dat, maximum length of d is dlen
293 */
294private void
295/*ARGSUSED*/
296re_insert(el, d, dat, dlen, s, num)
297    EditLine *el;
298    char *d;
299    int dat, dlen;
300    char *s;
301    int num;
302{
303    char *a, *b;
304
305    if (num <= 0)
306	return;
307    if (num > dlen - dat)
308	num = dlen - dat;
309
310    ELRE_DEBUG(1,(__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
311	    num, dat, dlen, d),);
312    ELRE_DEBUG(1,(__F, "s == \"%s\"n", s),);
313
314    /* open up the space for num chars */
315    if (num > 0) {
316	b = d + dlen - 1;
317	a = b - num;
318	while (a >= &d[dat])
319	    *b-- = *a--;
320	d[dlen] = '\0';		/* just in case */
321    }
322    ELRE_DEBUG(1,(__F,
323		"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
324		num, dat, dlen, d),);
325    ELRE_DEBUG(1,(__F, "s == \"%s\"n", s),);
326
327    /* copy the characters */
328    for (a = d + dat; (a < d + dlen) && (num > 0); num--)
329	*a++ = *s++;
330
331    ELRE_DEBUG(1,(__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
332	     num, dat, dlen, d, s),);
333    ELRE_DEBUG(1,(__F, "s == \"%s\"n", s),);
334} /* end re_insert */
335
336
337/* re_delete():
338 *	delete num characters d at dat, maximum length of d is dlen
339 */
340private void
341/*ARGSUSED*/
342re_delete(el, d, dat, dlen, num)
343    EditLine *el;
344    char *d;
345    int dat, dlen, num;
346{
347    char *a, *b;
348
349    if (num <= 0)
350	return;
351    if (dat + num >= dlen) {
352	d[dat] = '\0';
353	return;
354    }
355
356    ELRE_DEBUG(1,(__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
357	    num, dat, dlen, d),);
358
359    /* open up the space for num chars */
360    if (num > 0) {
361	b = d + dat;
362	a = b + num;
363	while (a < &d[dlen])
364	    *b++ = *a++;
365	d[dlen] = '\0';		/* just in case */
366    }
367    ELRE_DEBUG(1,(__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
368	    num, dat, dlen, d),);
369} /* end re_delete */
370
371
372/* re__strncopy():
373 *	Like strncpy without padding.
374 */
375private void
376re__strncopy(a, b, n)
377    char *a, *b;
378    size_t n;
379{
380    while (n-- && *b)
381	*a++ = *b++;
382} /* end re__strncopy */
383
384
385/* ****************************************************************
386    re_update_line() is based on finding the middle difference of each line
387    on the screen; vis:
388
389			     /old first difference
390	/beginning of line   |              /old last same       /old EOL
391	v		     v              v                    v
392old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
393new:	eddie> Oh, my little buggy says to me, as lurgid as
394	^		     ^        ^			   ^
395	\beginning of line   |        \new last same	   \new end of line
396			     \new first difference
397
398    all are character pointers for the sake of speed.  Special cases for
399    no differences, as well as for end of line additions must be handled.
400**************************************************************** */
401
402/* Minimum at which doing an insert it "worth it".  This should be about
403 * half the "cost" of going into insert mode, inserting a character, and
404 * going back out.  This should really be calculated from the termcap
405 * data...  For the moment, a good number for ANSI terminals.
406 */
407#define MIN_END_KEEP	4
408
409private void
410re_update_line(el, old, new, i)
411    EditLine *el;
412    char *old, *new;
413    int     i;
414{
415    char *o, *n, *p, c;
416    char   *ofd, *ols, *oe, *nfd, *nls, *ne;
417    char   *osb, *ose, *nsb, *nse;
418    int     fx, sx;
419
420    /*
421     * find first diff
422     */
423    for (o = old, n = new; *o && (*o == *n); o++, n++)
424	continue;
425    ofd = o;
426    nfd = n;
427
428    /*
429     * Find the end of both old and new
430     */
431    while (*o)
432	o++;
433    /*
434     * Remove any trailing blanks off of the end, being careful not to
435     * back up past the beginning.
436     */
437    while (ofd < o) {
438	if (o[-1] != ' ')
439	    break;
440	o--;
441    }
442    oe = o;
443    *oe = '\0';
444
445    while (*n)
446	n++;
447
448    /* remove blanks from end of new */
449    while (nfd < n) {
450	if (n[-1] != ' ')
451	    break;
452	n--;
453    }
454    ne = n;
455    *ne = '\0';
456
457    /*
458     * if no diff, continue to next line of redraw
459     */
460    if (*ofd == '\0' && *nfd == '\0') {
461	ELRE_DEBUG(1,(__F, "no difference.\r\n"),);
462	return;
463    }
464
465    /*
466     * find last same pointer
467     */
468    while ((o > ofd) && (n > nfd) && (*--o == *--n))
469	continue;
470    ols = ++o;
471    nls = ++n;
472
473    /*
474     * find same begining and same end
475     */
476    osb = ols;
477    nsb = nls;
478    ose = ols;
479    nse = nls;
480
481    /*
482     * case 1: insert: scan from nfd to nls looking for *ofd
483     */
484    if (*ofd) {
485	for (c = *ofd, n = nfd; n < nls; n++) {
486	    if (c == *n) {
487		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
488		    continue;
489		/*
490		 * if the new match is longer and it's worth keeping, then we
491		 * take it
492		 */
493		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
494		    nsb = n;
495		    nse = p;
496		    osb = ofd;
497		    ose = o;
498		}
499	    }
500	}
501    }
502
503    /*
504     * case 2: delete: scan from ofd to ols looking for *nfd
505     */
506    if (*nfd) {
507	for (c = *nfd, o = ofd; o < ols; o++) {
508	    if (c == *o) {
509		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
510		    continue;
511		/*
512		 * if the new match is longer and it's worth keeping, then we
513		 * take it
514		 */
515		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
516		    nsb = nfd;
517		    nse = n;
518		    osb = o;
519		    ose = p;
520		}
521	    }
522	}
523    }
524
525    /*
526     * Pragmatics I: If old trailing whitespace or not enough characters to
527     * save to be worth it, then don't save the last same info.
528     */
529    if ((oe - ols) < MIN_END_KEEP) {
530	ols = oe;
531	nls = ne;
532    }
533
534    /*
535     * Pragmatics II: if the terminal isn't smart enough, make the data dumber
536     * so the smart update doesn't try anything fancy
537     */
538
539    /*
540     * fx is the number of characters we need to insert/delete: in the
541     * beginning to bring the two same begins together
542     */
543    fx = (nsb - nfd) - (osb - ofd);
544    /*
545     * sx is the number of characters we need to insert/delete: in the end to
546     * bring the two same last parts together
547     */
548    sx = (nls - nse) - (ols - ose);
549
550    if (!EL_CAN_INSERT) {
551	if (fx > 0) {
552	    osb = ols;
553	    ose = ols;
554	    nsb = nls;
555	    nse = nls;
556	}
557	if (sx > 0) {
558	    ols = oe;
559	    nls = ne;
560	}
561	if ((ols - ofd) < (nls - nfd)) {
562	    ols = oe;
563	    nls = ne;
564	}
565    }
566    if (!EL_CAN_DELETE) {
567	if (fx < 0) {
568	    osb = ols;
569	    ose = ols;
570	    nsb = nls;
571	    nse = nls;
572	}
573	if (sx < 0) {
574	    ols = oe;
575	    nls = ne;
576	}
577	if ((ols - ofd) > (nls - nfd)) {
578	    ols = oe;
579	    nls = ne;
580	}
581    }
582
583    /*
584     * Pragmatics III: make sure the middle shifted pointers are correct if
585     * they don't point to anything (we may have moved ols or nls).
586     */
587    /* if the change isn't worth it, don't bother */
588    /* was: if (osb == ose) */
589    if ((ose - osb) < MIN_END_KEEP) {
590	osb = ols;
591	ose = ols;
592	nsb = nls;
593	nse = nls;
594    }
595
596    /*
597     * Now that we are done with pragmatics we recompute fx, sx
598     */
599    fx = (nsb - nfd) - (osb - ofd);
600    sx = (nls - nse) - (ols - ose);
601
602    ELRE_DEBUG(1,(__F, "\n"),);
603    ELRE_DEBUG(1,(__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
604	    ofd - old, osb - old, ose - old, ols - old, oe - old),);
605    ELRE_DEBUG(1,(__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
606	    nfd - new, nsb - new, nse - new, nls - new, ne - new),);
607    ELRE_DEBUG(1,(__F,
608		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"),);
609    ELRE_DEBUG(1,(__F,
610		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"),);
611#ifdef DEBUG_REFRESH
612    re_printstr(el, "old- oe", old, oe);
613    re_printstr(el, "new- ne", new, ne);
614    re_printstr(el, "old-ofd", old, ofd);
615    re_printstr(el, "new-nfd", new, nfd);
616    re_printstr(el, "ofd-osb", ofd, osb);
617    re_printstr(el, "nfd-nsb", nfd, nsb);
618    re_printstr(el, "osb-ose", osb, ose);
619    re_printstr(el, "nsb-nse", nsb, nse);
620    re_printstr(el, "ose-ols", ose, ols);
621    re_printstr(el, "nse-nls", nse, nls);
622    re_printstr(el, "ols- oe", ols, oe);
623    re_printstr(el, "nls- ne", nls, ne);
624#endif /* DEBUG_REFRESH */
625
626    /*
627     * el_cursor.v to this line i MUST be in this routine so that if we
628     * don't have to change the line, we don't move to it. el_cursor.h to first
629     * diff char
630     */
631    term_move_to_line(el, i);
632
633    /*
634     * at this point we have something like this:
635     *
636     * /old                  /ofd    /osb               /ose    /ols     /oe
637     * v.....................v       v..................v       v........v
638     * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
639     * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
640     * ^.....................^     ^..................^       ^........^
641     * \new                  \nfd  \nsb               \nse     \nls    \ne
642     *
643     * fx is the difference in length between the the chars between nfd and
644     * nsb, and the chars between ofd and osb, and is thus the number of
645     * characters to delete if < 0 (new is shorter than old, as above),
646     * or insert (new is longer than short).
647     *
648     * sx is the same for the second differences.
649     */
650
651    /*
652     * if we have a net insert on the first difference, AND inserting the net
653     * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
654     * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
655     * (el->el_term.t_size.h) else we do the deletes first so that we keep everything we need
656     * to.
657     */
658
659    /*
660     * if the last same is the same like the end, there is no last same part,
661     * otherwise we want to keep the last same part set p to the last useful
662     * old character
663     */
664    p = (ols != oe) ? oe : ose;
665
666    /*
667     * if (There is a diffence in the beginning) && (we need to insert
668     * characters) && (the number of characters to insert is less than the term
669     * width) We need to do an insert! else if (we need to delete characters)
670     * We need to delete characters! else No insert or delete
671     */
672    if ((nsb != nfd) && fx > 0 && ((p - old) + fx <= el->el_term.t_size.h)) {
673	ELRE_DEBUG(1,(__F, "first diff insert at %d...\r\n", nfd - new),);
674	/*
675	 * Move to the first char to insert, where the first diff is.
676	 */
677	term_move_to_char(el, nfd - new);
678	/*
679	 * Check if we have stuff to keep at end
680	 */
681	if (nsb != ne) {
682	    ELRE_DEBUG(1,(__F, "with stuff to keep at end\r\n"),);
683	    /*
684	     * insert fx chars of new starting at nfd
685	     */
686	    if (fx > 0) {
687		ELRE_DEBUG(!EL_CAN_INSERT,
688			 (__F, "ERROR: cannot insert in early first diff\n"),);
689		term_insertwrite(el, nfd, fx);
690		re_insert(el, old, ofd - old, el->el_term.t_size.h, nfd, fx);
691	    }
692	    /*
693	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
694	     */
695	    term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
696	    re__strncopy(ofd + fx, nfd + fx, (size_t)((nsb - nfd) - fx));
697	}
698	else {
699	    ELRE_DEBUG(1,(__F, "without anything to save\r\n"),);
700	    term_overwrite(el, nfd, (nsb - nfd));
701	    re__strncopy(ofd, nfd, (size_t)(nsb - nfd));
702	    /*
703	     * Done
704	     */
705	    return;
706	}
707    }
708    else if (fx < 0) {
709	ELRE_DEBUG(1,(__F, "first diff delete at %d...\r\n", ofd - old),);
710	/*
711	 * move to the first char to delete where the first diff is
712	 */
713	term_move_to_char(el, ofd - old);
714	/*
715	 * Check if we have stuff to save
716	 */
717	if (osb != oe) {
718	    ELRE_DEBUG(1,(__F, "with stuff to save at end\r\n"),);
719	    /*
720	     * fx is less than zero *always* here but we check for code
721	     * symmetry
722	     */
723	    if (fx < 0) {
724		ELRE_DEBUG(!EL_CAN_DELETE,
725			 (__F, "ERROR: cannot delete in first diff\n"),);
726		term_deletechars(el, -fx);
727		re_delete(el, old, ofd - old, el->el_term.t_size.h, -fx);
728	    }
729	    /*
730	     * write (nsb-nfd) chars of new starting at nfd
731	     */
732	    term_overwrite(el, nfd, (nsb - nfd));
733	    re__strncopy(ofd, nfd, (size_t)(nsb - nfd));
734
735	}
736	else {
737	    ELRE_DEBUG(1,(__F, "but with nothing left to save\r\n"),);
738	    /*
739	     * write (nsb-nfd) chars of new starting at nfd
740	     */
741	    term_overwrite(el, nfd, (nsb - nfd));
742	    ELRE_DEBUG(1,(__F, "cleareol %d\n", (oe - old) - (ne - new)),);
743	    term_clear_EOL(el, (oe - old) - (ne - new));
744	    /*
745	     * Done
746	     */
747	    return;
748	}
749    }
750    else
751	fx = 0;
752
753    if (sx < 0) {
754	ELRE_DEBUG(1,(__F, "second diff delete at %d...\r\n", (ose - old) + fx),);
755	/*
756	 * Check if we have stuff to delete
757	 */
758	/*
759	 * fx is the number of characters inserted (+) or deleted (-)
760	 */
761
762	term_move_to_char(el, (ose - old) + fx);
763	/*
764	 * Check if we have stuff to save
765	 */
766	if (ols != oe) {
767	    ELRE_DEBUG(1,(__F, "with stuff to save at end\r\n"),);
768	    /*
769	     * Again a duplicate test.
770	     */
771	    if (sx < 0) {
772		ELRE_DEBUG(!EL_CAN_DELETE,
773			 (__F, "ERROR: cannot delete in second diff\n"),);
774		term_deletechars(el, -sx);
775	    }
776
777	    /*
778	     * write (nls-nse) chars of new starting at nse
779	     */
780	    term_overwrite(el, nse, (nls - nse));
781	}
782	else {
783	    ELRE_DEBUG(1,(__F, "but with nothing left to save\r\n"),);
784	    term_overwrite(el, nse, (nls - nse));
785	    ELRE_DEBUG(1,(__F, "cleareol %d\n", (oe - old) - (ne - new)),);
786	    term_clear_EOL(el, (oe - old) - (ne - new));
787	}
788    }
789
790    /*
791     * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
792     */
793    if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
794	ELRE_DEBUG(1,(__F, "late first diff insert at %d...\r\n", nfd - new),);
795
796	term_move_to_char(el, nfd - new);
797	/*
798	 * Check if we have stuff to keep at the end
799	 */
800	if (nsb != ne) {
801	    ELRE_DEBUG(1,(__F, "with stuff to keep at end\r\n"),);
802	    /*
803	     * We have to recalculate fx here because we set it
804	     * to zero above as a flag saying that we hadn't done
805	     * an early first insert.
806	     */
807	    fx = (nsb - nfd) - (osb - ofd);
808	    if (fx > 0) {
809		/*
810		 * insert fx chars of new starting at nfd
811		 */
812		ELRE_DEBUG(!EL_CAN_INSERT,
813			 (__F, "ERROR: cannot insert in late first diff\n"),);
814		term_insertwrite(el, nfd, fx);
815		re_insert(el, old, ofd - old, el->el_term.t_size.h, nfd, fx);
816	    }
817
818	    /*
819	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
820	     */
821	    term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
822	    re__strncopy(ofd + fx, nfd + fx, (size_t)((nsb - nfd) - fx));
823	}
824	else {
825	    ELRE_DEBUG(1,(__F, "without anything to save\r\n"),);
826	    term_overwrite(el, nfd, (nsb - nfd));
827	    re__strncopy(ofd, nfd, (size_t)(nsb - nfd));
828	}
829    }
830
831    /*
832     * line is now NEW up to nse
833     */
834    if (sx >= 0) {
835	ELRE_DEBUG(1,(__F, "second diff insert at %d...\r\n", nse - new),);
836	term_move_to_char(el, nse - new);
837	if (ols != oe) {
838	    ELRE_DEBUG(1,(__F, "with stuff to keep at end\r\n"),);
839	    if (sx > 0) {
840		/* insert sx chars of new starting at nse */
841		ELRE_DEBUG(!EL_CAN_INSERT,
842		         (__F, "ERROR: cannot insert in second diff\n"),);
843		term_insertwrite(el, nse, sx);
844	    }
845
846	    /*
847	     * write (nls-nse) - sx chars of new starting at (nse + sx)
848	     */
849	    term_overwrite(el, nse + sx, (nls - nse) - sx);
850	}
851	else {
852	    ELRE_DEBUG(1,(__F, "without anything to save\r\n"),);
853	    term_overwrite(el, nse, (nls - nse));
854
855	    /*
856             * No need to do a clear-to-end here because we were doing
857	     * a second insert, so we will have over written all of the
858	     * old string.
859	     */
860	}
861    }
862    ELRE_DEBUG(1,(__F, "done.\r\n"),);
863} /* re_update_line */
864
865
866/* re__copy_and_pad():
867 *	Copy string and pad with spaces
868 */
869private void
870re__copy_and_pad(dst, src, width)
871    char *dst, *src;
872    size_t width;
873{
874    int i;
875
876    for (i = 0; i < width; i++) {
877	if (*src == '\0')
878	    break;
879	*dst++ = *src++;
880    }
881
882    while (i < width) {
883	*dst++ = ' ';
884	i++;
885    }
886    *dst = '\0';
887} /* end re__copy_and_pad */
888
889
890/* re_refresh_cursor():
891 *	Move to the new cursor position
892 */
893protected void
894re_refresh_cursor(el)
895    EditLine *el;
896{
897    char *cp, c;
898    int h, v, th;
899
900    /* first we must find where the cursor is... */
901    h  = el->el_prompt.p_pos.h;
902    v  = el->el_prompt.p_pos.v;
903    th = el->el_term.t_size.h;		/* optimize for speed 		*/
904
905    /* do input buffer to el->el_line.cursor */
906    for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
907	c = *cp;
908	h++;			/* all chars at least this long */
909
910	if (c == '\n') {	/* handle newline in data part too */
911	    h = 0;
912	    v++;
913	}
914	else {
915	    if (c == '\t') {	/* if a tab, to next tab stop */
916		while (h & 07) {
917		    h++;
918		}
919	    }
920	    else if (iscntrl((unsigned char) c)) {	/* if control char */
921		h++;
922		if (h > th) {	/* if overflow, compensate */
923		    h = 1;
924		    v++;
925		}
926	    }
927	    else if (!isprint((unsigned char) c)) {
928		h += 3;
929		if (h > th) {	/* if overflow, compensate */
930		    h = h - th;
931		    v++;
932		}
933	    }
934	}
935
936	if (h >= th) {		/* check, extra long tabs picked up here also */
937	    h = 0;
938	    v++;
939	}
940    }
941
942    /* now go there */
943    term_move_to_line(el, v);
944    term_move_to_char(el, h);
945    term__flush();
946} /* re_refresh_cursor */
947
948
949/* re_fastputc():
950 *	Add a character fast.
951 */
952private void
953re_fastputc(el, c)
954    EditLine *el;
955    int    c;
956{
957    term__putc(c);
958    el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
959    if (el->el_cursor.h >= el->el_term.t_size.h) {
960	/* if we must overflow */
961	el->el_cursor.h = 0;
962	el->el_cursor.v++;
963	el->el_refresh.r_oldcv++;
964	term__putc('\r');
965	term__putc('\n');
966    }
967} /* end re_fastputc */
968
969
970/* re_fastaddc():
971 *	we added just one char, handle it fast.
972 *	Assumes that screen cursor == real cursor
973 */
974protected void
975re_fastaddc(el)
976    EditLine *el;
977{
978    char c;
979    int rhdiff;
980
981    c = el->el_line.cursor[-1];
982
983    if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
984	re_refresh(el);		/* too hard to handle */
985	return;
986    }
987
988    rhdiff = el->el_term.t_size.h - el->el_cursor.h - el->el_rprompt.p_pos.h;
989    if (el->el_rprompt.p_pos.h && rhdiff < 3) {
990	re_refresh(el);		/* clear out rprompt if less than 1 char gap */
991	return;
992    }				/* else (only do at end of line, no TAB) */
993
994    if (iscntrl((unsigned char) c)) {		/* if control char, do caret */
995	char mc = (c == '\177') ? '?' : (c | 0100);
996	re_fastputc(el, '^');
997	re_fastputc(el, mc);
998    }
999    else if (isprint((unsigned char) c)) {	/* normal char */
1000	re_fastputc(el, c);
1001    }
1002    else {
1003	re_fastputc(el, '\\');
1004	re_fastputc(el, (int)((((unsigned int)c >> 6) & 7) + '0'));
1005	re_fastputc(el, (int)((((unsigned int)c >> 3) & 7) + '0'));
1006	re_fastputc(el, (c & 7) + '0');
1007    }
1008    term__flush();
1009} /* end re_fastaddc */
1010
1011
1012/* re_clear_display():
1013 *	clear the screen buffers so that new new prompt starts fresh.
1014 */
1015protected void
1016re_clear_display(el)
1017    EditLine *el;
1018{
1019    int i;
1020
1021    el->el_cursor.v = 0;
1022    el->el_cursor.h = 0;
1023    for (i = 0; i < el->el_term.t_size.v; i++)
1024	el->el_display[i][0] = '\0';
1025    el->el_refresh.r_oldcv = 0;
1026} /* end re_clear_display */
1027
1028
1029/* re_clear_lines():
1030 *	Make sure all lines are *really* blank
1031 */
1032protected void
1033re_clear_lines(el)
1034    EditLine *el;
1035{
1036    if (EL_CAN_CEOL) {
1037	int i;
1038	term_move_to_char(el, 0);
1039	for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1040	    /* for each line on the screen */
1041	    term_move_to_line(el, i);
1042	    term_clear_EOL(el, el->el_term.t_size.h);
1043	}
1044	term_move_to_line(el, 0);
1045    }
1046    else {
1047	term_move_to_line(el, el->el_refresh.r_oldcv);	/* go to last line */
1048	term__putc('\r');				/* go to BOL */
1049	term__putc('\n');				/* go to new line */
1050    }
1051} /* end re_clear_lines */
1052