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