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