[BACK]Return to line.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Diff for /src/usr.bin/less/line.c between version 1.1.1.2 and 1.1.1.3

version 1.1.1.2, 2003/04/13 18:21:21 version 1.1.1.3, 2011/09/16 17:47:06
Line 1 
Line 1 
 /*  /*
  * Copyright (C) 1984-2002  Mark Nudelman   * Copyright (C) 1984-2011  Mark Nudelman
  *   *
  * You may distribute under the terms of either the GNU General Public   * You may distribute under the terms of either the GNU General Public
  * License or the Less License, as specified in the README file.   * License or the Less License, as specified in the README file.
Line 16 
Line 16 
  */   */
   
 #include "less.h"  #include "less.h"
   #include "charset.h"
   
 #define IS_CONT(c)  (((c) & 0xC0) == 0x80)  static char *linebuf = NULL;    /* Buffer which holds the current output line */
   
 public char *linebuf = NULL;    /* Buffer which holds the current output line */  
 static char *attr = NULL;       /* Extension of linebuf to hold attributes */  static char *attr = NULL;       /* Extension of linebuf to hold attributes */
 public int size_linebuf = 0;    /* Size of line buffer (and attr buffer) */  public int size_linebuf = 0;    /* Size of line buffer (and attr buffer) */
   
 public int cshift;              /* Current left-shift of output line buffer */  static int cshift;              /* Current left-shift of output line buffer */
 public int hshift;              /* Desired left-shift of output line buffer */  public int hshift;              /* Desired left-shift of output line buffer */
 public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */  public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
 public int ntabstops = 1;       /* Number of tabstops */  public int ntabstops = 1;       /* Number of tabstops */
Line 36 
Line 35 
 static int last_overstrike = AT_NORMAL;  static int last_overstrike = AT_NORMAL;
 static int is_null_line;        /* There is no current line */  static int is_null_line;        /* There is no current line */
 static int lmargin;             /* Left margin */  static int lmargin;             /* Left margin */
 static int hilites;             /* Number of hilites in this line */  
 static char pendc;  static char pendc;
 static POSITION pendpos;  static POSITION pendpos;
 static char *end_ansi_chars;  static char *end_ansi_chars;
   static char *mid_ansi_chars;
   
   static int attr_swidth();
   static int attr_ewidth();
 static int do_append();  static int do_append();
   
   extern int sigs;
 extern int bs_mode;  extern int bs_mode;
 extern int linenums;  extern int linenums;
 extern int ctldisp;  extern int ctldisp;
Line 59 
Line 61 
 extern POSITION start_attnpos;  extern POSITION start_attnpos;
 extern POSITION end_attnpos;  extern POSITION end_attnpos;
   
   static char mbc_buf[MAX_UTF_CHAR_LEN];
   static int mbc_buf_len = 0;
   static int mbc_buf_index = 0;
   static POSITION mbc_pos;
   
 /*  /*
  * Initialize from environment variables.   * Initialize from environment variables.
  */   */
Line 68 
Line 75 
         end_ansi_chars = lgetenv("LESSANSIENDCHARS");          end_ansi_chars = lgetenv("LESSANSIENDCHARS");
         if (end_ansi_chars == NULL || *end_ansi_chars == '\0')          if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
                 end_ansi_chars = "m";                  end_ansi_chars = "m";
   
           mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
           if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
                   mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
   
         linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));          linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
         attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));          attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
         size_linebuf = LINEBUF_SIZE;          size_linebuf = LINEBUF_SIZE;
Line 76 
Line 88 
 /*  /*
  * Expand the line buffer.   * Expand the line buffer.
  */   */
         static int          static int
 expand_linebuf()  expand_linebuf()
 {  {
         int new_size = size_linebuf + LINEBUF_SIZE;          /* Double the size of the line buffer. */
           int new_size = size_linebuf * 2;
   
           /* Just realloc to expand the buffer, if we can. */
   #if HAVE_REALLOC
           char *new_buf = (char *) realloc(linebuf, new_size);
           char *new_attr = (char *) realloc(attr, new_size);
   #else
         char *new_buf = (char *) calloc(new_size, sizeof(char));          char *new_buf = (char *) calloc(new_size, sizeof(char));
         char *new_attr = (char *) calloc(new_size, sizeof(char));          char *new_attr = (char *) calloc(new_size, sizeof(char));
   #endif
         if (new_buf == NULL || new_attr == NULL)          if (new_buf == NULL || new_attr == NULL)
         {          {
                 if (new_attr != NULL)                  if (new_attr != NULL)
Line 90 
Line 110 
                         free(new_buf);                          free(new_buf);
                 return 1;                  return 1;
         }          }
   #if HAVE_REALLOC
           /*
            * We realloc'd the buffers; they already have the old contents.
            */
           #if 0
           memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
           memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
           #endif
   #else
           /*
            * We just calloc'd the buffers; copy the old contents.
            */
         memcpy(new_buf, linebuf, size_linebuf * sizeof(char));          memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
         memcpy(new_attr, attr, size_linebuf * sizeof(char));          memcpy(new_attr, attr, size_linebuf * sizeof(char));
         free(attr);          free(attr);
         free(linebuf);          free(linebuf);
   #endif
         linebuf = new_buf;          linebuf = new_buf;
         attr = new_attr;          attr = new_attr;
         size_linebuf = new_size;          size_linebuf = new_size;
Line 101 
Line 134 
 }  }
   
 /*  /*
    * Is a character ASCII?
    */
           public int
   is_ascii_char(ch)
           LWCHAR ch;
   {
           return (ch <= 0x7F);
   }
   
   /*
  * Rewind the line buffer.   * Rewind the line buffer.
  */   */
         public void          public void
