8d0e4d
diff --git a/src/cut.c b/src/cut.c
8d0e4d
index 7ab6be4..022d0ad 100644
8d0e4d
--- a/src/cut.c
8d0e4d
+++ b/src/cut.c
8d0e4d
@@ -28,6 +28,11 @@
8d0e4d
 #include <assert.h>
8d0e4d
 #include <getopt.h>
8d0e4d
 #include <sys/types.h>
8d0e4d
+
8d0e4d
+/* Get mbstate_t, mbrtowc().  */
8d0e4d
+#if HAVE_WCHAR_H
8d0e4d
+# include <wchar.h>
8d0e4d
+#endif
8d0e4d
 #include "system.h"
8d0e4d
 
8d0e4d
 #include "error.h"
8d0e4d
@@ -38,6 +43,18 @@
8d0e4d
 
8d0e4d
 #include "set-fields.h"
8d0e4d
 
8d0e4d
+/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC
8d0e4d
+   installation; work around this configuration error.        */
8d0e4d
+#if !defined MB_LEN_MAX || MB_LEN_MAX < 2
8d0e4d
+# undef MB_LEN_MAX
8d0e4d
+# define MB_LEN_MAX 16
8d0e4d
+#endif
8d0e4d
+
8d0e4d
+/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t.  */
8d0e4d
+#if HAVE_MBRTOWC && defined mbstate_t
8d0e4d
+# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
8d0e4d
+#endif
8d0e4d
+
8d0e4d
 /* The official name of this program (e.g., no 'g' prefix).  */
8d0e4d
 #define PROGRAM_NAME "cut"
8d0e4d
 
8d0e4d
@@ -54,6 +71,52 @@
8d0e4d
     }									\
8d0e4d
   while (0)
8d0e4d
 
8d0e4d
+/* Refill the buffer BUF to get a multibyte character. */
8d0e4d
+#define REFILL_BUFFER(BUF, BUFPOS, BUFLEN, STREAM)                        \
8d0e4d
+  do                                                                        \
8d0e4d
+    {                                                                        \
8d0e4d
+      if (BUFLEN < MB_LEN_MAX && !feof (STREAM) && !ferror (STREAM))        \
8d0e4d
+        {                                                                \
8d0e4d
+          memmove (BUF, BUFPOS, BUFLEN);                                \
8d0e4d
+          BUFLEN += fread (BUF + BUFLEN, sizeof(char), BUFSIZ, STREAM); \
8d0e4d
+          BUFPOS = BUF;                                                        \
8d0e4d
+        }                                                                \
8d0e4d
+    }                                                                        \
8d0e4d
+  while (0)
8d0e4d
+
8d0e4d
+/* Get wide character on BUFPOS. BUFPOS is not included after that.
8d0e4d
+   If byte sequence is not valid as a character, CONVFAIL is true. Otherwise false. */
8d0e4d
+#define GET_NEXT_WC_FROM_BUFFER(WC, BUFPOS, BUFLEN, MBLENGTH, STATE, CONVFAIL) \
8d0e4d
+  do                                                                        \
8d0e4d
+    {                                                                        \
8d0e4d
+      mbstate_t state_bak;                                                \
8d0e4d
+                                                                        \
8d0e4d
+      if (BUFLEN < 1)                                                        \
8d0e4d
+        {                                                                \
8d0e4d
+          WC = WEOF;                                                        \
8d0e4d
+          break;                                                        \
8d0e4d
+        }                                                                \
8d0e4d
+                                                                        \
8d0e4d
+      /* Get a wide character. */                                        \
8d0e4d
+      CONVFAIL = false;                                                        \
8d0e4d
+      state_bak = STATE;                                                \
8d0e4d
+      MBLENGTH = mbrtowc ((wchar_t *)&WC, BUFPOS, BUFLEN, &STATE);        \
8d0e4d
+                                                                        \
8d0e4d
+      switch (MBLENGTH)                                                        \
8d0e4d
+        {                                                                \
8d0e4d
+        case (size_t)-1:                                                \
8d0e4d
+        case (size_t)-2:                                                \
8d0e4d
+          CONVFAIL = true;                                                        \
8d0e4d
+          STATE = state_bak;                                                \
8d0e4d
+          /* Fall througn. */                                                \
8d0e4d
+                                                                        \
8d0e4d
+        case 0:                                                                \
8d0e4d
+          MBLENGTH = 1;                                                        \
8d0e4d
+          break;                                                        \
8d0e4d
+        }                                                                \
8d0e4d
+    }                                                                        \
8d0e4d
+  while (0)
8d0e4d
+
8d0e4d
 
