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