[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] issue #3555: make svn_stream_readline() a stream method

From: Stefan Sperling <stsp_at_elego.de>
Date: Wed, 7 Jul 2010 20:39:02 +0200

This change was proposed by Julian some time ago, because having
callback types that change the behaviour of svn_stream_readline(),
namely svn_io_line_filter_cb_t and svn_io_line_transformer_cb_t,
was the wrong approach.
See http://subversion.tigris.org/issues/show_bug.cgi?id=3555

The patch below implements Julian's second suggestion from the issue:
"make readline() a virtual method of svn_stream_t and then have the user
provide replacement implementations of readline() for doing the required
filtering."

One caveat is that to remain backwards compatible we cannot require
streams to set a readline method. So svn_stream_readline() falls back
to the default readline implementation if none has been set.

I have taken a shortcut and made svn_stream_readline_detect_eol() (which
is new in 1.7) also provide a readline fallback. This avoids having to
add dummy readline methods to streams elsewhere in our code, such as the
one in fs_fs.c.

svn_stream_readline_detect_eol() now behaves like svn_stream_readline()
if an EOL character is passed in.
I briefly considered deprecating svn_stream_readline() and renaming
svn_stream_readline_detect_eol() to svn_stream_readline2(), but
decided against doing so as it would cause a lot of code churn and
existing callers of svn_stream_readline() won't benefit from the upgrade.

The readline callbacks were initially added to address the needs of svn
patch, in particularly the diff parser. The parser now has two custom readline
implementations. There is some code duplication between implementations.
But the generic readline method becomes a bit simpler again. It's not as
simple as it was in 1.6.x, because we need a way to read lines with arbitrary
EOL characters. The diffstat illustrates nicely how code has shifted around:

 include/svn_io.h | 97 +++-----
 libsvn_client/patch.c | 16 -
 libsvn_diff/parse-diff.c | 452 +++++++++++++++++++++++++++++++++-------
 libsvn_subr/stream.c | 285 ++++++++++++-------------
 tests/libsvn_subr/stream-test.c | 142 ------------
 5 files changed, 572 insertions(+), 420 deletions(-)

The patch passes make check.
I'm posting it here for review by Julian and others who are interested.

Thanks,
Stefan

[[[
Fix issue #3555, 'Remove the "line_filter" and "line_transformer" callbacks
from svn_stream_t'.

Make svn_stream_readline() a virtual method of svn_stream_t,
and provide custom implementations of readline methods in the diff
parsing code (which is the only place where we need this right now).

* subversion/include/svn_io.h
  (svn_io_line_filter_cb_t, svn_io_line_transformer_cb_t,
   svn_stream_set_line_filter_callback,
   svn_stream_set_line_transformer_callback): Remove.
  (svn_io_readline_fn_t, svn_stream_set_readline): Declare.
  (svn_stream_readline): Adjust docstring.
  (svn_stream_readline_detect_eol): Switch to dual-pool paradigm,
   and document the new twisted behaviour of the EOL input/output parameter.

* subversion/libsvn_diff/parse-diff.c
  (original_line_filter, modified_line_filter, remove_leading_char_transformer,
   reverse_diff_transformer): Remove.
  (hunk_text_stream_baton, read_handler_hunk_text, write_handler_hunk_text,
   close_handler_hunk_text, reset_handler_hunk_text, mark_handler_hunk_text,
   seek_handler_hunk_text, scan_eol, readline_handler_hunk_text,
   stream_hunk_text, stream_hunk_text_original, stream_hunk_text_modified):
    New. Implementation of a stream which provides a special readline
    method to read original/modified texts of hunks from a patch file stream.
  (reverse_diff_text_stream_baton, read_handler_reverse_diff_text,
   write_handler_reverse_diff_text, close_handler_reverse_diff_text,
   reset_handler_reverse_diff_text, mark_handler_reverse_diff_text,
   seek_handler_reverse_diff_text, readline_handler_reverse_diff_text,
   stream_reverse_diff_text): New. Implementation of a stream which
    provides a special readline method which reverses unidiff data read
    from the wrapped stream.
  (parse_next_hunk): Track svn_stream_readline_detect_eol() dual-pool change.
   Add a comment explaining why the patch file gets opened multiple
   times (drive-by fix because this confused me at first).
   Instead of installing line-filter/transformation callbacks on
   streams, wrap streams with appropriate wrapper streams.

* subversion/libsvn_subr/stream.c
  (struct svn_stream_t): Replace line_filter_cb and line_transformer_cb
   members with readline_fn member.
  (svn_stream_create): Track changes to svn_stream_t.
  (svn_stream_set_line_filter_callback,
   svn_stream_set_line_transformer_callback,
   line_filter, line_transformer): Remove.
  (scan_eol): Tweak argument list for use within a stream method.
   This function can no longer expect a stream, so pass a baton
   and a set of required stream methods instead.
  (stream_readline): Make this in svn_io_readline_fn_t implementation.
   Remove handling of line filters/transformers.
  (svn_stream_readline): Instead of calling the stream_readline() helper
   directly, call a custom readline implementation if one is set on the
   stream. If no custom implementation is provided, fall back to the
   stream_readline() helper function to preserve compatibility with 1.6.x.
   Assert that the caller passed an EOL character, because the new
   svn_io_readline_fn_t does not strictly require it.
  (svn_stream_readline_detect_eol): As previous, but ensure that
   the stream has mark/seek support instead of asserting an EOL.
  (readline_handler_empty): Custom readline handler for the empty stream.
  (svn_stream_empty, svn_stream_disown, svn_stream_from_aprfile2,
   svn_stream_from_aprfile_range_readonly, svn_stream_compressed,
   svn_stream_checksummed2, svn_stream_checksummed, svn_stream_from_stringbuf,
   svn_stream_from_string): Set a readline method.

* subversion/libsvn_client/patch.c
  (read_line, match_hunk, reject_hunk, apply_hunk): Ensure that EOL_STR
    is always initialised to fulfill new API requirements of
    svn_stream_readline_detect_eol regarding EOLs.
    Also pass two pools to svn_stream_readline_detect_eol().

* subversion/tests/libsvn_subr/stream-test.c
  (line_filter, test_stream_line_filter, line_transformer,
   test_stream_line_transformer,
   test_stream_line_filter_and_transformer): Remove these tests.
  (test_funcs): Remove removed tests.

]]]

