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