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