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