Line 108 
Line 151 
 {  {
         curr = 0;          curr = 0;
         column = 0;          column = 0;
           cshift = 0;
         overstrike = 0;          overstrike = 0;
           last_overstrike = AT_NORMAL;
           mbc_buf_len = 0;
         is_null_line = 0;          is_null_line = 0;
         pendc = '\0';          pendc = '\0';
         lmargin = 0;          lmargin = 0;
         if (status_col)          if (status_col)
                 lmargin += 1;                  lmargin += 1;
 #if HILITE_SEARCH  
         hilites = 0;  
 #endif  
 }  }
   
 /*  /*
Line 150 
Line 193 
                 linebuf[curr] = ' ';                  linebuf[curr] = ' ';
                 if (start_attnpos != NULL_POSITION &&                  if (start_attnpos != NULL_POSITION &&
                     pos >= start_attnpos && pos < end_attnpos)                      pos >= start_attnpos && pos < end_attnpos)
                         attr[curr] = AT_STANDOUT;                          attr[curr] = AT_NORMAL|AT_HILITE;
                 else                  else
                         attr[curr] = 0;                          attr[curr] = AT_NORMAL;
                 curr++;                  curr++;
                 column++;                  column++;
         }          }
Line 190 
Line 233 
 }  }
   
 /*  /*
  * Determine how many characters are required to shift N columns.   * Shift the input line left.
    * This means discarding N printable chars at the start of the buffer.
  */   */
         static int          static void
 shift_chars(s, len)  pshift(shift)
         char *s;          int shift;
         int len;  
 {  {
         char *p = s;          LWCHAR prev_ch = 0;
           unsigned char c;
           int shifted = 0;
           int to;
           int from;
           int len;
           int width;
           int prev_attr;
           int next_attr;
   
           if (shift > column - lmargin)
                   shift = column - lmargin;
           if (shift > curr - lmargin)
                   shift = curr - lmargin;
   
           to = from = lmargin;
         /*          /*
          * Each char counts for one column, except ANSI color escape           * We keep on going when shifted == shift
          * sequences use no columns since they don't move the cursor.           * to get all combining chars.
          */           */
         while (*p != '\0' && len > 0)          while (shifted <= shift && from < curr)
         {          {
                 if (*p++ != ESC)                  c = linebuf[from];
                   if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
                 {                  {
                         len--;                          /* Keep cumulative effect.  */
                           linebuf[to] = c;
                           attr[to++] = attr[from++];
                           while (from < curr && linebuf[from])
                           {
                                   linebuf[to] = linebuf[from];
                                   attr[to++] = attr[from];
                                   if (!is_ansi_middle(linebuf[from++]))
                                           break;
                           }
                           continue;
                   }
   
                   width = 0;
   
                   if (!IS_ASCII_OCTET(c) && utf_mode)
                   {
                           /* Assumes well-formedness validation already done.  */
                           LWCHAR ch;
   
                           len = utf_len(c);
                           if (from + len > curr)
                                   break;
                           ch = get_wchar(linebuf + from);
                           if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
                                   width = is_wide_char(ch) ? 2 : 1;
                           prev_ch = ch;
                 } else                  } else
                 {                  {
                         while (*p != '\0')                          len = 1;
                           if (c == '\b')
                                   /* XXX - Incorrect if several '\b' in a row.  */
                                   width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
                           else if (!control_char(c))
                                   width = 1;
                           prev_ch = 0;
                   }
   
                   if (width == 2 && shift - shifted == 1) {
                           /* Should never happen when called by pshift_all().  */
                           attr[to] = attr[from];
                           /*
                            * Assume a wide_char will never be the first half of a
                            * combining_char pair, so reset prev_ch in case we're
                            * followed by a '\b'.
                            */
                           prev_ch = linebuf[to++] = ' ';
                           from += len;
                           shifted++;
                           continue;
                   }
   
                   /* Adjust width for magic cookies. */
                   prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
                   next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
                   if (!is_at_equiv(attr[from], prev_attr) &&
                           !is_at_equiv(attr[from], next_attr))
                   {
                           width += attr_swidth(attr[from]);
                           if (from + len < curr)
                                   width += attr_ewidth(attr[from]);
                           if (is_at_equiv(prev_attr, next_attr))
                         {                          {
                                 if (is_ansi_end(*p++))                                  width += attr_ewidth(prev_attr);
                                         break;                                  if (from + len < curr)
                                           width += attr_swidth(next_attr);
                         }                          }
                 }                  }
         }  
         return (p - s);  
 }  
   
 /*                  if (shift - shifted < width)
  * Determine how many characters are required to shift N columns (UTF version).                          break;
  * {{ FIXME: what about color escape sequences in UTF mode? }}                  from += len;
  */                  shifted += width;
         static int                  if (shifted < 0)
 utf_shift_chars(s, len)                          shifted = 0;
         char *s;  
         int len;  
 {  
         int ulen = 0;  
   
         while (*s != '\0' && len > 0)  
         {  
                 if (!IS_CONT(*s))  
                         len--;  
                 s++;  
                 ulen++;  
         }          }
         while (IS_CONT(*s))          while (from < curr)
         {          {
                 s++;                  linebuf[to] = linebuf[from];
                 ulen++;                  attr[to++] = attr[from++];
         }          }
         return (ulen);          curr = to;
           column -= shifted;
           cshift += shifted;
 }  }
   
 /*  /*
  * Shift the input line left.   *
  * This means discarding N printable chars at the start of the buffer.  
  */   */
         static void          public void
 pshift(shift)  pshift_all()
         int shift;  
 {  {
         int i;          pshift(column);
         int nchars;  
   
         if (shift > column - lmargin)  
                 shift = column - lmargin;  
         if (shift > curr - lmargin)  
                 shift = curr - lmargin;  
   
         if (utf_mode)  
                 nchars = utf_shift_chars(linebuf + lmargin, shift);  
         else  
                 nchars = shift_chars(linebuf + lmargin, shift);  
         if (nchars > curr)  
                 nchars = curr;  
         for (i = 0;  i < curr - nchars;  i++)  
         {  
                 linebuf[lmargin + i] = linebuf[lmargin + i + nchars];  
                 attr[lmargin + i] = attr[lmargin + i + nchars];  
         }  
         curr -= nchars;  
         column -= shift;  
         cshift += shift;  
 }  }
   
 /*  /*
Line 286 
Line 368 
 attr_swidth(a)  attr_swidth(a)
         int a;          int a;
 {  {
         switch (a)          int w = 0;
         {  
         case AT_BOLD:           return (bo_s_width);          a = apply_at_specials(a);
         case AT_UNDERLINE:      return (ul_s_width);  
         case AT_BLINK:          return (bl_s_width);          if (a & AT_UNDERLINE)
         case AT_STANDOUT:       return (so_s_width);                  w += ul_s_width;
         }          if (a & AT_BOLD)
         return (0);                  w += bo_s_width;
           if (a & AT_BLINK)
                   w += bl_s_width;
           if (a & AT_STANDOUT)
                   w += so_s_width;
   
           return w;
 }  }
   
 /*  /*
Line 304 
Line 392 
 attr_ewidth(a)  attr_ewidth(a)
         int a;          int a;
 {  {
         switch (a)          int w = 0;
         {  
         case AT_BOLD:           return (bo_e_width);          a = apply_at_specials(a);
         case AT_UNDERLINE:      return (ul_e_width);  
         case AT_BLINK:          return (bl_e_width);          if (a & AT_UNDERLINE)
         case AT_STANDOUT:       return (so_e_width);                  w += ul_e_width;
         }          if (a & AT_BOLD)
         return (0);                  w += bo_e_width;
           if (a & AT_BLINK)
                   w += bl_e_width;
           if (a & AT_STANDOUT)
                   w += so_e_width;
   
           return w;
 }  }
   
 /*  /*
Line 321 
Line 415 
  * attribute sequence to be inserted, so this must be taken into account.   * attribute sequence to be inserted, so this must be taken into account.
  */   */
         static int          static int
 pwidth(c, a)  pwidth(ch, a, prev_ch)
         int c;          LWCHAR ch;
         int a;          int a;
           LWCHAR prev_ch;
 {  {
         register int w;          int w;
   
         if (utf_mode && IS_CONT(c))          if (ch == '\b')
                 return (0);  
   
         if (c == '\b')  
                 /*                  /*
                  * Backspace moves backwards one position.                   * Backspace moves backwards one or two positions.
                    * XXX - Incorrect if several '\b' in a row.
                  */                   */
                 return (-1);                  return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
   
         if (control_char(c))          if (!utf_mode || is_ascii_char(ch))
                 /*          {
                  * Control characters do unpredicatable things,                  if (control_char((char)ch))
                  * so we don't even try to guess; say it doesn't move.                  {
                  * This can only happen if the -r flag is in effect.                          /*
                  */                           * Control characters do unpredictable things,
                 return (0);                           * so we don't even try to guess; say it doesn't move.
                            * This can only happen if the -r flag is in effect.
                            */
                           return (0);
                   }
           } else
           {
                   if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
                   {
                           /*
                            * Composing and combining chars take up no space.
                            *
                            * Some terminals, upon failure to compose a
                            * composing character with the character(s) that
                            * precede(s) it will actually take up one column
                            * for the composing character; there isn't much
                            * we could do short of testing the (complex)
                            * composition process ourselves and printing
                            * a binary representation when it fails.
                            */
                           return (0);
                   }
           }
   
         /*          /*
          * Other characters take one space,           * Other characters take one or two columns,
          * plus the width of any attribute enter/exit sequence.           * plus the width of any attribute enter/exit sequence.
          */           */
         w = 1;          w = 1;
         if (curr > 0 && attr[curr-1] != a)          if (is_wide_char(ch))
                   w++;
           if (curr > 0 && !is_at_equiv(attr[curr-1], a))
                 w += attr_ewidth(attr[curr-1]);                  w += attr_ewidth(attr[curr-1]);
         if (a && (curr == 0 || attr[curr-1] != a))          if ((apply_at_specials(a) != AT_NORMAL) &&
               (curr == 0 || !is_at_equiv(attr[curr-1], a)))
                 w += attr_swidth(a);                  w += attr_swidth(a);
         return (w);          return (w);
 }  }
   
 /*  /*
  * Delete the previous character in the line buffer.   * Delete to the previous base character in the line buffer.
    * Return 1 if one is found.
  */   */
         static void          static int
 backc()  backc()
 {  {
         curr--;          LWCHAR prev_ch;
         column -= pwidth(linebuf[curr], attr[curr]);          char *p = linebuf + curr;
           LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
           int width;
   
           /* This assumes that there is no '\b' in linebuf.  */
           while (   curr > lmargin
                  && column > lmargin
                  && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
           {
                   curr = p - linebuf;
                   prev_ch = step_char(&p, -1, linebuf + lmargin);
                   width = pwidth(ch, attr[curr], prev_ch);
                   column -= width;
                   if (width > 0)
                           return 1;
                   ch = prev_ch;
           }
   
           return 0;
 }  }
   
 /*  /*
Line 372 
Line 509 
         static int          static int
 in_ansi_esc_seq()  in_ansi_esc_seq()
 {  {
         int i;          char *p;
   
         /*          /*
          * Search backwards for either an ESC (which means we ARE in a seq);           * Search backwards for either an ESC (which means we ARE in a seq);
          * or an end char (which means we're NOT in a seq).           * or an end char (which means we're NOT in a seq).
          */           */
         for (i = curr-1;  i >= 0;  i--)          for (p = &linebuf[curr];  p > linebuf; )
         {          {
                 if (linebuf[i] == ESC)                  LWCHAR ch = step_char(&p, -1, linebuf);
                   if (IS_CSI_START(ch))
                         return (1);                          return (1);
                 if (is_ansi_end(linebuf[i]))                  if (!is_ansi_middle(ch))
                         return (0);                          return (0);
         }          }
         return (0);          return (0);
Line 392 
Line 530 
  * Is a character the end of an ANSI escape sequence?   * Is a character the end of an ANSI escape sequence?
  */   */
         public int          public int
 is_ansi_end(c)  is_ansi_end(ch)
         char c;          LWCHAR ch;
 {  {
         return (strchr(end_ansi_chars, c) != NULL);          if (!is_ascii_char(ch))
                   return (0);
           return (strchr(end_ansi_chars, (char) ch) != NULL);
 }  }
   
 /*  /*
    *
    */
           public int
   is_ansi_middle(ch)
           LWCHAR ch;
   {
           if (!is_ascii_char(ch))
                   return (0);
           if (is_ansi_end(ch))
                   return (0);
           return (strchr(mid_ansi_chars, (char) ch) != NULL);
   }
   
   /*
  * Append a character and attribute to the line buffer.   * Append a character and attribute to the line buffer.
  */   */
 #define STORE_CHAR(c,a,pos) \  #define STORE_CHAR(ch,a,rep,pos) \
         do { if (store_char((c),(a),(pos))) return (1); else curr++; } while (0)          do { \
                   if (store_char((ch),(a),(rep),(pos))) return (1); \
           } while (0)
   
         static int          static int
 store_char(c, a, pos)  store_char(ch, a, rep, pos)
         int c;          LWCHAR ch;
         int a;          int a;
           char *rep;
         POSITION pos;          POSITION pos;
 {  {
         register int w;          int w;
           int replen;
           char cs;
   
         if (a != AT_NORMAL)          w = (a & (AT_UNDERLINE|AT_BOLD));       /* Pre-use w.  */
                 last_overstrike = a;          if (w != AT_NORMAL)
                   last_overstrike = w;
   
 #if HILITE_SEARCH  #if HILITE_SEARCH
         if (is_hilited(pos, pos+1, 0))  
         {          {
                 /*                  int matches;
                  * This character should be highlighted.                  if (is_hilited(pos, pos+1, 0, &matches))
                  * Override the attribute passed in.                  {
                  */                          /*
                 a = AT_STANDOUT;                           * This character should be highlighted.
                 hilites++;                           * Override the attribute passed in.
                            */
                           if (a != AT_ANSI)
                                   a |= AT_HILITE;
                   }
         }          }
 #endif  #endif
   
         if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())          if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
           {
                   if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
                           /* Remove whole unrecognized sequence.  */
                           char *p = &linebuf[curr];
                           LWCHAR bch;
                           do {
                                   bch = step_char(&p, -1, linebuf);
                           } while (p > linebuf && !IS_CSI_START(bch));
                           curr = p - linebuf;
                           return 0;
                   }
                   a = AT_ANSI;    /* Will force re-AT_'ing around it.  */
                 w = 0;                  w = 0;
           }
           else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
           {
                   a = AT_ANSI;    /* Will force re-AT_'ing around it.  */
                   w = 0;
           }
         else          else
                 w = pwidth(c, a);          {
                   char *p = &linebuf[curr];
                   LWCHAR prev_ch = step_char(&p, -1, linebuf);
                   w = pwidth(ch, a, prev_ch);
           }
   
         if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)          if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
                 /*                  /*
                  * Won't fit on screen.                   * Won't fit on screen.
                  */                   */
                 return (1);                  return (1);
   
         if (curr >= size_linebuf-2)          if (rep == NULL)
         {          {
                   cs = (char) ch;
                   rep = &cs;
                   replen = 1;
           } else
           {
                   replen = utf_len(rep[0]);
           }
           if (curr + replen >= size_linebuf-6)
           {
                 /*                  /*
                  * Won't fit in line buffer.                   * Won't fit in line buffer.
                  * Try to expand it.                   * Try to expand it.
Line 445 
Line 642 
                         return (1);                          return (1);
         }          }
   
         /*          while (replen-- > 0)
          * Special handling for "magic cookie" terminals.  
          * If an attribute enter/exit sequence has a printing width > 0,  
          * and the sequence is adjacent to a space, delete the space.  
          * We just mark the space as invisible, to avoid having too  
          * many spaces deleted.  
          * {{ Note that even if the attribute width is > 1, we  
          *    delete only one space.  It's not worth trying to do more.  
          *    It's hardly worth doing this much. }}  
          */  
         if (curr > 0 && a != AT_NORMAL &&  
                 linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&  
                 attr_swidth(a) > 0)  
         {          {
                 /*                  linebuf[curr] = *rep++;
                  * We are about to append an enter-attribute sequence                  attr[curr] = a;
                  * just after a space.  Delete the space.                  curr++;
                  */  
                 attr[curr-1] = AT_INVIS;  
                 column--;  
         } else if (curr > 0 && attr[curr-1] != AT_NORMAL &&  
                 attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&  
                 attr_ewidth(attr[curr-1]) > 0)  
         {  
                 /*  
                  * We are about to append a space just after an  
                  * exit-attribute sequence.  Delete the space.  
                  */  
                 a = AT_INVIS;  
                 column--;  
         }          }
         /* End of magic cookie handling. */  
   
         linebuf[curr] = c;  
         attr[curr] = a;  
         column += w;          column += w;
         return (0);          return (0);
 }  }