8d0e4d
 /* Pointer inside RP.  When checking if a byte or field is selected
8d0e4d
    by a finite range, we check if it is between CURRENT_RP.LO
8d0e4d
@@ -61,6 +124,9 @@
8d0e4d
    CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */
8d0e4d
 static struct field_range_pair *current_rp;
8d0e4d
 
8d0e4d
+/* Length of the delimiter given as argument to -d.  */
8d0e4d
+size_t delimlen;
8d0e4d
+
8d0e4d
 /* This buffer is used to support the semantics of the -s option
8d0e4d
    (or lack of same) when the specified field list includes (does
8d0e4d
    not include) the first field.  In both of those cases, the entire
8d0e4d
@@ -77,15 +143,25 @@ enum operating_mode
8d0e4d
   {
8d0e4d
     undefined_mode,
8d0e4d
 
8d0e4d
-    /* Output characters that are in the given bytes. */
8d0e4d
+    /* Output bytes that are at the given positions. */
8d0e4d
     byte_mode,
8d0e4d
 
8d0e4d
+    /* Output characters that are at the given positions. */
8d0e4d
+    character_mode,
8d0e4d
+
8d0e4d
     /* Output the given delimiter-separated fields. */
8d0e4d
     field_mode
8d0e4d
   };
8d0e4d
 
8d0e4d
 static enum operating_mode operating_mode;
8d0e4d
 
8d0e4d
+/* If nonzero, when in byte mode, don't split multibyte characters.  */
8d0e4d
+static int byte_mode_character_aware;
8d0e4d
+
8d0e4d
+/* If nonzero, the function for single byte locale is work
8d0e4d
+   if this program runs on multibyte locale. */
8d0e4d
+static int force_singlebyte_mode;
8d0e4d
+
8d0e4d
 /* If true do not output lines containing no delimiter characters.
8d0e4d
    Otherwise, all such lines are printed.  This option is valid only
8d0e4d
    with field mode.  */
8d0e4d
@@ -97,6 +173,9 @@ static bool complement;
8d0e4d
 
8d0e4d
 /* The delimiter character for field mode. */
8d0e4d
 static unsigned char delim;
8d0e4d
+#if HAVE_WCHAR_H
8d0e4d
+static wchar_t wcdelim;
8d0e4d
+#endif
8d0e4d
 
8d0e4d
 /* The delimiter for each line/record. */
8d0e4d
 static unsigned char line_delim = '\n';
8d0e4d
@@ -164,7 +243,7 @@ Print selected parts of lines from each FILE to standard output.\n\
8d0e4d
   -f, --fields=LIST       select only these fields;  also print any line\n\
8d0e4d
                             that contains no delimiter character, unless\n\
8d0e4d
                             the -s option is specified\n\
8d0e4d
-  -n                      (ignored)\n\
8d0e4d
+  -n                      with -b: don't split multibyte characters\n\
8d0e4d
 "), stdout);
8d0e4d
       fputs (_("\
8d0e4d
       --complement        complement the set of selected bytes, characters\n\
8d0e4d
@@ -280,6 +359,82 @@ cut_bytes (FILE *stream)
8d0e4d
     }
8d0e4d
 }
8d0e4d
 
