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