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