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