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