home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / less / line.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  11KB  |  533 lines

  1. /*
  2.  * Routines to manipulate the "line buffer".
  3.  * The line buffer holds a line of output as it is being built
  4.  * in preparation for output to the screen.
  5.  */
  6.  
  7. #include "less.h"
  8.  
  9. static char linebuf[1024];    /* Buffer which holds the current output line */
  10. static char attr[1024];        /* Extension of linebuf to hold attributes */
  11. static int curr;        /* Index into linebuf */
  12. static int column;        /* Printable length, accounting for
  13.                    backspaces, etc. */
  14. static int overstrike;        /* Next char should overstrike previous char */
  15. static int is_null_line;    /* There is no current line */
  16. static char pendc;
  17.  
  18. extern int bs_mode;
  19. extern int tabstop;
  20. extern int linenums;
  21. extern int ctldisp;
  22. extern int twiddle;
  23. extern int auto_wrap, ignaw;
  24. extern int bo_s_width, bo_e_width;
  25. extern int ul_s_width, ul_e_width;
  26. extern int bl_s_width, bl_e_width;
  27. extern int sc_width, sc_height;
  28.  
  29. #ifdef OS2
  30. static int do_append();
  31. #endif
  32.  
  33. /*
  34.  * Rewind the line buffer.
  35.  */
  36.     public void
  37. prewind()
  38. {
  39.     curr = 0;
  40.     column = 0;
  41.     overstrike = 0;
  42.     is_null_line = 0;
  43.     pendc = '\0';
  44. }
  45.  
  46. /*
  47.  * Insert the line number (of the given position) into the line buffer.
  48.  */
  49.     public void
  50. plinenum(pos)
  51.     POSITION pos;
  52. {
  53.     register int lno;
  54.     register int i;
  55.     register int n;
  56.  
  57.     /*
  58.      * We display the line number at the start of each line
  59.      * only if the -N option is set.
  60.      */
  61.     if (linenums != 2)
  62.         return;
  63.  
  64.     /*
  65.      * Get the line number and put it in the current line.
  66.      * {{ Note: since find_linenum calls forw_raw_line,
  67.      *    it may seek in the input file, requiring the caller
  68.      *    of plinenum to re-seek if necessary. }}
  69.      */
  70.     lno = find_linenum(pos);
  71.  
  72.     sprintf(&linebuf[curr], "%6d", lno);
  73.     n = strlen(&linebuf[curr]);
  74.     column += n;
  75.     for (i = 0;  i < n;  i++)
  76.         attr[curr++] = 0;
  77.  
  78.     /*
  79.      * Append enough spaces to bring us to the next tab stop.
  80.      * {{ We could avoid this at the cost of adding some
  81.      *    complication to the tab stop logic in pappend(). }}
  82.      */
  83.     do
  84.     {
  85.         linebuf[curr] = ' ';
  86.         attr[curr++] = 0;
  87.         column++;
  88.     } while ((column % tabstop) != 0);
  89. }
  90.  
  91. /*
  92.  * Return the printing width of the start (enter) sequence
  93.  * for a given character attribute.
  94.  */
  95.     int
  96. attr_swidth(a)
  97.     int a;
  98. {
  99.     switch (a)
  100.     {
  101.     case BOLD:    return (bo_s_width);
  102.     case UNDERLINE:    return (ul_s_width);
  103.     case BLINK:    return (bl_s_width);
  104.     }
  105.     return (0);
  106. }
  107.  
  108. /*
  109.  * Return the printing width of the end (exit) sequence
  110.  * for a given character attribute.
  111.  */
  112.     int
  113. attr_ewidth(a)
  114.     int a;
  115. {
  116.     switch (a)
  117.     {
  118.     case BOLD:    return (bo_e_width);
  119.     case UNDERLINE:    return (ul_e_width);
  120.     case BLINK:    return (bl_e_width);
  121.     }
  122.     return (0);
  123. }
  124.  
  125. /*
  126.  * Return the printing width of a given character and attribute,
  127.  * if the character were added to the current position in the line buffer.
  128.  * Adding a character with a given attribute may cause an enter or exit
  129.  * attribute sequence to be inserted, so this must be taken into account.
  130.  */
  131.     static int
  132. pwidth(c, a)
  133.     int c;
  134.     int a;
  135. {
  136.     register int w;
  137.  
  138.     if (c == '\b')
  139.         /*
  140.          * Backspace moves backwards one position.
  141.          */
  142.         return (-1);
  143.  
  144.     if (control_char(c))
  145.         /*
  146.          * Control characters do unpredicatable things,
  147.          * so we don't even try to guess; say it doesn't move.
  148.          * This can only happen if the -r flag is in effect.
  149.          */
  150.         return (0);
  151.  
  152.     /*
  153.      * Other characters take one space,
  154.      * plus the width of any attribute enter/exit sequence.
  155.      */
  156.     w = 1;
  157.     if (curr > 0 && attr[curr-1] != a)
  158.         w += attr_ewidth(attr[curr-1]);
  159.     if (a && (curr == 0 || attr[curr-1] != a))
  160.         w += attr_swidth(a);
  161.     return (w);
  162. }
  163.  
  164. /*
  165.  * Delete the previous character in the line buffer.
  166.  */
  167.     static void
  168. backc()
  169. {
  170.     curr--;
  171.     column -= pwidth(linebuf[curr], attr[curr]);
  172. }
  173.  
  174. /*
  175.  * Append a character and attribute to the line buffer.
  176.  */
  177.     static int
  178. storec(c, a)
  179.     int c;
  180.     int a;
  181. {
  182.     register int w;
  183.  
  184.     w = pwidth(c, a);
  185.     if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width)
  186.         /*
  187.          * Won't fit on screen.
  188.          */
  189.         return (1);
  190.  
  191.     if (curr >= sizeof(linebuf)-2)
  192.         /*
  193.          * Won't fit in line buffer.
  194.          */
  195.         return (1);
  196.  
  197.     /*
  198.      * Special handling for "magic cookie" terminals.
  199.      * If an attribute enter/exit sequence has a printing width > 0,
  200.      * and the sequence is adjacent to a space, delete the space.
  201.      * We just mark the space as invisible, to avoid having too
  202.      * many spaces deleted.
  203.      * {{ Note that even if the attribute width is > 1, we
  204.      *    delete only one space.  It's not worth trying to do more.
  205.      *    It's hardly worth doing this much. }}
  206.      */
  207.     if (curr > 0 && a != NORMAL &&
  208.         linebuf[curr-1] == ' ' && attr[curr-1] == NORMAL &&
  209.         attr_swidth(a) > 0)
  210.     {
  211.         /*
  212.          * We are about to append an enter-attribute sequence
  213.          * just after a space.  Delete the space.
  214.          */
  215.         attr[curr-1] = INVIS;
  216.         column--;
  217.     } else if (curr > 0 && attr[curr-1] != NORMAL &&
  218.         attr[curr-1] != INVIS && c == ' ' && a == NORMAL &&
  219.         attr_ewidth(attr[curr-1]) > 0)
  220.     {
  221.         /*
  222.          * We are about to append a space just after an
  223.          * exit-attribute sequence.  Delete the space.
  224.          */
  225.         a = INVIS;
  226.         column--;
  227.     }
  228.     /* End of magic cookie handling. */
  229.  
  230.     linebuf[curr] = c;
  231.     attr[curr] = a;
  232.     column += w;
  233.     return (0);
  234. }
  235.  
  236. /*
  237.  * Append a character to the line buffer.
  238.  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  239.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  240.  */
  241.     public int
  242. pappend(c)
  243.     register int c;
  244. {
  245.     if (pendc)
  246.     {
  247.         if (do_append(pendc))
  248.             /*
  249.              * Oops.  We've probably lost the char which
  250.              * was in pendc, since caller won't back up.
  251.              */
  252.             return (1);
  253.         pendc = '\0';
  254.     }
  255.  
  256.     if (c == '\r' && bs_mode == BS_SPECIAL)
  257.     {
  258.         /*
  259.          * Don't put the CR into the buffer until we see
  260.          * the next char.  If the next char is a newline,
  261.          * discard the CR.
  262.          */
  263.         pendc = c;
  264.         return (0);
  265.     }
  266.  
  267.     return (do_append(c));
  268. }
  269.  
  270.     static int
  271. do_append(c)
  272.     int c;
  273. {
  274.     register char *s;
  275.     register int a;
  276.  
  277. #define    STOREC(c,a)    if (storec((c),(a))) return (1); else curr++
  278.  
  279.     if (overstrike)
  280.     {
  281.         /*
  282.          * Overstrike the character at the current position
  283.          * in the line buffer.  This will cause either
  284.          * underline (if a "_" is overstruck),
  285.          * bold (if an identical character is overstruck),
  286.          * or just deletion of the character in the buffer.
  287.          */
  288.         overstrike = 0;
  289.         if (c == linebuf[curr])
  290.             STOREC(linebuf[curr], BOLD);
  291.         else if (c == '_')
  292.             STOREC(linebuf[curr], UNDERLINE);
  293.         else if (linebuf[curr] == '_')
  294.             STOREC(c, UNDERLINE);
  295.         else if (control_char(c))
  296.             goto do_control_char;
  297.         else
  298.             STOREC(c, NORMAL);
  299.     } else if (c == '\b')
  300.     {
  301.         switch (bs_mode)
  302.         {
  303.         case BS_NORMAL:
  304.             STOREC(c, NORMAL);
  305.             break;
  306.         case BS_CONTROL:
  307.             goto do_control_char;
  308.         case BS_SPECIAL:
  309.             if (curr == 0)
  310.                 break;
  311.             backc();
  312.             overstrike = 1;
  313.             break;
  314.         }
  315.     } else if (c == '\t')
  316.     {
  317.         /*
  318.          * Expand a tab into spaces.
  319.          */
  320.         do
  321.         {
  322.             STOREC(' ', NORMAL);
  323.         } while ((column % tabstop) != 0);
  324.     }
  325.         /* else if (c == '\r')
  326.           return(0); */
  327.         else if (control_char(c))
  328.     {
  329.     do_control_char:
  330.         if (ctldisp == 0)
  331.         {
  332.             /*
  333.              * Output as a normal character.
  334.              */
  335.             STOREC(c, NORMAL);
  336.         } else
  337.         {
  338.             /*
  339.              * Output in the (blinking) ^X format.
  340.              */
  341.             s = prchar(c);
  342.             a = BLINK;
  343.  
  344.             /*
  345.              * Make sure we can get the entire representation
  346.              * the character on this line.
  347.              */
  348.             if (column + strlen(s) +
  349.                 attr_swidth(a) + attr_ewidth(a) > sc_width)
  350.                 return (1);
  351.  
  352.             for ( ;  *s != 0;  s++)
  353.                 STOREC(*s, a);
  354.         }
  355.     } else
  356.     {
  357.         STOREC(c, NORMAL);
  358.     }
  359.  
  360.     return (0);
  361. }
  362.  
  363. /*
  364.  * Terminate the line in the line buffer.
  365.  */
  366.     public void
  367. pdone(endline)
  368.     int endline;
  369. {
  370.     register char c;
  371.  
  372.     if (pendc && (pendc != '\r' || !endline))
  373.         /*
  374.          * If we had a pending character, put it in the buffer.
  375.          * But discard a pending CR if we are at end of line
  376.          * (that is, discard the CR in a CR/LF sequence).
  377.          */
  378.         (void) do_append(pendc);
  379.  
  380.     /*
  381.      * Add a newline if necessary,
  382.      * and append a '\0' to the end of the line.
  383.      */
  384.     if (column < sc_width || !auto_wrap || ignaw)
  385.     {
  386.         linebuf[curr] = '\n';
  387.         attr[curr] = NORMAL;
  388.         curr++;
  389.     }
  390.     linebuf[curr] = '\0';
  391.     attr[curr] = NORMAL;
  392. }
  393.  
  394. /*
  395.  * Get a character from the current line.
  396.  * Return the character as the function return value,
  397.  * and the character attribute in *ap.
  398.  */
  399.     public int
  400. gline(i, ap)
  401.     register int i;
  402.     register int *ap;
  403. {
  404.     if (is_null_line)
  405.     {
  406.         /*
  407.          * If there is no current line, we pretend the line is
  408.          * either "~" or "", depending on the "twiddle" flag.
  409.          */
  410.         *ap = NORMAL;
  411.         if (twiddle)
  412.             return ("~\n"[i]);
  413.         return ("\n"[i]);
  414.     }
  415.  
  416.     *ap = attr[i];
  417.     return (linebuf[i] & 0377);
  418. }
  419.  
  420. /*
  421.  * Indicate that there is no current line.
  422.  */
  423.     public void
  424. null_line()
  425. {
  426.     is_null_line = 1;
  427. }
  428.  
  429. /*
  430.  * Analogous to forw_line(), but deals with "raw lines":
  431.  * lines which are not split for screen width.
  432.  * {{ This is supposed to be more efficient than forw_line(). }}
  433.  */
  434.     public POSITION
  435. forw_raw_line(curr_pos, linep)
  436.     POSITION curr_pos;
  437.     char **linep;
  438. {
  439.     register char *p;
  440.     register int c;
  441.     POSITION new_pos;
  442.  
  443.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  444.         (c = ch_forw_get()) == EOI)
  445.         return (NULL_POSITION);
  446.  
  447.     p = linebuf;
  448.  
  449.     for (;;)
  450.     {
  451.         if (c == '\n' || c == EOI)
  452.         {
  453.             new_pos = ch_tell();
  454.             break;
  455.         }
  456.         if (p >= &linebuf[sizeof(linebuf)-1])
  457.         {
  458.             /*
  459.              * Overflowed the input buffer.
  460.              * Pretend the line ended here.
  461.              * {{ The line buffer is supposed to be big
  462.              *    enough that this never happens. }}
  463.              */
  464.             new_pos = ch_tell() - 1;
  465.             break;
  466.         }
  467.         *p++ = c;
  468.         c = ch_forw_get();
  469.     }
  470.     *p = '\0';
  471.     if (linep != NULL)
  472.         *linep = linebuf;
  473.     return (new_pos);
  474. }
  475.  
  476. /*
  477.  * Analogous to back_line(), but deals with "raw lines".
  478.  * {{ This is supposed to be more efficient than back_line(). }}
  479.  */
  480.     public POSITION
  481. back_raw_line(curr_pos, linep)
  482.     POSITION curr_pos;
  483.     char **linep;
  484. {
  485.     register char *p;
  486.     register int c;
  487.     POSITION new_pos;
  488.  
  489.     if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
  490.         ch_seek(curr_pos-1))
  491.         return (NULL_POSITION);
  492.  
  493.     p = &linebuf[sizeof(linebuf)];
  494.     *--p = '\0';
  495.  
  496.     for (;;)
  497.     {
  498.         c = ch_back_get();
  499.         if (c == '\n')
  500.         {
  501.             /*
  502.              * This is the newline ending the previous line.
  503.              * We have hit the beginning of the line.
  504.              */
  505.             new_pos = ch_tell() + 1;
  506.             break;
  507.         }
  508.         if (c == EOI)
  509.         {
  510.             /*
  511.              * We have hit the beginning of the file.
  512.              * This must be the first line in the file.
  513.              * This must, of course, be the beginning of the line.
  514.              */
  515.             new_pos = ch_zero();
  516.             break;
  517.         }
  518.         if (p <= linebuf)
  519.         {
  520.             /*
  521.              * Overflowed the input buffer.
  522.              * Pretend the line ended here.
  523.              */
  524.             new_pos = ch_tell() + 1;
  525.             break;
  526.         }
  527.         *--p = c;
  528.     }
  529.     if (linep != NULL)
  530.         *linep = p;
  531.     return (new_pos);
  532. }
  533.