refresh.c revision 1.30
1/* $NetBSD: refresh.c,v 1.30 2009/03/31 17:38:27 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Christos Zoulas of Cornell University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. 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 38static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; 39#else 40__RCSID("$NetBSD: refresh.c,v 1.30 2009/03/31 17:38:27 christos 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 54private void re_addc(EditLine *, int); 55private void re_update_line(EditLine *, char *, char *, int); 56private void re_insert (EditLine *, char *, int, int, char *, int); 57private void re_delete(EditLine *, char *, int, int, int); 58private void re_fastputc(EditLine *, int); 59private void re_clear_eol(EditLine *, int, int, int); 60private void re__strncopy(char *, char *, size_t); 61private void re__copy_and_pad(char *, const char *, size_t); 62 63#ifdef DEBUG_REFRESH 64private 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 */ 77private void 78re_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 */ 95private void 96re_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 */ 137protected void 138re_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 */ 185protected void 186re_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", (size_t)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 */ 321protected void 322re_goto_bottom(EditLine *el) 323{ 324 325 term_move_to_line(el, el->el_refresh.r_oldcv); 326 term__putc(el, '\n'); 327 re_clear_display(el); 328 term__flush(el); 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 */ 336private void 337/*ARGSUSED*/ 338re_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 */ 380private void 381/*ARGSUSED*/ 382re_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 */ 414private void 415re__strncopy(char *a, char *b, size_t n) 416{ 417 418 while (n-- && *b) 419 *a++ = *b++; 420} 421 422/* re_clear_eol(): 423 * Find the number of characters we need to clear till the end of line 424 * in order to make sure that we have cleared the previous contents of 425 * the line. fx and sx is the number of characters inserted or deleted 426 * int the first or second diff, diff is the difference between the 427 * number of characters between the new and old line. 428 */ 429private void 430re_clear_eol(EditLine *el, int fx, int sx, int diff) 431{ 432 433 ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", 434 sx, fx, diff)); 435 436 if (fx < 0) 437 fx = -fx; 438 if (sx < 0) 439 sx = -sx; 440 if (fx > diff) 441 diff = fx; 442 if (sx > diff) 443 diff = sx; 444 445 ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); 446 term_clear_EOL(el, diff); 447} 448 449/***************************************************************** 450 re_update_line() is based on finding the middle difference of each line 451 on the screen; vis: 452 453 /old first difference 454 /beginning of line | /old last same /old EOL 455 v v v v 456old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 457new: eddie> Oh, my little buggy says to me, as lurgid as 458 ^ ^ ^ ^ 459 \beginning of line | \new last same \new end of line 460 \new first difference 461 462 all are character pointers for the sake of speed. Special cases for 463 no differences, as well as for end of line additions must be handled. 464**************************************************************** */ 465 466/* Minimum at which doing an insert it "worth it". This should be about 467 * half the "cost" of going into insert mode, inserting a character, and 468 * going back out. This should really be calculated from the termcap 469 * data... For the moment, a good number for ANSI terminals. 470 */ 471#define MIN_END_KEEP 4 472 473private void 474re_update_line(EditLine *el, char *old, char *new, int i) 475{ 476 char *o, *n, *p, c; 477 char *ofd, *ols, *oe, *nfd, *nls, *ne; 478 char *osb, *ose, *nsb, *nse; 479 int fx, sx; 480 size_t len; 481 482 /* 483 * find first diff 484 */ 485 for (o = old, n = new; *o && (*o == *n); o++, n++) 486 continue; 487 ofd = o; 488 nfd = n; 489 490 /* 491 * Find the end of both old and new 492 */ 493 while (*o) 494 o++; 495 /* 496 * Remove any trailing blanks off of the end, being careful not to 497 * back up past the beginning. 498 */ 499 while (ofd < o) { 500 if (o[-1] != ' ') 501 break; 502 o--; 503 } 504 oe = o; 505 *oe = '\0'; 506 507 while (*n) 508 n++; 509 510 /* remove blanks from end of new */ 511 while (nfd < n) { 512 if (n[-1] != ' ') 513 break; 514 n--; 515 } 516 ne = n; 517 *ne = '\0'; 518 519 /* 520 * if no diff, continue to next line of redraw 521 */ 522 if (*ofd == '\0' && *nfd == '\0') { 523 ELRE_DEBUG(1, (__F, "no difference.\r\n")); 524 return; 525 } 526 /* 527 * find last same pointer 528 */ 529 while ((o > ofd) && (n > nfd) && (*--o == *--n)) 530 continue; 531 ols = ++o; 532 nls = ++n; 533 534 /* 535 * find same begining and same end 536 */ 537 osb = ols; 538 nsb = nls; 539 ose = ols; 540 nse = nls; 541 542 /* 543 * case 1: insert: scan from nfd to nls looking for *ofd 544 */ 545 if (*ofd) { 546 for (c = *ofd, n = nfd; n < nls; n++) { 547 if (c == *n) { 548 for (o = ofd, p = n; 549 p < nls && o < ols && *o == *p; 550 o++, p++) 551 continue; 552 /* 553 * if the new match is longer and it's worth 554 * keeping, then we take it 555 */ 556 if (((nse - nsb) < (p - n)) && 557 (2 * (p - n) > n - nfd)) { 558 nsb = n; 559 nse = p; 560 osb = ofd; 561 ose = o; 562 } 563 } 564 } 565 } 566 /* 567 * case 2: delete: scan from ofd to ols looking for *nfd 568 */ 569 if (*nfd) { 570 for (c = *nfd, o = ofd; o < ols; o++) { 571 if (c == *o) { 572 for (n = nfd, p = o; 573 p < ols && n < nls && *p == *n; 574 p++, n++) 575 continue; 576 /* 577 * if the new match is longer and it's worth 578 * keeping, then we take it 579 */ 580 if (((ose - osb) < (p - o)) && 581 (2 * (p - o) > o - ofd)) { 582 nsb = nfd; 583 nse = n; 584 osb = o; 585 ose = p; 586 } 587 } 588 } 589 } 590 /* 591 * Pragmatics I: If old trailing whitespace or not enough characters to 592 * save to be worth it, then don't save the last same info. 593 */ 594 if ((oe - ols) < MIN_END_KEEP) { 595 ols = oe; 596 nls = ne; 597 } 598 /* 599 * Pragmatics II: if the terminal isn't smart enough, make the data 600 * dumber so the smart update doesn't try anything fancy 601 */ 602 603 /* 604 * fx is the number of characters we need to insert/delete: in the 605 * beginning to bring the two same begins together 606 */ 607 fx = (int)((nsb - nfd) - (osb - ofd)); 608 /* 609 * sx is the number of characters we need to insert/delete: in the 610 * end to bring the two same last parts together 611 */ 612 sx = (int)((nls - nse) - (ols - ose)); 613 614 if (!EL_CAN_INSERT) { 615 if (fx > 0) { 616 osb = ols; 617 ose = ols; 618 nsb = nls; 619 nse = nls; 620 } 621 if (sx > 0) { 622 ols = oe; 623 nls = ne; 624 } 625 if ((ols - ofd) < (nls - nfd)) { 626 ols = oe; 627 nls = ne; 628 } 629 } 630 if (!EL_CAN_DELETE) { 631 if (fx < 0) { 632 osb = ols; 633 ose = ols; 634 nsb = nls; 635 nse = nls; 636 } 637 if (sx < 0) { 638 ols = oe; 639 nls = ne; 640 } 641 if ((ols - ofd) > (nls - nfd)) { 642 ols = oe; 643 nls = ne; 644 } 645 } 646 /* 647 * Pragmatics III: make sure the middle shifted pointers are correct if 648 * they don't point to anything (we may have moved ols or nls). 649 */ 650 /* if the change isn't worth it, don't bother */ 651 /* was: if (osb == ose) */ 652 if ((ose - osb) < MIN_END_KEEP) { 653 osb = ols; 654 ose = ols; 655 nsb = nls; 656 nse = nls; 657 } 658 /* 659 * Now that we are done with pragmatics we recompute fx, sx 660 */ 661 fx = (int)((nsb - nfd) - (osb - ofd)); 662 sx = (int)((nls - nse) - (ols - ose)); 663 664 ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); 665 ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n", 666 ofd - old, osb - old, ose - old, ols - old, oe - old)); 667 ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n", 668 nfd - new, nsb - new, nse - new, nls - new, ne - new)); 669 ELRE_DEBUG(1, (__F, 670 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); 671 ELRE_DEBUG(1, (__F, 672 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); 673#ifdef DEBUG_REFRESH 674 re_printstr(el, "old- oe", old, oe); 675 re_printstr(el, "new- ne", new, ne); 676 re_printstr(el, "old-ofd", old, ofd); 677 re_printstr(el, "new-nfd", new, nfd); 678 re_printstr(el, "ofd-osb", ofd, osb); 679 re_printstr(el, "nfd-nsb", nfd, nsb); 680 re_printstr(el, "osb-ose", osb, ose); 681 re_printstr(el, "nsb-nse", nsb, nse); 682 re_printstr(el, "ose-ols", ose, ols); 683 re_printstr(el, "nse-nls", nse, nls); 684 re_printstr(el, "ols- oe", ols, oe); 685 re_printstr(el, "nls- ne", nls, ne); 686#endif /* DEBUG_REFRESH */ 687 688 /* 689 * el_cursor.v to this line i MUST be in this routine so that if we 690 * don't have to change the line, we don't move to it. el_cursor.h to 691 * first diff char 692 */ 693 term_move_to_line(el, i); 694 695 /* 696 * at this point we have something like this: 697 * 698 * /old /ofd /osb /ose /ols /oe 699 * v.....................v v..................v v........v 700 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as 701 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as 702 * ^.....................^ ^..................^ ^........^ 703 * \new \nfd \nsb \nse \nls \ne 704 * 705 * fx is the difference in length between the chars between nfd and 706 * nsb, and the chars between ofd and osb, and is thus the number of 707 * characters to delete if < 0 (new is shorter than old, as above), 708 * or insert (new is longer than short). 709 * 710 * sx is the same for the second differences. 711 */ 712 713 /* 714 * if we have a net insert on the first difference, AND inserting the 715 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful 716 * character (which is ne if nls != ne, otherwise is nse) off the edge 717 * of the screen (el->el_term.t_size.h) else we do the deletes first 718 * so that we keep everything we need to. 719 */ 720 721 /* 722 * if the last same is the same like the end, there is no last same 723 * part, otherwise we want to keep the last same part set p to the 724 * last useful old character 725 */ 726 p = (ols != oe) ? oe : ose; 727 728 /* 729 * if (There is a diffence in the beginning) && (we need to insert 730 * characters) && (the number of characters to insert is less than 731 * the term width) 732 * We need to do an insert! 733 * else if (we need to delete characters) 734 * We need to delete characters! 735 * else 736 * No insert or delete 737 */ 738 if ((nsb != nfd) && fx > 0 && 739 ((p - old) + fx <= el->el_term.t_size.h)) { 740 ELRE_DEBUG(1, 741 (__F, "first diff insert at %d...\r\n", nfd - new)); 742 /* 743 * Move to the first char to insert, where the first diff is. 744 */ 745 term_move_to_char(el, (int)(nfd - new)); 746 /* 747 * Check if we have stuff to keep at end 748 */ 749 if (nsb != ne) { 750 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 751 /* 752 * insert fx chars of new starting at nfd 753 */ 754 if (fx > 0) { 755 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 756 "ERROR: cannot insert in early first diff\n")); 757 term_insertwrite(el, nfd, fx); 758 re_insert(el, old, (int)(ofd - old), 759 el->el_term.t_size.h, nfd, fx); 760 } 761 /* 762 * write (nsb-nfd) - fx chars of new starting at 763 * (nfd + fx) 764 */ 765 len = (size_t) ((nsb - nfd) - fx); 766 term_overwrite(el, (nfd + fx), len); 767 re__strncopy(ofd + fx, nfd + fx, len); 768 } else { 769 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 770 len = (size_t)(nsb - nfd); 771 term_overwrite(el, nfd, len); 772 re__strncopy(ofd, nfd, len); 773 /* 774 * Done 775 */ 776 return; 777 } 778 } else if (fx < 0) { 779 ELRE_DEBUG(1, 780 (__F, "first diff delete at %d...\r\n", ofd - old)); 781 /* 782 * move to the first char to delete where the first diff is 783 */ 784 term_move_to_char(el, (int)(ofd - old)); 785 /* 786 * Check if we have stuff to save 787 */ 788 if (osb != oe) { 789 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 790 /* 791 * fx is less than zero *always* here but we check 792 * for code symmetry 793 */ 794 if (fx < 0) { 795 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 796 "ERROR: cannot delete in first diff\n")); 797 term_deletechars(el, -fx); 798 re_delete(el, old, (int)(ofd - old), 799 el->el_term.t_size.h, -fx); 800 } 801 /* 802 * write (nsb-nfd) chars of new starting at nfd 803 */ 804 len = (size_t) (nsb - nfd); 805 term_overwrite(el, nfd, len); 806 re__strncopy(ofd, nfd, len); 807 808 } else { 809 ELRE_DEBUG(1, (__F, 810 "but with nothing left to save\r\n")); 811 /* 812 * write (nsb-nfd) chars of new starting at nfd 813 */ 814 term_overwrite(el, nfd, (size_t)(nsb - nfd)); 815 re_clear_eol(el, fx, sx, 816 (int)((oe - old) - (ne - new))); 817 /* 818 * Done 819 */ 820 return; 821 } 822 } else 823 fx = 0; 824 825 if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) { 826 ELRE_DEBUG(1, (__F, 827 "second diff delete at %d...\r\n", (ose - old) + fx)); 828 /* 829 * Check if we have stuff to delete 830 */ 831 /* 832 * fx is the number of characters inserted (+) or deleted (-) 833 */ 834 835 term_move_to_char(el, (int)((ose - old) + fx)); 836 /* 837 * Check if we have stuff to save 838 */ 839 if (ols != oe) { 840 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 841 /* 842 * Again a duplicate test. 843 */ 844 if (sx < 0) { 845 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 846 "ERROR: cannot delete in second diff\n")); 847 term_deletechars(el, -sx); 848 } 849 /* 850 * write (nls-nse) chars of new starting at nse 851 */ 852 term_overwrite(el, nse, (size_t)(nls - nse)); 853 } else { 854 ELRE_DEBUG(1, (__F, 855 "but with nothing left to save\r\n")); 856 term_overwrite(el, nse, (size_t)(nls - nse)); 857 re_clear_eol(el, fx, sx, 858 (int)((oe - old) - (ne - new))); 859 } 860 } 861 /* 862 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 863 */ 864 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 865 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n", 866 nfd - new)); 867 868 term_move_to_char(el, (int)(nfd - new)); 869 /* 870 * Check if we have stuff to keep at the end 871 */ 872 if (nsb != ne) { 873 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 874 /* 875 * We have to recalculate fx here because we set it 876 * to zero above as a flag saying that we hadn't done 877 * an early first insert. 878 */ 879 fx = (int)((nsb - nfd) - (osb - ofd)); 880 if (fx > 0) { 881 /* 882 * insert fx chars of new starting at nfd 883 */ 884 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 885 "ERROR: cannot insert in late first diff\n")); 886 term_insertwrite(el, nfd, fx); 887 re_insert(el, old, (int)(ofd - old), 888 el->el_term.t_size.h, nfd, fx); 889 } 890 /* 891 * write (nsb-nfd) - fx chars of new starting at 892 * (nfd + fx) 893 */ 894 len = (size_t) ((nsb - nfd) - fx); 895 term_overwrite(el, (nfd + fx), len); 896 re__strncopy(ofd + fx, nfd + fx, len); 897 } else { 898 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 899 len = (size_t) (nsb - nfd); 900 term_overwrite(el, nfd, len); 901 re__strncopy(ofd, nfd, len); 902 } 903 } 904 /* 905 * line is now NEW up to nse 906 */ 907 if (sx >= 0) { 908 ELRE_DEBUG(1, (__F, 909 "second diff insert at %d...\r\n", (int)(nse - new))); 910 term_move_to_char(el, (int)(nse - new)); 911 if (ols != oe) { 912 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 913 if (sx > 0) { 914 /* insert sx chars of new starting at nse */ 915 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 916 "ERROR: cannot insert in second diff\n")); 917 term_insertwrite(el, nse, sx); 918 } 919 /* 920 * write (nls-nse) - sx chars of new starting at 921 * (nse + sx) 922 */ 923 term_overwrite(el, (nse + sx), 924 (size_t)((nls - nse) - sx)); 925 } else { 926 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 927 term_overwrite(el, nse, (size_t)(nls - nse)); 928 929 /* 930 * No need to do a clear-to-end here because we were 931 * doing a second insert, so we will have over 932 * written all of the old string. 933 */ 934 } 935 } 936 ELRE_DEBUG(1, (__F, "done.\r\n")); 937} 938 939 940/* re__copy_and_pad(): 941 * Copy string and pad with spaces 942 */ 943private void 944re__copy_and_pad(char *dst, const char *src, size_t width) 945{ 946 size_t i; 947 948 for (i = 0; i < width; i++) { 949 if (*src == '\0') 950 break; 951 *dst++ = *src++; 952 } 953 954 for (; i < width; i++) 955 *dst++ = ' '; 956 957 *dst = '\0'; 958} 959 960 961/* re_refresh_cursor(): 962 * Move to the new cursor position 963 */ 964protected void 965re_refresh_cursor(EditLine *el) 966{ 967 char *cp, c; 968 int h, v, th; 969 970 if (el->el_line.cursor >= el->el_line.lastchar) { 971 if (el->el_map.current == el->el_map.alt 972 && el->el_line.lastchar != el->el_line.buffer) 973 el->el_line.cursor = el->el_line.lastchar - 1; 974 else 975 el->el_line.cursor = el->el_line.lastchar; 976 } 977 978 /* first we must find where the cursor is... */ 979 h = el->el_prompt.p_pos.h; 980 v = el->el_prompt.p_pos.v; 981 th = el->el_term.t_size.h; /* optimize for speed */ 982 983 /* do input buffer to el->el_line.cursor */ 984 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { 985 c = *cp; 986 h++; /* all chars at least this long */ 987 988 if (c == '\n') {/* handle newline in data part too */ 989 h = 0; 990 v++; 991 } else { 992 if (c == '\t') { /* if a tab, to next tab stop */ 993 while (h & 07) { 994 h++; 995 } 996 } else if (iscntrl((unsigned char) c)) { 997 /* if control char */ 998 h++; 999 if (h > th) { /* if overflow, compensate */ 1000 h = 1; 1001 v++; 1002 } 1003 } else if (!isprint((unsigned char) c)) { 1004 h += 3; 1005 if (h > th) { /* if overflow, compensate */ 1006 h = h - th; 1007 v++; 1008 } 1009 } 1010 } 1011 1012 if (h >= th) { /* check, extra long tabs picked up here also */ 1013 h = 0; 1014 v++; 1015 } 1016 } 1017 1018 /* now go there */ 1019 term_move_to_line(el, v); 1020 term_move_to_char(el, h); 1021 term__flush(el); 1022} 1023 1024 1025/* re_fastputc(): 1026 * Add a character fast. 1027 */ 1028private void 1029re_fastputc(EditLine *el, int c) 1030{ 1031 1032 term__putc(el, c); 1033 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; 1034 if (el->el_cursor.h >= el->el_term.t_size.h) { 1035 /* if we must overflow */ 1036 el->el_cursor.h = 0; 1037 1038 /* 1039 * If we would overflow (input is longer than terminal size), 1040 * emulate scroll by dropping first line and shuffling the rest. 1041 * We do this via pointer shuffling - it's safe in this case 1042 * and we avoid memcpy(). 1043 */ 1044 if (el->el_cursor.v + 1 >= el->el_term.t_size.v) { 1045 int i, lins = el->el_term.t_size.v; 1046 char *firstline = el->el_display[0]; 1047 1048 for(i=1; i < lins; i++) 1049 el->el_display[i-1] = el->el_display[i]; 1050 1051 re__copy_and_pad(firstline, "", 0); 1052 el->el_display[i-1] = firstline; 1053 } else { 1054 el->el_cursor.v++; 1055 el->el_refresh.r_oldcv++; 1056 } 1057 if (EL_HAS_AUTO_MARGINS) { 1058 if (EL_HAS_MAGIC_MARGINS) { 1059 term__putc(el, ' '); 1060 term__putc(el, '\b'); 1061 } 1062 } else { 1063 term__putc(el, '\r'); 1064 term__putc(el, '\n'); 1065 } 1066 } 1067} 1068 1069 1070/* re_fastaddc(): 1071 * we added just one char, handle it fast. 1072 * Assumes that screen cursor == real cursor 1073 */ 1074protected void 1075re_fastaddc(EditLine *el) 1076{ 1077 char c; 1078 int rhdiff; 1079 1080 c = el->el_line.cursor[-1]; 1081 1082 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { 1083 re_refresh(el); /* too hard to handle */ 1084 return; 1085 } 1086 rhdiff = el->el_term.t_size.h - el->el_cursor.h - 1087 el->el_rprompt.p_pos.h; 1088 if (el->el_rprompt.p_pos.h && rhdiff < 3) { 1089 re_refresh(el); /* clear out rprompt if less than 1 char gap */ 1090 return; 1091 } /* else (only do at end of line, no TAB) */ 1092 if (iscntrl((unsigned char) c)) { /* if control char, do caret */ 1093 char mc = (c == '\177') ? '?' : (c | 0100); 1094 re_fastputc(el, '^'); 1095 re_fastputc(el, mc); 1096 } else if (isprint((unsigned char) c)) { /* normal char */ 1097 re_fastputc(el, c); 1098 } else { 1099 re_fastputc(el, '\\'); 1100 re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0')); 1101 re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0')); 1102 re_fastputc(el, (c & 7) + '0'); 1103 } 1104 term__flush(el); 1105} 1106 1107 1108/* re_clear_display(): 1109 * clear the screen buffers so that new new prompt starts fresh. 1110 */ 1111protected void 1112re_clear_display(EditLine *el) 1113{ 1114 int i; 1115 1116 el->el_cursor.v = 0; 1117 el->el_cursor.h = 0; 1118 for (i = 0; i < el->el_term.t_size.v; i++) 1119 el->el_display[i][0] = '\0'; 1120 el->el_refresh.r_oldcv = 0; 1121} 1122 1123 1124/* re_clear_lines(): 1125 * Make sure all lines are *really* blank 1126 */ 1127protected void 1128re_clear_lines(EditLine *el) 1129{ 1130 1131 if (EL_CAN_CEOL) { 1132 int i; 1133 term_move_to_char(el, 0); 1134 for (i = 0; i <= el->el_refresh.r_oldcv; i++) { 1135 /* for each line on the screen */ 1136 term_move_to_line(el, i); 1137 term_clear_EOL(el, el->el_term.t_size.h); 1138 } 1139 term_move_to_line(el, 0); 1140 } else { 1141 term_move_to_line(el, el->el_refresh.r_oldcv); 1142 /* go to last line */ 1143 term__putc(el, '\r'); /* go to BOL */ 1144 term__putc(el, '\n'); /* go to new line */ 1145 } 1146} 1147