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

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

From: Stefan Sperling <stsp_at_elego.de>
Date: Thu, 8 Jul 2010 13:38:47 +0200

On Thu, Jul 08, 2010 at 09:52:51AM +0100, Julian Foad wrote:
> Hi Stefan. I've just started looking at this. Overall impression is it
> looks like you've done it very well - thank you!
>
> Could you possibly get the readline_detect_eol() API changes out of this
> patch, and make them in a separate patch either before or after this?
> Or if that change belongs in this patch, please explain why in the log
> msg.

It's actually not needed. I think I added it trying to make the
parameter list of svn_io_readline_fn_t match the ones of
svn_stream_readline and svn_stream_readline_detect_eol as closely
as possible.

Looking at it again, adding another parameter to svn_io_readline_fn_t
which says whether the EOL should be detected is much cleaner.
This simplifies implementations, and allows callers of
svn_stream_readline_detect_eol to be lazy about initialising the
eol output parameter.

Updated diff (verified by running just the patch tests which exercise
stream_readline a lot) and updated log message below. Thanks!

[[[
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 that the stream needs to support mark and seek.

* 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 an 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.
  (svn_stream_readline_detect_eol): As previous, and ensure that
   the stream has mark/seek support as it is needed for EOL detection.
  (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): 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,39 @@ 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 the stream. @a baton is the stream's baton.
+ * If @a detect_eol is @c FALSE, @a *eol is used as the expected line
+ * terminator. If @a detect_eol is @c TRUE, the line-terminator is
+ * detected automatically and stored in @a *eol if @a eol is not NULL.
+ * If EOF is reached and the stream does not end with a newline character,
+ * and @a eol is not NULL, @a *eol is set to NULL.
  *
- * 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 @c 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_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ svn_boolean_t detect_eol,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
 
 /** Create a generic stream. @see svn_stream_t. */
 svn_stream_t *
@@ -864,23 +863,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 +1190,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,
@@ -1230,6 +1204,11 @@ svn_stream_readline(svn_stream_t *stream,
  * is returned in @a *eol. If EOF is reached and the stream does not
  * end with a newline character, @a *eol will be NULL.
  *
+ * The @a stream is required to support mark and seek. @see svn_stream_mark.
+ *
+ * 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 +1216,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,405 @@ 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_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ svn_boolean_t detect_eol,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ 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 (detect_eol)
+ {
+ SVN_ERR(scan_eol(baton, read_fn, mark_fn, seek_fn,
+ &eol_str, iterpool));
+ if (eol)
+ *eol = eol_str;
+ if (eol_str == NULL)
+ {
+ /* 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;
+ 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_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ svn_boolean_t detect_eol,
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ 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. */
+ if (detect_eol)
+ SVN_ERR(svn_stream_readline_detect_eol(b->wrapped_stream,
+ &line, eol, eof,
+ result_pool, scratch_pool));
   else
- *buf = svn_stringbuf_create(line, result_pool);
+ SVN_ERR(svn_stream_readline(b->wrapped_stream, &line, *eol, eof,
+ result_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
+ {
+ 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 +667,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 +820,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 +1239,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_stringbuf_t **stringbuf,
                 const char **eol,
- svn_stream_t *stream,
+ svn_boolean_t *eof,
                 svn_boolean_t detect_eol,
- apr_pool_t *pool)
+ svn_read_fn_t read_fn,
+ svn_io_mark_fn_t mark_fn,
+ svn_io_seek_fn_t seek_fn,
+ 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 (detect_eol)
     {
- 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)
+ *eol = eol_str;
+ if (eol_str == NULL)
         {
- 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;
+ }
+ else
+ eol_str = *eol;
 
- /* Read into STR up to and including the next EOL sequence. */
- match = eol_str;
+ /* 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,23 @@ 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 overridden 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;
+
+ return svn_error_return(readline_fn(stream->baton,
+ stringbuf, &eol, eof,
+ FALSE,
+ stream->read_fn,
+ stream->mark_fn,
+ stream->seek_fn,
+ pool, pool));
 }
 
 svn_error_t *
@@ -416,13 +357,36 @@ 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 overridden 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,
+ stringbuf, eol, eof,
+ TRUE,
+ stream->read_fn,
+ stream->mark_fn,
+ stream->seek_fn,
+ 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 +500,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 +532,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 +638,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 +807,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 +886,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 +1176,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 +1292,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 +1376,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 +1469,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 +1507,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)
@@ -532,7 +532,7 @@ read_line(patch_target_t *target,
 
   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 +632,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 +677,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
@@ -928,7 +930,8 @@ reject_hunk(patch_target_t *target, hunk_info_t *h
       svn_pool_clear(iterpool);
 
       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)
@@ -994,7 +997,7 @@ apply_hunk(patch_target_t *target, hunk_info_t *hi
 
       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-08 13:39:53 CEST

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

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.