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