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