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