Blob Blame History Raw
diff -up diffutils-3.6/src/diff.c.i18n diffutils-3.6/src/diff.c
--- diffutils-3.6/src/diff.c.i18n	2017-05-06 20:02:54.000000000 +0100
+++ diffutils-3.6/src/diff.c	2017-05-22 10:52:21.989254674 +0100
@@ -76,6 +76,8 @@ static void try_help (char const *, char
 static void check_stdout (void);
 static void usage (void);
 
+bool (*lines_differ) (char const *, size_t, char const *, size_t);
+
 /* If comparing directories, compare their common subdirectories
    recursively.  */
 static bool recursive;
@@ -298,6 +300,13 @@ main (int argc, char **argv)
   excluded = new_exclude ();
   presume_output_tty = false;
 
+#ifdef HANDLE_MULTIBYTE
+  if (MB_CUR_MAX > 1)
+    lines_differ = lines_differ_multibyte;
+  else
+#endif
+    lines_differ = lines_differ_singlebyte;
+
   /* Decode the options.  */
 
   while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
diff -up diffutils-3.6/src/diff.h.i18n diffutils-3.6/src/diff.h
--- diffutils-3.6/src/diff.h.i18n	2017-01-01 11:22:36.000000000 +0000
+++ diffutils-3.6/src/diff.h	2017-05-22 10:51:09.050371844 +0100
@@ -23,6 +23,17 @@
 #include <stdio.h>
 #include <unlocked-io.h>
 
+/* For platforms which support the ISO C ammendment 1 functionality we
+   support user-defined character classes. */
+#if defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H
+/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
+# include <wchar.h>
+# include <wctype.h>
+# if defined (HAVE_MBRTOWC)
+#  define HANDLE_MULTIBYTE	1
+# endif
+#endif
+
 /* What kind of changes a hunk contains.  */
 enum changes
 {
@@ -381,7 +392,11 @@ extern void print_sdiff_script (struct c
 extern char const change_letter[4];
 extern char const pr_program[];
 extern char *concat (char const *, char const *, char const *);
-extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
+extern bool (*lines_differ) (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
+extern bool lines_differ_singlebyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
+#ifdef HANDLE_MULTIBYTE
+extern bool lines_differ_multibyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
+#endif
 extern lin translate_line_number (struct file_data const *, lin);
 extern struct change *find_change (struct change *);
 extern struct change *find_reverse_change (struct change *);
diff -up diffutils-3.6/src/io.c.i18n diffutils-3.6/src/io.c
--- diffutils-3.6/src/io.c.i18n	2017-01-01 11:22:36.000000000 +0000
+++ diffutils-3.6/src/io.c	2017-05-22 10:51:09.050371844 +0100
@@ -23,6 +23,7 @@
 #include <cmpbuf.h>
 #include <file-type.h>
 #include <xalloc.h>
+#include <assert.h>
 
 /* Rotate an unsigned value to the left.  */
 #define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n)))
@@ -215,6 +216,28 @@ slurp (struct file_data *current)
 
 /* Split the file into lines, simultaneously computing the equivalence
    class for each line.  */
+#ifdef HANDLE_MULTIBYTE
+# define MBC2WC(P, END, MBLENGTH, WC, STATE, CONVFAIL)		\
+do								\
+  {								\
+    mbstate_t state_bak = STATE;				\
+								\
+    CONVFAIL = 0;						\
+    MBLENGTH = mbrtowc (&WC, P, END - (char const *)P, &STATE);	\
+								\
+    switch (MBLENGTH)						\
+      {								\
+      case (size_t)-2:						\
+      case (size_t)-1:						\
+	STATE = state_bak;					\
+	++CONVFAIL;						\
+	/* Fall through. */					\
+      case 0:							\
+	MBLENGTH = 1;						\
+      }								\
+  }								\
+ while (0)
+#endif
 
 static void
 find_and_hash_each_line (struct file_data *current)
@@ -241,12 +264,300 @@ find_and_hash_each_line (struct file_dat
   bool same_length_diff_contents_compare_anyway =
     diff_length_compare_anyway | ig_case;
 
+#ifdef HANDLE_MULTIBYTE
+  wchar_t wc;
+  size_t mblength;
+  mbstate_t state;
+  int convfail;
+
+  memset (&state, '\0', sizeof (mbstate_t));
+#endif
+
   while (p < suffix_begin)
     {
       char const *ip = p;
       hash_value h = 0;
       unsigned char c;
 
+#ifdef HANDLE_MULTIBYTE
+      if (MB_CUR_MAX > 1)
+	{
+	  wchar_t lo_wc;
+	  char mbc[MB_LEN_MAX];
+	  mbstate_t state_wc;
+
+	  /* Hash this line until we find a newline.  */
+	  switch (ig_white_space)
+	    {
+	    case IGNORE_ALL_SPACE:
+	      while (1)
+		{
+		  if (*p == '\n')
+		    {
+		      ++p;
+		      break;
+		    }
+
+		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
+
+		  if (convfail)
+		    mbc[0] = *p++;
+		  else if (!iswspace (wc))
+		    {
+		      bool flag = 0;
+
+		      if (ig_case)
+			{
+			  lo_wc = towlower (wc);
+			  if (lo_wc != wc)
+			    {
+			      flag = 1;
+
+			      p += mblength;
+			      memset (&state_wc, '\0', sizeof(mbstate_t));
+			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
+
+			      assert (mblength != (size_t)-1 &&
+				      mblength != (size_t)-2);
+
+			      mblength = (mblength < 1) ? 1 : mblength;
+			    }
+			}
+
+		      if (!flag)
+			{
+			  for (i = 0; i < mblength; i++)
+			    mbc[i] = *p++;
+			}
+		    }
+		  else
+		    {
+		      p += mblength;
+		      continue;
+		    }
+
+		  for (i = 0; i < mblength; i++)
+		    h = HASH (h, mbc[i]);
+		}
+	      break;
+
+	    case IGNORE_SPACE_CHANGE:
+	      while (1)
+		{
+		  if (*p == '\n')
+		    {
+		      ++p;
+		      break;
+		    }
+
+		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
+
+		  if (!convfail && iswspace (wc))
+		    {
+		      while (1)
+			{
+			  if (*p == '\n')
+			    {
+			      ++p;
+			      goto hashing_done;
+			    }
+
+			  p += mblength;
+			  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
+			  if (convfail || !iswspace (wc))
+			    break;
+			}
+		      h = HASH (h, ' ');
+		    }
+
+		  /* WC is now the first non-space.  */
+		  if (convfail)
+		    mbc[0] = *p++;
+		  else
+		    {
+		      bool flag = 0;
+
+		      if (ignore_case)
+			{
+			  lo_wc = towlower (wc);
+			  if (lo_wc != wc)
+			    {
+			      flag = 1;
+
+			      p += mblength;
+			      memset (&state_wc, '\0', sizeof(mbstate_t));
+			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
+
+			      assert (mblength != (size_t)-1 &&
+				      mblength != (size_t)-2);
+
+			      mblength = (mblength < 1) ? 1 : mblength;
+			    }
+			}
+
+		      if (!flag)
+			{
+			  for (i = 0; i < mblength; i++)
+			    mbc[i] = *p++;
+			}
+		    }
+
+		  for (i = 0; i < mblength; i++)
+		    h = HASH (h, mbc[i]);
+		}
+	      break;
+
+	    case IGNORE_TAB_EXPANSION:
+	    case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
+	    case IGNORE_TRAILING_SPACE:
+	      {
+		size_t column = 0;
+		while (1)
+		  {
+		    if (*p == '\n')
+		      {
+			++p;
+			break;
+		      }
+
+		    MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
+
+		    if (!convfail
+			&& ig_white_space & IGNORE_TRAILING_SPACE
+			&& iswspace (wc))
+		      {
+			char const *p1 = p;
+			while (1)
+			  {
+			    if (*p1 == '\n')
+			      {
+				p = p1 + 1;
+				goto hashing_done;
+			      }
+
+			    p1 += mblength;
+			    MBC2WC (p1, suffix_begin, mblength, wc, state, convfail);
+			    if (convfail || !iswspace (wc))
+			      break;
+			  }
+		      }
+
+		    size_t repetitions = 1;
+		    bool no_convert = 0;
+
+		    if (ig_white_space & IGNORE_TAB_EXPANSION)
+		      {
+			if (convfail)
+			  column++;
+			else
+			  switch (wc)
+			    {
+			    case L'\b':
+			      column -= 0 < column;
+			      break;
+
+			    case L'\t':
+			      mbc[0] = ' ';
+			      mblength = 1;
+			      no_convert = 1;
+			      p++;
+			      assert(mblength == 1);
+			      repetitions = tabsize - column % tabsize;
+			      column = (column + repetitions < column
+					? 0
+					: column + repetitions);
+			      break;
+
+			    case L'\r':
+			      column = 0;
+			      break;
+
+			    default:
+			      column += wcwidth (wc);
+			      break;
+			    }
+		      }
+
+		    if (ig_case)
+		      {
+			lo_wc = towlower (wc);
+			if (lo_wc != wc)
+			  {
+			    no_convert = 1;
+			    p += mblength;
+			    memset (&state_wc, '\0', sizeof(mbstate_t));
+			    mblength = wcrtomb (mbc, lo_wc, &state_wc);
+
+			    assert (mblength != (size_t)-1 &&
+				    mblength != (size_t)-2);
+
+			    mblength = (mblength < 1) ? 1 : mblength;
+			  }
+		      }
+
+		    if (!no_convert)
+		      for (i = 0; i < mblength; i++)
+			mbc[i] = *p++;
+
+		    do
+		      {
+			for (i = 0; i < mblength; i++)
+			  h = HASH (h, mbc[i]);
+		      }
+		    while (--repetitions != 0);
+		  }
+	      }
+	      break;
+
+	    default:
+	      while (1)
+		{
+		  if (*p == '\n')
+		    {
+		      ++p;
+		      break;
+		    }
+
+		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
+
+		  if (convfail)
+		    mbc[0] = *p++;
+		  else
+		    {
+		      int flag = 0;
+
+		      if (ig_case)
+			{
+			  lo_wc = towlower (wc);
+			  if (lo_wc != wc)
+			    {
+			      flag = 1;
+			      p += mblength;
+			      memset (&state_wc, '\0', sizeof(mbstate_t));
+			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
+
+			      assert (mblength != (size_t)-1 &&
+				      mblength != (size_t)-2);
+
+			      mblength = (mblength < 1) ? 1 : mblength;
+			    }
+			}
+
+		      if (!flag)
+			{
+			  for (i = 0; i < mblength; i++)
+			    mbc[i] = *p++;
+			}
+		    }
+
+		  for (i = 0; i < mblength; i++)
+		    h = HASH (h, mbc[i]);
+		}
+	    }
+	}
+      else
+#endif
+
       /* Hash this line until we find a newline.  */
       switch (ig_white_space)
 	{
@@ -397,7 +708,7 @@ find_and_hash_each_line (struct file_dat
 	    else if (!diff_length_compare_anyway)
 	      continue;
 
-	    if (! lines_differ (eqline, ip))
+	    if (! lines_differ (eqline, eqs[i].length + 1, ip, length + 1))
 	      break;
 	  }
 
diff -up diffutils-3.6/src/util.c.i18n diffutils-3.6/src/util.c
--- diffutils-3.6/src/util.c.i18n	2017-05-18 18:39:59.000000000 +0100
+++ diffutils-3.6/src/util.c	2017-05-22 10:51:09.050371844 +0100
@@ -985,7 +985,8 @@ finish_output (void)
    Return nonzero if the lines differ.  */
 
 bool
-lines_differ (char const *s1, char const *s2)
+lines_differ_singlebyte (char const *s1, size_t s1len,
+			 char const *s2, size_t s2len)
 {
   register char const *t1 = s1;
   register char const *t2 = s2;
@@ -1141,6 +1142,354 @@ lines_differ (char const *s1, char const
 
   return true;
 }
+
+#ifdef HANDLE_MULTIBYTE
+# define MBC2WC(T, END, MBLENGTH, WC, STATE, CONVFAIL)	\
+do							\
+  {							\
+    mbstate_t bak = STATE;				\
+							\
+    CONVFAIL = 0;					\
+    MBLENGTH = mbrtowc (&WC, T, END - T, &STATE);	\
+							\
+    switch (MBLENGTH)					\
+      {							\
+      case (size_t)-2:					\
+      case (size_t)-1:					\
+	STATE = bak;					\
+	++CONVFAIL;					\
+	/* Fall through. */				\
+      case 0:						\
+	MBLENGTH = 1;					\
+      }							\
+  }							\
+ while (0)
+
+bool
+lines_differ_multibyte (char const *s1, size_t s1len,
+			char const *s2, size_t s2len)
+{
+  char const *end1, *end2;
+  char c1, c2;
+  wchar_t wc1, wc2, wc1_bak, wc2_bak;
+  size_t mblen1, mblen2;
+  mbstate_t state1, state2, state1_bak, state2_bak;
+  int convfail1, convfail2, convfail1_bak, convfail2_bak;
+  
+  char const *t1 = s1;
+  char const *t2 = s2;
+  char const *t1_bak, *t2_bak;
+  size_t column = 0;
+
+  if (ignore_white_space == IGNORE_NO_WHITE_SPACE  && !ignore_case)
+    {
+      while (*t1 != '\n')
+	if (*t1++ != *t2++)
+	  return 1;
+      return 0;
+    }
+
+  end1 = t1 + s1len;
+  end2 = t2 + s2len;
+
+  memset (&state1, '\0', sizeof (mbstate_t));
+  memset (&state2, '\0', sizeof (mbstate_t));
+
+  while (1)
+    {
+      c1 = *t1;
+      c2 = *t2;
+      MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
+      MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
+
+      /* Test for exact char equality first, since it's a common case.  */
+      if (convfail1 ^ convfail2)
+	break;
+      else if (convfail1 && convfail2 && c1 != c2)
+	break;
+      else if (!convfail1 && !convfail2 && wc1 != wc2)
+	{
+	  switch (ignore_white_space)
+	    {
+	    case IGNORE_ALL_SPACE:
+	      /* For -w, just skip past any white space.  */
+	      while (1)
+		{
+		  if (convfail1)
+		    break;
+		  else if (wc1 == L'\n' || !iswspace (wc1))
+		    break;
+
+		  t1 += mblen1;
+		  c1 = *t1;
+		  MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
+		}
+
+	      while (1)
+		{
+		  if (convfail2)
+		    break;
+		  else if (wc2 == L'\n' || !iswspace (wc2))
+		    break;
+
+		  t2 += mblen2;
+		  c2 = *t2;
+		  MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
+		}
+	      t1 += mblen1;
+	      t2 += mblen2;
+	      break;
+
+	    case IGNORE_SPACE_CHANGE:
+	      /* For -b, advance past any sequence of white space in
+		 line 1 and consider it just one space, or nothing at
+		 all if it is at the end of the line.  */
+	      if (wc1 != L'\n' && iswspace (wc1))
+		{
+		  size_t mblen_bak;
+		  mbstate_t state_bak;
+
+		  do
+		    {
+		      t1 += mblen1;
+		      mblen_bak = mblen1;
+		      state_bak = state1;
+		      MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
+		    }
+		  while (!convfail1 && (wc1 != L'\n' && iswspace (wc1)));
+
+		  state1 = state_bak;
+		  mblen1 = mblen_bak;
+		  t1 -= mblen1;
+		  convfail1 = 0;
+		  wc1 = L' ';
+		}
+
+	      /* Likewise for line 2.  */
+	      if (wc2 != L'\n' && iswspace (wc2))
+		{
+		  size_t mblen_bak;
+		  mbstate_t state_bak;
+
+		  do
+		    {
+		      t2 += mblen2;
+		      mblen_bak = mblen2;
+		      state_bak = state2;
+		      MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
+		    }
+		  while (!convfail2 && (wc2 != L'\n' && iswspace (wc2)));
+
+		  state2 = state_bak;
+		  mblen2 = mblen_bak;
+		  t2 -= mblen2;
+		  convfail2 = 0;
+		  wc2 = L' ';
+		}
+
+	      if (wc1 != wc2)
+		{
+		  /* If we went too far when doing the simple test for
+		     equality, go back to the first non-whitespace
+		     character in both sides and try again.  */
+		  if (wc2 == L' ' && wc1 != L'\n' &&
+		      t1 > s1 &&
+		      !convfail1_bak && iswspace (wc1_bak))
+		    {
+		      t1 = t1_bak;
+		      wc1 = wc1_bak;
+		      state1 = state1_bak;
+		      convfail1 = convfail1_bak;
+		      continue;
+		    }
+		  if (wc1 == L' ' && wc2 != L'\n'
+		      && t2 > s2
+		      && !convfail2_bak && iswspace (wc2_bak))
+		    {
+		      t2 = t2_bak;
+		      wc2 = wc2_bak;
+		      state2 = state2_bak;
+		      convfail2 = convfail2_bak;
+		      continue;
+		    }
+		}
+
+	      t1_bak = t1;		  t2_bak = t2;
+	      wc1_bak = wc1;		  wc2_bak = wc2;
+	      state1_bak = state1;	  state2_bak = state2;
+	      convfail1_bak = convfail1;  convfail2_bak = convfail2;
+
+	      if (wc1 == L'\n')
+		wc1 = L' ';
+	      else
+		t1 += mblen1;
+
+	      if (wc2 == L'\n')
+		wc2 = L' ';
+	      else
+		t2 += mblen2;
+
+	      break;
+
+	    case IGNORE_TRAILING_SPACE:
+	    case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
+	      if (iswspace (wc1) && iswspace (wc2))
+		{
+		  char const *p;
+		  wchar_t wc;
+		  size_t mblength;
+		  int convfail;
+		  mbstate_t state;
+		  bool just_whitespace_left = 1;
+		  if (wc1 != L'\n')
+		    {
+		      mblength = mblen1;
+		      p = t1;
+		      memset (&state, '\0', sizeof(mbstate_t));
+		      while (p < end1)
+			{
+			  if (*p == '\n')
+			    break;
+
+			  p += mblength;
+			  MBC2WC (p, end1, mblength, wc, state, convfail);
+			  if (convfail || !iswspace (wc))
+			    {
+			      just_whitespace_left = 0;
+			      break;
+			    }
+			}
+		    }
+		  if (just_whitespace_left && wc2 != L'\n')
+		    {
+		      mblength = mblen2;
+		      p = t2;
+		      memset (&state, '\0', sizeof(mbstate_t));
+		      while (p < end2)
+			{
+			  if (*p == '\n')
+			    break;
+
+			  p += mblength;
+			  MBC2WC (p, end2, mblength, wc, state, convfail);
+			  if (convfail || !iswspace (wc))
+			    {
+			      just_whitespace_left = 0;
+			      break;
+			    }
+			}
+		    }
+
+		  if (just_whitespace_left)
+		    /* Both lines have nothing but whitespace left.  */
+		    return false;
+		}
+
+	      if (ignore_white_space == IGNORE_TRAILING_SPACE)
+		break;
+	      /* Fall through.  */
+	    case IGNORE_TAB_EXPANSION:
+	      if ((wc1 == L' ' && wc2 == L'\t')
+		  || (wc1 == L'\t' && wc2 == L' '))
+		{
+		  size_t column2 = column;
+
+		  while (1)
+		    {
+		      if (convfail1)
+			{
+			  ++t1;
+			  break;
+			}
+		      else if (wc1 == L' ')
+			column++;
+		      else if (wc1 == L'\t')
+			column += tabsize - column % tabsize;
+		      else
+			{
+			  t1 += mblen1;
+			  break;
+			}
+
+		      t1 += mblen1;
+		      c1 = *t1;
+		      MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
+		    }
+
+		  while (1)
+		    {
+		      if (convfail2)
+			{
+			  ++t2;
+			  break;
+			}
+		      else if (wc2 == L' ')
+			column2++;
+		      else if (wc2 == L'\t')
+			column2 += tabsize - column2 % tabsize;
+		      else
+			{
+			  t2 += mblen2;
+			  break;
+			}
+
+		      t2 += mblen2;
+		      c2 = *t2;
+		      MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
+		    }
+
+		  if (column != column2)
+		    return 1;
+		}
+	      else
+		{
+		  t1 += mblen1;
+		  t2 += mblen2;
+		}
+	      break;
+
+	    case IGNORE_NO_WHITE_SPACE:
+	      t1 += mblen1;
+	      t2 += mblen2;
+	      break;
+	    }
+
+	  /* Lowercase all letters if -i is specified.  */
+	  if (ignore_case)
+	    {
+	      if (!convfail1)
+		wc1 = towlower (wc1);
+	      if (!convfail2)
+		wc2 = towlower (wc2);
+	    }
+
+	  if (convfail1 ^ convfail2)
+	    break;
+	  else if (convfail1 && convfail2 && c1 != c2)
+	    break;
+	  else if (!convfail1 && !convfail2 && wc1 != wc2)
+	    break;
+	}
+      else
+	{
+	  t1_bak = t1;			t2_bak = t2;
+	  wc1_bak = wc1;		wc2_bak = wc2;
+	  state1_bak = state1;		state2_bak = state2;
+	  convfail1_bak = convfail1;	convfail2_bak = convfail2;
+
+	  t1 += mblen1;			t2 += mblen2;
+	}
+      
+      if (!convfail1 && wc1 == L'\n')
+	return 0;
+
+      column += convfail1 ? 1 :
+	(wc1 == L'\t') ? tabsize - column % tabsize : wcwidth (wc1);
+    }
+
+  return 1;
+}
+#endif
 
 /* Find the consecutive changes at the start of the script START.
    Return the last link before the first gap.  */