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

  1. /*-
  2.  * Copyright (c) 1992, 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_refresh.c    10.44 (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 <limits.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26.  
  27. #include "../common/common.h"
  28. #include "vi.h"
  29.  
  30. #define    UPDATE_CURSOR    0x01            /* Update the cursor. */
  31. #define    UPDATE_SCREEN    0x02            /* Flush to screen. */
  32.  
  33. static void    vs_modeline __P((SCR *));
  34. static int    vs_paint __P((SCR *, u_int));
  35.  
  36. /*
  37.  * v_repaint --
  38.  *    Repaint selected lines from the screen.
  39.  *
  40.  * PUBLIC: int vs_repaint __P((SCR *, EVENT *));
  41.  */
  42. int
  43. vs_repaint(sp, evp)
  44.     SCR *sp;
  45.     EVENT *evp;
  46. {
  47.     SMAP *smp;
  48.  
  49.     for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) {
  50.         smp = HMAP + evp->e_flno - 1;
  51.         SMAP_FLUSH(smp);
  52.         if (vs_line(sp, smp, NULL, NULL))
  53.             return (1);
  54.     }
  55.     return (0);
  56. }
  57.  
  58. /*
  59.  * vs_refresh --
  60.  *    Refresh all screens.
  61.  *
  62.  * PUBLIC: int vs_refresh __P((SCR *, int));
  63.  */
  64. int
  65. vs_refresh(sp, forcepaint)
  66.     SCR *sp;
  67.     int forcepaint;
  68. {
  69.     GS *gp;
  70.     SCR *tsp;
  71.     int need_refresh;
  72.     u_int priv_paint, pub_paint;
  73.  
  74.     gp = sp->gp;
  75.  
  76.     /*
  77.      * 1: Refresh the screen.
  78.      *
  79.      * If SC_SCR_REDRAW is set in the current screen, repaint everything
  80.      * that we can find, including status lines.
  81.      */
  82.     if (F_ISSET(sp, SC_SCR_REDRAW))
  83.         for (tsp = gp->dq.cqh_first;
  84.             tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
  85.             if (tsp != sp)
  86.                 F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
  87.  
  88.     /*
  89.      * 2: Related or dirtied screens, or screens with messages.
  90.      *
  91.      * If related screens share a view into a file, they may have been
  92.      * modified as well.  Refresh any screens that aren't exiting that
  93.      * have paint or dirty bits set.  Always update their screens, we
  94.      * are not likely to get another chance.  Finally, if we refresh any
  95.      * screens other than the current one, the cursor will be trashed.
  96.      */
  97.     pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
  98.     priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
  99.     if (O_ISSET(sp, O_NUMBER))
  100.         priv_paint |= VIP_N_RENUMBER;
  101.     for (tsp = gp->dq.cqh_first;
  102.         tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
  103.         if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
  104.             (F_ISSET(tsp, pub_paint) ||
  105.             F_ISSET(VIP(tsp), priv_paint))) {
  106.             (void)vs_paint(tsp,
  107.                 (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
  108.                 UPDATE_CURSOR : 0) | UPDATE_SCREEN);
  109.             F_SET(VIP(sp), VIP_CUR_INVALID);
  110.         }
  111.  
  112.     /*
  113.      * 3: Refresh the current screen.
  114.      *
  115.      * Always refresh the current screen, it may be a cursor movement.
  116.      * Also, always do it last -- that way, SC_SCR_REDRAW can be set
  117.      * in the current screen only, and the screen won't flash.
  118.      */
  119.     if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
  120.         F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
  121.         return (1);
  122.  
  123.     /*
  124.      * 4: Paint any missing status lines.
  125.      *
  126.      * XXX
  127.      * This is fairly evil.  Status lines are written using the vi message
  128.      * mechanism, since we have no idea how long they are.  Since we may be
  129.      * painting screens other than the current one, we don't want to make
  130.      * the user wait.  We depend heavily on there not being any other lines
  131.      * currently waiting to be displayed and the message truncation code in
  132.      * the msgq_status routine working.
  133.      *
  134.      * And, finally, if we updated any status lines, make sure the cursor
  135.      * gets back to where it belongs.
  136.      */
  137.     for (need_refresh = 0, tsp = gp->dq.cqh_first;
  138.         tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
  139.         if (F_ISSET(tsp, SC_STATUS)) {
  140.             need_refresh = 1;
  141.             vs_resolve(tsp, sp, 0);
  142.         }
  143.     if (need_refresh)
  144.         (void)gp->scr_refresh(sp, 0);
  145.  
  146.     /*
  147.      * A side-effect of refreshing the screen is that it's now ready
  148.      * for everything else, i.e. messages.
  149.      */
  150.     F_SET(sp, SC_SCR_VI);
  151.     return (0);
  152. }
  153.  
  154. /*
  155.  * vs_paint --
  156.  *    This is the guts of the vi curses screen code.  The idea is that
  157.  *    the SCR structure passed in contains the new coordinates of the
  158.  *    screen.  What makes this hard is that we don't know how big
  159.  *    characters are, doing input can put the cursor in illegal places,
  160.  *    and we're frantically trying to avoid repainting unless it's
  161.  *    absolutely necessary.  If you change this code, you'd better know
  162.  *    what you're doing.  It's subtle and quick to anger.
  163.  */
  164. static int
  165. vs_paint(sp, flags)
  166.     SCR *sp;
  167.     u_int flags;
  168. {
  169.     GS *gp;
  170.     SMAP *smp, tmp;
  171.     VI_PRIVATE *vip;
  172.     recno_t lastline, lcnt;
  173.     size_t cwtotal, cnt, len, notused, off, y;
  174.     int ch, didpaint, isempty, leftright_warp;
  175.     char *p;
  176.  
  177. #define     LNO    sp->lno            /* Current file line. */
  178. #define    OLNO    vip->olno        /* Remembered file line. */
  179. #define     CNO    sp->cno            /* Current file column. */
  180. #define    OCNO    vip->ocno        /* Remembered file column. */
  181. #define    SCNO    vip->sc_col        /* Current screen column. */
  182.  
  183.     gp = sp->gp;
  184.     vip = VIP(sp);
  185.     didpaint = leftright_warp = 0;
  186.  
  187.     /*
  188.      * 5: Reformat the lines.
  189.      *
  190.      * If the lines themselves have changed (:set list, for example),
  191.      * fill in the map from scratch.  Adjust the screen that's being
  192.      * displayed if the leftright flag is set.
  193.      */
  194.     if (F_ISSET(sp, SC_SCR_REFORMAT)) {
  195.         /* Invalidate the line size cache. */
  196.         VI_SCR_CFLUSH(vip);
  197.  
  198.         /* Toss vs_line() cached information. */
  199.         if (F_ISSET(sp, SC_SCR_TOP)) {
  200.             if (vs_sm_fill(sp, LNO, P_TOP))
  201.                 return (1);
  202.         }
  203.         else if (F_ISSET(sp, SC_SCR_CENTER)) {
  204.             if (vs_sm_fill(sp, LNO, P_MIDDLE))
  205.                 return (1);
  206.         } else
  207.             if (vs_sm_fill(sp, OOBLNO, P_TOP))
  208.                 return (1);
  209.         F_SET(sp, SC_SCR_REDRAW);
  210.     }
  211.  
  212.     /*
  213.      * 6: Line movement.
  214.      *
  215.      * Line changes can cause the top line to change as well.  As
  216.      * before, if the movement is large, the screen is repainted.
  217.      *
  218.      * 6a: Small screens.
  219.      *
  220.      * Users can use the window, w300, w1200 and w9600 options to make
  221.      * the screen artificially small.  The behavior of these options
  222.      * in the historic vi wasn't all that consistent, and, in fact, it
  223.      * was never documented how various screen movements affected the
  224.      * screen size.  Generally, one of three things would happen:
  225.      *    1: The screen would expand in size, showing the line
  226.      *    2: The screen would scroll, showing the line
  227.      *    3: The screen would compress to its smallest size and
  228.      *        repaint.
  229.      * In general, scrolling didn't cause compression (200^D was handled
  230.      * the same as ^D), movement to a specific line would (:N where N
  231.      * was 1 line below the screen caused a screen compress), and cursor
  232.      * movement would scroll if it was 11 lines or less, and compress if
  233.      * it was more than 11 lines.  (And, no, I have no idea where the 11
  234.      * comes from.)
  235.      *
  236.      * What we do is try and figure out if the line is less than half of
  237.      * a full screen away.  If it is, we expand the screen if there's
  238.      * room, and then scroll as necessary.  The alternative is to compress
  239.      * and repaint.
  240.      *
  241.      * !!!
  242.      * This code is a special case from beginning to end.  Unfortunately,
  243.      * home modems are still slow enough that it's worth having.
  244.      *
  245.      * XXX
  246.      * If the line a really long one, i.e. part of the line is on the
  247.      * screen but the column offset is not, we'll end up in the adjust
  248.      * code, when we should probably have compressed the screen.
  249.      */
  250.     if (IS_SMALL(sp))
  251.         if (LNO < HMAP->lno) {
  252.             lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
  253.             if (lcnt <= HALFSCREEN(sp))
  254.                 for (; lcnt && sp->t_rows != sp->t_maxrows;
  255.                      --lcnt, ++sp->t_rows) {
  256.                     ++TMAP;
  257.                     if (vs_sm_1down(sp))
  258.                         return (1);
  259.                 }
  260.             else
  261.                 goto small_fill;
  262.         } else if (LNO > TMAP->lno) {
  263.             lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
  264.             if (lcnt <= HALFSCREEN(sp))
  265.                 for (; lcnt && sp->t_rows != sp->t_maxrows;
  266.                      --lcnt, ++sp->t_rows) {
  267.                     if (vs_sm_next(sp, TMAP, TMAP + 1))
  268.                         return (1);
  269.                     ++TMAP;
  270.                     if (vs_line(sp, TMAP, NULL, NULL))
  271.                         return (1);
  272.                 }
  273.             else {
  274. small_fill:            (void)gp->scr_move(sp, LASTLINE(sp), 0);
  275.                 (void)gp->scr_clrtoeol(sp);
  276.                 for (; sp->t_rows > sp->t_minrows;
  277.                     --sp->t_rows, --TMAP) {
  278.                     (void)gp->scr_move(sp, TMAP - HMAP, 0);
  279.                     (void)gp->scr_clrtoeol(sp);
  280.                 }
  281.                 if (vs_sm_fill(sp, LNO, P_FILL))
  282.                     return (1);
  283.                 F_SET(sp, SC_SCR_REDRAW);
  284.                 goto adjust;
  285.             }
  286.         }
  287.  
  288.     /*
  289.      * 6b: Line down, or current screen.
  290.      */
  291.     if (LNO >= HMAP->lno) {
  292.         /* Current screen. */
  293.         if (LNO <= TMAP->lno)
  294.             goto adjust;
  295.         if (F_ISSET(sp, SC_SCR_TOP))
  296.             goto top;
  297.         if (F_ISSET(sp, SC_SCR_CENTER))
  298.             goto middle;
  299.  
  300.         /*
  301.          * If less than half a screen above the line, scroll down
  302.          * until the line is on the screen.
  303.          */
  304.         lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
  305.         if (lcnt < HALFTEXT(sp)) {
  306.             while (lcnt--)
  307.                 if (vs_sm_1up(sp))
  308.                     return (1);
  309.             goto adjust;
  310.         }
  311.         goto bottom;
  312.     }
  313.  
  314.     /*
  315.      * 6c: If not on the current screen, may request center or top.
  316.      */
  317.     if (F_ISSET(sp, SC_SCR_TOP))
  318.         goto top;
  319.     if (F_ISSET(sp, SC_SCR_CENTER))
  320.         goto middle;
  321.  
  322.     /*
  323.      * 6d: Line up.
  324.      */
  325.     lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
  326.     if (lcnt < HALFTEXT(sp)) {
  327.         /*
  328.          * If less than half a screen below the line, scroll up until
  329.          * the line is the first line on the screen.  Special check so
  330.          * that if the screen has been emptied, we refill it.
  331.          */
  332.         if (db_exist(sp, HMAP->lno)) {
  333.             while (lcnt--)
  334.                 if (vs_sm_1down(sp))
  335.                     return (1);
  336.             goto adjust;
  337.         }
  338.  
  339.         /*
  340.          * If less than a half screen from the bottom of the file,
  341.          * put the last line of the file on the bottom of the screen.
  342.          */
  343. bottom:        if (db_last(sp, &lastline))
  344.             return (1);
  345.         tmp.lno = LNO;
  346.         tmp.coff = HMAP->coff;
  347.         tmp.soff = 1;
  348.         lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows);
  349.         if (lcnt < HALFTEXT(sp)) {
  350.             if (vs_sm_fill(sp, lastline, P_BOTTOM))
  351.                 return (1);
  352.             F_SET(sp, SC_SCR_REDRAW);
  353.             goto adjust;
  354.         }
  355.         /* It's not close, just put the line in the middle. */
  356.         goto middle;
  357.     }
  358.  
  359.     /*
  360.      * If less than half a screen from the top of the file, put the first
  361.      * line of the file at the top of the screen.  Otherwise, put the line
  362.      * in the middle of the screen.
  363.      */
  364.     tmp.lno = 1;
  365.     tmp.coff = HMAP->coff;
  366.     tmp.soff = 1;
  367.     lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
  368.     if (lcnt < HALFTEXT(sp)) {
  369.         if (vs_sm_fill(sp, 1, P_TOP))
  370.             return (1);
  371.     } else
  372. middle:        if (vs_sm_fill(sp, LNO, P_MIDDLE))
  373.             return (1);
  374.     if (0) {
  375. top:        if (vs_sm_fill(sp, LNO, P_TOP))
  376.             return (1);
  377.     }
  378.     F_SET(sp, SC_SCR_REDRAW);
  379.  
  380.     /*
  381.      * At this point we know part of the line is on the screen.  Since
  382.      * scrolling is done using logical lines, not physical, all of the
  383.      * line may not be on the screen.  While that's not necessarily bad,
  384.      * if the part the cursor is on isn't there, we're going to lose.
  385.      * This can be tricky; if the line covers the entire screen, lno
  386.      * may be the same as both ends of the map, that's why we test BOTH
  387.      * the top and the bottom of the map.  This isn't a problem for
  388.      * left-right scrolling, the cursor movement code handles the problem.
  389.      *
  390.      * There's a performance issue here if editing *really* long lines.
  391.      * This gets to the right spot by scrolling, and, in a binary, by
  392.      * scrolling hundreds of lines.  If the adjustment looks like it's
  393.      * going to be a serious problem, refill the screen and repaint.
  394.      */
  395. adjust:    if (!O_ISSET(sp, O_LEFTRIGHT) &&
  396.         (LNO == HMAP->lno || LNO == TMAP->lno)) {
  397.         cnt = vs_screens(sp, LNO, &CNO);
  398.         if (LNO == HMAP->lno && cnt < HMAP->soff)
  399.             if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
  400.                 HMAP->soff = cnt;
  401.                 vs_sm_fill(sp, OOBLNO, P_TOP);
  402.                 F_SET(sp, SC_SCR_REDRAW);
  403.             } else
  404.                 while (cnt < HMAP->soff)
  405.                     if (vs_sm_1down(sp))
  406.                         return (1);
  407.         if (LNO == TMAP->lno && cnt > TMAP->soff)
  408.             if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
  409.                 TMAP->soff = cnt;
  410.                 vs_sm_fill(sp, OOBLNO, P_BOTTOM);
  411.                 F_SET(sp, SC_SCR_REDRAW);
  412.             } else
  413.                 while (cnt > TMAP->soff)
  414.                     if (vs_sm_1up(sp))
  415.                         return (1);
  416.     }
  417.  
  418.     /*
  419.      * If the screen needs to be repainted, skip cursor optimization.
  420.      * However, in the code above we skipped leftright scrolling on
  421.      * the grounds that the cursor code would handle it.  Make sure
  422.      * the right screen is up.
  423.      */
  424.     if (F_ISSET(sp, SC_SCR_REDRAW)) {
  425.         if (O_ISSET(sp, O_LEFTRIGHT))
  426.             goto slow;
  427.         goto paint;
  428.     }
  429.  
  430.     /*
  431.      * 7: Cursor movements (current screen only).
  432.      */
  433.     if (!LF_ISSET(UPDATE_CURSOR))
  434.         goto number;
  435.  
  436.     /*
  437.      * Decide cursor position.  If the line has changed, the cursor has
  438.      * moved over a tab, or don't know where the cursor was, reparse the
  439.      * line.  Otherwise, we've just moved over fixed-width characters,
  440.      * and can calculate the left/right scrolling and cursor movement
  441.      * without reparsing the line.  Note that we don't know which (if any)
  442.      * of the characters between the old and new cursor positions changed.
  443.      *
  444.      * XXX
  445.      * With some work, it should be possible to handle tabs quickly, at
  446.      * least in obvious situations, like moving right and encountering
  447.      * a tab, without reparsing the whole line.
  448.      *
  449.      * If the line we're working with has changed, reread it..
  450.      */
  451.     if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
  452.         goto slow;
  453.  
  454.     /* Otherwise, if nothing's changed, ignore the cursor. */
  455.     if (CNO == OCNO)
  456.         goto fast;
  457.  
  458.     /*
  459.      * Get the current line.  If this fails, we either have an empty
  460.      * file and can just repaint, or there's a real problem.  This
  461.      * isn't a performance issue because there aren't any ways to get
  462.      * here repeatedly.
  463.      */
  464.     if (db_eget(sp, LNO, &p, &len, &isempty)) {
  465.         if (isempty)
  466.             goto slow;
  467.         return (1);
  468.     }
  469.  
  470. #ifdef DEBUG
  471.     /* Sanity checking. */
  472.     if (CNO >= len && len != 0) {
  473.         msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
  474.              tail(__FILE__), __LINE__, CNO, len);
  475.         return (1);
  476.     }
  477. #endif
  478.     /*
  479.      * The basic scheme here is to look at the characters in between
  480.      * the old and new positions and decide how big they are on the
  481.      * screen, and therefore, how many screen positions to move.
  482.      */
  483.     if (CNO < OCNO) {
  484.         /*
  485.          * 7a: Cursor moved left.
  486.          *
  487.          * Point to the old character.  The old cursor position can
  488.          * be past EOL if, for example, we just deleted the rest of
  489.          * the line.  In this case, since we don't know the width of
  490.          * the characters we traversed, we have to do it slowly.
  491.          */
  492.         p += OCNO;
  493.         cnt = (OCNO - CNO) + 1;
  494.         if (OCNO >= len)
  495.             goto slow;
  496.  
  497.         /*
  498.          * Quick sanity check -- it's hard to figure out exactly when
  499.          * we cross a screen boundary as we do in the cursor right
  500.          * movement.  If cnt is so large that we're going to cross the
  501.          * boundary no matter what, stop now.
  502.          */
  503.         if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
  504.             goto slow;
  505.  
  506.         /*
  507.          * Count up the widths of the characters.  If it's a tab
  508.          * character, go do it the the slow way.
  509.          */
  510.         for (cwtotal = 0; cnt--; cwtotal += KEY_LEN(sp, ch))
  511.             if ((ch = *(u_char *)p--) == '\t')
  512.                 goto slow;
  513.  
  514.         /*
  515.          * Decrement the screen cursor by the total width of the
  516.          * characters minus 1.
  517.          */
  518.         cwtotal -= 1;
  519.  
  520.         /*
  521.          * If we're moving left, and there's a wide character in the
  522.          * current position, go to the end of the character.
  523.          */
  524.         if (KEY_LEN(sp, ch) > 1)
  525.             cwtotal -= KEY_LEN(sp, ch) - 1;
  526.  
  527.         /*
  528.          * If the new column moved us off of the current logical line,
  529.          * calculate a new one.  If doing leftright scrolling, we've
  530.          * moved off of the current screen, as well.
  531.          */
  532.         if (SCNO < cwtotal)
  533.             goto slow;
  534.         SCNO -= cwtotal;
  535.     } else {
  536.         /*
  537.          * 7b: Cursor moved right.
  538.          *
  539.          * Point to the first character to the right.
  540.          */
  541.         p += OCNO + 1;
  542.         cnt = CNO - OCNO;
  543.  
  544.         /*
  545.          * Count up the widths of the characters.  If it's a tab
  546.          * character, go do it the the slow way.  If we cross a
  547.          * screen boundary, we can quit.
  548.          */
  549.         for (cwtotal = SCNO; cnt--;) {
  550.             if ((ch = *(u_char *)p++) == '\t')
  551.                 goto slow;
  552.             if ((cwtotal += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp))
  553.                 break;
  554.         }
  555.  
  556.         /*
  557.          * Increment the screen cursor by the total width of the
  558.          * characters.
  559.          */
  560.         SCNO = cwtotal;
  561.  
  562.         /* See screen change comment in section 6a. */
  563.         if (SCNO >= SCREEN_COLS(sp))
  564.             goto slow;
  565.     }
  566.  
  567.     /*
  568.      * 7c: Fast cursor update.
  569.      *
  570.      * We have the current column, retrieve the current row.
  571.      */
  572. fast:    (void)gp->scr_cursor(sp, &y, ¬used);
  573.     goto done_cursor;
  574.  
  575.     /*
  576.      * 7d: Slow cursor update.
  577.      *
  578.      * Walk through the map and find the current line.
  579.      */
  580. slow:    for (smp = HMAP; smp->lno != LNO; ++smp);
  581.  
  582.     /*
  583.      * 7e: Leftright scrolling adjustment.
  584.      *
  585.      * If doing left-right scrolling and the cursor movement has changed
  586.      * the displayed screen, scroll the screen left or right, unless we're
  587.      * updating the info line in which case we just scroll that one line.
  588.      * We adjust the offset up or down until we have a window that covers
  589.      * the current column, making sure that we adjust differently for the
  590.      * first screen as compared to subsequent ones.
  591.      */
  592.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  593.         /*
  594.          * Get the screen column for this character, and correct
  595.          * for the number option offset.
  596.          */
  597.         cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
  598.         if (O_ISSET(sp, O_NUMBER))
  599.             cnt -= O_NUMBER_LENGTH;
  600.  
  601.         /* Adjust the window towards the beginning of the line. */
  602.         off = smp->coff;
  603.         if (off >= cnt) {
  604.             do {
  605.                 if (off >= O_VAL(sp, O_SIDESCROLL))
  606.                     off -= O_VAL(sp, O_SIDESCROLL);
  607.                 else {
  608.                     off = 0;
  609.                     break;
  610.                 }
  611.             } while (off >= cnt);
  612.             goto shifted;
  613.         }
  614.  
  615.         /* Adjust the window towards the end of the line. */
  616.         if (off == 0 && off + SCREEN_COLS(sp) < cnt ||
  617.             off != 0 && off + sp->cols < cnt) {
  618.             do {
  619.                 off += O_VAL(sp, O_SIDESCROLL);
  620.             } while (off + sp->cols < cnt);
  621.  
  622. shifted:        /* Fill in screen map with the new offset. */
  623.             if (F_ISSET(sp, SC_TINPUT_INFO))
  624.                 smp->coff = off;
  625.             else {
  626.                 for (smp = HMAP; smp <= TMAP; ++smp)
  627.                     smp->coff = off;
  628.                 leftright_warp = 1;
  629.             }
  630.             goto paint;
  631.         }
  632.  
  633.         /*
  634.          * We may have jumped here to adjust a leftright screen because
  635.          * redraw was set.  If so, we have to paint the entire screen.
  636.          */
  637.         if (F_ISSET(sp, SC_SCR_REDRAW))
  638.             goto paint;
  639.     }
  640.  
  641.     /*
  642.      * Update the screen lines for this particular file line until we
  643.      * have a new screen cursor position.
  644.      */
  645.     for (y = -1,
  646.         vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
  647.         if (vs_line(sp, smp, &y, &SCNO))
  648.             return (1);
  649.         if (y != -1) {
  650.             vip->sc_smap = smp;
  651.             break;
  652.         }
  653.     }
  654.     goto done_cursor;
  655.  
  656.     /*
  657.      * 8: Repaint the entire screen.
  658.      *
  659.      * Lost big, do what you have to do.  We flush the cache, since
  660.      * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and
  661.      * it's simpler to repaint.  So, don't trust anything that we
  662.      * think we know about it.
  663.      */
  664. paint:    for (smp = HMAP; smp <= TMAP; ++smp)
  665.         SMAP_FLUSH(smp);
  666.     for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
  667.         if (vs_line(sp, smp, &y, &SCNO))
  668.             return (1);
  669.         if (y != -1 && vip->sc_smap == NULL)
  670.             vip->sc_smap = smp;
  671.     }
  672.     /*
  673.      * If it's a small screen and we're redrawing, clear the unused lines,
  674.      * ex may have overwritten them.
  675.      */
  676.     if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
  677.         for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
  678.             (void)gp->scr_move(sp, cnt, 0);
  679.             (void)gp->scr_clrtoeol(sp);
  680.         }
  681.  
  682.     didpaint = 1;
  683.  
  684. done_cursor:
  685.     /*
  686.      * Sanity checking.  When the repainting code messes up, the usual
  687.      * result is we don't repaint the cursor and so sc_smap will be
  688.      * NULL.  If we're debugging, die, otherwise restart from scratch.
  689.      */
  690. #ifdef DEBUG
  691.     if (vip->sc_smap == NULL)
  692.         abort();
  693. #else
  694.     if (vip->sc_smap == NULL) {
  695.         F_SET(sp, SC_SCR_REFORMAT);
  696.         return (vs_paint(sp, flags));
  697.     }
  698. #endif
  699.  
  700.     /*
  701.      * 9: Set the remembered cursor values.
  702.      */
  703.     OCNO = CNO;
  704.     OLNO = LNO;
  705.  
  706.     /*
  707.      * 10: Repaint the line numbers.
  708.      *
  709.      * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we
  710.      * didn't repaint the screen, repaint all of the line numbers,
  711.      * they've changed.
  712.      */
  713. number:    if (O_ISSET(sp, O_NUMBER) &&
  714.         F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
  715.         return (1);
  716.  
  717.     /*
  718.      * 11: Update the mode line, position the cursor, and flush changes.
  719.      *
  720.      * If we warped the screen, we have to refresh everything.
  721.      */
  722.     if (leftright_warp)
  723.         LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
  724.  
  725.     if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
  726.         !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
  727.         vs_modeline(sp);
  728.  
  729.     if (LF_ISSET(UPDATE_CURSOR)) {
  730.         (void)gp->scr_move(sp, y, SCNO);
  731.  
  732.         /*
  733.          * XXX
  734.          * If the screen shifted, we recalculate the "most favorite"
  735.          * cursor position.  Vi won't know that we've warped the
  736.          * screen, so it's going to have a wrong idea about where the
  737.          * cursor should be.  This is vi's problem, and fixing it here
  738.          * is a gross layering violation.
  739.          */
  740.         if (leftright_warp)
  741.             (void)vs_column(sp, &sp->rcm);
  742.     }
  743.  
  744.     if (LF_ISSET(UPDATE_SCREEN))
  745.         (void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
  746.  
  747.     /* 12: Clear the flags that are handled by this routine. */
  748.     F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
  749.     F_CLR(vip, VIP_CUR_INVALID |
  750.         VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
  751.  
  752.     return (0);
  753.  
  754. #undef     LNO
  755. #undef    OLNO
  756. #undef     CNO
  757. #undef    OCNO
  758. #undef    SCNO
  759. }
  760.  
  761. /*
  762.  * vs_modeline --
  763.  *    Update the mode line.
  764.  */
  765. static void
  766. vs_modeline(sp)
  767.     SCR *sp;
  768. {
  769.     static char * const modes[] = {
  770.         "215|Append",            /* SM_APPEND */
  771.         "216|Change",            /* SM_CHANGE */
  772.         "217|Command",            /* SM_COMMAND */
  773.         "218|Insert",            /* SM_INSERT */
  774.         "219|Replace",            /* SM_REPLACE */
  775.     };
  776.     GS *gp;
  777.     size_t cols, curcol, curlen, endpoint, len, midpoint;
  778.     const char *t;
  779.     int ellipsis;
  780.     char *p, buf[20];
  781.  
  782.     gp = sp->gp;
  783.  
  784.     /*
  785.      * We put down the file name, the ruler, the mode and the dirty flag.
  786.      * If there's not enough room, there's not enough room, we don't play
  787.      * any special games.  We try to put the ruler in the middle and the
  788.      * mode and dirty flag at the end.
  789.      *
  790.      * !!!
  791.      * Leave the last character blank, in case it's a really dumb terminal
  792.      * with hardware scroll.  Second, don't paint the last character in the
  793.      * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
  794.      *
  795.      * Move to the last line on the screen.
  796.      */
  797.     (void)gp->scr_move(sp, LASTLINE(sp), 0);
  798.  
  799.     /* If more than one screen in the display, show the file name. */
  800.     curlen = 0;
  801.     if (IS_SPLIT(sp)) {
  802.         for (p = sp->frp->name; *p != '\0'; ++p);
  803.         for (ellipsis = 0, cols = sp->cols / 2; --p > sp->frp->name;) {
  804.             if (*p == '/') {
  805.                 ++p;
  806.                 break;
  807.             }
  808.             if ((curlen += KEY_LEN(sp, *p)) > cols) {
  809.                 ellipsis = 3;
  810.                 curlen +=
  811.                     KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
  812.                 while (curlen > cols) {
  813.                     ++p;
  814.                     curlen -= KEY_LEN(sp, *p);
  815.                 }
  816.                 break;
  817.             }
  818.         }
  819.         if (ellipsis) {
  820.             while (ellipsis--)
  821.                 (void)gp->scr_addstr(sp,
  822.                     KEY_NAME(sp, '.'), KEY_LEN(sp, '.'));
  823.             (void)gp->scr_addstr(sp,
  824.                 KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
  825.         }
  826.         for (; *p != '\0'; ++p)
  827.             (void)gp->scr_addstr(sp,
  828.                 KEY_NAME(sp, *p), KEY_LEN(sp, *p));
  829.     }
  830.  
  831.     /* Clear the rest of the line. */
  832.     (void)gp->scr_clrtoeol(sp);
  833.  
  834.     /*
  835.      * Display the ruler.  If we're not at the midpoint yet, move there.
  836.      * Otherwise, add in two extra spaces.
  837.      *
  838.      * Adjust the current column for the fact that the editor uses it as
  839.      * a zero-based number.
  840.      *
  841.      * XXX
  842.      * Assume that numbers, commas, and spaces only take up a single
  843.      * column on the screen.
  844.      */
  845.     cols = sp->cols - 1;
  846.     if (O_ISSET(sp, O_RULER)) {
  847.         vs_column(sp, &curcol);
  848.         len =
  849.             snprintf(buf, sizeof(buf), "%lu,%lu", sp->lno, curcol + 1);
  850.  
  851.         midpoint = (cols - ((len + 1) / 2)) / 2;
  852.         if (curlen < midpoint) {
  853.             (void)gp->scr_move(sp, LASTLINE(sp), midpoint);
  854.             curlen += len;
  855.         } else if (curlen + 2 + len < cols) {
  856.             (void)gp->scr_addstr(sp, "  ", 2);
  857.             curlen += 2 + len;
  858.         }
  859.         (void)gp->scr_addstr(sp, buf, len);
  860.     }
  861.  
  862.     /*
  863.      * Display the mode and the modified flag, as close to the end of the
  864.      * line as possible, but guaranteeing at least two spaces between the
  865.      * ruler and the modified flag.
  866.      */
  867. #define    MODESIZE    9
  868.     endpoint = cols;
  869.     if (O_ISSET(sp, O_SHOWMODE)) {
  870.         if (F_ISSET(sp->ep, F_MODIFIED))
  871.             --endpoint;
  872.         t = msg_cat(sp, modes[sp->showmode], &len);
  873.         endpoint -= len;
  874.     }
  875.  
  876.     if (endpoint > curlen + 2) {
  877.         (void)gp->scr_move(sp, LASTLINE(sp), endpoint);
  878.         if (O_ISSET(sp, O_SHOWMODE)) {
  879.             if (F_ISSET(sp->ep, F_MODIFIED))
  880.                 (void)gp->scr_addstr(sp,
  881.                     KEY_NAME(sp, '*'), KEY_LEN(sp, '*'));
  882.             (void)gp->scr_addstr(sp, t, len);
  883.         }
  884.     }
  885. }
  886.