8d0e4d
+#if HAVE_MBRTOWC
8d0e4d
+/* This function is in use for the following case.
8d0e4d
+
8d0e4d
+   1. Read from the stream STREAM, printing to standard output any selected
8d0e4d
+   characters.
8d0e4d
+
8d0e4d
+   2. Read from stream STREAM, printing to standard output any selected bytes,
8d0e4d
+   without splitting multibyte characters.  */
8d0e4d
+
8d0e4d
+static void
8d0e4d
+cut_characters_or_cut_bytes_no_split (FILE *stream)
8d0e4d
+{
8d0e4d
+  uintmax_t idx;             /* number of bytes or characters in the line so far. */
8d0e4d
+  char buf[MB_LEN_MAX + BUFSIZ];  /* For spooling a read byte sequence. */
8d0e4d
+  char *bufpos;                /* Next read position of BUF. */
8d0e4d
+  size_t buflen;        /* The length of the byte sequence in buf. */
8d0e4d
+  wint_t wc;                /* A gotten wide character. */
8d0e4d
+  size_t mblength;        /* The byte size of a multibyte character which shows
8d0e4d
+                           as same character as WC. */
8d0e4d
+  mbstate_t state;        /* State of the stream. */
8d0e4d
+  bool convfail = false;  /* true, when conversion failed. Otherwise false. */
8d0e4d
+  /* Whether to begin printing delimiters between ranges for the current line.
8d0e4d
+     Set after we've begun printing data corresponding to the first range.  */
8d0e4d
+  bool print_delimiter = false;
8d0e4d
+
8d0e4d
+  idx = 0;
8d0e4d
+  buflen = 0;
8d0e4d
+  bufpos = buf;
8d0e4d
+  memset (&state, '\0', sizeof(mbstate_t));
8d0e4d
+
8d0e4d
+  current_rp = frp;
8d0e4d
+
8d0e4d
+  while (1)
8d0e4d
+    {
8d0e4d
+      REFILL_BUFFER (buf, bufpos, buflen, stream);
8d0e4d
+
8d0e4d
+      GET_NEXT_WC_FROM_BUFFER (wc, bufpos, buflen, mblength, state, convfail);
8d0e4d
+      (void) convfail;  /* ignore unused */
8d0e4d
+
8d0e4d
+      if (wc == WEOF)
8d0e4d
+        {
8d0e4d
+          if (idx > 0)
8d0e4d
+            putchar (line_delim);
8d0e4d
+          break;
8d0e4d
+        }
8d0e4d
+      else if (wc == line_delim)
8d0e4d
+        {
8d0e4d
+          putchar (line_delim);
8d0e4d
+          idx = 0;
8d0e4d
+          print_delimiter = false;
8d0e4d
+          current_rp = frp;
8d0e4d
+        }
8d0e4d
+      else
8d0e4d
+        {
8d0e4d
+          next_item (&idx);
8d0e4d
+          if (print_kth (idx))
8d0e4d
+            {
8d0e4d
+              if (output_delimiter_specified)
8d0e4d
+                {
8d0e4d
+                  if (print_delimiter && is_range_start_index (idx))
8d0e4d
+                    {
8d0e4d
+                      fwrite (output_delimiter_string, sizeof (char),
8d0e4d
+                              output_delimiter_length, stdout);
8d0e4d
+                    }
8d0e4d
+                  print_delimiter = true;
8d0e4d
+                }
8d0e4d
+              fwrite (bufpos, mblength, sizeof(char), stdout);
8d0e4d
+            }
8d0e4d
+        }
8d0e4d
+
8d0e4d
+      buflen -= mblength;
8d0e4d
+      bufpos += mblength;
8d0e4d
+    }
8d0e4d
+}
8d0e4d
+#endif
8d0e4d
+
8d0e4d
 /* Read from stream STREAM, printing to standard output any selected fields.  */
8d0e4d
 
8d0e4d
 static void
8d0e4d
@@ -425,13 +580,211 @@ cut_fields (FILE *stream)
8d0e4d
     }
8d0e4d
 }
8d0e4d
 
