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