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