home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / vs_smap.c < prev    next >
C/C++ Source or Header  |  1996-07-12  |  28KB  |  1,261 lines

  1. /*-
  2.  * Copyright (c) 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 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_smap.c    10.25 (Berkeley) 7/12/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 <limits.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25.  
  26. #include "../common/common.h"
  27. #include "vi.h"
  28.  
  29. static int    vs_deleteln __P((SCR *, int));
  30. static int    vs_insertln __P((SCR *, int));
  31. static int    vs_sm_delete __P((SCR *, recno_t));
  32. static int    vs_sm_down __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
  33. static int    vs_sm_erase __P((SCR *));
  34. static int    vs_sm_insert __P((SCR *, recno_t));
  35. static int    vs_sm_reset __P((SCR *, recno_t));
  36. static int    vs_sm_up __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
  37.  
  38. /*
  39.  * vs_change --
  40.  *    Make a change to the screen.
  41.  *
  42.  * PUBLIC: int vs_change __P((SCR *, recno_t, lnop_t));
  43.  */
  44. int
  45. vs_change(sp, lno, op)
  46.     SCR *sp;
  47.     recno_t lno;
  48.     lnop_t op;
  49. {
  50.     VI_PRIVATE *vip;
  51.     SMAP *p;
  52.     size_t cnt, oldy, oldx;
  53.  
  54.     vip = VIP(sp);
  55.  
  56.     /*
  57.      * XXX
  58.      * Very nasty special case.  The historic vi code displays a single
  59.      * space (or a '$' if the list option is set) for the first line in
  60.      * an "empty" file.  If we "insert" a line, that line gets scrolled
  61.      * down, not repainted, so it's incorrect when we refresh the screen.
  62.      * The vi text input functions detect it explicitly and don't insert
  63.      * a new line.
  64.      *
  65.      * Check for line #2 before going to the end of the file.
  66.      */
  67.     if ((op == LINE_APPEND && lno == 0 || op == LINE_INSERT && lno == 1) &&
  68.         !db_exist(sp, 2)) {
  69.         lno = 1;
  70.         op = LINE_RESET;
  71.     }
  72.  
  73.     /* Appending is the same as inserting, if the line is incremented. */
  74.     if (op == LINE_APPEND) {
  75.         ++lno;
  76.         op = LINE_INSERT;
  77.     }
  78.  
  79.     /* Ignore the change if the line is after the map. */
  80.     if (lno > TMAP->lno)
  81.         return (0);
  82.  
  83.     /*
  84.      * If the line is before the map, and it's a decrement, decrement
  85.      * the map.  If it's an increment, increment the map.  Otherwise,
  86.      * ignore it.
  87.      */
  88.     if (lno < HMAP->lno) {
  89.         switch (op) {
  90.         case LINE_APPEND:
  91.             abort();
  92.             /* NOTREACHED */
  93.         case LINE_DELETE:
  94.             for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
  95.                 --p->lno;
  96.             if (sp->lno >= lno)
  97.                 --sp->lno;
  98.             F_SET(vip, VIP_N_RENUMBER);
  99.             break;
  100.         case LINE_INSERT:
  101.             for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
  102.                 ++p->lno;
  103.             if (sp->lno >= lno)
  104.                 ++sp->lno;
  105.             F_SET(vip, VIP_N_RENUMBER);
  106.             break;
  107.         case LINE_RESET:
  108.             break;
  109.         }
  110.         return (0);
  111.     }
  112.  
  113.     F_SET(vip, VIP_N_REFRESH);
  114.  
  115.     /*
  116.      * Invalidate the line size cache, and invalidate the cursor if it's
  117.      * on this line,
  118.      */
  119.     VI_SCR_CFLUSH(vip);
  120.     if (sp->lno == lno)
  121.         F_SET(vip, VIP_CUR_INVALID);
  122.  
  123.     /*
  124.      * If ex modifies the screen after ex output is already on the screen
  125.      * or if we've switched into ex canonical mode, don't touch it -- we'll
  126.      * get scrolling wrong, at best.
  127.      */
  128.     if (!F_ISSET(sp, SC_TINPUT_INFO) &&
  129.         (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
  130.         F_SET(vip, VIP_N_EX_REDRAW);
  131.         return (0);
  132.     }
  133.  
  134.     /* Save and restore the cursor for these routines. */
  135.     (void)sp->gp->scr_cursor(sp, &oldy, &oldx);
  136.  
  137.     switch (op) {
  138.     case LINE_DELETE:
  139.         if (vs_sm_delete(sp, lno))
  140.             return (1);
  141.         F_SET(vip, VIP_N_RENUMBER);
  142.         break;
  143.     case LINE_INSERT:
  144.         if (vs_sm_insert(sp, lno))
  145.             return (1);
  146.         F_SET(vip, VIP_N_RENUMBER);
  147.         break;
  148.     case LINE_RESET:
  149.         if (vs_sm_reset(sp, lno))
  150.             return (1);
  151.         break;
  152.     default:
  153.         abort();
  154.     }
  155.  
  156.     (void)sp->gp->scr_move(sp, oldy, oldx);
  157.     return (0);
  158. }
  159.  
  160. /*
  161.  * vs_sm_fill --
  162.  *    Fill in the screen map, placing the specified line at the
  163.  *    right position.  There isn't any way to tell if an SMAP
  164.  *    entry has been filled in, so this routine had better be
  165.  *    called with P_FILL set before anything else is done.
  166.  *
  167.  * !!!
  168.  * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
  169.  * slot is already filled in, P_BOTTOM means that the TMAP slot is
  170.  * already filled in, and we just finish up the job.
  171.  *
  172.  * PUBLIC: int vs_sm_fill __P((SCR *, recno_t, pos_t));
  173.  */
  174. int
  175. vs_sm_fill(sp, lno, pos)
  176.     SCR *sp;
  177.     recno_t lno;
  178.     pos_t pos;
  179. {
  180.     SMAP *p, tmp;
  181.     size_t cnt;
  182.  
  183.     /* Flush all cached information from the SMAP. */
  184.     for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
  185.         SMAP_FLUSH(p);
  186.  
  187.     /*
  188.      * If the map is filled, the screen must be redrawn.
  189.      *
  190.      * XXX
  191.      * This is a bug.  We should try and figure out if the desired line
  192.      * is already in the map or close by -- scrolling the screen would
  193.      * be a lot better than redrawing.
  194.      */
  195.     F_SET(sp, SC_SCR_REDRAW);
  196.  
  197.     switch (pos) {
  198.     case P_FILL:
  199.         tmp.lno = 1;
  200.         tmp.coff = 0;
  201.         tmp.soff = 1;
  202.  
  203.         /* See if less than half a screen from the top. */
  204.         if (vs_sm_nlines(sp,
  205.             &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
  206.             lno = 1;
  207.             goto top;
  208.         }
  209.  
  210.         /* See if less than half a screen from the bottom. */
  211.         if (db_last(sp, &tmp.lno))
  212.             return (1);
  213.         tmp.coff = 0;
  214.         tmp.soff = vs_screens(sp, tmp.lno, NULL);
  215.         if (vs_sm_nlines(sp,
  216.             &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
  217.             TMAP->lno = tmp.lno;
  218.             TMAP->coff = tmp.coff;
  219.             TMAP->soff = tmp.soff;
  220.             goto bottom;
  221.         }
  222.         goto middle;
  223.     case P_TOP:
  224.         if (lno != OOBLNO) {
  225. top:            HMAP->lno = lno;
  226.             HMAP->coff = 0;
  227.             HMAP->soff = 1;
  228.         }
  229.         /* If we fail, just punt. */
  230.         for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
  231.             if (vs_sm_next(sp, p, p + 1))
  232.                 goto err;
  233.         break;
  234.     case P_MIDDLE:
  235.         /* If we fail, guess that the file is too small. */
  236. middle:        p = HMAP + sp->t_rows / 2;
  237.         p->lno = lno;
  238.         p->coff = 0;
  239.         p->soff = 1;
  240.         for (; p > HMAP; --p)
  241.             if (vs_sm_prev(sp, p, p - 1)) {
  242.                 lno = 1;
  243.                 goto top;
  244.             }
  245.  
  246.         /* If we fail, just punt. */
  247.         p = HMAP + sp->t_rows / 2;
  248.         for (; p < TMAP; ++p)
  249.             if (vs_sm_next(sp, p, p + 1))
  250.                 goto err;
  251.         break;
  252.     case P_BOTTOM:
  253.         if (lno != OOBLNO) {
  254.             TMAP->lno = lno;
  255.             TMAP->coff = 0;
  256.             TMAP->soff = vs_screens(sp, lno, NULL);
  257.         }
  258.         /* If we fail, guess that the file is too small. */
  259. bottom:        for (p = TMAP; p > HMAP; --p)
  260.             if (vs_sm_prev(sp, p, p - 1)) {
  261.                 lno = 1;
  262.                 goto top;
  263.             }
  264.         break;
  265.     default:
  266.         abort();
  267.     }
  268.     return (0);
  269.  
  270.     /*
  271.      * Try and put *something* on the screen.  If this fails, we have a
  272.      * serious hard error.
  273.      */
  274. err:    HMAP->lno = 1;
  275.     HMAP->coff = 0;
  276.     HMAP->soff = 1;
  277.     for (p = HMAP; p < TMAP; ++p)
  278.         if (vs_sm_next(sp, p, p + 1))
  279.             return (1);
  280.     return (0);
  281. }
  282.  
  283. /*
  284.  * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
  285.  * screen contains only a single line (whether because the screen is small
  286.  * or the line large), it gets fairly exciting.  Skip the fun, set a flag
  287.  * so the screen map is refilled and the screen redrawn, and return.  This
  288.  * is amazingly slow, but it's not clear that anyone will care.
  289.  */
  290. #define    HANDLE_WEIRDNESS(cnt) {                        \
  291.     if (cnt >= sp->t_rows) {                    \
  292.         F_SET(sp, SC_SCR_REFORMAT);                \
  293.         return (0);                        \
  294.     }                                \
  295. }
  296.  
  297. /*
  298.  * vs_sm_delete --
  299.  *    Delete a line out of the SMAP.
  300.  */
  301. static int
  302. vs_sm_delete(sp, lno)
  303.     SCR *sp;
  304.     recno_t lno;
  305. {
  306.     SMAP *p, *t;
  307.     size_t cnt_orig;
  308.  
  309.     /*
  310.      * Find the line in the map, and count the number of screen lines
  311.      * which display any part of the deleted line.
  312.      */
  313.     for (p = HMAP; p->lno != lno; ++p);
  314.     if (O_ISSET(sp, O_LEFTRIGHT))
  315.         cnt_orig = 1;
  316.     else
  317.         for (cnt_orig = 1, t = p + 1;
  318.             t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
  319.  
  320.     HANDLE_WEIRDNESS(cnt_orig);
  321.  
  322.     /* Delete that many lines from the screen. */
  323.     (void)sp->gp->scr_move(sp, p - HMAP, 0);
  324.     if (vs_deleteln(sp, cnt_orig))
  325.         return (1);
  326.  
  327.     /* Shift the screen map up. */
  328.     memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
  329.  
  330.     /* Decrement the line numbers for the rest of the map. */
  331.     for (t = TMAP - cnt_orig; p <= t; ++p)
  332.         --p->lno;
  333.  
  334.     /* Display the new lines. */
  335.     for (p = TMAP - cnt_orig;;) {
  336.         if (p < TMAP && vs_sm_next(sp, p, p + 1))
  337.             return (1);
  338.         /* vs_sm_next() flushed the cache. */
  339.         if (vs_line(sp, ++p, NULL, NULL))
  340.             return (1);
  341.         if (p == TMAP)
  342.             break;
  343.     }
  344.     return (0);
  345. }
  346.  
  347. /*
  348.  * vs_sm_insert --
  349.  *    Insert a line into the SMAP.
  350.  */
  351. static int
  352. vs_sm_insert(sp, lno)
  353.     SCR *sp;
  354.     recno_t lno;
  355. {
  356.     SMAP *p, *t;
  357.     size_t cnt_orig, cnt, coff;
  358.  
  359.     /* Save the offset. */
  360.     coff = HMAP->coff;
  361.  
  362.     /*
  363.      * Find the line in the map, find out how many screen lines
  364.      * needed to display the line.
  365.      */
  366.     for (p = HMAP; p->lno != lno; ++p);
  367.  
  368.     cnt_orig = vs_screens(sp, lno, NULL);
  369.     HANDLE_WEIRDNESS(cnt_orig);
  370.  
  371.     /*
  372.      * The lines left in the screen override the number of screen
  373.      * lines in the inserted line.
  374.      */
  375.     cnt = (TMAP - p) + 1;
  376.     if (cnt_orig > cnt)
  377.         cnt_orig = cnt;
  378.  
  379.     /* Push down that many lines. */
  380.     (void)sp->gp->scr_move(sp, p - HMAP, 0);
  381.     if (vs_insertln(sp, cnt_orig))
  382.         return (1);
  383.  
  384.     /* Shift the screen map down. */
  385.     memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
  386.  
  387.     /* Increment the line numbers for the rest of the map. */
  388.     for (t = p + cnt_orig; t <= TMAP; ++t)
  389.         ++t->lno;
  390.  
  391.     /* Fill in the SMAP for the new lines, and display. */
  392.     for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
  393.         t->lno = lno;
  394.         t->coff = coff;
  395.         t->soff = cnt;
  396.         SMAP_FLUSH(t);
  397.         if (vs_line(sp, t, NULL, NULL))
  398.             return (1);
  399.     }
  400.     return (0);
  401. }
  402.  
  403. /*
  404.  * vs_sm_reset --
  405.  *    Reset a line in the SMAP.
  406.  */
  407. static int
  408. vs_sm_reset(sp, lno)
  409.     SCR *sp;
  410.     recno_t lno;
  411. {
  412.     SMAP *p, *t;
  413.     size_t cnt_orig, cnt_new, cnt, diff;
  414.  
  415.     /*
  416.      * See if the number of on-screen rows taken up by the old display
  417.      * for the line is the same as the number needed for the new one.
  418.      * If so, repaint, otherwise do it the hard way.
  419.      */
  420.     for (p = HMAP; p->lno != lno; ++p);
  421.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  422.         t = p;
  423.         cnt_orig = cnt_new = 1;
  424.     } else {
  425.         for (cnt_orig = 0,
  426.             t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
  427.         cnt_new = vs_screens(sp, lno, NULL);
  428.     }
  429.  
  430.     HANDLE_WEIRDNESS(cnt_orig);
  431.  
  432.     if (cnt_orig == cnt_new) {
  433.         do {
  434.             SMAP_FLUSH(p);
  435.             if (vs_line(sp, p, NULL, NULL))
  436.                 return (1);
  437.         } while (++p < t);
  438.         return (0);
  439.     }
  440.  
  441.     if (cnt_orig < cnt_new) {
  442.         /* Get the difference. */
  443.         diff = cnt_new - cnt_orig;
  444.  
  445.         /*
  446.          * The lines left in the screen override the number of screen
  447.          * lines in the inserted line.
  448.          */
  449.         cnt = (TMAP - p) + 1;
  450.         if (diff > cnt)
  451.             diff = cnt;
  452.  
  453.         /* If there are any following lines, push them down. */
  454.         if (cnt > 1) {
  455.             (void)sp->gp->scr_move(sp, p - HMAP, 0);
  456.             if (vs_insertln(sp, diff))
  457.                 return (1);
  458.  
  459.             /* Shift the screen map down. */
  460.             memmove(p + diff, p,
  461.                 (((TMAP - p) - diff) + 1) * sizeof(SMAP));
  462.         }
  463.  
  464.         /* Fill in the SMAP for the replaced line, and display. */
  465.         for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
  466.             t->lno = lno;
  467.             t->soff = cnt;
  468.             SMAP_FLUSH(t);
  469.             if (vs_line(sp, t, NULL, NULL))
  470.                 return (1);
  471.         }
  472.     } else {
  473.         /* Get the difference. */
  474.         diff = cnt_orig - cnt_new;
  475.  
  476.         /* Delete that many lines from the screen. */
  477.         (void)sp->gp->scr_move(sp, p - HMAP, 0);
  478.         if (vs_deleteln(sp, diff))
  479.             return (1);
  480.  
  481.         /* Shift the screen map up. */
  482.         memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
  483.  
  484.         /* Fill in the SMAP for the replaced line, and display. */
  485.         for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
  486.             t->lno = lno;
  487.             t->soff = cnt;
  488.             SMAP_FLUSH(t);
  489.             if (vs_line(sp, t, NULL, NULL))
  490.                 return (1);
  491.         }
  492.  
  493.         /* Display the new lines at the bottom of the screen. */
  494.         for (t = TMAP - diff;;) {
  495.             if (t < TMAP && vs_sm_next(sp, t, t + 1))
  496.                 return (1);
  497.             /* vs_sm_next() flushed the cache. */
  498.             if (vs_line(sp, ++t, NULL, NULL))
  499.                 return (1);
  500.             if (t == TMAP)
  501.                 break;
  502.         }
  503.     }
  504.     return (0);
  505. }
  506.  
  507. /*
  508.  * vs_sm_scroll
  509.  *    Scroll the SMAP up/down count logical lines.  Different
  510.  *    semantics based on the vi command, *sigh*.
  511.  *
  512.  * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t));
  513.  */
  514. int
  515. vs_sm_scroll(sp, rp, count, scmd)
  516.     SCR *sp;
  517.     MARK *rp;
  518.     recno_t count;
  519.     scroll_t scmd;
  520. {
  521.     SMAP *smp;
  522.  
  523.     /*
  524.      * Invalidate the cursor.  The line is probably going to change,
  525.      * (although for ^E and ^Y it may not).  In any case, the scroll
  526.      * routines move the cursor to draw things.
  527.      */
  528.     F_SET(VIP(sp), VIP_CUR_INVALID);
  529.  
  530.     /* Find the cursor in the screen. */
  531.     if (vs_sm_cursor(sp, &smp))
  532.         return (1);
  533.  
  534.     switch (scmd) {
  535.     case CNTRL_B:
  536.     case CNTRL_U:
  537.     case CNTRL_Y:
  538.     case Z_CARAT:
  539.         if (vs_sm_down(sp, rp, count, scmd, smp))
  540.             return (1);
  541.         break;
  542.     case CNTRL_D:
  543.     case CNTRL_E:
  544.     case CNTRL_F:
  545.     case Z_PLUS:
  546.         if (vs_sm_up(sp, rp, count, scmd, smp))
  547.             return (1);
  548.         break;
  549.     default:
  550.         abort();
  551.     }
  552.  
  553.     /*
  554.      * !!!
  555.      * If we're at the start of a line, go for the first non-blank.
  556.      * This makes it look like the old vi, even though we're moving
  557.      * around by logical lines, not physical ones.
  558.      *
  559.      * XXX
  560.      * In the presence of a long line, which has more than a screen
  561.      * width of leading spaces, this code can cause a cursor warp.
  562.      * Live with it.
  563.      */
  564.     if (scmd != CNTRL_E && scmd != CNTRL_Y &&
  565.         rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
  566.         return (1);
  567.  
  568.     return (0);
  569. }
  570.  
  571. /*
  572.  * vs_sm_up --
  573.  *    Scroll the SMAP up count logical lines.
  574.  */
  575. static int
  576. vs_sm_up(sp, rp, count, scmd, smp)
  577.     SCR *sp;
  578.     MARK *rp;
  579.     scroll_t scmd;
  580.     recno_t count;
  581.     SMAP *smp;
  582. {
  583.     int cursor_set, echanged, zset;
  584.     SMAP *ssmp, s1, s2;
  585.  
  586.     /*
  587.      * Check to see if movement is possible.
  588.      *
  589.      * Get the line after the map.  If that line is a new one (and if
  590.      * O_LEFTRIGHT option is set, this has to be true), and the next
  591.      * line doesn't exist, and the cursor doesn't move, or the cursor
  592.      * isn't even on the screen, or the cursor is already at the last
  593.      * line in the map, it's an error.  If that test succeeded because
  594.      * the cursor wasn't at the end of the map, test to see if the map
  595.      * is mostly empty.
  596.      */
  597.     if (vs_sm_next(sp, TMAP, &s1))
  598.         return (1);
  599.     if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
  600.         if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
  601.             v_eof(sp, NULL);
  602.             return (1);
  603.         }
  604.         if (vs_sm_next(sp, smp, &s1))
  605.             return (1);
  606.         if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
  607.             v_eof(sp, NULL);
  608.             return (1);
  609.         }
  610.     }
  611.  
  612.     /*
  613.      * Small screens: see vs_refresh.c section 6a.
  614.      *
  615.      * If it's a small screen, and the movement isn't larger than a
  616.      * screen, i.e some context will remain, open up the screen and
  617.      * display by scrolling.  In this case, the cursor moves down one
  618.      * line for each line displayed.  Otherwise, erase/compress and
  619.      * repaint, and move the cursor to the first line in the screen.
  620.      * Note, the ^F command is always in the latter case, for historical
  621.      * reasons.
  622.      */
  623.     cursor_set = 0;
  624.     if (IS_SMALL(sp)) {
  625.         if (count >= sp->t_maxrows || scmd == CNTRL_F) {
  626.             s1 = TMAP[0];
  627.             if (vs_sm_erase(sp))
  628.                 return (1);
  629.             for (; count--; s1 = s2) {
  630.                 if (vs_sm_next(sp, &s1, &s2))
  631.                     return (1);
  632.                 if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
  633.                     break;
  634.             }
  635.             TMAP[0] = s2;
  636.             if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
  637.                 return (1);
  638.             return (vs_sm_position(sp, rp, 0, P_TOP));
  639.         }
  640.         cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
  641.         for (; count &&
  642.             sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
  643.             if (vs_sm_next(sp, TMAP, &s1))
  644.                 return (1);
  645.             if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
  646.                 break;
  647.             *++TMAP = s1;
  648.             /* vs_sm_next() flushed the cache. */
  649.             if (vs_line(sp, TMAP, NULL, NULL))
  650.                 return (1);
  651.  
  652.             if (!cursor_set)
  653.                 ++ssmp;
  654.         }
  655.         if (!cursor_set) {
  656.             rp->lno = ssmp->lno;
  657.             rp->cno = ssmp->c_sboff;
  658.         }
  659.         if (count == 0)
  660.             return (0);
  661.     }
  662.  
  663.     for (echanged = zset = 0; count; --count) {
  664.         /* Decide what would show up on the screen. */
  665.         if (vs_sm_next(sp, TMAP, &s1))
  666.             return (1);
  667.  
  668.         /* If the line doesn't exist, we're done. */
  669.         if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
  670.             break;
  671.  
  672.         /* Scroll the screen cursor up one logical line. */
  673.         if (vs_sm_1up(sp))
  674.             return (1);
  675.         switch (scmd) {
  676.         case CNTRL_E:
  677.             if (smp > HMAP)
  678.                 --smp;
  679.             else
  680.                 echanged = 1;
  681.             break;
  682.         case Z_PLUS:
  683.             if (zset) {
  684.                 if (smp > HMAP)
  685.                     --smp;
  686.             } else {
  687.                 smp = TMAP;
  688.                 zset = 1;
  689.             }
  690.             /* FALLTHROUGH */
  691.         default:
  692.             break;
  693.         }
  694.     }
  695.  
  696.     if (cursor_set)
  697.         return(0);
  698.  
  699.     switch (scmd) {
  700.     case CNTRL_E:
  701.         /*
  702.          * On a ^E that was forced to change lines, try and keep the
  703.          * cursor as close as possible to the last position, but also
  704.          * set it up so that the next "real" movement will return the
  705.          * cursor to the closest position to the last real movement.
  706.          */
  707.         if (echanged) {
  708.             rp->lno = smp->lno;
  709.             rp->cno = vs_colpos(sp, smp->lno,
  710.                 (O_ISSET(sp, O_LEFTRIGHT) ? 
  711.                 smp->coff : (smp->soff - 1) * sp->cols) +
  712.                 sp->rcm % sp->cols);
  713.         }
  714.         return (0);
  715.     case CNTRL_F:
  716.         /*
  717.          * If there are more lines, the ^F command is positioned at
  718.          * the first line of the screen.
  719.          */
  720.         if (!count) {
  721.             smp = HMAP;
  722.             break;
  723.         }
  724.         /* FALLTHROUGH */
  725.     case CNTRL_D:
  726.         /*
  727.          * The ^D and ^F commands move the cursor towards EOF
  728.          * if there are more lines to move.  Check to be sure
  729.          * the lines actually exist.  (They may not if the
  730.          * file is smaller than the screen.)
  731.          */
  732.         for (; count; --count, ++smp)
  733.             if (smp == TMAP || !db_exist(sp, smp[1].lno))
  734.                 break;
  735.         break;
  736.     case Z_PLUS:
  737.          /* The z+ command moves the cursor to the first new line. */
  738.         break;
  739.     default:
  740.         abort();
  741.     }
  742.  
  743.     if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
  744.         return (1);
  745.     rp->lno = smp->lno;
  746.     rp->cno = smp->c_sboff;
  747.     return (0);
  748. }
  749.  
  750. /*
  751.  * vs_sm_1up --
  752.  *    Scroll the SMAP up one.
  753.  *
  754.  * PUBLIC: int vs_sm_1up __P((SCR *));
  755.  */
  756. int
  757. vs_sm_1up(sp)
  758.     SCR *sp;
  759. {
  760.     /*
  761.      * Delete the top line of the screen.  Shift the screen map
  762.      * up and display a new line at the bottom of the screen.
  763.      */
  764.     (void)sp->gp->scr_move(sp, 0, 0);
  765.     if (vs_deleteln(sp, 1))
  766.         return (1);
  767.  
  768.     /* One-line screens can fail. */
  769.     if (IS_ONELINE(sp)) {
  770.         if (vs_sm_next(sp, TMAP, TMAP))
  771.             return (1);
  772.     } else {
  773.         memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
  774.         if (vs_sm_next(sp, TMAP - 1, TMAP))
  775.             return (1);
  776.     }
  777.     /* vs_sm_next() flushed the cache. */
  778.     return (vs_line(sp, TMAP, NULL, NULL));
  779. }
  780.  
  781. /*
  782.  * vs_deleteln --
  783.  *    Delete a line a la curses, make sure to put the information
  784.  *    line and other screens back.
  785.  */
  786. static int
  787. vs_deleteln(sp, cnt)
  788.     SCR *sp;
  789.     int cnt;
  790. {
  791.     GS *gp;
  792.     size_t oldy, oldx;
  793.  
  794.     gp = sp->gp;
  795.     if (IS_ONELINE(sp))
  796.         (void)gp->scr_clrtoeol(sp);
  797.     else {
  798.         (void)gp->scr_cursor(sp, &oldy, &oldx);
  799.         while (cnt--) {
  800.             (void)gp->scr_deleteln(sp);
  801.             (void)gp->scr_move(sp, LASTLINE(sp), 0);
  802.             (void)gp->scr_insertln(sp);
  803.             (void)gp->scr_move(sp, oldy, oldx);
  804.         }
  805.     }
  806.     return (0);
  807. }
  808.  
  809. /*
  810.  * vs_sm_down --
  811.  *    Scroll the SMAP down count logical lines.
  812.  */
  813. static int
  814. vs_sm_down(sp, rp, count, scmd, smp)
  815.     SCR *sp;
  816.     MARK *rp;
  817.     recno_t count;
  818.     SMAP *smp;
  819.     scroll_t scmd;
  820. {
  821.     SMAP *ssmp, s1, s2;
  822.     int cursor_set, ychanged, zset;
  823.  
  824.     /* Check to see if movement is possible. */
  825.     if (HMAP->lno == 1 &&
  826.         (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
  827.         (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
  828.         v_sof(sp, NULL);
  829.         return (1);
  830.     }
  831.  
  832.     /*
  833.      * Small screens: see vs_refresh.c section 6a.
  834.      *
  835.      * If it's a small screen, and the movement isn't larger than a
  836.      * screen, i.e some context will remain, open up the screen and
  837.      * display by scrolling.  In this case, the cursor moves up one
  838.      * line for each line displayed.  Otherwise, erase/compress and
  839.      * repaint, and move the cursor to the first line in the screen.
  840.      * Note, the ^B command is always in the latter case, for historical
  841.      * reasons.
  842.      */
  843.     cursor_set = scmd == CNTRL_Y;
  844.     if (IS_SMALL(sp)) {
  845.         if (count >= sp->t_maxrows || scmd == CNTRL_B) {
  846.             s1 = HMAP[0];
  847.             if (vs_sm_erase(sp))
  848.                 return (1);
  849.             for (; count--; s1 = s2) {
  850.                 if (vs_sm_prev(sp, &s1, &s2))
  851.                     return (1);
  852.                 if (s2.lno == 1 &&
  853.                     (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
  854.                     break;
  855.             }
  856.             HMAP[0] = s2;
  857.             if (vs_sm_fill(sp, OOBLNO, P_TOP))
  858.                 return (1);
  859.             return (vs_sm_position(sp, rp, 0, P_BOTTOM));
  860.         }
  861.         cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
  862.         for (; count &&
  863.             sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
  864.             if (HMAP->lno == 1 &&
  865.                 (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
  866.                 break;
  867.             ++TMAP;
  868.             if (vs_sm_1down(sp))
  869.                 return (1);
  870.         }
  871.         if (!cursor_set) {
  872.             rp->lno = ssmp->lno;
  873.             rp->cno = ssmp->c_sboff;
  874.         }
  875.         if (count == 0)
  876.             return (0);
  877.     }
  878.  
  879.     for (ychanged = zset = 0; count; --count) {
  880.         /* If the line doesn't exist, we're done. */
  881.         if (HMAP->lno == 1 &&
  882.             (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
  883.             break;
  884.  
  885.         /* Scroll the screen and cursor down one logical line. */
  886.         if (vs_sm_1down(sp))
  887.             return (1);
  888.         switch (scmd) {
  889.         case CNTRL_Y:
  890.             if (smp < TMAP)
  891.                 ++smp;
  892.             else
  893.                 ychanged = 1;
  894.             break;
  895.         case Z_CARAT:
  896.             if (zset) {
  897.                 if (smp < TMAP)
  898.                     ++smp;
  899.             } else {
  900.                 smp = HMAP;
  901.                 zset = 1;
  902.             }
  903.             /* FALLTHROUGH */
  904.         default:
  905.             break;
  906.         }
  907.     }
  908.  
  909.     if (scmd != CNTRL_Y && cursor_set)
  910.         return(0);
  911.  
  912.     switch (scmd) {
  913.     case CNTRL_B:
  914.         /*
  915.          * If there are more lines, the ^B command is positioned at
  916.          * the last line of the screen.  However, the line may not
  917.          * exist.
  918.          */
  919.         if (!count) {
  920.             for (smp = TMAP; smp > HMAP; --smp)
  921.                 if (db_exist(sp, smp->lno))
  922.                     break;
  923.             break;
  924.         }
  925.         /* FALLTHROUGH */
  926.     case CNTRL_U:
  927.         /*
  928.          * The ^B and ^U commands move the cursor towards SOF
  929.          * if there are more lines to move.
  930.          */
  931.         if (count < smp - HMAP)
  932.             smp -= count;
  933.         else
  934.             smp = HMAP;
  935.         break;
  936.     case CNTRL_Y:
  937.         /*
  938.          * On a ^Y that was forced to change lines, try and keep the
  939.          * cursor as close as possible to the last position, but also
  940.          * set it up so that the next "real" movement will return the
  941.          * cursor to the closest position to the last real movement.
  942.          */
  943.         if (ychanged) {
  944.             rp->lno = smp->lno;
  945.             rp->cno = vs_colpos(sp, smp->lno,
  946.                 (O_ISSET(sp, O_LEFTRIGHT) ? 
  947.                 smp->coff : (smp->soff - 1) * sp->cols) +
  948.                 sp->rcm % sp->cols);
  949.         }
  950.         return (0);
  951.     case Z_CARAT:
  952.          /* The z^ command moves the cursor to the first new line. */
  953.         break;
  954.     default:
  955.         abort();
  956.     }
  957.  
  958.     if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
  959.         return (1);
  960.     rp->lno = smp->lno;
  961.     rp->cno = smp->c_sboff;
  962.     return (0);
  963. }
  964.  
  965. /*
  966.  * vs_sm_erase --
  967.  *    Erase the small screen area for the scrolling functions.
  968.  */
  969. static int
  970. vs_sm_erase(sp)
  971.     SCR *sp;
  972. {
  973.     GS *gp;
  974.  
  975.     gp = sp->gp;
  976.     (void)gp->scr_move(sp, LASTLINE(sp), 0);
  977.     (void)gp->scr_clrtoeol(sp);
  978.     for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
  979.         (void)gp->scr_move(sp, TMAP - HMAP, 0);
  980.         (void)gp->scr_clrtoeol(sp);
  981.     }
  982.     return (0);
  983. }
  984.  
  985. /*
  986.  * vs_sm_1down --
  987.  *    Scroll the SMAP down one.
  988.  *
  989.  * PUBLIC: int vs_sm_1down __P((SCR *));
  990.  */
  991. int
  992. vs_sm_1down(sp)
  993.     SCR *sp;
  994. {
  995.     /*
  996.      * Insert a line at the top of the screen.  Shift the screen map
  997.      * down and display a new line at the top of the screen.
  998.      */
  999.     (void)sp->gp->scr_move(sp, 0, 0);
  1000.     if (vs_insertln(sp, 1))
  1001.         return (1);
  1002.  
  1003.     /* One-line screens can fail. */
  1004.     if (IS_ONELINE(sp)) {
  1005.         if (vs_sm_prev(sp, HMAP, HMAP))
  1006.             return (1);
  1007.     } else {
  1008.         memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
  1009.         if (vs_sm_prev(sp, HMAP + 1, HMAP))
  1010.             return (1);
  1011.     }
  1012.     /* vs_sm_prev() flushed the cache. */
  1013.     return (vs_line(sp, HMAP, NULL, NULL));
  1014. }
  1015.  
  1016. /*
  1017.  * vs_insertln --
  1018.  *    Insert a line a la curses, make sure to put the information
  1019.  *    line and other screens back.
  1020.  */
  1021. static int
  1022. vs_insertln(sp, cnt)
  1023.     SCR *sp;
  1024.     int cnt;
  1025. {
  1026.     GS *gp;
  1027.     size_t oldy, oldx;
  1028.  
  1029.     gp = sp->gp;
  1030.     if (IS_ONELINE(sp)) {
  1031.         (void)gp->scr_move(sp, LASTLINE(sp), 0);
  1032.         (void)gp->scr_clrtoeol(sp);
  1033.     } else {
  1034.         (void)gp->scr_cursor(sp, &oldy, &oldx);
  1035.         while (cnt--) {
  1036.             (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
  1037.             (void)gp->scr_deleteln(sp);
  1038.             (void)gp->scr_move(sp, oldy, oldx);
  1039.             (void)gp->scr_insertln(sp);
  1040.         }
  1041.     }
  1042.     return (0);
  1043. }
  1044.  
  1045. /*
  1046.  * vs_sm_next --
  1047.  *    Fill in the next entry in the SMAP.
  1048.  *
  1049.  * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *));
  1050.  */
  1051. int
  1052. vs_sm_next(sp, p, t)
  1053.     SCR *sp;
  1054.     SMAP *p, *t;
  1055. {
  1056.     size_t lcnt;
  1057.  
  1058.     SMAP_FLUSH(t);
  1059.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  1060.         t->lno = p->lno + 1;
  1061.         t->coff = p->coff;
  1062.     } else {
  1063.         lcnt = vs_screens(sp, p->lno, NULL);
  1064.         if (lcnt == p->soff) {
  1065.             t->lno = p->lno + 1;
  1066.             t->soff = 1;
  1067.         } else {
  1068.             t->lno = p->lno;
  1069.             t->soff = p->soff + 1;
  1070.         }
  1071.     }
  1072.     return (0);
  1073. }
  1074.  
  1075. /*
  1076.  * vs_sm_prev --
  1077.  *    Fill in the previous entry in the SMAP.
  1078.  *
  1079.  * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
  1080.  */
  1081. int
  1082. vs_sm_prev(sp, p, t)
  1083.     SCR *sp;
  1084.     SMAP *p, *t;
  1085. {
  1086.     SMAP_FLUSH(t);
  1087.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  1088.         t->lno = p->lno - 1;
  1089.         t->coff = p->coff;
  1090.     } else {
  1091.         if (p->soff != 1) {
  1092.             t->lno = p->lno;
  1093.             t->soff = p->soff - 1;
  1094.         } else {
  1095.             t->lno = p->lno - 1;
  1096.             t->soff = vs_screens(sp, t->lno, NULL);
  1097.         }
  1098.     }
  1099.     return (t->lno == 0);
  1100. }
  1101.  
  1102. /*
  1103.  * vs_sm_cursor --
  1104.  *    Return the SMAP entry referenced by the cursor.
  1105.  *
  1106.  * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **));
  1107.  */
  1108. int
  1109. vs_sm_cursor(sp, smpp)
  1110.     SCR *sp;
  1111.     SMAP **smpp;
  1112. {
  1113.     SMAP *p;
  1114.  
  1115.     /* See if the cursor is not in the map. */
  1116.     if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
  1117.         return (1);
  1118.  
  1119.     /* Find the first occurence of the line. */
  1120.     for (p = HMAP; p->lno != sp->lno; ++p);
  1121.  
  1122.     /* Fill in the map information until we find the right line. */
  1123.     for (; p <= TMAP; ++p) {
  1124.         /* Short lines are common and easy to detect. */
  1125.         if (p != TMAP && (p + 1)->lno != p->lno) {
  1126.             *smpp = p;
  1127.             return (0);
  1128.         }
  1129.         if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
  1130.             return (1);
  1131.         if (p->c_eboff >= sp->cno) {
  1132.             *smpp = p;
  1133.             return (0);
  1134.         }
  1135.     }
  1136.  
  1137.     /* It was past the end of the map after all. */
  1138.     return (1);
  1139. }
  1140.  
  1141. /*
  1142.  * vs_sm_position --
  1143.  *    Return the line/column of the top, middle or last line on the screen.
  1144.  *    (The vi H, M and L commands.)  Here because only the screen routines
  1145.  *    know what's really out there.
  1146.  *
  1147.  * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
  1148.  */
  1149. int
  1150. vs_sm_position(sp, rp, cnt, pos)
  1151.     SCR *sp;
  1152.     MARK *rp;
  1153.     u_long cnt;
  1154.     pos_t pos;
  1155. {
  1156.     SMAP *smp;
  1157.     recno_t last;
  1158.  
  1159.     switch (pos) {
  1160.     case P_TOP:
  1161.         /*
  1162.          * !!!
  1163.          * Historically, an invalid count to the H command failed.
  1164.          * We do nothing special here, just making sure that H in
  1165.          * an empty screen works.
  1166.          */
  1167.         if (cnt > TMAP - HMAP)
  1168.             goto sof;
  1169.         smp = HMAP + cnt;
  1170.         if (cnt && !db_exist(sp, smp->lno)) {
  1171. sof:            msgq(sp, M_BERR, "220|Movement past the end-of-screen");
  1172.             return (1);
  1173.         }
  1174.         break;
  1175.     case P_MIDDLE:
  1176.         /*
  1177.          * !!!
  1178.          * Historically, a count to the M command was ignored.
  1179.          * If the screen isn't filled, find the middle of what's
  1180.          * real and move there.
  1181.          */
  1182.         if (!db_exist(sp, TMAP->lno)) {
  1183.             if (db_last(sp, &last))
  1184.                 return (1);
  1185.             for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
  1186.             if (smp > HMAP)
  1187.                 smp -= (smp - HMAP) / 2;
  1188.         } else
  1189.             smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
  1190.         break;
  1191.     case P_BOTTOM:
  1192.         /*
  1193.          * !!!
  1194.          * Historically, an invalid count to the L command failed.
  1195.          * If the screen isn't filled, find the bottom of what's
  1196.          * real and try to offset from there.
  1197.          */
  1198.         if (cnt > TMAP - HMAP)
  1199.             goto eof;
  1200.         smp = TMAP - cnt;
  1201.         if (!db_exist(sp, smp->lno)) {
  1202.             if (db_last(sp, &last))
  1203.                 return (1);
  1204.             for (; smp->lno > last && smp > HMAP; --smp);
  1205.             if (cnt > smp - HMAP) {
  1206. eof:                msgq(sp, M_BERR,
  1207.                 "221|Movement past the beginning-of-screen");
  1208.                 return (1);
  1209.             }
  1210.             smp -= cnt;
  1211.         }
  1212.         break;
  1213.     default:
  1214.         abort();
  1215.     }
  1216.  
  1217.     /* Make sure that the cached information is valid. */
  1218.     if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
  1219.         return (1);
  1220.     rp->lno = smp->lno;
  1221.     rp->cno = smp->c_sboff;
  1222.  
  1223.     return (0);
  1224. }
  1225.  
  1226. /*
  1227.  * vs_sm_nlines --
  1228.  *    Return the number of screen lines from an SMAP entry to the
  1229.  *    start of some file line, less than a maximum value.
  1230.  *
  1231.  * PUBLIC: recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t));
  1232.  */
  1233. recno_t
  1234. vs_sm_nlines(sp, from_sp, to_lno, max)
  1235.     SCR *sp;
  1236.     SMAP *from_sp;
  1237.     recno_t to_lno;
  1238.     size_t max;
  1239. {
  1240.     recno_t lno, lcnt;
  1241.  
  1242.     if (O_ISSET(sp, O_LEFTRIGHT))
  1243.         return (from_sp->lno > to_lno ?
  1244.             from_sp->lno - to_lno : to_lno - from_sp->lno);
  1245.  
  1246.     if (from_sp->lno == to_lno)
  1247.         return (from_sp->soff - 1);
  1248.  
  1249.     if (from_sp->lno > to_lno) {
  1250.         lcnt = from_sp->soff - 1;    /* Correct for off-by-one. */
  1251.         for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
  1252.             lcnt += vs_screens(sp, lno, NULL);
  1253.     } else {
  1254.         lno = from_sp->lno;
  1255.         lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
  1256.         for (; ++lno < to_lno && lcnt <= max;)
  1257.             lcnt += vs_screens(sp, lno, NULL);
  1258.     }
  1259.     return (lcnt);
  1260. }
  1261.