119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: vs_msg.c,v 10.88 2013/03/19 09:59:03 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <stdio.h> 2319304Speter#include <stdlib.h> 2419304Speter#include <string.h> 2519304Speter#include <unistd.h> 2619304Speter 2719304Speter#include "../common/common.h" 2819304Speter#include "vi.h" 2919304Speter 3019304Spetertypedef enum { 3119304Speter SCROLL_W, /* User wait. */ 3219304Speter SCROLL_W_EX, /* User wait, or enter : to continue. */ 3319304Speter SCROLL_W_QUIT /* User wait, or enter q to quit. */ 3419304Speter /* 3519304Speter * SCROLL_W_QUIT has another semantic 3619304Speter * -- only wait if the screen is full 3719304Speter */ 3819304Speter} sw_t; 3919304Speter 4019304Speterstatic void vs_divider __P((SCR *)); 4119304Speterstatic void vs_msgsave __P((SCR *, mtype_t, char *, size_t)); 4219304Speterstatic void vs_output __P((SCR *, mtype_t, const char *, int)); 4319304Speterstatic void vs_scroll __P((SCR *, int *, sw_t)); 4419304Speterstatic void vs_wait __P((SCR *, int *, sw_t)); 4519304Speter 4619304Speter/* 4719304Speter * vs_busy -- 4819304Speter * Display, update or clear a busy message. 4919304Speter * 5019304Speter * This routine is the default editor interface for vi busy messages. It 5119304Speter * implements a standard strategy of stealing lines from the bottom of the 5219304Speter * vi text screen. Screens using an alternate method of displaying busy 5319304Speter * messages, e.g. X11 clock icons, should set their scr_busy function to the 5419304Speter * correct function before calling the main editor routine. 5519304Speter * 5619304Speter * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t)); 5719304Speter */ 5819304Spetervoid 59254225Spetervs_busy(SCR *sp, const char *msg, busy_t btype) 6019304Speter{ 6119304Speter GS *gp; 6219304Speter VI_PRIVATE *vip; 6319304Speter static const char flagc[] = "|/-\\"; 64254225Speter struct timespec ts, ts_diff; 65254225Speter const struct timespec ts_min = { 0, 125000000 }; 6619304Speter size_t len, notused; 6719304Speter const char *p; 6819304Speter 6919304Speter /* Ex doesn't display busy messages. */ 7019304Speter if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) 7119304Speter return; 7219304Speter 7319304Speter gp = sp->gp; 7419304Speter vip = VIP(sp); 7519304Speter 7619304Speter /* 7719304Speter * Most of this routine is to deal with the screen sharing real estate 7819304Speter * between the normal edit messages and the busy messages. Logically, 7919304Speter * all that's needed is something that puts up a message, periodically 8019304Speter * updates it, and then goes away. 8119304Speter */ 8219304Speter switch (btype) { 8319304Speter case BUSY_ON: 8419304Speter ++vip->busy_ref; 8519304Speter if (vip->totalcount != 0 || vip->busy_ref != 1) 8619304Speter break; 8719304Speter 8819304Speter /* Initialize state for updates. */ 8919304Speter vip->busy_ch = 0; 90254225Speter timepoint_steady(&vip->busy_ts); 9119304Speter 9219304Speter /* Save the current cursor. */ 9319304Speter (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx); 9419304Speter 9519304Speter /* Display the busy message. */ 9619304Speter p = msg_cat(sp, msg, &len); 9719304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 9819304Speter (void)gp->scr_addstr(sp, p, len); 9919304Speter (void)gp->scr_cursor(sp, ¬used, &vip->busy_fx); 10019304Speter (void)gp->scr_clrtoeol(sp); 10119304Speter (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 10219304Speter break; 10319304Speter case BUSY_OFF: 10419304Speter if (vip->busy_ref == 0) 10519304Speter break; 10619304Speter --vip->busy_ref; 10719304Speter 10819304Speter /* 10919304Speter * If the line isn't in use for another purpose, clear it. 11019304Speter * Always return to the original position. 11119304Speter */ 11219304Speter if (vip->totalcount == 0 && vip->busy_ref == 0) { 11319304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 11419304Speter (void)gp->scr_clrtoeol(sp); 11519304Speter } 11619304Speter (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx); 11719304Speter break; 11819304Speter case BUSY_UPDATE: 11919304Speter if (vip->totalcount != 0 || vip->busy_ref == 0) 12019304Speter break; 12119304Speter 12219304Speter /* Update no more than every 1/8 of a second. */ 123254225Speter timepoint_steady(&ts); 124254225Speter ts_diff = ts; 125254225Speter timespecsub(&ts_diff, &vip->busy_ts); 126254225Speter if (timespeccmp(&ts_diff, &ts_min, <)) 12719304Speter return; 128254225Speter vip->busy_ts = ts; 12919304Speter 13019304Speter /* Display the update. */ 13119304Speter if (vip->busy_ch == sizeof(flagc) - 1) 13219304Speter vip->busy_ch = 0; 13319304Speter (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 13419304Speter (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1); 13519304Speter (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx); 13619304Speter break; 13719304Speter } 13819304Speter (void)gp->scr_refresh(sp, 0); 13919304Speter} 14019304Speter 14119304Speter/* 14219304Speter * vs_home -- 14319304Speter * Home the cursor to the bottom row, left-most column. 14419304Speter * 14519304Speter * PUBLIC: void vs_home __P((SCR *)); 14619304Speter */ 14719304Spetervoid 148254225Spetervs_home(SCR *sp) 14919304Speter{ 15019304Speter (void)sp->gp->scr_move(sp, LASTLINE(sp), 0); 15119304Speter (void)sp->gp->scr_refresh(sp, 0); 15219304Speter} 15319304Speter 15419304Speter/* 15519304Speter * vs_update -- 15619304Speter * Update a command. 15719304Speter * 158254225Speter * PUBLIC: void vs_update __P((SCR *, const char *, const CHAR_T *)); 15919304Speter */ 16019304Spetervoid 161254225Spetervs_update(SCR *sp, const char *m1, const CHAR_T *m2) 16219304Speter{ 16319304Speter GS *gp; 16419304Speter size_t len, mlen, oldx, oldy; 165254225Speter CONST char *np; 166254225Speter size_t nlen; 16719304Speter 16819304Speter gp = sp->gp; 16919304Speter 17019304Speter /* 17119304Speter * This routine displays a message on the bottom line of the screen, 17219304Speter * without updating any of the command structures that would keep it 17319304Speter * there for any period of time, i.e. it is overwritten immediately. 17419304Speter * 17519304Speter * It's used by the ex read and ! commands when the user's command is 17619304Speter * expanded, and by the ex substitution confirmation prompt. 17719304Speter */ 17819304Speter if (F_ISSET(sp, SC_SCR_EXWROTE)) { 179254225Speter if (m2 != NULL) 180254225Speter INT2CHAR(sp, m2, STRLEN(m2) + 1, np, nlen); 18119304Speter (void)ex_printf(sp, 182254225Speter "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : np); 18319304Speter (void)ex_fflush(sp); 18419304Speter } 18519304Speter 18619304Speter /* 18719304Speter * Save the cursor position, the substitute-with-confirmation code 18819304Speter * will have already set it correctly. 18919304Speter */ 19019304Speter (void)gp->scr_cursor(sp, &oldy, &oldx); 19119304Speter 19219304Speter /* Clear the bottom line. */ 19319304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 19419304Speter (void)gp->scr_clrtoeol(sp); 19519304Speter 19619304Speter /* 19719304Speter * XXX 19819304Speter * Don't let long file names screw up the screen. 19919304Speter */ 20019304Speter if (m1 != NULL) { 20119304Speter mlen = len = strlen(m1); 20219304Speter if (len > sp->cols - 2) 20319304Speter mlen = len = sp->cols - 2; 20419304Speter (void)gp->scr_addstr(sp, m1, mlen); 20519304Speter } else 20619304Speter len = 0; 20719304Speter if (m2 != NULL) { 208254225Speter mlen = STRLEN(m2); 20919304Speter if (len + mlen > sp->cols - 2) 21019304Speter mlen = (sp->cols - 2) - len; 211254225Speter (void)gp->scr_waddstr(sp, m2, mlen); 21219304Speter } 21319304Speter 21419304Speter (void)gp->scr_move(sp, oldy, oldx); 21519304Speter (void)gp->scr_refresh(sp, 0); 21619304Speter} 21719304Speter 21819304Speter/* 21919304Speter * vs_msg -- 22019304Speter * Display ex output or error messages for the screen. 22119304Speter * 22219304Speter * This routine is the default editor interface for all ex output, and all ex 22319304Speter * and vi error/informational messages. It implements the standard strategy 22419304Speter * of stealing lines from the bottom of the vi text screen. Screens using an 22519304Speter * alternate method of displaying messages, e.g. dialog boxes, should set their 22619304Speter * scr_msg function to the correct function before calling the editor. 22719304Speter * 22819304Speter * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t)); 22919304Speter */ 23019304Spetervoid 231254225Spetervs_msg(SCR *sp, mtype_t mtype, char *line, size_t len) 23219304Speter{ 23319304Speter GS *gp; 23419304Speter VI_PRIVATE *vip; 23519304Speter size_t maxcols, oldx, oldy, padding; 23619304Speter const char *e, *s, *t; 23719304Speter 23819304Speter gp = sp->gp; 23919304Speter vip = VIP(sp); 24019304Speter 24119304Speter /* 24219304Speter * Ring the bell if it's scheduled. 24319304Speter * 24419304Speter * XXX 24519304Speter * Shouldn't we save this, too? 24619304Speter */ 24719304Speter if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED)) 24819304Speter if (F_ISSET(sp, SC_SCR_VI)) { 24919304Speter F_CLR(gp, G_BELLSCHED); 25019304Speter (void)gp->scr_bell(sp); 25119304Speter } else 25219304Speter F_SET(gp, G_BELLSCHED); 25319304Speter 25419304Speter /* 25519304Speter * If vi is using the error line for text input, there's no screen 25619304Speter * real-estate for the error message. Nothing to do without some 25719304Speter * information as to how important the error message is. 25819304Speter */ 25919304Speter if (F_ISSET(sp, SC_TINPUT_INFO)) 26019304Speter return; 26119304Speter 26219304Speter /* 26319304Speter * Ex or ex controlled screen output. 26419304Speter * 26519304Speter * If output happens during startup, e.g., a .exrc file, we may be 26619304Speter * in ex mode but haven't initialized the screen. Initialize here, 26719304Speter * and in this case, stay in ex mode. 26819304Speter * 26919304Speter * If the SC_SCR_EXWROTE bit is set, then we're switching back and 27019304Speter * forth between ex and vi, but the screen is trashed and we have 27119304Speter * to respect that. Switch to ex mode long enough to put out the 27219304Speter * message. 27319304Speter * 27419304Speter * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to 27519304Speter * the screen, so previous opinions are ignored. 27619304Speter */ 27719304Speter if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { 27819304Speter if (!F_ISSET(sp, SC_SCR_EX)) 27919304Speter if (F_ISSET(sp, SC_SCR_EXWROTE)) { 28019304Speter if (sp->gp->scr_screen(sp, SC_EX)) 28119304Speter return; 28219304Speter } else 28319304Speter if (ex_init(sp)) 28419304Speter return; 28519304Speter 28619304Speter if (mtype == M_ERR) 28719304Speter (void)gp->scr_attr(sp, SA_INVERSE, 1); 28819304Speter (void)printf("%.*s", (int)len, line); 28919304Speter if (mtype == M_ERR) 29019304Speter (void)gp->scr_attr(sp, SA_INVERSE, 0); 29119304Speter (void)fflush(stdout); 29219304Speter 29319304Speter F_CLR(sp, SC_EX_WAIT_NO); 29419304Speter 29519304Speter if (!F_ISSET(sp, SC_SCR_EX)) 29619304Speter (void)sp->gp->scr_screen(sp, SC_VI); 29719304Speter return; 29819304Speter } 29919304Speter 30019304Speter /* If the vi screen isn't ready, save the message. */ 30119304Speter if (!F_ISSET(sp, SC_SCR_VI)) { 30219304Speter (void)vs_msgsave(sp, mtype, line, len); 30319304Speter return; 30419304Speter } 30519304Speter 30619304Speter /* Save the cursor position. */ 30719304Speter (void)gp->scr_cursor(sp, &oldy, &oldx); 30819304Speter 30919304Speter /* If it's an ex output message, just write it out. */ 31019304Speter if (mtype == M_NONE) { 31119304Speter vs_output(sp, mtype, line, len); 31219304Speter goto ret; 31319304Speter } 31419304Speter 31519304Speter /* 31619304Speter * If it's a vi message, strip the trailing <newline> so we can 31719304Speter * try and paste messages together. 31819304Speter */ 31919304Speter if (line[len - 1] == '\n') 32019304Speter --len; 32119304Speter 32219304Speter /* 32319304Speter * If a message won't fit on a single line, try to split on a <blank>. 32419304Speter * If a subsequent message fits on the same line, write a separator 32519304Speter * and output it. Otherwise, put out a newline. 32619304Speter * 32719304Speter * Need up to two padding characters normally; a semi-colon and a 32819304Speter * separating space. If only a single line on the screen, add some 32919304Speter * more for the trailing continuation message. 33019304Speter * 33119304Speter * XXX 33219304Speter * Assume that periods and semi-colons take up a single column on the 33319304Speter * screen. 33419304Speter * 33519304Speter * XXX 33619304Speter * There are almost certainly pathological cases that will break this 33719304Speter * code. 33819304Speter */ 33919304Speter if (IS_ONELINE(sp)) 34019304Speter (void)msg_cmsg(sp, CMSG_CONT_S, &padding); 34119304Speter else 34219304Speter padding = 0; 34319304Speter padding += 2; 34419304Speter 34519304Speter maxcols = sp->cols - 1; 34619304Speter if (vip->lcontinue != 0) 34719304Speter if (len + vip->lcontinue + padding > maxcols) 34819304Speter vs_output(sp, vip->mtype, ".\n", 2); 34919304Speter else { 35019304Speter vs_output(sp, vip->mtype, ";", 1); 35119304Speter vs_output(sp, M_NONE, " ", 1); 35219304Speter } 35319304Speter vip->mtype = mtype; 35419304Speter for (s = line;; s = t) { 35519304Speter for (; len > 0 && isblank(*s); --len, ++s); 35619304Speter if (len == 0) 35719304Speter break; 35819304Speter if (len + vip->lcontinue > maxcols) { 35919304Speter for (e = s + (maxcols - vip->lcontinue); 36019304Speter e > s && !isblank(*e); --e); 36119304Speter if (e == s) 36219304Speter e = t = s + (maxcols - vip->lcontinue); 36319304Speter else 36419304Speter for (t = e; isblank(e[-1]); --e); 36519304Speter } else 36619304Speter e = t = s + len; 36719304Speter 36819304Speter /* 36919304Speter * If the message ends in a period, discard it, we want to 37019304Speter * gang messages where possible. 37119304Speter */ 37219304Speter len -= t - s; 37319304Speter if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.') 37419304Speter --e; 37519304Speter vs_output(sp, mtype, s, e - s); 37619304Speter 37719304Speter if (len != 0) 37819304Speter vs_output(sp, M_NONE, "\n", 1); 37919304Speter 38019304Speter if (INTERRUPTED(sp)) 38119304Speter break; 38219304Speter } 38319304Speter 38419304Speterret: (void)gp->scr_move(sp, oldy, oldx); 38519304Speter (void)gp->scr_refresh(sp, 0); 38619304Speter} 38719304Speter 38819304Speter/* 38919304Speter * vs_output -- 39019304Speter * Output the text to the screen. 39119304Speter */ 39219304Speterstatic void 393254225Spetervs_output(SCR *sp, mtype_t mtype, const char *line, int llen) 39419304Speter{ 39519304Speter GS *gp; 39619304Speter VI_PRIVATE *vip; 397254225Speter size_t notused; 398254225Speter int len, rlen, tlen; 39919304Speter const char *p, *t; 40019304Speter char *cbp, *ecbp, cbuf[128]; 40119304Speter 40219304Speter gp = sp->gp; 40319304Speter vip = VIP(sp); 40419304Speter for (p = line, rlen = llen; llen > 0;) { 40519304Speter /* Get the next physical line. */ 40619304Speter if ((p = memchr(line, '\n', llen)) == NULL) 40719304Speter len = llen; 40819304Speter else 40919304Speter len = p - line; 41019304Speter 41119304Speter /* 41219304Speter * The max is sp->cols characters, and we may have already 41319304Speter * written part of the line. 41419304Speter */ 41519304Speter if (len + vip->lcontinue > sp->cols) 41619304Speter len = sp->cols - vip->lcontinue; 41719304Speter 41819304Speter /* 41919304Speter * If the first line output, do nothing. If the second line 42019304Speter * output, draw the divider line. If drew a full screen, we 42119304Speter * remove the divider line. If it's a continuation line, move 42219304Speter * to the continuation point, else, move the screen up. 42319304Speter */ 42419304Speter if (vip->lcontinue == 0) { 42519304Speter if (!IS_ONELINE(sp)) { 42619304Speter if (vip->totalcount == 1) { 42719304Speter (void)gp->scr_move(sp, 42819304Speter LASTLINE(sp) - 1, 0); 42919304Speter (void)gp->scr_clrtoeol(sp); 43019304Speter (void)vs_divider(sp); 43119304Speter F_SET(vip, VIP_DIVIDER); 43219304Speter ++vip->totalcount; 43319304Speter ++vip->linecount; 43419304Speter } 43519304Speter if (vip->totalcount == sp->t_maxrows && 43619304Speter F_ISSET(vip, VIP_DIVIDER)) { 43719304Speter --vip->totalcount; 43819304Speter --vip->linecount; 43919304Speter F_CLR(vip, VIP_DIVIDER); 44019304Speter } 44119304Speter } 44219304Speter if (vip->totalcount != 0) 44319304Speter vs_scroll(sp, NULL, SCROLL_W_QUIT); 44419304Speter 44519304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 44619304Speter ++vip->totalcount; 44719304Speter ++vip->linecount; 44819304Speter 44919304Speter if (INTERRUPTED(sp)) 45019304Speter break; 45119304Speter } else 45219304Speter (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue); 45319304Speter 45419304Speter /* Error messages are in inverse video. */ 45519304Speter if (mtype == M_ERR) 45619304Speter (void)gp->scr_attr(sp, SA_INVERSE, 1); 45719304Speter 45819304Speter /* Display the line, doing character translation. */ 45919304Speter#define FLUSH { \ 46019304Speter *cbp = '\0'; \ 46119304Speter (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \ 46219304Speter cbp = cbuf; \ 46319304Speter} 46419304Speter ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; 46519304Speter for (t = line, tlen = len; tlen--; ++t) { 46619304Speter /* 46719304Speter * Replace tabs with spaces, there are places in 46819304Speter * ex that do column calculations without looking 46919304Speter * at <tabs> -- and all routines that care about 47019304Speter * <tabs> do their own expansions. This catches 47119304Speter * <tabs> in things like tag search strings. 47219304Speter */ 473254225Speter if (cbp + 1 >= ecbp) 47419304Speter FLUSH; 475254225Speter *cbp++ = *t == '\t' ? ' ' : *t; 47619304Speter } 47719304Speter if (cbp > cbuf) 47819304Speter FLUSH; 47919304Speter if (mtype == M_ERR) 48019304Speter (void)gp->scr_attr(sp, SA_INVERSE, 0); 48119304Speter 48219304Speter /* Clear the rest of the line. */ 48319304Speter (void)gp->scr_clrtoeol(sp); 48419304Speter 48519304Speter /* If we loop, it's a new line. */ 48619304Speter vip->lcontinue = 0; 48719304Speter 48819304Speter /* Reset for the next line. */ 48919304Speter line += len; 49019304Speter llen -= len; 49119304Speter if (p != NULL) { 49219304Speter ++line; 49319304Speter --llen; 49419304Speter } 49519304Speter } 49619304Speter 49719304Speter /* Set up next continuation line. */ 49819304Speter if (p == NULL) 49919304Speter gp->scr_cursor(sp, ¬used, &vip->lcontinue); 50019304Speter} 50119304Speter 50219304Speter/* 50319304Speter * vs_ex_resolve -- 50419304Speter * Deal with ex message output. 50519304Speter * 50619304Speter * This routine is called when exiting a colon command to resolve any ex 50719304Speter * output that may have occurred. 50819304Speter * 50919304Speter * PUBLIC: int vs_ex_resolve __P((SCR *, int *)); 51019304Speter */ 51119304Speterint 512254225Spetervs_ex_resolve(SCR *sp, int *continuep) 51319304Speter{ 51419304Speter EVENT ev; 51519304Speter GS *gp; 51619304Speter VI_PRIVATE *vip; 51719304Speter sw_t wtype; 51819304Speter 51919304Speter gp = sp->gp; 52019304Speter vip = VIP(sp); 52119304Speter *continuep = 0; 52219304Speter 52319304Speter /* If we ran any ex command, we can't trust the cursor position. */ 52419304Speter F_SET(vip, VIP_CUR_INVALID); 52519304Speter 52619304Speter /* Terminate any partially written message. */ 52719304Speter if (vip->lcontinue != 0) { 52819304Speter vs_output(sp, vip->mtype, ".", 1); 52919304Speter vip->lcontinue = 0; 53019304Speter 53119304Speter vip->mtype = M_NONE; 53219304Speter } 53319304Speter 53419304Speter /* 53519304Speter * If we switched out of the vi screen into ex, switch back while we 53619304Speter * figure out what to do with the screen and potentially get another 53719304Speter * command to execute. 53819304Speter * 53919304Speter * If we didn't switch into ex, we're not required to wait, and less 54019304Speter * than 2 lines of output, we can continue without waiting for the 54119304Speter * wait. 54219304Speter * 54319304Speter * Note, all other code paths require waiting, so we leave the report 54419304Speter * of modified lines until later, so that we won't wait for no other 54519304Speter * reason than a threshold number of lines were modified. This means 54619304Speter * we display cumulative line modification reports for groups of ex 54719304Speter * commands. That seems right to me (well, at least not wrong). 54819304Speter */ 54919304Speter if (F_ISSET(sp, SC_SCR_EXWROTE)) { 55019304Speter if (sp->gp->scr_screen(sp, SC_VI)) 55119304Speter return (1); 55219304Speter } else 55319304Speter if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) { 55419304Speter F_CLR(sp, SC_EX_WAIT_NO); 55519304Speter return (0); 55619304Speter } 55719304Speter 55819304Speter /* Clear the required wait flag, it's no longer needed. */ 55919304Speter F_CLR(sp, SC_EX_WAIT_YES); 56019304Speter 56119304Speter /* 56219304Speter * Wait, unless explicitly told not to wait or the user interrupted 56319304Speter * the command. If the user is leaving the screen, for any reason, 56419304Speter * they can't continue with further ex commands. 56519304Speter */ 56619304Speter if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) { 56719304Speter wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | 56819304Speter SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX; 56919304Speter if (F_ISSET(sp, SC_SCR_EXWROTE)) 57019304Speter vs_wait(sp, continuep, wtype); 57119304Speter else 57219304Speter vs_scroll(sp, continuep, wtype); 57319304Speter if (*continuep) 57419304Speter return (0); 57519304Speter } 57619304Speter 57719304Speter /* If ex wrote on the screen, refresh the screen image. */ 57819304Speter if (F_ISSET(sp, SC_SCR_EXWROTE)) 57919304Speter F_SET(vip, VIP_N_EX_PAINT); 58019304Speter 58119304Speter /* 58219304Speter * If we're not the bottom of the split screen stack, the screen 58319304Speter * image itself is wrong, so redraw everything. 58419304Speter */ 585254225Speter if (TAILQ_NEXT(sp, q) != NULL) 58619304Speter F_SET(sp, SC_SCR_REDRAW); 58719304Speter 58819304Speter /* If ex changed the underlying file, the map itself is wrong. */ 58919304Speter if (F_ISSET(vip, VIP_N_EX_REDRAW)) 59019304Speter F_SET(sp, SC_SCR_REFORMAT); 59119304Speter 59219304Speter /* Ex may have switched out of the alternate screen, return. */ 59319304Speter (void)gp->scr_attr(sp, SA_ALTERNATE, 1); 59419304Speter 59519304Speter /* 59619304Speter * Whew. We're finally back home, after what feels like years. 59719304Speter * Kiss the ground. 59819304Speter */ 59919304Speter F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO); 60019304Speter 60119304Speter /* 60219304Speter * We may need to repaint some of the screen, e.g.: 60319304Speter * 60419304Speter * :set 60519304Speter * :!ls 60619304Speter * 60719304Speter * gives us a combination of some lines that are "wrong", and a need 60819304Speter * for a full refresh. 60919304Speter */ 61019304Speter if (vip->totalcount > 1) { 61119304Speter /* Set up the redraw of the overwritten lines. */ 61219304Speter ev.e_event = E_REPAINT; 61319304Speter ev.e_flno = vip->totalcount >= 61419304Speter sp->rows ? 1 : sp->rows - vip->totalcount; 61519304Speter ev.e_tlno = sp->rows; 61619304Speter 61719304Speter /* Reset the count of overwriting lines. */ 61819304Speter vip->linecount = vip->lcontinue = vip->totalcount = 0; 61919304Speter 62019304Speter /* Redraw. */ 62119304Speter (void)vs_repaint(sp, &ev); 62219304Speter } else 62319304Speter /* Reset the count of overwriting lines. */ 62419304Speter vip->linecount = vip->lcontinue = vip->totalcount = 0; 62519304Speter 62619304Speter return (0); 62719304Speter} 62819304Speter 62919304Speter/* 63019304Speter * vs_resolve -- 63119304Speter * Deal with message output. 63219304Speter * 63319304Speter * PUBLIC: int vs_resolve __P((SCR *, SCR *, int)); 63419304Speter */ 63519304Speterint 636254225Spetervs_resolve(SCR *sp, SCR *csp, int forcewait) 63719304Speter{ 63819304Speter EVENT ev; 63919304Speter GS *gp; 64019304Speter MSGS *mp; 64119304Speter VI_PRIVATE *vip; 64219304Speter size_t oldy, oldx; 64319304Speter int redraw; 64419304Speter 64519304Speter /* 64619304Speter * Vs_resolve is called from the main vi loop and the refresh function 64719304Speter * to periodically ensure that the user has seen any messages that have 64819304Speter * been displayed and that any status lines are correct. The sp screen 64919304Speter * is the screen we're checking, usually the current screen. When it's 65019304Speter * not, csp is the current screen, used for final cursor positioning. 65119304Speter */ 65219304Speter gp = sp->gp; 65319304Speter vip = VIP(sp); 65419304Speter if (csp == NULL) 65519304Speter csp = sp; 65619304Speter 65719304Speter /* Save the cursor position. */ 65819304Speter (void)gp->scr_cursor(csp, &oldy, &oldx); 65919304Speter 66019304Speter /* Ring the bell if it's scheduled. */ 66119304Speter if (F_ISSET(gp, G_BELLSCHED)) { 66219304Speter F_CLR(gp, G_BELLSCHED); 66319304Speter (void)gp->scr_bell(sp); 66419304Speter } 66519304Speter 66619304Speter /* Display new file status line. */ 66719304Speter if (F_ISSET(sp, SC_STATUS)) { 66819304Speter F_CLR(sp, SC_STATUS); 66919304Speter msgq_status(sp, sp->lno, MSTAT_TRUNCATE); 67019304Speter } 67119304Speter 67219304Speter /* Report on line modifications. */ 67319304Speter mod_rpt(sp); 67419304Speter 67519304Speter /* 67619304Speter * Flush any saved messages. If the screen isn't ready, refresh 67719304Speter * it. (A side-effect of screen refresh is that we can display 67819304Speter * messages.) Once this is done, don't trust the cursor. That 67919304Speter * extra refresh screwed the pooch. 68019304Speter */ 681254225Speter if (!SLIST_EMPTY(gp->msgq)) { 68219304Speter if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1)) 68319304Speter return (1); 684254225Speter while ((mp = SLIST_FIRST(gp->msgq)) != NULL) { 68519304Speter gp->scr_msg(sp, mp->mtype, mp->buf, mp->len); 686254225Speter SLIST_REMOVE_HEAD(gp->msgq, q); 68719304Speter free(mp->buf); 68819304Speter free(mp); 68919304Speter } 69019304Speter F_SET(vip, VIP_CUR_INVALID); 69119304Speter } 69219304Speter 69319304Speter switch (vip->totalcount) { 69419304Speter case 0: 69519304Speter redraw = 0; 69619304Speter break; 69719304Speter case 1: 69819304Speter /* 69919304Speter * If we're switching screens, we have to wait for messages, 70019304Speter * regardless. If we don't wait, skip updating the modeline. 70119304Speter */ 70219304Speter if (forcewait) 70319304Speter vs_scroll(sp, NULL, SCROLL_W); 70419304Speter else 70519304Speter F_SET(vip, VIP_S_MODELINE); 70619304Speter 70719304Speter redraw = 0; 70819304Speter break; 70919304Speter default: 71019304Speter /* 71119304Speter * If >1 message line in use, prompt the user to continue and 71219304Speter * repaint overwritten lines. 71319304Speter */ 71419304Speter vs_scroll(sp, NULL, SCROLL_W); 71519304Speter 71619304Speter ev.e_event = E_REPAINT; 71719304Speter ev.e_flno = vip->totalcount >= 71819304Speter sp->rows ? 1 : sp->rows - vip->totalcount; 71919304Speter ev.e_tlno = sp->rows; 72019304Speter 72119304Speter redraw = 1; 72219304Speter break; 72319304Speter } 72419304Speter 72519304Speter /* Reset the count of overwriting lines. */ 72619304Speter vip->linecount = vip->lcontinue = vip->totalcount = 0; 72719304Speter 72819304Speter /* Redraw. */ 72919304Speter if (redraw) 73019304Speter (void)vs_repaint(sp, &ev); 73119304Speter 73219304Speter /* Restore the cursor position. */ 73319304Speter (void)gp->scr_move(csp, oldy, oldx); 73419304Speter 73519304Speter return (0); 73619304Speter} 73719304Speter 73819304Speter/* 73919304Speter * vs_scroll -- 74019304Speter * Scroll the screen for output. 74119304Speter */ 74219304Speterstatic void 743254225Spetervs_scroll(SCR *sp, int *continuep, sw_t wtype) 74419304Speter{ 74519304Speter GS *gp; 74619304Speter VI_PRIVATE *vip; 74719304Speter 74819304Speter gp = sp->gp; 74919304Speter vip = VIP(sp); 75019304Speter if (!IS_ONELINE(sp)) { 75119304Speter /* 75219304Speter * Scroll the screen. Instead of scrolling the entire screen, 75319304Speter * delete the line above the first line output so preserve the 75419304Speter * maximum amount of the screen. 75519304Speter */ 75619304Speter (void)gp->scr_move(sp, vip->totalcount < 75719304Speter sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0); 75819304Speter (void)gp->scr_deleteln(sp); 75919304Speter 76019304Speter /* If there are screens below us, push them back into place. */ 761254225Speter if (TAILQ_NEXT(sp, q) != NULL) { 76219304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 76319304Speter (void)gp->scr_insertln(sp); 76419304Speter } 76519304Speter } 76619304Speter if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows) 76719304Speter return; 76819304Speter vs_wait(sp, continuep, wtype); 76919304Speter} 77019304Speter 77119304Speter/* 77219304Speter * vs_wait -- 77319304Speter * Prompt the user to continue. 77419304Speter */ 77519304Speterstatic void 776254225Spetervs_wait(SCR *sp, int *continuep, sw_t wtype) 77719304Speter{ 77819304Speter EVENT ev; 77919304Speter VI_PRIVATE *vip; 78019304Speter const char *p; 78119304Speter GS *gp; 78219304Speter size_t len; 78319304Speter 78419304Speter gp = sp->gp; 78519304Speter vip = VIP(sp); 78619304Speter 78719304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 78819304Speter if (IS_ONELINE(sp)) 78919304Speter p = msg_cmsg(sp, CMSG_CONT_S, &len); 79019304Speter else 79119304Speter switch (wtype) { 79219304Speter case SCROLL_W_QUIT: 79319304Speter p = msg_cmsg(sp, CMSG_CONT_Q, &len); 79419304Speter break; 79519304Speter case SCROLL_W_EX: 79619304Speter p = msg_cmsg(sp, CMSG_CONT_EX, &len); 79719304Speter break; 79819304Speter case SCROLL_W: 79919304Speter p = msg_cmsg(sp, CMSG_CONT, &len); 80019304Speter break; 80119304Speter default: 80219304Speter abort(); 80319304Speter /* NOTREACHED */ 80419304Speter } 80519304Speter (void)gp->scr_addstr(sp, p, len); 80619304Speter 80719304Speter ++vip->totalcount; 80819304Speter vip->linecount = 0; 80919304Speter 81019304Speter (void)gp->scr_clrtoeol(sp); 81119304Speter (void)gp->scr_refresh(sp, 0); 81219304Speter 81319304Speter /* Get a single character from the terminal. */ 81419304Speter if (continuep != NULL) 81519304Speter *continuep = 0; 81619304Speter for (;;) { 81719304Speter if (v_event_get(sp, &ev, 0, 0)) 81819304Speter return; 81919304Speter if (ev.e_event == E_CHARACTER) 82019304Speter break; 82119304Speter if (ev.e_event == E_INTERRUPT) { 82219304Speter ev.e_c = CH_QUIT; 82319304Speter F_SET(gp, G_INTERRUPTED); 82419304Speter break; 82519304Speter } 82619304Speter (void)gp->scr_bell(sp); 82719304Speter } 82819304Speter switch (wtype) { 82919304Speter case SCROLL_W_QUIT: 83019304Speter if (ev.e_c == CH_QUIT) 83119304Speter F_SET(gp, G_INTERRUPTED); 83219304Speter break; 83319304Speter case SCROLL_W_EX: 83419304Speter if (ev.e_c == ':' && continuep != NULL) 83519304Speter *continuep = 1; 83619304Speter break; 83719304Speter case SCROLL_W: 83819304Speter break; 83919304Speter } 84019304Speter} 84119304Speter 84219304Speter/* 84319304Speter * vs_divider -- 84419304Speter * Draw a dividing line between the screen and the output. 84519304Speter */ 84619304Speterstatic void 847254225Spetervs_divider(SCR *sp) 84819304Speter{ 84919304Speter GS *gp; 85019304Speter size_t len; 85119304Speter 85219304Speter#define DIVIDESTR "+=+=+=+=+=+=+=+" 85319304Speter len = 85419304Speter sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1; 85519304Speter gp = sp->gp; 85619304Speter (void)gp->scr_attr(sp, SA_INVERSE, 1); 85719304Speter (void)gp->scr_addstr(sp, DIVIDESTR, len); 85819304Speter (void)gp->scr_attr(sp, SA_INVERSE, 0); 85919304Speter} 86019304Speter 86119304Speter/* 86219304Speter * vs_msgsave -- 86319304Speter * Save a message for later display. 86419304Speter */ 86519304Speterstatic void 866254225Spetervs_msgsave(SCR *sp, mtype_t mt, char *p, size_t len) 86719304Speter{ 86819304Speter GS *gp; 86919304Speter MSGS *mp_c, *mp_n; 87019304Speter 87119304Speter /* 87219304Speter * We have to handle messages before we have any place to put them. 87319304Speter * If there's no screen support yet, allocate a msg structure, copy 87419304Speter * in the message, and queue it on the global structure. If we can't 87519304Speter * allocate memory here, we're genuinely screwed, dump the message 87619304Speter * to stderr in the (probably) vain hope that someone will see it. 87719304Speter */ 87819304Speter CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS)); 87919304Speter MALLOC_GOTO(sp, mp_n->buf, char *, len); 88019304Speter 88119304Speter memmove(mp_n->buf, p, len); 88219304Speter mp_n->len = len; 88319304Speter mp_n->mtype = mt; 88419304Speter 88519304Speter gp = sp->gp; 886254225Speter if (SLIST_EMPTY(gp->msgq)) { 887254225Speter SLIST_INSERT_HEAD(gp->msgq, mp_n, q); 88819304Speter } else { 889254225Speter SLIST_FOREACH(mp_c, gp->msgq, q) 890254225Speter if (SLIST_NEXT(mp_c, q) == NULL) 891254225Speter break; 892254225Speter SLIST_INSERT_AFTER(mp_c, mp_n, q); 89319304Speter } 89419304Speter return; 89519304Speter 89619304Speteralloc_err: 89719304Speter if (mp_n != NULL) 89819304Speter free(mp_n); 89919304Speter (void)fprintf(stderr, "%.*s\n", (int)len, p); 90019304Speter} 901