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