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