refresh.c revision 1.28
1/* $NetBSD: refresh.c,v 1.28 2008/09/10 15:45:37 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.28 2008/09/10 15:45:37 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 = (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 = (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 = (nsb - nfd) - (osb - ofd); 661 sx = (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, 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, 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, (nsb - nfd) - fx); 765 re__strncopy(ofd + fx, nfd + fx, 766 (size_t) ((nsb - nfd) - fx)); 767 } else { 768 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 769 term_overwrite(el, nfd, (nsb - nfd)); 770 re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); 771 /* 772 * Done 773 */ 774 return; 775 } 776 } else if (fx < 0) { 777 ELRE_DEBUG(1, 778 (__F, "first diff delete at %d...\r\n", ofd - old)); 779 /* 780 * move to the first char to delete where the first diff is 781 */ 782 term_move_to_char(el, ofd - old); 783 /* 784 * Check if we have stuff to save 785 */ 786 if (osb != oe) { 787 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 788 /* 789 * fx is less than zero *always* here but we check 790 * for code symmetry 791 */ 792 if (fx < 0) { 793 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 794 "ERROR: cannot delete in first diff\n")); 795 term_deletechars(el, -fx); 796 re_delete(el, old, ofd - old, 797 el->el_term.t_size.h, -fx); 798 } 799 /* 800 * write (nsb-nfd) chars of new starting at nfd 801 */ 802 term_overwrite(el, nfd, (nsb - nfd)); 803 re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); 804 805 } else { 806 ELRE_DEBUG(1, (__F, 807 "but with nothing left to save\r\n")); 808 /* 809 * write (nsb-nfd) chars of new starting at nfd 810 */ 811 term_overwrite(el, nfd, (nsb - nfd)); 812 re_clear_eol(el, fx, sx, (oe - old) - (ne - new)); 813 /* 814 * Done 815 */ 816 return; 817 } 818 } else 819 fx = 0; 820 821 if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) { 822 ELRE_DEBUG(1, (__F, 823 "second diff delete at %d...\r\n", (ose - old) + fx)); 824 /* 825 * Check if we have stuff to delete 826 */ 827 /* 828 * fx is the number of characters inserted (+) or deleted (-) 829 */ 830 831 term_move_to_char(el, (ose - old) + fx); 832 /* 833 * Check if we have stuff to save 834 */ 835 if (ols != oe) { 836 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 837 /* 838 * Again a duplicate test. 839 */ 840 if (sx < 0) { 841 ELRE_DEBUG(!EL_CAN_DELETE, (__F, 842 "ERROR: cannot delete in second diff\n")); 843 term_deletechars(el, -sx); 844 } 845 /* 846 * write (nls-nse) chars of new starting at nse 847 */ 848 term_overwrite(el, nse, (nls - nse)); 849 } else { 850 ELRE_DEBUG(1, (__F, 851 "but with nothing left to save\r\n")); 852 term_overwrite(el, nse, (nls - nse)); 853 re_clear_eol(el, fx, sx, (oe - old) - (ne - new)); 854 } 855 } 856 /* 857 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 858 */ 859 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 860 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n", 861 nfd - new)); 862 863 term_move_to_char(el, nfd - new); 864 /* 865 * Check if we have stuff to keep at the end 866 */ 867 if (nsb != ne) { 868 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 869 /* 870 * We have to recalculate fx here because we set it 871 * to zero above as a flag saying that we hadn't done 872 * an early first insert. 873 */ 874 fx = (nsb - nfd) - (osb - ofd); 875 if (fx > 0) { 876 /* 877 * insert fx chars of new starting at nfd 878 */ 879 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 880 "ERROR: cannot insert in late first diff\n")); 881 term_insertwrite(el, nfd, fx); 882 re_insert(el, old, ofd - old, 883 el->el_term.t_size.h, nfd, fx); 884 } 885 /* 886 * write (nsb-nfd) - fx chars of new starting at 887 * (nfd + fx) 888 */ 889 term_overwrite(el, nfd + fx, (nsb - nfd) - fx); 890 re__strncopy(ofd + fx, nfd + fx, 891 (size_t) ((nsb - nfd) - fx)); 892 } else { 893 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 894 term_overwrite(el, nfd, (nsb - nfd)); 895 re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); 896 } 897 } 898 /* 899 * line is now NEW up to nse 900 */ 901 if (sx >= 0) { 902 ELRE_DEBUG(1, (__F, 903 "second diff insert at %d...\r\n", nse - new)); 904 term_move_to_char(el, nse - new); 905 if (ols != oe) { 906 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 907 if (sx > 0) { 908 /* insert sx chars of new starting at nse */ 909 ELRE_DEBUG(!EL_CAN_INSERT, (__F, 910 "ERROR: cannot insert in second diff\n")); 911 term_insertwrite(el, nse, sx); 912 } 913 /* 914 * write (nls-nse) - sx chars of new starting at 915 * (nse + sx) 916 */ 917 term_overwrite(el, nse + sx, (nls - nse) - sx); 918 } else { 919 ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 920 term_overwrite(el, nse, (nls - nse)); 921 922 /* 923 * No need to do a clear-to-end here because we were 924 * doing a second insert, so we will have over 925 * written all of the old string. 926 */ 927 } 928 } 929 ELRE_DEBUG(1, (__F, "done.\r\n")); 930} 931 932 933/* re__copy_and_pad(): 934 * Copy string and pad with spaces 935 */ 936private void 937re__copy_and_pad(char *dst, const char *src, size_t width) 938{ 939 size_t i; 940 941 for (i = 0; i < width; i++) { 942 if (*src == '\0') 943 break; 944 *dst++ = *src++; 945 } 946 947 for (; i < width; i++) 948 *dst++ = ' '; 949 950 *dst = '\0'; 951} 952 953 954/* re_refresh_cursor(): 955 * Move to the new cursor position 956 */ 957protected void 958re_refresh_cursor(EditLine *el) 959{ 960 char *cp, c; 961 int h, v, th; 962 963 if (el->el_line.cursor >= el->el_line.lastchar) { 964 if (el->el_map.current == el->el_map.alt 965 && el->el_line.lastchar != el->el_line.buffer) 966 el->el_line.cursor = el->el_line.lastchar - 1; 967 else 968 el->el_line.cursor = el->el_line.lastchar; 969 } 970 971 /* first we must find where the cursor is... */ 972 h = el->el_prompt.p_pos.h; 973 v = el->el_prompt.p_pos.v; 974 th = el->el_term.t_size.h; /* optimize for speed */ 975 976 /* do input buffer to el->el_line.cursor */ 977 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { 978 c = *cp; 979 h++; /* all chars at least this long */ 980 981 if (c == '\n') {/* handle newline in data part too */ 982 h = 0; 983 v++; 984 } else { 985 if (c == '\t') { /* if a tab, to next tab stop */ 986 while (h & 07) { 987 h++; 988 } 989 } else if (iscntrl((unsigned char) c)) { 990 /* if control char */ 991 h++; 992 if (h > th) { /* if overflow, compensate */ 993 h = 1; 994 v++; 995 } 996 } else if (!isprint((unsigned char) c)) { 997 h += 3; 998 if (h > th) { /* if overflow, compensate */ 999 h = h - th; 1000 v++; 1001 } 1002 } 1003 } 1004 1005 if (h >= th) { /* check, extra long tabs picked up here also */ 1006 h = 0; 1007 v++; 1008 } 1009 } 1010 1011 /* now go there */ 1012 term_move_to_line(el, v); 1013 term_move_to_char(el, h); 1014 term__flush(el); 1015} 1016 1017 1018/* re_fastputc(): 1019 * Add a character fast. 1020 */ 1021private void 1022re_fastputc(EditLine *el, int c) 1023{ 1024 1025 term__putc(el, c); 1026 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; 1027 if (el->el_cursor.h >= el->el_term.t_size.h) { 1028 /* if we must overflow */ 1029 el->el_cursor.h = 0; 1030 1031 /* 1032 * If we would overflow (input is longer than terminal size), 1033 * emulate scroll by dropping first line and shuffling the rest. 1034 * We do this via pointer shuffling - it's safe in this case 1035 * and we avoid memcpy(). 1036 */ 1037 if (el->el_cursor.v + 1 >= el->el_term.t_size.v) { 1038 int i, lins = el->el_term.t_size.v; 1039 char *firstline = el->el_display[0]; 1040 1041 for(i=1; i < lins; i++) 1042 el->el_display[i-1] = el->el_display[i]; 1043 1044 re__copy_and_pad(firstline, "", 0); 1045 el->el_display[i-1] = firstline; 1046 } else { 1047 el->el_cursor.v++; 1048 el->el_refresh.r_oldcv++; 1049 } 1050 if (EL_HAS_AUTO_MARGINS) { 1051 if (EL_HAS_MAGIC_MARGINS) { 1052 term__putc(el, ' '); 1053 term__putc(el, '\b'); 1054 } 1055 } else { 1056 term__putc(el, '\r'); 1057 term__putc(el, '\n'); 1058 } 1059 } 1060} 1061 1062 1063/* re_fastaddc(): 1064 * we added just one char, handle it fast. 1065 * Assumes that screen cursor == real cursor 1066 */ 1067protected void 1068re_fastaddc(EditLine *el) 1069{ 1070 char c; 1071 int rhdiff; 1072 1073 c = el->el_line.cursor[-1]; 1074 1075 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { 1076 re_refresh(el); /* too hard to handle */ 1077 return; 1078 } 1079 rhdiff = el->el_term.t_size.h - el->el_cursor.h - 1080 el->el_rprompt.p_pos.h; 1081 if (el->el_rprompt.p_pos.h && rhdiff < 3) { 1082 re_refresh(el); /* clear out rprompt if less than 1 char gap */ 1083 return; 1084 } /* else (only do at end of line, no TAB) */ 1085 if (iscntrl((unsigned char) c)) { /* if control char, do caret */ 1086 char mc = (c == '\177') ? '?' : (c | 0100); 1087 re_fastputc(el, '^'); 1088 re_fastputc(el, mc); 1089 } else if (isprint((unsigned char) c)) { /* normal char */ 1090 re_fastputc(el, c); 1091 } else { 1092 re_fastputc(el, '\\'); 1093 re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0')); 1094 re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0')); 1095 re_fastputc(el, (c & 7) + '0'); 1096 } 1097 term__flush(el); 1098} 1099 1100 1101/* re_clear_display(): 1102 * clear the screen buffers so that new new prompt starts fresh. 1103 */ 1104protected void 1105re_clear_display(EditLine *el) 1106{ 1107 int i; 1108 1109 el->el_cursor.v = 0; 1110 el->el_cursor.h = 0; 1111 for (i = 0; i < el->el_term.t_size.v; i++) 1112 el->el_display[i][0] = '\0'; 1113 el->el_refresh.r_oldcv = 0; 1114} 1115 1116 1117/* re_clear_lines(): 1118 * Make sure all lines are *really* blank 1119 */ 1120protected void 1121re_clear_lines(EditLine *el) 1122{ 1123 1124 if (EL_CAN_CEOL) { 1125 int i; 1126 term_move_to_char(el, 0); 1127 for (i = 0; i <= el->el_refresh.r_oldcv; i++) { 1128 /* for each line on the screen */ 1129 term_move_to_line(el, i); 1130 term_clear_EOL(el, el->el_term.t_size.h); 1131 } 1132 term_move_to_line(el, 0); 1133 } else { 1134 term_move_to_line(el, el->el_refresh.r_oldcv); 1135 /* go to last line */ 1136 term__putc(el, '\r'); /* go to BOL */ 1137 term__putc(el, '\n'); /* go to new line */ 1138 } 1139} 1140