home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  31.8 KB  |  1,500 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * screen.c: code for displaying on the screen
  13.  */
  14.  
  15. #include "vim.h"
  16. #include "globals.h"
  17. #include "proto.h"
  18. #include "param.h"
  19.  
  20. char *tgoto __PARMS((char *cm, int col, int line));
  21.  
  22. static u_char     *Nextscreen = NULL;     /* What's to be put on the screen. */
  23. static int         NumLineSizes = 0;        /* # of active LineSizes */
  24. static linenr_t *LineNumbers = NULL;    /* Pointer to the line for LineSizes */
  25. static u_char     *LineSizes = NULL;        /* Number of rows the lines occupy */
  26. static u_char     **LinePointers = NULL;    /* array of pointers into Netscreen */
  27.  
  28. /*
  29.  * The following variable is set (in cursupdate) to the number of physical
  30.  * lines taken by the line the cursor is on. We use this to avoid extra calls
  31.  * to plines(). The optimized routine updateline()
  32.  * makes sure that the size of the cursor line hasn't changed. If so, lines
  33.  * below the cursor will move up or down and we need to call the routine
  34.  * updateScreen() to examine the entire screen.
  35.  */
  36. static int        Cline_size;             /* size (in rows) of the cursor line */
  37. static int        Cline_row;                /* starting row of the cursor line */
  38. static int        Leftcol = 0;            /* starting column of the screen */
  39. static FPOS        oldCurpos = {0, 0};        /* last known end of visual part */
  40. static int        oldCurswant = 0;        /* last known value of Curswant */
  41. static int        canopt;                    /* TRUE when cursor goto can be optimized */
  42.  
  43. static int screenline __ARGS((linenr_t, int, int));
  44. static void screenchar __ARGS((u_char *, int, int));
  45. static void screenfill __ARGS((int, int));
  46. static void screenalloc __ARGS((int));
  47. static void screenclear2 __ARGS((void));
  48.  
  49. /*
  50.  * updateline() - like updateScreen() but only for cursor line
  51.  *
  52.  * This determines whether or not we need to call updateScreen() to examine
  53.  * the entire screen for changes. This occurs if the size of the cursor line
  54.  * (in rows) hasn't changed.
  55.  */
  56.     void
  57. updateline()
  58. {
  59.     int         row;
  60.     int         n;
  61.  
  62.     if (must_redraw)    /* must redraw whole screen */
  63.     {
  64.         updateScreen(VALID);
  65.         return;
  66.     }
  67.  
  68.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  69.  
  70.     if (Nextscreen == NULL || RedrawingDisabled)
  71.         return;
  72.  
  73.     screenchar(NULL, 0, 0);    /* init cursor position of screenchar() */
  74.     cursor_off();
  75.  
  76.     row = screenline(Curpos.lnum, Cline_row, (int)Rows - 1);
  77.  
  78.     cursor_on();
  79.  
  80.     if (row == Rows)            /* line too long for screen */
  81.         updateScreen(VALID_TO_CURSCHAR);
  82.     else
  83.     {
  84.         n = row - Cline_row;
  85.         if (n != Cline_size)        /* line changed size */
  86.         {
  87.             if (n < Cline_size)     /* got smaller: delete lines */
  88.                     s_del(row, Cline_size - n, FALSE);
  89.             else                    /* got bigger: insert lines */
  90.                     s_ins(Cline_row + Cline_size, n - Cline_size, FALSE);
  91.  
  92.             updateScreen(VALID_TO_CURSCHAR);
  93.         }
  94.     }
  95. }
  96.  
  97. /*
  98.  * updateScreen()
  99.  *
  100.  * Based on the current value of Topline, transfer a screenfull of stuff from
  101.  * Filemem to Nextscreen, and update Botline.
  102.  */
  103.  
  104.     void
  105. updateScreen(type)
  106.     int             type;
  107. {
  108.     register int    row;
  109.     register int    endrow;
  110.     linenr_t        lnum;
  111.     linenr_t        lastline = 0; /* only valid if endrow != Rows -1 */
  112.     int                done;        /* if TRUE, we hit the end of the file */
  113.     int                didline;    /* if TRUE, we finished the last line */
  114.     int             srow = 0;    /* starting row of the current line */
  115.     int             idx;
  116.     int             i;
  117.     long             j;
  118.     static int        postponed_not_valid = FALSE;
  119.     register u_char *screenp;
  120.  
  121.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  122.  
  123.     if (Nextscreen == NULL)
  124.         return;
  125.  
  126.     cmdoffset = 0;            /* after redraw command line has no offset */
  127.     if (must_redraw)
  128.     {
  129.         type = must_redraw;
  130.         must_redraw = 0;
  131.     }
  132.     if (type == CLEAR)        /* first clear screen */
  133.     {
  134.         screenclear();
  135.         type = NOT_VALID;
  136.     }
  137.     if (type == CURSUPD)    /* update cursor and then redraw */
  138.     {
  139.         NumLineSizes = 0;
  140.         cursupdate();        /* will call updateScreen(VALID) */
  141.         return;
  142.     }
  143.     if (NumLineSizes == 0)
  144.         type = NOT_VALID;
  145.  
  146.      if (RedrawingDisabled)
  147.     {
  148.         if (type == NOT_VALID)
  149.             postponed_not_valid = TRUE;        /* use NOT_VALID next time */
  150.         return;
  151.     }
  152.  
  153.     if (postponed_not_valid)
  154.     {
  155.         type = NOT_VALID;
  156.         postponed_not_valid = FALSE;
  157.     }
  158.  
  159. /* return if there is nothing to do */
  160.     if ((type == VALID && Topline == LineNumbers[0]) ||
  161.             (type == INVERTED && oldCurpos.lnum == Curpos.lnum &&
  162.                     oldCurpos.col == Curpos.col && Curswant == oldCurswant))
  163.         return;
  164.  
  165.     if (type == NOT_VALID)
  166.     {
  167.         redraw_msg = TRUE;
  168.         NumLineSizes = 0;
  169.     }
  170.  
  171.     idx = 0;
  172.     row = 0;
  173.     lnum = Topline;
  174.     cursor_off();
  175.  
  176.     /* The number of rows shown is Rows-1. */
  177.     /* The default last row is the status/command line. */
  178.     endrow = Rows - 1;
  179.  
  180.     if (type == VALID || type == VALID_TO_CURSCHAR)
  181.     {
  182.         /*
  183.          * We handle two special cases:
  184.          * 1: we are off the top of the screen by a few lines: scroll down
  185.          * 2: Topline is below LineNumbers[0]: may scroll up
  186.          */
  187.         if (Topline < LineNumbers[0])    /* may scroll down */
  188.         {
  189.             j = LineNumbers[0] - Topline;
  190.             if (j < Rows - 3)                /* not too far off */
  191.             {
  192.                 lastline = LineNumbers[0] - 1;
  193.                 i = plines_m(Topline, lastline);
  194.                 if (i < Rows - 3)        /* less than a screen off */
  195.                 {
  196.                     /*
  197.                      * Try to insert the correct number of lines.
  198.                      * This may fail and the screen may have been cleared.
  199.                      */
  200.                     if (s_ins(0, i, FALSE) && NumLineSizes)
  201.                     {
  202.                         endrow = i;
  203.  
  204.                         if ((NumLineSizes += j) > Rows - 1)
  205.                             NumLineSizes = Rows - 1;
  206.                         for (idx = NumLineSizes; idx - j >= 0; idx--)
  207.                         {
  208.                             LineNumbers[idx] = LineNumbers[idx - j];
  209.                             LineSizes[idx] = LineSizes[idx - j];
  210.                         }
  211.                         idx = 0;
  212.                     }
  213.                 }
  214.                 else        /* far off: clearing the screen is faster */
  215.                     screenclear();
  216.             }
  217.             else        /* far off: clearing the screen is faster */
  218.                 screenclear();
  219.         }
  220.         else                            /* may scroll up */
  221.         {
  222.             j = -1;
  223.             for (i = 0; i < NumLineSizes; i++) /* try to find Topline in LineNumbers[] */
  224.             {
  225.                 if (LineNumbers[i] == Topline)
  226.                 {
  227.                     j = i;
  228.                     break;
  229.                 }
  230.                 row += LineSizes[i];
  231.             }
  232.             if (j == -1)    /* Topline is not in LineNumbers */
  233.             {
  234.                 row = 0;
  235.                 screenclear();   /* far off: clearing the screen is faster */
  236.             }
  237.             else
  238.             {
  239.                 /*
  240.                  * Try to delete the correct number of lines.
  241.                  * Topline is at LineNumbers[i].
  242.                  */
  243.                 if ((row == 0 || s_del(0, row, FALSE)) && NumLineSizes)
  244.                 {
  245.                     srow = row;
  246.                     row = 0;
  247.                     for (;;)
  248.                     {
  249.                         if (type == VALID_TO_CURSCHAR && lnum == Curpos.lnum)
  250.                                 break;
  251.                         if (row + srow + (int)LineSizes[j] >= Rows - 1)
  252.                                 break;
  253.                         LineSizes[idx] = LineSizes[j];
  254.                         LineNumbers[idx] = lnum++;
  255.  
  256.                         row += LineSizes[idx++];
  257.                         if ((int)++j >= NumLineSizes)
  258.                             break;
  259.                     }
  260.                     NumLineSizes = idx;
  261.                 }
  262.                 else
  263.                     row = 0;        /* update all lines */
  264.             }
  265.         }
  266.         if (endrow == Rows - 1 && idx == 0)     /* no scrolling */
  267.                 NumLineSizes = 0;
  268.     }
  269.  
  270.     done = didline = FALSE;
  271.     screenchar(NULL, 0, 0);    /* init cursor position of screenchar() */
  272.  
  273.     if (Visual.lnum)                /* check if we are updating the inverted part */
  274.     {
  275.         linenr_t    from, to;
  276.  
  277.     /* find the line numbers that need to be updated */
  278.         if (Curpos.lnum < oldCurpos.lnum)
  279.         {
  280.             from = Curpos.lnum;
  281.             to = oldCurpos.lnum;
  282.         }
  283.         else
  284.         {
  285.             from = oldCurpos.lnum;
  286.             to = Curpos.lnum;
  287.         }
  288.     /* if in block mode and changed column or Curswant: update all lines */
  289.         if (Visual_block && (Curpos.col != oldCurpos.col || Curswant != oldCurswant))
  290.         {
  291.             if (from > Visual.lnum)
  292.                 from = Visual.lnum;
  293.             if (to < Visual.lnum)
  294.                 to = Visual.lnum;
  295.         }
  296.  
  297.         if (from < Topline)
  298.             from = Topline;
  299.         if (to >= Botline)
  300.             to = Botline - 1;
  301.  
  302.     /* find the minimal part to be updated */
  303.         if (type == INVERTED)
  304.         {
  305.             while (lnum < from)                        /* find start */
  306.             {
  307.                 row += LineSizes[idx++];
  308.                 ++lnum;
  309.             }
  310.             srow = row;
  311.             for (j = idx; j < NumLineSizes; ++j)    /* find end */
  312.             {
  313.                 if (LineNumbers[j] == to + 1)
  314.                 {
  315.                     endrow = srow;
  316.                     break;
  317.                 }
  318.                 srow += LineSizes[j];
  319.             }
  320.             oldCurpos = Curpos;
  321.             oldCurswant = Curswant;
  322.         }
  323.     /* if we update the lines between from and to set oldCurpos */
  324.         else if (lnum <= from && (endrow == Rows - 1 || lastline >= to))
  325.         {
  326.             oldCurpos = Curpos;
  327.             oldCurswant = Curswant;
  328.         }
  329.     }
  330.  
  331.     /*
  332.      * Update the screen rows from "row" to "endrow".
  333.      * Start at line "lnum" which is at LineNumbers[idx].
  334.      */
  335.     for (;;)
  336.     {
  337.             if (lnum > line_count)        /* hit the end of the file */
  338.             {
  339.                 done = TRUE;
  340.                 break;
  341.             }
  342.             srow = row;
  343.             row = screenline(lnum, srow, endrow);
  344.             if (row > endrow)    /* past end of screen */
  345.             {
  346.                 LineSizes[idx] = plines(lnum);    /* we may need the size of that */
  347.                 LineNumbers[idx++] = lnum;        /* too long line later on */
  348.                 break;
  349.             }
  350.  
  351.             LineSizes[idx] = row - srow;
  352.             LineNumbers[idx++] = lnum;
  353.             if (++lnum > line_count)
  354.             {
  355.                 done = TRUE;
  356.                 break;
  357.             }
  358.  
  359.             if (row == endrow)
  360.             {
  361.                 didline = TRUE;
  362.                 break;
  363.             }
  364.     }
  365.     if (idx > NumLineSizes)
  366.         NumLineSizes = idx;
  367.  
  368.     /* Do we have to do off the top of the screen processing ? */
  369.     if (endrow != Rows - 1)
  370.     {
  371.         row = 0;
  372.         for (idx = 0; idx < NumLineSizes && row < (Rows - 1); idx++)
  373.             row += LineSizes[idx];
  374.  
  375.         if (row < (Rows - 1))
  376.         {
  377.             done = TRUE;
  378.         }
  379.         else if (row > (Rows - 1))        /* Need to blank out the last line */
  380.         {
  381.             lnum = LineNumbers[idx - 1];
  382.             srow = row - LineSizes[idx - 1];
  383.             didline = FALSE;
  384.         }
  385.         else
  386.         {
  387.             lnum = LineNumbers[idx - 1] + 1;
  388.             didline = TRUE;
  389.         }
  390.     }
  391.  
  392.     emptyrows = 0;
  393.     /*
  394.      * If we didn't hit the end of the file, and we didn't finish the last
  395.      * line we were working on, then the line didn't fit.
  396.      */
  397.     if (!done && !didline)
  398.     {
  399.         if (lnum == Topline)
  400.         {
  401.             /*
  402.              * Single line that does not fit!
  403.              * Fill last line with '@' characters.
  404.              */
  405.             screenp = LinePointers[Rows - 2];
  406.             for (i = 0; i < Columns; ++i)
  407.             {
  408.                 if (*screenp != '@')
  409.                 {
  410.                     *screenp = '@';
  411.                     screenchar(screenp, (int)(Rows - 2), i);
  412.                 }
  413.                 ++screenp;
  414.             }
  415.             Botline = lnum + 1;
  416.         }
  417.         else
  418.         {
  419.             /*
  420.              * Clear the rest of the screen and mark the unused lines.
  421.              */
  422.             screenfill(srow, '@');
  423.             Botline = lnum;
  424.         }
  425.     }
  426.     else
  427.     {
  428.         /* make sure the rest of the screen is blank */
  429.         /* put '~'s on rows that aren't part of the file. */
  430.         screenfill(row, '~');
  431.         emptyrows = Rows - row - 1;
  432.  
  433.         if (done)                /* we hit the end of the file */
  434.             Botline = line_count + 1;
  435.         else
  436.             Botline = lnum;
  437.     }
  438.  
  439.     if (redraw_msg)
  440.     {
  441.         showmode();
  442.         redraw_msg = FALSE;
  443.     }
  444.  
  445.     cursor_on();
  446. }
  447.  
  448. static int        invert;        /* shared by screenline() and screenchar() */
  449.  
  450. /*
  451.  * Move line "lnum" to the screen.
  452.  * Start at row "startrow", stop when "endrow" is reached.
  453.  * Return the number of last row the line occupies.
  454.  */
  455.  
  456.     static int
  457. screenline(lnum, startrow, endrow)
  458.         linenr_t        lnum;
  459.         int             startrow;
  460.         int             endrow;
  461. {
  462.     register u_char  *screenp;
  463.     register u_char   c;
  464.     register int    col;                /* visual column on screen */
  465.     register int    vcol;                /* visual column for tabs */
  466.     register int    row;
  467.     register u_char *ptr;
  468.     char            extra[16];            /* "%ld" must fit in here */
  469.     char            *p_extra;
  470.     int             n_extra;
  471.     int                n_spaces = 0;
  472.  
  473.     int                fromcol, tocol;        /* start/end of inverting */
  474.     int                noinvcur = FALSE;    /* don't invert the cursor */
  475.     int                temp;
  476.     FPOS            *top, *bot;
  477.  
  478.     row = startrow;
  479.     col = 0;
  480.     vcol = 0;
  481.     invert = FALSE;
  482.     fromcol = -10;
  483.     tocol = MAXCOL;
  484.     ptr = (u_char *)nr2ptr(lnum);
  485.     canopt = TRUE;
  486.     if (Visual.lnum)                    /* visual active */
  487.     {
  488.         if (ltoreq(Curpos, Visual))        /* Visual is after Curpos */
  489.         {
  490.             top = &Curpos;
  491.             bot = &Visual;
  492.         }
  493.         else                            /* Visual is before Curpos */
  494.         {
  495.             top = &Visual;
  496.             bot = &Curpos;
  497.         }
  498.         if (Visual_block)                        /* block mode */
  499.         {
  500.             if (lnum >= top->lnum && lnum <= bot->lnum)
  501.             {
  502.                 fromcol = getvcol(top, 2);
  503.                 temp = getvcol(bot, 2);
  504.                 if (temp < fromcol)
  505.                     fromcol = temp;
  506.  
  507.                 if (Curswant != MAXCOL)
  508.                 {
  509.                     tocol = getvcol(top, 3);
  510.                     temp = getvcol(bot, 3);
  511.                     if (temp > tocol)
  512.                         tocol = temp;
  513.                     ++tocol;
  514.                 }
  515.             }
  516.         }
  517.         else                            /* non-block mode */
  518.         {
  519.             if (lnum > top->lnum && lnum <= bot->lnum)
  520.                 fromcol = 0;
  521.             else if (lnum == top->lnum)
  522.                 fromcol = getvcol(top, 2);
  523.             if (lnum == bot->lnum)
  524.                 tocol = getvcol(bot, 3) + 1;
  525.  
  526.             if (Visual.col == VISUALLINE)        /* linewise */
  527.             {
  528.                 if (fromcol > 0)
  529.                     fromcol = 0;
  530.                 tocol = VISUALLINE;
  531.             }
  532.         }
  533.             /* if the cursor can't be switched off, don't invert the character
  534.                         where the cursor is */
  535.         if ((T_CI == NULL || *T_CI == NUL) && lnum == Curpos.lnum)
  536.             noinvcur = TRUE;
  537.  
  538.         /* if inverting in this line, can't optimize cursor positioning */
  539.         if (fromcol >= 0)
  540.             canopt = FALSE;
  541.     }
  542.     if (!p_wrap)        /* advance to first character to be displayed */
  543.     {
  544.         while (vcol < Leftcol && *ptr)
  545.             vcol += chartabsize(*ptr++, vcol);
  546.         if (vcol > Leftcol)
  547.         {
  548.             n_spaces = vcol - Leftcol;    /* begin with some spaces */
  549.             vcol = Leftcol;
  550.         }
  551.     }
  552.     screenp = LinePointers[row];
  553.     if (p_nu)
  554.     {
  555.         sprintf(extra, "%7ld ", (long)lnum);
  556.         p_extra = extra;
  557.         n_extra = 8;
  558.         vcol -= 8;        /* so vcol is 0 when line number has been printed */
  559.     }
  560.     else
  561.     {
  562.         p_extra = NULL;
  563.         n_extra = 0;
  564.     }
  565.     for (;;)
  566.     {
  567.         if (!canopt)    /* Visual in this line */
  568.         {
  569.             if (((vcol == fromcol && !(noinvcur && vcol == Cursvcol)) ||
  570.                     (noinvcur && vcol == Cursvcol + 1 && vcol >= fromcol)) &&
  571.                     vcol < tocol)    /* start inverting */
  572.             {
  573.                 invert = TRUE;
  574.                 outstr(T_TI);
  575.             }
  576.             else if (invert && (vcol == tocol || (noinvcur && vcol == Cursvcol)))
  577.                                     /* stop inverting */
  578.             {
  579.                 invert = FALSE;
  580.                 outstr(T_TP);
  581.             }
  582.         }
  583.  
  584.         /* Get the next character to put on the screen. */
  585.         /*
  586.          * The 'extra' array contains the extra stuff that is inserted to
  587.          * represent special characters (non-printable stuff).
  588.          */
  589.  
  590.         if (n_extra)
  591.         {
  592.             c = (u_char)*p_extra++;
  593.             n_extra--;
  594.         }
  595.         else if (n_spaces)
  596.         {
  597.             c = ' ';
  598.             n_spaces--;
  599.         }
  600.         else
  601.         {
  602.             if ((c = *ptr++) < ' ' || (c > '~' && c <= 0xa0))
  603.             {
  604.                 /*
  605.                  * when getting a character from the file, we may have to turn it
  606.                  * into something else on the way to putting it into 'Nextscreen'.
  607.                  */
  608.                 if (c == TAB && !p_list)
  609.                 {
  610.                     /* tab amount depends on current column */
  611.                     n_spaces = (int)p_ts - vcol % (int)p_ts - 1;
  612.                     c = ' ';
  613.                 }
  614.                 else if (c == NUL && p_list)
  615.                 {
  616.                     p_extra = "";
  617.                     n_extra = 1;
  618.                     c = '$';
  619.                 }
  620.                 else if (c != NUL)
  621.                 {
  622.                     p_extra = (char *)transchar(c);
  623.                     n_extra = charsize(c) - 1;
  624.                     c = (u_char)*p_extra++;
  625.                 }
  626.             }
  627.         }
  628.  
  629.         if (c == NUL)
  630.         {
  631.             if (invert)
  632.             {
  633.                 if (vcol == 0)    /* invert first char of empty line */
  634.                 {
  635.                     if (*screenp != (' ' ^ 0x80))
  636.                     {
  637.                             *screenp = (' ' ^ 0x80);
  638.                             screenchar(screenp, row, col);
  639.                     }
  640.                     ++screenp;
  641.                     ++col;
  642.                 }
  643.                 outstr(T_TP);
  644.                 invert = FALSE;
  645.             }
  646.             /* 
  647.              * blank out the rest of this row
  648.              * could also use clear-to-end-of-line, but it is slower
  649.              * on an Amiga
  650.              */
  651.             while (col < Columns)
  652.             {
  653.                 if (*screenp != ' ')
  654.                 {
  655.                         *screenp = ' ';
  656.                         screenchar(screenp, row, col);
  657.                 }
  658.                 ++screenp;
  659.                 ++col;
  660.             }
  661.             row++;
  662.             break;
  663.         }
  664.         if (col >= Columns)
  665.         {
  666.             col = 0;
  667.             if (!p_wrap || ++row == endrow)        /* line got too long for screen */
  668.             {
  669.                 ++row;
  670.                 break;
  671.             }
  672.             screenp = LinePointers[row];
  673.         }
  674.         /* store the character in Nextscreen */
  675.         if (!invert)
  676.         {
  677.             if (*screenp != c)
  678.             {
  679.                 *screenp = c;
  680.                 screenchar(screenp, row, col);
  681.             }
  682.         }
  683.         else
  684.         {
  685.             if (*screenp != (c ^ 0x80))
  686.             {
  687.                 *screenp = c ^ 0x80;
  688.                 screenchar(screenp, row, col);
  689.             }
  690.         }
  691.         ++screenp;
  692.         col++;
  693.         vcol++;
  694.     }
  695.  
  696.     if (invert)
  697.     {
  698.         outstr(T_TP);
  699.         invert = FALSE;
  700.     }
  701.     return (row);
  702. }
  703.  
  704. /*
  705.  * put character '*p' on the screen at position 'row' and 'col'
  706.  */
  707.     static void
  708. screenchar(p, row, col)
  709.         u_char    *p;
  710.         int     row;
  711.         int     col;
  712. {
  713.     static int    oldrow, oldcol;        /* old cursor position */
  714.     int            c;
  715.  
  716.     if (p == NULL)                    /* initialize cursor position */
  717.     {
  718.         oldrow = oldcol = -1;
  719.         return;
  720.     }
  721.     if (oldcol != col || oldrow != row)
  722.     {
  723.         /*
  724.          * If we're on the same row (which happens a lot!), try to
  725.          * avoid a windgoto().
  726.          * If we are only a few characters off, output the
  727.          * characters. That is faster than cursor positioning.
  728.          * This can't be used when inverting (a part of) the line.
  729.          */
  730.         if (oldrow == row && oldcol < col)
  731.         {
  732.             register int i;
  733.  
  734.             i = col - oldcol;
  735.             if (i <= 4 && canopt)
  736.             {
  737.                 while (i)
  738.                 {
  739.                     c = *(p - i--);
  740.                     outchar(c);
  741.                 }
  742.             }
  743.             else if (T_CRI && *T_CRI)    /* use tgoto interface! jw */
  744.                 outstr(tgoto(T_CRI, 0, i));
  745.             else
  746.                 windgoto(row, col);
  747.             
  748.             oldcol = col;
  749.         }
  750.         else
  751.             windgoto(oldrow = row, oldcol = col);
  752.     }
  753.     if (invert)
  754.         outchar(*p ^ 0x80);
  755.     else
  756.         outchar(*p);
  757.     oldcol++;
  758. }
  759.  
  760. /*
  761.  * Fill the screen at 'srow' with character 'c' followed by blanks.
  762.  */
  763.     static void
  764. screenfill(srow, c)
  765.         int     srow;
  766.         int        c;
  767. {
  768.         register int row;
  769.         register int col;
  770.         register u_char *screenp;
  771.  
  772.         for (row = srow; row < (Rows - 1); ++row)
  773.         {
  774.             screenp = LinePointers[row];
  775.             if (*screenp != c)
  776.             {
  777.                 *screenp = c;
  778.                 screenchar(screenp, row, 0);
  779.             }
  780.             ++screenp;
  781.             for (col = 1; col < Columns; ++col)
  782.             {
  783.                 if (*screenp != ' ')
  784.                 {
  785.                     *screenp = ' ';
  786.                     screenchar(screenp, row, col);
  787.                 }
  788.                 ++screenp;
  789.             }
  790.         }
  791. }
  792.  
  793. /*
  794.  * compute Botline. Can be called after Topline or Rows changed.
  795.  */
  796.     void
  797. comp_Botline()
  798. {
  799.     linenr_t    lnum;
  800.     int            done = 0;
  801.  
  802.     for (lnum = Topline; lnum <= line_count; ++lnum)
  803.     {
  804.         if ((done += plines(lnum)) >= Rows)
  805.             break;
  806.     }
  807.     Botline = lnum;        /* Botline is the line that is just below the window */
  808. }
  809.  
  810. /*
  811.  * prt_line() - print the given line
  812.  * returns the number of characters written.
  813.  */
  814.     int
  815. prt_line(s)
  816.     char           *s;
  817. {
  818.     register int    si = 0;
  819.     register char    c;
  820.     register int    col = 0;
  821.  
  822.     int             n_extra = 0;
  823.     int             n_spaces = 0;
  824.     char            *p = NULL;            /* init to make SASC shut up */
  825.     int             n;
  826.  
  827.     for (;;)
  828.     {
  829.         if (n_extra)
  830.         {
  831.             --n_extra;
  832.             c = *p++;
  833.         }
  834.         else if (n_spaces)
  835.         {
  836.             --n_spaces;
  837.             c = ' ';
  838.         }
  839.         else
  840.         {
  841.             c = s[si++];
  842.             if (c == TAB && !p_list)
  843.             {
  844.                 /* tab amount depends on current column */
  845.                 n_spaces = p_ts - col % p_ts - 1;
  846.                 c = ' ';
  847.             }
  848.             else if (c == NUL && p_list)
  849.             {
  850.                 p = "";
  851.                 n_extra = 1;
  852.                 c = '$';
  853.             }
  854.             else if (c != NUL && (n = charsize(c)) > 1)
  855.             {
  856.                 n_extra = n - 1;
  857.                 p = transchar(c);
  858.                 c = *p++;
  859.             }
  860.         }
  861.  
  862.         if (c == NUL)
  863.             break;
  864.  
  865.         outchar(c);
  866.         col++;
  867.     }
  868.     return col;
  869. }
  870.  
  871.     static void
  872. screenalloc(clear)
  873.     int        clear;
  874. {
  875.     static int        old_Rows = 0;
  876.     static int        old_Columns = 0;
  877.     register int    i;
  878.  
  879.     /*
  880.      * Allocation of the sceen buffers is done only when the size changes
  881.      */
  882.     if ((Nextscreen != NULL && Rows == old_Rows && Columns == old_Columns) || Rows == 0 || Columns == 0)
  883.         return;
  884.  
  885.     comp_col();            /* recompute columns for shown command and ruler */
  886.     old_Rows = Rows;
  887.     old_Columns = Columns;
  888.  
  889.     /*
  890.      * If we're changing the size of the screen, free the old arrays
  891.      */
  892.     if (Nextscreen != NULL)
  893.         free((char *)Nextscreen);
  894.     if (LinePointers != NULL)
  895.         free((char *)LinePointers);
  896.     if (LineNumbers != NULL)
  897.         free((char *) LineNumbers);
  898.     if (LineSizes != NULL)
  899.         free(LineSizes);
  900.  
  901.     Nextscreen = (u_char *)malloc((size_t) (Rows * Columns));
  902.     LineNumbers = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
  903.     LineSizes = (u_char *)malloc((size_t) Rows);
  904.     LinePointers = (u_char **)malloc(sizeof(u_char *) * Rows);
  905.  
  906.     if (Nextscreen == NULL || LineNumbers == NULL || LineSizes == NULL ||
  907.                                                 LinePointers == NULL)
  908.     {
  909.         emsg(e_outofmem);
  910.         if (Nextscreen != NULL)
  911.             free((char *)Nextscreen);
  912.         Nextscreen = NULL;
  913.     }
  914.     else
  915.     {
  916.         for (i = 0; i < Rows; ++i)
  917.                 LinePointers[i] = Nextscreen + i * Columns;
  918.     }
  919.  
  920.     if (clear)
  921.         screenclear2();
  922. }
  923.  
  924.     void
  925. screenclear()
  926. {
  927.     screenalloc(FALSE);            /* allocate screen buffers if size changed */
  928.     screenclear2();
  929. }
  930.  
  931.     static void
  932. screenclear2()
  933. {
  934.     if (starting || Nextscreen == NULL)
  935.         return;
  936.  
  937.     outstr(T_ED);                /* clear the display */
  938.  
  939.                                 /* blank out Nextscreen */
  940.     memset((char *)Nextscreen, ' ', (size_t)(Rows * Columns));
  941.  
  942.     NumLineSizes = 0;            /* clear screen info */
  943.     redraw_msg = TRUE;            /* refresh cmdline at next screen redraw */
  944. }
  945.  
  946.     void
  947. cursupdate()
  948. {
  949.     linenr_t        p;
  950.     long             nlines;
  951.     int             i;
  952.     int             temp;
  953.  
  954.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  955.  
  956.     if (Nextscreen == NULL)
  957.         return;
  958.  
  959.     if (Curpos.lnum > line_count)
  960.         Curpos.lnum = line_count;
  961.     if (bufempty())             /* special case - file is empty */
  962.     {
  963.         Topline = 1;
  964.         Curpos.lnum = 1;
  965.         Curpos.col = 0;
  966.         for (i = 0; i < Rows; i++)
  967.             LineSizes[i] = 0;
  968.         if (NumLineSizes == 0)        /* don't know about screen contents */
  969.             updateScreen(NOT_VALID);
  970.         NumLineSizes = 1;
  971.     }
  972.     else if (Curpos.lnum < Topline)
  973.     {
  974.         /*
  975.          * If the cursor is above the top of the screen, scroll the screen to
  976.          * put it at the top of the screen.
  977.          * If we weren't very close to begin with, we scroll more, so that
  978.          * the line is close to the middle.
  979.          */
  980.         temp = Rows / 2 - 1;
  981.         if (Topline - Curpos.lnum >= temp)        /* not very close */
  982.         {
  983.             p = Curpos.lnum;
  984.             i = plines(p);
  985.             temp += i;
  986.                                 /* count lines for 1/2 screenheight */
  987.             while (i < Rows && i < temp && p > 1)
  988.                 i += plines(--p);
  989.             Topline = p;
  990.             if (i >= Rows)        /* cursor line won't fit, backup one line */
  991.                 ++Topline;
  992.         }
  993.         else if (p_sj > 1)        /* scroll at least p_sj lines */
  994.         {
  995.             for (i = 0; i < p_sj && Topline > 1; i += plines(--Topline))
  996.                 ;
  997.         }
  998.         if (Topline > Curpos.lnum)
  999.             Topline = Curpos.lnum;
  1000.         updateScreen(VALID);
  1001.     }
  1002.     else if (Curpos.lnum >= Botline)
  1003.     {
  1004.             /* number of lines the cursor is below the bottom of the screen */
  1005.         nlines = Curpos.lnum - Botline + 1;
  1006.         /*
  1007.          * If the cursor is less than a screenheight down
  1008.          * compute the number of lines at the top which have the same or more
  1009.          * rows than the rows of the lines below the bottom
  1010.          */
  1011.         if (nlines <= Rows)
  1012.         {
  1013.                 /* get the number or rows to scroll minus the number of
  1014.                                 free '~' rows */
  1015.             temp = plines_m(Botline, Curpos.lnum) - emptyrows;
  1016.             if (temp <= 0)                /* emptyrows is larger, no need to scroll */
  1017.                 nlines = 0;
  1018.             else if (temp >= Rows)        /* more than a screenfull, don't scroll */
  1019.                 nlines = temp;
  1020.             else
  1021.             {
  1022.                     /* scroll minimal number of lines */
  1023.                 if (temp < p_sj)
  1024.                     temp = p_sj;
  1025.                 for (i = 0, p = Topline; i < temp && p < Botline; ++p)
  1026.                     i += plines(p);
  1027.                 if (i >= temp)                /* it's possible to scroll */
  1028.                     nlines = p - Topline;
  1029.                 else                        /* below Botline, don't scroll */
  1030.                     nlines = 9999;
  1031.             }
  1032.         }
  1033.  
  1034.         /*
  1035.          * Scroll up if the cursor is off the bottom of the screen a bit.
  1036.          * Otherwise put it at 1/2 of the screen.
  1037.          */
  1038.         if (nlines >= Rows / 2 && nlines > p_sj)
  1039.         {
  1040.             p = Curpos.lnum;
  1041.             temp = Rows / 2 + 1;
  1042.             nlines = 0;
  1043.             i = 0;
  1044.             do                /* this loop could win a contest ... */
  1045.                 i += plines(p);
  1046.             while (i < temp && (nlines = 1) != 0 && --p != 0);
  1047.             Topline = p + nlines;
  1048.         }
  1049.         else
  1050.             scrollup(nlines);
  1051.         updateScreen(VALID);
  1052.     }
  1053.     else if (NumLineSizes == 0)        /* don't know about screen contents */
  1054.         updateScreen(NOT_VALID);
  1055.     Cursrow = Curscol = Cursvcol = i = 0;
  1056.     for (p = Topline; p != Curpos.lnum; ++p)
  1057.         if (RedrawingDisabled)        /* LineSizes[] invalid */
  1058.             Cursrow += plines(p);
  1059.         else
  1060.             Cursrow += LineSizes[i++];
  1061.  
  1062.     Cline_row = Cursrow;
  1063.     if (!RedrawingDisabled && i > NumLineSizes)
  1064.                                 /* Should only happen with a line that is too */
  1065.                                 /* long to fit on the last screen line. */
  1066.         Cline_size = 0;
  1067.     else
  1068.     {
  1069.         if (RedrawingDisabled)              /* LineSizes[] invalid */
  1070.             Cline_size = plines(Curpos.lnum);
  1071.         else
  1072.             Cline_size = LineSizes[i];
  1073.  
  1074.         curs_columns(!RedrawingDisabled);    /* compute Cursvcol and Curscol */
  1075.         if (must_redraw)
  1076.             updateScreen(VALID);
  1077.     }
  1078.  
  1079.     if (set_want_col)
  1080.     {
  1081.         Curswant = Cursvcol;
  1082.         set_want_col = FALSE;
  1083.     }
  1084. }
  1085.  
  1086. /*
  1087.  * compute Curscol and Cursvcol
  1088.  */
  1089.     void
  1090. curs_columns(scroll)
  1091.     int scroll;            /* when TRUE, may scroll horizontally */
  1092. {
  1093.     int diff;
  1094.  
  1095.     Cursvcol = getvcol(&Curpos, 1);
  1096.     Curscol = Cursvcol;
  1097.     if (p_nu)
  1098.         Curscol += 8;
  1099.  
  1100.     Cursrow = Cline_row;
  1101.     if (p_wrap)            /* long line wrapping, adjust Cursrow */
  1102.         while (Curscol >= Columns)
  1103.         {
  1104.             Curscol -= Columns;
  1105.             Cursrow++;
  1106.         }
  1107.     else if (scroll)    /* no line wrapping, compute Leftcol if scrolling is on */
  1108.                         /* if scrolling is off, Leftcol is assumed to be 0 */
  1109.     {
  1110.                         /* If Cursor is left of the screen, scroll rightwards */
  1111.                         /* If Cursor is right of the screen, scroll leftwards */
  1112.         if (((diff = Leftcol + (p_nu ? 8 : 0) - Curscol) > 0 ||
  1113.                     (diff = Curscol - (Leftcol + Columns) + 1) > 0))
  1114.         {
  1115.             if (p_ss == 0 || diff >= Columns / 2)
  1116.                 Leftcol = Curscol - Columns / 2;
  1117.             else
  1118.             {
  1119.                 if (diff < p_ss)
  1120.                     diff = p_ss;
  1121.                 if (Curscol < Leftcol + 8)
  1122.                     Leftcol -= diff;
  1123.                 else
  1124.                     Leftcol += diff;
  1125.             }
  1126.             if (Leftcol < 0)
  1127.                 Leftcol = 0;
  1128.             must_redraw = NOT_VALID;    /* screen has to be redrawn with new Leftcol */
  1129.         }
  1130.         Curscol -= Leftcol;
  1131.     }
  1132.     if (Cursrow > Rows - 2)        /* Cursor past end of screen */
  1133.         Cursrow = Rows - 2;        /* happens with line that does not fit on screen */
  1134. }
  1135.  
  1136. /*
  1137.  * get virtual column number of pos
  1138.  * type = 1: where the cursor is on this character
  1139.  * type = 2: on the first position of this character (TAB)
  1140.  * type = 3: on the last position of this character (TAB)
  1141.  */
  1142.     int
  1143. getvcol(pos, type)
  1144.     FPOS    *pos;
  1145.     int        type;
  1146. {
  1147.     int                col;
  1148.     int                vcol;
  1149.     u_char           *ptr;
  1150.     int             incr;
  1151.     u_char            c;
  1152.  
  1153.     vcol = 0;
  1154.     ptr = (u_char *)nr2ptr(pos->lnum);
  1155.     for (col = pos->col; col >= 0; --col)
  1156.     {
  1157.         c = *ptr++;
  1158.         if (c == NUL)        /* make sure we don't go past the end of the line */
  1159.             break;
  1160.  
  1161.         /* A tab gets expanded, depending on the current column */
  1162.         incr = chartabsize(c, vcol);
  1163.  
  1164.         if (col == 0)        /* character at pos.col */
  1165.         {
  1166.             if (type == 3 || (type == 1 && c == TAB && State == NORMAL && !p_list))
  1167.                 --incr;
  1168.             else
  1169.                 break;
  1170.         }
  1171.         vcol += incr;
  1172.     }
  1173.     return vcol;
  1174. }
  1175.  
  1176.     void
  1177. scrolldown(nlines)
  1178.     long    nlines;
  1179. {
  1180.     register long    done = 0;    /* total # of physical lines done */
  1181.  
  1182.     /* Scroll up 'nlines' lines. */
  1183.     while (nlines--)
  1184.     {
  1185.         if (Topline == 1)
  1186.             break;
  1187.         done += plines(--Topline);
  1188.     }
  1189.     /*
  1190.      * Compute the row number of the last row of the cursor line
  1191.      * and move it onto the screen.
  1192.      */
  1193.     Cursrow += done;
  1194.     if (p_wrap)
  1195.         Cursrow += plines(Curpos.lnum) - 1 - Cursvcol / Columns;
  1196.     while (Cursrow >= Rows - 1 && Curpos.lnum > 1)
  1197.         Cursrow -= plines(Curpos.lnum--);
  1198. }
  1199.  
  1200.     void
  1201. scrollup(nlines)
  1202.     long    nlines;
  1203. {
  1204. #ifdef NEVER
  1205.     register long    done = 0;    /* total # of physical lines done */
  1206.  
  1207.     /* Scroll down 'nlines' lines. */
  1208.     while (nlines--)
  1209.     {
  1210.         if (Topline == line_count)
  1211.             break;
  1212.         done += plines(Topline);
  1213.         if (Curpos.lnum == Topline)
  1214.             ++Curpos.lnum;
  1215.         ++Topline;
  1216.     }
  1217.     s_del(0, done, TRUE);
  1218. #endif
  1219.     Topline += nlines;
  1220.     if (Topline > line_count)
  1221.         Topline = line_count;
  1222.     if (Curpos.lnum < Topline)
  1223.         Curpos.lnum = Topline;
  1224. }
  1225.  
  1226. /*
  1227.  * The rest of the routines in this file perform screen manipulations. The
  1228.  * given operation is performed physically on the screen. The corresponding
  1229.  * change is also made to the internal screen image. In this way, the editor
  1230.  * anticipates the effect of editing changes on the appearance of the screen.
  1231.  * That way, when we call screenupdate a complete redraw isn't usually
  1232.  * necessary. Another advantage is that we can keep adding code to anticipate
  1233.  * screen changes, and in the meantime, everything still works.
  1234.  */
  1235.  
  1236. /*
  1237.  * s_ins(row, nlines, invalid) - insert 'nlines' lines at 'row'
  1238.  * if 'invalid' is TRUE the LineNumbers[] is invalidated.
  1239.  * Returns 0 if the lines are not inserted, 1 for success.
  1240.  */
  1241.     int
  1242. s_ins(row, nlines, invalid)
  1243.     int         row;
  1244.     int         nlines;
  1245.     int            invalid;
  1246. {
  1247.     int         i;
  1248.     int         j;
  1249.     u_char        *temp;
  1250.  
  1251.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  1252.  
  1253.     if (Nextscreen == NULL)
  1254.         return 0;
  1255.  
  1256.     if (invalid)
  1257.         NumLineSizes = 0;
  1258.  
  1259.     if (nlines > (Rows - 1 - row))
  1260.         nlines = Rows - 1 - row;
  1261.  
  1262.     if (RedrawingDisabled || nlines <= 0 ||
  1263.                         ((T_CIL == NULL || *T_CIL == NUL) &&
  1264.                         (T_IL == NULL || *T_IL == NUL) &&
  1265.                         (T_SR == NULL || *T_SR == NUL || row != 0)))
  1266.         return 0;
  1267.     
  1268.     if (Rows - nlines < 5)    /* only a few lines left: redraw is faster */
  1269.     {
  1270.         screenclear();        /* will set NumLineSizes to 0 */
  1271.         return 0;
  1272.     }
  1273.  
  1274.     if (Rows != Rows_max)
  1275.     {
  1276.         windgoto((int)Rows - 1, 0);        /* delete any garbage that may have */
  1277.         clear_line();                    /* been shifted to the bottom line */
  1278.     }
  1279.     /*
  1280.      * It "looks" better if we do all the inserts at once
  1281.      */
  1282.     if (T_CIL && *T_CIL) 
  1283.     {
  1284.         windgoto(row, 0);
  1285.         if (nlines == 1 && T_IL && *T_IL)
  1286.             outstr(T_IL);
  1287.         else
  1288.             outstr(tgoto(T_CIL, 0, nlines));
  1289.     }
  1290.     else
  1291.     {
  1292.         for (i = 0; i < nlines; i++) 
  1293.         {
  1294.             if (i == 0 || row != 0)
  1295.                 windgoto(row, 0);
  1296.             if (T_IL && *T_IL)
  1297.                 outstr(T_IL);
  1298.             else
  1299.                 outstr(T_SR);
  1300.         }
  1301.     }
  1302.     windgoto((int)Rows - 1, 0);        /* delete any garbage that may have */
  1303.     clear_line();                    /* been shifted to the bottom line */
  1304.     redraw_msg = TRUE;
  1305.  
  1306.     /*
  1307.      * Now shift LinePointers nlines down to reflect the inserted lines.
  1308.      * Clear the inserted lines.
  1309.      */
  1310.     for (i = 0; i < nlines; ++i)
  1311.     {
  1312.         j = Rows - 2 - i;
  1313.         temp = LinePointers[j];
  1314.         while ((j -= nlines) >= row)
  1315.                 LinePointers[j + nlines] = LinePointers[j];
  1316.         LinePointers[j + nlines] = temp;
  1317.         memset((char *)temp, ' ', (size_t)Columns);
  1318.     }
  1319.     return 1;
  1320. }
  1321.  
  1322. /*
  1323.  * s_del(row, nlines, invalid) - delete 'nlines' lines at 'row'
  1324.  * If 'invalid' is TRUE LineNumbers[] is ivalidated.
  1325.  * Return 1 for success, 0 if the lines are not deleted.
  1326.  */
  1327.     int
  1328. s_del(row, nlines, invalid)
  1329.     int             row;
  1330.     int             nlines;
  1331.     int            invalid;
  1332. {
  1333.     int             j;
  1334.     int             i;
  1335.     u_char        *temp;
  1336.  
  1337.     screenalloc(TRUE);        /* allocate screen buffers if size changed */
  1338.  
  1339.     if (Nextscreen == NULL)
  1340.         return 0;
  1341.  
  1342.     if (invalid)
  1343.         NumLineSizes = 0;
  1344.  
  1345.     if (nlines > (Rows - 1 - row))
  1346.         nlines = Rows - 1 - row;
  1347.  
  1348.     if (RedrawingDisabled || nlines <= 0 ||
  1349.                 ((T_DL == NULL || *T_DL == NUL) &&
  1350.                 (T_CDL == NULL || *T_CDL == NUL) &&
  1351.                 row != 0))
  1352.         return 0;
  1353.  
  1354.     if (Rows - nlines < 5)    /* only a few lines left: redraw is faster */
  1355.     {
  1356.         screenclear();        /* will set NumLineSizes to 0 */
  1357.         return 0;
  1358.     }
  1359.  
  1360.     windgoto((int)Rows - 1, 0);        /* delete any garbage that may be */
  1361.     clear_line();                    /* on the bottom line */
  1362.     redraw_msg = TRUE;
  1363.  
  1364.     /* delete the lines */
  1365.     if (T_CDL && *T_CDL) 
  1366.     {
  1367.         windgoto(row, 0);
  1368.         if (nlines == 1 && T_DL && *T_DL)
  1369.             outstr(T_DL);
  1370.         else
  1371.             outstr(tgoto(T_CDL, 0, nlines));
  1372.     } 
  1373.     else
  1374.     {
  1375.         if (row == 0)
  1376.         {
  1377.             if (Rows != Rows_max)
  1378.                 windgoto((int)Rows_max - 1, 0);
  1379.             for (i = 0; i < nlines; i++) 
  1380.                 outchar('\n');
  1381.         }
  1382.         else
  1383.         {
  1384.             for (i = 0; i < nlines; i++) 
  1385.             {
  1386.                 windgoto(row, 0);
  1387.                 outstr(T_DL);           /* delete a line */
  1388.             }
  1389.         }
  1390.     }
  1391.  
  1392.     /*
  1393.      * Now shift LinePointers nlines up to reflect the deleted lines.
  1394.      * Clear the deleted lines.
  1395.      */
  1396.     for (i = 0; i < nlines; ++i)
  1397.     {
  1398.         j = row + i;
  1399.         temp = LinePointers[j];
  1400.         while ((j += nlines) < Rows - 1)
  1401.                 LinePointers[j - nlines] = LinePointers[j];
  1402.         LinePointers[j - nlines] = temp;
  1403.         memset((char *)temp, ' ', (size_t)Columns);
  1404.     }
  1405.     return 1;
  1406. }
  1407.  
  1408.     void
  1409. showmode()
  1410. {
  1411.     if ((p_smd && (State == INSERT || State == REPLACE)) || Recording)
  1412.     {
  1413.         gotocmdline(TRUE, NUL);
  1414.         if (p_smd)
  1415.         {
  1416.             if (State == INSERT || State == REPLACE)
  1417.             {
  1418.                 outstrn("-- ");
  1419.                 if (p_ri)
  1420.                     outstrn("REVERSE ");
  1421.                 if (State == INSERT)
  1422.                     outstrn("INSERT --");
  1423.                 else
  1424.                     outstrn("REPLACE --");
  1425.             }
  1426.         }
  1427.         if (Recording)
  1428.             outstrn("recording");
  1429.     }
  1430.     showruler(1);
  1431. }
  1432.  
  1433. /*
  1434.  * delete mode message
  1435.  */
  1436.     void
  1437. delmode()
  1438. {
  1439.     if (Recording)
  1440.         msg("recording");
  1441.     else
  1442.         msg("");
  1443. }
  1444.  
  1445. /*
  1446.  * if ruler option is set: show current cursor position
  1447.  * if always is FALSE, only print if position has changed
  1448.  */
  1449.     void
  1450. showruler(always)
  1451.     int        always;
  1452. {
  1453.     static linenr_t    oldlnum = 0;
  1454.     static colnr_t    oldcol = 0;
  1455.     static int        oldlen = 0;
  1456.     int                newlen;
  1457.     char            buffer[20];
  1458.  
  1459.     if (p_ru && (redraw_msg || always || Curpos.lnum != oldlnum || Cursvcol != oldcol))
  1460.     {
  1461.         windgoto((int)Rows - 1, ru_col);
  1462.         /*
  1463.          * Some sprintfs return the lenght, some return a pointer.
  1464.          * To avoid portability problems we use strlen here.
  1465.          */
  1466.         sprintf(buffer, "%ld,%d", Curpos.lnum, (int)Curpos.col + 1);
  1467.         newlen = strlen(buffer);
  1468.         if (Curpos.col != Cursvcol)
  1469.         {
  1470.             sprintf(buffer + newlen, "-%d", Cursvcol + 1);
  1471.             newlen = strlen(buffer);
  1472.         }
  1473.         outstrn(buffer);
  1474.         while (newlen < oldlen)
  1475.         {
  1476.             outchar(' ');
  1477.             --oldlen;
  1478.         }
  1479.         oldlen = newlen;
  1480.         oldlnum = Curpos.lnum;
  1481.         oldcol = Cursvcol;
  1482.         redraw_msg = FALSE;
  1483.     }
  1484. }
  1485.  
  1486. /*
  1487.  * Clear a line. The cursor must be at the first char of the line.
  1488.  */
  1489.     void
  1490. clear_line()
  1491. {
  1492.     register int i;
  1493.  
  1494.     if (T_EL != NULL && *T_EL != NUL)
  1495.         outstr(T_EL);
  1496.     else
  1497.         for (i = 1; i < Columns; ++i)
  1498.             outchar(' ');
  1499. }
  1500.