1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.refresh.c,v 3.46 2006/08/23 15:03:14 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.46 2006/08/23 15:03:14 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); 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 dprintf (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 dprintf("%s:\"", str); 72 while (f < t) { 73 if (ASC(*f) & ~ASCII) 74 dprintf("[%x]", *f++); 75 else 76 dprintf("%c", CTL_ESC(ASCII & ASC(*f++))); 77 } 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 dprintf(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 static int 163 Draw(Char *cp, int nocomb) /* draw char at cp, expand tabs, ctl chars */ 164 { 165 int w, i, lv, lh; 166 Char c, attr; 167 168 attr = *cp & ~CHAR; 169 c = *cp & CHAR; 170 w = NLSClassify(c, nocomb); 171 switch (w) { 172 case NLSCLASS_NL: 173 Vdraw('\0', 0); /* assure end of line */ 174 vcursor_h = 0; /* reset cursor pos */ 175 vcursor_v++; 176 break; 177 case NLSCLASS_TAB: 178 do { 179 Vdraw(' ', 1); 180 } while ((vcursor_h & 07) != 0); 181 break; 182 case NLSCLASS_CTRL: 183 Vdraw('^' | attr, 1); 184 if (c == CTL_ESC('\177')) { 185 Vdraw('?' | attr, 1); 186 } else { 187 #ifdef IS_ASCII 188 /* uncontrolify it; works only for iso8859-1 like sets */ 189 Vdraw(c | 0100 | attr, 1); 190 #else 191 Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1); 192 #endif 193 } 194 break; 195 case NLSCLASS_ILLEGAL: 196 Vdraw('\\' | attr, 1); 197 Vdraw((((c >> 6) & 7) + '0') | attr, 1); 198 Vdraw((((c >> 3) & 7) + '0') | attr, 1); 199 Vdraw(((c & 7) + '0') | attr, 1); 200 break; 201 case NLSCLASS_ILLEGAL2: 202 case NLSCLASS_ILLEGAL3: 203 case NLSCLASS_ILLEGAL4: 204 Vdraw('\\' | attr, 1); 205 Vdraw('U' | attr, 1); 206 Vdraw('+' | attr, 1); 207 for (i = 8 * NLSCLASS_ILLEGAL_SIZE(w) - 4; i >= 0; i -= 4) 208 Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1); 209 break; 210 case 0: 211 lv = vcursor_v; 212 lh = vcursor_h; 213 for (;;) { 214 lh--; 215 if (lh < 0) { 216 lv--; 217 if (lv < 0) 218 break; 219 lh = Strlen(Vdisplay[lv]) - 1; 220 } 221 if (Vdisplay[lv][lh] != CHAR_DBWIDTH) 222 break; 223 } 224 if (lv < 0) { 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 } 231 Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]); 232 break; 233 default: 234 Vdraw(*cp, w); 235 break; 236 } 237 return 1; 238 } 239 240 static void 241 Vdraw(Char c, int width) /* draw char c onto V lines */ 242 { 243 #ifdef DEBUG_REFRESH 244 # ifdef SHORT_STRINGS 245 dprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width); 246 # else 247 dprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width); 248 # endif /* SHORT_STRNGS */ 249 #endif /* DEBUG_REFRESH */ 250 251 /* Hopefully this is what all the terminals do with multi-column characters 252 that "span line breaks". */ 253 while (vcursor_h + width > TermH) 254 Vdraw(' ', 1); 255 Vdisplay[vcursor_v][vcursor_h] = c; 256 if (width) 257 vcursor_h++; /* advance to next place */ 258 while (--width > 0) 259 Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH; 260 if (vcursor_h >= TermH) { 261 Vdisplay[vcursor_v][TermH] = '\0'; /* assure end of line */ 262 vcursor_h = 0; /* reset it. */ 263 vcursor_v++; 264 #ifdef DEBUG_REFRESH 265 if (vcursor_v >= TermV) { /* should NEVER happen. */ 266 dprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n", 267 vcursor_v, TermV); 268 abort(); 269 } 270 #endif /* DEBUG_REFRESH */ 271 } 272 } 273 274 /* 275 * RefreshPromptpart() 276 * draws a prompt element, expanding literals (we know it's ASCIZ) 277 */ 278 static void 279 RefreshPromptpart(Char *buf) 280 { 281 Char *cp; 282 int w; 283 284 if (buf == NULL) 285 return; 286 for (cp = buf; *cp; ) { 287 if (*cp & LITERAL) { 288 Char *litstart = cp; 289 while (*cp & LITERAL) 290 cp++; 291 if (*cp) { 292 w = NLSWidth(*cp & CHAR); 293 Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w); 294 cp++; 295 } 296 else { 297 /* 298 * XXX: This is a bug, we lose the last literal, if it is not 299 * followed by a normal character, but it is too hard to fix 300 */ 301 break; 302 } 303 } 304 else 305 cp += Draw(cp, cp == buf); 306 } 307 } 308 309 /* 310 * Refresh() 311 * draws the new virtual screen image from the current input 312 * line, then goes line-by-line changing the real image to the new 313 * virtual image. The routine to re-draw a line can be replaced 314 * easily in hopes of a smarter one being placed there. 315 */ 316 #ifndef WINNT_NATIVE 317 static 318 #endif 319 int OldvcV = 0; 320 321 void 322 Refresh(void) 323 { 324 int cur_line; 325 Char *cp; 326 int cur_h, cur_v = 0, new_vcv; 327 int rhdiff; 328 Char oldgetting; 329 330 #ifdef DEBUG_REFRESH 331 dprintf("Prompt = :%s:\r\n", short2str(Prompt)); 332 dprintf("InputBuf = :%s:\r\n", short2str(InputBuf)); 333 #endif /* DEBUG_REFRESH */ 334 oldgetting = GettingInput; 335 GettingInput = 0; /* avoid re-entrance via SIGWINCH */ 336 337 /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */ 338 vcursor_h = 0; 339 vcursor_v = 0; 340 RefreshPromptpart(RPrompt); 341 rprompt_h = vcursor_h; 342 rprompt_v = vcursor_v; 343 344 /* reset the Vdraw cursor, draw prompt */ 345 vcursor_h = 0; 346 vcursor_v = 0; 347 RefreshPromptpart(Prompt); 348 cur_h = -1; /* set flag in case I'm not set */ 349 350 /* draw the current input buffer */ 351 for (cp = InputBuf; (cp < LastChar); ) { 352 if (cp >= Cursor && cur_h == -1) { 353 cur_h = vcursor_h; /* save for later */ 354 cur_v = vcursor_v; 355 Cursor = cp; 356 } 357 cp += Draw(cp, cp == InputBuf); 358 } 359 360 if (cur_h == -1) { /* if I haven't been set yet, I'm at the end */ 361 cur_h = vcursor_h; 362 cur_v = vcursor_v; 363 } 364 365 rhdiff = TermH - vcursor_h - rprompt_h; 366 if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) { 367 /* 368 * have a right-hand side prompt that will fit on 369 * the end of the first line with at least one 370 * character gap to the input buffer. 371 */ 372 while (--rhdiff > 0) /* pad out with spaces */ 373 Vdraw(' ', 1); 374 RefreshPromptpart(RPrompt); 375 } 376 else { 377 rprompt_h = 0; /* flag "not using rprompt" */ 378 rprompt_v = 0; 379 } 380 381 new_vcv = vcursor_v; /* must be done BEFORE the NUL is written */ 382 Vdraw('\0', 1); /* put NUL on end */ 383 384 #if defined (DEBUG_REFRESH) 385 dprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n", 386 TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0])); 387 #endif /* DEBUG_REFRESH */ 388 389 #ifdef DEBUG_UPDATE 390 dprintf("updating %d lines.\r\n", new_vcv); 391 #endif /* DEBUG_UPDATE */ 392 for (cur_line = 0; cur_line <= new_vcv; cur_line++) { 393 /* NOTE THAT update_line MAY CHANGE Display[cur_line] */ 394 update_line(Display[cur_line], Vdisplay[cur_line], cur_line); 395 #ifdef WINNT_NATIVE 396 flush(); 397 #endif /* WINNT_NATIVE */ 398 399 /* 400 * Copy the new line to be the current one, and pad out with spaces 401 * to the full width of the terminal so that if we try moving the 402 * cursor by writing the character that is at the end of the 403 * screen line, it won't be a NUL or some old leftover stuff. 404 */ 405 cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH); 406 } 407 #ifdef DEBUG_REFRESH 408 dprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n", 409 vcursor_v, OldvcV, cur_line); 410 #endif /* DEBUG_REFRESH */ 411 if (OldvcV > new_vcv) { 412 for (; cur_line <= OldvcV; cur_line++) { 413 update_line(Display[cur_line], STRNULL, cur_line); 414 *Display[cur_line] = '\0'; 415 } 416 } 417 OldvcV = new_vcv; /* set for next time */ 418 #ifdef DEBUG_REFRESH 419 dprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n", 420 CursorH, CursorV, cur_h, cur_v); 421 #endif /* DEBUG_REFRESH */ 422 #ifdef WINNT_NATIVE 423 flush(); 424 #endif /* WINNT_NATIVE */ 425 MoveToLine(cur_v); /* go to where the cursor is */ 426 MoveToChar(cur_h); 427 SetAttributes(0); /* Clear all attributes */ 428 flush(); /* send the output... */ 429 GettingInput = oldgetting; /* reset to old value */ 430 } 431 432 #ifdef notdef 433 GotoBottom(void) 434 { /* used to go to last used screen line */ 435 MoveToLine(OldvcV); 436 } 437 438 #endif 439 440 void 441 PastBottom(void) 442 { /* used to go to last used screen line */ 443 MoveToLine(OldvcV); 444 (void) putraw('\r'); 445 (void) putraw('\n'); 446 ClearDisp(); 447 flush(); 448 } 449 450 451 /* insert num characters of s into d (in front of the character) at dat, 452 maximum length of d is dlen */ 453 static void 454 str_insert(Char *d, int dat, int dlen, Char *s, int num) 455 { 456 Char *a, *b; 457 458 if (num <= 0) 459 return; 460 if (num > dlen - dat) 461 num = dlen - dat; 462 463 #ifdef DEBUG_REFRESH 464 dprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n", 465 num, dat, dlen, short2str(d)); 466 dprintf("s == \"%s\"n", short2str(s)); 467 #endif /* DEBUG_REFRESH */ 468 469 /* open up the space for num chars */ 470 if (num > 0) { 471 b = d + dlen - 1; 472 a = b - num; 473 while (a >= &d[dat]) 474 *b-- = *a--; 475 d[dlen] = '\0'; /* just in case */ 476 } 477 #ifdef DEBUG_REFRESH 478 dprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n", 479 num, dat, dlen, short2str(d)); 480 dprintf("s == \"%s\"n", short2str(s)); 481 #endif /* DEBUG_REFRESH */ 482 483 /* copy the characters */ 484 for (a = d + dat; (a < d + dlen) && (num > 0); num--) 485 *a++ = *s++; 486 487 #ifdef DEBUG_REFRESH 488 dprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n", 489 num, dat, dlen, d, short2str(s)); 490 dprintf("s == \"%s\"n", short2str(s)); 491 #endif /* DEBUG_REFRESH */ 492 } 493 494 /* delete num characters d at dat, maximum length of d is dlen */ 495 static void 496 str_delete(Char *d, int dat, int dlen, int num) 497 { 498 Char *a, *b; 499 500 if (num <= 0) 501 return; 502 if (dat + num >= dlen) { 503 d[dat] = '\0'; 504 return; 505 } 506 507 #ifdef DEBUG_REFRESH 508 dprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n", 509 num, dat, dlen, short2str(d)); 510 #endif /* DEBUG_REFRESH */ 511 512 /* open up the space for num chars */ 513 if (num > 0) { 514 b = d + dat; 515 a = b + num; 516 while (a < &d[dlen]) 517 *b++ = *a++; 518 d[dlen] = '\0'; /* just in case */ 519 } 520 #ifdef DEBUG_REFRESH 521 dprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n", 522 num, dat, dlen, short2str(d)); 523 #endif /* DEBUG_REFRESH */ 524 } 525 526 static void 527 str_cp(Char *a, Char *b, int n) 528 { 529 while (n-- && *b) 530 *a++ = *b++; 531 } 532 533 534 /* **************************************************************** 535 update_line() is based on finding the middle difference of each line 536 on the screen; vis: 537 538 /old first difference 539 /beginning of line | /old last same /old EOL 540 v v v v 541 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 542 new: eddie> Oh, my little buggy says to me, as lurgid as 543 ^ ^ ^ ^ 544 \beginning of line | \new last same \new end of line 545 \new first difference 546 547 all are character pointers for the sake of speed. Special cases for 548 no differences, as well as for end of line additions must be handled. 549 **************************************************************** */ 550 551 /* Minimum at which doing an insert it "worth it". This should be about 552 * half the "cost" of going into insert mode, inserting a character, and 553 * going back out. This should really be calculated from the termcap 554 * data... For the moment, a good number for ANSI terminals. 555 */ 556 #define MIN_END_KEEP 4 557 558 static void /* could be changed to make it smarter */ 559 update_line(Char *old, Char *new, int cur_line) 560 { 561 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 (won't be CHAR_DBWIDTH in either line) 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 o = Strend(o); 578 579 /* 580 * Remove any trailing blanks off of the end, being careful not to 581 * back up past the beginning. 582 */ 583 if (!(adrof(STRhighlight) && MarkIsSet)) { 584 while (ofd < o) { 585 if (o[-1] != ' ') 586 break; 587 o--; 588 } 589 } 590 oe = o; 591 *oe = (Char) 0; 592 593 n = Strend(n); 594 595 /* remove blanks from end of new */ 596 if (!(adrof(STRhighlight) && MarkIsSet)) { 597 while (nfd < n) { 598 if (n[-1] != ' ') 599 break; 600 n--; 601 } 602 } 603 ne = n; 604 *ne = (Char) 0; 605 606 /* 607 * if no diff, continue to next line of redraw 608 */ 609 if (*ofd == '\0' && *nfd == '\0') { 610 #ifdef DEBUG_UPDATE 611 dprintf("no difference.\r\n"); 612 #endif /* DEBUG_UPDATE */ 613 return; 614 } 615 616 /* 617 * find last same pointer 618 */ 619 while ((o > ofd) && (n > nfd) && (*--o == *--n)) 620 continue; 621 if (*o != *n) { 622 o++; 623 n++; 624 } 625 while (*o == CHAR_DBWIDTH) { 626 o++; 627 n++; 628 } 629 ols = o; 630 nls = n; 631 632 /* 633 * find same begining and same end 634 */ 635 osb = ols; 636 nsb = nls; 637 ose = ols; 638 nse = nls; 639 640 /* 641 * case 1: insert: scan from nfd to nls looking for *ofd 642 */ 643 if (*ofd) { 644 for (c = *ofd, n = nfd; n < nls; n++) { 645 if (c == *n) { 646 for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++) 647 continue; 648 /* 649 * if the new match is longer and it's worth keeping, then we 650 * take it 651 */ 652 if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) { 653 nsb = n; 654 nse = p; 655 osb = ofd; 656 ose = o; 657 } 658 } 659 } 660 } 661 662 /* 663 * case 2: delete: scan from ofd to ols looking for *nfd 664 */ 665 if (*nfd) { 666 for (c = *nfd, o = ofd; o < ols; o++) { 667 if (c == *o) { 668 for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++) 669 continue; 670 /* 671 * if the new match is longer and it's worth keeping, then we 672 * take it 673 */ 674 if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) { 675 nsb = nfd; 676 nse = n; 677 osb = o; 678 ose = p; 679 } 680 } 681 } 682 } 683 #ifdef notdef 684 /* 685 * If `last same' is before `same end' re-adjust 686 */ 687 if (ols < ose) 688 ols = ose; 689 if (nls < nse) 690 nls = nse; 691 #endif 692 693 /* 694 * Pragmatics I: If old trailing whitespace or not enough characters to 695 * save to be worth it, then don't save the last same info. 696 */ 697 if ((oe - ols) < MIN_END_KEEP) { 698 ols = oe; 699 nls = ne; 700 } 701 702 /* 703 * Pragmatics II: if the terminal isn't smart enough, make the data dumber 704 * so the smart update doesn't try anything fancy 705 */ 706 707 /* 708 * fx is the number of characters we need to insert/delete: in the 709 * beginning to bring the two same begins together 710 */ 711 fx = (int) ((nsb - nfd) - (osb - ofd)); 712 /* 713 * sx is the number of characters we need to insert/delete: in the end to 714 * bring the two same last parts together 715 */ 716 sx = (int) ((nls - nse) - (ols - ose)); 717 718 if (!T_CanIns) { 719 if (fx > 0) { 720 osb = ols; 721 ose = ols; 722 nsb = nls; 723 nse = nls; 724 } 725 if (sx > 0) { 726 ols = oe; 727 nls = ne; 728 } 729 if ((ols - ofd) < (nls - nfd)) { 730 ols = oe; 731 nls = ne; 732 } 733 } 734 if (!T_CanDel) { 735 if (fx < 0) { 736 osb = ols; 737 ose = ols; 738 nsb = nls; 739 nse = nls; 740 } 741 if (sx < 0) { 742 ols = oe; 743 nls = ne; 744 } 745 if ((ols - ofd) > (nls - nfd)) { 746 ols = oe; 747 nls = ne; 748 } 749 } 750 751 /* 752 * Pragmatics III: make sure the middle shifted pointers are correct if 753 * they don't point to anything (we may have moved ols or nls). 754 */ 755 /* if the change isn't worth it, don't bother */ 756 /* was: if (osb == ose) */ 757 if ((ose - osb) < MIN_END_KEEP) { 758 osb = ols; 759 ose = ols; 760 nsb = nls; 761 nse = nls; 762 } 763 764 /* 765 * Now that we are done with pragmatics we recompute fx, sx 766 */ 767 fx = (int) ((nsb - nfd) - (osb - ofd)); 768 sx = (int) ((nls - nse) - (ols - ose)); 769 770 #ifdef DEBUG_UPDATE 771 dprintf("\n"); 772 dprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n", 773 ofd - old, osb - old, ose - old, ols - old, oe - old); 774 dprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n", 775 nfd - new, nsb - new, nse - new, nls - new, ne - new); 776 dprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"); 777 dprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"); 778 dprintstr("old- oe", old, oe); 779 dprintstr("new- ne", new, ne); 780 dprintstr("old-ofd", old, ofd); 781 dprintstr("new-nfd", new, nfd); 782 dprintstr("ofd-osb", ofd, osb); 783 dprintstr("nfd-nsb", nfd, nsb); 784 dprintstr("osb-ose", osb, ose); 785 dprintstr("nsb-nse", nsb, nse); 786 dprintstr("ose-ols", ose, ols); 787 dprintstr("nse-nls", nse, nls); 788 dprintstr("ols- oe", ols, oe); 789 dprintstr("nls- ne", nls, ne); 790 #endif /* DEBUG_UPDATE */ 791 792 /* 793 * CursorV to this line cur_line MUST be in this routine so that if we 794 * don't have to change the line, we don't move to it. CursorH to first 795 * diff char 796 */ 797 MoveToLine(cur_line); 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(Char *dst, Char *src, int width) 1097 { 1098 int i; 1099 1100 for (i = 0; i < width; i++) { 1101 if (*src == (Char) 0) 1102 break; 1103 *dst++ = *src++; 1104 } 1105 1106 while (i < width) { 1107 *dst++ = ' '; 1108 i++; 1109 } 1110 *dst = (Char) 0; 1111 } 1112 1113 void 1114 RefCursor(void) 1115 { /* only move to new cursor pos */ 1116 Char *cp; 1117 int w, h, th, v; 1118 1119 /* first we must find where the cursor is... */ 1120 h = 0; 1121 v = 0; 1122 th = TermH; /* optimize for speed */ 1123 1124 for (cp = Prompt; cp != NULL && *cp; ) { /* do prompt */ 1125 if (*cp & LITERAL) { 1126 cp++; 1127 continue; 1128 } 1129 w = NLSClassify(*cp & CHAR, cp == Prompt); 1130 cp++; 1131 switch(w) { 1132 case NLSCLASS_NL: 1133 h = 0; 1134 v++; 1135 break; 1136 case NLSCLASS_TAB: 1137 while (++h & 07) 1138 ; 1139 break; 1140 case NLSCLASS_CTRL: 1141 h += 2; 1142 break; 1143 case NLSCLASS_ILLEGAL: 1144 h += 4; 1145 break; 1146 case NLSCLASS_ILLEGAL2: 1147 case NLSCLASS_ILLEGAL3: 1148 case NLSCLASS_ILLEGAL4: 1149 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w); 1150 break; 1151 default: 1152 h += w; 1153 } 1154 if (h >= th) { /* check, extra long tabs picked up here also */ 1155 h -= th; 1156 v++; 1157 } 1158 } 1159 1160 for (cp = InputBuf; cp < Cursor;) { /* do input buffer to Cursor */ 1161 w = NLSClassify(*cp & CHAR, cp == InputBuf); 1162 cp++; 1163 switch(w) { 1164 case NLSCLASS_NL: 1165 h = 0; 1166 v++; 1167 break; 1168 case NLSCLASS_TAB: 1169 while (++h & 07) 1170 ; 1171 break; 1172 case NLSCLASS_CTRL: 1173 h += 2; 1174 break; 1175 case NLSCLASS_ILLEGAL: 1176 h += 4; 1177 break; 1178 case NLSCLASS_ILLEGAL2: 1179 case NLSCLASS_ILLEGAL3: 1180 case NLSCLASS_ILLEGAL4: 1181 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w); 1182 break; 1183 default: 1184 h += w; 1185 } 1186 if (h >= th) { /* check, extra long tabs picked up here also */ 1187 h -= th; 1188 v++; 1189 } 1190 } 1191 1192 /* now go there */ 1193 MoveToLine(v); 1194 MoveToChar(h); 1195 if (adrof(STRhighlight) && MarkIsSet) { 1196 ClearLines(); 1197 ClearDisp(); 1198 Refresh(); 1199 } 1200 flush(); 1201 } 1202 1203 #ifndef WINTT_NATIVE 1204 static void 1205 PutPlusOne(Char c, int width) 1206 { 1207 while (width > 1 && CursorH + width > TermH) 1208 PutPlusOne(' ', 1); 1209 if ((c & LITERAL) != 0) { 1210 Char *d; 1211 for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++) 1212 (void) putwraw(*d); 1213 } else { 1214 (void) putwraw(c); 1215 } 1216 Display[CursorV][CursorH++] = (Char) c; 1217 while (--width > 0) 1218 Display[CursorV][CursorH++] = CHAR_DBWIDTH; 1219 if (CursorH >= TermH) { /* if we must overflow */ 1220 CursorH = 0; 1221 CursorV++; 1222 OldvcV++; 1223 if (T_Margin & MARGIN_AUTO) { 1224 if (T_Margin & MARGIN_MAGIC) { 1225 (void) putraw(' '); 1226 (void) putraw('\b'); 1227 } 1228 } 1229 else { 1230 (void) putraw('\r'); 1231 (void) putraw('\n'); 1232 } 1233 } 1234 } 1235 #endif 1236 1237 void 1238 RefPlusOne(int l) 1239 { /* we added just one char, handle it fast. 1240 * assumes that screen cursor == real cursor */ 1241 Char *cp, c; 1242 int w; 1243 1244 if (Cursor != LastChar) { 1245 Refresh(); /* too hard to handle */ 1246 return; 1247 } 1248 if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) { 1249 Refresh(); /* clear out rprompt if less than one char gap*/ 1250 return; 1251 } 1252 cp = Cursor - l; 1253 c = *cp & CHAR; 1254 w = NLSClassify(c, cp == InputBuf); 1255 switch(w) { 1256 case NLSCLASS_CTRL: 1257 PutPlusOne('^', 1); 1258 if (c == CTL_ESC('\177')) { 1259 PutPlusOne('?', 1); 1260 break; 1261 } 1262 #ifdef IS_ASCII 1263 /* uncontrolify it; works only for iso8859-1 like sets */ 1264 PutPlusOne((c | 0100), 1); 1265 #else 1266 PutPlusOne(_toebcdic[_toascii[c]|0100], 1); 1267 #endif 1268 break; 1269 case NLSCLASS_ILLEGAL: 1270 PutPlusOne('\\', 1); 1271 PutPlusOne(((c >> 6) & 7) + '0', 1); 1272 PutPlusOne(((c >> 3) & 7) + '0', 1); 1273 PutPlusOne((c & 7) + '0', 1); 1274 break; 1275 case 1: 1276 if (adrof(STRhighlight) && MarkIsSet) 1277 StartHighlight(); 1278 if (l > 1) 1279 PutPlusOne(MakeLiteral(cp, l, 0), 1); 1280 else 1281 PutPlusOne(*cp, 1); 1282 if (adrof(STRhighlight) && MarkIsSet) 1283 StopHighlight(); 1284 break; 1285 default: 1286 Refresh(); /* too hard to handle */ 1287 return; 1288 } 1289 flush(); 1290 } 1291 1292 /* clear the screen buffers so that new new prompt starts fresh. */ 1293 1294 void 1295 ClearDisp(void) 1296 { 1297 int i; 1298 1299 CursorV = 0; /* clear the display buffer */ 1300 CursorH = 0; 1301 for (i = 0; i < TermV; i++) 1302 (void) memset(Display[i], 0, TermH * sizeof(Display[0][0])); 1303 OldvcV = 0; 1304 litlen = 0; 1305 } 1306 1307 void 1308 ClearLines(void) 1309 { /* Make sure all lines are *really* blank */ 1310 int i; 1311 1312 if (T_CanCEOL) { 1313 /* 1314 * Clear the lines from the bottom up so that if we try moving 1315 * the cursor down by writing the character that is at the end 1316 * of the screen line, we won't rewrite a character that shouldn't 1317 * be there. 1318 */ 1319 for (i = OldvcV; i >= 0; i--) { /* for each line on the screen */ 1320 MoveToLine(i); 1321 MoveToChar(0); 1322 ClearEOL(TermH); 1323 } 1324 } 1325 else { 1326 MoveToLine(OldvcV); /* go to last line */ 1327 (void) putraw('\r'); /* go to BOL */ 1328 (void) putraw('\n'); /* go to new line */ 1329 } 1330 } 1331