Ondrej Oprala 01067b2
--- coreutils-8.24/src/cut.c	2015-06-26 19:05:22.000000000 +0200
Ondrej Oprala 01067b2
+++ cut.c	2016-01-15 10:15:04.863804121 +0100
Ondrej Oprala 01067b2
@@ -28,6 +28,11 @@
Ondrej Oprala 01067b2
 #include <assert.h>
Ondrej Oprala 01067b2
 #include <getopt.h>
Ondrej Oprala 01067b2
 #include <sys/types.h>
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+#include <mbfile.h>
Ondrej Oprala 01067b2
+#include <mbiter.h>
Ondrej Oprala 01067b2
+#include <string.h>
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
 #include "system.h"
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 #include "error.h"
d3849ce
@@ -61,25 +66,16 @@
7d9c9af
    CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */
7d9c9af
 static struct field_range_pair *current_rp;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-/* This buffer is used to support the semantics of the -s option
Ondrej Oprala 01067b2
-   (or lack of same) when the specified field list includes (does
Ondrej Oprala 01067b2
-   not include) the first field.  In both of those cases, the entire
Ondrej Oprala 01067b2
-   first field must be read into this buffer to determine whether it
Ondrej Oprala 01067b2
-   is followed by a delimiter or a newline before any of it may be
Ondrej Oprala 01067b2
-   output.  Otherwise, cut_fields can do the job without using this
Ondrej Oprala 01067b2
-   buffer.  */
Ondrej Oprala 01067b2
-static char *field_1_buffer;
Ondrej Oprala 01067b2
-
Ondrej Oprala 01067b2
-/* The number of bytes allocated for FIELD_1_BUFFER.  */
Ondrej Oprala 01067b2
-static size_t field_1_bufsize;
Ondrej Oprala 01067b2
-
Ondrej Oprala 01067b2
 enum operating_mode
Ondrej Oprala 01067b2
   {
Ondrej Oprala 01067b2
     undefined_mode,
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-    /* Output characters that are in the given bytes. */
Ondrej Oprala 01067b2
+    /* Output the given bytes. */
Ondrej Oprala 01067b2
     byte_mode,
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
+    /* Output characters that are in the given positions . */
Ondrej Oprala 01067b2
+    char_mode,
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
     /* Output the given delimiter-separated fields. */
Ondrej Oprala 01067b2
     field_mode
Ondrej Oprala 01067b2
   };
d3849ce
@@ -91,12 +87,16 @@ static enum operating_mode operating_mode;
Ondrej Oprala 01067b2
    with field mode.  */
Ondrej Oprala 01067b2
 static bool suppress_non_delimited;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
+/* Unless true, we do not recognize multibyte characters in byte-splitting
Ondrej Oprala 01067b2
+   mode.  */
Ondrej Oprala 01067b2
+static bool no_break_mb_chars;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
 /* If true, print all bytes, characters, or fields _except_
Ondrej Oprala 01067b2
    those that were specified.  */
Ondrej Oprala 01067b2
 static bool complement;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 /* The delimiter character for field mode. */
Ondrej Oprala 01067b2
-static unsigned char delim;
Ondrej Oprala 01067b2
+static mbf_char_t delim;
Ondrej Oprala 01067b2
 
7d9c9af
 /* The delimiter for each line/record. */
7d9c9af
 static unsigned char line_delim = '\n';
d3849ce
@@ -109,7 +109,7 @@ static size_t output_delimiter_length;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 /* The output field separator string.  Defaults to the 1-character
Ondrej Oprala 01067b2
    string consisting of the input delimiter.  */
Ondrej Oprala 01067b2
-static char *output_delimiter_string;
Ondrej Oprala 01067b2
+static char const *output_delimiter_string;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 /* True if we have ever read standard input. */
Ondrej Oprala 01067b2
 static bool have_read_stdin;
d3849ce
@@ -164,7 +164,7 @@ Print selected parts of lines from each FILE to standard output.\n\
Ondrej Oprala 01067b2
   -f, --fields=LIST       select only these fields;  also print any line\n\
Ondrej Oprala 01067b2
                             that contains no delimiter character, unless\n\
Ondrej Oprala 01067b2
                             the -s option is specified\n\