8d0e4d
+#if HAVE_MBRTOWC
8d0e4d
+static void
8d0e4d
+cut_fields_mb (FILE *stream)
8d0e4d
+{
8d0e4d
+  int c;
8d0e4d
+  uintmax_t field_idx;
8d0e4d
+  int found_any_selected_field;
8d0e4d
+  int buffer_first_field;
8d0e4d
+  int empty_input;
8d0e4d
+  char buf[MB_LEN_MAX + BUFSIZ];  /* For spooling a read byte sequence. */
8d0e4d
+  char *bufpos;                /* Next read position of BUF. */
8d0e4d
+  size_t buflen;        /* The length of the byte sequence in buf. */
8d0e4d
+  wint_t wc = 0;        /* A gotten wide character. */
8d0e4d
+  size_t mblength;        /* The byte size of a multibyte character which shows
8d0e4d
+                           as same character as WC. */
8d0e4d
+  mbstate_t state;        /* State of the stream. */
8d0e4d
+  bool convfail = false;  /* true, when conversion failed. Otherwise false. */
8d0e4d
+
8d0e4d
+  current_rp = frp;
8d0e4d
+
8d0e4d
+  found_any_selected_field = 0;
8d0e4d
+  field_idx = 1;
8d0e4d
+  bufpos = buf;
8d0e4d
+  buflen = 0;
8d0e4d
+  memset (&state, '\0', sizeof(mbstate_t));
8d0e4d
+
8d0e4d
+  c = getc (stream);
8d0e4d
+  empty_input = (c == EOF);
8d0e4d
+  if (c != EOF)
8d0e4d
+  {
8d0e4d
+    ungetc (c, stream);
8d0e4d
+    wc = 0;
8d0e4d
+  }
8d0e4d
+  else
8d0e4d
+    wc = WEOF;
8d0e4d
+
8d0e4d
+  /* To support the semantics of the -s flag, we may have to buffer
8d0e4d
+     all of the first field to determine whether it is `delimited.'
8d0e4d
+     But that is unnecessary if all non-delimited lines must be printed
8d0e4d
+     and the first field has been selected, or if non-delimited lines
8d0e4d
+     must be suppressed and the first field has *not* been selected.
8d0e4d
+     That is because a non-delimited line has exactly one field.  */
8d0e4d
+  buffer_first_field = (suppress_non_delimited ^ !print_kth (1));
8d0e4d
+
8d0e4d
+  while (1)
8d0e4d
+    {
8d0e4d
+      if (field_idx == 1 && buffer_first_field)
8d0e4d
+        {
8d0e4d
+          int len = 0;
8d0e4d
+
8d0e4d
+          while (1)
8d0e4d
+            {
8d0e4d
+              REFILL_BUFFER (buf, bufpos, buflen, stream);
8d0e4d
+
8d0e4d
+              GET_NEXT_WC_FROM_BUFFER
8d0e4d
+                (wc, bufpos, buflen, mblength, state, convfail);
8d0e4d
+
8d0e4d
+              if (wc == WEOF)
8d0e4d
+                break;
8d0e4d
+
8d0e4d
+              field_1_buffer = xrealloc (field_1_buffer, len + mblength);
8d0e4d
+              memcpy (field_1_buffer + len, bufpos, mblength);
8d0e4d
+              len += mblength;
8d0e4d
+              buflen -= mblength;
8d0e4d
+              bufpos += mblength;
8d0e4d
+
8d0e4d
+              if (!convfail && (wc == line_delim || wc == wcdelim))
8d0e4d
+                break;
8d0e4d
+            }
8d0e4d
+
8d0e4d
+          if (len <= 0 && wc == WEOF)
8d0e4d
+            break;
8d0e4d
+
8d0e4d
+          /* If the first field extends to the end of line (it is not
8d0e4d
+             delimited) and we are printing all non-delimited lines,
8d0e4d
+             print this one.  */
8d0e4d
+          if (convfail || (!convfail && wc != wcdelim))
8d0e4d
+            {
8d0e4d
+              if (suppress_non_delimited)
8d0e4d
+                {
8d0e4d
+                  /* Empty.        */
8d0e4d
+                }
8d0e4d
+              else
8d0e4d
+                {
8d0e4d
+                  fwrite (field_1_buffer, sizeof (char), len, stdout);
8d0e4d
+                  /* Make sure the output line is newline terminated.  */
8d0e4d
+                  if (convfail || (!convfail && wc != line_delim))
8d0e4d
+                    putchar (line_delim);
8d0e4d
+                }
8d0e4d
+              continue;
8d0e4d
+            }
8d0e4d
+
8d0e4d
+          if (print_kth (1))
8d0e4d
+            {
8d0e4d
+              /* Print the field, but not the trailing delimiter.  */
8d0e4d
+              fwrite (field_1_buffer, sizeof (char), len - 1, stdout);
8d0e4d
+              found_any_selected_field = 1;
8d0e4d
+            }
8d0e4d
+          next_item (&field_idx);
8d0e4d
+        }
8d0e4d
+
8d0e4d
+      if (wc != WEOF)
8d0e4d
+        {
8d0e4d
+          if (print_kth (field_idx))
8d0e4d
+            {
8d0e4d
+              if (found_any_selected_field)
8d0e4d
+                {
8d0e4d
+                  fwrite (output_delimiter_string, sizeof (char),
8d0e4d
+                          output_delimiter_length, stdout);
8d0e4d
+                }
8d0e4d
+              found_any_selected_field = 1;
8d0e4d
+            }
8d0e4d
+
8d0e4d
+          while (1)
8d0e4d
+            {
8d0e4d
+              REFILL_BUFFER (buf, bufpos, buflen, stream);
8d0e4d
+
8d0e4d
+              GET_NEXT_WC_FROM_BUFFER
8d0e4d
+                (wc, bufpos, buflen, mblength, state, convfail);
8d0e4d
+
8d0e4d
+              if (wc == WEOF)
8d0e4d
+                break;
8d0e4d
+              else if (!convfail && (wc == wcdelim || wc == line_delim))
8d0e4d
+                {
8d0e4d
+                  buflen -= mblength;
8d0e4d
+                  bufpos += mblength;
8d0e4d
+                  break;
8d0e4d
+                }
8d0e4d
+
8d0e4d
+              if (print_kth (field_idx))
8d0e4d
+                fwrite (bufpos, mblength, sizeof(char), stdout);
8d0e4d
+
8d0e4d
+              buflen -= mblength;
8d0e4d
+              bufpos += mblength;
8d0e4d
+            }
8d0e4d
+        }
8d0e4d
+
8d0e4d
+      if ((!convfail || wc == line_delim) && buflen < 1)
8d0e4d
+        wc = WEOF;
8d0e4d
+
8d0e4d
+      if (!convfail && wc == wcdelim)
8d0e4d
+        next_item (&field_idx);
8d0e4d
+      else if (wc == WEOF || (!convfail && wc == line_delim))
8d0e4d
+        {
8d0e4d
+          if (found_any_selected_field
8d0e4d
+              || (!empty_input && !(suppress_non_delimited && field_idx == 1)))
8d0e4d
+            putchar (line_delim);
8d0e4d
+          if (wc == WEOF)
8d0e4d
+            break;
8d0e4d
+          field_idx = 1;
8d0e4d
+          current_rp = frp;
8d0e4d
+          found_any_selected_field = 0;
8d0e4d
+        }
8d0e4d
+    }
8d0e4d
+}
8d0e4d
+#endif
8d0e4d
+
8d0e4d
 static void
