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