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