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