8d0e4d
 cut_stream (FILE *stream)
8d0e4d
 {
8d0e4d
-  if (operating_mode == byte_mode)
8d0e4d
-    cut_bytes (stream);
8d0e4d
+#if HAVE_MBRTOWC
8d0e4d
+  if (MB_CUR_MAX > 1 && !force_singlebyte_mode)
8d0e4d
+    {
8d0e4d
+      switch (operating_mode)
8d0e4d
+        {
8d0e4d
+        case byte_mode:
8d0e4d
+          if (byte_mode_character_aware)
8d0e4d
+            cut_characters_or_cut_bytes_no_split (stream);
8d0e4d
+          else
8d0e4d
+            cut_bytes (stream);
8d0e4d
+          break;
8d0e4d
+
8d0e4d
+        case character_mode:
8d0e4d
+          cut_characters_or_cut_bytes_no_split (stream);
8d0e4d
+          break;
8d0e4d
+
8d0e4d
+        case field_mode:
8d0e4d
+          if (delimlen == 1)
8d0e4d
+            {
8d0e4d
+              /* Check if we have utf8 multibyte locale, so we can use this
8d0e4d
+                 optimization because of uniqueness of characters, which is
8d0e4d
+                 not true for e.g. SJIS */
8d0e4d
+              char * loc = setlocale(LC_CTYPE, NULL);
8d0e4d
+              if (loc && (strstr (loc, "UTF-8") || strstr (loc, "utf-8") ||
8d0e4d
+                  strstr (loc, "UTF8") || strstr (loc, "utf8")))
8d0e4d
+                {
8d0e4d
+                  cut_fields (stream);
8d0e4d
+                  break;
8d0e4d
+                }
8d0e4d
+            }
8d0e4d
+          cut_fields_mb (stream);
8d0e4d
+          break;
8d0e4d
+
8d0e4d
+        default:
8d0e4d
+          abort ();
8d0e4d
+        }
8d0e4d
+    }
8d0e4d
   else
8d0e4d
-    cut_fields (stream);
8d0e4d
+#endif
8d0e4d
+    {
8d0e4d
+      if (operating_mode == field_mode)
8d0e4d
+        cut_fields (stream);
8d0e4d
+      else
8d0e4d
+        cut_bytes (stream);
8d0e4d
+    }
8d0e4d
 }
