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