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