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