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