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