home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / v / vim_src.zip / SCREEN.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  28KB  |  1,342 lines

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