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