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