1 /* $NetBSD: refresh.c,v 1.58 2021/09/09 20:24:07 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Christos Zoulas of Cornell University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "config.h" 36 #if !defined(lint) && !defined(SCCSID) 37 #if 0 38 static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; 39 #else 40 __RCSID("$NetBSD: refresh.c,v 1.58 2021/09/09 20:24:07 christos Exp $"); 41 #endif 42 #endif /* not lint && not SCCSID */ 43 44 /* 45 * refresh.c: Lower level screen refreshing functions 46 */ 47 #include <stdio.h> 48 #include <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 wint_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, (wchar_t *)el->el_display[i], 338 (wchar_t *)el->el_vdisplay[i], i); 339 340 /* 341 * Copy the new line to be the current one, and pad out with 342 * spaces to the full width of the terminal so that if we try 343 * moving the cursor by writing the character that is at the 344 * end of the screen line, it won't be a NUL or some old 345 * leftover stuff. 346 */ 347 re__copy_and_pad((wchar_t *)el->el_display[i], 348 (wchar_t *)el->el_vdisplay[i], 349 (size_t) el->el_terminal.t_size.h); 350 } 351 ELRE_DEBUG(1, (__F, 352 "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", 353 el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); 354 355 if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) 356 for (; i <= el->el_refresh.r_oldcv; i++) { 357 terminal_move_to_line(el, i); 358 terminal_move_to_char(el, 0); 359 /* This wcslen should be safe even with MB_FILL_CHARs */ 360 terminal_clear_EOL(el, 361 (int) wcslen((const wchar_t *)el->el_display[i])); 362 #ifdef DEBUG_REFRESH 363 terminal_overwrite(el, L"C\b", 2); 364 #endif /* DEBUG_REFRESH */ 365 el->el_display[i][0] = '\0'; 366 } 367 368 el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ 369 ELRE_DEBUG(1, (__F, 370 "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", 371 el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, 372 cur.h, cur.v)); 373 terminal_move_to_line(el, cur.v); /* go to where the cursor is */ 374 terminal_move_to_char(el, cur.h); 375 } 376 377 378 /* re_goto_bottom(): 379 * used to go to last used screen line 380 */ 381 libedit_private void 382 re_goto_bottom(EditLine *el) 383 { 384 385 terminal_move_to_line(el, el->el_refresh.r_oldcv); 386 terminal__putc(el, '\n'); 387 re_clear_display(el); 388 terminal__flush(el); 389 } 390 391 392 /* re_insert(): 393 * insert num characters of s into d (in front of the character) 394 * at dat, maximum length of d is dlen 395 */ 396 static void 397 /*ARGSUSED*/ 398 re_insert(EditLine *el __attribute__((__unused__)), 399 wchar_t *d, int dat, int dlen, wchar_t *s, int num) 400 { 401 wchar_t *a, *b; 402 403 if (num <= 0) 404 return; 405 if (num > dlen - dat) 406 num = dlen - dat; 407 408 ELRE_DEBUG(1, 409 (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", 410 num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 411 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, 412 &el->el_scratch))); 413 414 /* open up the space for num chars */ 415 if (num > 0) { 416 b = d + dlen - 1; 417 a = b - num; 418 while (a >= &d[dat]) 419 *b-- = *a--; 420 d[dlen] = '\0'; /* just in case */ 421 } 422 423 ELRE_DEBUG(1, (__F, 424 "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", 425 num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 426 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, 427 &el->el_scratch))); 428 429 /* copy the characters */ 430 for (a = d + dat; (a < d + dlen) && (num > 0); num--) 431 *a++ = *s++; 432 433 #ifdef notyet 434 /* ct_encode_string() uses a static buffer, so we can't conveniently 435 * encode both d & s here */ 436 ELRE_DEBUG(1, 437 (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", 438 num, dat, dlen, d, s)); 439 ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); 440 #endif 441 } 442 443 444 /* re_delete(): 445 * delete num characters d at dat, maximum length of d is dlen 446 */ 447 static void 448 /*ARGSUSED*/ 449 re_delete(EditLine *el __attribute__((__unused__)), 450 wchar_t *d, int dat, int dlen, int num) 451 { 452 wchar_t *a, *b; 453 454 if (num <= 0) 455 return; 456 if (dat + num >= dlen) { 457 d[dat] = '\0'; 458 return; 459 } 460 ELRE_DEBUG(1, 461 (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", 462 num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 463 464 /* open up the space for num chars */ 465 if (num > 0) { 466 b = d + dat; 467 a = b + num; 468 while (a < &d[dlen]) 469 *b++ = *a++; 470 d[dlen] = '\0'; /* just in case */ 471 } 472 ELRE_DEBUG(1, 473 (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", 474 num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 475 } 476 477 478 /* re__strncopy(): 479 * Like strncpy without padding. 480 */ 481 static void 482 re__strncopy(wchar_t *a, wchar_t *b, size_t n) 483 { 484 485 while (n-- && *b) 486 *a++ = *b++; 487 } 488 489 /* re_clear_eol(): 490 * Find the number of characters we need to clear till the end of line 491 * in order to make sure that we have cleared the previous contents of 492 * the line. fx and sx is the number of characters inserted or deleted 493 * in the first or second diff, diff is the difference between the 494 * number of characters between the new and old line. 495 */ 496 static void 497 re_clear_eol(EditLine *el, int fx, int sx, int diff) 498 { 499 500 ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", 501 sx, fx, diff)); 502 503 if (fx < 0) 504 fx = -fx; 505 if (sx < 0) 506 sx = -sx; 507 if (fx > diff) 508 diff = fx; 509 if (sx > diff) 510 diff = sx; 511 512 ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); 513 terminal_clear_EOL(el, diff); 514 } 515 516 /***************************************************************** 517 re_update_line() is based on finding the middle difference of each line 518 on the screen; vis: 519 520 /old first difference 521 /beginning of line | /old last same /old EOL 522 v v v v 523 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 524 new: eddie> Oh, my little buggy says to me, as lurgid as 525 ^ ^ ^ ^ 526 \beginning of line | \new last same \new end of line 527 \new first difference 528 529 all are character pointers for the sake of speed. Special cases for 530 no differences, as well as for end of line additions must be handled. 531 **************************************************************** */ 532 533 /* Minimum at which doing an insert it "worth it". This should be about 534 * half the "cost" of going into insert mode, inserting a character, and 535 * going back out. This should really be calculated from the termcap 536 * data... For the moment, a good number for ANSI terminals. 537 */ 538 #define MIN_END_KEEP 4 539 540 static void 541 re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i) 542 { 543 wchar_t *o, *n, *p, c; 544 wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne; 545 wchar_t *osb, *ose, *nsb, *nse; 546 int fx, sx; 547 size_t len; 548 549 /* 550 * find first diff 551 */ 552 for (o = old, n = new; *o && (*o == *n); o++, n++) 553 continue; 554 ofd = o; 555 nfd = n; 556 557 /* 558 * Find the end of both old and new 559 */ 560 while (*o) 561 o++; 562 /* 563 * Remove any trailing blanks off of the end, being careful not to 564 * back up past the beginning. 565 */ 566 while (ofd < o) { 567 if (o[-1] != ' ') 568 break; 569 o--; 570 } 571 oe = o; 572 *oe = '\0'; 573 574 while (*n) 575 n++; 576 577 /* remove blanks from end of new */ 578 while (nfd < n) { 579 if (n[-1] != ' ') 580 break; 581 n--; 582 } 583 ne = n; 584 *ne = '\0'; 585 586 /* 587 * if no diff, continue to next line of redraw 588 */ 589 if (*ofd == '\0' && *nfd == '\0') { 590 ELRE_DEBUG(1, (__F, "no difference.\r\n")); 591 return; 592 } 593 /* 594 * find last same pointer 595 */ 596 while ((o > ofd) && (n > nfd) && (*--o == *--n)) 597 continue; 598 ols = ++o; 599 nls = ++n; 600 601 /* 602 * find same beginning and same end 603 */ 604 osb = ols; 605 nsb = nls; 606 ose = ols; 607 nse = nls; 608 609 /* 610 * case 1: insert: scan from nfd to nls looking for *ofd 611 */ 612 if (*ofd) { 613 for (c = *ofd, n = nfd; n < nls; n++) { 614 if (c == *n) { 615 for (o = ofd, p = n; 616 p < nls && o < ols && *o == *p; 617 o++, p++) 618 continue; 619 /* 620 * if the new match is longer and it's worth 621 * keeping, then we take it 622 */ 623 if (((nse - nsb) < (p - n)) && 624 (2 * (p - n) > n - nfd)) { 625 nsb = n; 626 nse = p; 627 osb = ofd; 628 ose = o; 629 } 630 } 631 } 632 } 633 /* 634 * case 2: delete: scan from ofd to ols looking for *nfd 635 */ 636 if (*nfd) { 637 for (c = *nfd, o = ofd; o < ols; o++) { 638 if (c == *o) { 639 for (n = nfd, p = o; 640 p < ols && n < nls && *p == *n; 641 p++, n++) 642 continue; 643 /* 644 * if the new match is longer and it's worth 645 * keeping, then we take it 646 */ 647 if (((ose - osb) < (p - o)) && 648 (2 * (p - o) > o - ofd)) { 649 nsb = nfd; 650 nse = n; 651 osb = o; 652 ose = p; 653 } 654 } 655 } 656 } 657 /* 658 * Pragmatics I: If old trailing whitespace or not enough characters to 659 * save to be worth it, then don't save the last same info. 660 */ 661 if ((oe - ols) < MIN_END_KEEP) { 662 ols = oe; 663 nls = ne; 664 } 665 /* 666 * Pragmatics II: if the terminal isn't smart enough, make the data 667 * dumber so the smart update doesn't try anything fancy 668 */ 669 670 /* 671 * fx is the number of characters we need to insert/delete: in the 672 * beginning to bring the two same begins together 673 */ 674 fx = (int)((nsb - nfd) - (osb - ofd)); 675 /* 676 * sx is the number of characters we need to insert/delete: in the 677 * end to bring the two same last parts together 678 */ 679 sx = (int)((nls - nse) - (ols - ose)); 680 681 if (!EL_CAN_INSERT) { 682 if (fx > 0) { 683 osb = ols; 684 ose = ols; 685 nsb = nls; 686 nse = nls; 687 } 688 if (sx > 0) { 689 ols = oe; 690 nls = ne; 691 } 692 if ((ols - ofd) < (nls - nfd)) { 693 ols = oe; 694 nls = ne; 695 } 696 } 697 if (!EL_CAN_DELETE) { 698 if (fx < 0) { 699 osb = ols; 700 ose = ols; 701 nsb = nls; 702 nse = nls; 703 } 704 if (sx < 0) { 705 ols = oe; 706 nls = ne; 707 } 708 if ((ols - ofd) > (nls - nfd)) { 709 ols = oe; 710 nls = ne; 711 } 712 } 713 /* 714 * Pragmatics III: make sure the middle shifted pointers are correct if 715 * they don't point to anything (we may have moved ols or nls). 716 */ 717 /* if the change isn't worth it, don't bother */ 718 /* was: if (osb == ose) */ 719 if ((ose - osb) < MIN_END_KEEP) { 720 osb = ols; 721 ose = ols; 722 nsb = nls; 723 nse = nls; 724 } 725 /* 726 * Now that we are done with pragmatics we recompute fx, sx 727 */ 728 fx = (int)((nsb - nfd) - (osb - ofd)); 729 sx = (int)((nls - nse) - (ols - ose)); 730 731 ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); 732 ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n", 733 ofd - old, osb - old, ose - old, ols - old, oe - old)); 734 ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n", 735 nfd - new, nsb - new, nse - new, nls - new, ne - new)); 736 ELRE_DEBUG(1, (__F, 737 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); 738 ELRE_DEBUG(1, (__F, 739 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); 740 #ifdef DEBUG_REFRESH 741 re_printstr(el, "old- oe", old, oe); 742 re_printstr(el, "new- ne", new, ne); 743 re_printstr(el, "old-ofd", old, ofd); 744 re_printstr(el, "new-nfd", new, nfd); 745 re_printstr(el, "ofd-osb", ofd, osb); 746 re_printstr(el, "nfd-nsb", nfd, nsb); 747 re_printstr(el, "osb-ose", osb, ose); 748 re_printstr(el, "nsb-nse", nsb, nse); 749 re_printstr(el, "ose-ols", ose, ols); 750 re_printstr(el, "nse-nls", nse, nls); 751 re_printstr(el, "ols- oe", ols, oe); 752 re_printstr(el, "nls- ne", nls, ne); 753 #endif /* DEBUG_REFRESH */ 754 755 /* 756 * el_cursor.v to this line i MUST be in this routine so that if we 757 * don't have to change the line, we don't move to it. el_cursor.h to 758 * first diff char 759 */ 760 terminal_move_to_line(el, i); 761 762 /* 763 * at this point we have something like this: 764 * 765 * /old /ofd /osb /ose /ols /oe 766 * v.....................v v..................v v........v 767 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as 768 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as 769 * ^.....................^ ^..................^ ^........^ 770 * \new \nfd \nsb \nse \nls \ne 771 * 772 * fx is the difference in length between the chars between nfd and 773 * nsb, and the chars between ofd and osb, and is thus the number of 774 * characters to delete if < 0 (new is shorter than old, as above), 775 * or insert (new is longer than short). 776 * 777 * sx is the same for the second differences. 778 */ 779 780 /* 781 * if we have a net insert on the first difference, AND inserting the 782 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful 783 * character (which is ne if nls != ne, otherwise is nse) off the edge 784 * of the screen (el->el_terminal.t_size.h) else we do the deletes first 785 * so that we keep everything we need to. 786 */ 787 788 /* 789 * if the last same is the same like the end, there is no last same 790 * part, otherwise we want to keep the last same part set p to the 791 * last useful old character 792 */ 793 p = (ols != oe) ? oe : ose; 794 795 /* 796 * if (There is a diffence in the beginning) && (we need to insert 797 * characters) && (the number of characters to insert is less than 798 * the term width) 799 * We need to do an insert! 800 * else if (we need to delete characters) 801 * We need to delete characters! 802 * else 803 * No insert or delete 804 */ 805 if ((nsb != nfd) && fx > 0 && 806 ((p - old) + fx <= el->el_terminal.t_size.h)) { 807 ELRE_DEBUG(1, 808 (__F, "first diff insert at %td...\r\n", nfd - new)); 809 /* 810 * Move to the first char to insert, where the first diff is. 811 */ 812 terminal_move_to_char(el, (int)(nfd - new)); 813 /* 814 * Check if we have stuff to keep at end 815 */ 816 if (nsb != ne) { 817 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 818 /* 819 * insert fx chars of new starting at nfd 820 */ 821 if (fx > 0) { 822 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 823 "ERROR: cannot insert in early first diff\n")); 824 terminal_insertwrite(el, nfd, fx); 825 re_insert(el, old, (int)(ofd - old), 826 el->el_terminal.t_size.h, nfd, fx); 827 } 828 /* 829 * write (nsb-nfd) - fx chars of new starting at 830 * (nfd + fx) 831 */ 832 len = (size_t) ((nsb - nfd) - fx); 833 terminal_overwrite(el, (nfd + fx), len); 834 re__strncopy(ofd + fx, nfd + fx, len); 835 } else { 836 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 837 len = (size_t)(nsb - nfd); 838 terminal_overwrite(el, nfd, len); 839 re__strncopy(ofd, nfd, len); 840 /* 841 * Done 842 */ 843 return; 844 } 845 } else if (fx < 0) { 846 ELRE_DEBUG(1, 847 (__F, "first diff delete at %td...\r\n", ofd - old)); 848 /* 849 * move to the first char to delete where the first diff is 850 */ 851 terminal_move_to_char(el, (int)(ofd - old)); 852 /* 853 * Check if we have stuff to save 854 */ 855 if (osb != oe) { 856 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 857 /* 858 * fx is less than zero *always* here but we check 859 * for code symmetry 860 */ 861 if (fx < 0) { 862 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 863 "ERROR: cannot delete in first diff\n")); 864 terminal_deletechars(el, -fx); 865 re_delete(el, old, (int)(ofd - old), 866 el->el_terminal.t_size.h, -fx); 867 } 868 /* 869 * write (nsb-nfd) chars of new starting at nfd 870 */ 871 len = (size_t) (nsb - nfd); 872 terminal_overwrite(el, nfd, len); 873 re__strncopy(ofd, nfd, len); 874 875 } else { 876 ELRE_DEBUG(1, (__F, 877 "but with nothing left to save\r\n")); 878 /* 879 * write (nsb-nfd) chars of new starting at nfd 880 */ 881 terminal_overwrite(el, nfd, (size_t)(nsb - nfd)); 882 re_clear_eol(el, fx, sx, 883 (int)((oe - old) - (ne - new))); 884 /* 885 * Done 886 */ 887 return; 888 } 889 } else 890 fx = 0; 891 892 if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) { 893 ELRE_DEBUG(1, (__F, 894 "second diff delete at %td...\r\n", (ose - old) + fx)); 895 /* 896 * Check if we have stuff to delete 897 */ 898 /* 899 * fx is the number of characters inserted (+) or deleted (-) 900 */ 901 902 terminal_move_to_char(el, (int)((ose - old) + fx)); 903 /* 904 * Check if we have stuff to save 905 */ 906 if (ols != oe) { 907 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 908 /* 909 * Again a duplicate test. 910 */ 911 if (sx < 0) { 912 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 913 "ERROR: cannot delete in second diff\n")); 914 terminal_deletechars(el, -sx); 915 } 916 /* 917 * write (nls-nse) chars of new starting at nse 918 */ 919 terminal_overwrite(el, nse, (size_t)(nls - nse)); 920 } else { 921 ELRE_DEBUG(1, (__F, 922 "but with nothing left to save\r\n")); 923 terminal_overwrite(el, nse, (size_t)(nls - nse)); 924 re_clear_eol(el, fx, sx, 925 (int)((oe - old) - (ne - new))); 926 } 927 } 928 /* 929 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 930 */ 931 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 932 ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n", 933 nfd - new)); 934 935 terminal_move_to_char(el, (int)(nfd - new)); 936 /* 937 * Check if we have stuff to keep at the end 938 */ 939 if (nsb != ne) { 940 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 941 /* 942 * We have to recalculate fx here because we set it 943 * to zero above as a flag saying that we hadn't done 944 * an early first insert. 945 */ 946 fx = (int)((nsb - nfd) - (osb - ofd)); 947 if (fx > 0) { 948 /* 949 * insert fx chars of new starting at nfd 950 */ 951 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 952 "ERROR: cannot insert in late first diff\n")); 953 terminal_insertwrite(el, nfd, fx); 954 re_insert(el, old, (int)(ofd - old), 955 el->el_terminal.t_size.h, nfd, fx); 956 } 957 /* 958 * write (nsb-nfd) - fx chars of new starting at 959 * (nfd + fx) 960 */ 961 len = (size_t) ((nsb - nfd) - fx); 962 terminal_overwrite(el, (nfd + fx), len); 963 re__strncopy(ofd + fx, nfd + fx, len); 964 } else { 965 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 966 len = (size_t) (nsb - nfd); 967 terminal_overwrite(el, nfd, len); 968 re__strncopy(ofd, nfd, len); 969 } 970 } 971 /* 972 * line is now NEW up to nse 973 */ 974 if (sx >= 0) { 975 ELRE_DEBUG(1, (__F, 976 "second diff insert at %d...\r\n", (int)(nse - new))); 977 terminal_move_to_char(el, (int)(nse - new)); 978 if (ols != oe) { 979 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 980 if (sx > 0) { 981 /* insert sx chars of new starting at nse */ 982 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 983 "ERROR: cannot insert in second diff\n")); 984 terminal_insertwrite(el, nse, sx); 985 } 986 /* 987 * write (nls-nse) - sx chars of new starting at 988 * (nse + sx) 989 */ 990 terminal_overwrite(el, (nse + sx), 991 (size_t)((nls - nse) - sx)); 992 } else { 993 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 994 terminal_overwrite(el, nse, (size_t)(nls - nse)); 995 996 /* 997 * No need to do a clear-to-end here because we were 998 * doing a second insert, so we will have over 999 * written all of the old string. 1000 */ 1001 } 1002 } 1003 ELRE_DEBUG(1, (__F, "done.\r\n")); 1004 } 1005 1006 1007 /* re__copy_and_pad(): 1008 * Copy string and pad with spaces 1009 */ 1010 static void 1011 re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width) 1012 { 1013 size_t i; 1014 1015 for (i = 0; i < width; i++) { 1016 if (*src == '\0') 1017 break; 1018 *dst++ = *src++; 1019 } 1020 1021 for (; i < width; i++) 1022 *dst++ = ' '; 1023 1024 *dst = '\0'; 1025 } 1026 1027 1028 /* re_refresh_cursor(): 1029 * Move to the new cursor position 1030 */ 1031 libedit_private void 1032 re_refresh_cursor(EditLine *el) 1033 { 1034 wchar_t *cp; 1035 int h, v, th, w; 1036 1037 if (el->el_line.cursor >= el->el_line.lastchar) { 1038 if (el->el_map.current == el->el_map.alt 1039 && el->el_line.lastchar != el->el_line.buffer) 1040 el->el_line.cursor = el->el_line.lastchar - 1; 1041 else 1042 el->el_line.cursor = el->el_line.lastchar; 1043 } 1044 1045 /* first we must find where the cursor is... */ 1046 h = el->el_prompt.p_pos.h; 1047 v = el->el_prompt.p_pos.v; 1048 th = el->el_terminal.t_size.h; /* optimize for speed */ 1049 1050 /* do input buffer to el->el_line.cursor */ 1051 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { 1052 switch (ct_chr_class(*cp)) { 1053 case CHTYPE_NL: /* handle newline in data part too */ 1054 h = 0; 1055 v++; 1056 break; 1057 case CHTYPE_TAB: /* if a tab, to next tab stop */ 1058 while (++h & 07) 1059 continue; 1060 break; 1061 default: 1062 w = wcwidth(*cp); 1063 if (w > 1 && h + w > th) { /* won't fit on line */ 1064 h = 0; 1065 v++; 1066 } 1067 h += ct_visual_width(*cp); 1068 break; 1069 } 1070 1071 if (h >= th) { /* check, extra long tabs picked up here also */ 1072 h -= th; 1073 v++; 1074 } 1075 } 1076 /* if we have a next character, and it's a doublewidth one, we need to 1077 * check whether we need to linebreak for it to fit */ 1078 if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1) 1079 if (h + w > th) { 1080 h = 0; 1081 v++; 1082 } 1083 1084 /* now go there */ 1085 terminal_move_to_line(el, v); 1086 terminal_move_to_char(el, h); 1087 terminal__flush(el); 1088 } 1089 1090 1091 /* re_fastputc(): 1092 * Add a character fast. 1093 */ 1094 static void 1095 re_fastputc(EditLine *el, wint_t c) 1096 { 1097 wint_t *lastline; 1098 int w; 1099 1100 w = wcwidth(c); 1101 while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h) 1102 re_fastputc(el, ' '); 1103 1104 terminal__putc(el, c); 1105 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; 1106 while (--w > 0) 1107 el->el_display[el->el_cursor.v][el->el_cursor.h++] 1108 = MB_FILL_CHAR; 1109 1110 if (el->el_cursor.h >= el->el_terminal.t_size.h) { 1111 /* if we must overflow */ 1112 el->el_cursor.h = 0; 1113 1114 /* 1115 * If we would overflow (input is longer than terminal size), 1116 * emulate scroll by dropping first line and shuffling the rest. 1117 * We do this via pointer shuffling - it's safe in this case 1118 * and we avoid memcpy(). 1119 */ 1120 if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) { 1121 int i, lins = el->el_terminal.t_size.v; 1122 1123 lastline = el->el_display[0]; 1124 for(i = 1; i < lins; i++) 1125 el->el_display[i - 1] = el->el_display[i]; 1126 1127 el->el_display[i - 1] = lastline; 1128 } else { 1129 el->el_cursor.v++; 1130 lastline = el->el_display[++el->el_refresh.r_oldcv]; 1131 } 1132 re__copy_and_pad((wchar_t *)lastline, L"", 1133 (size_t)el->el_terminal.t_size.h); 1134 1135 if (EL_HAS_AUTO_MARGINS) { 1136 if (EL_HAS_MAGIC_MARGINS) { 1137 terminal__putc(el, ' '); 1138 terminal__putc(el, '\b'); 1139 } 1140 } else { 1141 terminal__putc(el, '\r'); 1142 terminal__putc(el, '\n'); 1143 } 1144 } 1145 } 1146 1147 1148 /* re_fastaddc(): 1149 * we added just one char, handle it fast. 1150 * Assumes that screen cursor == real cursor 1151 */ 1152 libedit_private void 1153 re_fastaddc(EditLine *el) 1154 { 1155 wchar_t c; 1156 int rhdiff; 1157 1158 c = el->el_line.cursor[-1]; 1159 1160 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { 1161 re_refresh(el); /* too hard to handle */ 1162 return; 1163 } 1164 rhdiff = el->el_terminal.t_size.h - el->el_cursor.h - 1165 el->el_rprompt.p_pos.h; 1166 if (el->el_rprompt.p_pos.h && rhdiff < 3) { 1167 re_refresh(el); /* clear out rprompt if less than 1 char gap */ 1168 return; 1169 } /* else (only do at end of line, no TAB) */ 1170 switch (ct_chr_class(c)) { 1171 case CHTYPE_TAB: /* already handled, should never happen here */ 1172 break; 1173 case CHTYPE_NL: 1174 case CHTYPE_PRINT: 1175 re_fastputc(el, c); 1176 break; 1177 case CHTYPE_ASCIICTL: 1178 case CHTYPE_NONPRINT: { 1179 wchar_t visbuf[VISUAL_WIDTH_MAX]; 1180 ssize_t i, n = 1181 ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c); 1182 for (i = 0; n-- > 0; ++i) 1183 re_fastputc(el, visbuf[i]); 1184 break; 1185 } 1186 } 1187 terminal__flush(el); 1188 } 1189 1190 1191 /* re_clear_display(): 1192 * clear the screen buffers so that new new prompt starts fresh. 1193 */ 1194 libedit_private void 1195 re_clear_display(EditLine *el) 1196 { 1197 int i; 1198 1199 el->el_cursor.v = 0; 1200 el->el_cursor.h = 0; 1201 for (i = 0; i < el->el_terminal.t_size.v; i++) 1202 el->el_display[i][0] = '\0'; 1203 el->el_refresh.r_oldcv = 0; 1204 } 1205 1206 1207 /* re_clear_lines(): 1208 * Make sure all lines are *really* blank 1209 */ 1210 libedit_private void 1211 re_clear_lines(EditLine *el) 1212 { 1213 1214 if (EL_CAN_CEOL) { 1215 int i; 1216 for (i = el->el_refresh.r_oldcv; i >= 0; i--) { 1217 /* for each line on the screen */ 1218 terminal_move_to_line(el, i); 1219 terminal_move_to_char(el, 0); 1220 terminal_clear_EOL(el, el->el_terminal.t_size.h); 1221 } 1222 } else { 1223 terminal_move_to_line(el, el->el_refresh.r_oldcv); 1224 /* go to last line */ 1225 terminal__putc(el, '\r'); /* go to BOL */ 1226 terminal__putc(el, '\n'); /* go to new line */ 1227 } 1228 } 1229