1 /*- 2 * Copyright (c) 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1992, 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #include <sys/types.h> 13 #include <sys/queue.h> 14 #include <sys/time.h> 15 16 #include <bitstring.h> 17 #include <ctype.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <unistd.h> 22 23 #include "../common/common.h" 24 #include "vi.h" 25 26 typedef enum { 27 SCROLL_W, /* User wait. */ 28 SCROLL_W_EX, /* User wait, or enter : to continue. */ 29 SCROLL_W_QUIT /* User wait, or enter q to quit. */ 30 /* 31 * SCROLL_W_QUIT has another semantic 32 * -- only wait if the screen is full 33 */ 34 } sw_t; 35 36 static void vs_divider(SCR *); 37 static void vs_msgsave(SCR *, mtype_t, char *, size_t); 38 static void vs_output(SCR *, mtype_t, const char *, int); 39 static void vs_scroll(SCR *, int *, sw_t); 40 static void vs_wait(SCR *, int *, sw_t); 41 42 /* 43 * vs_busy -- 44 * Display, update or clear a busy message. 45 * 46 * This routine is the default editor interface for vi busy messages. It 47 * implements a standard strategy of stealing lines from the bottom of the 48 * vi text screen. Screens using an alternate method of displaying busy 49 * messages, e.g. X11 clock icons, should set their scr_busy function to the 50 * correct function before calling the main editor routine. 51 * 52 * PUBLIC: void vs_busy(SCR *, const char *, busy_t); 53 */ 54 void 55 vs_busy(SCR *sp, const char *msg, busy_t btype) 56 { 57 GS *gp; 58 VI_PRIVATE *vip; 59 static const char flagc[] = "|/-\\"; 60 struct timespec ts, ts_diff; 61 const struct timespec ts_min = { 0, 125000000 }; 62 size_t len, notused; 63 const char *p; 64 65 /* Ex doesn't display busy messages. */ 66 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) 67 return; 68 69 gp = sp->gp; 70 vip = VIP(sp); 71 72 /* 73 * Most of this routine is to deal with the screen sharing real estate 74 * between the normal edit messages and the busy messages. Logically, 75 * all that's needed is something that puts up a message, periodically 76 * updates it, and then goes away. 77 */ 78 switch (btype) { 79 case BUSY_ON: 80 ++vip->busy_ref; 81 if (vip->totalcount != 0 || vip->busy_ref != 1) 82 break; 83 84 /* Initialize state for updates. */ 85 vip->busy_ch = 0; 86 timepoint_steady(&vip->busy_ts); 87 88 /* Save the current cursor. */ 89 (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx); 90 91 /* Display the busy message. */ 92 p = msg_cat(sp, msg, &len); 93 (void)gp->scr_move(sp, LASTLINE(sp), 0); 94 (void)gp->scr_addstr(sp, p, len); 95 (void)gp->scr_cursor(sp, ¬used, &vip->busy_fx); 96 (void)gp->scr_clrtoeol(sp); 97 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 98 break; 99 case BUSY_OFF: 100 if (vip->busy_ref == 0) 101 break; 102 --vip->busy_ref; 103 104 /* 105 * If the line isn't in use for another purpose, clear it. 106 * Always return to the original position. 107 */ 108 if (vip->totalcount == 0 && vip->busy_ref == 0) { 109 (void)gp->scr_move(sp, LASTLINE(sp), 0); 110 (void)gp->scr_clrtoeol(sp); 111 } 112 (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx); 113 break; 114 case BUSY_UPDATE: 115 if (vip->totalcount != 0 || vip->busy_ref == 0) 116 break; 117 118 /* Update no more than every 1/8 of a second. */ 119 timepoint_steady(&ts); 120 ts_diff = ts; 121 timespecsub(&ts_diff, &vip->busy_ts); 122 if (timespeccmp(&ts_diff, &ts_min, <)) 123 return; 124 vip->busy_ts = ts; 125 126 /* Display the update. */ 127 if (vip->busy_ch == sizeof(flagc) - 1) 128 vip->busy_ch = 0; 129 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 130 (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1); 131 (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 132 break; 133 } 134 (void)gp->scr_refresh(sp, 0); 135 } 136 137 /* 138 * vs_home -- 139 * Home the cursor to the bottom row, left-most column. 140 * 141 * PUBLIC: void vs_home(SCR *); 142 */ 143 void 144 vs_home(SCR *sp) 145 { 146 (void)sp->gp->scr_move(sp, LASTLINE(sp), 0); 147 (void)sp->gp->scr_refresh(sp, 0); 148 } 149 150 /* 151 * vs_update -- 152 * Update a command. 153 * 154 * PUBLIC: void vs_update(SCR *, const char *, const CHAR_T *); 155 */ 156 void 157 vs_update(SCR *sp, const char *m1, const CHAR_T *m2) 158 { 159 GS *gp; 160 size_t len, mlen, oldx, oldy; 161 CONST char *np; 162 size_t nlen; 163 164 gp = sp->gp; 165 166 /* 167 * This routine displays a message on the bottom line of the screen, 168 * without updating any of the command structures that would keep it 169 * there for any period of time, i.e. it is overwritten immediately. 170 * 171 * It's used by the ex read and ! commands when the user's command is 172 * expanded, and by the ex substitution confirmation prompt. 173 */ 174 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 175 if (m2 != NULL) 176 INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen); 177 (void)ex_printf(sp, 178 "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np); 179 (void)ex_fflush(sp); 180 } 181 182 /* 183 * Save the cursor position, the substitute-with-confirmation code 184 * will have already set it correctly. 185 */ 186 (void)gp->scr_cursor(sp, &oldy, &oldx); 187 188 /* Clear the bottom line. */ 189 (void)gp->scr_move(sp, LASTLINE(sp), 0); 190 (void)gp->scr_clrtoeol(sp); 191 192 /* 193 * XXX 194 * Don't let long file names screw up the screen. 195 */ 196 if (m1 != NULL) { 197 mlen = len = strlen(m1); 198 if (len > sp->cols - 2) 199 mlen = len = sp->cols - 2; 200 (void)gp->scr_addstr(sp, m1, mlen); 201 } else 202 len = 0; 203 if (m2 != NULL) { 204 mlen = STRLEN(m2); 205 if (len + mlen > sp->cols - 2) 206 mlen = (sp->cols - 2) - len; 207 (void)gp->scr_waddstr(sp, m2, mlen); 208 } 209 210 (void)gp->scr_move(sp, oldy, oldx); 211 (void)gp->scr_refresh(sp, 0); 212 } 213 214 /* 215 * vs_msg -- 216 * Display ex output or error messages for the screen. 217 * 218 * This routine is the default editor interface for all ex output, and all ex 219 * and vi error/informational messages. It implements the standard strategy 220 * of stealing lines from the bottom of the vi text screen. Screens using an 221 * alternate method of displaying messages, e.g. dialog boxes, should set their 222 * scr_msg function to the correct function before calling the editor. 223 * 224 * PUBLIC: void vs_msg(SCR *, mtype_t, char *, size_t); 225 */ 226 void 227 vs_msg(SCR *sp, mtype_t mtype, char *line, size_t len) 228 { 229 GS *gp; 230 VI_PRIVATE *vip; 231 size_t maxcols, oldx, oldy, padding; 232 const char *e, *s, *t; 233 234 gp = sp->gp; 235 vip = VIP(sp); 236 237 /* 238 * Ring the bell if it's scheduled. 239 * 240 * XXX 241 * Shouldn't we save this, too? 242 */ 243 if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) 244 if (F_ISSET(sp, SC_SCR_VI)) { 245 F_CLR(gp, G_BELLSCHED); 246 (void)gp->scr_bell(sp); 247 } else 248 F_SET(gp, G_BELLSCHED); 249 250 /* 251 * If vi is using the error line for text input, there's no screen 252 * real-estate for the error message. Nothing to do without some 253 * information as to how important the error message is. 254 */ 255 if (F_ISSET(sp, SC_TINPUT_INFO)) 256 return; 257 258 /* 259 * Ex or ex controlled screen output. 260 * 261 * If output happens during startup, e.g., a .exrc file, we may be 262 * in ex mode but haven't initialized the screen. Initialize here, 263 * and in this case, stay in ex mode. 264 * 265 * If the SC_SCR_EXWROTE bit is set, then we're switching back and 266 * forth between ex and vi, but the screen is trashed and we have 267 * to respect that. Switch to ex mode long enough to put out the 268 * message. 269 * 270 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to 271 * the screen, so previous opinions are ignored. 272 */ 273 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { 274 if (!F_ISSET(sp, SC_SCR_EX)) 275 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 276 if (sp->gp->scr_screen(sp, SC_EX)) 277 return; 278 } else 279 if (ex_init(sp)) 280 return; 281 282 if (mtype == M_ERR) 283 (void)gp->scr_attr(sp, SA_INVERSE, 1); 284 (void)printf("%.*s", (int)len, line); 285 if (mtype == M_ERR) 286 (void)gp->scr_attr(sp, SA_INVERSE, 0); 287 (void)fflush(stdout); 288 289 F_CLR(sp, SC_EX_WAIT_NO); 290 291 if (!F_ISSET(sp, SC_SCR_EX)) 292 (void)sp->gp->scr_screen(sp, SC_VI); 293 return; 294 } 295 296 /* If the vi screen isn't ready, save the message. */ 297 if (!F_ISSET(sp, SC_SCR_VI)) { 298 (void)vs_msgsave(sp, mtype, line, len); 299 return; 300 } 301 302 /* Save the cursor position. */ 303 (void)gp->scr_cursor(sp, &oldy, &oldx); 304 305 /* If it's an ex output message, just write it out. */ 306 if (mtype == M_NONE) { 307 vs_output(sp, mtype, line, len); 308 goto ret; 309 } 310 311 /* 312 * If it's a vi message, strip the trailing <newline> so we can 313 * try and paste messages together. 314 */ 315 if (line[len - 1] == '\n') 316 --len; 317 318 /* 319 * If a message won't fit on a single line, try to split on a <blank>. 320 * If a subsequent message fits on the same line, write a separator 321 * and output it. Otherwise, put out a newline. 322 * 323 * Need up to two padding characters normally; a semi-colon and a 324 * separating space. If only a single line on the screen, add some 325 * more for the trailing continuation message. 326 * 327 * XXX 328 * Assume that periods and semi-colons take up a single column on the 329 * screen. 330 * 331 * XXX 332 * There are almost certainly pathological cases that will break this 333 * code. 334 */ 335 if (IS_ONELINE(sp)) 336 (void)msg_cmsg(sp, CMSG_CONT_S, &padding); 337 else 338 padding = 0; 339 padding += 2; 340 341 maxcols = sp->cols - 1; 342 if (vip->lcontinue != 0) 343 if (len + vip->lcontinue + padding > maxcols) 344 vs_output(sp, vip->mtype, ".\n", 2); 345 else { 346 vs_output(sp, vip->mtype, ";", 1); 347 vs_output(sp, M_NONE, " ", 1); 348 } 349 vip->mtype = mtype; 350 for (s = line;; s = t) { 351 for (; len > 0 && isblank((u_char)*s); --len, ++s); 352 if (len == 0) 353 break; 354 if (len + vip->lcontinue > maxcols) { 355 for (e = s + (maxcols - vip->lcontinue); 356 e > s && !isblank((u_char)*e); --e); 357 if (e == s) 358 e = t = s + (maxcols - vip->lcontinue); 359 else 360 for (t = e; isblank((u_char)e[-1]); --e); 361 } else 362 e = t = s + len; 363 364 /* 365 * If the message ends in a period, discard it, we want to 366 * gang messages where possible. 367 */ 368 len -= t - s; 369 if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.') 370 --e; 371 vs_output(sp, mtype, s, e - s); 372 373 if (len != 0) 374 vs_output(sp, M_NONE, "\n", 1); 375 376 if (INTERRUPTED(sp)) 377 break; 378 } 379 380 ret: (void)gp->scr_move(sp, oldy, oldx); 381 (void)gp->scr_refresh(sp, 0); 382 } 383 384 /* 385 * vs_output -- 386 * Output the text to the screen. 387 */ 388 static void 389 vs_output(SCR *sp, mtype_t mtype, const char *line, int llen) 390 { 391 GS *gp; 392 VI_PRIVATE *vip; 393 size_t notused; 394 int len, rlen, tlen; 395 const char *p, *t; 396 char *cbp, *ecbp, cbuf[128]; 397 398 gp = sp->gp; 399 vip = VIP(sp); 400 for (p = line, rlen = llen; llen > 0;) { 401 /* Get the next physical line. */ 402 if ((p = memchr(line, '\n', llen)) == NULL) 403 len = llen; 404 else 405 len = p - line; 406 407 /* 408 * The max is sp->cols characters, and we may have already 409 * written part of the line. 410 */ 411 if (len + vip->lcontinue > sp->cols) 412 len = sp->cols - vip->lcontinue; 413 414 /* 415 * If the first line output, do nothing. If the second line 416 * output, draw the divider line. If drew a full screen, we 417 * remove the divider line. If it's a continuation line, move 418 * to the continuation point, else, move the screen up. 419 */ 420 if (vip->lcontinue == 0) { 421 if (!IS_ONELINE(sp)) { 422 if (vip->totalcount == 1) { 423 (void)gp->scr_move(sp, 424 LASTLINE(sp) - 1, 0); 425 (void)gp->scr_clrtoeol(sp); 426 (void)vs_divider(sp); 427 F_SET(vip, VIP_DIVIDER); 428 ++vip->totalcount; 429 ++vip->linecount; 430 } 431 if (vip->totalcount == sp->t_maxrows && 432 F_ISSET(vip, VIP_DIVIDER)) { 433 --vip->totalcount; 434 --vip->linecount; 435 F_CLR(vip, VIP_DIVIDER); 436 } 437 } 438 if (vip->totalcount != 0) 439 vs_scroll(sp, NULL, SCROLL_W_QUIT); 440 441 (void)gp->scr_move(sp, LASTLINE(sp), 0); 442 ++vip->totalcount; 443 ++vip->linecount; 444 445 if (INTERRUPTED(sp)) 446 break; 447 } else 448 (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); 449 450 /* Error messages are in inverse video. */ 451 if (mtype == M_ERR) 452 (void)gp->scr_attr(sp, SA_INVERSE, 1); 453 454 /* Display the line, doing character translation. */ 455 #define FLUSH { \ 456 *cbp = '\0'; \ 457 (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ 458 cbp = cbuf; \ 459 } 460 ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; 461 for (t = line, tlen = len; tlen--; ++t) { 462 /* 463 * Replace tabs with spaces, there are places in 464 * ex that do column calculations without looking 465 * at <tabs> -- and all routines that care about 466 * <tabs> do their own expansions. This catches 467 * <tabs> in things like tag search strings. 468 */ 469 if (cbp + 1 >= ecbp) 470 FLUSH; 471 *cbp++ = *t == '\t' ? ' ' : *t; 472 } 473 if (cbp > cbuf) 474 FLUSH; 475 if (mtype == M_ERR) 476 (void)gp->scr_attr(sp, SA_INVERSE, 0); 477 478 /* Clear the rest of the line. */ 479 (void)gp->scr_clrtoeol(sp); 480 481 /* If we loop, it's a new line. */ 482 vip->lcontinue = 0; 483 484 /* Reset for the next line. */ 485 line += len; 486 llen -= len; 487 if (p != NULL) { 488 ++line; 489 --llen; 490 } 491 } 492 493 /* Set up next continuation line. */ 494 if (p == NULL) 495 gp->scr_cursor(sp, ¬used, &vip->lcontinue); 496 } 497 498 /* 499 * vs_ex_resolve -- 500 * Deal with ex message output. 501 * 502 * This routine is called when exiting a colon command to resolve any ex 503 * output that may have occurred. 504 * 505 * PUBLIC: int vs_ex_resolve(SCR *, int *); 506 */ 507 int 508 vs_ex_resolve(SCR *sp, int *continuep) 509 { 510 EVENT ev; 511 GS *gp; 512 VI_PRIVATE *vip; 513 sw_t wtype; 514 515 gp = sp->gp; 516 vip = VIP(sp); 517 *continuep = 0; 518 519 /* If we ran any ex command, we can't trust the cursor position. */ 520 F_SET(vip, VIP_CUR_INVALID); 521 522 /* Terminate any partially written message. */ 523 if (vip->lcontinue != 0) { 524 vs_output(sp, vip->mtype, ".", 1); 525 vip->lcontinue = 0; 526 527 vip->mtype = M_NONE; 528 } 529 530 /* 531 * If we switched out of the vi screen into ex, switch back while we 532 * figure out what to do with the screen and potentially get another 533 * command to execute. 534 * 535 * If we didn't switch into ex, we're not required to wait, and less 536 * than 2 lines of output, we can continue without waiting for the 537 * wait. 538 * 539 * Note, all other code paths require waiting, so we leave the report 540 * of modified lines until later, so that we won't wait for no other 541 * reason than a threshold number of lines were modified. This means 542 * we display cumulative line modification reports for groups of ex 543 * commands. That seems right to me (well, at least not wrong). 544 */ 545 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 546 if (sp->gp->scr_screen(sp, SC_VI)) 547 return (1); 548 } else 549 if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) { 550 F_CLR(sp, SC_EX_WAIT_NO); 551 return (0); 552 } 553 554 /* Clear the required wait flag, it's no longer needed. */ 555 F_CLR(sp, SC_EX_WAIT_YES); 556 557 /* 558 * Wait, unless explicitly told not to wait or the user interrupted 559 * the command. If the user is leaving the screen, for any reason, 560 * they can't continue with further ex commands. 561 */ 562 if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) { 563 wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | 564 SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX; 565 if (F_ISSET(sp, SC_SCR_EXWROTE)) 566 vs_wait(sp, continuep, wtype); 567 else 568 vs_scroll(sp, continuep, wtype); 569 if (*continuep) 570 return (0); 571 } 572 573 /* If ex wrote on the screen, refresh the screen image. */ 574 if (F_ISSET(sp, SC_SCR_EXWROTE)) 575 F_SET(vip, VIP_N_EX_PAINT); 576 577 /* 578 * If we're not the bottom of the split screen stack, the screen 579 * image itself is wrong, so redraw everything. 580 */ 581 if (TAILQ_NEXT(sp, q) != NULL) 582 F_SET(sp, SC_SCR_REDRAW); 583 584 /* If ex changed the underlying file, the map itself is wrong. */ 585 if (F_ISSET(vip, VIP_N_EX_REDRAW)) 586 F_SET(sp, SC_SCR_REFORMAT); 587 588 /* Ex may have switched out of the alternate screen, return. */ 589 (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 590 591 /* 592 * Whew. We're finally back home, after what feels like years. 593 * Kiss the ground. 594 */ 595 F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO); 596 597 /* 598 * We may need to repaint some of the screen, e.g.: 599 * 600 * :set 601 * :!ls 602 * 603 * gives us a combination of some lines that are "wrong", and a need 604 * for a full refresh. 605 */ 606 if (vip->totalcount > 1) { 607 /* Set up the redraw of the overwritten lines. */ 608 ev.e_event = E_REPAINT; 609 ev.e_flno = vip->totalcount >= 610 sp->rows ? 1 : sp->rows - vip->totalcount; 611 ev.e_tlno = sp->rows; 612 613 /* Reset the count of overwriting lines. */ 614 vip->linecount = vip->lcontinue = vip->totalcount = 0; 615 616 /* Redraw. */ 617 (void)vs_repaint(sp, &ev); 618 } else 619 /* Reset the count of overwriting lines. */ 620 vip->linecount = vip->lcontinue = vip->totalcount = 0; 621 622 return (0); 623 } 624 625 /* 626 * vs_resolve -- 627 * Deal with message output. 628 * 629 * PUBLIC: int vs_resolve(SCR *, SCR *, int); 630 */ 631 int 632 vs_resolve(SCR *sp, SCR *csp, int forcewait) 633 { 634 EVENT ev; 635 GS *gp; 636 MSGS *mp; 637 VI_PRIVATE *vip; 638 size_t oldy, oldx; 639 int redraw; 640 641 /* 642 * Vs_resolve is called from the main vi loop and the refresh function 643 * to periodically ensure that the user has seen any messages that have 644 * been displayed and that any status lines are correct. The sp screen 645 * is the screen we're checking, usually the current screen. When it's 646 * not, csp is the current screen, used for final cursor positioning. 647 */ 648 gp = sp->gp; 649 vip = VIP(sp); 650 if (csp == NULL) 651 csp = sp; 652 653 /* Save the cursor position. */ 654 (void)gp->scr_cursor(csp, &oldy, &oldx); 655 656 /* Ring the bell if it's scheduled. */ 657 if (F_ISSET(gp, G_BELLSCHED)) { 658 F_CLR(gp, G_BELLSCHED); 659 (void)gp->scr_bell(sp); 660 } 661 662 /* Display new file status line. */ 663 if (F_ISSET(sp, SC_STATUS)) { 664 F_CLR(sp, SC_STATUS); 665 msgq_status(sp, sp->lno, MSTAT_TRUNCATE); 666 } 667 668 /* Report on line modifications. */ 669 mod_rpt(sp); 670 671 /* 672 * Flush any saved messages. If the screen isn't ready, refresh 673 * it. (A side-effect of screen refresh is that we can display 674 * messages.) Once this is done, don't trust the cursor. That 675 * extra refresh screwed the pooch. 676 */ 677 if (!SLIST_EMPTY(gp->msgq)) { 678 if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1)) 679 return (1); 680 while ((mp = SLIST_FIRST(gp->msgq)) != NULL) { 681 gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); 682 SLIST_REMOVE_HEAD(gp->msgq, q); 683 free(mp->buf); 684 free(mp); 685 } 686 F_SET(vip, VIP_CUR_INVALID); 687 } 688 689 switch (vip->totalcount) { 690 case 0: 691 redraw = 0; 692 break; 693 case 1: 694 /* 695 * If we're switching screens, we have to wait for messages, 696 * regardless. If we don't wait, skip updating the modeline. 697 */ 698 if (forcewait) 699 vs_scroll(sp, NULL, SCROLL_W); 700 else 701 F_SET(vip, VIP_S_MODELINE); 702 703 redraw = 0; 704 break; 705 default: 706 /* 707 * If >1 message line in use, prompt the user to continue and 708 * repaint overwritten lines. 709 */ 710 vs_scroll(sp, NULL, SCROLL_W); 711 712 ev.e_event = E_REPAINT; 713 ev.e_flno = vip->totalcount >= 714 sp->rows ? 1 : sp->rows - vip->totalcount; 715 ev.e_tlno = sp->rows; 716 717 redraw = 1; 718 break; 719 } 720 721 /* Reset the count of overwriting lines. */ 722 vip->linecount = vip->lcontinue = vip->totalcount = 0; 723 724 /* Redraw. */ 725 if (redraw) 726 (void)vs_repaint(sp, &ev); 727 728 /* Restore the cursor position. */ 729 (void)gp->scr_move(csp, oldy, oldx); 730 731 return (0); 732 } 733 734 /* 735 * vs_scroll -- 736 * Scroll the screen for output. 737 */ 738 static void 739 vs_scroll(SCR *sp, int *continuep, sw_t wtype) 740 { 741 GS *gp; 742 VI_PRIVATE *vip; 743 744 gp = sp->gp; 745 vip = VIP(sp); 746 if (!IS_ONELINE(sp)) { 747 /* 748 * Scroll the screen. Instead of scrolling the entire screen, 749 * delete the line above the first line output so preserve the 750 * maximum amount of the screen. 751 */ 752 (void)gp->scr_move(sp, vip->totalcount < 753 sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0); 754 (void)gp->scr_deleteln(sp); 755 756 /* If there are screens below us, push them back into place. */ 757 if (TAILQ_NEXT(sp, q) != NULL) { 758 (void)gp->scr_move(sp, LASTLINE(sp), 0); 759 (void)gp->scr_insertln(sp); 760 } 761 } 762 if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows) 763 return; 764 vs_wait(sp, continuep, wtype); 765 } 766 767 /* 768 * vs_wait -- 769 * Prompt the user to continue. 770 */ 771 static void 772 vs_wait(SCR *sp, int *continuep, sw_t wtype) 773 { 774 EVENT ev; 775 VI_PRIVATE *vip; 776 const char *p; 777 GS *gp; 778 size_t len; 779 780 gp = sp->gp; 781 vip = VIP(sp); 782 783 (void)gp->scr_move(sp, LASTLINE(sp), 0); 784 if (IS_ONELINE(sp)) 785 p = msg_cmsg(sp, CMSG_CONT_S, &len); 786 else 787 switch (wtype) { 788 case SCROLL_W_QUIT: 789 p = msg_cmsg(sp, CMSG_CONT_Q, &len); 790 break; 791 case SCROLL_W_EX: 792 p = msg_cmsg(sp, CMSG_CONT_EX, &len); 793 break; 794 case SCROLL_W: 795 p = msg_cmsg(sp, CMSG_CONT, &len); 796 break; 797 default: 798 abort(); 799 /* NOTREACHED */ 800 } 801 (void)gp->scr_addstr(sp, p, len); 802 803 ++vip->totalcount; 804 vip->linecount = 0; 805 806 (void)gp->scr_clrtoeol(sp); 807 (void)gp->scr_refresh(sp, 0); 808 809 /* Get a single character from the terminal. */ 810 if (continuep != NULL) 811 *continuep = 0; 812 for (;;) { 813 if (v_event_get(sp, &ev, 0, 0)) 814 return; 815 if (ev.e_event == E_CHARACTER) 816 break; 817 if (ev.e_event == E_INTERRUPT) { 818 ev.e_c = CH_QUIT; 819 F_SET(gp, G_INTERRUPTED); 820 break; 821 } 822 (void)gp->scr_bell(sp); 823 } 824 switch (wtype) { 825 case SCROLL_W_QUIT: 826 if (ev.e_c == CH_QUIT) 827 F_SET(gp, G_INTERRUPTED); 828 break; 829 case SCROLL_W_EX: 830 if (ev.e_c == ':' && continuep != NULL) 831 *continuep = 1; 832 break; 833 case SCROLL_W: 834 break; 835 } 836 } 837 838 /* 839 * vs_divider -- 840 * Draw a dividing line between the screen and the output. 841 */ 842 static void 843 vs_divider(SCR *sp) 844 { 845 GS *gp; 846 size_t len; 847 848 #define DIVIDESTR "+=+=+=+=+=+=+=+" 849 len = 850 sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1; 851 gp = sp->gp; 852 (void)gp->scr_attr(sp, SA_INVERSE, 1); 853 (void)gp->scr_addstr(sp, DIVIDESTR, len); 854 (void)gp->scr_attr(sp, SA_INVERSE, 0); 855 } 856 857 /* 858 * vs_msgsave -- 859 * Save a message for later display. 860 */ 861 static void 862 vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len) 863 { 864 GS *gp; 865 MSGS *mp_c, *mp_n; 866 867 /* 868 * We have to handle messages before we have any place to put them. 869 * If there's no screen support yet, allocate a msg structure, copy 870 * in the message, and queue it on the global structure. If we can't 871 * allocate memory here, we're genuinely screwed, dump the message 872 * to stderr in the (probably) vain hope that someone will see it. 873 */ 874 CALLOC_GOTO(sp, mp_n, 1, sizeof(MSGS)); 875 MALLOC_GOTO(sp, mp_n->buf, len); 876 877 memmove(mp_n->buf, p, len); 878 mp_n->len = len; 879 mp_n->mtype = mt; 880 881 gp = sp->gp; 882 if (SLIST_EMPTY(gp->msgq)) { 883 SLIST_INSERT_HEAD(gp->msgq, mp_n, q); 884 } else { 885 SLIST_FOREACH(mp_c, gp->msgq, q) 886 if (SLIST_NEXT(mp_c, q) == NULL) 887 break; 888 SLIST_INSERT_AFTER(mp_c, mp_n, q); 889 } 890 return; 891 892 alloc_err: 893 free(mp_n); 894 (void)fprintf(stderr, "%.*s\n", (int)len, p); 895 } 896