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