Ondrej Oprala 01067b2
-  -n                      (ignored)\n\
Ondrej Oprala 01067b2
+  -n                      with -b, don't split multibyte characters\n\
Ondrej Oprala 01067b2
 "), stdout);
Ondrej Oprala 01067b2
       fputs (_("\
Ondrej Oprala 01067b2
       --complement        complement the set of selected bytes, characters\n\
d3849ce
@@ -211,6 +211,12 @@ next_item (size_t *item_idx)
Ondrej Oprala 01067b2
     current_rp++;
Ondrej Oprala 01067b2
 }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
+static inline void
Ondrej Oprala 01067b2
+next_item_n (size_t *item_idx, size_t n)
Ondrej Oprala 01067b2
+{
Ondrej Oprala 01067b2
+  while (n-- > 0)
Ondrej Oprala 01067b2
+    next_item (item_idx);
Ondrej Oprala 01067b2
+}
Ondrej Oprala 01067b2
 /* Return nonzero if the K'th field or byte is printable. */
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 static inline bool
d3849ce
@@ -219,6 +225,15 @@ print_kth (size_t k)
Ondrej Oprala 01067b2
   return current_rp->lo <= k;
Ondrej Oprala 01067b2
 }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
+/* The lo and hi params should be used for the current characters byte position
Ondrej Oprala 01067b2
+ * and byte size, respectively. */
Ondrej Oprala 01067b2
+static inline bool
Ondrej Oprala 01067b2
+rp_intersect (size_t lo, size_t hi)
Ondrej Oprala 01067b2
+{
Ondrej Oprala 01067b2
+  return ((current_rp->lo <= lo && current_rp->hi >= lo)
Ondrej Oprala 01067b2
+	  || (current_rp->lo <= hi && current_rp->hi >= hi));
Ondrej Oprala 01067b2
+}
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
 /* Return nonzero if K'th byte is the beginning of a range. */
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 static inline bool
d3849ce
@@ -281,23 +296,215 @@ cut_bytes (FILE *stream)
Ondrej Oprala 01067b2
 }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 /* Read from stream STREAM, printing to standard output any selected fields.  */
