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