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