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