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