1 /* $Header: /src/pub/tcsh/ed.refresh.c,v 3.25 1998/11/24 18:17:22 christos Exp $ */ 2 /* 3 * ed.refresh.c: Lower level screen refreshing functions 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #include "sh.h" 38 39 RCSID("$Id: ed.refresh.c,v 3.25 1998/11/24 18:17:22 christos Exp $") 40 41 #include "ed.h" 42 /* #define DEBUG_UPDATE */ 43 /* #define DEBUG_REFRESH */ 44 /* #define DEBUG_LITERAL */ 45 46 /* refresh.c -- refresh the current set of lines on the screen */ 47 48 Char *litptr[256]; 49 static int vcursor_h, vcursor_v; 50 static int rprompt_h, rprompt_v; 51 52 static void Draw __P((int)); 53 static void Vdraw __P((int)); 54 static void RefreshPromptpart __P((Char *)); 55 static void update_line __P((Char *, Char *, int)); 56 static void str_insert __P((Char *, int, int, Char *, int)); 57 static void str_delete __P((Char *, int, int, int)); 58 static void str_cp __P((Char *, Char *, int)); 59 static void PutPlusOne __P((int)); 60 static void cpy_pad_spaces __P((Char *, Char *, int)); 61 #if defined(DSPMBYTE) 62 static Char *update_line_fix_mbyte_point __P((Char *, Char *, int)); 63 #endif 64 #if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL) 65 static void dprintf __P((char *, ...)); 66 #ifdef DEBUG_UPDATE 67 static void dprintstr __P((char *, Char *, Char *)); 68 69 static void 70 dprintstr(str, f, t) 71 char *str; 72 Char *f, *t; 73 { 74 dprintf("%s:\"", str); 75 while (f < t) 76 dprintf("%c", *f++ & ASCII); 77 dprintf("\"\r\n"); 78 } 79 #endif /* DEBUG_UPDATE */ 80 81 /* dprintf(): 82 * Print to $DEBUGTTY, so that we can test editing on one pty, and 83 * print debugging stuff on another. Don't interrupt the shell while 84 * debugging cause you'll mangle up the file descriptors! 85 */ 86 static void 87 #ifdef FUNCPROTO 88 dprintf(char *fmt, ...) 89 #else 90 dprintf(va_list) 91 va_dcl 92 #endif /* __STDC__ */ 93 { 94 static int fd = -1; 95 char *dtty; 96 97 if ((dtty = getenv("DEBUGTTY"))) { 98 int o; 99 va_list va; 100 #ifdef FUNCPROTO 101 va_start(va, fmt); 102 #else 103 char *fmt; 104 va_start(va); 105 fmt = va_arg(va, char *); 106 #endif /* __STDC__ */ 107 108 if (fd == -1) 109 fd = open(dtty, O_RDWR); 110 o = SHOUT; 111 flush(); 112 SHOUT = fd; 113 xvprintf(fmt, va); 114 va_end(va); 115 flush(); 116 SHOUT = o; 117 } 118 } 119 #endif /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */ 120 121 static void 122 Draw(c) /* draw c, expand tabs, ctl chars */ 123 register int c; 124 { 125 register Char ch = c & CHAR; 126 127 if (Isprint(ch)) { 128 Vdraw(c); 129 return; 130 } 131 /* from wolman%crltrx.DEC@decwrl.dec.com (Alec Wolman) */ 132 if (ch == '\n') { /* expand the newline */ 133 /* 134 * Don't force a newline if Vdraw does it (i.e. we're at end of line) 135 * - or we will get two newlines and possibly garbage in between 136 */ 137 int oldv = vcursor_v; 138 139 Vdraw('\0'); /* assure end of line */ 140 if (oldv == vcursor_v) { 141 vcursor_h = 0; /* reset cursor pos */ 142 vcursor_v++; 143 } 144 return; 145 } 146 if (ch == '\t') { /* expand the tab */ 147 for (;;) { 148 Vdraw(' '); 149 if ((vcursor_h & 07) == 0) 150 break; /* go until tab stop */ 151 } 152 } 153 else if (Iscntrl(ch)) { 154 #ifndef _OSD_POSIX 155 Vdraw('^'); 156 if (ch == CTL_ESC('\177')) { 157 Vdraw('?'); 158 } 159 else { 160 /* uncontrolify it; works only for iso8859-1 like sets */ 161 Vdraw((c | 0100)); 162 #else /*_OSD_POSIX*/ 163 if (ch == CTL_ESC('\177')) { 164 Vdraw('^'); 165 Vdraw('?'); 166 } 167 else { 168 if (Isupper(_toebcdic[_toascii[c]|0100]) 169 || strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL) 170 { 171 Vdraw('^'); 172 Vdraw(_toebcdic[_toascii[c]|0100]); 173 } 174 else 175 { 176 Vdraw('\\'); 177 Vdraw(((c >> 6) & 7) + '0'); 178 Vdraw(((c >> 3) & 7) + '0'); 179 Vdraw((c & 7) + '0'); 180 } 181 #endif /*_OSD_POSIX*/ 182 } 183 } 184 #ifdef KANJI 185 else if (!adrof(STRnokanji)) { 186 Vdraw(c); 187 return; 188 } 189 #endif 190 else { 191 Vdraw('\\'); 192 Vdraw(((c >> 6) & 7) + '0'); 193 Vdraw(((c >> 3) & 7) + '0'); 194 Vdraw((c & 7) + '0'); 195 } 196 } 197 198 static void 199 Vdraw(c) /* draw char c onto V lines */ 200 register int c; 201 { 202 #ifdef DEBUG_REFRESH 203 # ifdef SHORT_STRINGS 204 dprintf("Vdrawing %6.6o '%c'\r\n", c, c & ASCII); 205 # else 206 dprintf("Vdrawing %3.3o '%c'\r\n", c, c); 207 # endif /* SHORT_STRNGS */ 208 #endif /* DEBUG_REFRESH */ 209 210 Vdisplay[vcursor_v][vcursor_h] = (Char) c; 211 vcursor_h++; /* advance to next place */ 212 if (vcursor_h >= TermH) { 213 Vdisplay[vcursor_v][TermH] = '\0'; /* assure end of line */ 214 vcursor_h = 0; /* reset it. */ 215 vcursor_v++; 216 #ifdef DEBUG_REFRESH 217 if (vcursor_v >= TermV) { /* should NEVER happen. */ 218 dprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n", 219 vcursor_v, TermV); 220 abort(); 221 } 222 #endif /* DEBUG_REFRESH */ 223 } 224 } 225 226 /* 227 * RefreshPromptpart() 228 * draws a prompt element, expanding literals (we know it's ASCIZ) 229 */ 230 static void 231 RefreshPromptpart(buf) 232 Char *buf; 233 { 234 register Char *cp; 235 static unsigned int litnum = 0; 236 if (buf == NULL) 237 { 238 litnum = 0; 239 return; 240 } 241 242 for (cp = buf; *cp; cp++) { 243 if (*cp & LITERAL) { 244 if (litnum < (sizeof(litptr) / sizeof(litptr[0]))) { 245 litptr[litnum] = cp; 246 #ifdef DEBUG_LITERAL 247 dprintf("litnum = %d, litptr = %x:\r\n", 248 litnum, litptr[litnum]); 249 #endif /* DEBUG_LITERAL */ 250 } 251 while (*cp & LITERAL) 252 cp++; 253 if (*cp) 254 Vdraw((int) (litnum++ | LITERAL)); 255 else { 256 /* 257 * XXX: This is a bug, we lose the last literal, if it is not 258 * followed by a normal character, but it is too hard to fix 259 */ 260 break; 261 } 262 } 263 else 264 Draw(*cp); 265 } 266 } 267 268 /* 269 * Refresh() 270 * draws the new virtual screen image from the current input 271 * line, then goes line-by-line changing the real image to the new 272 * virtual image. The routine to re-draw a line can be replaced 273 * easily in hopes of a smarter one being placed there. 274 */ 275 static int OldvcV = 0; 276 void 277 Refresh() 278 { 279 register int cur_line; 280 register Char *cp; 281 int cur_h, cur_v = 0, new_vcv; 282 int rhdiff; 283 Char oldgetting; 284 285 #ifdef DEBUG_REFRESH 286 dprintf("PromptBuf = :%s:\r\n", short2str(PromptBuf)); 287 dprintf("InputBuf = :%s:\r\n", short2str(InputBuf)); 288 #endif /* DEBUG_REFRESH */ 289 oldgetting = GettingInput; 290 GettingInput = 0; /* avoid re-entrance via SIGWINCH */ 291 292 /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */ 293 vcursor_h = 0; 294 vcursor_v = 0; 295 RefreshPromptpart(NULL); 296 RefreshPromptpart(RPromptBuf); 297 rprompt_h = vcursor_h; 298 rprompt_v = vcursor_v; 299 300 /* reset the Vdraw cursor, draw prompt */ 301 vcursor_h = 0; 302 vcursor_v = 0; 303 RefreshPromptpart(NULL); 304 RefreshPromptpart(PromptBuf); 305 cur_h = -1; /* set flag in case I'm not set */ 306 307 /* draw the current input buffer */ 308 for (cp = InputBuf; (cp < LastChar); cp++) { 309 if (cp == Cursor) { 310 cur_h = vcursor_h; /* save for later */ 311 cur_v = vcursor_v; 312 } 313 Draw(*cp); 314 } 315 316 if (cur_h == -1) { /* if I haven't been set yet, I'm at the end */ 317 cur_h = vcursor_h; 318 cur_v = vcursor_v; 319 } 320 321 rhdiff = TermH - vcursor_h - rprompt_h; 322 if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) { 323 /* 324 * have a right-hand side prompt that will fit on 325 * the end of the first line with at least one 326 * character gap to the input buffer. 327 */ 328 while (--rhdiff > 0) /* pad out with spaces */ 329 Draw(' '); 330 RefreshPromptpart(RPromptBuf); 331 } 332 else { 333 rprompt_h = 0; /* flag "not using rprompt" */ 334 rprompt_v = 0; 335 } 336 337 new_vcv = vcursor_v; /* must be done BEFORE the NUL is written */ 338 Vdraw('\0'); /* put NUL on end */ 339 340 #ifdef DEBUG_REFRESH 341 dprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n", 342 TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0])); 343 #endif /* DEBUG_REFRESH */ 344 345 #ifdef DEBUG_UPDATE 346 dprintf("updating %d lines.\r\n", new_vcv); 347 #endif /* DEBUG_UPDATE */ 348 for (cur_line = 0; cur_line <= new_vcv; cur_line++) { 349 /* NOTE THAT update_line MAY CHANGE Display[cur_line] */ 350 update_line(Display[cur_line], Vdisplay[cur_line], cur_line); 351 #ifdef WINNT 352 flush(); 353 #endif /* WINNT */ 354 355 /* 356 * Copy the new line to be the current one, and pad out with spaces 357 * to the full width of the terminal so that if we try moving the 358 * cursor by writing the character that is at the end of the 359 * screen line, it won't be a NUL or some old leftover stuff. 360 */ 361 cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH); 362 #ifdef notdef 363 (void) Strncpy(Display[cur_line], Vdisplay[cur_line], (size_t) TermH); 364 Display[cur_line][TermH] = '\0'; /* just in case */ 365 #endif 366 } 367 #ifdef DEBUG_REFRESH 368 dprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n", 369 vcursor_v, OldvcV, cur_line); 370 #endif /* DEBUG_REFRESH */ 371 if (OldvcV > new_vcv) { 372 for (; cur_line <= OldvcV; cur_line++) { 373 update_line(Display[cur_line], STRNULL, cur_line); 374 *Display[cur_line] = '\0'; 375 } 376 } 377 OldvcV = new_vcv; /* set for next time */ 378 #ifdef DEBUG_REFRESH 379 dprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n", 380 CursorH, CursorV, cur_h, cur_v); 381 #endif /* DEBUG_REFRESH */ 382 #ifdef WINNT 383 flush(); 384 #endif /* WINNT */ 385 MoveToLine(cur_v); /* go to where the cursor is */ 386 MoveToChar(cur_h); 387 SetAttributes(0); /* Clear all attributes */ 388 flush(); /* send the output... */ 389 GettingInput = oldgetting; /* reset to old value */ 390 } 391 392 #ifdef notdef 393 GotoBottom() 394 { /* used to go to last used screen line */ 395 MoveToLine(OldvcV); 396 } 397 398 #endif 399 400 void 401 PastBottom() 402 { /* used to go to last used screen line */ 403 MoveToLine(OldvcV); 404 (void) putraw('\r'); 405 (void) putraw('\n'); 406 ClearDisp(); 407 flush(); 408 } 409 410 411 /* insert num characters of s into d (in front of the character) at dat, 412 maximum length of d is dlen */ 413 static void 414 str_insert(d, dat, dlen, s, num) 415 register Char *d; 416 register int dat, dlen; 417 register Char *s; 418 register int num; 419 { 420 register Char *a, *b; 421 422 if (num <= 0) 423 return; 424 if (num > dlen - dat) 425 num = dlen - dat; 426 427 #ifdef DEBUG_REFRESH 428 dprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n", 429 num, dat, dlen, short2str(d)); 430 dprintf("s == \"%s\"n", short2str(s)); 431 #endif /* DEBUG_REFRESH */ 432 433 /* open up the space for num chars */ 434 if (num > 0) { 435 b = d + dlen - 1; 436 a = b - num; 437 while (a >= &d[dat]) 438 *b-- = *a--; 439 d[dlen] = '\0'; /* just in case */ 440 } 441 #ifdef DEBUG_REFRESH 442 dprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n", 443 num, dat, dlen, short2str(d)); 444 dprintf("s == \"%s\"n", short2str(s)); 445 #endif /* DEBUG_REFRESH */ 446 447 /* copy the characters */ 448 for (a = d + dat; (a < d + dlen) && (num > 0); num--) 449 *a++ = *s++; 450 451 #ifdef DEBUG_REFRESH 452 dprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n", 453 num, dat, dlen, d, short2str(s)); 454 dprintf("s == \"%s\"n", short2str(s)); 455 #endif /* DEBUG_REFRESH */ 456 } 457 458 /* delete num characters d at dat, maximum length of d is dlen */ 459 static void 460 str_delete(d, dat, dlen, num) 461 register Char *d; 462 register int dat, dlen, num; 463 { 464 register Char *a, *b; 465 466 if (num <= 0) 467 return; 468 if (dat + num >= dlen) { 469 d[dat] = '\0'; 470 return; 471 } 472 473 #ifdef DEBUG_REFRESH 474 dprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n", 475 num, dat, dlen, short2str(d)); 476 #endif /* DEBUG_REFRESH */ 477 478 /* open up the space for num chars */ 479 if (num > 0) { 480 b = d + dat; 481 a = b + num; 482 while (a < &d[dlen]) 483 *b++ = *a++; 484 d[dlen] = '\0'; /* just in case */ 485 } 486 #ifdef DEBUG_REFRESH 487 dprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n", 488 num, dat, dlen, short2str(d)); 489 #endif /* DEBUG_REFRESH */ 490 } 491 492 static void 493 str_cp(a, b, n) 494 register Char *a, *b; 495 register int n; 496 { 497 while (n-- && *b) 498 *a++ = *b++; 499 } 500 501 502 #if defined(DSPMBYTE) /* BY TAGA Nayuta VERY THANKS */ 503 static Char * 504 update_line_fix_mbyte_point(start, target, d) 505 Char *start, *target; 506 int d; 507 { 508 if (_enable_mbdisp) { 509 while (*start) { 510 if (target == start) 511 break; 512 if (target < start) 513 return target + d; 514 if (Ismbyte1(*start) && Ismbyte2(*(start + 1))) 515 start++; 516 start++; 517 } 518 } 519 return target; 520 } 521 #endif 522 523 /* **************************************************************** 524 update_line() is based on finding the middle difference of each line 525 on the screen; vis: 526 527 /old first difference 528 /beginning of line | /old last same /old EOL 529 v v v v 530 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 531 new: eddie> Oh, my little buggy says to me, as lurgid as 532 ^ ^ ^ ^ 533 \beginning of line | \new last same \new end of line 534 \new first difference 535 536 all are character pointers for the sake of speed. Special cases for 537 no differences, as well as for end of line additions must be handled. 538 **************************************************************** */ 539 540 /* Minimum at which doing an insert it "worth it". This should be about 541 * half the "cost" of going into insert mode, inserting a character, and 542 * going back out. This should really be calculated from the termcap 543 * data... For the moment, a good number for ANSI terminals. 544 */ 545 #define MIN_END_KEEP 4 546 547 static void /* could be changed to make it smarter */ 548 update_line(old, new, cur_line) 549 register Char *old, *new; 550 int cur_line; 551 { 552 register Char *o, *n, *p, c; 553 Char *ofd, *ols, *oe, *nfd, *nls, *ne; 554 Char *osb, *ose, *nsb, *nse; 555 int fx, sx; 556 557 /* 558 * find first diff 559 */ 560 for (o = old, n = new; *o && (*o == *n); o++, n++) 561 continue; 562 ofd = o; 563 nfd = n; 564 565 /* 566 * Find the end of both old and new 567 */ 568 while (*o) 569 o++; 570 /* 571 * Remove any trailing blanks off of the end, being careful not to 572 * back up past the beginning. 573 */ 574 while (ofd < o) { 575 if (o[-1] != ' ') 576 break; 577 o--; 578 } 579 oe = o; 580 *oe = (Char) 0; 581 582 while (*n) 583 n++; 584 585 /* remove blanks from end of new */ 586 while (nfd < n) { 587 if (n[-1] != ' ') 588 break; 589 n--; 590 } 591 ne = n; 592 *ne = (Char) 0; 593 594 /* 595 * if no diff, continue to next line of redraw 596 */ 597 if (*ofd == '\0' && *nfd == '\0') { 598 #ifdef DEBUG_UPDATE 599 dprintf("no difference.\r\n"); 600 #endif /* DEBUG_UPDATE */ 601 return; 602 } 603 604 /* 605 * find last same pointer 606 */ 607 while ((o > ofd) && (n > nfd) && (*--o == *--n)) 608 continue; 609 ols = ++o; 610 nls = ++n; 611 612 /* 613 * find same begining and same end 614 */ 615 osb = ols; 616 nsb = nls; 617 ose = ols; 618 nse = nls; 619 620 /* 621 * case 1: insert: scan from nfd to nls looking for *ofd 622 */ 623 if (*ofd) { 624 for (c = *ofd, n = nfd; n < nls; n++) { 625 if (c == *n) { 626 for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++) 627 continue; 628 /* 629 * if the new match is longer and it's worth keeping, then we 630 * take it 631 */ 632 if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) { 633 nsb = n; 634 nse = p; 635 osb = ofd; 636 ose = o; 637 } 638 } 639 } 640 } 641 642 /* 643 * case 2: delete: scan from ofd to ols looking for *nfd 644 */ 645 if (*nfd) { 646 for (c = *nfd, o = ofd; o < ols; o++) { 647 if (c == *o) { 648 for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++) 649 continue; 650 /* 651 * if the new match is longer and it's worth keeping, then we 652 * take it 653 */ 654 if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) { 655 nsb = nfd; 656 nse = n; 657 osb = o; 658 ose = p; 659 } 660 } 661 } 662 } 663 #ifdef notdef 664 /* 665 * If `last same' is before `same end' re-adjust 666 */ 667 if (ols < ose) 668 ols = ose; 669 if (nls < nse) 670 nls = nse; 671 #endif 672 673 /* 674 * Pragmatics I: If old trailing whitespace or not enough characters to 675 * save to be worth it, then don't save the last same info. 676 */ 677 if ((oe - ols) < MIN_END_KEEP) { 678 ols = oe; 679 nls = ne; 680 } 681 682 /* 683 * Pragmatics II: if the terminal isn't smart enough, make the data dumber 684 * so the smart update doesn't try anything fancy 685 */ 686 687 /* 688 * fx is the number of characters we need to insert/delete: in the 689 * beginning to bring the two same begins together 690 */ 691 fx = (int) ((nsb - nfd) - (osb - ofd)); 692 /* 693 * sx is the number of characters we need to insert/delete: in the end to 694 * bring the two same last parts together 695 */ 696 sx = (int) ((nls - nse) - (ols - ose)); 697 698 if (!T_CanIns) { 699 if (fx > 0) { 700 osb = ols; 701 ose = ols; 702 nsb = nls; 703 nse = nls; 704 } 705 if (sx > 0) { 706 ols = oe; 707 nls = ne; 708 } 709 if ((ols - ofd) < (nls - nfd)) { 710 ols = oe; 711 nls = ne; 712 } 713 } 714 if (!T_CanDel) { 715 if (fx < 0) { 716 osb = ols; 717 ose = ols; 718 nsb = nls; 719 nse = nls; 720 } 721 if (sx < 0) { 722 ols = oe; 723 nls = ne; 724 } 725 if ((ols - ofd) > (nls - nfd)) { 726 ols = oe; 727 nls = ne; 728 } 729 } 730 731 /* 732 * Pragmatics III: make sure the middle shifted pointers are correct if 733 * they don't point to anything (we may have moved ols or nls). 734 */ 735 /* if the change isn't worth it, don't bother */ 736 /* was: if (osb == ose) */ 737 if ((ose - osb) < MIN_END_KEEP) { 738 osb = ols; 739 ose = ols; 740 nsb = nls; 741 nse = nls; 742 } 743 744 /* 745 * Now that we are done with pragmatics we recompute fx, sx 746 */ 747 fx = (int) ((nsb - nfd) - (osb - ofd)); 748 sx = (int) ((nls - nse) - (ols - ose)); 749 750 #ifdef DEBUG_UPDATE 751 dprintf("\n"); 752 dprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n", 753 ofd - old, osb - old, ose - old, ols - old, oe - old); 754 dprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n", 755 nfd - new, nsb - new, nse - new, nls - new, ne - new); 756 dprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"); 757 dprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"); 758 dprintstr("old- oe", old, oe); 759 dprintstr("new- ne", new, ne); 760 dprintstr("old-ofd", old, ofd); 761 dprintstr("new-nfd", new, nfd); 762 dprintstr("ofd-osb", ofd, osb); 763 dprintstr("nfd-nsb", nfd, nsb); 764 dprintstr("osb-ose", osb, ose); 765 dprintstr("nsb-nse", nsb, nse); 766 dprintstr("ose-ols", ose, ols); 767 dprintstr("nse-nls", nse, nls); 768 dprintstr("ols- oe", ols, oe); 769 dprintstr("nls- ne", nls, ne); 770 #endif /* DEBUG_UPDATE */ 771 772 /* 773 * CursorV to this line cur_line MUST be in this routine so that if we 774 * don't have to change the line, we don't move to it. CursorH to first 775 * diff char 776 */ 777 MoveToLine(cur_line); 778 779 #if defined(DSPMBYTE) /* BY TAGA Nayuta VERY THANKS */ 780 ofd = update_line_fix_mbyte_point(old, ofd, -1); 781 osb = update_line_fix_mbyte_point(old, osb, 1); 782 ose = update_line_fix_mbyte_point(old, ose, -1); 783 ols = update_line_fix_mbyte_point(old, ols, 1); 784 nfd = update_line_fix_mbyte_point(new, nfd, -1); 785 nsb = update_line_fix_mbyte_point(new, nsb, 1); 786 nse = update_line_fix_mbyte_point(new, nse, -1); 787 nls = update_line_fix_mbyte_point(new, nls, 1); 788 #endif 789 790 /* 791 * at this point we have something like this: 792 * 793 * /old /ofd /osb /ose /ols /oe 794 * v.....................v v..................v v........v 795 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as 796 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as 797 * ^.....................^ ^..................^ ^........^ 798 * \new \nfd \nsb \nse \nls \ne 799 * 800 * fx is the difference in length between the the chars between nfd and 801 * nsb, and the chars between ofd and osb, and is thus the number of 802 * characters to delete if < 0 (new is shorter than old, as above), 803 * or insert (new is longer than short). 804 * 805 * sx is the same for the second differences. 806 */ 807 808 /* 809 * if we have a net insert on the first difference, AND inserting the net 810 * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character 811 * (which is ne if nls != ne, otherwise is nse) off the edge of the screen 812 * (TermH - 1) else we do the deletes first so that we keep everything we 813 * need to. 814 */ 815 816 /* 817 * if the last same is the same like the end, there is no last same part, 818 * otherwise we want to keep the last same part set p to the last useful 819 * old character 820 */ 821 p = (ols != oe) ? oe : ose; 822 823 /* 824 * if (There is a diffence in the beginning) && (we need to insert 825 * characters) && (the number of characters to insert is less than the term 826 * width) We need to do an insert! else if (we need to delete characters) 827 * We need to delete characters! else No insert or delete 828 */ 829 if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) { 830 #ifdef DEBUG_UPDATE 831 dprintf("first diff insert at %d...\r\n", nfd - new); 832 #endif /* DEBUG_UPDATE */ 833 /* 834 * Move to the first char to insert, where the first diff is. 835 */ 836 MoveToChar(nfd - new); 837 /* 838 * Check if we have stuff to keep at end 839 */ 840 if (nsb != ne) { 841 #ifdef DEBUG_UPDATE 842 dprintf("with stuff to keep at end\r\n"); 843 #endif /* DEBUG_UPDATE */ 844 /* 845 * insert fx chars of new starting at nfd 846 */ 847 if (fx > 0) { 848 #ifdef DEBUG_UPDATE 849 if (!T_CanIns) 850 dprintf(" ERROR: cannot insert in early first diff\n"); 851 #endif /* DEBUG_UPDATE */ 852 Insert_write(nfd, fx); 853 str_insert(old, (int) (ofd - old), TermH, nfd, fx); 854 } 855 /* 856 * write (nsb-nfd) - fx chars of new starting at (nfd + fx) 857 */ 858 so_write(nfd + fx, (nsb - nfd) - fx); 859 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx)); 860 } 861 else { 862 #ifdef DEBUG_UPDATE 863 dprintf("without anything to save\r\n"); 864 #endif /* DEBUG_UPDATE */ 865 so_write(nfd, (nsb - nfd)); 866 str_cp(ofd, nfd, (int) (nsb - nfd)); 867 /* 868 * Done 869 */ 870 return; 871 } 872 } 873 else if (fx < 0) { 874 #ifdef DEBUG_UPDATE 875 dprintf("first diff delete at %d...\r\n", ofd - old); 876 #endif /* DEBUG_UPDATE */ 877 /* 878 * move to the first char to delete where the first diff is 879 */ 880 MoveToChar(ofd - old); 881 /* 882 * Check if we have stuff to save 883 */ 884 if (osb != oe) { 885 #ifdef DEBUG_UPDATE 886 dprintf("with stuff to save at end\r\n"); 887 #endif /* DEBUG_UPDATE */ 888 /* 889 * fx is less than zero *always* here but we check for code 890 * symmetry 891 */ 892 if (fx < 0) { 893 #ifdef DEBUG_UPDATE 894 if (!T_CanDel) 895 dprintf(" ERROR: cannot delete in first diff\n"); 896 #endif /* DEBUG_UPDATE */ 897 DeleteChars(-fx); 898 str_delete(old, (int) (ofd - old), TermH, -fx); 899 } 900 /* 901 * write (nsb-nfd) chars of new starting at nfd 902 */ 903 so_write(nfd, (nsb - nfd)); 904 str_cp(ofd, nfd, (int) (nsb - nfd)); 905 906 } 907 else { 908 #ifdef DEBUG_UPDATE 909 dprintf("but with nothing left to save\r\n"); 910 #endif /* DEBUG_UPDATE */ 911 /* 912 * write (nsb-nfd) chars of new starting at nfd 913 */ 914 so_write(nfd, (nsb - nfd)); 915 #ifdef DEBUG_REFRESH 916 dprintf("cleareol %d\n", (oe - old) - (ne - new)); 917 #endif /* DEBUG_UPDATE */ 918 #ifndef WINNT 919 ClearEOL((oe - old) - (ne - new)); 920 #else 921 /* 922 * The calculation above does not work too well on NT 923 */ 924 ClearEOL(TermH - CursorH); 925 #endif /*WINNT*/ 926 /* 927 * Done 928 */ 929 return; 930 } 931 } 932 else 933 fx = 0; 934 935 if (sx < 0) { 936 #ifdef DEBUG_UPDATE 937 dprintf("second diff delete at %d...\r\n", (ose - old) + fx); 938 #endif /* DEBUG_UPDATE */ 939 /* 940 * Check if we have stuff to delete 941 */ 942 /* 943 * fx is the number of characters inserted (+) or deleted (-) 944 */ 945 946 MoveToChar((ose - old) + fx); 947 /* 948 * Check if we have stuff to save 949 */ 950 if (ols != oe) { 951 #ifdef DEBUG_UPDATE 952 dprintf("with stuff to save at end\r\n"); 953 #endif /* DEBUG_UPDATE */ 954 /* 955 * Again a duplicate test. 956 */ 957 if (sx < 0) { 958 #ifdef DEBUG_UPDATE 959 if (!T_CanDel) 960 dprintf(" ERROR: cannot delete in second diff\n"); 961 #endif /* DEBUG_UPDATE */ 962 DeleteChars(-sx); 963 } 964 965 /* 966 * write (nls-nse) chars of new starting at nse 967 */ 968 so_write(nse, (nls - nse)); 969 } 970 else { 971 int olen = (int) (oe - old + fx); 972 if (olen > TermH) 973 olen = TermH; 974 #ifdef DEBUG_UPDATE 975 dprintf("but with nothing left to save\r\n"); 976 #endif /* DEBUG_UPDATE */ 977 so_write(nse, (nls - nse)); 978 #ifdef DEBUG_REFRESH 979 dprintf("cleareol %d\n", olen - (ne - new)); 980 #endif /* DEBUG_UPDATE */ 981 #ifndef WINNT 982 ClearEOL(olen - (ne - new)); 983 #else 984 /* 985 * The calculation above does not work too well on NT 986 */ 987 ClearEOL(TermH - CursorH); 988 #endif /*WINNT*/ 989 } 990 } 991 992 /* 993 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 994 */ 995 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 996 #ifdef DEBUG_UPDATE 997 dprintf("late first diff insert at %d...\r\n", nfd - new); 998 #endif /* DEBUG_UPDATE */ 999 1000 MoveToChar(nfd - new); 1001 /* 1002 * Check if we have stuff to keep at the end 1003 */ 1004 if (nsb != ne) { 1005 #ifdef DEBUG_UPDATE 1006 dprintf("with stuff to keep at end\r\n"); 1007 #endif /* DEBUG_UPDATE */ 1008 /* 1009 * We have to recalculate fx here because we set it 1010 * to zero above as a flag saying that we hadn't done 1011 * an early first insert. 1012 */ 1013 fx = (int) ((nsb - nfd) - (osb - ofd)); 1014 if (fx > 0) { 1015 /* 1016 * insert fx chars of new starting at nfd 1017 */ 1018 #ifdef DEBUG_UPDATE 1019 if (!T_CanIns) 1020 dprintf(" ERROR: cannot insert in late first diff\n"); 1021 #endif /* DEBUG_UPDATE */ 1022 Insert_write(nfd, fx); 1023 str_insert(old, (int) (ofd - old), TermH, nfd, fx); 1024 } 1025 1026 /* 1027 * write (nsb-nfd) - fx chars of new starting at (nfd + fx) 1028 */ 1029 so_write(nfd + fx, (nsb - nfd) - fx); 1030 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx)); 1031 } 1032 else { 1033 #ifdef DEBUG_UPDATE 1034 dprintf("without anything to save\r\n"); 1035 #endif /* DEBUG_UPDATE */ 1036 so_write(nfd, (nsb - nfd)); 1037 str_cp(ofd, nfd, (int) (nsb - nfd)); 1038 } 1039 } 1040 1041 /* 1042 * line is now NEW up to nse 1043 */ 1044 if (sx >= 0) { 1045 #ifdef DEBUG_UPDATE 1046 dprintf("second diff insert at %d...\r\n", nse - new); 1047 #endif /* DEBUG_UPDATE */ 1048 MoveToChar(nse - new); 1049 if (ols != oe) { 1050 #ifdef DEBUG_UPDATE 1051 dprintf("with stuff to keep at end\r\n"); 1052 #endif /* DEBUG_UPDATE */ 1053 if (sx > 0) { 1054 /* insert sx chars of new starting at nse */ 1055 #ifdef DEBUG_UPDATE 1056 if (!T_CanIns) 1057 dprintf(" ERROR: cannot insert in second diff\n"); 1058 #endif /* DEBUG_UPDATE */ 1059 Insert_write(nse, sx); 1060 } 1061 1062 /* 1063 * write (nls-nse) - sx chars of new starting at (nse + sx) 1064 */ 1065 so_write(nse + sx, (nls - nse) - sx); 1066 } 1067 else { 1068 #ifdef DEBUG_UPDATE 1069 dprintf("without anything to save\r\n"); 1070 #endif /* DEBUG_UPDATE */ 1071 so_write(nse, (nls - nse)); 1072 1073 /* 1074 * No need to do a clear-to-end here because we were doing 1075 * a second insert, so we will have over written all of the 1076 * old string. 1077 */ 1078 } 1079 } 1080 #ifdef DEBUG_UPDATE 1081 dprintf("done.\r\n"); 1082 #endif /* DEBUG_UPDATE */ 1083 } 1084 1085 1086 static void 1087 cpy_pad_spaces(dst, src, width) 1088 register Char *dst, *src; 1089 register int width; 1090 { 1091 register int i; 1092 1093 for (i = 0; i < width; i++) { 1094 if (*src == (Char) 0) 1095 break; 1096 *dst++ = *src++; 1097 } 1098 1099 while (i < width) { 1100 *dst++ = ' '; 1101 i++; 1102 } 1103 *dst = (Char) 0; 1104 } 1105 1106 void 1107 RefCursor() 1108 { /* only move to new cursor pos */ 1109 register Char *cp, c; 1110 register int h, th, v; 1111 1112 /* first we must find where the cursor is... */ 1113 h = 0; 1114 v = 0; 1115 th = TermH; /* optimize for speed */ 1116 1117 for (cp = PromptBuf; *cp; cp++) { /* do prompt */ 1118 if (*cp & LITERAL) 1119 continue; 1120 c = *cp & CHAR; /* extra speed plus strip the inverse */ 1121 h++; /* all chars at least this long */ 1122 1123 /* from wolman%crltrx.DEC@decwrl.dec.com (Alec Wolman) */ 1124 /* lets handle newline as part of the prompt */ 1125 1126 if (c == '\n') { 1127 h = 0; 1128 v++; 1129 } 1130 else { 1131 if (c == '\t') { /* if a tab, to next tab stop */ 1132 while (h & 07) { 1133 h++; 1134 } 1135 } 1136 else if (Iscntrl(c)) { /* if control char */ 1137 h++; 1138 if (h > th) { /* if overflow, compensate */ 1139 h = 1; 1140 v++; 1141 } 1142 } 1143 else if (!Isprint(c)) { 1144 h += 3; 1145 if (h > th) { /* if overflow, compensate */ 1146 h = h - th; 1147 v++; 1148 } 1149 } 1150 } 1151 1152 if (h >= th) { /* check, extra long tabs picked up here also */ 1153 h = 0; 1154 v++; 1155 } 1156 } 1157 1158 for (cp = InputBuf; cp < Cursor; cp++) { /* do input buffer to Cursor */ 1159 c = *cp & CHAR; /* extra speed plus strip the inverse */ 1160 h++; /* all chars at least this long */ 1161 1162 if (c == '\n') { /* handle newline in data part too */ 1163 h = 0; 1164 v++; 1165 } 1166 else { 1167 if (c == '\t') { /* if a tab, to next tab stop */ 1168 while (h & 07) { 1169 h++; 1170 } 1171 } 1172 else if (Iscntrl(c)) { /* if control char */ 1173 h++; 1174 if (h > th) { /* if overflow, compensate */ 1175 h = 1; 1176 v++; 1177 } 1178 } 1179 else if (!Isprint(c)) { 1180 h += 3; 1181 if (h > th) { /* if overflow, compensate */ 1182 h = h - th; 1183 v++; 1184 } 1185 } 1186 } 1187 1188 if (h >= th) { /* check, extra long tabs picked up here also */ 1189 h = 0; 1190 v++; 1191 } 1192 } 1193 1194 /* now go there */ 1195 MoveToLine(v); 1196 MoveToChar(h); 1197 flush(); 1198 } 1199 1200 static void 1201 PutPlusOne(c) 1202 int c; 1203 { 1204 (void) putraw(c); 1205 Display[CursorV][CursorH++] = (Char) c; 1206 if (CursorH >= TermH) { /* if we must overflow */ 1207 CursorH = 0; 1208 CursorV++; 1209 OldvcV++; 1210 if (T_Margin & MARGIN_AUTO) { 1211 if (T_Margin & MARGIN_MAGIC) { 1212 (void) putraw(' '); 1213 (void) putraw('\b'); 1214 } 1215 } 1216 else { 1217 (void) putraw('\r'); 1218 (void) putraw('\n'); 1219 } 1220 } 1221 } 1222 1223 void 1224 RefPlusOne() 1225 { /* we added just one char, handle it fast. 1226 * assumes that screen cursor == real cursor */ 1227 register Char c, mc; 1228 1229 c = Cursor[-1] & CHAR; /* the char we just added */ 1230 1231 if (c == '\t' || Cursor != LastChar) { 1232 Refresh(); /* too hard to handle */ 1233 return; 1234 } 1235 1236 if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) { 1237 Refresh(); /* clear out rprompt if less than one char gap*/ 1238 return; 1239 } /* else (only do at end of line, no TAB) */ 1240 1241 if (Iscntrl(c)) { /* if control char, do caret */ 1242 #ifndef _OSD_POSIX 1243 mc = (c == '\177') ? '?' : (c | 0100); 1244 PutPlusOne('^'); 1245 PutPlusOne(mc); 1246 #else /*_OSD_POSIX*/ 1247 if (_toascii[c] == '\177' || Isupper(_toebcdic[_toascii[c]|0100]) 1248 || strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL) 1249 { 1250 mc = (_toascii[c] == '\177') ? '?' : _toebcdic[_toascii[c]|0100]; 1251 PutPlusOne('^'); 1252 PutPlusOne(mc); 1253 } 1254 else 1255 { 1256 PutPlusOne('\\'); 1257 PutPlusOne(((c >> 6) & 7) + '0'); 1258 PutPlusOne(((c >> 3) & 7) + '0'); 1259 PutPlusOne((c & 7) + '0'); 1260 } 1261 #endif /*_OSD_POSIX*/ 1262 } 1263 else if (Isprint(c)) { /* normal char */ 1264 PutPlusOne(c); 1265 } 1266 #ifdef KANJI 1267 else if (!adrof(STRnokanji)) { 1268 PutPlusOne(c); 1269 } 1270 #endif 1271 else { 1272 PutPlusOne('\\'); 1273 PutPlusOne(((c >> 6) & 7) + '0'); 1274 PutPlusOne(((c >> 3) & 7) + '0'); 1275 PutPlusOne((c & 7) + '0'); 1276 } 1277 flush(); 1278 } 1279 1280 /* clear the screen buffers so that new new prompt starts fresh. */ 1281 1282 void 1283 ClearDisp() 1284 { 1285 register int i; 1286 1287 CursorV = 0; /* clear the display buffer */ 1288 CursorH = 0; 1289 for (i = 0; i < TermV; i++) 1290 (void) memset(Display[i], 0, TermH * sizeof(Display[0][0])); 1291 OldvcV = 0; 1292 } 1293 1294 void 1295 ClearLines() 1296 { /* Make sure all lines are *really* blank */ 1297 register int i; 1298 1299 if (T_CanCEOL) { 1300 /* 1301 * Clear the lines from the bottom up so that if we try moving 1302 * the cursor down by writing the character that is at the end 1303 * of the screen line, we won't rewrite a character that shouldn't 1304 * be there. 1305 */ 1306 for (i = OldvcV; i >= 0; i--) { /* for each line on the screen */ 1307 MoveToLine(i); 1308 MoveToChar(0); 1309 ClearEOL(TermH); 1310 } 1311 } 1312 else { 1313 MoveToLine(OldvcV); /* go to last line */ 1314 (void) putraw('\r'); /* go to BOL */ 1315 (void) putraw('\n'); /* go to new line */ 1316 } 1317 } 1318