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