131921Sbrian/*- 231921Sbrian * Copyright (c) 1993, 1994 331921Sbrian * The Regents of the University of California. All rights reserved. 431921Sbrian * Copyright (c) 1992, 1993, 1994, 1995, 1996 531921Sbrian * Keith Bostic. All rights reserved. 631921Sbrian * 731921Sbrian * See the LICENSE file for redistribution information. 831921Sbrian */ 931921Sbrian 1031921Sbrian#include "config.h" 1131921Sbrian 1231921Sbrian#ifndef lint 1331921Sbrianstatic const char sccsid[] = "$Id: vs_msg.c,v 10.88 2013/03/19 09:59:03 zy Exp $"; 1431921Sbrian#endif /* not lint */ 1531921Sbrian 1631921Sbrian#include <sys/types.h> 1731921Sbrian#include <sys/queue.h> 1831921Sbrian#include <sys/time.h> 1931921Sbrian 2031921Sbrian#include <bitstring.h> 2131921Sbrian#include <ctype.h> 2231921Sbrian#include <stdio.h> 2331921Sbrian#include <stdlib.h> 2431921Sbrian#include <string.h> 2531921Sbrian#include <unistd.h> 2650479Speter 2731272Sbrian#include "../common/common.h" 2831272Sbrian#include "vi.h" 2936285Sbrian 3031272Sbriantypedef enum { 3131272Sbrian SCROLL_W, /* User wait. */ 3249434Sbrian SCROLL_W_EX, /* User wait, or enter : to continue. */ 3336285Sbrian SCROLL_W_QUIT /* User wait, or enter q to quit. */ 3436285Sbrian /* 3531272Sbrian * SCROLL_W_QUIT has another semantic 3631272Sbrian * -- only wait if the screen is full 3731343Sbrian */ 3831272Sbrian} sw_t; 3931272Sbrian 4036285Sbrianstatic void vs_divider __P((SCR *)); 4136285Sbrianstatic void vs_msgsave __P((SCR *, mtype_t, char *, size_t)); 4231272Sbrianstatic void vs_output __P((SCR *, mtype_t, const char *, int)); 4364670Sbrianstatic void vs_scroll __P((SCR *, int *, sw_t)); 4431272Sbrianstatic void vs_wait __P((SCR *, int *, sw_t)); 4549434Sbrian 4631272Sbrian/* 4765178Sbrian * vs_busy -- 4849434Sbrian * Display, update or clear a busy message. 4964670Sbrian * 5064670Sbrian * This routine is the default editor interface for vi busy messages. It 5164670Sbrian * implements a standard strategy of stealing lines from the bottom of the 5264670Sbrian * vi text screen. Screens using an alternate method of displaying busy 5364670Sbrian * messages, e.g. X11 clock icons, should set their scr_busy function to the 5464670Sbrian * correct function before calling the main editor routine. 5564670Sbrian * 5649434Sbrian * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t)); 5749434Sbrian */ 5836285Sbrianvoid 5936285Sbrianvs_busy(SCR *sp, const char *msg, busy_t btype) 6036285Sbrian{ 6149434Sbrian GS *gp; 6236285Sbrian VI_PRIVATE *vip; 6349434Sbrian static const char flagc[] = "|/-\\"; 6449434Sbrian struct timespec ts, ts_diff; 6531272Sbrian const struct timespec ts_min = { 0, 125000000 }; 6631272Sbrian size_t len, notused; 6731272Sbrian const char *p; 6831272Sbrian 6949434Sbrian /* Ex doesn't display busy messages. */ 7049434Sbrian if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) 7164670Sbrian return; 7249434Sbrian 7364670Sbrian gp = sp->gp; 7464670Sbrian vip = VIP(sp); 7564670Sbrian 7664670Sbrian /* 7749434Sbrian * Most of this routine is to deal with the screen sharing real estate 7849434Sbrian * between the normal edit messages and the busy messages. Logically, 7949434Sbrian * all that's needed is something that puts up a message, periodically 8049434Sbrian * updates it, and then goes away. 8149434Sbrian */ 8249434Sbrian switch (btype) { 8349434Sbrian case BUSY_ON: 8449434Sbrian ++vip->busy_ref; 8549434Sbrian if (vip->totalcount != 0 || vip->busy_ref != 1) 8649447Sbrian break; 8749447Sbrian 8849447Sbrian /* Initialize state for updates. */ 8949447Sbrian vip->busy_ch = 0; 9049447Sbrian timepoint_steady(&vip->busy_ts); 9164670Sbrian 9249447Sbrian /* Save the current cursor. */ 9349447Sbrian (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx); 9449447Sbrian 9549434Sbrian /* Display the busy message. */ 9649434Sbrian p = msg_cat(sp, msg, &len); 9749434Sbrian (void)gp->scr_move(sp, LASTLINE(sp), 0); 9849434Sbrian (void)gp->scr_addstr(sp, p, len); 9936285Sbrian (void)gp->scr_cursor(sp, ¬used, &vip->busy_fx); 10031272Sbrian (void)gp->scr_clrtoeol(sp); 10149434Sbrian (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 10231272Sbrian break; 10349434Sbrian case BUSY_OFF: 10449434Sbrian if (vip->busy_ref == 0) 10549434Sbrian break; 10649434Sbrian --vip->busy_ref; 10749434Sbrian 10849434Sbrian /* 10949434Sbrian * If the line isn't in use for another purpose, clear it. 11049434Sbrian * Always return to the original position. 11149434Sbrian */ 11249582Sbrian if (vip->totalcount == 0 && vip->busy_ref == 0) { 11336285Sbrian (void)gp->scr_move(sp, LASTLINE(sp), 0); 11465178Sbrian (void)gp->scr_clrtoeol(sp); 11565178Sbrian } 11636285Sbrian (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx); 11746686Sbrian break; 11849434Sbrian case BUSY_UPDATE: 11964670Sbrian if (vip->totalcount != 0 || vip->busy_ref == 0) 12064670Sbrian break; 12164670Sbrian 12264670Sbrian /* Update no more than every 1/8 of a second. */ 12349434Sbrian timepoint_steady(&ts); 12446686Sbrian ts_diff = ts; 12536819Sbrian timespecsub(&ts_diff, &vip->busy_ts); 12631272Sbrian if (timespeccmp(&ts_diff, &ts_min, <)) 12749582Sbrian return; 12849434Sbrian vip->busy_ts = ts; 12931272Sbrian 13031272Sbrian /* Display the update. */ 13131272Sbrian if (vip->busy_ch == sizeof(flagc) - 1) 13231272Sbrian vip->busy_ch = 0; 13331272Sbrian (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 13431272Sbrian (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1); 13531272Sbrian (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 13631272Sbrian break; 13731272Sbrian } 13849434Sbrian (void)gp->scr_refresh(sp, 0); 13964670Sbrian} 14064670Sbrian 14164670Sbrian/* 14265178Sbrian * vs_home -- 14365178Sbrian * Home the cursor to the bottom row, left-most column. 14465178Sbrian * 14565178Sbrian * PUBLIC: void vs_home __P((SCR *)); 14631272Sbrian */ 14731272Sbrianvoid 14836285Sbrianvs_home(SCR *sp) 14949582Sbrian{ 15049434Sbrian (void)sp->gp->scr_move(sp, LASTLINE(sp), 0); 15149434Sbrian (void)sp->gp->scr_refresh(sp, 0); 15231272Sbrian} 15349582Sbrian 15449434Sbrian/* 15531272Sbrian * vs_update -- 15631272Sbrian * Update a command. 15731272Sbrian * 15831272Sbrian * PUBLIC: void vs_update __P((SCR *, const char *, const CHAR_T *)); 15931343Sbrian */ 16031272Sbrianvoid 16131343Sbrianvs_update(SCR *sp, const char *m1, const CHAR_T *m2) 16246686Sbrian{ 16349434Sbrian GS *gp; 16464670Sbrian size_t len, mlen, oldx, oldy; 16531272Sbrian CONST char *np; 16636285Sbrian size_t nlen; 16731272Sbrian 16849434Sbrian gp = sp->gp; 16949434Sbrian 17064670Sbrian /* 17164670Sbrian * This routine displays a message on the bottom line of the screen, 17264670Sbrian * without updating any of the command structures that would keep it 17364670Sbrian * there for any period of time, i.e. it is overwritten immediately. 17464670Sbrian * 17564670Sbrian * It's used by the ex read and ! commands when the user's command is 17664670Sbrian * expanded, and by the ex substitution confirmation prompt. 17764670Sbrian */ 17864670Sbrian if (F_ISSET(sp, SC_SCR_EXWROTE)) { 17964670Sbrian if (m2 != NULL) 18064670Sbrian INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen); 18164670Sbrian (void)ex_printf(sp, 18249434Sbrian "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np); 18336819Sbrian (void)ex_fflush(sp); 18464670Sbrian } 18549434Sbrian 18631272Sbrian /* 18731272Sbrian * Save the cursor position, the substitute-with-confirmation code 18849434Sbrian * will have already set it correctly. 18949434Sbrian */ 19049434Sbrian (void)gp->scr_cursor(sp, &oldy, &oldx); 19136285Sbrian 19231272Sbrian /* Clear the bottom line. */ 19331272Sbrian (void)gp->scr_move(sp, LASTLINE(sp), 0); 19431272Sbrian (void)gp->scr_clrtoeol(sp); 19536285Sbrian 19631272Sbrian /* 19749434Sbrian * XXX 19836285Sbrian * Don't let long file names screw up the screen. 19949434Sbrian */ 20049434Sbrian if (m1 != NULL) { 20164670Sbrian mlen = len = strlen(m1); 20249434Sbrian if (len > sp->cols - 2) 20349434Sbrian mlen = len = sp->cols - 2; 20464670Sbrian (void)gp->scr_addstr(sp, m1, mlen); 20549434Sbrian } else 20649434Sbrian len = 0; 20749434Sbrian if (m2 != NULL) { 20849434Sbrian mlen = STRLEN(m2); 20949434Sbrian if (len + mlen > sp->cols - 2) 21049434Sbrian mlen = (sp->cols - 2) - len; 21149434Sbrian (void)gp->scr_waddstr(sp, m2, mlen); 21249434Sbrian } 21349434Sbrian 21449434Sbrian (void)gp->scr_move(sp, oldy, oldx); 21536285Sbrian (void)gp->scr_refresh(sp, 0); 21636285Sbrian} 21731272Sbrian 21831272Sbrian/* 21936285Sbrian * vs_msg -- 22031272Sbrian * Display ex output or error messages for the screen. 22136285Sbrian * 22249434Sbrian * This routine is the default editor interface for all ex output, and all ex 22349434Sbrian * and vi error/informational messages. It implements the standard strategy 22449434Sbrian * of stealing lines from the bottom of the vi text screen. Screens using an 22549434Sbrian * alternate method of displaying messages, e.g. dialog boxes, should set their 22649434Sbrian * scr_msg function to the correct function before calling the editor. 22731272Sbrian * 22831272Sbrian * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t)); 22931272Sbrian */ 23031272Sbrianvoid 23131272Sbrianvs_msg(SCR *sp, mtype_t mtype, char *line, size_t len) 23231272Sbrian{ 23349434Sbrian GS *gp; 23449434Sbrian VI_PRIVATE *vip; 23536285Sbrian size_t maxcols, oldx, oldy, padding; 23631272Sbrian const char *e, *s, *t; 23731272Sbrian 23831272Sbrian gp = sp->gp; 23946686Sbrian vip = VIP(sp); 24031272Sbrian 24131272Sbrian /* 24265178Sbrian * Ring the bell if it's scheduled. 24331272Sbrian * 24431272Sbrian * XXX 24531272Sbrian * Shouldn't we save this, too? 24646686Sbrian */ 24731272Sbrian if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) 24831272Sbrian if (F_ISSET(sp, SC_SCR_VI)) { 24965178Sbrian F_CLR(gp, G_BELLSCHED); 25031272Sbrian (void)gp->scr_bell(sp); 25136934Sbrian } else 25236934Sbrian F_SET(gp, G_BELLSCHED); 25336934Sbrian 25436934Sbrian /* 25536934Sbrian * If vi is using the error line for text input, there's no screen 25636934Sbrian * real-estate for the error message. Nothing to do without some 25736934Sbrian * information as to how important the error message is. 25849434Sbrian */ 25964670Sbrian if (F_ISSET(sp, SC_TINPUT_INFO)) 26036934Sbrian return; 26136934Sbrian 26236934Sbrian /* 26336934Sbrian * Ex or ex controlled screen output. 26449434Sbrian * 26536934Sbrian * If output happens during startup, e.g., a .exrc file, we may be 26649434Sbrian * in ex mode but haven't initialized the screen. Initialize here, 26749434Sbrian * and in this case, stay in ex mode. 26846686Sbrian * 26949434Sbrian * If the SC_SCR_EXWROTE bit is set, then we're switching back and 27036934Sbrian * forth between ex and vi, but the screen is trashed and we have 27149434Sbrian * to respect that. Switch to ex mode long enough to put out the 27249434Sbrian * message. 27336934Sbrian * 27436934Sbrian * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to 27536934Sbrian * the screen, so previous opinions are ignored. 27664670Sbrian */ 27764670Sbrian if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { 27864670Sbrian if (!F_ISSET(sp, SC_SCR_EX)) 27964670Sbrian if (F_ISSET(sp, SC_SCR_EXWROTE)) { 28036934Sbrian if (sp->gp->scr_screen(sp, SC_EX)) 28136934Sbrian return; 28236934Sbrian } else 28336934Sbrian if (ex_init(sp)) 28436934Sbrian return; 28536934Sbrian 28636934Sbrian if (mtype == M_ERR) 28736934Sbrian (void)gp->scr_attr(sp, SA_INVERSE, 1); 28836934Sbrian (void)printf("%.*s", (int)len, line); 28946686Sbrian if (mtype == M_ERR) 29049434Sbrian (void)gp->scr_attr(sp, SA_INVERSE, 0); 29136934Sbrian (void)fflush(stdout); 29249434Sbrian 29336934Sbrian F_CLR(sp, SC_EX_WAIT_NO); 29436934Sbrian 29549434Sbrian if (!F_ISSET(sp, SC_SCR_EX)) 29649434Sbrian (void)sp->gp->scr_screen(sp, SC_VI); 29749434Sbrian return; 29849434Sbrian } 29949434Sbrian 30049434Sbrian /* If the vi screen isn't ready, save the message. */ 30149434Sbrian 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 vip->mtype = mtype; 354 for (s = line;; s = t) { 355 for (; len > 0 && isblank(*s); --len, ++s); 356 if (len == 0) 357 break; 358 if (len + vip->lcontinue > maxcols) { 359 for (e = s + (maxcols - vip->lcontinue); 360 e > s && !isblank(*e); --e); 361 if (e == s) 362 e = t = s + (maxcols - vip->lcontinue); 363 else 364 for (t = e; isblank(e[-1]); --e); 365 } else 366 e = t = s + len; 367 368 /* 369 * If the message ends in a period, discard it, we want to 370 * gang messages where possible. 371 */ 372 len -= t - s; 373 if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.') 374 --e; 375 vs_output(sp, mtype, s, e - s); 376 377 if (len != 0) 378 vs_output(sp, M_NONE, "\n", 1); 379 380 if (INTERRUPTED(sp)) 381 break; 382 } 383 384ret: (void)gp->scr_move(sp, oldy, oldx); 385 (void)gp->scr_refresh(sp, 0); 386} 387 388/* 389 * vs_output -- 390 * Output the text to the screen. 391 */ 392static void 393vs_output(SCR *sp, mtype_t mtype, const char *line, int llen) 394{ 395 GS *gp; 396 VI_PRIVATE *vip; 397 size_t notused; 398 int len, rlen, tlen; 399 const char *p, *t; 400 char *cbp, *ecbp, cbuf[128]; 401 402 gp = sp->gp; 403 vip = VIP(sp); 404 for (p = line, rlen = llen; llen > 0;) { 405 /* Get the next physical line. */ 406 if ((p = memchr(line, '\n', llen)) == NULL) 407 len = llen; 408 else 409 len = p - line; 410 411 /* 412 * The max is sp->cols characters, and we may have already 413 * written part of the line. 414 */ 415 if (len + vip->lcontinue > sp->cols) 416 len = sp->cols - vip->lcontinue; 417 418 /* 419 * If the first line output, do nothing. If the second line 420 * output, draw the divider line. If drew a full screen, we 421 * remove the divider line. If it's a continuation line, move 422 * to the continuation point, else, move the screen up. 423 */ 424 if (vip->lcontinue == 0) { 425 if (!IS_ONELINE(sp)) { 426 if (vip->totalcount == 1) { 427 (void)gp->scr_move(sp, 428 LASTLINE(sp) - 1, 0); 429 (void)gp->scr_clrtoeol(sp); 430 (void)vs_divider(sp); 431 F_SET(vip, VIP_DIVIDER); 432 ++vip->totalcount; 433 ++vip->linecount; 434 } 435 if (vip->totalcount == sp->t_maxrows && 436 F_ISSET(vip, VIP_DIVIDER)) { 437 --vip->totalcount; 438 --vip->linecount; 439 F_CLR(vip, VIP_DIVIDER); 440 } 441 } 442 if (vip->totalcount != 0) 443 vs_scroll(sp, NULL, SCROLL_W_QUIT); 444 445 (void)gp->scr_move(sp, LASTLINE(sp), 0); 446 ++vip->totalcount; 447 ++vip->linecount; 448 449 if (INTERRUPTED(sp)) 450 break; 451 } else 452 (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); 453 454 /* Error messages are in inverse video. */ 455 if (mtype == M_ERR) 456 (void)gp->scr_attr(sp, SA_INVERSE, 1); 457 458 /* Display the line, doing character translation. */ 459#define FLUSH { \ 460 *cbp = '\0'; \ 461 (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ 462 cbp = cbuf; \ 463} 464 ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; 465 for (t = line, tlen = len; tlen--; ++t) { 466 /* 467 * Replace tabs with spaces, there are places in 468 * ex that do column calculations without looking 469 * at <tabs> -- and all routines that care about 470 * <tabs> do their own expansions. This catches 471 * <tabs> in things like tag search strings. 472 */ 473 if (cbp + 1 >= ecbp) 474 FLUSH; 475 *cbp++ = *t == '\t' ? ' ' : *t; 476 } 477 if (cbp > cbuf) 478 FLUSH; 479 if (mtype == M_ERR) 480 (void)gp->scr_attr(sp, SA_INVERSE, 0); 481 482 /* Clear the rest of the line. */ 483 (void)gp->scr_clrtoeol(sp); 484 485 /* If we loop, it's a new line. */ 486 vip->lcontinue = 0; 487 488 /* Reset for the next line. */ 489 line += len; 490 llen -= len; 491 if (p != NULL) { 492 ++line; 493 --llen; 494 } 495 } 496 497 /* Set up next continuation line. */ 498 if (p == NULL) 499 gp->scr_cursor(sp, ¬used, &vip->lcontinue); 500} 501 502/* 503 * vs_ex_resolve -- 504 * Deal with ex message output. 505 * 506 * This routine is called when exiting a colon command to resolve any ex 507 * output that may have occurred. 508 * 509 * PUBLIC: int vs_ex_resolve __P((SCR *, int *)); 510 */ 511int 512vs_ex_resolve(SCR *sp, int *continuep) 513{ 514 EVENT ev; 515 GS *gp; 516 VI_PRIVATE *vip; 517 sw_t wtype; 518 519 gp = sp->gp; 520 vip = VIP(sp); 521 *continuep = 0; 522 523 /* If we ran any ex command, we can't trust the cursor position. */ 524 F_SET(vip, VIP_CUR_INVALID); 525 526 /* Terminate any partially written message. */ 527 if (vip->lcontinue != 0) { 528 vs_output(sp, vip->mtype, ".", 1); 529 vip->lcontinue = 0; 530 531 vip->mtype = M_NONE; 532 } 533 534 /* 535 * If we switched out of the vi screen into ex, switch back while we 536 * figure out what to do with the screen and potentially get another 537 * command to execute. 538 * 539 * If we didn't switch into ex, we're not required to wait, and less 540 * than 2 lines of output, we can continue without waiting for the 541 * wait. 542 * 543 * Note, all other code paths require waiting, so we leave the report 544 * of modified lines until later, so that we won't wait for no other 545 * reason than a threshold number of lines were modified. This means 546 * we display cumulative line modification reports for groups of ex 547 * commands. That seems right to me (well, at least not wrong). 548 */ 549 if (F_ISSET(sp, SC_SCR_EXWROTE)) { 550 if (sp->gp->scr_screen(sp, SC_VI)) 551 return (1); 552 } else 553 if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) { 554 F_CLR(sp, SC_EX_WAIT_NO); 555 return (0); 556 } 557 558 /* Clear the required wait flag, it's no longer needed. */ 559 F_CLR(sp, SC_EX_WAIT_YES); 560 561 /* 562 * Wait, unless explicitly told not to wait or the user interrupted 563 * the command. If the user is leaving the screen, for any reason, 564 * they can't continue with further ex commands. 565 */ 566 if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) { 567 wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | 568 SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX; 569 if (F_ISSET(sp, SC_SCR_EXWROTE)) 570 vs_wait(sp, continuep, wtype); 571 else 572 vs_scroll(sp, continuep, wtype); 573 if (*continuep) 574 return (0); 575 } 576 577 /* If ex wrote on the screen, refresh the screen image. */ 578 if (F_ISSET(sp, SC_SCR_EXWROTE)) 579 F_SET(vip, VIP_N_EX_PAINT); 580 581 /* 582 * If we're not the bottom of the split screen stack, the screen 583 * image itself is wrong, so redraw everything. 584 */ 585 if (TAILQ_NEXT(sp, q) != NULL) 586 F_SET(sp, SC_SCR_REDRAW); 587 588 /* If ex changed the underlying file, the map itself is wrong. */ 589 if (F_ISSET(vip, VIP_N_EX_REDRAW)) 590 F_SET(sp, SC_SCR_REFORMAT); 591 592 /* Ex may have switched out of the alternate screen, return. */ 593 (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 594 595 /* 596 * Whew. We're finally back home, after what feels like years. 597 * Kiss the ground. 598 */ 599 F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO); 600 601 /* 602 * We may need to repaint some of the screen, e.g.: 603 * 604 * :set 605 * :!ls 606 * 607 * gives us a combination of some lines that are "wrong", and a need 608 * for a full refresh. 609 */ 610 if (vip->totalcount > 1) { 611 /* Set up the redraw of the overwritten lines. */ 612 ev.e_event = E_REPAINT; 613 ev.e_flno = vip->totalcount >= 614 sp->rows ? 1 : sp->rows - vip->totalcount; 615 ev.e_tlno = sp->rows; 616 617 /* Reset the count of overwriting lines. */ 618 vip->linecount = vip->lcontinue = vip->totalcount = 0; 619 620 /* Redraw. */ 621 (void)vs_repaint(sp, &ev); 622 } else 623 /* Reset the count of overwriting lines. */ 624 vip->linecount = vip->lcontinue = vip->totalcount = 0; 625 626 return (0); 627} 628 629/* 630 * vs_resolve -- 631 * Deal with message output. 632 * 633 * PUBLIC: int vs_resolve __P((SCR *, SCR *, int)); 634 */ 635int 636vs_resolve(SCR *sp, SCR *csp, int forcewait) 637{ 638 EVENT ev; 639 GS *gp; 640 MSGS *mp; 641 VI_PRIVATE *vip; 642 size_t oldy, oldx; 643 int redraw; 644 645 /* 646 * Vs_resolve is called from the main vi loop and the refresh function 647 * to periodically ensure that the user has seen any messages that have 648 * been displayed and that any status lines are correct. The sp screen 649 * is the screen we're checking, usually the current screen. When it's 650 * not, csp is the current screen, used for final cursor positioning. 651 */ 652 gp = sp->gp; 653 vip = VIP(sp); 654 if (csp == NULL) 655 csp = sp; 656 657 /* Save the cursor position. */ 658 (void)gp->scr_cursor(csp, &oldy, &oldx); 659 660 /* Ring the bell if it's scheduled. */ 661 if (F_ISSET(gp, G_BELLSCHED)) { 662 F_CLR(gp, G_BELLSCHED); 663 (void)gp->scr_bell(sp); 664 } 665 666 /* Display new file status line. */ 667 if (F_ISSET(sp, SC_STATUS)) { 668 F_CLR(sp, SC_STATUS); 669 msgq_status(sp, sp->lno, MSTAT_TRUNCATE); 670 } 671 672 /* Report on line modifications. */ 673 mod_rpt(sp); 674 675 /* 676 * Flush any saved messages. If the screen isn't ready, refresh 677 * it. (A side-effect of screen refresh is that we can display 678 * messages.) Once this is done, don't trust the cursor. That 679 * extra refresh screwed the pooch. 680 */ 681 if (!SLIST_EMPTY(gp->msgq)) { 682 if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1)) 683 return (1); 684 while ((mp = SLIST_FIRST(gp->msgq)) != NULL) { 685 gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); 686 SLIST_REMOVE_HEAD(gp->msgq, q); 687 free(mp->buf); 688 free(mp); 689 } 690 F_SET(vip, VIP_CUR_INVALID); 691 } 692 693 switch (vip->totalcount) { 694 case 0: 695 redraw = 0; 696 break; 697 case 1: 698 /* 699 * If we're switching screens, we have to wait for messages, 700 * regardless. If we don't wait, skip updating the modeline. 701 */ 702 if (forcewait) 703 vs_scroll(sp, NULL, SCROLL_W); 704 else 705 F_SET(vip, VIP_S_MODELINE); 706 707 redraw = 0; 708 break; 709 default: 710 /* 711 * If >1 message line in use, prompt the user to continue and 712 * repaint overwritten lines. 713 */ 714 vs_scroll(sp, NULL, SCROLL_W); 715 716 ev.e_event = E_REPAINT; 717 ev.e_flno = vip->totalcount >= 718 sp->rows ? 1 : sp->rows - vip->totalcount; 719 ev.e_tlno = sp->rows; 720 721 redraw = 1; 722 break; 723 } 724 725 /* Reset the count of overwriting lines. */ 726 vip->linecount = vip->lcontinue = vip->totalcount = 0; 727 728 /* Redraw. */ 729 if (redraw) 730 (void)vs_repaint(sp, &ev); 731 732 /* Restore the cursor position. */ 733 (void)gp->scr_move(csp, oldy, oldx); 734 735 return (0); 736} 737 738/* 739 * vs_scroll -- 740 * Scroll the screen for output. 741 */ 742static void 743vs_scroll(SCR *sp, int *continuep, sw_t wtype) 744{ 745 GS *gp; 746 VI_PRIVATE *vip; 747 748 gp = sp->gp; 749 vip = VIP(sp); 750 if (!IS_ONELINE(sp)) { 751 /* 752 * Scroll the screen. Instead of scrolling the entire screen, 753 * delete the line above the first line output so preserve the 754 * maximum amount of the screen. 755 */ 756 (void)gp->scr_move(sp, vip->totalcount < 757 sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0); 758 (void)gp->scr_deleteln(sp); 759 760 /* If there are screens below us, push them back into place. */ 761 if (TAILQ_NEXT(sp, q) != NULL) { 762 (void)gp->scr_move(sp, LASTLINE(sp), 0); 763 (void)gp->scr_insertln(sp); 764 } 765 } 766 if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows) 767 return; 768 vs_wait(sp, continuep, wtype); 769} 770 771/* 772 * vs_wait -- 773 * Prompt the user to continue. 774 */ 775static void 776vs_wait(SCR *sp, int *continuep, sw_t wtype) 777{ 778 EVENT ev; 779 VI_PRIVATE *vip; 780 const char *p; 781 GS *gp; 782 size_t len; 783 784 gp = sp->gp; 785 vip = VIP(sp); 786 787 (void)gp->scr_move(sp, LASTLINE(sp), 0); 788 if (IS_ONELINE(sp)) 789 p = msg_cmsg(sp, CMSG_CONT_S, &len); 790 else 791 switch (wtype) { 792 case SCROLL_W_QUIT: 793 p = msg_cmsg(sp, CMSG_CONT_Q, &len); 794 break; 795 case SCROLL_W_EX: 796 p = msg_cmsg(sp, CMSG_CONT_EX, &len); 797 break; 798 case SCROLL_W: 799 p = msg_cmsg(sp, CMSG_CONT, &len); 800 break; 801 default: 802 abort(); 803 /* NOTREACHED */ 804 } 805 (void)gp->scr_addstr(sp, p, len); 806 807 ++vip->totalcount; 808 vip->linecount = 0; 809 810 (void)gp->scr_clrtoeol(sp); 811 (void)gp->scr_refresh(sp, 0); 812 813 /* Get a single character from the terminal. */ 814 if (continuep != NULL) 815 *continuep = 0; 816 for (;;) { 817 if (v_event_get(sp, &ev, 0, 0)) 818 return; 819 if (ev.e_event == E_CHARACTER) 820 break; 821 if (ev.e_event == E_INTERRUPT) { 822 ev.e_c = CH_QUIT; 823 F_SET(gp, G_INTERRUPTED); 824 break; 825 } 826 (void)gp->scr_bell(sp); 827 } 828 switch (wtype) { 829 case SCROLL_W_QUIT: 830 if (ev.e_c == CH_QUIT) 831 F_SET(gp, G_INTERRUPTED); 832 break; 833 case SCROLL_W_EX: 834 if (ev.e_c == ':' && continuep != NULL) 835 *continuep = 1; 836 break; 837 case SCROLL_W: 838 break; 839 } 840} 841 842/* 843 * vs_divider -- 844 * Draw a dividing line between the screen and the output. 845 */ 846static void 847vs_divider(SCR *sp) 848{ 849 GS *gp; 850 size_t len; 851 852#define DIVIDESTR "+=+=+=+=+=+=+=+" 853 len = 854 sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1; 855 gp = sp->gp; 856 (void)gp->scr_attr(sp, SA_INVERSE, 1); 857 (void)gp->scr_addstr(sp, DIVIDESTR, len); 858 (void)gp->scr_attr(sp, SA_INVERSE, 0); 859} 860 861/* 862 * vs_msgsave -- 863 * Save a message for later display. 864 */ 865static void 866vs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len) 867{ 868 GS *gp; 869 MSGS *mp_c, *mp_n; 870 871 /* 872 * We have to handle messages before we have any place to put them. 873 * If there's no screen support yet, allocate a msg structure, copy 874 * in the message, and queue it on the global structure. If we can't 875 * allocate memory here, we're genuinely screwed, dump the message 876 * to stderr in the (probably) vain hope that someone will see it. 877 */ 878 CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS)); 879 MALLOC_GOTO(sp, mp_n->buf, char *, len); 880 881 memmove(mp_n->buf, p, len); 882 mp_n->len = len; 883 mp_n->mtype = mt; 884 885 gp = sp->gp; 886 if (SLIST_EMPTY(gp->msgq)) { 887 SLIST_INSERT_HEAD(gp->msgq, mp_n, q); 888 } else { 889 SLIST_FOREACH(mp_c, gp->msgq, q) 890 if (SLIST_NEXT(mp_c, q) == NULL) 891 break; 892 SLIST_INSERT_AFTER(mp_c, mp_n, q); 893 } 894 return; 895 896alloc_err: 897 if (mp_n != NULL) 898 free(mp_n); 899 (void)fprintf(stderr, "%.*s\n", (int)len, p); 900} 901