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