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