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