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