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