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