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