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