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