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