Ondrej Oprala 01067b2
+extern ssize_t
Ondrej Oprala 01067b2
+mb_getndelim2 (mbf_char_t **lineptr, size_t *linesize, size_t nmax,
Ondrej Oprala 01067b2
+	       mbf_char_t delim1, mbf_char_t delim2, mb_file_t *stream)
Ondrej Oprala 01067b2
+{
Ondrej Oprala 01067b2
+/* The maximum value that getndelim2 can return without suffering from
Ondrej Oprala 01067b2
+   overflow problems, either internally (because of pointer
Ondrej Oprala 01067b2
+   subtraction overflow) or due to the API (because of ssize_t).  */
Ondrej Oprala 01067b2
+#define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+/* Try to add at least this many bytes when extending the buffer.
Ondrej Oprala 01067b2
+   MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM.  */
Ondrej Oprala 01067b2
+#define MIN_CHUNK 64
Ondrej Oprala 01067b2
+  size_t nchars_avail;          /* Allocated but unused chars in *LINEPTR.  */
Ondrej Oprala 01067b2
+  mbf_char_t *read_pos;               /* Where we're reading into *LINEPTR. */
Ondrej Oprala 01067b2
+  ssize_t chars_stored = -1;
Ondrej Oprala 01067b2
+  mbf_char_t *ptr = *lineptr;
Ondrej Oprala 01067b2
+  size_t size = *linesize;
Ondrej Oprala 01067b2
+  bool found_delimiter;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  if (!ptr)
Ondrej Oprala 01067b2
+    {
Ondrej Oprala 01067b2
+      size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK;
Ondrej Oprala 01067b2
+      ptr = malloc (size * sizeof (mbf_char_t));
Ondrej Oprala 01067b2
+      if (!ptr)
Ondrej Oprala 01067b2
+        return -1;
Ondrej Oprala 01067b2
+    }
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  if (size < 0)
Ondrej Oprala 01067b2
+    goto done;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  nchars_avail = size;
Ondrej Oprala 01067b2
+  read_pos = ptr;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  if (nchars_avail == 0 && nmax <= size)
Ondrej Oprala 01067b2
+    goto done;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  /* Normalize delimiters, since memchr2 doesn't handle EOF.  */
Ondrej Oprala 01067b2
+  if (mb_iseof (delim1))
Ondrej Oprala 01067b2
+    mb_copy (&delim1, &delim2);
Ondrej Oprala 01067b2
+  else if (mb_iseof (delim2))
Ondrej Oprala 01067b2
+    mb_copy (&delim2, &delim1);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  flockfile (stream);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  found_delimiter = false;
Ondrej Oprala 01067b2
+  do
Ondrej Oprala 01067b2
+    {
Ondrej Oprala 01067b2
+      /* Here always ptr + size == read_pos + nchars_avail.
Ondrej Oprala 01067b2
+         Also nchars_avail > 0 || size < nmax.  */
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+      mbf_char_t c IF_LINT (= 0);
Ondrej Oprala 01067b2
+        {
Ondrej Oprala 01067b2
+          mbf_getc (c, *stream);
Ondrej Oprala 01067b2
+          if (mb_iseof (c))
Ondrej Oprala 01067b2
+            {
Ondrej Oprala 01067b2
+              /* Return partial line, if any.  */
Ondrej Oprala 01067b2
+              if (read_pos == ptr)
Ondrej Oprala 01067b2
+                goto unlock_done;
Ondrej Oprala 01067b2
+              else
Ondrej Oprala 01067b2
+                break;
Ondrej Oprala 01067b2
+            }
Ondrej Oprala 01067b2
+          if (mb_equal (c, delim1) || mb_equal (c, delim2))
Ondrej Oprala 01067b2
+            found_delimiter = true;
Ondrej Oprala 01067b2
+        }
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+      /* We always want at least one byte left in the buffer, since we
Ondrej Oprala 01067b2
+         always (unless we get an error while reading the first byte)
Ondrej Oprala 01067b2
+         NUL-terminate the line buffer.  */
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+      if (!nchars_avail)
Ondrej Oprala 01067b2
+        {
Ondrej Oprala 01067b2
+          /* Grow size proportionally, not linearly, to avoid O(n^2)
Ondrej Oprala 01067b2
+             running time.  */
Ondrej Oprala 01067b2
+          size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
Ondrej Oprala 01067b2
+          mbf_char_t *newptr;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+          /* Respect nmax.  This handles possible integer overflow.  */
Ondrej Oprala 01067b2
+          if (! (size < newsize && newsize <= nmax))
Ondrej Oprala 01067b2
+            newsize = nmax;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+          if (GETNDELIM2_MAXIMUM < newsize)
Ondrej Oprala 01067b2
+            {
Ondrej Oprala 01067b2
+              size_t newsizemax = GETNDELIM2_MAXIMUM + 1;
Ondrej Oprala 01067b2
+              if (size == newsizemax)
Ondrej Oprala 01067b2
+                goto unlock_done;
Ondrej Oprala 01067b2
+              newsize = newsizemax;
Ondrej Oprala 01067b2
+            }
Ondrej Oprala 01067b2
+          nchars_avail = newsize - (read_pos - ptr);
Ondrej Oprala 01067b2
+          newptr = realloc (ptr, newsize * sizeof (mbf_char_t));
Ondrej Oprala 01067b2
+          if (!newptr)
Ondrej Oprala 01067b2
+            goto unlock_done;
Ondrej Oprala 01067b2
+          ptr = newptr;
Ondrej Oprala 01067b2
+          size = newsize;
Ondrej Oprala 01067b2
+          read_pos = size - nchars_avail + ptr;
Ondrej Oprala 01067b2
+        }
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+      /* Here, if size < nmax, nchars_avail >= buffer_len + 1.
Ondrej Oprala 01067b2
+         If size == nmax, nchars_avail > 0.  */
Ondrej Oprala 01067b2
+
06a5ca2
+      if (1 < nchars_avail--)
Ondrej Oprala 01067b2
+        {
Ondrej Oprala 01067b2
+          mb_copy(read_pos++, &c);
Ondrej Oprala 01067b2
+        }
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+    }
Ondrej Oprala 01067b2
+  while (!found_delimiter);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  chars_stored = (read_pos - ptr);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+ unlock_done:
Ondrej Oprala 01067b2
+  funlockfile (stream);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+ done:
Ondrej Oprala 01067b2
+  *lineptr = ptr;
Ondrej Oprala 01067b2
+  *linesize = size;
Ondrej Oprala 01067b2
+  return chars_stored;
Ondrej Oprala 01067b2
+}
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+static void
Ondrej Oprala 01067b2
+cut_chars (FILE *stream)
Ondrej Oprala 01067b2
+{
Ondrej Oprala 01067b2
+  size_t char_idx;	/* Number of chars in the line so far. */
Ondrej Oprala 01067b2
+  bool print_delimiter;
Ondrej Oprala 01067b2
+  mbf_char_t c;
Ondrej Oprala 01067b2
+  mb_file_t mbf;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  print_delimiter = false;
Ondrej Oprala 01067b2
+  char_idx = 0;
7d9c9af
+  current_rp = frp;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  mbf_init (mbf, stream);
Ondrej Oprala 01067b2
+  while (true)
Ondrej Oprala 01067b2
+    {
Ondrej Oprala 01067b2
+      mbf_getc (c, mbf);
Ondrej Oprala 01067b2
+
7d9c9af
+      if (mb_iseq (c, line_delim))
Ondrej Oprala 01067b2
+	{
7d9c9af
+	  putc (line_delim, stdout);
Ondrej Oprala 01067b2
+	  char_idx = 0;
Ondrej Oprala 01067b2
+	  print_delimiter = false;
7d9c9af
+          current_rp = frp;
Ondrej Oprala 01067b2
+	}
Ondrej Oprala 01067b2
+      else if (mb_iseof (c))
Ondrej Oprala 01067b2
+	{
Ondrej Oprala 01067b2
+	  if (char_idx > 0)
7d9c9af
+	    putc (line_delim, stdout);
Ondrej Oprala 01067b2
+	  break;
Ondrej Oprala 01067b2
+	}
Ondrej Oprala 01067b2
+      else
Ondrej Oprala 01067b2
+	{
Ondrej Oprala 01067b2
+	  /* Forward by one byte.  */
Ondrej Oprala 01067b2
+	  next_item (&char_idx);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+	  /* Check if the current characters byte range is within
Ondrej Oprala 01067b2
+	   * the argument list.  */
Ondrej Oprala 01067b2
+	  if (rp_intersect (char_idx, char_idx + mb_len (c) - 1))
Ondrej Oprala 01067b2
+	    {
Ondrej Oprala 01067b2
+	      if (output_delimiter_specified)
Ondrej Oprala 01067b2
+		{
Ondrej Oprala 01067b2
+		  if (print_delimiter && is_range_start_index (char_idx))
Ondrej Oprala 01067b2
+		    {
Ondrej Oprala 01067b2
+		      fwrite (output_delimiter_string, sizeof (char),
Ondrej Oprala 01067b2
+			      output_delimiter_length, stdout);
Ondrej Oprala 01067b2
+		    }
Ondrej Oprala 01067b2
+		  print_delimiter = true;
Ondrej Oprala 01067b2
+		}
Ondrej Oprala 01067b2
+	      mb_putc (c, stdout);
Ondrej Oprala 01067b2
+	    }
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+	  /* Byte mode with multibyte characters uncut (-b -n).  */
Ondrej Oprala 01067b2
+	  if (no_break_mb_chars)
Ondrej Oprala 01067b2
+	    /* Forward by an additional byte_length (c) - 1.  */
Ondrej Oprala 01067b2
+	    next_item_n (&char_idx, mb_len (c) - 1);
Ondrej Oprala 01067b2
+       }
Ondrej Oprala 01067b2
+    }
Ondrej Oprala 01067b2
+}
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
 static void
