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