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