Index: subversion/include/svn_io.h
===================================================================
--- subversion/include/svn_io.h (revision 961349)
+++ subversion/include/svn_io.h (working copy)
@@ -780,40 +780,36 @@ typedef svn_error_t *(*svn_io_mark_fn_t)(void *bat
 typedef svn_error_t *(*svn_io_seek_fn_t)(void *baton,
                                          svn_stream_mark_t *mark);
 
-/** Line-filtering callback function for a generic stream.
- * @a baton is the stream's baton.
- * @see svn_stream_t, svn_stream_set_baton() and svn_stream_readline().
+/** Line-reading handler function for a generic stream.
  *
- * @since New in 1.7.
- */
-typedef svn_error_t *(*svn_io_line_filter_cb_t)(svn_boolean_t *filtered,
- const char *line,
- void *baton,
- apr_pool_t *scratch_pool);
-
-/** A callback function, invoked by svn_stream_readline(), which can perform
- * arbitary transformations on the line before it is passed back to the caller
- * of svn_stream_readline().
+ * Allocate @a *stringbuf in @a result_pool, and read into it one line
+ * from @a stream. If @a *eol is not NULL, it is used as the expected
+ * line terminator. If @a eol is NULL, the line-terminator is detected
+ * automatically. If @a *eol is NULL, the line-terminator is detected
+ * automatically and is returned in @a *eol.
  *
- * Returns a transformed stringbuf in @a buf, allocated in @a result_pool.
- * This callback gets invoked on lines which were not filtered by the
- * line-filtering callback function.
+ * @a mark_fn and @a seek_fn are required to be non-NULL if the end-of-line
+ * indicator is to be detected automatically. Else, they may be NULL.
  *
- * Implementations should always at least return an empty stringbuf.
- * It is a fatal error if an implementation returns @a *buf as NULL.
+ * The line-terminator is read from the stream, but is not added to
+ * the end of the stringbuf. Instead, the stringbuf ends with a usual '\\0'.
  *
- * @a baton is the stream's baton.
+ * If @a stream runs out of bytes before encountering a line-terminator,
+ * then set @a *eof to @c TRUE, otherwise set @a *eof to FALSE.
  *
- * @see svn_stream_t, svn_stream_set_baton(), svn_io_line_filter_cb_t and
- * svn_stream_readline().
+ * Temporary allocations will be performed in @a scratch_pool.
  *
- * @since New in 1.7.
- */
-typedef svn_error_t *(*svn_io_line_transformer_cb_t)(svn_stringbuf_t **buf,
- const char *line,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+ * @see svn_stream_readline and svn_stream_readline_detect_eol.
+ * @since New in 1.7. */
+typedef svn_error_t *(*svn_io_readline_fn_t)(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
 
 /** Create a generic stream. @see svn_stream_t. */
 svn_stream_t *
@@ -864,23 +860,11 @@ void
 svn_stream_set_seek(svn_stream_t *stream,
                     svn_io_seek_fn_t seek_fn);
 
-/** Set @a stream's line-filtering callback function to @a line_filter_cb
- *
- * @since New in 1.7.
- */
+/** Set @a stream's readline function to @a readline_fn */
 void
-svn_stream_set_line_filter_callback(svn_stream_t *stream,
- svn_io_line_filter_cb_t line_filter_cb);
+svn_stream_set_readline(svn_stream_t *stream,
+ svn_io_readline_fn_t readline_fn);
 
-/** Set @a streams's line-transforming callback function to
- * @a line_transformer_cb.
- *
- * @since New in 1.7.
- */
-void
-svn_stream_set_line_transformer_callback(
- svn_stream_t *stream,
- svn_io_line_transformer_cb_t line_transformer_cb);
 
 /** Create a stream that is empty for reading and infinite for writing. */
 svn_stream_t *
@@ -1203,19 +1187,6 @@ svn_stream_printf_from_utf8(svn_stream_t *stream,
  *
  * If @a stream runs out of bytes before encountering a line-terminator,
  * then set @a *eof to @c TRUE, otherwise set @a *eof to FALSE.
- *
- * If a line-filter callback function was set on the stream using
- * svn_stream_set_line_filter_callback(), lines will only be returned
- * if they pass the filtering decision of the callback function.
- * If end-of-file is encountered while reading the line and the line
- * is filtered, @a *stringbuf will be empty.
- *
- * If a line-transformer callback function was set on the stream using
- * svn_stream_set_line_transformer_callback(), lines will be returned
- * transformed, in a way determined by the callback.
- *
- * Note that the line-transformer callback gets called after the line-filter
- * callback, not before.
  */
 svn_error_t *
 svn_stream_readline(svn_stream_t *stream,
@@ -1225,11 +1196,14 @@ svn_stream_readline(svn_stream_t *stream,
                     apr_pool_t *pool);
 
 /**
- * Similar to svn_stream_readline(). The line-terminator is detected
- * automatically. If @a eol is not NULL, the detected line-terminator
- * is returned in @a *eol. If EOF is reached and the stream does not
- * end with a newline character, @a *eol will be NULL.
+ * Similar to svn_stream_readline(). If @a *eol is not NULL, it is used
+ * as the expected line terminator. If @a eol is NULL, the line-terminator
+ * is detected automatically. If @a *eol is NULL, the line-terminator is
+ * detected automatically and is returned in @a *eol.
  *
+ * The @a *stringbuf will be allocated in @a result_pool.
+ * @a scratch_pool is used for temporary allocations.
+ *
  * @since New in 1.7.
  */
 svn_error_t *
@@ -1237,7 +1211,8 @@ svn_stream_readline_detect_eol(svn_stream_t *strea
                                svn_stringbuf_t **stringbuf,
                                const char **eol,
                                svn_boolean_t *eof,
- apr_pool_t *pool);
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
 
 /**
  * Read the contents of the readable stream @a from and write them to the
Index: subversion/libsvn_diff/parse-diff.c
===================================================================
--- subversion/libsvn_diff/parse-diff.c (revision 961349)
+++ subversion/libsvn_diff/parse-diff.c (working copy)
@@ -33,6 +33,8 @@
 #include "svn_dirent_uri.h"
 #include "svn_diff.h"
 
+#include "private/svn_eol_private.h"
+
 /* Helper macro for readability */
 #define starts_with(str, start) \
   (strncmp((str), (start), strlen(start)) == 0)
@@ -184,89 +186,398 @@ parse_hunk_header(const char *header, svn_hunk_t *
   return TRUE;
 }
 
-/* A stream line-filter which allows only original text from a hunk,
- * and filters special lines (which start with a backslash). */
+/* Users of the diff parsing API are provided with various streams
+ * to read lines from a hunk. These streams return:
+ *
+ * - the original hunk text (all lines starting with '-' or ' ')
+ * - the modified hunk text (all lines starting with '+' or ' ')
+ * - the plain unidiff text (possibly reversed)
+ *
+ * To achieve this, we wrap the patch file stream with custom streams,
+ * which override the wrapped stream's readline method. */
+
+/* Baton for a stream that reads hunk texts. */
+struct hunk_text_stream_baton {
+ /* The leading unidiff symbol of lines which should be filtered.
+ * Can be '+' or '-', depending on whether we're providing the original
+ * or modified version of the hunk. */
+ char verboten;
+
+ svn_stream_t *wrapped_stream;
+};
+
+/* An implementation of svn_read_fn_t. */
 static svn_error_t *
-original_line_filter(svn_boolean_t *filtered, const char *line, void *baton,
- apr_pool_t *scratch_pool)
+read_handler_hunk_text(void *baton, char *buffer, apr_size_t *len)
 {
- *filtered = (line[0] == '+' || line[0] == '\\');
- return SVN_NO_ERROR;
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_read(b->wrapped_stream, buffer, len);
 }
 
-/* A stream line-filter which allows only modified text from a hunk,
- * and filters special lines (which start with a backslash). */
+/* An implementation of svn_write_fn_t. */
 static svn_error_t *
-modified_line_filter(svn_boolean_t *filtered, const char *line, void *baton,
- apr_pool_t *scratch_pool)
+write_handler_hunk_text(void *baton, const char *buffer, apr_size_t *len)
 {
- *filtered = (line[0] == '-' || line[0] == '\\');
- return SVN_NO_ERROR;
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_write(b->wrapped_stream, buffer, len);
 }
 
-/** line-transformer callback to shave leading diff symbols. */
+/* An implementation of svn_close_fn_t. */
 static svn_error_t *
-remove_leading_char_transformer(svn_stringbuf_t **buf,
- const char *line,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+close_handler_hunk_text(void *baton)
 {
- if (line[0] == '+' || line[0] == '-' || line[0] == ' ')
- *buf = svn_stringbuf_create(line + 1, result_pool);
- else
- *buf = svn_stringbuf_create(line, result_pool);
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_close(b->wrapped_stream);
+}
 
+/* An implementation of svn_io_reset_fn_t. */
+static svn_error_t *
+reset_handler_hunk_text(void *baton)
+{
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_reset(b->wrapped_stream);
+}
+
+/* An implementation of svn_io_mark_fn_t. */
+static svn_error_t *
+mark_handler_hunk_text(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
+{
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_mark(b->wrapped_stream, mark, pool);
+}
+
+/* An implementation of svn_io_seek_fn_t. */
+static svn_error_t *
+seek_handler_hunk_text(void *baton, svn_stream_mark_t *mark)
+{
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_seek(b->wrapped_stream, mark);
+}
+
+/* Invoke the READ_FN function of a stream to scan for an end-of-line
+ * indicator, and return it in *EOL.
+ * Set *EOL to NULL if the stream runs out before an end-of-line indicator
+ * is found. */
+static svn_error_t *
+scan_eol(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ const char **eol,
+ apr_pool_t *pool)
+{
+ const char *eol_str;
+ svn_stream_mark_t *mark;
+
+ SVN_ERR(mark_fn(baton, &mark, pool));
+
+ eol_str = NULL;
+ while (! eol_str)
+ {
+ char buf[512];
+ apr_size_t len;
+
+ len = sizeof(buf);
+ SVN_ERR(read_fn(baton, buf, &len));
+ if (len == 0)
+ break; /* EOF */
+ eol_str = svn_eol__detect_eol(buf, buf + len);
+ }
+
+ SVN_ERR(seek_fn(baton, mark));
+
+ *eol = eol_str;
+
   return SVN_NO_ERROR;
 }
 
-/** line-transformer callback to reverse a diff text. */
+/* An implementation of svn_io_readline_fn_t. */
 static svn_error_t *
-reverse_diff_transformer(svn_stringbuf_t **buf,
- const char *line,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+readline_handler_hunk_text(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
 {
- svn_hunk_t hunk;
+ svn_stringbuf_t *str;
+ apr_pool_t *iterpool;
+ svn_boolean_t filtered;
+ const char *eol_str;
+ struct hunk_text_stream_baton *b = baton;
 
- /* ### Pass the already parsed hunk via the baton?
- * ### Maybe we should really make svn_stream_readline() a proper stream
- * ### method and override it instead of adding special callbacks? */
- if (parse_hunk_header(line, &hunk, "@@", FALSE, scratch_pool))
+ *eof = FALSE;
+
+ iterpool = svn_pool_create(scratch_pool);
+ do
     {
- *buf = svn_stringbuf_createf(result_pool,
- "@@ -%lu,%lu +%lu,%lu @@",
- hunk.modified_start,
- hunk.modified_length,
- hunk.original_start,
- hunk.original_length);
+ apr_size_t numbytes;
+ const char *match;
+ char c;
+
+ svn_pool_clear(iterpool);
+
+ /* Since we're reading one character at a time, let's at least
+ optimize for the 90% case. 90% of the time, we can avoid the
+ stringbuf ever having to realloc() itself if we start it out at
+ 80 chars. */
+ str = svn_stringbuf_create_ensure(80, iterpool);
+
+ if (eol == NULL || *eol == NULL)
+ {
+ SVN_ERR(scan_eol(baton, read_fn, mark_fn, seek_fn,
+ &eol_str, iterpool));
+ if (! eol_str)
+ {
+ /* No newline until EOF, EOL_STR can be anything. */
+ eol_str = APR_EOL_STR;
+ }
+ if (eol)
+ *eol = eol_str;
+ }
+ else
+ eol_str = *eol;
+
+ /* Read into STR up to and including the next EOL sequence. */
+ match = eol_str;
+ numbytes = 1;
+ while (*match)
+ {
+ SVN_ERR(read_fn(baton, &c, &numbytes));
+ if (numbytes != 1)
+ {
+ /* a 'short' read means the stream has run out. */
+ *eof = TRUE;
+ /* We know we don't have a whole EOL sequence, but ensure we
+ * don't chop off any partial EOL sequence that we may have. */
+ match = eol_str;
+ /* Process this short (or empty) line just like any other
+ * except with *EOF set. */
+ break;
+ }
+
+ if (c == *match)
+ match++;
+ else
+ match = eol_str;
+
+ svn_stringbuf_appendbytes(str, &c, 1);
+ }
+
+ svn_stringbuf_chop(str, match - eol_str);
+ filtered = (str->data[0] == b->verboten || str->data[0] == '\\');
     }
- else if (parse_hunk_header(line, &hunk, "##", FALSE, scratch_pool))
+ while (filtered && ! *eof);
+ /* Not destroying the iterpool just yet since we still need STR
+ * which is allocated in it. */
+
+ if (filtered)
     {
- *buf = svn_stringbuf_createf(result_pool,
- "## -%lu,%lu +%lu,%lu ##",
- hunk.modified_start,
- hunk.modified_length,
- hunk.original_start,
- hunk.original_length);
+ /* EOF, return an empty string. */
+ *stringbuf = svn_stringbuf_create_ensure(0, result_pool);
     }
- else if (line[0] == '+')
+ else if (str->data[0] == '+' || str->data[0] == '-' || str->data[0] == ' ')
     {
- *buf = svn_stringbuf_create(line, result_pool);
- (*buf)->data[0] = '-';
+ /* Shave off leading unidiff symbols. */
+ *stringbuf = svn_stringbuf_create(str->data + 1, result_pool);
     }
- else if (line[0] == '-')
+ else
     {
- *buf = svn_stringbuf_create(line, result_pool);
- (*buf)->data[0] = '+';
+ /* Return the line as-is. */
+ *stringbuf = svn_stringbuf_dup(str, result_pool);
     }
+
+ /* Done. RIP iterpool. */
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a stream for reading hunk text from WRAPPED_STREAM.
+ * VERBOTEN is the leading character of lines which should be
+ * filtered by this stream's readline method ('+' or '-').
+ * Allocate the new stream in RESULT_POOL. */
+static svn_stream_t *
+stream_hunk_text(svn_stream_t *wrapped_stream,
+ char verboten,
+ apr_pool_t *result_pool)
+{
+ svn_stream_t *stream;
+ struct hunk_text_stream_baton *baton;
+
+ baton = apr_palloc(result_pool, sizeof(*baton));
+ baton->wrapped_stream = wrapped_stream;
+ baton->verboten = verboten;
+
+ stream = svn_stream_create(baton, result_pool);
+
+ svn_stream_set_read(stream, read_handler_hunk_text);
+ svn_stream_set_write(stream, write_handler_hunk_text);
+ svn_stream_set_close(stream, close_handler_hunk_text);
+ svn_stream_set_reset(stream, reset_handler_hunk_text);
+ svn_stream_set_mark(stream, mark_handler_hunk_text);
+ svn_stream_set_seek(stream, seek_handler_hunk_text);
+ svn_stream_set_readline(stream, readline_handler_hunk_text);
+
+ return stream;
+}
+
+/* Return a stream to read the original text of a hunk from
+ * WRAPPED_STREAM, and allocated in RESULT_POOL.*/
+static svn_stream_t *
+stream_hunk_text_original(svn_stream_t *wrapped_stream,
+ apr_pool_t *result_pool)
+{
+ return stream_hunk_text(wrapped_stream, '+', result_pool);
+}
+
+/* Return a stream to read the modified text of a hunk from
+ * WRAPPED_STREAM, and allocated in RESULT_POOL.*/
+static svn_stream_t *
+stream_hunk_text_modified(svn_stream_t *wrapped_stream,
+ apr_pool_t *result_pool)
+{
+ return stream_hunk_text(wrapped_stream, '-', result_pool);
+}
+
+
+/* Baton for a stream that reads unidiff text reversed. */
+struct reverse_diff_text_stream_baton {
+ svn_stream_t *wrapped_stream;
+};
+
+/* An implementation of svn_read_fn_t. */
+static svn_error_t *
+read_handler_reverse_diff_text(void *baton, char *buffer, apr_size_t *len)
+{
+ struct reverse_diff_text_stream_baton *b = baton;
+ return svn_stream_read(b->wrapped_stream, buffer, len);
+}
+
+/* An implementation of svn_write_fn_t. */
+static svn_error_t *
+write_handler_reverse_diff_text(void *baton, const char *buffer,
+ apr_size_t *len)
+{
+ struct reverse_diff_text_stream_baton *b = baton;
+ return svn_stream_write(b->wrapped_stream, buffer, len);
+}
+
+/* An implementation of svn_close_fn_t. */
+static svn_error_t *
+close_handler_reverse_diff_text(void *baton)
+{
+ struct reverse_diff_text_stream_baton *b = baton;
+ return svn_stream_close(b->wrapped_stream);
+}
+
+/* An implementation of svn_io_reset_fn_t. */
+static svn_error_t *
+reset_handler_reverse_diff_text(void *baton)
+{
+ struct reverse_diff_text_stream_baton *b = baton;
+ return svn_stream_reset(b->wrapped_stream);
+}
+
+/* An implementation of svn_io_mark_fn_t. */
+static svn_error_t *
+mark_handler_reverse_diff_text(void *baton, svn_stream_mark_t **mark,
+ apr_pool_t *pool)
+{
+ struct reverse_diff_text_stream_baton *b = baton;
+ return svn_stream_mark(b->wrapped_stream, mark, pool);
+}
+
+/* An implementation of svn_io_seek_fn_t. */
+static svn_error_t *
+seek_handler_reverse_diff_text(void *baton, svn_stream_mark_t *mark)
+{
+ struct hunk_text_stream_baton *b = baton;
+ return svn_stream_seek(b->wrapped_stream, mark);
+}
+
+/* An implementation of svn_io_readline_fn_t. */
+static svn_error_t *
+readline_handler_reverse_diff_text(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_hunk_t hunk;
+ svn_stringbuf_t *line;
+ struct reverse_diff_text_stream_baton *b = baton;
+
+ /* Read the line and perform necessary transformations to
+ * produce a reversed diff. */
+ SVN_ERR(svn_stream_readline_detect_eol(b->wrapped_stream,
+ &line, eol, eof,
+ result_pool, scratch_pool));
+ if (parse_hunk_header(line->data, &hunk, "@@", FALSE, scratch_pool))
+ {
+ /* Line is a hunk header, reverse it. */
+ *stringbuf = svn_stringbuf_createf(result_pool,
+ "@@ -%lu,%lu +%lu,%lu @@",
+ hunk.modified_start,
+ hunk.modified_length,
+ hunk.original_start,
+ hunk.original_length);
+ }
+ else if (parse_hunk_header(line->data, &hunk, "##", FALSE, scratch_pool))
+ {
+ /* Line is a hunk header, reverse it. */
+ *stringbuf = svn_stringbuf_createf(result_pool,
+ "## -%lu,%lu +%lu,%lu ##",
+ hunk.modified_start,
+ hunk.modified_length,
+ hunk.original_start,
+ hunk.original_length);
+ }
   else
- *buf = svn_stringbuf_create(line, result_pool);
+ {
+ if (line->data[0] == '+')
+ line->data[0] = '-';
+ else if (line->data[0] == '-')
+ line->data[0] = '+';
 
+ *stringbuf = line;
+ }
+
   return SVN_NO_ERROR;
 }
 
+/* Return a stream for reading diff text from WRAPPED_STREAM.
+ * The unidiff will appear reversed when read via the stream's readline method.
+ * Allocate the new stream in RESULT_POOL. */
+static svn_stream_t *
+stream_reverse_diff_text(svn_stream_t *wrapped_stream,
+ apr_pool_t *result_pool)
+{
+ svn_stream_t *stream;
+ struct reverse_diff_text_stream_baton *baton;
+
+ baton = apr_palloc(result_pool, sizeof(*baton));
+ baton->wrapped_stream = wrapped_stream;
+
+ stream = svn_stream_create(baton, result_pool);
+ svn_stream_set_read(stream, read_handler_reverse_diff_text);
+ svn_stream_set_write(stream, write_handler_reverse_diff_text);
+ svn_stream_set_close(stream, close_handler_reverse_diff_text);
+ svn_stream_set_reset(stream, reset_handler_reverse_diff_text);
+ svn_stream_set_mark(stream, mark_handler_reverse_diff_text);
+ svn_stream_set_seek(stream, seek_handler_reverse_diff_text);
+ svn_stream_set_readline(stream, readline_handler_reverse_diff_text);
+
+ return stream;
+}
+
 /* Parse PROP_NAME from HEADER as the part after the INDICATOR line. */
 static svn_error_t *
 parse_prop_name(const char **prop_name, const char *header,
@@ -349,7 +660,7 @@ parse_next_hunk(svn_hunk_t **hunk,
       /* Remember the current line's offset, and read the line. */
       last_line = pos;
       SVN_ERR(svn_stream_readline_detect_eol(stream, &line, NULL, &eof,
- iterpool));
+ iterpool, iterpool));
 
       if (! eof)
         {
@@ -502,38 +813,41 @@ parse_next_hunk(svn_hunk_t **hunk,
       apr_file_t *f;
       apr_int32_t flags = APR_READ | APR_BUFFERED;
 
+ /* For each of the streams created below, we re-open the patch file.
+ * Each stream needs its own file descriptor in order to have
+ * independent seek behaviour. */
+
       /* Create a stream which returns the hunk text itself. */
       SVN_ERR(svn_io_file_open(&f, patch->path, flags, APR_OS_DEFAULT,
                                result_pool));
       diff_text = svn_stream_from_aprfile_range_readonly(f, FALSE,
                                                          start, end,
                                                          result_pool);
+ if (reverse)
+ diff_text = stream_reverse_diff_text(diff_text, result_pool);
 
       /* Create a stream which returns the original hunk text. */
       SVN_ERR(svn_io_file_open(&f, patch->path, flags, APR_OS_DEFAULT,
                                result_pool));
- original_text = svn_stream_from_aprfile_range_readonly(f, FALSE,
- start, end,
- result_pool);
- svn_stream_set_line_filter_callback(original_text, original_line_filter);
- svn_stream_set_line_transformer_callback(original_text,
- remove_leading_char_transformer);
+ original_text = stream_hunk_text_original(
+ svn_stream_from_aprfile_range_readonly(f, FALSE,
+ start, end,
+ result_pool),
+ result_pool);
 
       /* Create a stream which returns the modified hunk text. */
       SVN_ERR(svn_io_file_open(&f, patch->path, flags, APR_OS_DEFAULT,
                                result_pool));
- modified_text = svn_stream_from_aprfile_range_readonly(f, FALSE,
- start, end,
- result_pool);
- svn_stream_set_line_filter_callback(modified_text, modified_line_filter);
- svn_stream_set_line_transformer_callback(modified_text,
- remove_leading_char_transformer);
+ modified_text = stream_hunk_text_modified(
+ svn_stream_from_aprfile_range_readonly(f, FALSE,
+ start, end,
+ result_pool),
+ result_pool);
+
       /* Set the hunk's texts. */
       (*hunk)->diff_text = diff_text;
       if (reverse)
         {
- svn_stream_set_line_transformer_callback(diff_text,
- reverse_diff_transformer);
           (*hunk)->original_text = modified_text;
           (*hunk)->modified_text = original_text;
         }
@@ -918,7 +1232,7 @@ svn_diff_parse_next_patch(svn_patch_t **patch,
       /* Remember the current line's offset, and read the line. */
       last_line = pos;
       SVN_ERR(svn_stream_readline_detect_eol(stream, &line, NULL, &eof,
- iterpool));
+ iterpool, iterpool));
 
       if (! eof)
         {
Index: subversion/libsvn_subr/stream.c
===================================================================
--- subversion/libsvn_subr/stream.c (revision 961349)
+++ subversion/libsvn_subr/stream.c (working copy)
@@ -53,8 +53,7 @@ struct svn_stream_t {
   svn_io_reset_fn_t reset_fn;
   svn_io_mark_fn_t mark_fn;
   svn_io_seek_fn_t seek_fn;
- svn_io_line_filter_cb_t line_filter_cb;
- svn_io_line_transformer_cb_t line_transformer_cb;
+ svn_io_readline_fn_t readline_fn;
 };
 
 
@@ -73,8 +72,7 @@ svn_stream_create(void *baton, apr_pool_t *pool)
   stream->reset_fn = NULL;
   stream->mark_fn = NULL;
   stream->seek_fn = NULL;
- stream->line_filter_cb = NULL;
- stream->line_transformer_cb = NULL;
+ stream->readline_fn = NULL;
   return stream;
 }
 
@@ -124,20 +122,11 @@ svn_stream_set_seek(svn_stream_t *stream, svn_io_s
 }
 
 void
-svn_stream_set_line_filter_callback(svn_stream_t *stream,
- svn_io_line_filter_cb_t line_filter_cb)
+svn_stream_set_readline(svn_stream_t *stream, svn_io_readline_fn_t readline_fn)
 {
- stream->line_filter_cb = line_filter_cb;
+ stream->readline_fn = readline_fn;
 }
 
-void
-svn_stream_set_line_transformer_callback(
- svn_stream_t *stream,
- svn_io_line_transformer_cb_t line_transformer_cb)
-{
- stream->line_transformer_cb = line_transformer_cb;
-}
-
 svn_error_t *
 svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
 {
@@ -233,55 +222,23 @@ svn_stream_printf_from_utf8(svn_stream_t *stream,
   return svn_stream_write(stream, translated, &len);
 }
 
-/* If a line filter callback was set on STREAM, invoke it on LINE,
- * and indicate in *FILTERED whether the line should be filtered.
- * If no line filter callback was set on STREAM, just set *FILTERED to FALSE.
- */
-static svn_error_t *
-line_filter(svn_stream_t *stream, svn_boolean_t *filtered, const char *line,
- apr_pool_t *pool)
-{
- apr_pool_t *scratch_pool;
-
- if (! stream->line_filter_cb)
- {
- *filtered = FALSE;
- return SVN_NO_ERROR;
- }
-
- scratch_pool = svn_pool_create(pool);
- SVN_ERR(stream->line_filter_cb(filtered, line, stream->baton, scratch_pool));
- svn_pool_destroy(scratch_pool);
- return SVN_NO_ERROR;
-}
-
-/* Run the line transformer callback of STREAM with LINE as input,
- * and expect the transformation result to be returned in BUF,
- * allocated in POOL. */
-static svn_error_t *
-line_transformer(svn_stream_t *stream, svn_stringbuf_t **buf,
- const char *line, apr_pool_t *pool, apr_pool_t *scratch_pool)
-{
- *buf = NULL; /* so we can assert that the callback has set it non-null */
- SVN_ERR(stream->line_transformer_cb(buf, line, stream->baton,
- pool, scratch_pool));
-
- /* Die if the line transformer didn't provide any output. */
- SVN_ERR_ASSERT(*buf);
-
- return SVN_NO_ERROR;
-}
-
-/* Scan STREAM for an end-of-line indicatior, and return it in *EOL.
+/* Invoke the READ_FN function of a stream to scan for an end-of-line
+ * indicatior, and return it in *EOL.
+ * Use MARK_FN and SEEK_FN to seek in the stream.
  * Set *EOL to NULL if the stream runs out before an end-of-line indicator
  * is found. */
 static svn_error_t *
-scan_eol(const char **eol, svn_stream_t *stream, apr_pool_t *pool)
+scan_eol(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ const char **eol,
+ apr_pool_t *pool)
 {
   const char *eol_str;
   svn_stream_mark_t *mark;
 
- SVN_ERR(svn_stream_mark(stream, &mark, pool));
+ SVN_ERR(mark_fn(baton, &mark, pool));
 
   eol_str = NULL;
   while (! eol_str)
@@ -290,113 +247,82 @@ static svn_error_t *
       apr_size_t len;
 
       len = sizeof(buf);
- SVN_ERR(svn_stream_read(stream, buf, &len));
+ SVN_ERR(read_fn(baton, buf, &len));
       if (len == 0)
           break; /* EOF */
       eol_str = svn_eol__detect_eol(buf, buf + len);
     }
 
- SVN_ERR(svn_stream_seek(stream, mark));
+ SVN_ERR(seek_fn(baton, mark));
 
   *eol = eol_str;
 
   return SVN_NO_ERROR;
 }
 
-/* Guts of svn_stream_readline() and svn_stream_readline_detect_eol().
- * Returns the line read from STREAM in *STRINGBUF, and indicates
- * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator
- * is detected automatically and returned in *EOL.
- * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
- * indicator. STRINGBUF is allocated in POOL. */
+/* An implementation of svn_io_readline_fn_t */
 static svn_error_t *
-stream_readline(svn_stringbuf_t **stringbuf,
- svn_boolean_t *eof,
+stream_readline(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ svn_stringbuf_t **stringbuf,
                 const char **eol,
- svn_stream_t *stream,
- svn_boolean_t detect_eol,
- apr_pool_t *pool)
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
 {
- svn_stringbuf_t *str;
- apr_pool_t *iterpool;
- svn_boolean_t filtered;
+ apr_size_t numbytes;
+ const char *match;
+ char c;
   const char *eol_str;
+ /* Since we're reading one character at a time, let's at least
+ optimize for the 90% case. 90% of the time, we can avoid the
+ stringbuf ever having to realloc() itself if we start it out at
+ 80 chars. */
+ svn_stringbuf_t *str = svn_stringbuf_create_ensure(80, scratch_pool);
 
- *eof = FALSE;
-
- iterpool = svn_pool_create(pool);
- do
+ if (eol == NULL || *eol == NULL)
     {
- apr_size_t numbytes;
- const char *match;
- char c;
-
- svn_pool_clear(iterpool);
-
- /* Since we're reading one character at a time, let's at least
- optimize for the 90% case. 90% of the time, we can avoid the
- stringbuf ever having to realloc() itself if we start it out at
- 80 chars. */
- str = svn_stringbuf_create_ensure(80, iterpool);
-
- if (detect_eol)
+ SVN_ERR(scan_eol(baton, read_fn, mark_fn, seek_fn,
+ &eol_str, scratch_pool));
+ if (! eol_str)
         {
- SVN_ERR(scan_eol(&eol_str, stream, iterpool));
- if (eol)
- *eol = eol_str;
- if (! eol_str)
- {
- /* No newline until EOF, EOL_STR can be anything. */
- eol_str = APR_EOL_STR;
- }
+ /* No newline until EOF, EOL_STR can be anything. */
+ eol_str = APR_EOL_STR;
         }
- else
- eol_str = *eol;
 
- /* Read into STR up to and including the next EOL sequence. */
- match = eol_str;
+ if (eol)
+ *eol = eol_str;
+ }
+ else
+ eol_str = *eol;
+
+ /* Read into STR up to and including the next EOL sequence. */
+ match = eol_str;
+ while (*match)
+ {
       numbytes = 1;
- while (*match)
+ SVN_ERR(read_fn(baton, &c, &numbytes));
+ if (numbytes != 1)
         {
- SVN_ERR(svn_stream_read(stream, &c, &numbytes));
- if (numbytes != 1)
- {
- /* a 'short' read means the stream has run out. */
- *eof = TRUE;
- /* We know we don't have a whole EOL sequence, but ensure we
- * don't chop off any partial EOL sequence that we may have. */
- match = eol_str;
- /* Process this short (or empty) line just like any other
- * except with *EOF set. */
- break;
- }
-
- if (c == *match)
- match++;
- else
- match = eol_str;
-
- svn_stringbuf_appendbytes(str, &c, 1);
+ /* a 'short' read means the stream has run out. */
+ *eof = TRUE;
+ *stringbuf = svn_stringbuf_dup(str, result_pool);
+ return SVN_NO_ERROR;
         }
 
- svn_stringbuf_chop(str, match - eol_str);
+ if (c == *match)
+ match++;
+ else
+ match = eol_str;
 
- SVN_ERR(line_filter(stream, &filtered, str->data, iterpool));
+ svn_stringbuf_appendbytes(str, &c, 1);
     }
- while (filtered && ! *eof);
- /* Not destroying the iterpool just yet since we still need STR
- * which is allocated in it. */
 
- if (filtered)
- *stringbuf = svn_stringbuf_create_ensure(0, pool);
- else if (stream->line_transformer_cb)
- SVN_ERR(line_transformer(stream, stringbuf, str->data, pool, iterpool));
- else
- *stringbuf = svn_stringbuf_dup(str, pool);
-
- /* Done. RIP iterpool. */
- svn_pool_destroy(iterpool);
-
+ *eof = FALSE;
+ svn_stringbuf_chop(str, match - eol_str);
+ *stringbuf = svn_stringbuf_dup(str, result_pool);
   return SVN_NO_ERROR;
 }
 
@@ -407,8 +333,25 @@ svn_stream_readline(svn_stream_t *stream,
                     svn_boolean_t *eof,
                     apr_pool_t *pool)
 {
- return svn_error_return(stream_readline(stringbuf, eof, &eol, stream,
- FALSE, pool));
+ svn_io_readline_fn_t readline_fn;
+
+ /* Provide a default readline implementation if the stream
+ * hasn't overriden it. This is needed for backwards compat
+ * to 1.6.x and earlier. */
+ if (stream->readline_fn)
+ readline_fn = stream->readline_fn;
+ else
+ readline_fn = stream_readline;
+
+ /* Caller must provide an EOL character. */
+ SVN_ERR_ASSERT(eol && *eol);
+
+ return svn_error_return(readline_fn(stream->baton,
+ stream->read_fn,
+ stream->mark_fn,
+ stream->seek_fn,
+ stringbuf, &eol, eof,
+ pool, pool));
 }
 
 svn_error_t *
@@ -416,13 +359,35 @@ svn_stream_readline_detect_eol(svn_stream_t *strea
                                svn_stringbuf_t **stringbuf,
                                const char **eol,
                                svn_boolean_t *eof,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
 {
- return svn_error_return(stream_readline(stringbuf, eof, eol, stream,
- TRUE, pool));
-}
+ svn_io_readline_fn_t readline_fn;
 
+ /* Provide a default readline implementation if the stream
+ * hasn't overriden it. This is not needed for backwards compat
+ * to 1.6.x and earlier (this function is new in 1.7), but it
+ * is nice anyway because it saves us from adding dummy readline
+ * methods to custom streams sprinkled throughout the code. */
+ if (stream->readline_fn)
+ readline_fn = stream->readline_fn;
+ else
+ readline_fn = stream_readline;
 
+ /* EOL-detection requires mark/seek support. */
+ if (stream->mark_fn == NULL)
+ return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
+ if (stream->seek_fn == NULL)
+ return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
+
+ return svn_error_return(readline_fn(stream->baton,
+ stream->read_fn,
+ stream->mark_fn,
+ stream->seek_fn,
+ stringbuf, eol, eof,
+ result_pool, scratch_pool));
+}
+
 svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
                               svn_cancel_func_t cancel_func,
                               void *cancel_baton,
@@ -536,7 +501,27 @@ seek_handler_empty(void *baton, svn_stream_mark_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+readline_handler_empty(void *baton,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ *stringbuf = svn_stringbuf_create_ensure(0, result_pool);
 
+ if (eol && *eol == NULL)
+ *eol = APR_EOL_STR;
+
+ *eof = TRUE;
+
+ return SVN_NO_ERROR;
+}
+
 svn_stream_t *
 svn_stream_empty(apr_pool_t *pool)
 {
@@ -548,6 +533,8 @@ svn_stream_empty(apr_pool_t *pool)
   svn_stream_set_reset(stream, reset_handler_empty);
   svn_stream_set_mark(stream, mark_handler_empty);
   svn_stream_set_seek(stream, seek_handler_empty);
+ svn_stream_set_readline(stream, readline_handler_empty);
+
   return stream;
 }
 
@@ -652,6 +639,7 @@ svn_stream_disown(svn_stream_t *stream, apr_pool_t
   svn_stream_set_reset(s, reset_handler_disown);
   svn_stream_set_mark(s, mark_handler_disown);
   svn_stream_set_seek(s, seek_handler_disown);
+ svn_stream_set_readline(s, stream_readline);
 
   return s;
 }
@@ -820,6 +808,7 @@ svn_stream_from_aprfile2(apr_file_t *file,
   svn_stream_set_reset(stream, reset_handler_apr);
   svn_stream_set_mark(stream, mark_handler_apr);
   svn_stream_set_seek(stream, seek_handler_apr);
+ svn_stream_set_readline(stream, stream_readline);
 
   if (! disown)
     svn_stream_set_close(stream, close_handler_apr);
@@ -898,6 +887,7 @@ svn_stream_from_aprfile_range_readonly(apr_file_t
   svn_stream_set_reset(stream, reset_handler_apr);
   svn_stream_set_mark(stream, mark_handler_apr);
   svn_stream_set_seek(stream, seek_handler_apr);
+ svn_stream_set_readline(stream, stream_readline);
 
   if (! disown)
     svn_stream_set_close(stream, close_handler_apr);
@@ -1187,6 +1177,7 @@ svn_stream_compressed(svn_stream_t *stream, apr_po
   svn_stream_set_read(zstream, read_handler_gz);
   svn_stream_set_write(zstream, write_handler_gz);
   svn_stream_set_close(zstream, close_handler_gz);
+ svn_stream_set_readline(zstream, stream_readline);
 
   return zstream;
 }
@@ -1302,6 +1293,8 @@ svn_stream_checksummed2(svn_stream_t *stream,
   svn_stream_set_read(s, read_handler_checksum);
   svn_stream_set_write(s, write_handler_checksum);
   svn_stream_set_close(s, close_handler_checksum);
+ svn_stream_set_readline(s, stream_readline);
+
   return s;
 }
 
@@ -1384,6 +1377,8 @@ svn_stream_checksummed(svn_stream_t *stream,
   svn_stream_set_read(s, read_handler_md5);
   svn_stream_set_write(s, write_handler_md5);
   svn_stream_set_close(s, close_handler_md5);
+ svn_stream_set_readline(s, stream_readline);
+
   return s;
 }
 
@@ -1475,6 +1470,8 @@ svn_stream_from_stringbuf(svn_stringbuf_t *str,
   svn_stream_set_reset(stream, reset_handler_stringbuf);
   svn_stream_set_mark(stream, mark_handler_stringbuf);
   svn_stream_set_seek(stream, seek_handler_stringbuf);
+ svn_stream_set_readline(stream, stream_readline);
+
   return stream;
 }
 
@@ -1511,6 +1508,8 @@ svn_stream_from_string(const svn_string_t *str,
   baton->amt_read = 0;
   stream = svn_stream_create(baton, pool);
   svn_stream_set_read(stream, read_handler_string);
+ svn_stream_set_readline(stream, stream_readline);
+
   return stream;
 }
 
Index: subversion/libsvn_client/patch.c
===================================================================
--- subversion/libsvn_client/patch.c (revision 961349)
+++ subversion/libsvn_client/patch.c (working copy)
@@ -530,9 +530,10 @@ read_line(patch_target_t *target,
       APR_ARRAY_PUSH(target->lines, svn_stream_mark_t *) = mark;
     }
 
+ eol_str = NULL;
   SVN_ERR(svn_stream_readline_detect_eol(target->stream, &line_raw,
                                          &eol_str, &target->eof,
- scratch_pool));
+ scratch_pool, scratch_pool));
   if (target->eol_style == svn_subst_eol_style_none)
     target->eol_str = eol_str;
 
@@ -632,7 +633,8 @@ match_hunk(svn_boolean_t *matched, patch_target_t
 
       SVN_ERR(svn_stream_readline_detect_eol(hunk->original_text,
                                              &hunk_line, NULL,
- &hunk_eof, iterpool));
+ &hunk_eof,
+ iterpool, iterpool));
       /* Contract keywords, if any, before matching. */
       SVN_ERR(svn_subst_translate_cstring2(hunk_line->data,
                                            &hunk_line_translated,
@@ -676,7 +678,8 @@ match_hunk(svn_boolean_t *matched, patch_target_t
       /* If the target has no newline at end-of-file, we get an EOF
        * indication for the target earlier than we do get it for the hunk. */
       SVN_ERR(svn_stream_readline_detect_eol(hunk->original_text, &hunk_line,
- NULL, &hunk_eof, iterpool));
+ NULL, &hunk_eof,
+ iterpool, iterpool));
       if (hunk_line->len == 0 && hunk_eof)
         *matched = lines_matched;
       else
@@ -927,8 +930,10 @@ reject_hunk(patch_target_t *target, hunk_info_t *h
 
       svn_pool_clear(iterpool);
 
+ eol_str = NULL;
       SVN_ERR(svn_stream_readline_detect_eol(hi->hunk->diff_text, &hunk_line,
- &eol_str, &eof, iterpool));
+ &eol_str, &eof, iterpool,
+ iterpool));
       if (! eof)
         {
           if (hunk_line->len >= 1)
@@ -992,9 +997,10 @@ apply_hunk(patch_target_t *target, hunk_info_t *hi
 
       svn_pool_clear(iterpool);
 
+ eol_str = NULL;
       SVN_ERR(svn_stream_readline_detect_eol(hi->hunk->modified_text,
                                              &hunk_line, &eol_str,
- &eof, iterpool));
+ &eof, iterpool, iterpool));
       lines_read++;
       if (! eof && lines_read > hi->fuzz &&
           lines_read <= hi->hunk->modified_length - hi->fuzz)
Index: subversion/tests/libsvn_subr/stream-test.c
===================================================================
--- subversion/tests/libsvn_subr/stream-test.c (revision 961349)
+++ subversion/tests/libsvn_subr/stream-test.c (working copy)
@@ -307,143 +307,7 @@ test_stream_range(apr_pool_t *pool)
     return SVN_NO_ERROR;
 }
 
-/* An implementation of svn_io_line_filter_cb_t */
 static svn_error_t *
-line_filter(svn_boolean_t *filtered, const char *line, void *baton,
- apr_pool_t *scratch_pool)
-{
- *filtered = strchr(line, '!') != NULL;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-test_stream_line_filter(apr_pool_t *pool)
-{
- static const char *lines[4] = {"Not filtered.", "Filtered!",
- "Not filtered either.", "End of the lines!"};
- svn_string_t *string;
- svn_stream_t *stream;
- svn_stringbuf_t *line;
- svn_boolean_t eof;
-
- string = svn_string_createf(pool, "%s\n%s\n%s\n%s", lines[0], lines[1],
- lines[2], lines[3]);
- stream = svn_stream_from_string(string, pool);
-
- svn_stream_set_line_filter_callback(stream, line_filter);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, lines[0]);
- /* line[1] should be filtered */
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, lines[2]);
-
- /* The last line should also be filtered, and the resulting
- * stringbuf should be empty. */
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_ASSERT(eof && svn_stringbuf_isempty(line));
-
- return SVN_NO_ERROR;
-}
-
-/* An implementation of svn_io_line_transformer_cb_t */
-static svn_error_t *
-line_transformer(svn_stringbuf_t **buf, const char *line, void *baton,
- apr_pool_t *result_pool, apr_pool_t *scratch_pool)
-{
- int i, len = strlen(line);
- char *temp = apr_palloc(scratch_pool, len + 1 );
-
- for (i = 0; i < len; i++)
- {
- temp[i] = line[len - 1 - i];
- }
-
- temp[len] = '\0';
-
- *buf = svn_stringbuf_create(temp, result_pool);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-test_stream_line_transformer(apr_pool_t *pool)
-{
- static const char *lines[4] = {"gamma", "",
- "iota", "!"};
-
- static const char *inv_lines[4] = {"ammag", "",
- "atoi", "!"};
- svn_string_t *string;
- svn_stream_t *stream;
- svn_stringbuf_t *line;
- svn_boolean_t eof;
-
- string = svn_string_createf(pool, "%s\n%s\n%s\n%s", lines[0], lines[1],
- lines[2], lines[3]);
-
- stream = svn_stream_from_string(string, pool);
-
- svn_stream_set_line_transformer_callback(stream, line_transformer);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[0]);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[1]);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[2]);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[3]);
-
- /* We should have reached eof and the stringbuf should be emtpy. */
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_ASSERT(eof && svn_stringbuf_isempty(line));
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-test_stream_line_filter_and_transformer(apr_pool_t *pool)
-{
- static const char *lines[4] = {"!gamma", "",
- "iota", "!"};
-
- static const char *inv_lines[4] = {"ammag", "",
- "atoi", "!"};
- svn_string_t *string;
- svn_stream_t *stream;
- svn_stringbuf_t *line;
- svn_boolean_t eof;
-
- string = svn_string_createf(pool, "%s\n%s\n%s\n%s", lines[0], lines[1],
- lines[2], lines[3]);
-
- stream = svn_stream_from_string(string, pool);
-
- svn_stream_set_line_filter_callback(stream, line_filter);
-
- svn_stream_set_line_transformer_callback(stream, line_transformer);
-
- /* Line one should be filtered. */
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[1]);
-
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_STRING_ASSERT(line->data, inv_lines[2]);
-
- /* The last line should also be filtered, and the resulting
- * stringbuf should be empty. */
- svn_stream_readline(stream, &line, "\n", &eof, pool);
- SVN_TEST_ASSERT(eof && svn_stringbuf_isempty(line));
-
- return SVN_NO_ERROR;
-
-}
-
-static svn_error_t *
 test_stream_tee(apr_pool_t *pool)
 {
   svn_stringbuf_t *test_bytes = generate_test_bytes(100, pool);
@@ -647,12 +511,6 @@ struct svn_test_descriptor_t test_funcs[] =
                    "test compressed streams"),
     SVN_TEST_PASS2(test_stream_range,
                    "test streams reading from range of file"),
- SVN_TEST_PASS2(test_stream_line_filter,
- "test stream line filtering"),
- SVN_TEST_PASS2(test_stream_line_transformer,
- "test stream line transforming"),
- SVN_TEST_PASS2(test_stream_line_filter_and_transformer,
- "test stream line filtering and transforming"),
     SVN_TEST_PASS2(test_stream_tee,
                    "test 'tee' streams"),
     SVN_TEST_PASS2(test_stream_seek_file,
Received on 2010-07-07 20:40:14 CEST

This is an archived mail posted to the Subversion Dev mailing list.