summaryrefslogtreecommitdiff
path: root/contrib/nvi/vi/vs_msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/vi/vs_msg.c')
-rw-r--r--contrib/nvi/vi/vs_msg.c927
1 files changed, 927 insertions, 0 deletions
diff --git a/contrib/nvi/vi/vs_msg.c b/contrib/nvi/vi/vs_msg.c
new file mode 100644
index 000000000000..7ef8f5343c15
--- /dev/null
+++ b/contrib/nvi/vi/vs_msg.c
@@ -0,0 +1,927 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_msg.c 10.77 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+typedef enum {
+ SCROLL_W, /* User wait. */
+ SCROLL_W_EX, /* User wait, or enter : to continue. */
+ SCROLL_W_QUIT /* User wait, or enter q to quit. */
+ /*
+ * SCROLL_W_QUIT has another semantic
+ * -- only wait if the screen is full
+ */
+} sw_t;
+
+static void vs_divider __P((SCR *));
+static void vs_msgsave __P((SCR *, mtype_t, char *, size_t));
+static void vs_output __P((SCR *, mtype_t, const char *, int));
+static void vs_scroll __P((SCR *, int *, sw_t));
+static void vs_wait __P((SCR *, int *, sw_t));
+
+/*
+ * vs_busy --
+ * Display, update or clear a busy message.
+ *
+ * This routine is the default editor interface for vi busy messages. It
+ * implements a standard strategy of stealing lines from the bottom of the
+ * vi text screen. Screens using an alternate method of displaying busy
+ * messages, e.g. X11 clock icons, should set their scr_busy function to the
+ * correct function before calling the main editor routine.
+ *
+ * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
+ */
+void
+vs_busy(sp, msg, btype)
+ SCR *sp;
+ const char *msg;
+ busy_t btype;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+ static const char flagc[] = "|/-\\";
+ struct timeval tv;
+ size_t len, notused;
+ const char *p;
+
+ /* Ex doesn't display busy messages. */
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
+ return;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /*
+ * Most of this routine is to deal with the screen sharing real estate
+ * between the normal edit messages and the busy messages. Logically,
+ * all that's needed is something that puts up a message, periodically
+ * updates it, and then goes away.
+ */
+ switch (btype) {
+ case BUSY_ON:
+ ++vip->busy_ref;
+ if (vip->totalcount != 0 || vip->busy_ref != 1)
+ break;
+
+ /* Initialize state for updates. */
+ vip->busy_ch = 0;
+ (void)gettimeofday(&vip->busy_tv, NULL);
+
+ /* Save the current cursor. */
+ (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
+
+ /* Display the busy message. */
+ p = msg_cat(sp, msg, &len);
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_addstr(sp, p, len);
+ (void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
+ (void)gp->scr_clrtoeol(sp);
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ break;
+ case BUSY_OFF:
+ if (vip->busy_ref == 0)
+ break;
+ --vip->busy_ref;
+
+ /*
+ * If the line isn't in use for another purpose, clear it.
+ * Always return to the original position.
+ */
+ if (vip->totalcount == 0 && vip->busy_ref == 0) {
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+ }
+ (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
+ break;
+ case BUSY_UPDATE:
+ if (vip->totalcount != 0 || vip->busy_ref == 0)
+ break;
+
+ /* Update no more than every 1/8 of a second. */
+ (void)gettimeofday(&tv, NULL);
+ if (((tv.tv_sec - vip->busy_tv.tv_sec) * 1000000 +
+ (tv.tv_usec - vip->busy_tv.tv_usec)) < 125000)
+ return;
+ vip->busy_tv = tv;
+
+ /* Display the update. */
+ if (vip->busy_ch == sizeof(flagc) - 1)
+ vip->busy_ch = 0;
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ break;
+ }
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_home --
+ * Home the cursor to the bottom row, left-most column.
+ *
+ * PUBLIC: void vs_home __P((SCR *));
+ */
+void
+vs_home(sp)
+ SCR *sp;
+{
+ (void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)sp->gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_update --
+ * Update a command.
+ *
+ * PUBLIC: void vs_update __P((SCR *, const char *, const char *));
+ */
+void
+vs_update(sp, m1, m2)
+ SCR *sp;
+ const char *m1, *m2;
+{
+ GS *gp;
+ size_t len, mlen, oldx, oldy;
+
+ gp = sp->gp;
+
+ /*
+ * This routine displays a message on the bottom line of the screen,
+ * without updating any of the command structures that would keep it
+ * there for any period of time, i.e. it is overwritten immediately.
+ *
+ * It's used by the ex read and ! commands when the user's command is
+ * expanded, and by the ex substitution confirmation prompt.
+ */
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ (void)ex_printf(sp,
+ "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : m2);
+ (void)ex_fflush(sp);
+ }
+
+ /*
+ * Save the cursor position, the substitute-with-confirmation code
+ * will have already set it correctly.
+ */
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+
+ /* Clear the bottom line. */
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+
+ /*
+ * XXX
+ * Don't let long file names screw up the screen.
+ */
+ if (m1 != NULL) {
+ mlen = len = strlen(m1);
+ if (len > sp->cols - 2)
+ mlen = len = sp->cols - 2;
+ (void)gp->scr_addstr(sp, m1, mlen);
+ } else
+ len = 0;
+ if (m2 != NULL) {
+ mlen = strlen(m2);
+ if (len + mlen > sp->cols - 2)
+ mlen = (sp->cols - 2) - len;
+ (void)gp->scr_addstr(sp, m2, mlen);
+ }
+
+ (void)gp->scr_move(sp, oldy, oldx);
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_msg --
+ * Display ex output or error messages for the screen.
+ *
+ * This routine is the default editor interface for all ex output, and all ex
+ * and vi error/informational messages. It implements the standard strategy
+ * of stealing lines from the bottom of the vi text screen. Screens using an
+ * alternate method of displaying messages, e.g. dialog boxes, should set their
+ * scr_msg function to the correct function before calling the editor.
+ *
+ * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
+ */
+void
+vs_msg(sp, mtype, line, len)
+ SCR *sp;
+ mtype_t mtype;
+ char *line;
+ size_t len;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+ size_t maxcols, oldx, oldy, padding;
+ const char *e, *s, *t;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /*
+ * Ring the bell if it's scheduled.
+ *
+ * XXX
+ * Shouldn't we save this, too?
+ */
+ if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
+ if (F_ISSET(sp, SC_SCR_VI)) {
+ F_CLR(gp, G_BELLSCHED);
+ (void)gp->scr_bell(sp);
+ } else
+ F_SET(gp, G_BELLSCHED);
+
+ /*
+ * If vi is using the error line for text input, there's no screen
+ * real-estate for the error message. Nothing to do without some
+ * information as to how important the error message is.
+ */
+ if (F_ISSET(sp, SC_TINPUT_INFO))
+ return;
+
+ /*
+ * Ex or ex controlled screen output.
+ *
+ * If output happens during startup, e.g., a .exrc file, we may be
+ * in ex mode but haven't initialized the screen. Initialize here,
+ * and in this case, stay in ex mode.
+ *
+ * If the SC_SCR_EXWROTE bit is set, then we're switching back and
+ * forth between ex and vi, but the screen is trashed and we have
+ * to respect that. Switch to ex mode long enough to put out the
+ * message.
+ *
+ * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
+ * the screen, so previous opinions are ignored.
+ */
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
+ if (!F_ISSET(sp, SC_SCR_EX))
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ if (sp->gp->scr_screen(sp, SC_EX))
+ return;
+ } else
+ if (ex_init(sp))
+ return;
+
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+ (void)printf("%.*s", (int)len, line);
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+ (void)fflush(stdout);
+
+ F_CLR(sp, SC_EX_WAIT_NO);
+
+ if (!F_ISSET(sp, SC_SCR_EX))
+ (void)sp->gp->scr_screen(sp, SC_VI);
+ return;
+ }
+
+ /* If the vi screen isn't ready, save the message. */
+ if (!F_ISSET(sp, SC_SCR_VI)) {
+ (void)vs_msgsave(sp, mtype, line, len);
+ return;
+ }
+
+ /* Save the cursor position. */
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+
+ /* If it's an ex output message, just write it out. */
+ if (mtype == M_NONE) {
+ vs_output(sp, mtype, line, len);
+ goto ret;
+ }
+
+ /*
+ * If it's a vi message, strip the trailing <newline> so we can
+ * try and paste messages together.
+ */
+ if (line[len - 1] == '\n')
+ --len;
+
+ /*
+ * If a message won't fit on a single line, try to split on a <blank>.
+ * If a subsequent message fits on the same line, write a separator
+ * and output it. Otherwise, put out a newline.
+ *
+ * Need up to two padding characters normally; a semi-colon and a
+ * separating space. If only a single line on the screen, add some
+ * more for the trailing continuation message.
+ *
+ * XXX
+ * Assume that periods and semi-colons take up a single column on the
+ * screen.
+ *
+ * XXX
+ * There are almost certainly pathological cases that will break this
+ * code.
+ */
+ if (IS_ONELINE(sp))
+ (void)msg_cmsg(sp, CMSG_CONT_S, &padding);
+ else
+ padding = 0;
+ padding += 2;
+
+ maxcols = sp->cols - 1;
+ if (vip->lcontinue != 0)
+ if (len + vip->lcontinue + padding > maxcols)
+ vs_output(sp, vip->mtype, ".\n", 2);
+ else {
+ vs_output(sp, vip->mtype, ";", 1);
+ vs_output(sp, M_NONE, " ", 1);
+ }
+ vip->mtype = mtype;
+ for (s = line;; s = t) {
+ for (; len > 0 && isblank(*s); --len, ++s);
+ if (len == 0)
+ break;
+ if (len + vip->lcontinue > maxcols) {
+ for (e = s + (maxcols - vip->lcontinue);
+ e > s && !isblank(*e); --e);
+ if (e == s)
+ e = t = s + (maxcols - vip->lcontinue);
+ else
+ for (t = e; isblank(e[-1]); --e);
+ } else
+ e = t = s + len;
+
+ /*
+ * If the message ends in a period, discard it, we want to
+ * gang messages where possible.
+ */
+ len -= t - s;
+ if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
+ --e;
+ vs_output(sp, mtype, s, e - s);
+
+ if (len != 0)
+ vs_output(sp, M_NONE, "\n", 1);
+
+ if (INTERRUPTED(sp))
+ break;
+ }
+
+ret: (void)gp->scr_move(sp, oldy, oldx);
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_output --
+ * Output the text to the screen.
+ */
+static void
+vs_output(sp, mtype, line, llen)
+ SCR *sp;
+ mtype_t mtype;
+ const char *line;
+ int llen;
+{
+ CHAR_T *kp;
+ GS *gp;
+ VI_PRIVATE *vip;
+ size_t chlen, notused;
+ int ch, len, rlen, tlen;
+ const char *p, *t;
+ char *cbp, *ecbp, cbuf[128];
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ for (p = line, rlen = llen; llen > 0;) {
+ /* Get the next physical line. */
+ if ((p = memchr(line, '\n', llen)) == NULL)
+ len = llen;
+ else
+ len = p - line;
+
+ /*
+ * The max is sp->cols characters, and we may have already
+ * written part of the line.
+ */
+ if (len + vip->lcontinue > sp->cols)
+ len = sp->cols - vip->lcontinue;
+
+ /*
+ * If the first line output, do nothing. If the second line
+ * output, draw the divider line. If drew a full screen, we
+ * remove the divider line. If it's a continuation line, move
+ * to the continuation point, else, move the screen up.
+ */
+ if (vip->lcontinue == 0) {
+ if (!IS_ONELINE(sp)) {
+ if (vip->totalcount == 1) {
+ (void)gp->scr_move(sp,
+ LASTLINE(sp) - 1, 0);
+ (void)gp->scr_clrtoeol(sp);
+ (void)vs_divider(sp);
+ F_SET(vip, VIP_DIVIDER);
+ ++vip->totalcount;
+ ++vip->linecount;
+ }
+ if (vip->totalcount == sp->t_maxrows &&
+ F_ISSET(vip, VIP_DIVIDER)) {
+ --vip->totalcount;
+ --vip->linecount;
+ F_CLR(vip, VIP_DIVIDER);
+ }
+ }
+ if (vip->totalcount != 0)
+ vs_scroll(sp, NULL, SCROLL_W_QUIT);
+
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ ++vip->totalcount;
+ ++vip->linecount;
+
+ if (INTERRUPTED(sp))
+ break;
+ } else
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
+
+ /* Error messages are in inverse video. */
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+
+ /* Display the line, doing character translation. */
+#define FLUSH { \
+ *cbp = '\0'; \
+ (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \
+ cbp = cbuf; \
+}
+ ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
+ for (t = line, tlen = len; tlen--; ++t) {
+ ch = *t;
+ /*
+ * Replace tabs with spaces, there are places in
+ * ex that do column calculations without looking
+ * at <tabs> -- and all routines that care about
+ * <tabs> do their own expansions. This catches
+ * <tabs> in things like tag search strings.
+ */
+ if (ch == '\t')
+ ch = ' ';
+ chlen = KEY_LEN(sp, ch);
+ if (cbp + chlen >= ecbp)
+ FLUSH;
+ for (kp = KEY_NAME(sp, ch); chlen--;)
+ *cbp++ = *kp++;
+ }
+ if (cbp > cbuf)
+ FLUSH;
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+
+ /* Clear the rest of the line. */
+ (void)gp->scr_clrtoeol(sp);
+
+ /* If we loop, it's a new line. */
+ vip->lcontinue = 0;
+
+ /* Reset for the next line. */
+ line += len;
+ llen -= len;
+ if (p != NULL) {
+ ++line;
+ --llen;
+ }
+ }
+
+ /* Set up next continuation line. */
+ if (p == NULL)
+ gp->scr_cursor(sp, &notused, &vip->lcontinue);
+}
+
+/*
+ * vs_ex_resolve --
+ * Deal with ex message output.
+ *
+ * This routine is called when exiting a colon command to resolve any ex
+ * output that may have occurred.
+ *
+ * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
+ */
+int
+vs_ex_resolve(sp, continuep)
+ SCR *sp;
+ int *continuep;
+{
+ EVENT ev;
+ GS *gp;
+ VI_PRIVATE *vip;
+ sw_t wtype;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ *continuep = 0;
+
+ /* If we ran any ex command, we can't trust the cursor position. */
+ F_SET(vip, VIP_CUR_INVALID);
+
+ /* Terminate any partially written message. */
+ if (vip->lcontinue != 0) {
+ vs_output(sp, vip->mtype, ".", 1);
+ vip->lcontinue = 0;
+
+ vip->mtype = M_NONE;
+ }
+
+ /*
+ * If we switched out of the vi screen into ex, switch back while we
+ * figure out what to do with the screen and potentially get another
+ * command to execute.
+ *
+ * If we didn't switch into ex, we're not required to wait, and less
+ * than 2 lines of output, we can continue without waiting for the
+ * wait.
+ *
+ * Note, all other code paths require waiting, so we leave the report
+ * of modified lines until later, so that we won't wait for no other
+ * reason than a threshold number of lines were modified. This means
+ * we display cumulative line modification reports for groups of ex
+ * commands. That seems right to me (well, at least not wrong).
+ */
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ if (sp->gp->scr_screen(sp, SC_VI))
+ return (1);
+ } else
+ if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
+ F_CLR(sp, SC_EX_WAIT_NO);
+ return (0);
+ }
+
+ /* Clear the required wait flag, it's no longer needed. */
+ F_CLR(sp, SC_EX_WAIT_YES);
+
+ /*
+ * Wait, unless explicitly told not to wait or the user interrupted
+ * the command. If the user is leaving the screen, for any reason,
+ * they can't continue with further ex commands.
+ */
+ if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
+ wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
+ SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
+ if (F_ISSET(sp, SC_SCR_EXWROTE))
+ vs_wait(sp, continuep, wtype);
+ else
+ vs_scroll(sp, continuep, wtype);
+ if (*continuep)
+ return (0);
+ }
+
+ /* If ex wrote on the screen, refresh the screen image. */
+ if (F_ISSET(sp, SC_SCR_EXWROTE))
+ F_SET(vip, VIP_N_EX_PAINT);
+
+ /*
+ * If we're not the bottom of the split screen stack, the screen
+ * image itself is wrong, so redraw everything.
+ */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ F_SET(sp, SC_SCR_REDRAW);
+
+ /* If ex changed the underlying file, the map itself is wrong. */
+ if (F_ISSET(vip, VIP_N_EX_REDRAW))
+ F_SET(sp, SC_SCR_REFORMAT);
+
+ /* Ex may have switched out of the alternate screen, return. */
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
+
+ /*
+ * Whew. We're finally back home, after what feels like years.
+ * Kiss the ground.
+ */
+ F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
+
+ /*
+ * We may need to repaint some of the screen, e.g.:
+ *
+ * :set
+ * :!ls
+ *
+ * gives us a combination of some lines that are "wrong", and a need
+ * for a full refresh.
+ */
+ if (vip->totalcount > 1) {
+ /* Set up the redraw of the overwritten lines. */
+ ev.e_event = E_REPAINT;
+ ev.e_flno = vip->totalcount >=
+ sp->rows ? 1 : sp->rows - vip->totalcount;
+ ev.e_tlno = sp->rows;
+
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ /* Redraw. */
+ (void)vs_repaint(sp, &ev);
+ } else
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ return (0);
+}
+
+/*
+ * vs_resolve --
+ * Deal with message output.
+ *
+ * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
+ */
+int
+vs_resolve(sp, csp, forcewait)
+ SCR *sp, *csp;
+ int forcewait;
+{
+ EVENT ev;
+ GS *gp;
+ MSGS *mp;
+ VI_PRIVATE *vip;
+ size_t oldy, oldx;
+ int redraw;
+
+ /*
+ * Vs_resolve is called from the main vi loop and the refresh function
+ * to periodically ensure that the user has seen any messages that have
+ * been displayed and that any status lines are correct. The sp screen
+ * is the screen we're checking, usually the current screen. When it's
+ * not, csp is the current screen, used for final cursor positioning.
+ */
+ gp = sp->gp;
+ vip = VIP(sp);
+ if (csp == NULL)
+ csp = sp;
+
+ /* Save the cursor position. */
+ (void)gp->scr_cursor(csp, &oldy, &oldx);
+
+ /* Ring the bell if it's scheduled. */
+ if (F_ISSET(gp, G_BELLSCHED)) {
+ F_CLR(gp, G_BELLSCHED);
+ (void)gp->scr_bell(sp);
+ }
+
+ /* Display new file status line. */
+ if (F_ISSET(sp, SC_STATUS)) {
+ F_CLR(sp, SC_STATUS);
+ msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
+ }
+
+ /* Report on line modifications. */
+ mod_rpt(sp);
+
+ /*
+ * Flush any saved messages. If the screen isn't ready, refresh
+ * it. (A side-effect of screen refresh is that we can display
+ * messages.) Once this is done, don't trust the cursor. That
+ * extra refresh screwed the pooch.
+ */
+ if (gp->msgq.lh_first != NULL) {
+ if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
+ return (1);
+ while ((mp = gp->msgq.lh_first) != NULL) {
+ gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
+ LIST_REMOVE(mp, q);
+ free(mp->buf);
+ free(mp);
+ }
+ F_SET(vip, VIP_CUR_INVALID);
+ }
+
+ switch (vip->totalcount) {
+ case 0:
+ redraw = 0;
+ break;
+ case 1:
+ /*
+ * If we're switching screens, we have to wait for messages,
+ * regardless. If we don't wait, skip updating the modeline.
+ */
+ if (forcewait)
+ vs_scroll(sp, NULL, SCROLL_W);
+ else
+ F_SET(vip, VIP_S_MODELINE);
+
+ redraw = 0;
+ break;
+ default:
+ /*
+ * If >1 message line in use, prompt the user to continue and
+ * repaint overwritten lines.
+ */
+ vs_scroll(sp, NULL, SCROLL_W);
+
+ ev.e_event = E_REPAINT;
+ ev.e_flno = vip->totalcount >=
+ sp->rows ? 1 : sp->rows - vip->totalcount;
+ ev.e_tlno = sp->rows;
+
+ redraw = 1;
+ break;
+ }
+
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ /* Redraw. */
+ if (redraw)
+ (void)vs_repaint(sp, &ev);
+
+ /* Restore the cursor position. */
+ (void)gp->scr_move(csp, oldy, oldx);
+
+ return (0);
+}
+
+/*
+ * vs_scroll --
+ * Scroll the screen for output.
+ */
+static void
+vs_scroll(sp, continuep, wtype)
+ SCR *sp;
+ int *continuep;
+ sw_t wtype;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ if (!IS_ONELINE(sp)) {
+ /*
+ * Scroll the screen. Instead of scrolling the entire screen,
+ * delete the line above the first line output so preserve the
+ * maximum amount of the screen.
+ */
+ (void)gp->scr_move(sp, vip->totalcount <
+ sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
+ (void)gp->scr_deleteln(sp);
+
+ /* If there are screens below us, push them back into place. */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_insertln(sp);
+ }
+ }
+ if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
+ return;
+ vs_wait(sp, continuep, wtype);
+}
+
+/*
+ * vs_wait --
+ * Prompt the user to continue.
+ */
+static void
+vs_wait(sp, continuep, wtype)
+ SCR *sp;
+ int *continuep;
+ sw_t wtype;
+{
+ EVENT ev;
+ VI_PRIVATE *vip;
+ const char *p;
+ GS *gp;
+ size_t len;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ if (IS_ONELINE(sp))
+ p = msg_cmsg(sp, CMSG_CONT_S, &len);
+ else
+ switch (wtype) {
+ case SCROLL_W_QUIT:
+ p = msg_cmsg(sp, CMSG_CONT_Q, &len);
+ break;
+ case SCROLL_W_EX:
+ p = msg_cmsg(sp, CMSG_CONT_EX, &len);
+ break;
+ case SCROLL_W:
+ p = msg_cmsg(sp, CMSG_CONT, &len);
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ (void)gp->scr_addstr(sp, p, len);
+
+ ++vip->totalcount;
+ vip->linecount = 0;
+
+ (void)gp->scr_clrtoeol(sp);
+ (void)gp->scr_refresh(sp, 0);
+
+ /* Get a single character from the terminal. */
+ if (continuep != NULL)
+ *continuep = 0;
+ for (;;) {
+ if (v_event_get(sp, &ev, 0, 0))
+ return;
+ if (ev.e_event == E_CHARACTER)
+ break;
+ if (ev.e_event == E_INTERRUPT) {
+ ev.e_c = CH_QUIT;
+ F_SET(gp, G_INTERRUPTED);
+ break;
+ }
+ (void)gp->scr_bell(sp);
+ }
+ switch (wtype) {
+ case SCROLL_W_QUIT:
+ if (ev.e_c == CH_QUIT)
+ F_SET(gp, G_INTERRUPTED);
+ break;
+ case SCROLL_W_EX:
+ if (ev.e_c == ':' && continuep != NULL)
+ *continuep = 1;
+ break;
+ case SCROLL_W:
+ break;
+ }
+}
+
+/*
+ * vs_divider --
+ * Draw a dividing line between the screen and the output.
+ */
+static void
+vs_divider(sp)
+ SCR *sp;
+{
+ GS *gp;
+ size_t len;
+
+#define DIVIDESTR "+=+=+=+=+=+=+=+"
+ len =
+ sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
+ gp = sp->gp;
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+ (void)gp->scr_addstr(sp, DIVIDESTR, len);
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+}
+
+/*
+ * vs_msgsave --
+ * Save a message for later display.
+ */
+static void
+vs_msgsave(sp, mt, p, len)
+ SCR *sp;
+ mtype_t mt;
+ char *p;
+ size_t len;
+{
+ GS *gp;
+ MSGS *mp_c, *mp_n;
+
+ /*
+ * We have to handle messages before we have any place to put them.
+ * If there's no screen support yet, allocate a msg structure, copy
+ * in the message, and queue it on the global structure. If we can't
+ * allocate memory here, we're genuinely screwed, dump the message
+ * to stderr in the (probably) vain hope that someone will see it.
+ */
+ CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
+ MALLOC_GOTO(sp, mp_n->buf, char *, len);
+
+ memmove(mp_n->buf, p, len);
+ mp_n->len = len;
+ mp_n->mtype = mt;
+
+ gp = sp->gp;
+ if ((mp_c = gp->msgq.lh_first) == NULL) {
+ LIST_INSERT_HEAD(&gp->msgq, mp_n, q);
+ } else {
+ for (; mp_c->q.le_next != NULL; mp_c = mp_c->q.le_next);
+ LIST_INSERT_AFTER(mp_c, mp_n, q);
+ }
+ return;
+
+alloc_err:
+ if (mp_n != NULL)
+ free(mp_n);
+ (void)fprintf(stderr, "%.*s\n", (int)len, p);
+}