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