Merge commit 'dfc115332c94a2f62058ac7f2bce7631fbd20b3d'
[unleashed/tickless.git] / lib / libedit / refresh.c
blobf2d001d67cb20bdc1cfb5a9d98578c57d257be7a
1 /* $OpenBSD: refresh.c,v 1.21 2017/07/26 12:10:56 schwarze Exp $ */
2 /* $NetBSD: refresh.c,v 1.50 2016/05/02 16:35:17 christos Exp $ */
4 /*-
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "config.h"
39 * refresh.c: Lower level screen refreshing functions
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
45 #include "el.h"
47 static void re_nextline(EditLine *);
48 static void re_addc(EditLine *, wint_t);
49 static void re_update_line(EditLine *, wchar_t *, wchar_t *, int);
50 static void re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int);
51 static void re_delete(EditLine *, wchar_t *, int, int, int);
52 static void re_fastputc(EditLine *, wint_t);
53 static void re_clear_eol(EditLine *, int, int, int);
54 static void re__strncopy(wchar_t *, wchar_t *, size_t);
55 static void re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
57 #ifdef DEBUG_REFRESH
58 static void re_printstr(EditLine *, const char *, wchar_t *, wchar_t *);
59 #define __F el->el_errfile
60 #define ELRE_ASSERT(a, b, c) do \
61 if (/*CONSTCOND*/ a) { \
62 (void) fprintf b; \
63 c; \
64 } \
65 while (/*CONSTCOND*/0)
66 #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
68 /* re_printstr():
69 * Print a string on the debugging pty
71 static void
72 re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t)
75 ELRE_DEBUG(1, (__F, "%s:\"", str));
76 while (f < t)
77 ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
78 ELRE_DEBUG(1, (__F, "\"\r\n"));
80 #else
81 #define ELRE_ASSERT(a, b, c)
82 #define ELRE_DEBUG(a, b)
83 #endif
85 /* re_nextline():
86 * Move to the next line or scroll
88 static void
89 re_nextline(EditLine *el)
91 el->el_refresh.r_cursor.h = 0; /* reset it. */
94 * If we would overflow (input is longer than terminal size),
95 * emulate scroll by dropping first line and shuffling the rest.
96 * We do this via pointer shuffling - it's safe in this case
97 * and we avoid memcpy().
99 if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) {
100 int i, lins = el->el_terminal.t_size.v;
101 wchar_t *firstline = el->el_vdisplay[0];
103 for(i = 1; i < lins; i++)
104 el->el_vdisplay[i - 1] = el->el_vdisplay[i];
106 firstline[0] = '\0'; /* empty the string */
107 el->el_vdisplay[i - 1] = firstline;
108 } else
109 el->el_refresh.r_cursor.v++;
111 ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v,
112 (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
113 el->el_refresh.r_cursor.v, el->el_terminal.t_size.v),
114 abort());
117 /* re_addc():
118 * Draw c, expanding tabs, control chars etc.
120 static void
121 re_addc(EditLine *el, wint_t c)
123 switch (ct_chr_class(c)) {
124 case CHTYPE_TAB: /* expand the tab */
125 for (;;) {
126 re_putc(el, ' ', 1);
127 if ((el->el_refresh.r_cursor.h & 07) == 0)
128 break; /* go until tab stop */
130 break;
131 case CHTYPE_NL: {
132 int oldv = el->el_refresh.r_cursor.v;
133 re_putc(el, '\0', 0); /* assure end of line */
134 if (oldv == el->el_refresh.r_cursor.v) /* XXX */
135 re_nextline(el);
136 break;
138 case CHTYPE_PRINT:
139 re_putc(el, c, 1);
140 break;
141 default: {
142 wchar_t visbuf[VISUAL_WIDTH_MAX];
143 ssize_t i, n =
144 ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
145 for (i = 0; n-- > 0; ++i)
146 re_putc(el, visbuf[i], 1);
147 break;
153 /* re_putc():
154 * Draw the character given
156 protected void
157 re_putc(EditLine *el, wint_t c, int shift)
159 int i, w = wcwidth(c);
160 ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c));
161 if (w == -1)
162 w = 0;
164 while (shift && (el->el_refresh.r_cursor.h + w > el->el_terminal.t_size.h))
165 re_putc(el, ' ', 1);
167 el->el_vdisplay[el->el_refresh.r_cursor.v]
168 [el->el_refresh.r_cursor.h] = c;
169 /* assumes !shift is only used for single-column chars */
170 i = w;
171 while (--i > 0)
172 el->el_vdisplay[el->el_refresh.r_cursor.v]
173 [el->el_refresh.r_cursor.h + i] = MB_FILL_CHAR;
175 if (!shift)
176 return;
178 el->el_refresh.r_cursor.h += w; /* advance to next place */
179 if (el->el_refresh.r_cursor.h >= el->el_terminal.t_size.h) {
180 /* assure end of line */
181 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_terminal.t_size.h]
182 = '\0';
183 re_nextline(el);
188 /* re_refresh():
189 * draws the new virtual screen image from the current input
190 * line, then goes line-by-line changing the real image to the new
191 * virtual image. The routine to re-draw a line can be replaced
192 * easily in hopes of a smarter one being placed there.
194 protected void
195 re_refresh(EditLine *el)
197 int i, rhdiff;
198 wchar_t *cp, *st;
199 coord_t cur;
200 #ifdef notyet
201 size_t termsz;
202 #endif
204 ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n",
205 el->el_line.buffer));
207 /* reset the Drawing cursor */
208 el->el_refresh.r_cursor.h = 0;
209 el->el_refresh.r_cursor.v = 0;
211 /* temporarily draw rprompt to calculate its size */
212 prompt_print(el, EL_RPROMPT);
214 /* reset the Drawing cursor */
215 el->el_refresh.r_cursor.h = 0;
216 el->el_refresh.r_cursor.v = 0;
218 if (el->el_line.cursor >= el->el_line.lastchar) {
219 if (el->el_map.current == el->el_map.alt
220 && el->el_line.lastchar != el->el_line.buffer)
221 el->el_line.cursor = el->el_line.lastchar - 1;
222 else
223 el->el_line.cursor = el->el_line.lastchar;
226 cur.h = -1; /* set flag in case I'm not set */
227 cur.v = 0;
229 prompt_print(el, EL_PROMPT);
231 /* draw the current input buffer */
232 #if notyet
233 termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v;
234 if (el->el_line.lastchar - el->el_line.buffer > termsz) {
236 * If line is longer than terminal, process only part
237 * of line which would influence display.
239 size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
241 st = el->el_line.lastchar - rem
242 - (termsz - (((rem / el->el_terminal.t_size.v) - 1)
243 * el->el_terminal.t_size.v));
244 } else
245 #endif
246 st = el->el_line.buffer;
248 for (cp = st; cp < el->el_line.lastchar; cp++) {
249 if (cp == el->el_line.cursor) {
250 int w = wcwidth(*cp);
251 /* save for later */
252 cur.h = el->el_refresh.r_cursor.h;
253 cur.v = el->el_refresh.r_cursor.v;
254 /* handle being at a linebroken doublewidth char */
255 if (w > 1 && el->el_refresh.r_cursor.h + w >
256 el->el_terminal.t_size.h) {
257 cur.h = 0;
258 cur.v++;
261 re_addc(el, *cp);
264 if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */
265 cur.h = el->el_refresh.r_cursor.h;
266 cur.v = el->el_refresh.r_cursor.v;
268 rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h -
269 el->el_rprompt.p_pos.h;
270 if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
271 !el->el_refresh.r_cursor.v && rhdiff > 1) {
273 * have a right-hand side prompt that will fit
274 * on the end of the first line with at least
275 * one character gap to the input buffer.
277 while (--rhdiff > 0) /* pad out with spaces */
278 re_putc(el, ' ', 1);
279 prompt_print(el, EL_RPROMPT);
280 } else {
281 el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */
282 el->el_rprompt.p_pos.v = 0;
285 re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
287 el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
289 ELRE_DEBUG(1, (__F,
290 "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
291 el->el_terminal.t_size.h, el->el_refresh.r_cursor.h,
292 el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0],
293 &el->el_scratch)));
295 ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
296 for (i = 0; i <= el->el_refresh.r_newcv; i++) {
297 /* NOTE THAT re_update_line MAY CHANGE el_display[i] */
298 re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
301 * Copy the new line to be the current one, and pad out with
302 * spaces to the full width of the terminal so that if we try
303 * moving the cursor by writing the character that is at the
304 * end of the screen line, it won't be a NUL or some old
305 * leftover stuff.
307 re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
308 (size_t) el->el_terminal.t_size.h);
310 ELRE_DEBUG(1, (__F,
311 "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
312 el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
314 if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
315 for (; i <= el->el_refresh.r_oldcv; i++) {
316 terminal_move_to_line(el, i);
317 terminal_move_to_char(el, 0);
318 /* This wcslen should be safe even with MB_FILL_CHARs */
319 terminal_clear_EOL(el, (int) wcslen(el->el_display[i]));
320 #ifdef DEBUG_REFRESH
321 terminal_overwrite(el, L"C\b", 2);
322 #endif /* DEBUG_REFRESH */
323 el->el_display[i][0] = '\0';
326 el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
327 ELRE_DEBUG(1, (__F,
328 "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
329 el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
330 cur.h, cur.v));
331 terminal_move_to_line(el, cur.v); /* go to where the cursor is */
332 terminal_move_to_char(el, cur.h);
336 /* re_goto_bottom():
337 * used to go to last used screen line
339 protected void
340 re_goto_bottom(EditLine *el)
343 terminal_move_to_line(el, el->el_refresh.r_oldcv);
344 terminal__putc(el, '\n');
345 re_clear_display(el);
346 terminal__flush(el);
350 /* re_insert():
351 * insert num characters of s into d (in front of the character)
352 * at dat, maximum length of d is dlen
354 static void
355 /*ARGSUSED*/
356 re_insert(EditLine *el __attribute__((__unused__)),
357 wchar_t *d, int dat, int dlen, wchar_t *s, int num)
359 wchar_t *a, *b;
361 if (num <= 0)
362 return;
363 if (num > dlen - dat)
364 num = dlen - dat;
366 ELRE_DEBUG(1,
367 (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
368 num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
369 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
370 &el->el_scratch)));
372 /* open up the space for num chars */
373 if (num > 0) {
374 b = d + dlen - 1;
375 a = b - num;
376 while (a >= &d[dat])
377 *b-- = *a--;
378 d[dlen] = '\0'; /* just in case */
381 ELRE_DEBUG(1, (__F,
382 "re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
383 num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
384 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
385 &el->el_scratch)));
387 /* copy the characters */
388 for (a = d + dat; (a < d + dlen) && (num > 0); num--)
389 *a++ = *s++;
391 #ifdef notyet
392 /* ct_encode_string() uses a static buffer, so we can't conveniently
393 * encode both d & s here */
394 ELRE_DEBUG(1,
395 (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
396 num, dat, dlen, d, s));
397 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s));
398 #endif
402 /* re_delete():
403 * delete num characters d at dat, maximum length of d is dlen
405 static void
406 /*ARGSUSED*/
407 re_delete(EditLine *el __attribute__((__unused__)),
408 wchar_t *d, int dat, int dlen, int num)
410 wchar_t *a, *b;
412 if (num <= 0)
413 return;
414 if (dat + num >= dlen) {
415 d[dat] = '\0';
416 return;
418 ELRE_DEBUG(1,
419 (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
420 num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
422 /* open up the space for num chars */
423 if (num > 0) {
424 b = d + dat;
425 a = b + num;
426 while (a < &d[dlen])
427 *b++ = *a++;
428 d[dlen] = '\0'; /* just in case */
430 ELRE_DEBUG(1,
431 (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
432 num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
436 /* re__strncopy():
437 * Like strncpy without padding.
439 static void
440 re__strncopy(wchar_t *a, wchar_t *b, size_t n)
443 while (n-- && *b)
444 *a++ = *b++;
447 /* re_clear_eol():
448 * Find the number of characters we need to clear till the end of line
449 * in order to make sure that we have cleared the previous contents of
450 * the line. fx and sx is the number of characters inserted or deleted
451 * in the first or second diff, diff is the difference between the
452 * number of characters between the new and old line.
454 static void
455 re_clear_eol(EditLine *el, int fx, int sx, int diff)
458 ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
459 sx, fx, diff));
461 if (fx < 0)
462 fx = -fx;
463 if (sx < 0)
464 sx = -sx;
465 if (fx > diff)
466 diff = fx;
467 if (sx > diff)
468 diff = sx;
470 ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
471 terminal_clear_EOL(el, diff);
474 /*****************************************************************
475 re_update_line() is based on finding the middle difference of each line
476 on the screen; vis:
478 /old first difference
479 /beginning of line | /old last same /old EOL
480 v v v v
481 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
482 new: eddie> Oh, my little buggy says to me, as lurgid as
483 ^ ^ ^ ^
484 \beginning of line | \new last same \new end of line
485 \new first difference
487 all are character pointers for the sake of speed. Special cases for
488 no differences, as well as for end of line additions must be handled.
489 **************************************************************** */
491 /* Minimum at which doing an insert it "worth it". This should be about
492 * half the "cost" of going into insert mode, inserting a character, and
493 * going back out. This should really be calculated from the termcap
494 * data... For the moment, a good number for ANSI terminals.
496 #define MIN_END_KEEP 4
498 static void
499 re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i)
501 wchar_t *o, *n, *p, c;
502 wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne;
503 wchar_t *osb, *ose, *nsb, *nse;
504 int fx, sx;
505 size_t len;
508 * find first diff
510 for (o = old, n = new; *o && (*o == *n); o++, n++)
511 continue;
512 ofd = o;
513 nfd = n;
516 * Find the end of both old and new
518 while (*o)
519 o++;
521 * Remove any trailing blanks off of the end, being careful not to
522 * back up past the beginning.
524 while (ofd < o) {
525 if (o[-1] != ' ')
526 break;
527 o--;
529 oe = o;
530 *oe = '\0';
532 while (*n)
533 n++;
535 /* remove blanks from end of new */
536 while (nfd < n) {
537 if (n[-1] != ' ')
538 break;
539 n--;
541 ne = n;
542 *ne = '\0';
545 * if no diff, continue to next line of redraw
547 if (*ofd == '\0' && *nfd == '\0') {
548 ELRE_DEBUG(1, (__F, "no difference.\r\n"));
549 return;
552 * find last same pointer
554 while ((o > ofd) && (n > nfd) && (*--o == *--n))
555 continue;
556 ols = ++o;
557 nls = ++n;
560 * find same beginning and same end
562 osb = ols;
563 nsb = nls;
564 ose = ols;
565 nse = nls;
568 * case 1: insert: scan from nfd to nls looking for *ofd
570 if (*ofd) {
571 for (c = *ofd, n = nfd; n < nls; n++) {
572 if (c == *n) {
573 for (o = ofd, p = n;
574 p < nls && o < ols && *o == *p;
575 o++, p++)
576 continue;
578 * if the new match is longer and it's worth
579 * keeping, then we take it
581 if (((nse - nsb) < (p - n)) &&
582 (2 * (p - n) > n - nfd)) {
583 nsb = n;
584 nse = p;
585 osb = ofd;
586 ose = o;
592 * case 2: delete: scan from ofd to ols looking for *nfd
594 if (*nfd) {
595 for (c = *nfd, o = ofd; o < ols; o++) {
596 if (c == *o) {
597 for (n = nfd, p = o;
598 p < ols && n < nls && *p == *n;
599 p++, n++)
600 continue;
602 * if the new match is longer and it's worth
603 * keeping, then we take it
605 if (((ose - osb) < (p - o)) &&
606 (2 * (p - o) > o - ofd)) {
607 nsb = nfd;
608 nse = n;
609 osb = o;
610 ose = p;
616 * Pragmatics I: If old trailing whitespace or not enough characters to
617 * save to be worth it, then don't save the last same info.
619 if ((oe - ols) < MIN_END_KEEP) {
620 ols = oe;
621 nls = ne;
624 * Pragmatics II: if the terminal isn't smart enough, make the data
625 * dumber so the smart update doesn't try anything fancy
629 * fx is the number of characters we need to insert/delete: in the
630 * beginning to bring the two same begins together
632 fx = (int)((nsb - nfd) - (osb - ofd));
634 * sx is the number of characters we need to insert/delete: in the
635 * end to bring the two same last parts together
637 sx = (int)((nls - nse) - (ols - ose));
639 if (!EL_CAN_INSERT) {
640 if (fx > 0) {
641 osb = ols;
642 ose = ols;
643 nsb = nls;
644 nse = nls;
646 if (sx > 0) {
647 ols = oe;
648 nls = ne;
650 if ((ols - ofd) < (nls - nfd)) {
651 ols = oe;
652 nls = ne;
655 if (!EL_CAN_DELETE) {
656 if (fx < 0) {
657 osb = ols;
658 ose = ols;
659 nsb = nls;
660 nse = nls;
662 if (sx < 0) {
663 ols = oe;
664 nls = ne;
666 if ((ols - ofd) > (nls - nfd)) {
667 ols = oe;
668 nls = ne;
672 * Pragmatics III: make sure the middle shifted pointers are correct if
673 * they don't point to anything (we may have moved ols or nls).
675 /* if the change isn't worth it, don't bother */
676 /* was: if (osb == ose) */
677 if ((ose - osb) < MIN_END_KEEP) {
678 osb = ols;
679 ose = ols;
680 nsb = nls;
681 nse = nls;
684 * Now that we are done with pragmatics we recompute fx, sx
686 fx = (int)((nsb - nfd) - (osb - ofd));
687 sx = (int)((nls - nse) - (ols - ose));
689 ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
690 ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n",
691 ofd - old, osb - old, ose - old, ols - old, oe - old));
692 ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n",
693 nfd - new, nsb - new, nse - new, nls - new, ne - new));
694 ELRE_DEBUG(1, (__F,
695 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
696 ELRE_DEBUG(1, (__F,
697 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
698 #ifdef DEBUG_REFRESH
699 re_printstr(el, "old- oe", old, oe);
700 re_printstr(el, "new- ne", new, ne);
701 re_printstr(el, "old-ofd", old, ofd);
702 re_printstr(el, "new-nfd", new, nfd);
703 re_printstr(el, "ofd-osb", ofd, osb);
704 re_printstr(el, "nfd-nsb", nfd, nsb);
705 re_printstr(el, "osb-ose", osb, ose);
706 re_printstr(el, "nsb-nse", nsb, nse);
707 re_printstr(el, "ose-ols", ose, ols);
708 re_printstr(el, "nse-nls", nse, nls);
709 re_printstr(el, "ols- oe", ols, oe);
710 re_printstr(el, "nls- ne", nls, ne);
711 #endif /* DEBUG_REFRESH */
714 * el_cursor.v to this line i MUST be in this routine so that if we
715 * don't have to change the line, we don't move to it. el_cursor.h to
716 * first diff char
718 terminal_move_to_line(el, i);
721 * at this point we have something like this:
723 * /old /ofd /osb /ose /ols /oe
724 * v.....................v v..................v v........v
725 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
726 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
727 * ^.....................^ ^..................^ ^........^
728 * \new \nfd \nsb \nse \nls \ne
730 * fx is the difference in length between the chars between nfd and
731 * nsb, and the chars between ofd and osb, and is thus the number of
732 * characters to delete if < 0 (new is shorter than old, as above),
733 * or insert (new is longer than short).
735 * sx is the same for the second differences.
739 * if we have a net insert on the first difference, AND inserting the
740 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
741 * character (which is ne if nls != ne, otherwise is nse) off the edge
742 * of the screen (el->el_terminal.t_size.h) else we do the deletes first
743 * so that we keep everything we need to.
747 * if the last same is the same like the end, there is no last same
748 * part, otherwise we want to keep the last same part set p to the
749 * last useful old character
751 p = (ols != oe) ? oe : ose;
754 * if (There is a diffence in the beginning) && (we need to insert
755 * characters) && (the number of characters to insert is less than
756 * the term width)
757 * We need to do an insert!
758 * else if (we need to delete characters)
759 * We need to delete characters!
760 * else
761 * No insert or delete
763 if ((nsb != nfd) && fx > 0 &&
764 ((p - old) + fx <= el->el_terminal.t_size.h)) {
765 ELRE_DEBUG(1,
766 (__F, "first diff insert at %td...\r\n", nfd - new));
768 * Move to the first char to insert, where the first diff is.
770 terminal_move_to_char(el, (int)(nfd - new));
772 * Check if we have stuff to keep at end
774 if (nsb != ne) {
775 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
777 * insert fx chars of new starting at nfd
779 if (fx > 0) {
780 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
781 "ERROR: cannot insert in early first diff\n"));
782 terminal_insertwrite(el, nfd, fx);
783 re_insert(el, old, (int)(ofd - old),
784 el->el_terminal.t_size.h, nfd, fx);
787 * write (nsb-nfd) - fx chars of new starting at
788 * (nfd + fx)
790 len = (size_t) ((nsb - nfd) - fx);
791 terminal_overwrite(el, (nfd + fx), len);
792 re__strncopy(ofd + fx, nfd + fx, len);
793 } else {
794 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
795 len = (size_t)(nsb - nfd);
796 terminal_overwrite(el, nfd, len);
797 re__strncopy(ofd, nfd, len);
799 * Done
801 return;
803 } else if (fx < 0) {
804 ELRE_DEBUG(1,
805 (__F, "first diff delete at %td...\r\n", ofd - old));
807 * move to the first char to delete where the first diff is
809 terminal_move_to_char(el, (int)(ofd - old));
811 * Check if we have stuff to save
813 if (osb != oe) {
814 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
816 * fx is less than zero *always* here but we check
817 * for code symmetry
819 if (fx < 0) {
820 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
821 "ERROR: cannot delete in first diff\n"));
822 terminal_deletechars(el, -fx);
823 re_delete(el, old, (int)(ofd - old),
824 el->el_terminal.t_size.h, -fx);
827 * write (nsb-nfd) chars of new starting at nfd
829 len = (size_t) (nsb - nfd);
830 terminal_overwrite(el, nfd, len);
831 re__strncopy(ofd, nfd, len);
833 } else {
834 ELRE_DEBUG(1, (__F,
835 "but with nothing left to save\r\n"));
837 * write (nsb-nfd) chars of new starting at nfd
839 terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
840 re_clear_eol(el, fx, sx,
841 (int)((oe - old) - (ne - new)));
843 * Done
845 return;
847 } else
848 fx = 0;
850 if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
851 ELRE_DEBUG(1, (__F,
852 "second diff delete at %td...\r\n", (ose - old) + fx));
854 * Check if we have stuff to delete
857 * fx is the number of characters inserted (+) or deleted (-)
860 terminal_move_to_char(el, (int)((ose - old) + fx));
862 * Check if we have stuff to save
864 if (ols != oe) {
865 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
867 * Again a duplicate test.
869 if (sx < 0) {
870 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
871 "ERROR: cannot delete in second diff\n"));
872 terminal_deletechars(el, -sx);
875 * write (nls-nse) chars of new starting at nse
877 terminal_overwrite(el, nse, (size_t)(nls - nse));
878 } else {
879 ELRE_DEBUG(1, (__F,
880 "but with nothing left to save\r\n"));
881 terminal_overwrite(el, nse, (size_t)(nls - nse));
882 re_clear_eol(el, fx, sx,
883 (int)((oe - old) - (ne - new)));
887 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
889 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
890 ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n",
891 nfd - new));
893 terminal_move_to_char(el, (int)(nfd - new));
895 * Check if we have stuff to keep at the end
897 if (nsb != ne) {
898 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
900 * We have to recalculate fx here because we set it
901 * to zero above as a flag saying that we hadn't done
902 * an early first insert.
904 fx = (int)((nsb - nfd) - (osb - ofd));
905 if (fx > 0) {
907 * insert fx chars of new starting at nfd
909 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
910 "ERROR: cannot insert in late first diff\n"));
911 terminal_insertwrite(el, nfd, fx);
912 re_insert(el, old, (int)(ofd - old),
913 el->el_terminal.t_size.h, nfd, fx);
916 * write (nsb-nfd) - fx chars of new starting at
917 * (nfd + fx)
919 len = (size_t) ((nsb - nfd) - fx);
920 terminal_overwrite(el, (nfd + fx), len);
921 re__strncopy(ofd + fx, nfd + fx, len);
922 } else {
923 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
924 len = (size_t) (nsb - nfd);
925 terminal_overwrite(el, nfd, len);
926 re__strncopy(ofd, nfd, len);
930 * line is now NEW up to nse
932 if (sx >= 0) {
933 ELRE_DEBUG(1, (__F,
934 "second diff insert at %d...\r\n", (int)(nse - new)));
935 terminal_move_to_char(el, (int)(nse - new));
936 if (ols != oe) {
937 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
938 if (sx > 0) {
939 /* insert sx chars of new starting at nse */
940 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
941 "ERROR: cannot insert in second diff\n"));
942 terminal_insertwrite(el, nse, sx);
945 * write (nls-nse) - sx chars of new starting at
946 * (nse + sx)
948 terminal_overwrite(el, (nse + sx),
949 (size_t)((nls - nse) - sx));
950 } else {
951 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
952 terminal_overwrite(el, nse, (size_t)(nls - nse));
955 * No need to do a clear-to-end here because we were
956 * doing a second insert, so we will have over
957 * written all of the old string.
961 ELRE_DEBUG(1, (__F, "done.\r\n"));
965 /* re__copy_and_pad():
966 * Copy string and pad with spaces
968 static void
969 re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width)
971 size_t i;
973 for (i = 0; i < width; i++) {
974 if (*src == '\0')
975 break;
976 *dst++ = *src++;
979 for (; i < width; i++)
980 *dst++ = ' ';
982 *dst = '\0';
986 /* re_refresh_cursor():
987 * Move to the new cursor position
989 protected void
990 re_refresh_cursor(EditLine *el)
992 wchar_t *cp;
993 int h, v, th, w;
995 if (el->el_line.cursor >= el->el_line.lastchar) {
996 if (el->el_map.current == el->el_map.alt
997 && el->el_line.lastchar != el->el_line.buffer)
998 el->el_line.cursor = el->el_line.lastchar - 1;
999 else
1000 el->el_line.cursor = el->el_line.lastchar;
1003 /* first we must find where the cursor is... */
1004 h = el->el_prompt.p_pos.h;
1005 v = el->el_prompt.p_pos.v;
1006 th = el->el_terminal.t_size.h; /* optimize for speed */
1008 /* do input buffer to el->el_line.cursor */
1009 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
1010 switch (ct_chr_class(*cp)) {
1011 case CHTYPE_NL: /* handle newline in data part too */
1012 h = 0;
1013 v++;
1014 break;
1015 case CHTYPE_TAB: /* if a tab, to next tab stop */
1016 while (++h & 07)
1017 continue;
1018 break;
1019 default:
1020 w = wcwidth(*cp);
1021 if (w > 1 && h + w > th) { /* won't fit on line */
1022 h = 0;
1023 v++;
1025 h += ct_visual_width(*cp);
1026 break;
1029 if (h >= th) { /* check, extra long tabs picked up here also */
1030 h -= th;
1031 v++;
1034 /* if we have a next character, and it's a doublewidth one, we need to
1035 * check whether we need to linebreak for it to fit */
1036 if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
1037 if (h + w > th) {
1038 h = 0;
1039 v++;
1042 /* now go there */
1043 terminal_move_to_line(el, v);
1044 terminal_move_to_char(el, h);
1045 terminal__flush(el);
1049 /* re_fastputc():
1050 * Add a character fast.
1052 static void
1053 re_fastputc(EditLine *el, wint_t c)
1055 wchar_t *lastline;
1056 int w;
1058 w = wcwidth(c);
1059 while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
1060 re_fastputc(el, ' ');
1062 terminal__putc(el, c);
1063 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1064 while (--w > 0)
1065 el->el_display[el->el_cursor.v][el->el_cursor.h++]
1066 = MB_FILL_CHAR;
1068 if (el->el_cursor.h >= el->el_terminal.t_size.h) {
1069 /* if we must overflow */
1070 el->el_cursor.h = 0;
1073 * If we would overflow (input is longer than terminal size),
1074 * emulate scroll by dropping first line and shuffling the rest.
1075 * We do this via pointer shuffling - it's safe in this case
1076 * and we avoid memcpy().
1078 if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
1079 int i, lins = el->el_terminal.t_size.v;
1080 lastline = el->el_display[0];
1081 for(i = 1; i < lins; i++)
1082 el->el_display[i - 1] = el->el_display[i];
1083 el->el_display[i - 1] = lastline;
1084 } else {
1085 el->el_cursor.v++;
1086 lastline = el->el_display[el->el_refresh.r_oldcv++];
1088 re__copy_and_pad(lastline, L"", el->el_terminal.t_size.h);
1090 if (EL_HAS_AUTO_MARGINS) {
1091 if (EL_HAS_MAGIC_MARGINS) {
1092 terminal__putc(el, ' ');
1093 terminal__putc(el, '\b');
1095 } else {
1096 terminal__putc(el, '\r');
1097 terminal__putc(el, '\n');
1103 /* re_fastaddc():
1104 * we added just one char, handle it fast.
1105 * Assumes that screen cursor == real cursor
1107 protected void
1108 re_fastaddc(EditLine *el)
1110 wchar_t c;
1111 int rhdiff;
1113 c = el->el_line.cursor[-1];
1115 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1116 re_refresh(el); /* too hard to handle */
1117 return;
1119 rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
1120 el->el_rprompt.p_pos.h;
1121 if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1122 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1123 return;
1124 } /* else (only do at end of line, no TAB) */
1125 switch (ct_chr_class(c)) {
1126 case CHTYPE_TAB: /* already handled, should never happen here */
1127 break;
1128 case CHTYPE_NL:
1129 case CHTYPE_PRINT:
1130 re_fastputc(el, c);
1131 break;
1132 case CHTYPE_ASCIICTL:
1133 case CHTYPE_NONPRINT: {
1134 wchar_t visbuf[VISUAL_WIDTH_MAX];
1135 ssize_t i, n =
1136 ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
1137 for (i = 0; n-- > 0; ++i)
1138 re_fastputc(el, visbuf[i]);
1139 break;
1142 terminal__flush(el);
1146 /* re_clear_display():
1147 * clear the screen buffers so that new new prompt starts fresh.
1149 protected void
1150 re_clear_display(EditLine *el)
1152 int i;
1154 el->el_cursor.v = 0;
1155 el->el_cursor.h = 0;
1156 for (i = 0; i < el->el_terminal.t_size.v; i++)
1157 el->el_display[i][0] = '\0';
1158 el->el_refresh.r_oldcv = 0;
1162 /* re_clear_lines():
1163 * Make sure all lines are *really* blank
1165 protected void
1166 re_clear_lines(EditLine *el)
1169 if (EL_CAN_CEOL) {
1170 int i;
1171 for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
1172 /* for each line on the screen */
1173 terminal_move_to_line(el, i);
1174 terminal_move_to_char(el, 0);
1175 terminal_clear_EOL(el, el->el_terminal.t_size.h);
1177 } else {
1178 terminal_move_to_line(el, el->el_refresh.r_oldcv);
1179 /* go to last line */
1180 terminal__putc(el, '\r'); /* go to BOL */
1181 terminal__putc(el, '\n'); /* go to new line */