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