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