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