refresh.c revision 1.57
1/*	$NetBSD: refresh.c,v 1.57 2020/03/30 06:54:37 ryo 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.57 2020/03/30 06:54:37 ryo 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		wchar_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, el->el_display[i], el->el_vdisplay[i], i);
338
339		/*
340		 * Copy the new line to be the current one, and pad out with
341		 * spaces to the full width of the terminal so that if we try
342		 * moving the cursor by writing the character that is at the
343		 * end of the screen line, it won't be a NUL or some old
344		 * leftover stuff.
345		 */
346		re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
347		    (size_t) el->el_terminal.t_size.h);
348	}
349	ELRE_DEBUG(1, (__F,
350	"\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
351	    el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
352
353	if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
354		for (; i <= el->el_refresh.r_oldcv; i++) {
355			terminal_move_to_line(el, i);
356			terminal_move_to_char(el, 0);
357                        /* This wcslen should be safe even with MB_FILL_CHARs */
358			terminal_clear_EOL(el, (int) wcslen(el->el_display[i]));
359#ifdef DEBUG_REFRESH
360			terminal_overwrite(el, L"C\b", 2);
361#endif /* DEBUG_REFRESH */
362			el->el_display[i][0] = '\0';
363		}
364
365	el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
366	ELRE_DEBUG(1, (__F,
367	    "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
368	    el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
369	    cur.h, cur.v));
370	terminal_move_to_line(el, cur.v);	/* go to where the cursor is */
371	terminal_move_to_char(el, cur.h);
372}
373
374
375/* re_goto_bottom():
376 *	 used to go to last used screen line
377 */
378libedit_private void
379re_goto_bottom(EditLine *el)
380{
381
382	terminal_move_to_line(el, el->el_refresh.r_oldcv);
383	terminal__putc(el, '\n');
384	re_clear_display(el);
385	terminal__flush(el);
386}
387
388
389/* re_insert():
390 *	insert num characters of s into d (in front of the character)
391 *	at dat, maximum length of d is dlen
392 */
393static void
394/*ARGSUSED*/
395re_insert(EditLine *el __attribute__((__unused__)),
396    wchar_t *d, int dat, int dlen, wchar_t *s, int num)
397{
398	wchar_t *a, *b;
399
400	if (num <= 0)
401		return;
402	if (num > dlen - dat)
403		num = dlen - dat;
404
405	ELRE_DEBUG(1,
406	    (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
407	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
408	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
409	    &el->el_scratch)));
410
411	/* open up the space for num chars */
412	if (num > 0) {
413		b = d + dlen - 1;
414		a = b - num;
415		while (a >= &d[dat])
416			*b-- = *a--;
417		d[dlen] = '\0';	/* just in case */
418	}
419
420	ELRE_DEBUG(1, (__F,
421		"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
422		num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
423	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
424		&el->el_scratch)));
425
426	/* copy the characters */
427	for (a = d + dat; (a < d + dlen) && (num > 0); num--)
428		*a++ = *s++;
429
430#ifdef notyet
431        /* ct_encode_string() uses a static buffer, so we can't conveniently
432         * encode both d & s here */
433	ELRE_DEBUG(1,
434	    (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
435	    num, dat, dlen, d, s));
436	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s));
437#endif
438}
439
440
441/* re_delete():
442 *	delete num characters d at dat, maximum length of d is dlen
443 */
444static void
445/*ARGSUSED*/
446re_delete(EditLine *el __attribute__((__unused__)),
447    wchar_t *d, int dat, int dlen, int num)
448{
449	wchar_t *a, *b;
450
451	if (num <= 0)
452		return;
453	if (dat + num >= dlen) {
454		d[dat] = '\0';
455		return;
456	}
457	ELRE_DEBUG(1,
458	    (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
459	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
460
461	/* open up the space for num chars */
462	if (num > 0) {
463		b = d + dat;
464		a = b + num;
465		while (a < &d[dlen])
466			*b++ = *a++;
467		d[dlen] = '\0';	/* just in case */
468	}
469	ELRE_DEBUG(1,
470	    (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
471	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
472}
473
474
475/* re__strncopy():
476 *	Like strncpy without padding.
477 */
478static void
479re__strncopy(wchar_t *a, wchar_t *b, size_t n)
480{
481
482	while (n-- && *b)
483		*a++ = *b++;
484}
485
486/* re_clear_eol():
487 *	Find the number of characters we need to clear till the end of line
488 *	in order to make sure that we have cleared the previous contents of
489 *	the line. fx and sx is the number of characters inserted or deleted
490 *	in the first or second diff, diff is the difference between the
491 *	number of characters between the new and old line.
492 */
493static void
494re_clear_eol(EditLine *el, int fx, int sx, int diff)
495{
496
497	ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
498	    sx, fx, diff));
499
500	if (fx < 0)
501		fx = -fx;
502	if (sx < 0)
503		sx = -sx;
504	if (fx > diff)
505		diff = fx;
506	if (sx > diff)
507		diff = sx;
508
509	ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
510	terminal_clear_EOL(el, diff);
511}
512
513/*****************************************************************
514    re_update_line() is based on finding the middle difference of each line
515    on the screen; vis:
516
517			     /old first difference
518	/beginning of line   |              /old last same       /old EOL
519	v		     v              v                    v
520old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
521new:	eddie> Oh, my little buggy says to me, as lurgid as
522	^		     ^        ^			   ^
523	\beginning of line   |        \new last same	   \new end of line
524			     \new first difference
525
526    all are character pointers for the sake of speed.  Special cases for
527    no differences, as well as for end of line additions must be handled.
528**************************************************************** */
529
530/* Minimum at which doing an insert it "worth it".  This should be about
531 * half the "cost" of going into insert mode, inserting a character, and
532 * going back out.  This should really be calculated from the termcap
533 * data...  For the moment, a good number for ANSI terminals.
534 */
535#define	MIN_END_KEEP	4
536
537static void
538re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i)
539{
540	wchar_t *o, *n, *p, c;
541	wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne;
542	wchar_t *osb, *ose, *nsb, *nse;
543	int fx, sx;
544	size_t len;
545
546	/*
547         * find first diff
548         */
549	for (o = old, n = new; *o && (*o == *n); o++, n++)
550		continue;
551	ofd = o;
552	nfd = n;
553
554	/*
555         * Find the end of both old and new
556         */
557	while (*o)
558		o++;
559	/*
560         * Remove any trailing blanks off of the end, being careful not to
561         * back up past the beginning.
562         */
563	while (ofd < o) {
564		if (o[-1] != ' ')
565			break;
566		o--;
567	}
568	oe = o;
569	*oe = '\0';
570
571	while (*n)
572		n++;
573
574	/* remove blanks from end of new */
575	while (nfd < n) {
576		if (n[-1] != ' ')
577			break;
578		n--;
579	}
580	ne = n;
581	*ne = '\0';
582
583	/*
584         * if no diff, continue to next line of redraw
585         */
586	if (*ofd == '\0' && *nfd == '\0') {
587		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
588		return;
589	}
590	/*
591         * find last same pointer
592         */
593	while ((o > ofd) && (n > nfd) && (*--o == *--n))
594		continue;
595	ols = ++o;
596	nls = ++n;
597
598	/*
599         * find same beginning and same end
600         */
601	osb = ols;
602	nsb = nls;
603	ose = ols;
604	nse = nls;
605
606	/*
607         * case 1: insert: scan from nfd to nls looking for *ofd
608         */
609	if (*ofd) {
610		for (c = *ofd, n = nfd; n < nls; n++) {
611			if (c == *n) {
612				for (o = ofd, p = n;
613				    p < nls && o < ols && *o == *p;
614				    o++, p++)
615					continue;
616				/*
617				 * if the new match is longer and it's worth
618				 * keeping, then we take it
619				 */
620				if (((nse - nsb) < (p - n)) &&
621				    (2 * (p - n) > n - nfd)) {
622					nsb = n;
623					nse = p;
624					osb = ofd;
625					ose = o;
626				}
627			}
628		}
629	}
630	/*
631         * case 2: delete: scan from ofd to ols looking for *nfd
632         */
633	if (*nfd) {
634		for (c = *nfd, o = ofd; o < ols; o++) {
635			if (c == *o) {
636				for (n = nfd, p = o;
637				    p < ols && n < nls && *p == *n;
638				    p++, n++)
639					continue;
640				/*
641				 * if the new match is longer and it's worth
642				 * keeping, then we take it
643				 */
644				if (((ose - osb) < (p - o)) &&
645				    (2 * (p - o) > o - ofd)) {
646					nsb = nfd;
647					nse = n;
648					osb = o;
649					ose = p;
650				}
651			}
652		}
653	}
654	/*
655         * Pragmatics I: If old trailing whitespace or not enough characters to
656         * save to be worth it, then don't save the last same info.
657         */
658	if ((oe - ols) < MIN_END_KEEP) {
659		ols = oe;
660		nls = ne;
661	}
662	/*
663         * Pragmatics II: if the terminal isn't smart enough, make the data
664         * dumber so the smart update doesn't try anything fancy
665         */
666
667	/*
668         * fx is the number of characters we need to insert/delete: in the
669         * beginning to bring the two same begins together
670         */
671	fx = (int)((nsb - nfd) - (osb - ofd));
672	/*
673         * sx is the number of characters we need to insert/delete: in the
674         * end to bring the two same last parts together
675         */
676	sx = (int)((nls - nse) - (ols - ose));
677
678	if (!EL_CAN_INSERT) {
679		if (fx > 0) {
680			osb = ols;
681			ose = ols;
682			nsb = nls;
683			nse = nls;
684		}
685		if (sx > 0) {
686			ols = oe;
687			nls = ne;
688		}
689		if ((ols - ofd) < (nls - nfd)) {
690			ols = oe;
691			nls = ne;
692		}
693	}
694	if (!EL_CAN_DELETE) {
695		if (fx < 0) {
696			osb = ols;
697			ose = ols;
698			nsb = nls;
699			nse = nls;
700		}
701		if (sx < 0) {
702			ols = oe;
703			nls = ne;
704		}
705		if ((ols - ofd) > (nls - nfd)) {
706			ols = oe;
707			nls = ne;
708		}
709	}
710	/*
711         * Pragmatics III: make sure the middle shifted pointers are correct if
712         * they don't point to anything (we may have moved ols or nls).
713         */
714	/* if the change isn't worth it, don't bother */
715	/* was: if (osb == ose) */
716	if ((ose - osb) < MIN_END_KEEP) {
717		osb = ols;
718		ose = ols;
719		nsb = nls;
720		nse = nls;
721	}
722	/*
723         * Now that we are done with pragmatics we recompute fx, sx
724         */
725	fx = (int)((nsb - nfd) - (osb - ofd));
726	sx = (int)((nls - nse) - (ols - ose));
727
728	ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
729	ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n",
730		ofd - old, osb - old, ose - old, ols - old, oe - old));
731	ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n",
732		nfd - new, nsb - new, nse - new, nls - new, ne - new));
733	ELRE_DEBUG(1, (__F,
734		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
735	ELRE_DEBUG(1, (__F,
736		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
737#ifdef DEBUG_REFRESH
738	re_printstr(el, "old- oe", old, oe);
739	re_printstr(el, "new- ne", new, ne);
740	re_printstr(el, "old-ofd", old, ofd);
741	re_printstr(el, "new-nfd", new, nfd);
742	re_printstr(el, "ofd-osb", ofd, osb);
743	re_printstr(el, "nfd-nsb", nfd, nsb);
744	re_printstr(el, "osb-ose", osb, ose);
745	re_printstr(el, "nsb-nse", nsb, nse);
746	re_printstr(el, "ose-ols", ose, ols);
747	re_printstr(el, "nse-nls", nse, nls);
748	re_printstr(el, "ols- oe", ols, oe);
749	re_printstr(el, "nls- ne", nls, ne);
750#endif /* DEBUG_REFRESH */
751
752	/*
753         * el_cursor.v to this line i MUST be in this routine so that if we
754         * don't have to change the line, we don't move to it. el_cursor.h to
755         * first diff char
756         */
757	terminal_move_to_line(el, i);
758
759	/*
760         * at this point we have something like this:
761         *
762         * /old                  /ofd    /osb               /ose    /ols     /oe
763         * v.....................v       v..................v       v........v
764         * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
765         * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
766         * ^.....................^     ^..................^       ^........^
767         * \new                  \nfd  \nsb               \nse     \nls    \ne
768         *
769         * fx is the difference in length between the chars between nfd and
770         * nsb, and the chars between ofd and osb, and is thus the number of
771         * characters to delete if < 0 (new is shorter than old, as above),
772         * or insert (new is longer than short).
773         *
774         * sx is the same for the second differences.
775         */
776
777	/*
778         * if we have a net insert on the first difference, AND inserting the
779         * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
780         * character (which is ne if nls != ne, otherwise is nse) off the edge
781	 * of the screen (el->el_terminal.t_size.h) else we do the deletes first
782	 * so that we keep everything we need to.
783         */
784
785	/*
786         * if the last same is the same like the end, there is no last same
787         * part, otherwise we want to keep the last same part set p to the
788         * last useful old character
789         */
790	p = (ols != oe) ? oe : ose;
791
792	/*
793         * if (There is a diffence in the beginning) && (we need to insert
794         *   characters) && (the number of characters to insert is less than
795         *   the term width)
796	 *	We need to do an insert!
797	 * else if (we need to delete characters)
798	 *	We need to delete characters!
799	 * else
800	 *	No insert or delete
801         */
802	if ((nsb != nfd) && fx > 0 &&
803	    ((p - old) + fx <= el->el_terminal.t_size.h)) {
804		ELRE_DEBUG(1,
805		    (__F, "first diff insert at %td...\r\n", nfd - new));
806		/*
807		 * Move to the first char to insert, where the first diff is.
808		 */
809		terminal_move_to_char(el, (int)(nfd - new));
810		/*
811		 * Check if we have stuff to keep at end
812		 */
813		if (nsb != ne) {
814			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
815			/*
816		         * insert fx chars of new starting at nfd
817		         */
818			if (fx > 0) {
819				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
820				"ERROR: cannot insert in early first diff\n"));
821				terminal_insertwrite(el, nfd, fx);
822				re_insert(el, old, (int)(ofd - old),
823				    el->el_terminal.t_size.h, nfd, fx);
824			}
825			/*
826		         * write (nsb-nfd) - fx chars of new starting at
827		         * (nfd + fx)
828			 */
829			len = (size_t) ((nsb - nfd) - fx);
830			terminal_overwrite(el, (nfd + fx), len);
831			re__strncopy(ofd + fx, nfd + fx, len);
832		} else {
833			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
834			len = (size_t)(nsb - nfd);
835			terminal_overwrite(el, nfd, len);
836			re__strncopy(ofd, nfd, len);
837			/*
838		         * Done
839		         */
840			return;
841		}
842	} else if (fx < 0) {
843		ELRE_DEBUG(1,
844		    (__F, "first diff delete at %td...\r\n", ofd - old));
845		/*
846		 * move to the first char to delete where the first diff is
847		 */
848		terminal_move_to_char(el, (int)(ofd - old));
849		/*
850		 * Check if we have stuff to save
851		 */
852		if (osb != oe) {
853			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
854			/*
855		         * fx is less than zero *always* here but we check
856		         * for code symmetry
857		         */
858			if (fx < 0) {
859				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
860				    "ERROR: cannot delete in first diff\n"));
861				terminal_deletechars(el, -fx);
862				re_delete(el, old, (int)(ofd - old),
863				    el->el_terminal.t_size.h, -fx);
864			}
865			/*
866		         * write (nsb-nfd) chars of new starting at nfd
867		         */
868			len = (size_t) (nsb - nfd);
869			terminal_overwrite(el, nfd, len);
870			re__strncopy(ofd, nfd, len);
871
872		} else {
873			ELRE_DEBUG(1, (__F,
874			    "but with nothing left to save\r\n"));
875			/*
876		         * write (nsb-nfd) chars of new starting at nfd
877		         */
878			terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
879			re_clear_eol(el, fx, sx,
880			    (int)((oe - old) - (ne - new)));
881			/*
882		         * Done
883		         */
884			return;
885		}
886	} else
887		fx = 0;
888
889	if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
890		ELRE_DEBUG(1, (__F,
891		    "second diff delete at %td...\r\n", (ose - old) + fx));
892		/*
893		 * Check if we have stuff to delete
894		 */
895		/*
896		 * fx is the number of characters inserted (+) or deleted (-)
897		 */
898
899		terminal_move_to_char(el, (int)((ose - old) + fx));
900		/*
901		 * Check if we have stuff to save
902		 */
903		if (ols != oe) {
904			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
905			/*
906		         * Again a duplicate test.
907		         */
908			if (sx < 0) {
909				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
910				    "ERROR: cannot delete in second diff\n"));
911				terminal_deletechars(el, -sx);
912			}
913			/*
914		         * write (nls-nse) chars of new starting at nse
915		         */
916			terminal_overwrite(el, nse, (size_t)(nls - nse));
917		} else {
918			ELRE_DEBUG(1, (__F,
919			    "but with nothing left to save\r\n"));
920			terminal_overwrite(el, nse, (size_t)(nls - nse));
921			re_clear_eol(el, fx, sx,
922			    (int)((oe - old) - (ne - new)));
923		}
924	}
925	/*
926         * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
927         */
928	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
929		ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n",
930		    nfd - new));
931
932		terminal_move_to_char(el, (int)(nfd - new));
933		/*
934		 * Check if we have stuff to keep at the end
935		 */
936		if (nsb != ne) {
937			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
938			/*
939		         * We have to recalculate fx here because we set it
940		         * to zero above as a flag saying that we hadn't done
941		         * an early first insert.
942		         */
943			fx = (int)((nsb - nfd) - (osb - ofd));
944			if (fx > 0) {
945				/*
946				 * insert fx chars of new starting at nfd
947				 */
948				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
949				 "ERROR: cannot insert in late first diff\n"));
950				terminal_insertwrite(el, nfd, fx);
951				re_insert(el, old, (int)(ofd - old),
952				    el->el_terminal.t_size.h, nfd, fx);
953			}
954			/*
955		         * write (nsb-nfd) - fx chars of new starting at
956		         * (nfd + fx)
957			 */
958			len = (size_t) ((nsb - nfd) - fx);
959			terminal_overwrite(el, (nfd + fx), len);
960			re__strncopy(ofd + fx, nfd + fx, len);
961		} else {
962			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
963			len = (size_t) (nsb - nfd);
964			terminal_overwrite(el, nfd, len);
965			re__strncopy(ofd, nfd, len);
966		}
967	}
968	/*
969         * line is now NEW up to nse
970         */
971	if (sx >= 0) {
972		ELRE_DEBUG(1, (__F,
973		    "second diff insert at %d...\r\n", (int)(nse - new)));
974		terminal_move_to_char(el, (int)(nse - new));
975		if (ols != oe) {
976			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
977			if (sx > 0) {
978				/* insert sx chars of new starting at nse */
979				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
980				    "ERROR: cannot insert in second diff\n"));
981				terminal_insertwrite(el, nse, sx);
982			}
983			/*
984		         * write (nls-nse) - sx chars of new starting at
985			 * (nse + sx)
986		         */
987			terminal_overwrite(el, (nse + sx),
988			    (size_t)((nls - nse) - sx));
989		} else {
990			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
991			terminal_overwrite(el, nse, (size_t)(nls - nse));
992
993			/*
994	                 * No need to do a clear-to-end here because we were
995	                 * doing a second insert, so we will have over
996	                 * written all of the old string.
997		         */
998		}
999	}
1000	ELRE_DEBUG(1, (__F, "done.\r\n"));
1001}
1002
1003
1004/* re__copy_and_pad():
1005 *	Copy string and pad with spaces
1006 */
1007static void
1008re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width)
1009{
1010	size_t i;
1011
1012	for (i = 0; i < width; i++) {
1013		if (*src == '\0')
1014			break;
1015		*dst++ = *src++;
1016	}
1017
1018	for (; i < width; i++)
1019		*dst++ = ' ';
1020
1021	*dst = '\0';
1022}
1023
1024
1025/* re_refresh_cursor():
1026 *	Move to the new cursor position
1027 */
1028libedit_private void
1029re_refresh_cursor(EditLine *el)
1030{
1031	wchar_t *cp;
1032	int h, v, th, w;
1033
1034	if (el->el_line.cursor >= el->el_line.lastchar) {
1035		if (el->el_map.current == el->el_map.alt
1036		    && el->el_line.lastchar != el->el_line.buffer)
1037			el->el_line.cursor = el->el_line.lastchar - 1;
1038		else
1039			el->el_line.cursor = el->el_line.lastchar;
1040	}
1041
1042	/* first we must find where the cursor is... */
1043	h = el->el_prompt.p_pos.h;
1044	v = el->el_prompt.p_pos.v;
1045	th = el->el_terminal.t_size.h;	/* optimize for speed */
1046
1047	/* do input buffer to el->el_line.cursor */
1048	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
1049                switch (ct_chr_class(*cp)) {
1050		case CHTYPE_NL:  /* handle newline in data part too */
1051			h = 0;
1052			v++;
1053			break;
1054		case CHTYPE_TAB: /* if a tab, to next tab stop */
1055			while (++h & 07)
1056				continue;
1057			break;
1058		default:
1059			w = wcwidth(*cp);
1060			if (w > 1 && h + w > th) { /* won't fit on line */
1061				h = 0;
1062				v++;
1063			}
1064			h += ct_visual_width(*cp);
1065			break;
1066                }
1067
1068		if (h >= th) {	/* check, extra long tabs picked up here also */
1069			h -= th;
1070			v++;
1071		}
1072	}
1073        /* if we have a next character, and it's a doublewidth one, we need to
1074         * check whether we need to linebreak for it to fit */
1075        if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
1076                if (h + w > th) {
1077                    h = 0;
1078                    v++;
1079                }
1080
1081	/* now go there */
1082	terminal_move_to_line(el, v);
1083	terminal_move_to_char(el, h);
1084	terminal__flush(el);
1085}
1086
1087
1088/* re_fastputc():
1089 *	Add a character fast.
1090 */
1091static void
1092re_fastputc(EditLine *el, wint_t c)
1093{
1094	wchar_t *lastline;
1095	int w;
1096
1097	w = wcwidth(c);
1098	while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
1099	    re_fastputc(el, ' ');
1100
1101	terminal__putc(el, c);
1102	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1103	while (--w > 0)
1104		el->el_display[el->el_cursor.v][el->el_cursor.h++]
1105			= MB_FILL_CHAR;
1106
1107	if (el->el_cursor.h >= el->el_terminal.t_size.h) {
1108		/* if we must overflow */
1109		el->el_cursor.h = 0;
1110
1111		/*
1112		 * If we would overflow (input is longer than terminal size),
1113		 * emulate scroll by dropping first line and shuffling the rest.
1114		 * We do this via pointer shuffling - it's safe in this case
1115		 * and we avoid memcpy().
1116		 */
1117		if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
1118			int i, lins = el->el_terminal.t_size.v;
1119
1120			lastline = el->el_display[0];
1121			for(i = 1; i < lins; i++)
1122				el->el_display[i - 1] = el->el_display[i];
1123
1124			el->el_display[i - 1] = lastline;
1125		} else {
1126			el->el_cursor.v++;
1127			lastline = el->el_display[++el->el_refresh.r_oldcv];
1128		}
1129		re__copy_and_pad(lastline, L"", (size_t)el->el_terminal.t_size.h);
1130
1131		if (EL_HAS_AUTO_MARGINS) {
1132			if (EL_HAS_MAGIC_MARGINS) {
1133				terminal__putc(el, ' ');
1134				terminal__putc(el, '\b');
1135			}
1136		} else {
1137			terminal__putc(el, '\r');
1138			terminal__putc(el, '\n');
1139		}
1140	}
1141}
1142
1143
1144/* re_fastaddc():
1145 *	we added just one char, handle it fast.
1146 *	Assumes that screen cursor == real cursor
1147 */
1148libedit_private void
1149re_fastaddc(EditLine *el)
1150{
1151	wchar_t c;
1152	int rhdiff;
1153
1154	c = el->el_line.cursor[-1];
1155
1156	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1157		re_refresh(el);	/* too hard to handle */
1158		return;
1159	}
1160	rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
1161	    el->el_rprompt.p_pos.h;
1162	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1163		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
1164		return;
1165	}			/* else (only do at end of line, no TAB) */
1166	switch (ct_chr_class(c)) {
1167	case CHTYPE_TAB: /* already handled, should never happen here */
1168		break;
1169	case CHTYPE_NL:
1170	case CHTYPE_PRINT:
1171		re_fastputc(el, c);
1172		break;
1173	case CHTYPE_ASCIICTL:
1174	case CHTYPE_NONPRINT: {
1175		wchar_t visbuf[VISUAL_WIDTH_MAX];
1176		ssize_t i, n =
1177		    ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
1178		for (i = 0; n-- > 0; ++i)
1179			re_fastputc(el, visbuf[i]);
1180		break;
1181	}
1182	}
1183	terminal__flush(el);
1184}
1185
1186
1187/* re_clear_display():
1188 *	clear the screen buffers so that new new prompt starts fresh.
1189 */
1190libedit_private void
1191re_clear_display(EditLine *el)
1192{
1193	int i;
1194
1195	el->el_cursor.v = 0;
1196	el->el_cursor.h = 0;
1197	for (i = 0; i < el->el_terminal.t_size.v; i++)
1198		el->el_display[i][0] = '\0';
1199	el->el_refresh.r_oldcv = 0;
1200}
1201
1202
1203/* re_clear_lines():
1204 *	Make sure all lines are *really* blank
1205 */
1206libedit_private void
1207re_clear_lines(EditLine *el)
1208{
1209
1210	if (EL_CAN_CEOL) {
1211		int i;
1212		for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
1213			/* for each line on the screen */
1214			terminal_move_to_line(el, i);
1215			terminal_move_to_char(el, 0);
1216			terminal_clear_EOL(el, el->el_terminal.t_size.h);
1217		}
1218	} else {
1219		terminal_move_to_line(el, el->el_refresh.r_oldcv);
1220					/* go to last line */
1221		terminal__putc(el, '\r');	/* go to BOL */
1222		terminal__putc(el, '\n');	/* go to new line */
1223	}
1224}
1225