home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / less_332.lzh / less_332 / line.c < prev    next >
Text File  |  1998-03-03  |  14KB  |  633 lines

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