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