home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 191.lha / Less_v1.3 / Sources / line.c < prev    next >
C/C++ Source or Header  |  1988-04-28  |  11KB  |  489 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.  * We keep track of the PRINTABLE length of the line as it is being built.
  6.  */
  7.  
  8. #include "less.h"
  9.  
  10. static char linebuf[1024];    /* Buffer which holds the current output line */
  11. static char *curr;        /* Pointer into linebuf */
  12. static int column;        /* Printable length, accounting for
  13.                    backspaces, etc. */
  14. /*
  15.  * A ridiculously complex state machine takes care of backspaces 
  16.  * when in BS_SPECIAL mode.  The complexity arises from the attempt
  17.  * to deal with all cases, especially involving long lines with underlining,
  18.  * boldfacing or whatever.  There are still some cases which will break it.
  19.  *
  20.  * There are four states:
  21.  *    LN_NORMAL is the normal state (not in underline mode).
  22.  *    LN_UNDERLINE means we are in underline mode.  We expect to get
  23.  *        either a sequence like "_\bX" or "X\b_" to continue
  24.  *        underline mode, or anything else to end underline mode.
  25.  *    LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
  26.  *        like "X\bX\b...X\bX" to continue boldface mode, or anything
  27.  *        else to end boldface mode.
  28.  *    LN_UL_X means we are one character after LN_UNDERLINE
  29.  *        (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  30.  *    LN_UL_XB means we are one character after LN_UL_X 
  31.  *        (we have gotten the backspace in "_\bX" or "X\b_";
  32.  *        we expect one more ordinary character, 
  33.  *        which will put us back in state LN_UNDERLINE).
  34.  *    LN_BO_X means we are one character after LN_BOLDFACE
  35.  *        (we have gotten the 'X' in "X\bX").
  36.  *    LN_BO_XB means we are one character after LN_BO_X
  37.  *        (we have gotten the backspace in "X\bX";
  38.  *        we expect one more 'X' which will put us back
  39.  *        in LN_BOLDFACE).
  40.  */
  41. static int ln_state;        /* Currently in normal/underline/bold/etc mode? */
  42. #define    LN_NORMAL    0    /* Not in underline, boldface or whatever mode */
  43. #define    LN_UNDERLINE    1    /* In underline, need next char */
  44. #define    LN_UL_X        2    /* In underline, got char, need \b */
  45. #define    LN_UL_XB    3    /* In underline, got char & \b, need one more */
  46. #define    LN_BOLDFACE    4    /* In boldface, need next char */
  47. #define    LN_BO_X        5    /* In boldface, got char, need \b */
  48. #define    LN_BO_XB    6    /* In boldface, got char & \b, need same char */
  49.  
  50. public char *line;        /* Pointer to the current line.
  51.                    Usually points to linebuf. */
  52.  
  53. extern int bs_mode;
  54. extern int tabstop;
  55. extern int bo_width, be_width;
  56. extern int ul_width, ue_width;
  57. extern int sc_width, sc_height;
  58.  
  59. /*
  60.  * Rewind the line buffer.
  61.  */
  62.     public void
  63. prewind()
  64. {
  65.     line = curr = linebuf;
  66.     ln_state = LN_NORMAL;
  67.     column = 0;
  68. }
  69.  
  70. /*
  71.  * Append a character to the line buffer.
  72.  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  73.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  74.  */
  75.  
  76. #define    NEW_COLUMN(newcol)    if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
  77.                     return (1); else column = (newcol)
  78.  
  79.     public int
  80. pappend(c)
  81.     int c;
  82. {
  83.     if (c == '\0')
  84.     {
  85.         /*
  86.          * Terminate any special modes, if necessary.
  87.          * Append a '\0' to the end of the line.
  88.          */
  89.         switch (ln_state)
  90.         {
  91.         case LN_UL_X:
  92.             curr[0] = curr[-1];
  93.             curr[-1] = UE_CHAR;
  94.             curr++;
  95.             break;
  96.         case LN_BO_X:
  97.             curr[0] = curr[-1];
  98.             curr[-1] = BE_CHAR;
  99.             curr++;
  100.             break;
  101.         case LN_UL_XB:
  102.         case LN_UNDERLINE:
  103.             *curr++ = UE_CHAR;
  104.             break;
  105.         case LN_BO_XB:
  106.         case LN_BOLDFACE:
  107.             *curr++ = BE_CHAR;
  108.             break;
  109.         }
  110.         ln_state = LN_NORMAL;
  111.         *curr = '\0';
  112.         return (0);
  113.     }
  114.  
  115.     if (curr > linebuf + sizeof(linebuf) - 12)
  116.         /*
  117.          * Almost out of room in the line buffer.
  118.          * Don't take any chances.
  119.          * {{ Linebuf is supposed to be big enough that this
  120.          *    will never happen, but may need to be made 
  121.          *    bigger for wide screens or lots of backspaces. }}
  122.          */
  123.         return (1);
  124.  
  125.     if (bs_mode == BS_SPECIAL)
  126.     {
  127.         /*
  128.          * Advance the state machine.
  129.          */
  130.         switch (ln_state)
  131.         {
  132.         case LN_NORMAL:
  133.             if (curr <= linebuf + 1 || curr[-1] != '\b')
  134.                 break;
  135.  
  136.             if (c == curr[-2])
  137.                 goto enter_boldface;
  138.             if (c == '_' || curr[-2] == '_')
  139.                 goto enter_underline;
  140.             curr -= 2;
  141.             break;
  142.  
  143. enter_boldface:
  144.             /*
  145.              * We have "X\bX" (including the current char).
  146.              * Switch into boldface mode.
  147.              */
  148.             if (column + bo_width + be_width + 1 >= sc_width)
  149.                 /*
  150.                  * Not enough room left on the screen to 
  151.                  * enter and exit boldface mode.
  152.                  */
  153.                 return (1);
  154.  
  155.             if (bo_width > 0 && 
  156.                 curr > linebuf + 2 && curr[-3] == ' ')
  157.             {
  158.                 /*
  159.                  * Special case for magic cookie terminals:
  160.                  * if the previous char was a space, replace 
  161.                  * it with the "enter boldface" sequence.
  162.                  */
  163.                 curr[-3] = BO_CHAR;
  164.                 column += bo_width-1;
  165.             } else
  166.             {
  167.                 curr[-1] = curr[-2];
  168.                 curr[-2] = BO_CHAR;
  169.                 column += bo_width;
  170.                 curr++;
  171.             }
  172.             goto ln_bo_xb_case;
  173.  
  174. enter_underline:
  175.             /*
  176.              * We have either "_\bX" or "X\b_" (including
  177.              * the current char).  Switch into underline mode.
  178.              */
  179.             if (column + ul_width + ue_width + 1 >= sc_width)
  180.                 /*
  181.                  * Not enough room left on the screen to 
  182.                  * enter and exit underline mode.
  183.                  */
  184.                 return (1);
  185.  
  186.             if (ul_width > 0 && 
  187.                 curr > linebuf + 2 && curr[-3] == ' ')
  188.             {
  189.                 /*
  190.                  * Special case for magic cookie terminals:
  191.                  * if the previous char was a space, replace 
  192.                  * it with the "enter underline" sequence.
  193.                  */
  194.                 curr[-3] = UL_CHAR;
  195.                 column += ul_width-1;
  196.             } else
  197.             {
  198.                 curr[-1] = curr[-2];
  199.                 curr[-2] = UL_CHAR;
  200.                 column += ul_width;
  201.                 curr++;
  202.             }
  203.             goto ln_ul_xb_case;
  204.             /*NOTREACHED*/
  205.         case LN_UL_XB:
  206.             /*
  207.              * Termination of a sequence "_\bX" or "X\b_".
  208.              */
  209.             if (c != '_' && curr[-2] != '_' && c == curr[-2])
  210.             {
  211.                 /*
  212.                  * We seem to have run on from underlining
  213.                  * into boldfacing - this is a nasty fix, but
  214.                  * until this whole routine is rewritten as a
  215.                  * real DFA, ...  well ...
  216.                  */
  217.                 curr[0] = curr[-2];
  218.                 curr[-2] = UE_CHAR;
  219.                 curr[-1] = BO_CHAR;
  220.                 curr += 2; /* char & non-existent backspace */
  221.                 ln_state = LN_BO_XB;
  222.                 goto ln_bo_xb_case;
  223.             }
  224. ln_ul_xb_case:
  225.             if (c == '_')
  226.                 c = curr[-2];
  227.             curr -= 2;
  228.             ln_state = LN_UNDERLINE;
  229.             break;
  230.         case LN_BO_XB:
  231.             /*
  232.              * Termination of a sequnce "X\bX".
  233.              */
  234.             if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
  235.             {
  236.                 /*
  237.                  * We seem to have run on from
  238.                  * boldfacing into underlining.
  239.                  */
  240.                 curr[0] = curr[-2];
  241.                 curr[-2] = BE_CHAR;
  242.                 curr[-1] = UL_CHAR;
  243.                 curr += 2; /* char & non-existent backspace */
  244.                 ln_state = LN_UL_XB;
  245.                 goto ln_ul_xb_case;
  246.             }
  247. ln_bo_xb_case:
  248.             curr -= 2;
  249.             ln_state = LN_BOLDFACE;
  250.             break;
  251.         case LN_UNDERLINE:
  252.             if (column + ue_width + bo_width + 1 + be_width >= sc_width)
  253.                 /*
  254.                  * We have just barely enough room to 
  255.                  * exit underline mode and handle a possible
  256.                  * underline/boldface run on mixup.
  257.                  */
  258.                 return (1);
  259.             ln_state = LN_UL_X;
  260.             break;
  261.         case LN_BOLDFACE:
  262.             if (c == '\b')
  263.             {
  264.                 ln_state = LN_BO_XB;
  265.                 break;
  266.             }
  267.             if (column + be_width + ul_width + 1 + ue_width >= sc_width)
  268.                 /*
  269.                  * We have just barely enough room to 
  270.                  * exit underline mode and handle a possible
  271.                  * underline/boldface run on mixup.
  272.                  */
  273.                 return (1);
  274.             ln_state = LN_BO_X;
  275.             break;
  276.         case LN_UL_X:
  277.             if (c == '\b')
  278.                 ln_state = LN_UL_XB;
  279.             else
  280.             {
  281.                 /*
  282.                  * Exit underline mode.
  283.                  * We have to shuffle the chars a bit
  284.                  * to make this work.
  285.                  */
  286.                 curr[0] = curr[-1];
  287.                 curr[-1] = UE_CHAR;
  288.                 column += ue_width;
  289.                 if (ue_width > 0 && curr[0] == ' ')
  290.                     /*
  291.                      * Another special case for magic
  292.                      * cookie terminals: if the next
  293.                      * char is a space, replace it
  294.                      * with the "exit underline" sequence.
  295.                      */
  296.                     column--;
  297.                 else
  298.                     curr++;
  299.                 ln_state = LN_NORMAL;
  300.             } 
  301.             break;
  302.         case LN_BO_X:
  303.             if (c == '\b')
  304.                 ln_state = LN_BO_XB;
  305.             else
  306.             {
  307.                 /*
  308.                  * Exit boldface mode.
  309.                  * We have to shuffle the chars a bit
  310.                  * to make this work.
  311.                  */
  312.                 curr[0] = curr[-1];
  313.                 curr[-1] = BE_CHAR;
  314.                 column += be_width;
  315.                 if (be_width > 0 && curr[0] == ' ')
  316.                     /*
  317.                      * Another special case for magic
  318.                      * cookie terminals: if the next
  319.                      * char is a space, replace it
  320.                      * with the "exit boldface" sequence.
  321.                      */
  322.                     column--;
  323.                 else
  324.                     curr++;
  325.                 ln_state = LN_NORMAL;
  326.             } 
  327.             break;
  328.         }
  329.     }
  330.     
  331.     if (c == '\t') 
  332.     {
  333.         /*
  334.          * Expand a tab into spaces.
  335.          */
  336.         do
  337.         {
  338.             NEW_COLUMN(column+1);
  339.         } while ((column % tabstop) != 0);
  340.         *curr++ = '\t';
  341.         return (0);
  342.     }
  343.  
  344.     if (c == '\b')
  345.     {
  346.         if (bs_mode == BS_CONTROL)
  347.         {
  348.             /*
  349.              * Treat backspace as a control char: output "^H".
  350.              */
  351.             NEW_COLUMN(column+2);
  352.             *curr++ = ('H' | 0200);
  353.         } else
  354.         {
  355.             /*
  356.              * Output a real backspace.
  357.              */
  358.             column--;
  359.             *curr++ = '\b';
  360.         }
  361.         return (0);
  362.     } 
  363.  
  364.     if (control_char(c))
  365.     {
  366.         /*
  367.          * Put a "^X" into the buffer.
  368.          * The 0200 bit is used to tell put_line() to prefix
  369.          * the char with a ^.  We don't actually put the ^
  370.          * in the buffer because we sometimes need to move
  371.          * chars around, and such movement might separate 
  372.          * the ^ from its following character.
  373.          * {{ This should be redone so that we can use an
  374.          *    8 bit (e.g. international) character set. }}
  375.          */
  376.         NEW_COLUMN(column+2);
  377.         *curr++ = (carat_char(c) | 0200);
  378.         return (0);
  379.     }
  380.  
  381.     /*
  382.      * Ordinary character.  Just put it in the buffer.
  383.      */
  384.     NEW_COLUMN(column+1);
  385.     *curr++ = c;
  386.     return (0);
  387. }
  388.  
  389. /*
  390.  * Analogous to forw_line(), but deals with "raw lines":
  391.  * lines which are not split for screen width.
  392.  * {{ This is supposed to be more efficient than forw_line(). }}
  393.  */
  394.     public POSITION
  395. forw_raw_line(curr_pos)
  396.     POSITION curr_pos;
  397. {
  398.     register char *p;
  399.     register int c;
  400.     POSITION new_pos;
  401.  
  402.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  403.         (c = ch_forw_get()) == EOF)
  404.         return (NULL_POSITION);
  405.  
  406.     p = linebuf;
  407.  
  408.     for (;;)
  409.     {
  410.         if (c == '\n' || c == EOF)
  411.         {
  412.             new_pos = ch_tell();
  413.             break;
  414.         }
  415.         if (p >= &linebuf[sizeof(linebuf)-1])
  416.         {
  417.             /*
  418.              * Overflowed the input buffer.
  419.              * Pretend the line ended here.
  420.              * {{ The line buffer is supposed to be big
  421.              *    enough that this never happens. }}
  422.              */
  423.             new_pos = ch_tell() - 1;
  424.             break;
  425.         }
  426.         *p++ = c;
  427.         c = ch_forw_get();
  428.     }
  429.     *p = '\0';
  430.     line = linebuf;
  431.     return (new_pos);
  432. }
  433.  
  434. /*
  435.  * Analogous to back_line(), but deals with "raw lines".
  436.  * {{ This is supposed to be more efficient than back_line(). }}
  437.  */
  438.     public POSITION
  439. back_raw_line(curr_pos)
  440.     POSITION curr_pos;
  441. {
  442.     register char *p;
  443.     register int c;
  444.     POSITION new_pos;
  445.  
  446.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  447.         ch_seek(curr_pos-1))
  448.         return (NULL_POSITION);
  449.  
  450.     p = &linebuf[sizeof(linebuf)];
  451.     *--p = '\0';
  452.  
  453.     for (;;)
  454.     {
  455.         c = ch_back_get();
  456.         if (c == '\n')
  457.         {
  458.             /*
  459.              * This is the newline ending the previous line.
  460.              * We have hit the beginning of the line.
  461.              */
  462.             new_pos = ch_tell() + 1;
  463.             break;
  464.         }
  465.         if (c == EOF)
  466.         {
  467.             /*
  468.              * We have hit the beginning of the file.
  469.              * This must be the first line in the file.
  470.              * This must, of course, be the beginning of the line.
  471.              */
  472.             new_pos = (POSITION)0;
  473.             break;
  474.         }
  475.         if (p <= linebuf)
  476.         {
  477.             /*
  478.              * Overflowed the input buffer.
  479.              * Pretend the line ended here.
  480.              */
  481.             new_pos = ch_tell() + 1;
  482.             break;
  483.         }
  484.         *--p = c;
  485.     }
  486.     line = p;
  487.     return (new_pos);
  488. }
  489.