[[[
Issue #3404: Provide string function to normalize eol-style.

* subversion/subversion/include/svn_string.h
  (svn_string_with_normalized_eol_style): new function
* subversion/libsvn_subr/svn_string.c
  (svn_string_with_normalized_eol_style): new function
]]]

diff -r 98d5ccb8e188 subversion/include/svn_string.h
--- a/subversion/include/svn_string.h	Sun Apr 19 15:42:10 2009 +0200
+++ b/subversion/include/svn_string.h	Sun Apr 19 19:21:41 2009 +0200
@@ -290,6 +290,30 @@
 void
 svn_stringbuf_strip_whitespace(svn_stringbuf_t *str);
 
+/** Return the contents of @a str, with line ends normalized.
+ *
+ * @a str must not be NULL.
+ * @a str contains zero or more line ends of the form \n, \r\n or \r.
+ *
+ * The returned svn_string_t will have the same number of line ends as
+ * @a str but will use only \n. As a consequence, the result will
+ * contain no \r characters.
+ *
+ * We do this in a way that tries to respect the three end of line
+ * style conventions: \r\n, \r, and \n.  The sequence \r\n, common on
+ * Microsoft systems, and the unadorned \r used by the classic Mac OS
+ * are both normalized to Subversion's preferred '\n'.
+ *
+ * Note that there is ambiguity here, which we resolve as follows: We
+ * always map the pair \r\n to a single \n and never to \n\n.
+ *
+ * This function doesn't promise that the returned svn_string_t will always
+ * be distinct from @a str.
+ */
+svn_string_t *
+svn_string_with_normalized_eol_style(const svn_string_t *str, apr_pool_t *pool);
+
+
 /** Return position of last occurrence of @a ch in @a str, or return
  * @a str->len if no occurrence.
  */
diff -r 98d5ccb8e188 subversion/libsvn_subr/svn_string.c
--- a/subversion/libsvn_subr/svn_string.c	Sun Apr 19 15:42:10 2009 +0200
+++ b/subversion/libsvn_subr/svn_string.c	Sun Apr 19 19:21:41 2009 +0200
@@ -20,7 +20,7 @@
 
 
 
-#include <string.h>      /* for memcpy(), memcmp(), strlen() */
+#include <string.h>      /* for memcpy(), memcmp(), strlen(), strchr() */
 #include <apr_lib.h>     /* for apr_isspace() */
 #include <apr_fnmatch.h>
 #include "svn_string.h"  /* loads "svn_types.h" and <apr_pools.h> */
@@ -460,6 +460,55 @@
 }
 
 
+svn_string_t *
+svn_string_with_normalized_eol_style(const svn_string_t *str, apr_pool_t *pool)
+{
+  /* A str whithout \r must be eol-normalized and can be returned given. */
+  if (strchr(str->data, '\r') == NULL)
+    {
+      return str;
+    }
+
+  /* We'll be copying characters from str->data into data. */
+  char *const data = apr_palloc(pool, str->len + 1); /* + space for '\0' */
+
+  const char *r = str->data;  /* Read form str->data through pionter r */
+  char *w = data;             /* Write  to      data through pointer w */
+
+  /* We read str->data though r, while writing to data though w.
+     Initially, r and w advance in lockstep, though w lags as we
+     encounter \r\n pairs. */
+  while (*r)
+    {
+      /* When we read \r, write \n instead. A \n that immediately
+         follows \r is ignored. Other characters are copied
+         unchanged. */
+      if (*r == '\r')
+        {
+          *w = '\n';
+          ++w;
+          ++r;
+          if (*r == '\n')
+            {
+              ++r;
+            }
+        }
+      else
+        {
+          *w = *r;
+          ++w;
+          ++r;
+        }
+      /* Every iteration advances w once and r once or twice. */
+    }
+
+  /* w points one past the last character written to data.
+     This is the correct place for the '\0' terminator. */
+  *w = '\0';
+
+  return create_string(data, w - data, pool);
+}
+
 apr_size_t
 svn_stringbuf_find_char_backward(const svn_stringbuf_t *str, char ch)
 {
