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