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