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