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