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