refresh.c revision 1.29
1/*	$NetBSD: refresh.c,v 1.29 2009/02/15 21:55:23 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.29 2009/02/15 21:55:23 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", 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
481	/*
482         * find first diff
483         */
484	for (o = old, n = new; *o && (*o == *n); o++, n++)
485		continue;
486	ofd = o;
487	nfd = n;
488
489	/*
490         * Find the end of both old and new
491         */
492	while (*o)
493		o++;
494	/*
495         * Remove any trailing blanks off of the end, being careful not to
496         * back up past the beginning.
497         */
498	while (ofd < o) {
499		if (o[-1] != ' ')
500			break;
501		o--;
502	}
503	oe = o;
504	*oe = '\0';
505
506	while (*n)
507		n++;
508
509	/* remove blanks from end of new */
510	while (nfd < n) {
511		if (n[-1] != ' ')
512			break;
513		n--;
514	}
515	ne = n;
516	*ne = '\0';
517
518	/*
519         * if no diff, continue to next line of redraw
520         */
521	if (*ofd == '\0' && *nfd == '\0') {
522		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
523		return;
524	}
525	/*
526         * find last same pointer
527         */
528	while ((o > ofd) && (n > nfd) && (*--o == *--n))
529		continue;
530	ols = ++o;
531	nls = ++n;
532
533	/*
534         * find same begining and same end
535         */
536	osb = ols;
537	nsb = nls;
538	ose = ols;
539	nse = nls;
540
541	/*
542         * case 1: insert: scan from nfd to nls looking for *ofd
543         */
544	if (*ofd) {
545		for (c = *ofd, n = nfd; n < nls; n++) {
546			if (c == *n) {
547				for (o = ofd, p = n;
548				    p < nls && o < ols && *o == *p;
549				    o++, p++)
550					continue;
551				/*
552				 * if the new match is longer and it's worth
553				 * keeping, then we take it
554				 */
555				if (((nse - nsb) < (p - n)) &&
556				    (2 * (p - n) > n - nfd)) {
557					nsb = n;
558					nse = p;
559					osb = ofd;
560					ose = o;
561				}
562			}
563		}
564	}
565	/*
566         * case 2: delete: scan from ofd to ols looking for *nfd
567         */
568	if (*nfd) {
569		for (c = *nfd, o = ofd; o < ols; o++) {
570			if (c == *o) {
571				for (n = nfd, p = o;
572				    p < ols && n < nls && *p == *n;
573				    p++, n++)
574					continue;
575				/*
576				 * if the new match is longer and it's worth
577				 * keeping, then we take it
578				 */
579				if (((ose - osb) < (p - o)) &&
580				    (2 * (p - o) > o - ofd)) {
581					nsb = nfd;
582					nse = n;
583					osb = o;
584					ose = p;
585				}
586			}
587		}
588	}
589	/*
590         * Pragmatics I: If old trailing whitespace or not enough characters to
591         * save to be worth it, then don't save the last same info.
592         */
593	if ((oe - ols) < MIN_END_KEEP) {
594		ols = oe;
595		nls = ne;
596	}
597	/*
598         * Pragmatics II: if the terminal isn't smart enough, make the data
599         * dumber so the smart update doesn't try anything fancy
600         */
601
602	/*
603         * fx is the number of characters we need to insert/delete: in the
604         * beginning to bring the two same begins together
605         */
606	fx = (int)((nsb - nfd) - (osb - ofd));
607	/*
608         * sx is the number of characters we need to insert/delete: in the
609         * end to bring the two same last parts together
610         */
611	sx = (int)((nls - nse) - (ols - ose));
612
613	if (!EL_CAN_INSERT) {
614		if (fx > 0) {
615			osb = ols;
616			ose = ols;
617			nsb = nls;
618			nse = nls;
619		}
620		if (sx > 0) {
621			ols = oe;
622			nls = ne;
623		}
624		if ((ols - ofd) < (nls - nfd)) {
625			ols = oe;
626			nls = ne;
627		}
628	}
629	if (!EL_CAN_DELETE) {
630		if (fx < 0) {
631			osb = ols;
632			ose = ols;
633			nsb = nls;
634			nse = nls;
635		}
636		if (sx < 0) {
637			ols = oe;
638			nls = ne;
639		}
640		if ((ols - ofd) > (nls - nfd)) {
641			ols = oe;
642			nls = ne;
643		}
644	}
645	/*
646         * Pragmatics III: make sure the middle shifted pointers are correct if
647         * they don't point to anything (we may have moved ols or nls).
648         */
649	/* if the change isn't worth it, don't bother */
650	/* was: if (osb == ose) */
651	if ((ose - osb) < MIN_END_KEEP) {
652		osb = ols;
653		ose = ols;
654		nsb = nls;
655		nse = nls;
656	}
657	/*
658         * Now that we are done with pragmatics we recompute fx, sx
659         */
660	fx = (int)((nsb - nfd) - (osb - ofd));
661	sx = (int)((nls - nse) - (ols - ose));
662
663	ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
664	ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
665		ofd - old, osb - old, ose - old, ols - old, oe - old));
666	ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
667		nfd - new, nsb - new, nse - new, nls - new, ne - new));
668	ELRE_DEBUG(1, (__F,
669		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
670	ELRE_DEBUG(1, (__F,
671		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
672#ifdef DEBUG_REFRESH
673	re_printstr(el, "old- oe", old, oe);
674	re_printstr(el, "new- ne", new, ne);
675	re_printstr(el, "old-ofd", old, ofd);
676	re_printstr(el, "new-nfd", new, nfd);
677	re_printstr(el, "ofd-osb", ofd, osb);
678	re_printstr(el, "nfd-nsb", nfd, nsb);
679	re_printstr(el, "osb-ose", osb, ose);
680	re_printstr(el, "nsb-nse", nsb, nse);
681	re_printstr(el, "ose-ols", ose, ols);
682	re_printstr(el, "nse-nls", nse, nls);
683	re_printstr(el, "ols- oe", ols, oe);
684	re_printstr(el, "nls- ne", nls, ne);
685#endif /* DEBUG_REFRESH */
686
687	/*
688         * el_cursor.v to this line i MUST be in this routine so that if we
689         * don't have to change the line, we don't move to it. el_cursor.h to
690         * first diff char
691         */
692	term_move_to_line(el, i);
693
694	/*
695         * at this point we have something like this:
696         *
697         * /old                  /ofd    /osb               /ose    /ols     /oe
698         * v.....................v       v..................v       v........v
699         * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
700         * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
701         * ^.....................^     ^..................^       ^........^
702         * \new                  \nfd  \nsb               \nse     \nls    \ne
703         *
704         * fx is the difference in length between the chars between nfd and
705         * nsb, and the chars between ofd and osb, and is thus the number of
706         * characters to delete if < 0 (new is shorter than old, as above),
707         * or insert (new is longer than short).
708         *
709         * sx is the same for the second differences.
710         */
711
712	/*
713         * if we have a net insert on the first difference, AND inserting the
714         * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
715         * character (which is ne if nls != ne, otherwise is nse) off the edge
716	 * of the screen (el->el_term.t_size.h) else we do the deletes first
717	 * so that we keep everything we need to.
718         */
719
720	/*
721         * if the last same is the same like the end, there is no last same
722         * part, otherwise we want to keep the last same part set p to the
723         * last useful old character
724         */
725	p = (ols != oe) ? oe : ose;
726
727	/*
728         * if (There is a diffence in the beginning) && (we need to insert
729         *   characters) && (the number of characters to insert is less than
730         *   the term width)
731	 *	We need to do an insert!
732	 * else if (we need to delete characters)
733	 *	We need to delete characters!
734	 * else
735	 *	No insert or delete
736         */
737	if ((nsb != nfd) && fx > 0 &&
738	    ((p - old) + fx <= el->el_term.t_size.h)) {
739		ELRE_DEBUG(1,
740		    (__F, "first diff insert at %d...\r\n", nfd - new));
741		/*
742		 * Move to the first char to insert, where the first diff is.
743		 */
744		term_move_to_char(el, (int)(nfd - new));
745		/*
746		 * Check if we have stuff to keep at end
747		 */
748		if (nsb != ne) {
749			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
750			/*
751		         * insert fx chars of new starting at nfd
752		         */
753			if (fx > 0) {
754				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
755				"ERROR: cannot insert in early first diff\n"));
756				term_insertwrite(el, nfd, fx);
757				re_insert(el, old, (int)(ofd - old),
758				    el->el_term.t_size.h, nfd, fx);
759			}
760			/*
761		         * write (nsb-nfd) - fx chars of new starting at
762		         * (nfd + fx)
763			 */
764			term_overwrite(el, (nfd + fx),
765			    (int)((nsb - nfd) - fx));
766			re__strncopy(ofd + fx, nfd + fx,
767			    (size_t) ((nsb - nfd) - fx));
768		} else {
769			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
770			term_overwrite(el, nfd, (int)(nsb - nfd));
771			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
772			/*
773		         * Done
774		         */
775			return;
776		}
777	} else if (fx < 0) {
778		ELRE_DEBUG(1,
779		    (__F, "first diff delete at %d...\r\n", ofd - old));
780		/*
781		 * move to the first char to delete where the first diff is
782		 */
783		term_move_to_char(el, (int)(ofd - old));
784		/*
785		 * Check if we have stuff to save
786		 */
787		if (osb != oe) {
788			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
789			/*
790		         * fx is less than zero *always* here but we check
791		         * for code symmetry
792		         */
793			if (fx < 0) {
794				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
795				    "ERROR: cannot delete in first diff\n"));
796				term_deletechars(el, -fx);
797				re_delete(el, old, (int)(ofd - old),
798				    el->el_term.t_size.h, -fx);
799			}
800			/*
801		         * write (nsb-nfd) chars of new starting at nfd
802		         */
803			term_overwrite(el, nfd, (int)(nsb - nfd));
804			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
805
806		} else {
807			ELRE_DEBUG(1, (__F,
808			    "but with nothing left to save\r\n"));
809			/*
810		         * write (nsb-nfd) chars of new starting at nfd
811		         */
812			term_overwrite(el, nfd, (int)(nsb - nfd));
813			re_clear_eol(el, fx, sx,
814			    (int)((oe - old) - (ne - new)));
815			/*
816		         * Done
817		         */
818			return;
819		}
820	} else
821		fx = 0;
822
823	if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
824		ELRE_DEBUG(1, (__F,
825		    "second diff delete at %d...\r\n", (ose - old) + fx));
826		/*
827		 * Check if we have stuff to delete
828		 */
829		/*
830		 * fx is the number of characters inserted (+) or deleted (-)
831		 */
832
833		term_move_to_char(el, (int)((ose - old) + fx));
834		/*
835		 * Check if we have stuff to save
836		 */
837		if (ols != oe) {
838			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
839			/*
840		         * Again a duplicate test.
841		         */
842			if (sx < 0) {
843				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
844				    "ERROR: cannot delete in second diff\n"));
845				term_deletechars(el, -sx);
846			}
847			/*
848		         * write (nls-nse) chars of new starting at nse
849		         */
850			term_overwrite(el, nse, (int)(nls - nse));
851		} else {
852			ELRE_DEBUG(1, (__F,
853			    "but with nothing left to save\r\n"));
854			term_overwrite(el, nse, (int)(nls - nse));
855			re_clear_eol(el, fx, sx,
856			    (int)((oe - old) - (ne - new)));
857		}
858	}
859	/*
860         * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
861         */
862	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
863		ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
864		    nfd - new));
865
866		term_move_to_char(el, (int)(nfd - new));
867		/*
868		 * Check if we have stuff to keep at the end
869		 */
870		if (nsb != ne) {
871			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
872			/*
873		         * We have to recalculate fx here because we set it
874		         * to zero above as a flag saying that we hadn't done
875		         * an early first insert.
876		         */
877			fx = (int)((nsb - nfd) - (osb - ofd));
878			if (fx > 0) {
879				/*
880				 * insert fx chars of new starting at nfd
881				 */
882				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
883				 "ERROR: cannot insert in late first diff\n"));
884				term_insertwrite(el, nfd, fx);
885				re_insert(el, old, (int)(ofd - old),
886				    el->el_term.t_size.h, nfd, fx);
887			}
888			/*
889		         * write (nsb-nfd) - fx chars of new starting at
890		         * (nfd + fx)
891			 */
892			term_overwrite(el, (nfd + fx),
893			    (int)((nsb - nfd) - fx));
894			re__strncopy(ofd + fx, nfd + fx,
895			    (size_t) ((nsb - nfd) - fx));
896		} else {
897			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
898			term_overwrite(el, nfd, (int)(nsb - nfd));
899			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
900		}
901	}
902	/*
903         * line is now NEW up to nse
904         */
905	if (sx >= 0) {
906		ELRE_DEBUG(1, (__F,
907		    "second diff insert at %d...\r\n", (int)(nse - new)));
908		term_move_to_char(el, (int)(nse - new));
909		if (ols != oe) {
910			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
911			if (sx > 0) {
912				/* insert sx chars of new starting at nse */
913				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
914				    "ERROR: cannot insert in second diff\n"));
915				term_insertwrite(el, nse, sx);
916			}
917			/*
918		         * write (nls-nse) - sx chars of new starting at
919			 * (nse + sx)
920		         */
921			term_overwrite(el, (nse + sx),
922			    (int)((nls - nse) - sx));
923		} else {
924			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
925			term_overwrite(el, nse, (int)(nls - nse));
926
927			/*
928	                 * No need to do a clear-to-end here because we were
929	                 * doing a second insert, so we will have over
930	                 * written all of the old string.
931		         */
932		}
933	}
934	ELRE_DEBUG(1, (__F, "done.\r\n"));
935}
936
937
938/* re__copy_and_pad():
939 *	Copy string and pad with spaces
940 */
941private void
942re__copy_and_pad(char *dst, const char *src, size_t width)
943{
944	size_t i;
945
946	for (i = 0; i < width; i++) {
947		if (*src == '\0')
948			break;
949		*dst++ = *src++;
950	}
951
952	for (; i < width; i++)
953		*dst++ = ' ';
954
955	*dst = '\0';
956}
957
958
959/* re_refresh_cursor():
960 *	Move to the new cursor position
961 */
962protected void
963re_refresh_cursor(EditLine *el)
964{
965	char *cp, c;
966	int h, v, th;
967
968	if (el->el_line.cursor >= el->el_line.lastchar) {
969		if (el->el_map.current == el->el_map.alt
970		    && el->el_line.lastchar != el->el_line.buffer)
971			el->el_line.cursor = el->el_line.lastchar - 1;
972		else
973			el->el_line.cursor = el->el_line.lastchar;
974	}
975
976	/* first we must find where the cursor is... */
977	h = el->el_prompt.p_pos.h;
978	v = el->el_prompt.p_pos.v;
979	th = el->el_term.t_size.h;	/* optimize for speed */
980
981	/* do input buffer to el->el_line.cursor */
982	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
983		c = *cp;
984		h++;		/* all chars at least this long */
985
986		if (c == '\n') {/* handle newline in data part too */
987			h = 0;
988			v++;
989		} else {
990			if (c == '\t') {	/* if a tab, to next tab stop */
991				while (h & 07) {
992					h++;
993				}
994			} else if (iscntrl((unsigned char) c)) {
995						/* if control char */
996				h++;
997				if (h > th) {	/* if overflow, compensate */
998					h = 1;
999					v++;
1000				}
1001			} else if (!isprint((unsigned char) c)) {
1002				h += 3;
1003				if (h > th) {	/* if overflow, compensate */
1004					h = h - th;
1005					v++;
1006				}
1007			}
1008		}
1009
1010		if (h >= th) {	/* check, extra long tabs picked up here also */
1011			h = 0;
1012			v++;
1013		}
1014	}
1015
1016	/* now go there */
1017	term_move_to_line(el, v);
1018	term_move_to_char(el, h);
1019	term__flush(el);
1020}
1021
1022
1023/* re_fastputc():
1024 *	Add a character fast.
1025 */
1026private void
1027re_fastputc(EditLine *el, int c)
1028{
1029
1030	term__putc(el, c);
1031	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1032	if (el->el_cursor.h >= el->el_term.t_size.h) {
1033		/* if we must overflow */
1034		el->el_cursor.h = 0;
1035
1036		/*
1037		 * If we would overflow (input is longer than terminal size),
1038		 * emulate scroll by dropping first line and shuffling the rest.
1039		 * We do this via pointer shuffling - it's safe in this case
1040		 * and we avoid memcpy().
1041		 */
1042		if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1043			int i, lins = el->el_term.t_size.v;
1044			char *firstline = el->el_display[0];
1045
1046			for(i=1; i < lins; i++)
1047				el->el_display[i-1] = el->el_display[i];
1048
1049			re__copy_and_pad(firstline, "", 0);
1050			el->el_display[i-1] = firstline;
1051		} else {
1052			el->el_cursor.v++;
1053			el->el_refresh.r_oldcv++;
1054		}
1055		if (EL_HAS_AUTO_MARGINS) {
1056			if (EL_HAS_MAGIC_MARGINS) {
1057				term__putc(el, ' ');
1058				term__putc(el, '\b');
1059			}
1060		} else {
1061			term__putc(el, '\r');
1062			term__putc(el, '\n');
1063		}
1064	}
1065}
1066
1067
1068/* re_fastaddc():
1069 *	we added just one char, handle it fast.
1070 *	Assumes that screen cursor == real cursor
1071 */
1072protected void
1073re_fastaddc(EditLine *el)
1074{
1075	char c;
1076	int rhdiff;
1077
1078	c = el->el_line.cursor[-1];
1079
1080	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1081		re_refresh(el);	/* too hard to handle */
1082		return;
1083	}
1084	rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1085	    el->el_rprompt.p_pos.h;
1086	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1087		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
1088		return;
1089	}			/* else (only do at end of line, no TAB) */
1090	if (iscntrl((unsigned char) c)) {	/* if control char, do caret */
1091		char mc = (c == '\177') ? '?' : (c | 0100);
1092		re_fastputc(el, '^');
1093		re_fastputc(el, mc);
1094	} else if (isprint((unsigned char) c)) {	/* normal char */
1095		re_fastputc(el, c);
1096	} else {
1097		re_fastputc(el, '\\');
1098		re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1099		re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1100		re_fastputc(el, (c & 7) + '0');
1101	}
1102	term__flush(el);
1103}
1104
1105
1106/* re_clear_display():
1107 *	clear the screen buffers so that new new prompt starts fresh.
1108 */
1109protected void
1110re_clear_display(EditLine *el)
1111{
1112	int i;
1113
1114	el->el_cursor.v = 0;
1115	el->el_cursor.h = 0;
1116	for (i = 0; i < el->el_term.t_size.v; i++)
1117		el->el_display[i][0] = '\0';
1118	el->el_refresh.r_oldcv = 0;
1119}
1120
1121
1122/* re_clear_lines():
1123 *	Make sure all lines are *really* blank
1124 */
1125protected void
1126re_clear_lines(EditLine *el)
1127{
1128
1129	if (EL_CAN_CEOL) {
1130		int i;
1131		term_move_to_char(el, 0);
1132		for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1133			/* for each line on the screen */
1134			term_move_to_line(el, i);
1135			term_clear_EOL(el, el->el_term.t_size.h);
1136		}
1137		term_move_to_line(el, 0);
1138	} else {
1139		term_move_to_line(el, el->el_refresh.r_oldcv);
1140					/* go to last line */
1141		term__putc(el, '\r');	/* go to BOL */
1142		term__putc(el, '\n');	/* go to new line */
1143	}
1144}
1145