home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / LINE.C < prev    next >
C/C++ Source or Header  |  1994-09-02  |  12KB  |  583 lines

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