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