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