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