[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: Julian Foad <julian.foad_at_wandisco.com>
Date: Thu, 08 Jul 2010 09:52:51 +0100

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.

Thanks.
- Julian

On Wed, 2010-07-07 at 20:39 +0200, Stefan Sperling wrote:
> 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-08 10:54:19 CEST

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