Ondrej Oprala 01067b2
 cut_fields (FILE *stream)
Ondrej Oprala 01067b2
 {
Ondrej Oprala 01067b2
-  int c;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  /* This buffer is used to support the semantics of the -s option
Ondrej Oprala 01067b2
+     (or lack of same) when the specified field list includes (does
Ondrej Oprala 01067b2
+     not include) the first field.  In both of those cases, the entire
Ondrej Oprala 01067b2
+     first field must be read into this buffer to determine whether it
Ondrej Oprala 01067b2
+     is followed by a delimiter or a newline before any of it may be
Ondrej Oprala 01067b2
+     output.  Otherwise, cut_fields can do the job without using this
Ondrej Oprala 01067b2
+     buffer.  */
Ondrej Oprala 01067b2
+  mbf_char_t *field_1_buffer = 0;
Ondrej Oprala 01067b2
+  /* The number of bytes allocated for FIELD_1_BUFFER.  */
Ondrej Oprala 01067b2
+  size_t field_1_bufsize;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+  mbf_char_t c, d;
Ondrej Oprala 01067b2
+  mb_file_t mbf;
Ondrej Oprala 01067b2
   size_t field_idx = 1;
Ondrej Oprala 01067b2
   bool found_any_selected_field = false;
Ondrej Oprala 01067b2
   bool buffer_first_field;
Ondrej Oprala 01067b2
 
7d9c9af
   current_rp = frp;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-  c = getc (stream);
Ondrej Oprala 01067b2
-  if (c == EOF)
Ondrej Oprala 01067b2
+  mbf_init (mbf, stream);
Ondrej Oprala 01067b2
+  mbf_getc (c, mbf);
Ondrej Oprala 01067b2
+  if (mb_iseof (c))
Ondrej Oprala 01067b2
     return;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-  ungetc (c, stream);
Ondrej Oprala 01067b2
-  c = 0;
Ondrej Oprala 01067b2
+  mbf_ungetc (c, mbf);
Ondrej Oprala 01067b2
+  mb_setascii (&c, 0);
Ondrej Oprala 01067b2
+  mb_copy (&d, &delim);
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   /* To support the semantics of the -s flag, we may have to buffer
Ondrej Oprala 01067b2
      all of the first field to determine whether it is 'delimited.'
d3849ce
@@ -312,10 +519,14 @@ cut_fields (FILE *stream)
Ondrej Oprala 01067b2
       if (field_idx == 1 && buffer_first_field)
Ondrej Oprala 01067b2
         {
Ondrej Oprala 01067b2
           ssize_t len;
Ondrej Oprala 01067b2
-          size_t n_bytes;
Ondrej Oprala 01067b2
+          size_t n_chars;
Ondrej Oprala 01067b2
+	  mbf_char_t nl;
7d9c9af
+	  mb_setascii (&nl, line_delim);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
+          len = mb_getndelim2 (&field_1_buffer, &field_1_bufsize,
Ondrej Oprala 01067b2
+			       GETNLINE_NO_LIMIT, d, nl, &mbf);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-          len = getndelim2 (&field_1_buffer, &field_1_bufsize, 0,
7d9c9af
-                            GETNLINE_NO_LIMIT, delim, line_delim, stream);
Ondrej Oprala 01067b2
           if (len < 0)
Ondrej Oprala 01067b2
             {
Ondrej Oprala 01067b2
               free (field_1_buffer);
d3849ce
@@ -325,15 +536,15 @@ cut_fields (FILE *stream)
Ondrej Oprala 01067b2
               xalloc_die ();
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-          n_bytes = len;
Ondrej Oprala 01067b2
-          assert (n_bytes != 0);
Ondrej Oprala 01067b2
+          n_chars = len;
Ondrej Oprala 01067b2
+          //assert (n_chars != 0);
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-          c = 0;
Ondrej Oprala 01067b2
+	  mb_setascii (&c, 0);
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
           /* If the first field extends to the end of line (it is not
Ondrej Oprala 01067b2
              delimited) and we are printing all non-delimited lines,
Ondrej Oprala 01067b2
              print this one.  */
Ondrej Oprala 01067b2
-          if (to_uchar (field_1_buffer[n_bytes - 1]) != delim)
Ondrej Oprala 01067b2
+          if (!mb_equal (field_1_buffer[n_chars - 1], d))
Ondrej Oprala 01067b2
             {
Ondrej Oprala 01067b2
               if (suppress_non_delimited)
Ondrej Oprala 01067b2
                 {
d3849ce
@@ -341,26 +552,30 @@ cut_fields (FILE *stream)
Ondrej Oprala 01067b2
                 }
Ondrej Oprala 01067b2
               else
Ondrej Oprala 01067b2
                 {
Ondrej Oprala 01067b2
-                  fwrite (field_1_buffer, sizeof (char), n_bytes, stdout);
Ondrej Oprala 01067b2
+		  for (int i = 0; i < n_chars; ++i)
Ondrej Oprala 01067b2
+		    mb_putc (field_1_buffer[i], stdout);
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
                   /* Make sure the output line is newline terminated.  */
7d9c9af
-                  if (field_1_buffer[n_bytes - 1] != line_delim)
7d9c9af
+                  if (!mb_iseq (field_1_buffer[n_chars - 1], line_delim))
7d9c9af
                     putchar (line_delim);
7d9c9af
-                  c = line_delim;
7d9c9af
+                  mb_setascii (&c, line_delim);
Ondrej Oprala 01067b2
                 }
Ondrej Oprala 01067b2
               continue;
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
           if (print_kth (1))
Ondrej Oprala 01067b2
             {
Ondrej Oprala 01067b2
               /* Print the field, but not the trailing delimiter.  */
Ondrej Oprala 01067b2
-              fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout);
Ondrej Oprala 01067b2
+	      for (int i = 0; i < n_chars - 1; ++i)
Ondrej Oprala 01067b2
+		mb_putc (field_1_buffer[i], stdout);
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
               /* With -d$'\n' don't treat the last '\n' as a delimiter.  */
7d9c9af
-              if (delim == line_delim)
7d9c9af
+              if (mb_iseq (d, line_delim))
Ondrej Oprala 01067b2
                 {
Ondrej Oprala 01067b2
-                  int last_c = getc (stream);
Ondrej Oprala 01067b2
-                  if (last_c != EOF)
Ondrej Oprala 01067b2
+                  mbf_char_t last_c;
Ondrej Oprala 01067b2
+		  mbf_getc (last_c, mbf);
Ondrej Oprala 01067b2
+                  if (!mb_iseof (last_c))
Ondrej Oprala 01067b2
                     {
Ondrej Oprala 01067b2
-                      ungetc (last_c, stream);
Ondrej Oprala 01067b2
+                      mbf_ungetc (last_c, mbf);
Ondrej Oprala 01067b2
                       found_any_selected_field = true;
Ondrej Oprala 01067b2
                     }
Ondrej Oprala 01067b2
                 }
d3849ce
@@ -370,7 +585,8 @@ cut_fields (FILE *stream)
Ondrej Oprala 01067b2
           next_item (&field_idx);
Ondrej Oprala 01067b2
         }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-      int prev_c = c;
Ondrej Oprala 01067b2
+      mbf_char_t prev_c;
Ondrej Oprala 01067b2
+      mb_copy (&prev_c, &c);
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
       if (print_kth (field_idx))
Ondrej Oprala 01067b2
         {
d3849ce
@@ -381,42 +597,46 @@ cut_fields (FILE *stream)
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
           found_any_selected_field = true;
Ondrej Oprala 01067b2
 
7d9c9af
-          while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
Ondrej Oprala 01067b2
+	  mbf_getc (c, mbf);
7d9c9af
+          while (!mb_equal (c, d) && !mb_iseq (c, line_delim) && !mb_iseof (c))
Ondrej Oprala 01067b2
             {
Ondrej Oprala 01067b2
-              putchar (c);
Ondrej Oprala 01067b2
-              prev_c = c;
Ondrej Oprala 01067b2
+              mb_putc (c, stdout);
Ondrej Oprala 01067b2
+	      mb_copy (&prev_c, &c);
Ondrej Oprala 01067b2
+	      mbf_getc (c, mbf);
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
         }
Ondrej Oprala 01067b2
       else
Ondrej Oprala 01067b2
         {
7d9c9af
-          while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
Ondrej Oprala 01067b2
+	  mbf_getc (c, mbf);
7d9c9af
+          while (!mb_equal (c, d) && !mb_iseq (c, line_delim) && !mb_iseof (c))
Ondrej Oprala 01067b2
             {
Ondrej Oprala 01067b2
-              prev_c = c;
Ondrej Oprala 01067b2
+	      mb_copy (&prev_c, &c);
Ondrej Oprala 01067b2
+	      mbf_getc (c, mbf);
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
         }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
       /* With -d$'\n' don't treat the last '\n' as a delimiter.  */
7d9c9af
-      if (delim == line_delim && c == delim)
7d9c9af
+      if (mb_iseq (d, line_delim) && mb_equal (c, d))
Ondrej Oprala 01067b2
         {
Ondrej Oprala 01067b2
-          int last_c = getc (stream);
Ondrej Oprala 01067b2
-          if (last_c != EOF)
Ondrej Oprala 01067b2
-            ungetc (last_c, stream);
Ondrej Oprala 01067b2
+	  mbf_char_t last_c;
Ondrej Oprala 01067b2
+	  mbf_getc (last_c, mbf);
Ondrej Oprala 01067b2
+	  if (!mb_iseof (last_c))
Ondrej Oprala 01067b2
+	    mbf_ungetc (last_c, mbf);
Ondrej Oprala 01067b2
           else
Ondrej Oprala 01067b2
-            c = last_c;
Ondrej Oprala 01067b2
+	    mb_copy (&c, &last_c);
Ondrej Oprala 01067b2
         }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-      if (c == delim)
Ondrej Oprala 01067b2
+      if (mb_equal (c, d))
Ondrej Oprala 01067b2
         next_item (&field_idx);
7d9c9af
-      else if (c == line_delim || c == EOF)
7d9c9af
+      else if (mb_iseq (c, line_delim) || mb_iseof (c))
Ondrej Oprala 01067b2
         {
Ondrej Oprala 01067b2
           if (found_any_selected_field
Ondrej Oprala 01067b2
               || !(suppress_non_delimited && field_idx == 1))
Ondrej Oprala 01067b2
             {
7d9c9af
-              if (c == line_delim || prev_c != line_delim
7d9c9af
-                  || delim == line_delim)
7d9c9af
+              if (mb_iseq (c, line_delim) || !mb_iseq (prev_c, line_delim) || mb_iseq (d, line_delim))
7d9c9af
                 putchar (line_delim);
Ondrej Oprala 01067b2
             }
Ondrej Oprala 01067b2
-          if (c == EOF)
Ondrej Oprala 01067b2
+          if (mb_iseof (c))
Ondrej Oprala 01067b2
             break;
Ondrej Oprala 01067b2
           field_idx = 1;
7d9c9af
           current_rp = frp;
d3849ce
@@ -429,7 +649,14 @@ static void
Ondrej Oprala 01067b2
 cut_stream (FILE *stream)
Ondrej Oprala 01067b2
 {
Ondrej Oprala 01067b2
   if (operating_mode == byte_mode)
Ondrej Oprala 01067b2
-    cut_bytes (stream);
Ondrej Oprala 01067b2
+    {
Ondrej Oprala 01067b2
+      if (no_break_mb_chars)
Ondrej Oprala 01067b2
+	cut_chars (stream);
Ondrej Oprala 01067b2
+      else
Ondrej Oprala 01067b2
+	cut_bytes (stream);
Ondrej Oprala 01067b2
+    }
Ondrej Oprala 01067b2
+  else if (operating_mode == char_mode)
Ondrej Oprala 01067b2
+    cut_chars (stream);
Ondrej Oprala 01067b2
   else
Ondrej Oprala 01067b2
     cut_fields (stream);
Ondrej Oprala 01067b2
 }
d3849ce
@@ -483,6 +710,7 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
   bool ok;
Ondrej Oprala 01067b2
   bool delim_specified = false;
Ondrej Oprala 01067b2
   char *spec_list_string IF_LINT ( = NULL);
Ondrej Oprala 01067b2
+  mbi_iterator_t iter;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   initialize_main (&argc, &argv);
Ondrej Oprala 01067b2
   set_program_name (argv[0]);
d3849ce
@@ -496,8 +724,10 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   /* By default, all non-delimited lines are printed.  */
Ondrej Oprala 01067b2
   suppress_non_delimited = false;
Ondrej Oprala 01067b2
+  /* Default behaviour for -b, unless -n is also specified.  */
Ondrej Oprala 01067b2
+  no_break_mb_chars = false;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
-  delim = '\0';
Ondrej Oprala 01067b2
+  mb_setascii (&delim, '\0');
Ondrej Oprala 01067b2
   have_read_stdin = false;
Ondrej Oprala 01067b2
 
7d9c9af
   while ((optc = getopt_long (argc, argv, "b:c:d:f:nsz", longopts, NULL)) != -1)
d3849ce
@@ -505,7 +735,6 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
       switch (optc)
Ondrej Oprala 01067b2
         {
Ondrej Oprala 01067b2
         case 'b':
Ondrej Oprala 01067b2
-        case 'c':
Ondrej Oprala 01067b2
           /* Build the byte list. */
Ondrej Oprala 01067b2
           if (operating_mode != undefined_mode)
Ondrej Oprala 01067b2
             FATAL_ERROR (_("only one type of list may be specified"));
d3849ce
@@ -513,6 +742,14 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
           spec_list_string = optarg;
Ondrej Oprala 01067b2
           break;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
+        case 'c':
Ondrej Oprala 01067b2
+          /* Build the char list. */
Ondrej Oprala 01067b2
+          if (operating_mode != undefined_mode)
Ondrej Oprala 01067b2
+            FATAL_ERROR (_("only one type of list may be specified"));
Ondrej Oprala 01067b2
+          operating_mode = char_mode;
Ondrej Oprala 01067b2
+          spec_list_string = optarg;
Ondrej Oprala 01067b2
+          break;
Ondrej Oprala 01067b2
+
Ondrej Oprala 01067b2
         case 'f':
Ondrej Oprala 01067b2
           /* Build the field list. */
Ondrej Oprala 01067b2
           if (operating_mode != undefined_mode)
d3849ce
@@ -524,9 +761,17 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
         case 'd':
Ondrej Oprala 01067b2
           /* New delimiter. */
Ondrej Oprala 01067b2
           /* Interpret -d '' to mean 'use the NUL byte as the delimiter.'  */
Ondrej Oprala 01067b2
-          if (optarg[0] != '\0' && optarg[1] != '\0')
Ondrej Oprala 01067b2
+	  mbi_init (iter, optarg, strlen (optarg));
Ondrej Oprala 01067b2
+	  if  (!mbi_avail (iter))
Ondrej Oprala 01067b2
+	    mb_setascii (&delim, '\0');
Ondrej Oprala 01067b2
+	  else
Ondrej Oprala 11e5aa1
+           {
Ondrej Oprala 11e5aa1
+             mb_copy (&delim, &mbi_cur (iter));
Ondrej Oprala 01067b2
+
Ondrej Oprala 11e5aa1
+             mbi_advance (iter);
Ondrej Oprala 11e5aa1
+             if (mbi_avail (iter))
Ondrej Oprala 01067b2
             FATAL_ERROR (_("the delimiter must be a single character"));
Ondrej Oprala 01067b2
-          delim = optarg[0];
d3849ce
+           }
Ondrej Oprala 01067b2
           delim_specified = true;
Ondrej Oprala 01067b2
           break;
Ondrej Oprala 01067b2
 
d3849ce
@@ -540,6 +785,7 @@ main (int argc, char **argv)
Ondrej Oprala 01067b2
           break;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
         case 'n':
Ondrej Oprala 01067b2
+	  no_break_mb_chars = true;
Ondrej Oprala 01067b2
           break;
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
         case 's':
d3849ce
@@ -579,15 +825,12 @@ main (int argc, char **argv)
7d9c9af
               | (complement ? SETFLD_COMPLEMENT : 0) );
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   if (!delim_specified)
Ondrej Oprala 01067b2
-    delim = '\t';
Ondrej Oprala 01067b2
+    mb_setascii (&delim, '\t');
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   if (output_delimiter_string == NULL)
Ondrej Oprala 01067b2
     {
Ondrej Oprala 01067b2
-      static char dummy[2];
Ondrej Oprala 01067b2
-      dummy[0] = delim;
Ondrej Oprala 01067b2
-      dummy[1] = '\0';
Ondrej Oprala 01067b2
-      output_delimiter_string = dummy;
Ondrej Oprala 01067b2
-      output_delimiter_length = 1;
Ondrej Oprala 01067b2
+      output_delimiter_string = mb_ptr (delim);
Ondrej Oprala 01067b2
+      output_delimiter_length = mb_len (delim);
Ondrej Oprala 01067b2
     }
Ondrej Oprala 01067b2
 
Ondrej Oprala 01067b2
   if (optind == argc)