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