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