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