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