refresh.c revision 1.12
1/* $NetBSD: refresh.c,v 1.12 2000/01/20 22:56:21 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.12 2000/01/20 22:56:21 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 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 */ 83private void 84re_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 */ 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 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 */ 147protected void 148re_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 */ 173protected void 174re_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 */ 281protected void 282re_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 */ 297private void 298/*ARGSUSED*/ 299re_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 */ 343private void 344/*ARGSUSED*/ 345re_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 */ 378private void 379re__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 395old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 396new: 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 412private void 413re_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 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) { 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 term_clear_EOL(el, (oe - old) - (ne - new)); 790 } 791 } 792 793 /* 794 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 795 */ 796 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 797 ELRE_DEBUG(1,(__F, "late first diff insert at %d...\r\n", nfd - new),); 798 799 term_move_to_char(el, nfd - new); 800 /* 801 * Check if we have stuff to keep at the end 802 */ 803 if (nsb != ne) { 804 ELRE_DEBUG(1,(__F, "with stuff to keep at end\r\n"),); 805 /* 806 * We have to recalculate fx here because we set it 807 * to zero above as a flag saying that we hadn't done 808 * an early first insert. 809 */ 810 fx = (nsb - nfd) - (osb - ofd); 811 if (fx > 0) { 812 /* 813 * insert fx chars of new starting at nfd 814 */ 815 ELRE_DEBUG(!EL_CAN_INSERT, 816 (__F, "ERROR: cannot insert in late first diff\n"),); 817 term_insertwrite(el, nfd, fx); 818 re_insert(el, old, ofd - old, el->el_term.t_size.h, nfd, fx); 819 } 820 821 /* 822 * write (nsb-nfd) - fx chars of new starting at (nfd + fx) 823 */ 824 term_overwrite(el, nfd + fx, (nsb - nfd) - fx); 825 re__strncopy(ofd + fx, nfd + fx, (size_t)((nsb - nfd) - fx)); 826 } 827 else { 828 ELRE_DEBUG(1,(__F, "without anything to save\r\n"),); 829 term_overwrite(el, nfd, (nsb - nfd)); 830 re__strncopy(ofd, nfd, (size_t)(nsb - nfd)); 831 } 832 } 833 834 /* 835 * line is now NEW up to nse 836 */ 837 if (sx >= 0) { 838 ELRE_DEBUG(1,(__F, "second diff insert at %d...\r\n", nse - new),); 839 term_move_to_char(el, nse - new); 840 if (ols != oe) { 841 ELRE_DEBUG(1,(__F, "with stuff to keep at end\r\n"),); 842 if (sx > 0) { 843 /* insert sx chars of new starting at nse */ 844 ELRE_DEBUG(!EL_CAN_INSERT, 845 (__F, "ERROR: cannot insert in second diff\n"),); 846 term_insertwrite(el, nse, sx); 847 } 848 849 /* 850 * write (nls-nse) - sx chars of new starting at (nse + sx) 851 */ 852 term_overwrite(el, nse + sx, (nls - nse) - sx); 853 } 854 else { 855 ELRE_DEBUG(1,(__F, "without anything to save\r\n"),); 856 term_overwrite(el, nse, (nls - nse)); 857 858 /* 859 * No need to do a clear-to-end here because we were doing 860 * a second insert, so we will have over written all of the 861 * old string. 862 */ 863 } 864 } 865 ELRE_DEBUG(1,(__F, "done.\r\n"),); 866} /* re_update_line */ 867 868 869/* re__copy_and_pad(): 870 * Copy string and pad with spaces 871 */ 872private void 873re__copy_and_pad(dst, src, width) 874 char *dst, *src; 875 size_t width; 876{ 877 int i; 878 879 for (i = 0; i < width; i++) { 880 if (*src == '\0') 881 break; 882 *dst++ = *src++; 883 } 884 885 while (i < width) { 886 *dst++ = ' '; 887 i++; 888 } 889 *dst = '\0'; 890} /* end re__copy_and_pad */ 891 892 893/* re_refresh_cursor(): 894 * Move to the new cursor position 895 */ 896protected void 897re_refresh_cursor(el) 898 EditLine *el; 899{ 900 char *cp, c; 901 int h, v, th; 902 903 /* first we must find where the cursor is... */ 904 h = el->el_prompt.p_pos.h; 905 v = el->el_prompt.p_pos.v; 906 th = el->el_term.t_size.h; /* optimize for speed */ 907 908 /* do input buffer to el->el_line.cursor */ 909 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { 910 c = *cp; 911 h++; /* all chars at least this long */ 912 913 if (c == '\n') { /* handle newline in data part too */ 914 h = 0; 915 v++; 916 } 917 else { 918 if (c == '\t') { /* if a tab, to next tab stop */ 919 while (h & 07) { 920 h++; 921 } 922 } 923 else if (iscntrl((unsigned char) c)) { /* if control char */ 924 h++; 925 if (h > th) { /* if overflow, compensate */ 926 h = 1; 927 v++; 928 } 929 } 930 else if (!isprint((unsigned char) c)) { 931 h += 3; 932 if (h > th) { /* if overflow, compensate */ 933 h = h - th; 934 v++; 935 } 936 } 937 } 938 939 if (h >= th) { /* check, extra long tabs picked up here also */ 940 h = 0; 941 v++; 942 } 943 } 944 945 /* now go there */ 946 term_move_to_line(el, v); 947 term_move_to_char(el, h); 948 term__flush(); 949} /* re_refresh_cursor */ 950 951 952/* re_fastputc(): 953 * Add a character fast. 954 */ 955private void 956re_fastputc(el, c) 957 EditLine *el; 958 int c; 959{ 960 term__putc(c); 961 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; 962 if (el->el_cursor.h >= el->el_term.t_size.h) { 963 /* if we must overflow */ 964 el->el_cursor.h = 0; 965 el->el_cursor.v++; 966 el->el_refresh.r_oldcv++; 967 if (EL_HAS_AUTO_MARGINS) { 968 if (EL_HAS_MAGIC_MARGINS) { 969 term__putc(' '); 970 term__putc('\b'); 971 } 972 } 973 else { 974 term__putc('\r'); 975 term__putc('\n'); 976 } 977 } 978} /* end re_fastputc */ 979 980 981/* re_fastaddc(): 982 * we added just one char, handle it fast. 983 * Assumes that screen cursor == real cursor 984 */ 985protected void 986re_fastaddc(el) 987 EditLine *el; 988{ 989 char c; 990 int rhdiff; 991 992 c = el->el_line.cursor[-1]; 993 994 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { 995 re_refresh(el); /* too hard to handle */ 996 return; 997 } 998 999 rhdiff = el->el_term.t_size.h - el->el_cursor.h - el->el_rprompt.p_pos.h; 1000 if (el->el_rprompt.p_pos.h && rhdiff < 3) { 1001 re_refresh(el); /* clear out rprompt if less than 1 char gap */ 1002 return; 1003 } /* else (only do at end of line, no TAB) */ 1004 1005 if (iscntrl((unsigned char) c)) { /* if control char, do caret */ 1006 char mc = (c == '\177') ? '?' : (c | 0100); 1007 re_fastputc(el, '^'); 1008 re_fastputc(el, mc); 1009 } 1010 else if (isprint((unsigned char) c)) { /* normal char */ 1011 re_fastputc(el, c); 1012 } 1013 else { 1014 re_fastputc(el, '\\'); 1015 re_fastputc(el, (int)((((unsigned int)c >> 6) & 7) + '0')); 1016 re_fastputc(el, (int)((((unsigned int)c >> 3) & 7) + '0')); 1017 re_fastputc(el, (c & 7) + '0'); 1018 } 1019 term__flush(); 1020} /* end re_fastaddc */ 1021 1022 1023/* re_clear_display(): 1024 * clear the screen buffers so that new new prompt starts fresh. 1025 */ 1026protected void 1027re_clear_display(el) 1028 EditLine *el; 1029{ 1030 int i; 1031 1032 el->el_cursor.v = 0; 1033 el->el_cursor.h = 0; 1034 for (i = 0; i < el->el_term.t_size.v; i++) 1035 el->el_display[i][0] = '\0'; 1036 el->el_refresh.r_oldcv = 0; 1037} /* end re_clear_display */ 1038 1039 1040/* re_clear_lines(): 1041 * Make sure all lines are *really* blank 1042 */ 1043protected void 1044re_clear_lines(el) 1045 EditLine *el; 1046{ 1047 if (EL_CAN_CEOL) { 1048 int i; 1049 term_move_to_char(el, 0); 1050 for (i = 0; i <= el->el_refresh.r_oldcv; i++) { 1051 /* for each line on the screen */ 1052 term_move_to_line(el, i); 1053 term_clear_EOL(el, el->el_term.t_size.h); 1054 } 1055 term_move_to_line(el, 0); 1056 } 1057 else { 1058 term_move_to_line(el, el->el_refresh.r_oldcv); /* go to last line */ 1059 term__putc('\r'); /* go to BOL */ 1060 term__putc('\n'); /* go to new line */ 1061 } 1062} /* end re_clear_lines */ 1063