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