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