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