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