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