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 /* 252 * If vi is using the error line for text input, there's no screen 253 * real-estate for the error message. Nothing to do without some 254 * information as to how important the error message is. 255 */ 256 if (F_ISSET(sp, SC_TINPUT_INFO)) 257 return; 258 259 /* 260 * Ex or ex controlled screen output. 261 * 262 * If output happens during startup, e.g., a .exrc file, we may be 263 * in ex mode but haven't initialized the screen. Initialize here, 264 * and in this case, stay in ex mode. 265 * 266 * If the SC_SCR_EXWROTE bit is set, then we're switching back and 267 * forth between ex and vi, but the screen is trashed and we have 268 * to respect that. Switch to ex mode long enough to put out the 269 * message. 270 * 271 * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to 272 * the screen, so previous opinions are ignored. 273 */ 274 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { 275 if (!F_ISSET(sp, SC_SCR_EX)) { 276 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 277 if (sp->gp->scr_screen(sp, SC_EX)) 278 return; 279 } else 280 if (ex_init(sp)) 281 return; 282 } 283 284 if (mtype == M_ERR) 285 (void)gp->scr_attr(sp, SA_INVERSE, 1); 286 (void)printf("%.*s", (int)len, line); 287 if (mtype == M_ERR) 288 (void)gp->scr_attr(sp, SA_INVERSE, 0); 289 (void)fflush(stdout); 290 291 F_CLR(sp, SC_EX_WAIT_NO); 292 293 if (!F_ISSET(sp, SC_SCR_EX)) 294 (void)sp->gp->scr_screen(sp, SC_VI); 295 return; 296 } 297 298 /* If the vi screen isn't ready, save the message. */ 299 if (!F_ISSET(sp, SC_SCR_VI)) { 300 (void)vs_msgsave(sp, mtype, line, len); 301 return; 302 } 303 304 /* Save the cursor position. */ 305 (void)gp->scr_cursor(sp, &oldy, &oldx); 306 307 /* If it's an ex output message, just write it out. */ 308 if (mtype == M_NONE) { 309 vs_output(sp, mtype, line, len); 310 goto ret; 311 } 312 313 /* 314 * If it's a vi message, strip the trailing <newline> so we can 315 * try and paste messages together. 316 */ 317 if (line[len - 1] == '\n') 318 --len; 319 320 /* 321 * If a message won't fit on a single line, try to split on a <blank>. 322 * If a subsequent message fits on the same line, write a separator 323 * and output it. Otherwise, put out a newline. 324 * 325 * Need up to two padding characters normally; a semi-colon and a 326 * separating space. If only a single line on the screen, add some 327 * more for the trailing continuation message. 328 * 329 * XXX 330 * Assume that periods and semi-colons take up a single column on the 331 * screen. 332 * 333 * XXX 334 * There are almost certainly pathological cases that will break this 335 * code. 336 */ 337 if (IS_ONELINE(sp)) 338 (void)msg_cmsg(sp, CMSG_CONT_S, &padding); 339 else 340 padding = 0; 341 padding += 2; 342 343 maxcols = sp->cols - 1; 344 if (vip->lcontinue != 0) { 345 if (len + vip->lcontinue + padding > maxcols) 346 vs_output(sp, vip->mtype, ".\n", 2); 347 else { 348 vs_output(sp, vip->mtype, ";", 1); 349 vs_output(sp, M_NONE, " ", 1); 350 } 351 } 352 vip->mtype = mtype; 353 for (s = line;; s = t) { 354 for (; len > 0 && isblank((u_char)*s); --len, ++s); 355 if (len == 0) 356 break; 357 if (len + vip->lcontinue > maxcols) { 358 for (e = s + (maxcols - vip->lcontinue); 359 e > s && !isblank((u_char)*e); --e); 360 if (e == s) 361 e = t = s + (maxcols - vip->lcontinue); 362 else 363 for (t = e; isblank((u_char)e[-1]); --e); 364 } else 365 e = t = s + len; 366 367 /* 368 * If the message ends in a period, discard it, we want to 369 * gang messages where possible. 370 */ 371 len -= t - s; 372 if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.') 373 --e; 374 vs_output(sp, mtype, s, e - s); 375 376 if (len != 0) 377 vs_output(sp, M_NONE, "\n", 1); 378 379 if (INTERRUPTED(sp)) 380 break; 381 } 382 383 ret: (void)gp->scr_move(sp, oldy, oldx); 384 (void)gp->scr_refresh(sp, 0); 385 } 386 387 /* 388 * vs_output -- 389 * Output the text to the screen. 390 */ 391 static void 392 vs_output(SCR *sp, mtype_t mtype, const char *line, int llen) 393 { 394 GS *gp; 395 VI_PRIVATE *vip; 396 size_t notused; 397 int len, rlen, tlen; 398 const char *p, *t; 399 char *cbp, *ecbp, cbuf[128]; 400 401 gp = sp->gp; 402 vip = VIP(sp); 403 for (p = line, rlen = llen; llen > 0;) { 404 /* Get the next physical line. */ 405 if ((p = memchr(line, '\n', llen)) == NULL) 406 len = llen; 407 else 408 len = p - line; 409 410 /* 411 * The max is sp->cols characters, and we may have already 412 * written part of the line. 413 */ 414 if (len + vip->lcontinue > sp->cols) 415 len = sp->cols - vip->lcontinue; 416 417 /* 418 * If the first line output, do nothing. If the second line 419 * output, draw the divider line. If drew a full screen, we 420 * remove the divider line. If it's a continuation line, move 421 * to the continuation point, else, move the screen up. 422 */ 423 if (vip->lcontinue == 0) { 424 if (!IS_ONELINE(sp)) { 425 if (vip->totalcount == 1) { 426 (void)gp->scr_move(sp, 427 LASTLINE(sp) - 1, 0); 428 (void)gp->scr_clrtoeol(sp); 429 (void)vs_divider(sp); 430 F_SET(vip, VIP_DIVIDER); 431 ++vip->totalcount; 432 ++vip->linecount; 433 } 434 if (vip->totalcount == sp->t_maxrows && 435 F_ISSET(vip, VIP_DIVIDER)) { 436 --vip->totalcount; 437 --vip->linecount; 438 F_CLR(vip, VIP_DIVIDER); 439 } 440 } 441 if (vip->totalcount != 0) 442 vs_scroll(sp, NULL, SCROLL_W_QUIT); 443 444 (void)gp->scr_move(sp, LASTLINE(sp), 0); 445 ++vip->totalcount; 446 ++vip->linecount; 447 448 if (INTERRUPTED(sp)) 449 break; 450 } else 451 (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); 452 453 /* Error messages are in inverse video. */ 454 if (mtype == M_ERR) 455 (void)gp->scr_attr(sp, SA_INVERSE, 1); 456 457 /* Display the line, doing character translation. */ 458 #define FLUSH do { \ 459 *cbp = '\0'; \ 460 (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ 461 cbp = cbuf; \ 462 } while (0) 463 ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; 464 for (t = line, tlen = len; tlen--; ++t) { 465 /* 466 * Replace tabs with spaces, there are places in 467 * ex that do column calculations without looking 468 * at <tabs> -- and all routines that care about 469 * <tabs> do their own expansions. This catches 470 * <tabs> in things like tag search strings. 471 */ 472 if (cbp + 1 >= ecbp) 473 FLUSH; 474 *cbp++ = *t == '\t' ? ' ' : *t; 475 } 476 if (cbp > cbuf) 477 FLUSH; 478 if (mtype == M_ERR) 479 (void)gp->scr_attr(sp, SA_INVERSE, 0); 480 481 /* Clear the rest of the line. */ 482 (void)gp->scr_clrtoeol(sp); 483 484 /* If we loop, it's a new line. */ 485 vip->lcontinue = 0; 486 487 /* Reset for the next line. */ 488 line += len; 489 llen -= len; 490 if (p != NULL) { 491 ++line; 492 --llen; 493 } 494 } 495 496 /* Set up next continuation line. */ 497 if (p == NULL) 498 gp->scr_cursor(sp, ¬used, &vip->lcontinue); 499 } 500 501 /* 502 * vs_ex_resolve -- 503 * Deal with ex message output. 504 * 505 * This routine is called when exiting a colon command to resolve any ex 506 * output that may have occurred. 507 * 508 * PUBLIC: int vs_ex_resolve(SCR *, int *); 509 */ 510 int 511 vs_ex_resolve(SCR *sp, int *continuep) 512 { 513 EVENT ev; 514 GS *gp; 515 VI_PRIVATE *vip; 516 sw_t wtype; 517 518 gp = sp->gp; 519 vip = VIP(sp); 520 *continuep = 0; 521 522 /* If we ran any ex command, we can't trust the cursor position. */ 523 F_SET(vip, VIP_CUR_INVALID); 524 525 /* Terminate any partially written message. */ 526 if (vip->lcontinue != 0) { 527 vs_output(sp, vip->mtype, ".", 1); 528 vip->lcontinue = 0; 529 530 vip->mtype = M_NONE; 531 } 532 533 /* 534 * If we switched out of the vi screen into ex, switch back while we 535 * figure out what to do with the screen and potentially get another 536 * command to execute. 537 * 538 * If we didn't switch into ex, we're not required to wait, and less 539 * than 2 lines of output, we can continue without waiting for the 540 * wait. 541 * 542 * Note, all other code paths require waiting, so we leave the report 543 * of modified lines until later, so that we won't wait for no other 544 * reason than a threshold number of lines were modified. This means 545 * we display cumulative line modification reports for groups of ex 546 * commands. That seems right to me (well, at least not wrong). 547 */ 548 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 549 if (sp->gp->scr_screen(sp, SC_VI)) 550 return (1); 551 } else 552 if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) { 553 F_CLR(sp, SC_EX_WAIT_NO); 554 return (0); 555 } 556 557 /* Clear the required wait flag, it's no longer needed. */ 558 F_CLR(sp, SC_EX_WAIT_YES); 559 560 /* 561 * Wait, unless explicitly told not to wait or the user interrupted 562 * the command. If the user is leaving the screen, for any reason, 563 * they can't continue with further ex commands. 564 */ 565 if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) { 566 wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | 567 SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX; 568 if (F_ISSET(sp, SC_SCR_EXWROTE)) 569 vs_wait(sp, continuep, wtype); 570 else 571 vs_scroll(sp, continuep, wtype); 572 if (*continuep) 573 return (0); 574 } 575 576 /* If ex wrote on the screen, refresh the screen image. */ 577 if (F_ISSET(sp, SC_SCR_EXWROTE)) 578 F_SET(vip, VIP_N_EX_PAINT); 579 580 /* 581 * If we're not the bottom of the split screen stack, the screen 582 * image itself is wrong, so redraw everything. 583 */ 584 if (TAILQ_NEXT(sp, q) != NULL) 585 F_SET(sp, SC_SCR_REDRAW); 586 587 /* If ex changed the underlying file, the map itself is wrong. */ 588 if (F_ISSET(vip, VIP_N_EX_REDRAW)) 589 F_SET(sp, SC_SCR_REFORMAT); 590 591 /* Ex may have switched out of the alternate screen, return. */ 592 (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 593 594 /* 595 * Whew. We're finally back home, after what feels like years. 596 * Kiss the ground. 597 */ 598 F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO); 599 600 /* 601 * We may need to repaint some of the screen, e.g.: 602 * 603 * :set 604 * :!ls 605 * 606 * gives us a combination of some lines that are "wrong", and a need 607 * for a full refresh. 608 */ 609 if (vip->totalcount > 1) { 610 /* Set up the redraw of the overwritten lines. */ 611 ev.e_event = E_REPAINT; 612 ev.e_flno = vip->totalcount >= 613 sp->rows ? 1 : sp->rows - vip->totalcount; 614 ev.e_tlno = sp->rows; 615 616 /* Reset the count of overwriting lines. */ 617 vip->linecount = vip->lcontinue = vip->totalcount = 0; 618 619 /* Redraw. */ 620 (void)vs_repaint(sp, &ev); 621 } else 622 /* Reset the count of overwriting lines. */ 623 vip->linecount = vip->lcontinue = vip->totalcount = 0; 624 625 return (0); 626 } 627 628 /* 629 * vs_resolve -- 630 * Deal with message output. 631 * 632 * PUBLIC: int vs_resolve(SCR *, SCR *, int); 633 */ 634 int 635 vs_resolve(SCR *sp, SCR *csp, int forcewait) 636 { 637 EVENT ev; 638 GS *gp; 639 MSGS *mp; 640 VI_PRIVATE *vip; 641 size_t oldy, oldx; 642 int redraw; 643 644 /* 645 * Vs_resolve is called from the main vi loop and the refresh function 646 * to periodically ensure that the user has seen any messages that have 647 * been displayed and that any status lines are correct. The sp screen 648 * is the screen we're checking, usually the current screen. When it's 649 * not, csp is the current screen, used for final cursor positioning. 650 */ 651 gp = sp->gp; 652 vip = VIP(sp); 653 if (csp == NULL) 654 csp = sp; 655 656 /* Save the cursor position. */ 657 (void)gp->scr_cursor(csp, &oldy, &oldx); 658 659 /* Ring the bell if it's scheduled. */ 660 if (F_ISSET(gp, G_BELLSCHED)) { 661 F_CLR(gp, G_BELLSCHED); 662 (void)gp->scr_bell(sp); 663 } 664 665 /* Display new file status line. */ 666 if (F_ISSET(sp, SC_STATUS)) { 667 F_CLR(sp, SC_STATUS); 668 msgq_status(sp, sp->lno, MSTAT_TRUNCATE); 669 } 670 671 /* Report on line modifications. */ 672 mod_rpt(sp); 673 674 /* 675 * Flush any saved messages. If the screen isn't ready, refresh 676 * it. (A side-effect of screen refresh is that we can display 677 * messages.) Once this is done, don't trust the cursor. That 678 * extra refresh screwed the pooch. 679 */ 680 if (!SLIST_EMPTY(gp->msgq)) { 681 if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1)) 682 return (1); 683 while ((mp = SLIST_FIRST(gp->msgq)) != NULL) { 684 gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); 685 SLIST_REMOVE_HEAD(gp->msgq, q); 686 free(mp->buf); 687 free(mp); 688 } 689 F_SET(vip, VIP_CUR_INVALID); 690 } 691 692 switch (vip->totalcount) { 693 case 0: 694 redraw = 0; 695 break; 696 case 1: 697 /* 698 * If we're switching screens, we have to wait for messages, 699 * regardless. If we don't wait, skip updating the modeline. 700 */ 701 if (forcewait) 702 vs_scroll(sp, NULL, SCROLL_W); 703 else 704 F_SET(vip, VIP_S_MODELINE); 705 706 redraw = 0; 707 break; 708 default: 709 /* 710 * If >1 message line in use, prompt the user to continue and 711 * repaint overwritten lines. 712 */ 713 vs_scroll(sp, NULL, SCROLL_W); 714 715 ev.e_event = E_REPAINT; 716 ev.e_flno = vip->totalcount >= 717 sp->rows ? 1 : sp->rows - vip->totalcount; 718 ev.e_tlno = sp->rows; 719 720 redraw = 1; 721 break; 722 } 723 724 /* Reset the count of overwriting lines. */ 725 vip->linecount = vip->lcontinue = vip->totalcount = 0; 726 727 /* Redraw. */ 728 if (redraw) 729 (void)vs_repaint(sp, &ev); 730 731 /* Restore the cursor position. */ 732 (void)gp->scr_move(csp, oldy, oldx); 733 734 return (0); 735 } 736 737 /* 738 * vs_scroll -- 739 * Scroll the screen for output. 740 */ 741 static void 742 vs_scroll(SCR *sp, int *continuep, sw_t wtype) 743 { 744 GS *gp; 745 VI_PRIVATE *vip; 746 747 gp = sp->gp; 748 vip = VIP(sp); 749 if (!IS_ONELINE(sp)) { 750 /* 751 * Scroll the screen. Instead of scrolling the entire screen, 752 * delete the line above the first line output so preserve the 753 * maximum amount of the screen. 754 */ 755 (void)gp->scr_move(sp, vip->totalcount < 756 sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0); 757 (void)gp->scr_deleteln(sp); 758 759 /* If there are screens below us, push them back into place. */ 760 if (TAILQ_NEXT(sp, q) != NULL) { 761 (void)gp->scr_move(sp, LASTLINE(sp), 0); 762 (void)gp->scr_insertln(sp); 763 } 764 } 765 if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows) 766 return; 767 vs_wait(sp, continuep, wtype); 768 } 769 770 /* 771 * vs_wait -- 772 * Prompt the user to continue. 773 */ 774 static void 775 vs_wait(SCR *sp, int *continuep, sw_t wtype) 776 { 777 EVENT ev; 778 VI_PRIVATE *vip; 779 const char *p; 780 GS *gp; 781 size_t len; 782 783 gp = sp->gp; 784 vip = VIP(sp); 785 786 (void)gp->scr_move(sp, LASTLINE(sp), 0); 787 if (IS_ONELINE(sp)) 788 p = msg_cmsg(sp, CMSG_CONT_S, &len); 789 else 790 switch (wtype) { 791 case SCROLL_W_QUIT: 792 p = msg_cmsg(sp, CMSG_CONT_Q, &len); 793 break; 794 case SCROLL_W_EX: 795 p = msg_cmsg(sp, CMSG_CONT_EX, &len); 796 break; 797 case SCROLL_W: 798 p = msg_cmsg(sp, CMSG_CONT, &len); 799 break; 800 default: 801 abort(); 802 /* NOTREACHED */ 803 } 804 (void)gp->scr_addstr(sp, p, len); 805 806 ++vip->totalcount; 807 vip->linecount = 0; 808 809 (void)gp->scr_clrtoeol(sp); 810 (void)gp->scr_refresh(sp, 0); 811 812 /* Get a single character from the terminal. */ 813 if (continuep != NULL) 814 *continuep = 0; 815 for (;;) { 816 if (v_event_get(sp, &ev, 0, 0)) 817 return; 818 if (ev.e_event == E_CHARACTER) 819 break; 820 if (ev.e_event == E_INTERRUPT) { 821 ev.e_c = CH_QUIT; 822 F_SET(gp, G_INTERRUPTED); 823 break; 824 } 825 (void)gp->scr_bell(sp); 826 } 827 switch (wtype) { 828 case SCROLL_W_QUIT: 829 if (ev.e_c == CH_QUIT) 830 F_SET(gp, G_INTERRUPTED); 831 break; 832 case SCROLL_W_EX: 833 if (ev.e_c == ':' && continuep != NULL) 834 *continuep = 1; 835 break; 836 case SCROLL_W: 837 break; 838 } 839 } 840 841 /* 842 * vs_divider -- 843 * Draw a dividing line between the screen and the output. 844 */ 845 static void 846 vs_divider(SCR *sp) 847 { 848 GS *gp; 849 size_t len; 850 851 #define DIVIDESTR "+=+=+=+=+=+=+=+" 852 len = 853 sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1; 854 gp = sp->gp; 855 (void)gp->scr_attr(sp, SA_INVERSE, 1); 856 (void)gp->scr_addstr(sp, DIVIDESTR, len); 857 (void)gp->scr_attr(sp, SA_INVERSE, 0); 858 } 859 860 /* 861 * vs_msgsave -- 862 * Save a message for later display. 863 */ 864 static void 865 vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len) 866 { 867 GS *gp; 868 MSGS *mp_c, *mp_n; 869 870 /* 871 * We have to handle messages before we have any place to put them. 872 * If there's no screen support yet, allocate a msg structure, copy 873 * in the message, and queue it on the global structure. If we can't 874 * allocate memory here, we're genuinely screwed, dump the message 875 * to stderr in the (probably) vain hope that someone will see it. 876 */ 877 CALLOC_GOTO(sp, mp_n, 1, sizeof(MSGS)); 878 MALLOC_GOTO(sp, mp_n->buf, len); 879 880 memmove(mp_n->buf, p, len); 881 mp_n->len = len; 882 mp_n->mtype = mt; 883 884 gp = sp->gp; 885 if (SLIST_EMPTY(gp->msgq)) { 886 SLIST_INSERT_HEAD(gp->msgq, mp_n, q); 887 } else { 888 SLIST_FOREACH(mp_c, gp->msgq, q) 889 if (SLIST_NEXT(mp_c, q) == NULL) 890 break; 891 SLIST_INSERT_AFTER(mp_c, mp_n, q); 892 } 893 return; 894 895 alloc_err: 896 free(mp_n); 897 (void)fprintf(stderr, "%.*s\n", (int)len, p); 898 } 899