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