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