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