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