home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / LES177AS.ZIP / LINE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-09  |  10.8 KB  |  531 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], "%6u", 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 BLINKING:    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 BLINKING:    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 ((c != '\r') && 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
  379. #ifndef TURBOC
  380. || ctldisp == 0
  381. #endif
  382.     )
  383.     {
  384.         linebuf[curr] = '\n';
  385.         attr[curr] = NORMAL;
  386.         curr++;
  387.     }
  388.     linebuf[curr] = '\0';
  389.     attr[curr] = NORMAL;
  390. }
  391.  
  392. /*
  393.  * Get a character from the current line.
  394.  * Return the character as the function return value,
  395.  * and the character attribute in *ap.
  396.  */
  397.     public int
  398. gline(i, ap)
  399.     register int i;
  400.     register int *ap;
  401. {
  402.     if (is_null_line)
  403.     {
  404.         /*
  405.          * If there is no current line, we pretend the line is
  406.          * either "~" or "", depending on the "twiddle" flag.
  407.          */
  408.         *ap = NORMAL;
  409.         if (twiddle)
  410.             return ("~\n"[i]);
  411.         return ("\n"[i]);
  412.     }
  413.  
  414.     *ap = attr[i];
  415.     return (linebuf[i] & 0377);
  416. }
  417.  
  418. /*
  419.  * Indicate that there is no current line.
  420.  */
  421.     public void
  422. null_line()
  423. {
  424.     is_null_line = 1;
  425. }
  426.  
  427. /*
  428.  * Analogous to forw_line(), but deals with "raw lines":
  429.  * lines which are not split for screen width.
  430.  * {{ This is supposed to be more efficient than forw_line(). }}
  431.  */
  432.     public POSITION
  433. forw_raw_line(curr_pos, linep)
  434.     POSITION curr_pos;
  435.     char **linep;
  436. {
  437.     register char *p;
  438.     register int c;
  439.     POSITION new_pos;
  440.  
  441.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  442.         (c = ch_forw_get()) == EOI)
  443.         return (NULL_POSITION);
  444.  
  445.     p = linebuf;
  446.  
  447.     for (;;)
  448.     {
  449.         if (c == '\n' || c == EOI)
  450.         {
  451.             new_pos = ch_tell();
  452.             break;
  453.         }
  454.         if (p >= &linebuf[sizeof(linebuf)-1])
  455.         {
  456.             /*
  457.              * Overflowed the input buffer.
  458.              * Pretend the line ended here.
  459.              * {{ The line buffer is supposed to be big
  460.              *    enough that this never happens. }}
  461.              */
  462.             new_pos = ch_tell() - 1;
  463.             break;
  464.         }
  465.         *p++ = c;
  466.         c = ch_forw_get();
  467.     }
  468.     *p = '\0';
  469.     if (linep != NULL)
  470.         *linep = linebuf;
  471.     return (new_pos);
  472. }
  473.  
  474. /*
  475.  * Analogous to back_line(), but deals with "raw lines".
  476.  * {{ This is supposed to be more efficient than back_line(). }}
  477.  */
  478.     public POSITION
  479. back_raw_line(curr_pos, linep)
  480.     POSITION curr_pos;
  481.     char **linep;
  482. {
  483.     register char *p;
  484.     register int c;
  485.     POSITION new_pos;
  486.  
  487.     if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
  488.         ch_seek(curr_pos-1))
  489.         return (NULL_POSITION);
  490.  
  491.     p = &linebuf[sizeof(linebuf)];
  492.     *--p = '\0';
  493.  
  494.     for (;;)
  495.     {
  496.         c = ch_back_get();
  497.         if (c == '\n')
  498.         {
  499.             /*
  500.              * This is the newline ending the previous line.
  501.              * We have hit the beginning of the line.
  502.              */
  503.             new_pos = ch_tell() + 1;
  504.             break;
  505.         }
  506.         if (c == EOI)
  507.         {
  508.             /*
  509.              * We have hit the beginning of the file.
  510.              * This must be the first line in the file.
  511.              * This must, of course, be the beginning of the line.
  512.              */
  513.             new_pos = ch_zero();
  514.             break;
  515.         }
  516.         if (p <= linebuf)
  517.         {
  518.             /*
  519.              * Overflowed the input buffer.
  520.              * Pretend the line ended here.
  521.              */
  522.             new_pos = ch_tell() + 1;
  523.             break;
  524.         }
  525.         *--p = c;
  526.     }
  527.     if (linep != NULL)
  528.         *linep = p;
  529.     return (new_pos);
  530. }
  531.