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