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