1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ex.h" 36 #include "ex_tune.h" 37 #include "ex_tty.h" 38 #include "ex_vis.h" 39 40 /* 41 * Routines to deal with management of logical versus physical 42 * display, opening and redisplaying lines on the screen, and 43 * use of intelligent terminal operations. Routines to deal with 44 * screen cleanup after a change. 45 */ 46 47 /* 48 * Display a new line at physical line p, returning 49 * the depth of the newly displayed line. We may decide 50 * to expand the window on an intelligent terminal if it is 51 * less than a full screen by deleting a line above the top of the 52 * window before doing an insert line to keep all the good text 53 * on the screen in which case the line may actually end up 54 * somewhere other than line p. 55 */ 56 void 57 vopen(line *tp, int p) 58 { 59 int cnt; 60 struct vlinfo *vp, *vpc; 61 62 #ifdef ADEBUG 63 if (trace != NULL) 64 tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p); 65 #endif 66 if (state != VISUAL) { 67 if (vcnt) 68 if (hold & HOLDROL) 69 vup1(); 70 else 71 vclean(); 72 73 /* 74 * Forget all that we once knew. 75 */ 76 vcnt = vcline = 0; 77 p = WBOT; LASTLINE = WBOT + 1; 78 state = bastate; 79 WTOP = basWTOP; 80 WLINES = basWLINES; 81 } 82 vpc = &vlinfo[vcline]; 83 for (vp = &vlinfo[vcnt]; vp >= vpc; vp--) 84 vlcopy(vp[1], vp[0]); 85 vcnt++; 86 if (Pline == numbline) 87 /* 88 * Dirtying all the lines is rather inefficient 89 * internally, but number mode is used rarely 90 * and so it's not worth optimizing. 91 */ 92 vdirty(vcline+1, WECHO); 93 getline(*tp); 94 95 /* 96 * If we are opening at the top of the window, can try a window 97 * expansion at the top. 98 */ 99 if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) { 100 cnt = p + vdepth() - LINE(1); 101 if (cnt > 0) { 102 p -= cnt; 103 if (p < ZERO) 104 p = ZERO; 105 WTOP = p; 106 WLINES = WBOT - WTOP + 1; 107 } 108 } 109 vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0; 110 cnt = vreopen(p, lineno(tp), vcline); 111 if (vcline + 1 == vcnt) 112 LINE(vcnt) = LINE(vcline) + cnt; 113 } 114 115 /* 116 * Redisplay logical line l at physical line p with line number lineno. 117 */ 118 int 119 vreopen(int p, int lineno, int l) 120 { 121 int d; 122 struct vlinfo *vp = &vlinfo[l]; 123 124 d = vp->vdepth; 125 if (d == 0 || (vp->vflags & VDIRT)) 126 vp->vdepth = d = vdepth(); 127 vp->vliny = p, vp->vflags &= ~VDIRT; 128 129 /* 130 * Try to win by making the screen larger rather than inserting 131 * a line and driving text off the bottom. 132 */ 133 p = vglitchup(l, 0); 134 135 /* 136 * BUG: Should consider using clr_eol here to clear to end of line. 137 * As it stands we always strike over the current text. 138 * Since often the current text is the same as what 139 * we are overstriking with, it tends not to show. 140 * On the other hand if it is different and we end up 141 * spacing out a lot of text, we could have won with 142 * a clr_eol. This is probably worthwhile at low speed 143 * only however, since clearly computation will be 144 * necessary to determine which way to go. 145 */ 146 vigoto(p, 0); 147 pline(lineno); 148 149 if (state == VISUAL && l == vcline && vp->vliny < 0) { 150 vp->vliny = 0; 151 vscrap(); 152 return (d); 153 } 154 155 /* 156 * When we are typing part of a line for hardcopy open, don't 157 * want to type the '$' marking an end of line if in list mode. 158 */ 159 if (hold & HOLDDOL) 160 return (d); 161 if (Putchar == listchar) 162 putchar('$'); 163 164 /* 165 * Optimization of cursor motion may prevent screen rollup if the 166 * line has blanks/tabs at the end unless we force the cursor to appear 167 * on the last line segment. 168 */ 169 if (vp->vliny + d - 1 > WBOT) 170 vcsync(); 171 172 /* 173 * Switch into hardcopy open mode if we are in one line (adm3) 174 * open mode and this line is now too long. If in hardcopy 175 * open mode, then call sethard to move onto the next line 176 * with appropriate positioning. 177 */ 178 if (state == ONEOPEN) { 179 WCOLS = OCOLUMNS; 180 if (vdepth() > 1) { 181 WCOLS = TUBECOLS; 182 sethard(); 183 } else 184 WCOLS = TUBECOLS; 185 } else if (state == HARDOPEN) 186 sethard(); 187 188 /* 189 * Unless we filled (completely) the last line we typed on, 190 * we have to clear to the end of the line 191 * in case stuff is left from before. 192 */ 193 if (vp->vliny + d > destline) { 194 if (insert_null_glitch && destcol == WCOLS) 195 vigoto(vp->vliny + d - 1, 0); 196 vclreol(); 197 } 198 return (d); 199 } 200 201 /* 202 * Real work for winning growing of window at top 203 * when inserting in the middle of a partially full 204 * screen on an intelligent terminal. We have as argument 205 * the logical line number to be inserted after, and the offset 206 * from that line where the insert will go. 207 * We look at the picture of depths and positions, and if we can 208 * delete some (blank) lines from the top of the screen so that 209 * later inserts will not push stuff off the bottom. 210 */ 211 int 212 vglitchup(int l, int o) 213 { 214 struct vlinfo *vp = &vlinfo[l]; 215 int need; 216 int p = vp->vliny; 217 short oldhold, oldheldech; 218 bool glitched = 0; 219 220 if (l < vcnt - 1) { 221 need = p + vp->vdepth - (vp+1)->vliny; 222 if (need > 0) { 223 if (state == VISUAL && WTOP - ZERO >= need && insert_line && delete_line) { 224 glitched++; 225 WTOP -= need; 226 WLINES = WBOT - WTOP + 1; 227 p -= need; 228 if (p + o == WTOP) { 229 vp->vliny = WTOP; 230 return (WTOP + o); 231 } 232 vdellin(WTOP, need, -1); 233 oldheldech = heldech; 234 oldhold = hold; 235 hold |= HOLDECH; 236 } 237 vinslin((vp+1)->vliny, need, l); 238 if (glitched) { 239 hold = oldhold; 240 heldech = oldheldech; 241 } 242 } 243 } else 244 vp[1].vliny = vp[0].vliny + vp->vdepth; 245 return (p + o); 246 } 247 248 /* 249 * Insert cnt blank lines before line p, 250 * logically and (if supported) physically. 251 */ 252 void 253 vinslin(int p, int cnt, int l) 254 { 255 int i; 256 bool could = 1; 257 258 #ifdef ADEBUG 259 if (trace) 260 tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l); 261 #endif 262 if (p + cnt > WBOT && clr_eos) { 263 /* 264 * Really quick -- clear to end of screen. 265 */ 266 cnt = WECHO + 1 - p; 267 vgoto(p, 0), vputp(clr_eos, cnt); 268 vclrech(1); 269 vadjAL(p, cnt); 270 } else if (scroll_reverse && p == WTOP && costSR < costAL) { 271 /* 272 * Use reverse scroll mode of the terminal, at 273 * the top of the window. Reverse linefeed works 274 * too, since we only use it from line WTOP. 275 */ 276 for (i = cnt; i > 0; i--) { 277 vgoto(p, 0), vputp(scroll_reverse, 0); 278 if (i > 1 && (hold & HOLDAT) == 0) 279 putchar('@'); 280 /* 281 * If we are at the top of the screen, and the 282 * terminal retains display above, then we 283 * should try to clear to end of line. 284 * Have to use clr_eol since we don't remember what is 285 * actually on the line. 286 */ 287 if (clr_eol && (memory_above || p != 0)) 288 vputp(clr_eol, 1); 289 } 290 vadjAL(p, cnt); 291 } else if (insert_line) { 292 /* 293 * Use insert line. 294 */ 295 vgoto(p, 0); 296 if (parm_insert_line && (cnt>1 || *insert_line==0)) { 297 /* insert cnt lines. Should do @'s too. */ 298 vputp(tparm(parm_insert_line, cnt, p), WECHO+1-p); 299 } 300 else if (change_scroll_region && *insert_line==0) { 301 /* vt100 change scrolling region to fake insert_line */ 302 vputp(save_cursor, 1); 303 vputp(tparm(change_scroll_region, p, lines-1), 1); 304 vputp(restore_cursor, 1); /* change_scroll_region homes stupid cursor */ 305 for (i=cnt; i>0; i--) 306 vputp(scroll_reverse, 1); /* should do @'s */ 307 vputp(tparm(change_scroll_region, 0, lines-1), 1); 308 vputp(restore_cursor, 1); /* Once again put it back */ 309 } 310 else { 311 vputp(insert_line, WECHO + 1 - p); 312 for (i = cnt - 1; i > 0; i--) { 313 vgoto(outline+1, 0); 314 vputp(insert_line, WECHO + 1 - outline); 315 if ((hold & HOLDAT) == 0) 316 putchar('@'); 317 } 318 } 319 vadjAL(p, cnt); 320 } else 321 could = 0; 322 vopenup(cnt, could, l); 323 } 324 325 /* 326 * Logically open up after line l, cnt of them. 327 * We need to know if it was done ``physically'' since in this 328 * case we accept what the hardware gives us. If we have to do 329 * it ourselves (brute force) we will squish out @ lines in the process 330 * if this will save us work. 331 */ 332 void 333 vopenup(int cnt, bool could, int l) 334 { 335 struct vlinfo *vc = &vlinfo[l + 1]; 336 struct vlinfo *ve = &vlinfo[vcnt]; 337 338 #ifdef ADEBUG 339 if (trace) 340 tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l); 341 #endif 342 if (could) 343 /* 344 * This will push @ lines down the screen, 345 * just as the hardware did. Since the default 346 * for intelligent terminals is to never have @ 347 * lines on the screen, this should never happen, 348 * and the code makes no special effort to be nice in this 349 * case, e.g. squishing out the @ lines by delete lines 350 * before doing append lines. 351 */ 352 for (; vc <= ve; vc++) 353 vc->vliny += cnt; 354 else { 355 /* 356 * Will have to clean up brute force eventually, 357 * so push the line data around as little as possible. 358 */ 359 vc->vliny += cnt, vc->vflags |= VDIRT; 360 while (vc < ve) { 361 int i = vc->vliny + vc->vdepth; 362 363 vc++; 364 if (i <= vc->vliny) 365 break; 366 vc->vliny = i, vc->vflags |= VDIRT; 367 } 368 } 369 vscrap(); 370 } 371 372 /* 373 * Adjust data structure internally to account for insertion of 374 * blank lines on the screen. 375 */ 376 void 377 vadjAL(int p, int cnt) 378 { 379 wchar_t *tlines[TUBELINES]; 380 int from, to; 381 382 #ifdef ADEBUG 383 if (trace) 384 tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt); 385 #endif 386 copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ 387 for (from = p, to = p + cnt; to <= WECHO; from++, to++) 388 vtube[to] = tlines[from]; 389 for (to = p; from <= WECHO; from++, to++) { 390 vtube[to] = tlines[from]; 391 vclrbyte(vtube[to], WCOLS); 392 } 393 /* 394 * Have to clear the echo area since its contents aren't 395 * necessarily consistent with the rest of the display. 396 */ 397 vclrech(0); 398 } 399 400 /* 401 * Roll the screen up logically and physically 402 * so that line dl is the bottom line on the screen. 403 */ 404 void 405 vrollup(int dl) 406 { 407 int cnt; 408 int dc = destcol; 409 410 #ifdef ADEBUG 411 if (trace) 412 tfixnl(), fprintf(trace, "vrollup(%d)\n", dl); 413 #endif 414 cnt = dl - (splitw ? WECHO : WBOT); 415 if (splitw && (state == VISUAL || state == CRTOPEN)) 416 holdupd = 1; 417 vmoveitup(cnt, 1); 418 vscroll(cnt); 419 destline = dl - cnt, destcol = dc; 420 } 421 422 void 423 vup1(void) 424 { 425 426 vrollup(WBOT + 1); 427 } 428 429 /* 430 * Scroll the screen up cnt lines physically. 431 * If doclr is true, do a clear eol if the terminal 432 * has standout (to prevent it from scrolling up) 433 */ 434 void 435 vmoveitup(int cnt, bool doclr) 436 { 437 438 if (cnt == 0) 439 return; 440 #ifdef ADEBUG 441 if (trace) 442 tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt); 443 #endif 444 if (doclr) 445 vclrech(0); 446 if (scroll_forward) { 447 destline = WECHO; 448 destcol = (NONL ? 0 : outcol % WCOLS); 449 fgoto(); 450 while (cnt > 0) 451 vputp(scroll_forward, 0), cnt--; 452 } 453 else { 454 destline = WECHO + cnt; 455 destcol = (NONL ? 0 : outcol % WCOLS); 456 fgoto(); 457 if (state == ONEOPEN || state == HARDOPEN) { 458 outline = destline = 0; 459 vclrbyte(vtube[0], WCOLS); 460 } 461 } 462 /* Get rid of line we just rolled up */ 463 if (doclr && memory_below && clr_eol) 464 vclrech(0); 465 } 466 467 /* 468 * Scroll the screen up cnt lines logically. 469 */ 470 void 471 vscroll(int cnt) 472 { 473 int from, to; 474 wchar_t *tlines[TUBELINES]; 475 476 #ifdef ADEBUG 477 if (trace) 478 fprintf(trace, "vscroll(%d)\n", cnt); 479 #endif 480 if (cnt < 0 || cnt > TUBELINES) 481 error(gettext("Internal error: vscroll")); 482 if (cnt == 0) 483 return; 484 copy(tlines, vtube, sizeof vtube); 485 for (to = ZERO, from = ZERO + cnt; to <= WECHO - cnt; to++, from++) 486 vtube[to] = tlines[from]; 487 for (from = ZERO; to <= WECHO; to++, from++) { 488 vtube[to] = tlines[from]; 489 vclrbyte(vtube[to], WCOLS); 490 } 491 for (from = 0; from <= vcnt; from++) 492 LINE(from) -= cnt; 493 } 494 495 /* 496 * Discard logical lines due to physical wandering off the screen. 497 */ 498 void 499 vscrap(void) 500 { 501 int i, j; 502 503 #ifdef ADEBUG 504 if (trace) 505 tfixnl(), fprintf(trace, "vscrap\n"), tvliny(); 506 #endif 507 if (splitw) 508 return; 509 if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) { 510 WTOP = LINE(0); 511 WLINES = WBOT - WTOP + 1; 512 } 513 for (j = 0; j < vcnt; j++) 514 if (LINE(j) >= WTOP) { 515 if (j == 0) 516 break; 517 /* 518 * Discard the first j physical lines off the top. 519 */ 520 vcnt -= j, vcline -= j; 521 for (i = 0; i <= vcnt; i++) 522 vlcopy(vlinfo[i], vlinfo[i + j]); 523 break; 524 } 525 /* 526 * Discard lines off the bottom. 527 */ 528 if (vcnt) { 529 for (j = 0; j <= vcnt; j++) 530 if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) { 531 vcnt = j; 532 break; 533 } 534 if (vcnt == 0) 535 LASTLINE = 0; 536 else 537 LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1); 538 } 539 #ifdef ADEBUG 540 if (trace) 541 tvliny(); 542 #endif 543 /* 544 * May have no lines! 545 */ 546 } 547 548 /* 549 * Repaint the screen, with cursor at curs, aftern an arbitrary change. 550 * Handle notification on large changes. 551 */ 552 void 553 vrepaint(unsigned char *curs) 554 { 555 556 wdot = NOLINE; 557 /* 558 * In open want to notify first. 559 */ 560 noteit(0); 561 vscrap(); 562 563 /* 564 * Deal with a totally useless display. 565 */ 566 if (vcnt == 0 || vcline < 0 || vcline > vcnt || holdupd && state != VISUAL) { 567 line *odol = dol; 568 569 vcnt = 0; 570 if (holdupd) 571 if (state == VISUAL) 572 (void)peekkey(); 573 else 574 vup1(); 575 holdupd = 0; 576 if (odol == zero) 577 fixzero(); 578 vcontext(dot, '.'); 579 noteit(1); 580 if (noteit(1) == 0 && odol == zero) { 581 CATCH 582 error(gettext("No lines in buffer")); 583 ENDCATCH 584 linebuf[0] = 0; 585 splitw = 0; 586 } 587 vnline(curs); 588 return; 589 } 590 591 /* 592 * Have some useful displayed text; refresh it. 593 */ 594 getDOT(); 595 596 /* 597 * This is for boundary conditions in open mode. 598 */ 599 if (FLAGS(0) & VDIRT) 600 vsync(WTOP); 601 602 /* 603 * If the current line is after the last displayed line 604 * or the bottom of the screen, then special effort is needed 605 * to get it on the screen. We first try a redraw at the 606 * last line on the screen, hoping it will fill in where @ 607 * lines are now. If this doesn't work, then roll it onto 608 * the screen. 609 */ 610 if (vcline >= vcnt || LINE(vcline) > WBOT) { 611 short oldhold = hold; 612 hold |= HOLDAT, vredraw(LASTLINE), hold = oldhold; 613 if (vcline >= vcnt) { 614 int i = vcline - vcnt + 1; 615 616 dot -= i; 617 vcline -= i; 618 vroll(i); 619 } else 620 vsyncCL(); 621 } else 622 vsync(vcline > 0 ? LINE(vcline - 1) : WTOP); 623 624 /* 625 * Notification on large change for visual 626 * has to be done last or we may lose 627 * the echo area with redisplay. 628 */ 629 noteit(1); 630 631 /* 632 * Finally. Move the cursor onto the current line. 633 */ 634 vnline(curs); 635 } 636 637 /* 638 * Fully cleanup the screen, leaving no @ lines except at end when 639 * line after last won't completely fit. The routine vsync is 640 * more conservative and much less work on dumb terminals. 641 */ 642 void 643 vredraw(int p) 644 { 645 int l; 646 line *tp; 647 unsigned char temp[LBSIZE]; 648 bool anydl = 0; 649 short oldhold = hold; 650 651 #ifdef ADEBUG 652 if (trace) 653 tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny(); 654 #endif 655 if (holdupd) { 656 holdupd = 3; 657 return; 658 } 659 if (state == HARDOPEN || splitw) 660 return; 661 if (p < 0 /* || p > WECHO */) 662 error(gettext("Internal error: vredraw")); 663 664 /* 665 * Trim the ragged edges (lines which are off the screen but 666 * not yet logically discarded), save the current line, and 667 * search for first logical line affected by the redraw. 668 */ 669 vscrap(); 670 CP(temp, linebuf); 671 l = 0; 672 tp = dot - vcline; 673 if (vcnt == 0) 674 LINE(0) = WTOP; 675 while (l < vcnt && LINE(l) < p) 676 l++, tp++; 677 678 /* 679 * We hold off echo area clearing during the redraw in deference 680 * to a final clear of the echo area at the end if appropriate. 681 */ 682 heldech = 0; 683 hold |= HOLDECH; 684 for (; l < vcnt && Peekkey != ATTN; l++) { 685 if (l == vcline) 686 strcLIN(temp); 687 else 688 getline(*tp); 689 690 /* 691 * Delete junk between displayed lines. 692 */ 693 if (LINE(l) != LINE(l + 1) && LINE(l) != p) { 694 if (anydl == 0 && memory_below && clr_eos) { 695 hold = oldhold; 696 vclrech(0); 697 anydl = 1; 698 hold |= HOLDECH; 699 heldech = 0; 700 } 701 vdellin(p, LINE(l) - p, l); 702 } 703 704 /* 705 * If line image is not know to be up to date, then 706 * redisplay it; else just skip onward. 707 */ 708 LINE(l) = p; 709 if (FLAGS(l) & VDIRT) { 710 DEPTH(l) = vdepth(); 711 if (l != vcline && p + DEPTH(l) - 1 > WBOT) { 712 vscrap(); 713 break; 714 } 715 FLAGS(l) &= ~VDIRT; 716 (void) vreopen(p, lineno(tp), l); 717 p = LINE(l) + DEPTH(l); 718 } else 719 p += DEPTH(l); 720 tp++; 721 } 722 723 /* 724 * That takes care of lines which were already partially displayed. 725 * Now try to fill the rest of the screen with text. 726 */ 727 if (state == VISUAL && p <= WBOT) { 728 int ovcline = vcline; 729 730 vcline = l; 731 for (; tp <= dol && Peekkey != ATTN; tp++) { 732 getline(*tp); 733 if (p + vdepth() - 1 > WBOT) 734 break; 735 vopen(tp, p); 736 p += DEPTH(vcline); 737 vcline++; 738 } 739 vcline = ovcline; 740 } 741 742 /* 743 * That's all the text we can get on. 744 * Now rest of lines (if any) get either a ~ if they 745 * are past end of file, or an @ if the next line won't fit. 746 */ 747 for (; p <= WBOT && Peekkey != ATTN; p++) 748 vclrlin(p, tp); 749 strcLIN(temp); 750 hold = oldhold; 751 if (heldech) 752 vclrech(0); 753 #ifdef ADEBUG 754 if (trace) 755 tvliny(); 756 #endif 757 } 758 759 /* 760 * Do the real work in deleting cnt lines starting at line p from 761 * the display. First affected line is line l. 762 */ 763 void 764 vdellin(int p, int cnt, int l) 765 { 766 int i; 767 768 if (cnt == 0) 769 return; 770 if (delete_line == NOSTR || cnt < 0) { 771 /* 772 * Can't do it; just remember that line l is munged. 773 */ 774 FLAGS(l) |= VDIRT; 775 return; 776 } 777 #ifdef ADEBUG 778 if (trace) 779 tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l); 780 #endif 781 /* 782 * Send the deletes to the screen and then adjust logical 783 * and physical internal data structures. 784 */ 785 vgoto(p, 0); 786 if (parm_delete_line && (cnt>1 || *delete_line==0)) { 787 vputp(tparm(parm_delete_line, cnt, p), WECHO-p); 788 } 789 else if (change_scroll_region && *delete_line==0) { 790 /* vt100: fake delete_line by changing scrolling region */ 791 vputp(save_cursor, 1); /* Save since change_scroll_region homes stupid cursor */ 792 vputp(tparm(change_scroll_region, p, lines-1), 1); 793 vputp(tparm(cursor_address, lines-1, 0), 1);/* Go to lower left corner */ 794 for (i=0; i<cnt; i++) /* .. and scroll cnt times */ 795 (void) putch('\n'); /* should check NL too */ 796 vputp(tparm(change_scroll_region, 0, lines-1), 1);/* restore scrolling region */ 797 vputp(restore_cursor, 1); /* put cursor back */ 798 } 799 else { 800 for (i = 0; i < cnt; i++) 801 vputp(delete_line, WECHO - p); 802 } 803 vadjDL(p, cnt); 804 vcloseup(l, cnt); 805 } 806 /* 807 * Adjust internal physical screen image to account for deleted lines. 808 */ 809 void 810 vadjDL(int p, int cnt) 811 { 812 wchar_t *tlines[TUBELINES]; 813 int from, to; 814 815 #ifdef ADEBUG 816 if (trace) 817 tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt); 818 #endif 819 /* 820 * Would like to use structured assignment but early 821 * v7 compiler (released with phototypesetter for v6) 822 * can't hack it. 823 */ 824 copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ 825 for (from = p + cnt, to = p; from <= WECHO; from++, to++) 826 vtube[to] = tlines[from]; 827 for (from = p; to <= WECHO; from++, to++) { 828 vtube[to] = tlines[from]; 829 vclrbyte(vtube[to], WCOLS); 830 } 831 } 832 /* 833 * Sync the screen, like redraw but more lazy and willing to leave 834 * @ lines on the screen. VsyncCL syncs starting at the current line. 835 * In any case, if the redraw option is set then all syncs map to redraws 836 * as if vsync didn't exist. 837 */ 838 void 839 vsyncCL(void) 840 { 841 842 vsync(LINE(vcline)); 843 } 844 845 void 846 vsync(int p) 847 { 848 849 if (value(vi_REDRAW)) 850 vredraw(p); 851 else 852 vsync1(p); 853 } 854 855 /* 856 * The guts of a sync. Similar to redraw but 857 * just less ambitious. 858 */ 859 void 860 vsync1(int p) 861 { 862 int l; 863 unsigned char temp[LBSIZE]; 864 struct vlinfo *vp = &vlinfo[0]; 865 short oldhold = hold; 866 867 #ifdef ADEBUG 868 if (trace) 869 tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny(); 870 #endif 871 if (holdupd) { 872 if (holdupd < 3) 873 holdupd = 2; 874 return; 875 } 876 if (state == HARDOPEN || splitw) 877 return; 878 vscrap(); 879 CP(temp, linebuf); 880 if (vcnt == 0) 881 LINE(0) = WTOP; 882 l = 0; 883 while (l < vcnt && vp->vliny < p) 884 l++, vp++; 885 heldech = 0; 886 hold |= HOLDECH; 887 while (p <= WBOT && Peekkey != ATTN) { 888 /* 889 * Want to put a line here if not in visual and first line 890 * or if there are lies left and this line starts before 891 * the current line, or if this line is piled under the 892 * next line (vreplace does this and we undo it). 893 */ 894 if (l == 0 && state != VISUAL || 895 (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) { 896 if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) { 897 if (l == vcline) 898 strcLIN(temp); 899 else 900 getline(dot[l - vcline]); 901 /* 902 * Be careful that a long line doesn't cause the 903 * screen to shoot up. 904 */ 905 if (l != vcline && (vp->vflags & VDIRT)) { 906 vp->vdepth = vdepth(); 907 vp->vflags &= ~VDIRT; 908 if (p + vp->vdepth - 1 > WBOT) 909 break; 910 } 911 (void) vreopen(p, lineDOT() + (l - vcline), l); 912 } 913 p = vp->vliny + vp->vdepth; 914 vp++; 915 l++; 916 } else 917 /* 918 * A physical line between logical lines, 919 * so we settle for an @ at the beginning. 920 */ 921 vclrlin(p, dot + (l - vcline)), p++; 922 } 923 strcLIN(temp); 924 hold = oldhold; 925 if (heldech) 926 vclrech(0); 927 } 928 929 /* 930 * Subtract (logically) cnt physical lines from the 931 * displayed position of lines starting with line l. 932 */ 933 void 934 vcloseup(int l, int cnt) 935 { 936 int i; 937 938 #ifdef ADEBUG 939 if (trace) 940 tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt); 941 #endif 942 for (i = l + 1; i <= vcnt; i++) 943 LINE(i) -= cnt; 944 } 945 946 /* 947 * Workhorse for rearranging line descriptors on changes. 948 * The idea here is that, starting with line l, cnt lines 949 * have been replaced with newcnt lines. All of these may 950 * be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0, 951 * since we may be called from an undo after the screen has 952 * moved a lot. Thus we have to be careful. 953 * 954 * Many boundary conditions here. 955 */ 956 void 957 vreplace(int l, int cnt, int newcnt) 958 { 959 int from, to, i; 960 bool savenote = 0; 961 962 #ifdef ADEBUG 963 if (trace) { 964 tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt); 965 tvliny(); 966 } 967 #endif 968 if (l >= vcnt) 969 return; 970 if (l < 0) { 971 if (l + cnt < 0) { 972 /* 973 * Nothing on the screen is relevant. 974 * Settle for redrawing from scratch (later). 975 */ 976 vcnt = 0; 977 return; 978 } 979 /* 980 * Normalize l to top of screen; the add is 981 * really a subtract from cnt since l is negative. 982 */ 983 cnt += l; 984 l = 0; 985 986 /* 987 * Unseen lines were affected so notify (later). 988 */ 989 savenote++; 990 } 991 992 /* 993 * These shouldn't happen 994 * but would cause great havoc. 995 */ 996 if (cnt < 0) 997 cnt = 0; 998 if (newcnt < 0) 999 newcnt = 0; 1000 1001 /* 1002 * Surely worthy of note if more than report 1003 * lines were changed. 1004 */ 1005 if (cnt > value(vi_REPORT) || newcnt > value(vi_REPORT)) 1006 savenote++; 1007 1008 /* 1009 * Same number of lines affected as on screen, and we 1010 * can insert and delete lines. Thus we just type 1011 * over them, since otherwise we will push them 1012 * slowly off the screen, a clear lose. 1013 */ 1014 if (cnt == newcnt || vcnt - l == newcnt && insert_line && delete_line) { 1015 if (cnt > 1 && l + cnt > vcnt) 1016 savenote++; 1017 vdirty(l, newcnt); 1018 } else { 1019 /* 1020 * Lines are going away, squish them out. 1021 */ 1022 if (cnt > 0) { 1023 /* 1024 * If non-displayed lines went away, 1025 * always notify. 1026 */ 1027 if (cnt > 1 && l + cnt > vcnt) 1028 savenote++; 1029 if (l + cnt >= vcnt) 1030 cnt = vcnt - l; 1031 else 1032 for (from = l + cnt, to = l; from <= vcnt; to++, from++) 1033 vlcopy(vlinfo[to], vlinfo[from]); 1034 vcnt -= cnt; 1035 } 1036 /* 1037 * Open up space for new lines appearing. 1038 * All new lines are piled in the same place, 1039 * and will be unpiled by vredraw/vsync, which 1040 * inserts lines in front as it unpiles. 1041 */ 1042 if (newcnt > 0) { 1043 /* 1044 * Newlines are appearing which may not show, 1045 * so notify (this is only approximately correct 1046 * when long lines are present). 1047 */ 1048 if (newcnt > 1 && l + newcnt > vcnt + 1) 1049 savenote++; 1050 1051 /* 1052 * If there will be more lines than fit, then 1053 * just throw way the rest of the stuff on the screen. 1054 */ 1055 if (l + newcnt > WBOT && insert_line && delete_line) { 1056 vcnt = l; 1057 goto skip; 1058 } 1059 from = vcnt, to = vcnt + newcnt; 1060 i = TUBELINES - to; 1061 if (i < 0) 1062 from += i, to += i; 1063 vcnt = to; 1064 for (; from >= l; from--, to--) 1065 vlcopy(vlinfo[to], vlinfo[from]); 1066 for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) { 1067 LINE(to) = LINE(from); 1068 DEPTH(to) = 0; 1069 FLAGS(to) = VDIRT; 1070 } 1071 } 1072 } 1073 skip: 1074 if (Pline == numbline && cnt != newcnt) 1075 /* 1076 * When lines positions are shifted, the numbers 1077 * will be wrong. 1078 */ 1079 vdirty(l, WECHO); 1080 if (!savenote) 1081 notecnt = 0; 1082 #ifdef ADEBUG 1083 if (trace) 1084 tvliny(); 1085 #endif 1086 } 1087 1088 /* 1089 * Start harcopy open. 1090 * Print an image of the line to the left of the cursor 1091 * under the full print of the line and position the cursor. 1092 * If we are in a scroll ^D within hardcopy open then all this 1093 * is suppressed. 1094 */ 1095 void 1096 sethard(void) 1097 { 1098 1099 if (state == VISUAL) 1100 return; 1101 rubble = 0; 1102 state = HARDOPEN; 1103 if (hold & HOLDROL) 1104 return; 1105 vup1(); 1106 LINE(0) = WBOT; 1107 if (Pline == numbline) 1108 vgoto(WBOT, 0), viprintf("%6d ", lineDOT()); 1109 } 1110 1111 /* 1112 * Mark the lines starting at base for i lines 1113 * as dirty so that they will be checked for correct 1114 * display at next sync/redraw. 1115 */ 1116 void 1117 vdirty(int base, int i) 1118 { 1119 int l; 1120 1121 for (l = base; l < vcnt; l++) { 1122 if (--i < 0) 1123 return; 1124 FLAGS(l) |= VDIRT; 1125 } 1126 } 1127