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