8d0e4d
 
8d0e4d
 /* Process file FILE to standard output.
8d0e4d
@@ -483,6 +836,7 @@ main (int argc, char **argv)
8d0e4d
   bool ok;
8d0e4d
   bool delim_specified = false;
8d0e4d
   char *spec_list_string IF_LINT ( = NULL);
8d0e4d
+  char mbdelim[MB_LEN_MAX + 1];
8d0e4d
 
8d0e4d
   initialize_main (&argc, &argv);
8d0e4d
   set_program_name (argv[0]);
8d0e4d
@@ -505,7 +859,6 @@ main (int argc, char **argv)
8d0e4d
       switch (optc)
8d0e4d
         {
8d0e4d
         case 'b':
8d0e4d
-        case 'c':
8d0e4d
           /* Build the byte list. */
8d0e4d
           if (operating_mode != undefined_mode)
8d0e4d
             FATAL_ERROR (_("only one type of list may be specified"));
8d0e4d
@@ -513,6 +866,14 @@ main (int argc, char **argv)
8d0e4d
           spec_list_string = optarg;
8d0e4d
           break;
8d0e4d
 
8d0e4d
+        case 'c':
8d0e4d
+          /* Build the character list. */
8d0e4d
+          if (operating_mode != undefined_mode)
8d0e4d
+            FATAL_ERROR (_("only one type of list may be specified"));
8d0e4d
+          operating_mode = character_mode;
8d0e4d
+          spec_list_string = optarg;
8d0e4d
+          break;
8d0e4d
+
8d0e4d
         case 'f':
8d0e4d
           /* Build the field list. */
8d0e4d
           if (operating_mode != undefined_mode)
8d0e4d
@@ -524,10 +885,38 @@ main (int argc, char **argv)
8d0e4d
         case 'd':
8d0e4d
           /* New delimiter. */
8d0e4d
           /* Interpret -d '' to mean 'use the NUL byte as the delimiter.'  */
