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