home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d9xx / d902 / less.lha / Less / Source / source.lha / line.c < prev    next >
C/C++ Source or Header  |  1993-02-21  |  24KB  |  624 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.  
  11. static char linebuf[1024];      /* Buffer which holds the current output line */
  12. static char *curr;              /* Pointer into linebuf */
  13. static int column;              /* Printable length, accounting for
  14.                                    backspaces, etc. */
  15. /*
  16.  * A ridiculously complex state machine takes care of backspaces
  17.  * when in BS_SPECIAL mode.  The complexity arises from the attempt
  18.  * to deal with all cases, especially involving long lines with underlining,
  19.  * boldfacing or whatever.  There are still some cases which will break it.
  20.  *
  21.  * If ANSIGR is defined, we also recognized ANSI Graphic Rendition commands
  22.  * for underline, boldface, italics, and inverse video.  As with the
  23.  * backspace modes, the emulation is imperfect and can be broken under
  24.  * certain rare circumstances.
  25.  *
  26.  * There are five (four, if not ANSIGR) states:
  27.  *      LN_NORMAL is the normal state (not in underline mode).
  28.  *      LN_UNDERLINE means we are in underline mode.  We expect to get
  29.  *              either a sequence like "_\bX" or "X\b_" to continue
  30.  *              underline mode, or anything else to end underline mode.
  31.  *      LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
  32.  *              like "X\bX\b...X\bX" to continue boldface mode, or anything
  33.  *              else to end boldface mode.
  34.  *      LN_UL_X means we are one character after LN_UNDERLINE
  35.  *              (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  36.  *      LN_UL_XB means we are one character after LN_UL_X
  37.  *              (we have gotten the backspace in "_\bX" or "X\b_";
  38.  *              we expect one more ordinary character,
  39.  *              which will put us back in state LN_UNDERLINE).
  40.  *      LN_BO_X means we are one character after LN_BOLDFACE
  41.  *              (we have gotten the 'X' in "X\bX").
  42.  *      LN_BO_XB means we are one character after LN_BO_X
  43.  *              (we have gotten the backspace in "X\bX";
  44.  *              we expect one more 'X' which will put us back
  45.  *              in LN_BOLDFACE).
  46.  *ifdef ANSIGR
  47.  *      LN_CSI  means we are in an ANSI Graphic Rendition sequence.
  48.  *              Variable AnsiLen is the number of characters we have seen
  49.  *              before this one in this sequence.  AnsiMode is the numeric
  50.  *              value of the first character after the CSI.
  51.  *endif
  52.  */
  53. static int ln_state;            /* Currently in normal/underline/bold/etc mode? */
  54. #define LN_NORMAL       0       /* Not in underline, boldface or whatever mode */
  55. #define LN_UNDERLINE    1       /* In underline, need next char */
  56. #define LN_UL_X         2       /* In underline, got char, need \b */
  57. #define LN_UL_XB        3       /* In underline, got char & \b, need one more */
  58. #define LN_BOLDFACE     4       /* In boldface, need next char */
  59. #define LN_BO_X         5       /* In boldface, got char, need \b */
  60. #define LN_BO_XB        6       /* In boldface, got char & \b, need same char */
  61. #ifdef ANSIGR
  62. #define LN_CSI          7       /* In an Ansi Graphic Rendition sequence */
  63.  
  64. static int AnsiLen, AnsiMode, AnsiNum;
  65. #endif
  66.  
  67. public char *line;              /* Pointer to the current line.
  68.                                    Usually points to linebuf. */
  69.  
  70. extern int bs_mode;
  71. extern int tabstop;
  72. extern int bo_width, be_width;
  73. extern int ul_width, ue_width;
  74. #ifdef AMIGA
  75. extern int it_width, ie_width;
  76. extern int nv_width, ne_width;
  77. #endif
  78. extern int sc_width, sc_height;
  79.  
  80. /*
  81.  * Rewind the line buffer.
  82.  */
  83. #ifdef __STDC__
  84. void prewind (void)
  85. #else
  86.         public void
  87. prewind()
  88. #endif
  89. {
  90.         line = curr = linebuf;
  91.         ln_state = LN_NORMAL;
  92.         column = 0;
  93. }
  94.  
  95. /*
  96.  * Append a character to the line buffer.
  97.  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  98.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  99.  */
  100.  
  101. #ifdef AMIGA
  102. /* ue_width is zero on the Amiga */
  103. #define NEW_COLUMN(newcol)      if ((newcol) > sc_width) \
  104.                                         return (1); else column = (newcol)
  105.  
  106. #else
  107. #define NEW_COLUMN(newcol)      if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
  108.                                         return (1); else column = (newcol)
  109. #endif
  110.  
  111. #ifdef __STDC__
  112. int pappend (int c)
  113. #else
  114.         public int
  115. pappend(c)
  116.         int c;
  117. #endif
  118. {
  119.         if (c == '\0')
  120.         {
  121.                 /*
  122.                  * Terminate any special modes, if necessary.
  123.                  * Append a '\0' to the end of the line.
  124.                  */
  125.                 switch (ln_state)
  126.                 {
  127.                 case LN_UL_X:
  128.                         curr[0] = curr[-1];
  129.                         curr[-1] = UE_CHAR;
  130.                         curr++;
  131.                         break;
  132.                 case LN_BO_X:
  133.                         curr[0] = curr[-1];
  134.                         curr[-1] = BE_CHAR;
  135.                         curr++;
  136.                         break;
  137.                 case LN_UL_XB:
  138.                 case LN_UNDERLINE:
  139.                         *curr++ = UE_CHAR;
  140.                         break;
  141.                 case LN_BO_XB:
  142.                 case LN_BOLDFACE:
  143.                         *curr++ = BE_CHAR;
  144.                         break;
  145.                 }
  146.                 ln_state = LN_NORMAL;
  147.                 *curr = '\0';
  148.                 return (0);
  149.         }
  150.  
  151.         if (curr > linebuf + sizeof(linebuf) - 12)
  152.                 /*
  153.                  * Almost out of room in the line buffer.
  154.                  * Don't take any chances.
  155.                  * {{ Linebuf is supposed to be big enough that this
  156.                  *    will never happen, but may need to be made
  157.                  *    bigger for wide screens or lots of backspaces. }}
  158.                  */
  159.                 return (1);
  160.  
  161.         if (bs_mode == BS_SPECIAL)
  162.         {
  163.                 /*
  164.                  * Advance the state machine.
  165.                  */
  166.                 switch (ln_state)
  167.                 {
  168. #ifdef ANSIGR
  169.                 case LN_CSI:
  170.                 /* interpret an ANSI control string.  We have seen an ESC
  171.                    or CSI (0x9b) and possibly some more characters that
  172.                    make a valid prefix of an ANSI set-graphics-mode cmd.
  173.                    As long as the prefix remains valid, nothing is put in
  174.                    the line buffer.  This avoids the buffer overflowing
  175.                    before we reach the end of some long ANSI sequence.
  176.                    If we get an invalid character in the sequence, we
  177.                    start printing control characters with the first thing
  178.                    we did not recognize.
  179.                 */
  180.                         if ( c == '[' && AnsiLen == 1 ) /* esc-[ ? */
  181.                             return 0;
  182.                         if ( c == ';' || c == 'm' )
  183.                             if ( AnsiNum >= 0 && AnsiNum < 8 )
  184.                                 AnsiMode = AnsiNum;
  185.                         if ( !isdigit(c) )
  186.                             AnsiNum = -1;
  187.                         if ( c == 'm' ) /* end of sequence ? */
  188.                         {
  189.                             ln_state = LN_NORMAL;
  190.                             switch ( AnsiMode )
  191.                             {
  192.                             /* Note: several NEW_COLUMN lines are commented
  193.                             out in the following code.  Even though this is
  194.                             Amiga-specific code, the 'column' *should* be
  195.                             reset in each of these cases.  It just so
  196.                             happens that all the xx_width are zero, so the
  197.                             whole thing is academic.
  198.                             */
  199.                             case -1: /* no mode set */
  200.                                     return 0;
  201.                             case 0:  /* normal */
  202.                                     *curr++ = UE_CHAR;
  203.                                     /* NEW_COLUMN(column+ue_width); */
  204.                                     return 0;
  205.                             case 1:  /* bold */
  206.                                     *curr++ = BO_CHAR;
  207.                                     /* NEW_COLUMN(column+bo_width); */
  208.                                     return 0;
  209.                             case 3:  /* Italic */
  210.                                     *curr++ = IT_CHAR;
  211.                                     /* NEW_COLUMN(column+it_width); */
  212.                                     return 0;
  213.                             case 4:  /* underline */
  214.                                     *curr++ = UL_CHAR;
  215.                                     /* NEW_COLUMN(column+ul_width); */
  216.                                     return 0;
  217.                             case 7:  /* inverse video */
  218.                                     *curr++ = NV_CHAR;
  219.                                     /* NEW_COLUMN(column+nv_width); */
  220.                                     return 0;
  221.                             default: return 0;
  222.                             }
  223.                         }
  224.                         else if ( isdigit(c) )
  225.                         {
  226.                             AnsiLen++;
  227.                             if (AnsiNum < 0) AnsiNum = 0;
  228.                             AnsiNum *= 10;
  229.                             AnsiNum += c - '0';
  230.                             return 0;
  231.                         }
  232.                         else if ( c == ';' )
  233.                         {
  234.                             AnsiLen++;
  235.                             return 0;
  236.                         }
  237.                         else ln_state = LN_NORMAL;
  238.                         /* v v v fall through v v v */
  239. #endif
  240.                 case LN_NORMAL:
  241. #ifdef ANSIGR
  242.                         if ( (unsigned char)c == 0x9b || c == 0x1b )
  243.                         {
  244.                                 ln_state = LN_CSI;
  245.                                 AnsiMode = 0;
  246.                                 AnsiLen = 1;
  247.                                 AnsiNum = -1;
  248.                                 return 0;
  249.                         }
  250. #endif
  251.                         if (curr <= linebuf + 1 || curr[-1] != '\b')
  252.                                 break;
  253.  
  254.                         if (c == curr[-2])
  255.                                 goto enter_boldface;
  256.                         if (c == '_' || curr[-2] == '_')
  257.                                 goto enter_underline;
  258.                         curr -= 2;
  259.                         break;
  260.  
  261. enter_boldface:
  262.                         /*
  263.                          * We have "X\bX" (including the current char).
  264.                          * Switch into boldface mode.
  265.                          */
  266.                         if (column + bo_width + be_width + 1 >= sc_width)
  267.                                 /*
  268.                                  * Not enough room left on the screen to
  269.                                  * enter and exit boldface mode.
  270.                                  */
  271.                                 return (1);
  272.  
  273.                         if (bo_width > 0 &&
  274.                             curr > linebuf + 2 && curr[-3] == ' ')
  275.                         {
  276.                                 /*
  277.                                  * Special case for magic cookie terminals:
  278.                                  * if the previous char was a space, replace
  279.                                  * it with the "enter boldface" sequence.
  280.                                  */
  281.                                 curr[-3] = BO_CHAR;
  282.                                 column += bo_width-1;
  283.                         } else
  284.                         {
  285.                                 curr[-1] = curr[-2];
  286.                                 curr[-2] = BO_CHAR;
  287.                                 column += bo_width;
  288.                                 curr++;
  289.                         }
  290.                         goto ln_bo_xb_case;
  291.  
  292. enter_underline:
  293.                         /*
  294.                          * We have either "_\bX" or "X\b_" (including
  295.                          * the current char).  Switch into underline mode.
  296.                          */
  297.                         if (column + ul_width + ue_width + 1 >= sc_width)
  298.                                 /*
  299.                                  * Not enough room left on the screen to
  300.                                  * enter and exit underline mode.
  301.                                  */
  302.                                 return (1);
  303.  
  304.                         if (ul_width > 0 &&
  305.                             curr > linebuf + 2 && curr[-3] == ' ')
  306.                         {
  307.                                 /*
  308.                                  * Special case for magic cookie terminals:
  309.                                  * if the previous char was a space, replace
  310.                                  * it with the "enter underline" sequence.
  311.                                  */
  312.                                 curr[-3] = UL_CHAR;
  313.                                 column += ul_width-1;
  314.                         } else
  315.                         {
  316.                                 curr[-1] = curr[-2];
  317.                                 curr[-2] = UL_CHAR;
  318.                                 column += ul_width;
  319.                                 curr++;
  320.                         }
  321.                         goto ln_ul_xb_case;
  322.                         /*NOTREACHED*/
  323.                 case LN_UL_XB:
  324.                         /*
  325.                          * Termination of a sequence "_\bX" or "X\b_".
  326.                          */
  327.                         if (c != '_' && curr[-2] != '_' && c == curr[-2])
  328.                         {
  329.                                 /*
  330.                                  * We seem to have run on from underlining
  331.                                  * into boldfacing - this is a nasty fix, but
  332.                                  * until this whole routine is rewritten as a
  333.                                  * real DFA, ...  well ...
  334.                                  */
  335.                                 curr[0] = curr[-2];
  336.                                 curr[-2] = UE_CHAR;
  337.                                 curr[-1] = BO_CHAR;
  338.                                 curr += 2; /* char & non-existent backspace */
  339.                                 ln_state = LN_BO_XB;
  340.                                 goto ln_bo_xb_case;
  341.                         }
  342. ln_ul_xb_case:
  343.                         if (c == '_')
  344.                                 c = curr[-2];
  345.                         curr -= 2;
  346.                         ln_state = LN_UNDERLINE;
  347.                         break;
  348.                 case LN_BO_XB:
  349.                         /*
  350.                          * Termination of a sequnce "X\bX".
  351.                          */
  352.                         if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
  353.                         {
  354.                                 /*
  355.                                  * We seem to have run on from
  356.                                  * boldfacing into underlining.
  357.                                  */
  358.                                 curr[0] = curr[-2];
  359.                                 curr[-2] = BE_CHAR;
  360.                                 curr[-1] = UL_CHAR;
  361.                                 curr += 2; /* char & non-existent backspace */
  362.                                 ln_state = LN_UL_XB;
  363.                                 goto ln_ul_xb_case;
  364.                         }
  365. ln_bo_xb_case:
  366.                         curr -= 2;
  367.                         ln_state = LN_BOLDFACE;
  368.                         break;
  369.                 case LN_UNDERLINE:
  370.                         if (column + ue_width + bo_width + 1 + be_width >= sc_width)
  371.                                 /*
  372.                                  * We have just barely enough room to
  373.                                  * exit underline mode and handle a possible
  374.                                  * underline/boldface run on mixup.
  375.                                  */
  376.                                 return (1);
  377.                         ln_state = LN_UL_X;
  378.                         break;
  379.                 case LN_BOLDFACE:
  380.                         if (c == '\b')
  381.                         {
  382.                                 ln_state = LN_BO_XB;
  383.                                 break;
  384.                         }
  385.                         if (column + be_width + ul_width + 1 + ue_width >= sc_width)
  386.                                 /*
  387.                                  * We have just barely enough room to
  388.                                  * exit underline mode and handle a possible
  389.                                  * underline/boldface run on mixup.
  390.                                  */
  391.                                 return (1);
  392.                         ln_state = LN_BO_X;
  393.                         break;
  394.                 case LN_UL_X:
  395.                         if (c == '\b')
  396.                                 ln_state = LN_UL_XB;
  397.                         else
  398.                         {
  399.                                 /*
  400.                                  * Exit underline mode.
  401.                                  * We have to shuffle the chars a bit
  402.                                  * to make this work.
  403.                                  */
  404.                                 curr[0] = curr[-1];
  405.                                 curr[-1] = UE_CHAR;
  406.                                 column += ue_width;
  407.                                 if (ue_width > 0 && curr[0] == ' ')
  408.                                         /*
  409.                                          * Another special case for magic
  410.                                          * cookie terminals: if the next
  411.                                          * char is a space, replace it
  412.                                          * with the "exit underline" sequence.
  413.                                          */
  414.                                         column--;
  415.                                 else
  416.                                         curr++;
  417.                                 ln_state = LN_NORMAL;
  418.                         }
  419.                         break;
  420.                 case LN_BO_X:
  421.                         if (c == '\b')
  422.                                 ln_state = LN_BO_XB;
  423.                         else
  424.                         {
  425.                                 /*
  426.                                  * Exit boldface mode.
  427.                                  * We have to shuffle the chars a bit
  428.                                  * to make this work.
  429.                                  */
  430.                                 curr[0] = curr[-1];
  431.                                 curr[-1] = BE_CHAR;
  432.                                 column += be_width;
  433.                                 if (be_width > 0 && curr[0] == ' ')
  434.                                         /*
  435.                                          * Another special case for magic
  436.                                          * cookie terminals: if the next
  437.                                          * char is a space, replace it
  438.                                          * with the "exit boldface" sequence.
  439.                                          */
  440.                                         column--;
  441.                                 else
  442.                                         curr++;
  443.                                 ln_state = LN_NORMAL;
  444.                         }
  445.                         break;
  446.                 }
  447.         }
  448.  
  449.         if (c == '\t')
  450.         {
  451.                 /*
  452.                  * Expand a tab into spaces.
  453.                  */
  454.                 do
  455.                 {
  456.                         NEW_COLUMN(column+1);
  457.                 } while ((column % tabstop) != 0);
  458.                 *curr++ = '\t';
  459.                 return (0);
  460.         }
  461.  
  462.         if (c == '\b')
  463.         {
  464.                 if (bs_mode == BS_CONTROL)
  465.                 {
  466.                         /*
  467.                          * Treat backspace as a control char: output "^H".
  468.                          */
  469.                         NEW_COLUMN(column+2);
  470. #ifdef EIGHTBIT
  471.                         *curr++ = c;
  472. #else
  473.                         *curr++ = ('H' | 0200);
  474. #endif
  475.                 } else
  476.                 {
  477.                         /*
  478.                          * Output a real backspace.
  479.                          */
  480.                         column--;
  481.                         *curr++ = '\b';
  482.                 }
  483.                 return (0);
  484.         }
  485.  
  486.         if (control_char(c))
  487.         {
  488.                 /*
  489.                  * Put a "^X" into the buffer.
  490.                  * The 0200 bit is used to tell put_line() to prefix
  491.                  * the char with a ^.  We don't actually put the ^
  492.                  * in the buffer because we sometimes need to move
  493.                  * chars around, and such movement might separate
  494.                  * the ^ from its following character.
  495.                  * {{ This should be redone so that we can use an
  496.                  *    8 bit (e.g. international) character set. }}
  497.                  */
  498.                 NEW_COLUMN(column+2);
  499.                 *curr++ =
  500. #ifdef EIGHTBIT
  501.                     c;
  502. #else
  503.                     (carat_char(c) | 0200);
  504. #endif
  505.                 return (0);
  506.         }
  507.  
  508.         /*
  509.          * Ordinary character.  Just put it in the buffer.
  510.          */
  511.         NEW_COLUMN(column+1);
  512.         *curr++ = c;
  513.         return (0);
  514. }
  515.  
  516. /*
  517.  * Analogous to forw_line(), but deals with "raw lines":
  518.  * lines which are not split for screen width.
  519.  * {{ This is supposed to be more efficient than forw_line(). }}
  520.  */
  521. #ifdef __STDC__
  522. POSITION forw_raw_line (POSITION curr_pos)
  523. #else
  524.         public POSITION
  525. forw_raw_line(curr_pos)
  526.         POSITION curr_pos;
  527. #endif
  528. {
  529.         register char *p;
  530.         register int c;
  531.         POSITION new_pos;
  532.  
  533.         if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  534.                 (c = ch_forw_get()) == EOF)
  535.                 return (NULL_POSITION);
  536.  
  537.         p = linebuf;
  538.  
  539.         for (;;)
  540.         {
  541.                 if (c == '\n' || c == EOF)
  542.                 {
  543.                         new_pos = ch_tell();
  544.                         break;
  545.                 }
  546.                 if (p >= &linebuf[sizeof(linebuf)-1])
  547.                 {
  548.                         /*
  549.                          * Overflowed the input buffer.
  550.                          * Pretend the line ended here.
  551.                          * {{ The line buffer is supposed to be big
  552.                          *    enough that this never happens. }}
  553.                          */
  554.                         new_pos = ch_tell() - 1;
  555.                         break;
  556.                 }
  557.                 *p++ = c;
  558.                 c = ch_forw_get();
  559.         }
  560.         *p = '\0';
  561.         line = linebuf;
  562.         return (new_pos);
  563. }
  564.  
  565. /*
  566.  * Analogous to back_line(), but deals with "raw lines".
  567.  * {{ This is supposed to be more efficient than back_line(). }}
  568.  */
  569. #ifdef __STDC__
  570. POSITION back_raw_line (POSITION curr_pos)
  571. #else
  572.         public POSITION
  573. back_raw_line(curr_pos)
  574.         POSITION curr_pos;
  575. #endif
  576. {
  577.         register char *p;
  578.         register int c;
  579.         POSITION new_pos;
  580.  
  581.         if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  582.                 ch_seek(curr_pos-1))
  583.                 return (NULL_POSITION);
  584.  
  585.         p = &linebuf[sizeof(linebuf)];
  586.         *--p = '\0';
  587.  
  588.         for (;;)
  589.         {
  590.                 c = ch_back_get();
  591.                 if (c == '\n')
  592.                 {
  593.                         /*
  594.                          * This is the newline ending the previous line.
  595.                          * We have hit the beginning of the line.
  596.                          */
  597.                         new_pos = ch_tell() + 1;
  598.                         break;
  599.                 }
  600.                 if (c == EOF)
  601.                 {
  602.                         /*
  603.                          * We have hit the beginning of the file.
  604.                          * This must be the first line in the file.
  605.                          * This must, of course, be the beginning of the line.
  606.                          */
  607.                         new_pos = (POSITION)0;
  608.                         break;
  609.                 }
  610.                 if (p <= linebuf)
  611.                 {
  612.                         /*
  613.                          * Overflowed the input buffer.
  614.                          * Pretend the line ended here.
  615.                          */
  616.                         new_pos = ch_tell() + 1;
  617.                         break;
  618.                 }
  619.                 *--p = c;
  620.         }
  621.         line = p;
  622.         return (new_pos);
  623. }
  624.