8d0e4d
-          if (optarg[0] != '\0' && optarg[1] != '\0')
8d0e4d
-            FATAL_ERROR (_("the delimiter must be a single character"));
8d0e4d
-          delim = optarg[0];
8d0e4d
-          delim_specified = true;
8d0e4d
+            {
8d0e4d
+#if HAVE_MBRTOWC
8d0e4d
+              if(MB_CUR_MAX > 1)
8d0e4d
+                {
8d0e4d
+                  mbstate_t state;
8d0e4d
+
8d0e4d
+                  memset (&state, '\0', sizeof(mbstate_t));
8d0e4d
+                  delimlen = mbrtowc (&wcdelim, optarg, strnlen(optarg, MB_LEN_MAX), &state);
8d0e4d
+
8d0e4d
+                  if (delimlen == (size_t)-1 || delimlen == (size_t)-2)
8d0e4d
+                    ++force_singlebyte_mode;
8d0e4d
+                  else
8d0e4d
+                    {
8d0e4d
+                      delimlen = (delimlen < 1) ? 1 : delimlen;
8d0e4d
+                      if (wcdelim != L'\0' && *(optarg + delimlen) != '\0')
8d0e4d
+                        FATAL_ERROR (_("the delimiter must be a single character"));
8d0e4d
+                      memcpy (mbdelim, optarg, delimlen);
8d0e4d
+                      mbdelim[delimlen] = '\0';
8d0e4d
+                      if (delimlen == 1)
8d0e4d
+                        delim = *optarg;
8d0e4d
+                    }
8d0e4d
+                }
8d0e4d
+
8d0e4d
+              if (MB_CUR_MAX <= 1 || force_singlebyte_mode)
8d0e4d
+#endif
8d0e4d
+                {
8d0e4d
+                  if (optarg[0] != '\0' && optarg[1] != '\0')
8d0e4d
+                    FATAL_ERROR (_("the delimiter must be a single character"));
8d0e4d
+                  delim = (unsigned char) optarg[0];
8d0e4d
+                }
8d0e4d
+            delim_specified = true;
8d0e4d
+          }
8d0e4d
           break;
8d0e4d
 
8d0e4d
         case OUTPUT_DELIMITER_OPTION:
8d0e4d
@@ -540,6 +929,7 @@ main (int argc, char **argv)
8d0e4d
           break;
8d0e4d
 
8d0e4d
         case 'n':
8d0e4d
+          byte_mode_character_aware = 1;
8d0e4d
           break;
8d0e4d
 
8d0e4d
         case 's':
8d0e4d
@@ -579,15 +969,34 @@ main (int argc, char **argv)
8d0e4d
               | (complement ? SETFLD_COMPLEMENT : 0) );
8d0e4d
 
8d0e4d
   if (!delim_specified)
8d0e4d
-    delim = '\t';
8d0e4d
+    {
8d0e4d
+      delim = '\t';
8d0e4d
+#ifdef HAVE_MBRTOWC
8d0e4d
+      wcdelim = L'\t';
8d0e4d
+      mbdelim[0] = '\t';
8d0e4d
+      mbdelim[1] = '\0';
8d0e4d
+      delimlen = 1;
8d0e4d
+#endif
8d0e4d
+    }
8d0e4d
 
8d0e4d
   if (output_delimiter_string == NULL)
8d0e4d
     {
8d0e4d
-      static char dummy[2];
8d0e4d
-      dummy[0] = delim;
8d0e4d
-      dummy[1] = '\0';
8d0e4d
-      output_delimiter_string = dummy;
8d0e4d
-      output_delimiter_length = 1;
8d0e4d
+#ifdef HAVE_MBRTOWC
8d0e4d
+      if (MB_CUR_MAX > 1 && !force_singlebyte_mode)
8d0e4d
+        {
8d0e4d
+          output_delimiter_string = xstrdup(mbdelim);
8d0e4d
+          output_delimiter_length = delimlen;
8d0e4d
+        }
8d0e4d
+
8d0e4d
+      if (MB_CUR_MAX <= 1 || force_singlebyte_mode)
8d0e4d
+#endif
8d0e4d
+        {
8d0e4d
+          static char dummy[2];
8d0e4d
+          dummy[0] = delim;
8d0e4d
+          dummy[1] = '\0';
8d0e4d
+          output_delimiter_string = dummy;
8d0e4d
+          output_delimiter_length = 1;
8d0e4d
+        }
8d0e4d
     }
8d0e4d
 
8d0e4d
   if (optind == argc)