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