Line 510 
Line 678 
                 to_tab = tabstops[i+1] - to_tab;                  to_tab = tabstops[i+1] - to_tab;
         }          }
   
           if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
                   return 1;
   
         do {          do {
                 STORE_CHAR(' ', attr, pos);                  STORE_CHAR(' ', attr, " ", pos);
         } while (--to_tab > 0);          } while (--to_tab > 0);
         return 0;          return 0;
 }  }
   
   #define STORE_PRCHAR(c, pos) \
           do { if (store_prchar((c), (pos))) return 1; } while (0)
   
           static int
   store_prchar(c, pos)
           char c;
           POSITION pos;
   {
           char *s;
   
           /*
            * Convert to printable representation.
            */
           s = prchar(c);
   
           /*
            * Make sure we can get the entire representation
            * of the character on this line.
            */
           if (column + (int) strlen(s) - 1 +
               pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
                   return 1;
   
           for ( ;  *s != 0;  s++)
                   STORE_CHAR(*s, AT_BINARY, NULL, pos);
   
           return 0;
   }
   
           static int
   flush_mbc_buf(pos)
           POSITION pos;
   {
           int i;
   
           for (i = 0; i < mbc_buf_index; i++)
                   if (store_prchar(mbc_buf[i], pos))
                           return mbc_buf_index - i;
   
           return 0;
   }
   
 /*  /*
  * Append a character to the line buffer.   * Append a character to the line buffer.
  * Expand tabs into spaces, handle underlining, boldfacing, etc.   * Expand tabs into spaces, handle underlining, boldfacing, etc.
Line 523 
Line 736 
  */   */
         public int          public int
 pappend(c, pos)  pappend(c, pos)
         register int c;          char c;
         POSITION pos;          POSITION pos;
 {  {
         int r;          int r;
   
         if (pendc)          if (pendc)
         {          {
                 if (do_append(pendc, pendpos))                  if (do_append(pendc, NULL, pendpos))
                         /*                          /*
                          * Oops.  We've probably lost the char which                           * Oops.  We've probably lost the char which
                          * was in pendc, since caller won't back up.                           * was in pendc, since caller won't back up.
Line 541 
Line 754 
   
         if (c == '\r' && bs_mode == BS_SPECIAL)          if (c == '\r' && bs_mode == BS_SPECIAL)
         {          {
                   if (mbc_buf_len > 0)  /* utf_mode must be on. */
                   {
                           /* Flush incomplete (truncated) sequence. */
                           r = flush_mbc_buf(mbc_pos);
                           mbc_buf_index = r + 1;
                           mbc_buf_len = 0;
                           if (r)
                                   return (mbc_buf_index);
                   }
   
                 /*                  /*
                  * Don't put the CR into the buffer until we see                   * Don't put the CR into the buffer until we see
                  * the next char.  If the next char is a newline,                   * the next char.  If the next char is a newline,
Line 551 
Line 774 
                 return (0);                  return (0);
         }          }
   
         r = do_append(c, pos);          if (!utf_mode)
           {
                   r = do_append((LWCHAR) c, NULL, pos);
           } else
           {
                   /* Perform strict validation in all possible cases. */
                   if (mbc_buf_len == 0)
                   {
                   retry:
                           mbc_buf_index = 1;
                           *mbc_buf = c;
                           if (IS_ASCII_OCTET(c))
                                   r = do_append((LWCHAR) c, NULL, pos);
                           else if (IS_UTF8_LEAD(c))
                           {
                                   mbc_buf_len = utf_len(c);
                                   mbc_pos = pos;
                                   return (0);
                           } else
                                   /* UTF8_INVALID or stray UTF8_TRAIL */
                                   r = flush_mbc_buf(pos);
                   } else if (IS_UTF8_TRAIL(c))
                   {
                           mbc_buf[mbc_buf_index++] = c;
                           if (mbc_buf_index < mbc_buf_len)
                                   return (0);
                           if (is_utf8_well_formed(mbc_buf))
                                   r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
                           else
                                   /* Complete, but not shortest form, sequence. */
                                   mbc_buf_index = r = flush_mbc_buf(mbc_pos);
                           mbc_buf_len = 0;
                   } else
                   {
                           /* Flush incomplete (truncated) sequence.  */
                           r = flush_mbc_buf(mbc_pos);
                           mbc_buf_index = r + 1;
                           mbc_buf_len = 0;
                           /* Handle new char.  */
                           if (!r)
                                   goto retry;
                   }
           }
   
         /*          /*
          * If we need to shift the line, do it.           * If we need to shift the line, do it.
          * But wait until we get to at least the middle of the screen,           * But wait until we get to at least the middle of the screen,
Line 563 
Line 829 
                 linebuf[curr] = '\0';                  linebuf[curr] = '\0';
                 pshift(hshift - cshift);                  pshift(hshift - cshift);
         }          }
           if (r)
           {
                   /* How many chars should caller back up? */
                   r = (!utf_mode) ? 1 : mbc_buf_index;
           }
         return (r);          return (r);
 }  }
   
 #define IS_UTF8_4BYTE(c) ( ((c) & 0xf8) == 0xf0 )  
 #define IS_UTF8_3BYTE(c) ( ((c) & 0xf0) == 0xe0 )  
 #define IS_UTF8_2BYTE(c) ( ((c) & 0xe0) == 0xc0 )  
 #define IS_UTF8_TRAIL(c) ( ((c) & 0xc0) == 0x80 )  
   
         static int          static int
 do_append(c, pos)  do_append(ch, rep, pos)
         int c;          LWCHAR ch;
           char *rep;
         POSITION pos;          POSITION pos;
 {  {
         register char *s;  
         register int a;          register int a;
           LWCHAR prev_ch;
   
 #define STOREC(c,a) \          a = AT_NORMAL;
         if ((c) == '\t') STORE_TAB((a),pos); else STORE_CHAR((c),(a),pos)  
   
         if (c == '\b')          if (ch == '\b')
         {          {
                 switch (bs_mode)                  if (bs_mode == BS_CONTROL)
                 {  
                 case BS_NORMAL:  
                         STORE_CHAR(c, AT_NORMAL, pos);  
                         break;  
                 case BS_CONTROL:  
                         goto do_control_char;                          goto do_control_char;
                 case BS_SPECIAL:  
                         if (curr == 0)                  /*
                                 break;                   * A better test is needed here so we don't
                         backc();                   * backspace over part of the printed
                         overstrike = 1;                   * representation of a binary character.
                         break;                   */
                 }                  if (   curr <= lmargin
         } else if (overstrike)                      || column <= lmargin
                       || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
                           STORE_PRCHAR('\b', pos);
                   else if (bs_mode == BS_NORMAL)
                           STORE_CHAR(ch, AT_NORMAL, NULL, pos);
                   else if (bs_mode == BS_SPECIAL)
                           overstrike = backc();
   
                   return 0;
           }
   
           if (overstrike > 0)
         {          {
                 /*                  /*
                  * Overstrike the character at the current position                   * Overstrike the character at the current position
Line 607 
Line 879 
                  * bold (if an identical character is overstruck),                   * bold (if an identical character is overstruck),
                  * or just deletion of the character in the buffer.                   * or just deletion of the character in the buffer.
                  */                   */
                 overstrike--;                  overstrike = utf_mode ? -1 : 0;
                 if (utf_mode && IS_UTF8_4BYTE(c) && curr > 2 && (char)c == linebuf[curr-3])                  /* To be correct, this must be a base character.  */
                   prev_ch = get_wchar(linebuf + curr);
                   a = attr[curr];
                   if (ch == prev_ch)
                 {                  {
                         backc();  
                         backc();  
                         backc();  
                         STORE_CHAR(linebuf[curr], AT_BOLD, pos);  
                         overstrike = 3;  
                 } else if (utf_mode && (IS_UTF8_3BYTE(c) || (overstrike==2 && IS_UTF8_TRAIL(c))) && curr > 1 && (char)c == linebuf[curr-2])  
                 {  
                         backc();  
                         backc();  
                         STORE_CHAR(linebuf[curr], AT_BOLD, pos);  
                         overstrike = 2;  
                 } else if (utf_mode && curr > 0 && (IS_UTF8_2BYTE(c) || (overstrike==1 && IS_UTF8_TRAIL(c))) && (char)c == linebuf[curr-1])  
                 {  
                         backc();  
                         STORE_CHAR(linebuf[curr], AT_BOLD, pos);  
                         overstrike = 1;  
                 } else if (utf_mode && curr > 0 && IS_UTF8_TRAIL(c) && attr[curr-1] == AT_UNDERLINE)  
                 {  
                         STOREC(c, AT_UNDERLINE);  
                 } else if ((char)c == linebuf[curr])  
                 {  
                         /*                          /*
                          * Overstriking a char with itself means make it bold.                           * Overstriking a char with itself means make it bold.
                          * But overstriking an underscore with itself is                           * But overstriking an underscore with itself is
Line 638 
Line 892 
                          * it could mean make it underlined.                           * it could mean make it underlined.
                          * Use the previous overstrike to resolve it.                           * Use the previous overstrike to resolve it.
                          */                           */
                         if (c == '_' && last_overstrike != AT_NORMAL)                          if (ch == '_')
                                 STOREC(c, last_overstrike);  
                         else  
                                 STOREC(c, AT_BOLD);  
                 } else if (c == '_')  
                 {  
                         if (utf_mode)  
                         {                          {
                                 int i;                                  if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
                                 for (i = 0;  i < 5;  i++)                                          a |= (AT_BOLD|AT_UNDERLINE);
                                 {                                  else if (last_overstrike != AT_NORMAL)
                                         if (curr <= i || !IS_CONT(linebuf[curr-i]))                                          a |= last_overstrike;
                                                 break;                                  else
                                         attr[curr-i-1] = AT_UNDERLINE;                                          a |= AT_BOLD;
                                 }                          } else
                         }                                  a |= AT_BOLD;
                         STOREC(linebuf[curr], AT_UNDERLINE);                  } else if (ch == '_')
                 } else if (linebuf[curr] == '_')  
                 {                  {
                         if (utf_mode)                          a |= AT_UNDERLINE;
                         {                          ch = prev_ch;
                                 if (IS_UTF8_2BYTE(c))                          rep = linebuf + curr;
                                         overstrike = 1;                  } else if (prev_ch == '_')
                                 else if (IS_UTF8_3BYTE(c))                  {
                                         overstrike = 2;                          a |= AT_UNDERLINE;
                                 else if (IS_UTF8_4BYTE(c))                  }
                                         overstrike = 3;                  /* Else we replace prev_ch, but we keep its attributes.  */
                         }          } else if (overstrike < 0)
                         STOREC(c, AT_UNDERLINE);          {
                 } else if (control_char(c))                  if (   is_composing_char(ch)
                         goto do_control_char;                      || is_combining_char(get_wchar(linebuf + curr), ch))
                           /* Continuation of the same overstrike.  */
                           a = last_overstrike;
                 else                  else
                         STOREC(c, AT_NORMAL);                          overstrike = 0;
         } else if (c == '\t')          }
   
           if (ch == '\t')
         {          {
                 /*                  /*
                  * Expand a tab into spaces.                   * Expand a tab into spaces.
Line 682 
Line 933 
                         goto do_control_char;                          goto do_control_char;
                 case BS_NORMAL:                  case BS_NORMAL:
                 case BS_SPECIAL:                  case BS_SPECIAL:
                         STORE_TAB(AT_NORMAL, pos);                          STORE_TAB(a, pos);
                         break;                          break;
                 }                  }
         } else if (control_char(c))          } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
         {          {
         do_control_char:          do_control_char:
                 if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))                  if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
                 {                  {
                         /*                          /*
                          * Output as a normal character.                           * Output as a normal character.
                          */                           */
                         STORE_CHAR(c, AT_NORMAL, pos);                          STORE_CHAR(ch, AT_NORMAL, rep, pos);
                 } else                  } else
                 {                  {
                         /*                          STORE_PRCHAR((char) ch, pos);
                          * Convert to printable representation.                  }
                          */          } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
                         s = prchar(c);          {
                         a = binattr;                  char *s;
   
                         /*                  s = prutfchar(ch);
                          * Make sure we can get the entire representation  
                          * of the character on this line.  
                          */  
                         if (column + (int) strlen(s) +  
                             attr_swidth(a) + attr_ewidth(a) > sc_width)  
                                 return (1);  
   
                         for ( ;  *s != 0;  s++)                  if (column + (int) strlen(s) - 1 +
                                 STORE_CHAR(*s, a, pos);                      pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
                 }                          return (1);
         } else  
                   for ( ;  *s != 0;  s++)
                           STORE_CHAR(*s, AT_BINARY, NULL, pos);
           } else
         {          {
                 STOREC(c, AT_NORMAL);                  STORE_CHAR(ch, a, rep, pos);
         }          }
           return (0);
   }
   
         return (0);  /*
    *
    */
           public int
   pflushmbc()
   {
           int r = 0;
   
           if (mbc_buf_len > 0)
           {
                   /* Flush incomplete (truncated) sequence.  */
                   r = flush_mbc_buf(mbc_pos);
                   mbc_buf_len = 0;
           }
           return r;
 }  }
   
 /*  /*
  * Terminate the line in the line buffer.   * Terminate the line in the line buffer.
  */   */
         public void          public void
 pdone(endline)  pdone(endline, forw)
         int endline;          int endline;
           int forw;
 {  {
           (void) pflushmbc();
   
         if (pendc && (pendc != '\r' || !endline))          if (pendc && (pendc != '\r' || !endline))
                 /*                  /*
                  * If we had a pending character, put it in the buffer.                   * If we had a pending character, put it in the buffer.
                  * But discard a pending CR if we are at end of line                   * But discard a pending CR if we are at end of line
                  * (that is, discard the CR in a CR/LF sequence).                   * (that is, discard the CR in a CR/LF sequence).
                  */                   */
                 (void) do_append(pendc, pendpos);                  (void) do_append(pendc, NULL, pendpos);
   
         /*          /*
          * Make sure we've shifted the line, if we need to.           * Make sure we've shifted the line, if we need to.
Line 742 
Line 1009 
         if (cshift < hshift)          if (cshift < hshift)
                 pshift(hshift - cshift);                  pshift(hshift - cshift);
   
           if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
           {
                   /* Switch to normal attribute at end of line. */
                   char *p = "\033[m";
                   for ( ;  *p != '\0';  p++)
                   {
                           linebuf[curr] = *p;
                           attr[curr++] = AT_ANSI;
                   }
           }
   
         /*          /*
          * Add a newline if necessary,           * Add a newline if necessary,
          * and append a '\0' to the end of the line.           * and append a '\0' to the end of the line.
            * We output a newline if we're not at the right edge of the screen,
            * or if the terminal doesn't auto wrap,
            * or if this is really the end of the line AND the terminal ignores
            * a newline at the right edge.
            * (In the last case we don't want to output a newline if the terminal
            * doesn't ignore it since that would produce an extra blank line.
            * But we do want to output a newline if the terminal ignores it in case
            * the next line is blank.  In that case the single newline output for
            * that blank line would be ignored!)
          */           */
         if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)          if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
         {          {
                 linebuf[curr] = '\n';                  linebuf[curr] = '\n';
                 attr[curr] = AT_NORMAL;                  attr[curr] = AT_NORMAL;
                 curr++;                  curr++;
           }
           else if (ignaw && column >= sc_width && forw)
           {
                   /*
                    * Terminals with "ignaw" don't wrap until they *really* need
                    * to, i.e. when the character *after* the last one to fit on a
                    * line is output. But they are too hard to deal with when they
                    * get in the state where a full screen width of characters
                    * have been output but the cursor is sitting on the right edge
                    * instead of at the start of the next line.
                    * So we nudge them into wrapping by outputting a space
                    * character plus a backspace.  But do this only if moving
                    * forward; if we're moving backward and drawing this line at
                    * the top of the screen, the space would overwrite the first
                    * char on the next line.  We don't need to do this "nudge"
                    * at the top of the screen anyway.
                    */
                   linebuf[curr] = ' ';
                   attr[curr++] = AT_NORMAL;
                   linebuf[curr] = '\b';
                   attr[curr++] = AT_NORMAL;
         }          }
         linebuf[curr] = '\0';          linebuf[curr] = '\0';
         attr[curr] = AT_NORMAL;          attr[curr] = AT_NORMAL;
   }
   
 #if HILITE_SEARCH  /*
         if (status_col && hilites > 0)   *
         {   */
                 linebuf[0] = '*';          public void
                 attr[0] = AT_STANDOUT;  set_status_col(c)
         }          char c;
 #endif  {
         /*          linebuf[0] = c;
          * If we are done with this line, reset the current shift.          attr[0] = AT_NORMAL|AT_HILITE;
          */  
         if (endline)  
                 cshift = 0;  
 }  }
   
 /*  /*
Line 779 
Line 1085 
         register int i;          register int i;
         register int *ap;          register int *ap;
 {  {
         char *s;  
   
         if (is_null_line)          if (is_null_line)
         {          {
                 /*                  /*
                  * If there is no current line, we pretend the line is                   * If there is no current line, we pretend the line is
                  * either "~" or "", depending on the "twiddle" flag.                   * either "~" or "", depending on the "twiddle" flag.
                  */                   */
                 *ap = AT_BOLD;                  if (twiddle)
                 s = (twiddle) ? "~\n" : "\n";                  {
                 return (s[i]);                          if (i == 0)
                           {
                                   *ap = AT_BOLD;
                                   return '~';
                           }
                           --i;
                   }
                   /* Make sure we're back to AT_NORMAL before the '\n'.  */
                   *ap = AT_NORMAL;
                   return i ? '\0' : '\n';
         }          }
   
         *ap = attr[i];          *ap = attr[i];
         return (linebuf[i] & 0377);          return (linebuf[i] & 0xFF);
 }  }
   
 /*  /*
Line 812 
Line 1125 
  * {{ This is supposed to be more efficient than forw_line(). }}   * {{ This is supposed to be more efficient than forw_line(). }}
  */   */
         public POSITION          public POSITION
 forw_raw_line(curr_pos, linep)  forw_raw_line(curr_pos, linep, line_lenp)
         POSITION curr_pos;          POSITION curr_pos;
         char **linep;          char **linep;
           int *line_lenp;
 {  {
         register int n;          register int n;
         register int c;          register int c;
Line 827 
Line 1141 
         n = 0;          n = 0;
         for (;;)          for (;;)
         {          {
                 if (c == '\n' || c == EOI)                  if (c == '\n' || c == EOI || ABORT_SIGS())
                 {                  {
                         new_pos = ch_tell();                          new_pos = ch_tell();
                         break;                          break;
Line 850 
Line 1164 
         linebuf[n] = '\0';          linebuf[n] = '\0';
         if (linep != NULL)          if (linep != NULL)
                 *linep = linebuf;                  *linep = linebuf;
           if (line_lenp != NULL)
                   *line_lenp = n;
         return (new_pos);          return (new_pos);
 }  }
   
Line 858 
Line 1174 
  * {{ This is supposed to be more efficient than back_line(). }}   * {{ This is supposed to be more efficient than back_line(). }}
  */   */
         public POSITION          public POSITION
 back_raw_line(curr_pos, linep)  back_raw_line(curr_pos, linep, line_lenp)
         POSITION curr_pos;          POSITION curr_pos;
         char **linep;          char **linep;
           int *line_lenp;
 {  {
         register int n;          register int n;
         register int c;          register int c;
Line 875 
Line 1192 
         for (;;)          for (;;)
         {          {
                 c = ch_back_get();                  c = ch_back_get();
                 if (c == '\n')                  if (c == '\n' || ABORT_SIGS())
                 {                  {
                         /*                          /*
                          * This is the newline ending the previous line.                           * This is the newline ending the previous line.
Line 911 
Line 1228 
                         /*                          /*
                          * Shift the data to the end of the new linebuf.                           * Shift the data to the end of the new linebuf.
                          */                           */
                         for (fm = linebuf + old_size_linebuf,                          for (fm = linebuf + old_size_linebuf - 1,
                               to = linebuf + size_linebuf;                                to = linebuf + size_linebuf - 1;
                              fm >= linebuf;  fm--, to--)                               fm >= linebuf;  fm--, to--)
                                 *to = *fm;                                  *to = *fm;
                         n = size_linebuf - old_size_linebuf;                          n = size_linebuf - old_size_linebuf;
Line 921 
Line 1238 
         }          }
         if (linep != NULL)          if (linep != NULL)
                 *linep = &linebuf[n];                  *linep = &linebuf[n];
           if (line_lenp != NULL)
                   *line_lenp = size_linebuf - 1 - n;
         return (new_pos);          return (new_pos);
 }  }

Legend:
Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3