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