home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / vs_msg.c < prev    next >
C/C++ Source or Header  |  1996-10-13  |  22KB  |  928 lines

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