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