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