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