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