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