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