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