Author: stsp
Date: Thu Apr 22 13:49:14 2010
New Revision: 936844
URL: http://svn.apache.org/viewvc?rev=936844&view=rev
Log:
Hopefully fix issue #3616, "Mark/seek in svn_subst_stream_translated() -
needs to restore translation state"
"Hopefully" because an apparent bug in the translation stream prevents
a new test which attempts to verify the fix from passing.
* subversion/libsvn_subr/subst.c
(keywords_hash_deep_copy, dup_translation_baton): New helper functions.
(mark_translated_t): New type.
(translated_stream_mark, translated_stream_seek): Save translation state
upon mark, and restore it upon seek.
(svn_subst_stream_translated): Drive-by comment fix, and call the new
keywords_hash_deep_copy() helper which is based on code which used to
live here.
* subversion/tests/libsvn_subr/stream-test.c
(): Include svn_subst.h.
(test_funcs): Add new test.
(test_stream_seek_translated): New test. Currently XFail because of
aforementioned bug, which was discussed on IRC as follows:
<stsp> svn_subst_stream_translated() does not expand keywords if
they're not read in their entirety by the caller
<stsp> is that a bug?
<stsp> i would expect that, if I have the input containing a keyword
such as One$MyKeyword$Two, which expands to "my keyword expanded",
and I read up to the position of the character K, the result would
be "Onemy"
<stsp> but what is actually returned from svn_stream_read is One$My
<stsp> that makes setting a mark within a keyword rather pointless
<julianf> stsp: ew, that's an ugly bug in stream_translated.
<julianf> That's just waiting to blow up.
<stsp> julianf, yeah I think it should read ahead, expand the keyword,
and return part of the expansion
<julianf> +1
Review by: julianfoad
philip
Modified:
subversion/trunk/subversion/libsvn_subr/subst.c
subversion/trunk/subversion/tests/libsvn_subr/stream-test.c
Modified: subversion/trunk/subversion/libsvn_subr/subst.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/subst.c?rev=936844&r1=936843&r2=936844&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/subst.c (original)
+++ subversion/trunk/subversion/libsvn_subr/subst.c Thu Apr 22 13:49:14 2010
@@ -839,6 +839,43 @@ create_translation_baton(const char *eol
return b;
}
+/* Create a deep copy of the KEYWORDS hash, allocated in RESULT_POOL,
+ * and return a pointer to it. */
+static apr_hash_t *
+keywords_hash_deep_copy(const apr_hash_t *keywords, apr_pool_t *result_pool)
+{
+ apr_hash_t *copy = apr_hash_make(result_pool);
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(result_pool, keywords);
+ hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+ apr_hash_set(copy, apr_pstrdup(result_pool, key),
+ APR_HASH_KEY_STRING,
+ svn_string_dup(val, result_pool));
+ }
+
+ return copy;
+}
+
+/* Create a deep copy of TRANSLATION_BATON, allocated in RESULT_POOL,
+ * and return a pointer to it. */
+static struct translation_baton *
+dup_translation_baton(const struct translation_baton *translation_baton,
+ apr_pool_t *result_pool)
+{
+ struct translation_baton *b = apr_palloc(result_pool, sizeof(*b));
+
+ *b = *translation_baton;
+ b->keywords = keywords_hash_deep_copy(translation_baton->keywords,
+ result_pool);
+ return b;
+}
+
/* Translate eols and keywords of a 'chunk' of characters BUF of size BUFLEN
* according to the settings and state stored in baton B.
*
@@ -1164,13 +1201,40 @@ translated_stream_reset(void *baton)
return svn_error_return(err);
}
+/* svn_stream_mark_t for translation streams. */
+typedef struct
+{
+ /* Saved translation state. */
+ struct translated_stream_baton *saved_baton;
+
+ /* Mark set on the underlying stream. */
+ svn_stream_mark_t *mark;
+} mark_translated_t;
+
/* Implements svn_io_mark_fn_t. */
static svn_error_t *
translated_stream_mark(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
{
+ mark_translated_t *mark_translated;
struct translated_stream_baton *b = baton;
+ struct translated_stream_baton *sb;
+
+ mark_translated = apr_palloc(pool, sizeof(*mark_translated));
+ SVN_ERR(svn_stream_mark(b->stream, &mark_translated->mark, pool));
+
+ /* Save translation state. */
+ sb = apr_pcalloc(pool, sizeof(*sb));
+ sb->in_baton = dup_translation_baton(b->in_baton, pool);
+ sb->out_baton = dup_translation_baton(b->out_baton, pool);
+ sb->written = b->written;
+ sb->readbuf = svn_stringbuf_dup(b->readbuf, pool);
+ sb->readbuf_off = b->readbuf_off;
+ sb->buf = apr_pmemdup(pool, b->buf, SVN__STREAM_CHUNK_SIZE + 1);
- return svn_error_return(svn_stream_mark(b->stream, mark, pool));
+ mark_translated->saved_baton = sb;
+ *mark = (svn_stream_mark_t *)mark_translated;
+
+ return SVN_NO_ERROR;
}
/* Implements svn_io_seek_fn_t. */
@@ -1178,30 +1242,25 @@ static svn_error_t *
translated_stream_seek(void *baton, svn_stream_mark_t *mark)
{
struct translated_stream_baton *b = baton;
- svn_error_t *err;
+ struct translated_stream_baton *sb;
+ mark_translated_t *mark_translated = (mark_translated_t *)mark;
/* Flush output buffer if necessary. */
if (b->written)
SVN_ERR(translate_chunk(b->stream, b->out_baton, NULL, 0, b->iterpool));
- err = svn_stream_seek(b->stream, mark);
- if (err == NULL)
- {
- /* Force into boring state. */
- b->in_baton->newline_off = 0;
- b->in_baton->keyword_off = 0;
- b->out_baton->newline_off = 0;
- b->out_baton->keyword_off = 0;
-
- /* Output buffer has been flushed. */
- b->written = FALSE;
+ SVN_ERR(svn_stream_seek(b->stream, mark_translated->mark));
- /* Reset read buffer. */
- svn_stringbuf_setempty(b->readbuf);
- b->readbuf_off = 0;
- }
+ /* Restore translation state. */
+ sb = mark_translated->saved_baton;
+ b->in_baton = dup_translation_baton(sb->in_baton, b->pool);
+ b->out_baton = dup_translation_baton(sb->out_baton, b->pool);
+ b->written = sb->written;
+ b->readbuf = svn_stringbuf_dup(sb->readbuf, b->pool);
+ b->readbuf_off = sb->readbuf_off;
+ b->buf = apr_pmemdup(b->pool, sb->buf, SVN__STREAM_CHUNK_SIZE + 1);
- return svn_error_return(err);
+ return SVN_NO_ERROR;
}
svn_error_t *
@@ -1255,7 +1314,7 @@ svn_subst_stream_translated(svn_stream_t
= apr_palloc(baton_pool, sizeof(*baton));
svn_stream_t *s = svn_stream_create(baton, baton_pool);
- /* Make sure EOL_STR and KEYWORDS are allocated in POOL, as
+ /* Make sure EOL_STR and KEYWORDS are allocated in BATON_POOL, as
required by create_translation_baton() */
if (eol_str)
eol_str = apr_pstrdup(baton_pool, eol_str);
@@ -1265,22 +1324,8 @@ svn_subst_stream_translated(svn_stream_t
keywords = NULL;
else
{
- /* deep copy the hash to make sure it's allocated in POOL */
- apr_hash_t *copy = apr_hash_make(baton_pool);
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(pool, keywords);
- hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- apr_hash_this(hi, &key, NULL, &val);
- apr_hash_set(copy, apr_pstrdup(baton_pool, key),
- APR_HASH_KEY_STRING,
- svn_string_dup(val, baton_pool));
- }
-
- keywords = copy;
+ /* deep copy the hash to make sure it's allocated in BATON_POOL */
+ keywords = keywords_hash_deep_copy(keywords, baton_pool);
}
}
Modified: subversion/trunk/subversion/tests/libsvn_subr/stream-test.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/stream-test.c?rev=936844&r1=936843&r2=936844&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/stream-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/stream-test.c Thu Apr 22
13:49:14 2010
@@ -24,6 +24,7 @@
#include <stdio.h>
#include "svn_pools.h"
#include "svn_io.h"
+#include "svn_subst.h"
#include <apr_general.h>
#include "../svn_test.h"
@@ -554,6 +555,86 @@ test_stream_seek_stringbuf(apr_pool_t *p
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_stream_seek_translated(apr_pool_t *pool)
+{
+ svn_stream_t *stream, *translated_stream;
+ svn_stringbuf_t *stringbuf;
+ char buf[23];
+ apr_size_t len;
+ svn_stream_mark_t *mark, *mark2;
+ apr_hash_t *keywords;
+ svn_string_t *keyword_val;
+
+ keywords = apr_hash_make(pool);
+ keyword_val = svn_string_create("my key word was expanded", pool);
+ apr_hash_set(keywords, "MyKeyword", APR_HASH_KEY_STRING, keyword_val);
+ stringbuf = svn_stringbuf_create("One$MyKeyword$Two", pool);
+ stream = svn_stream_from_stringbuf(stringbuf, pool);
+ translated_stream = svn_subst_stream_translated(stream, APR_EOL_STR,
+ FALSE, keywords, TRUE, pool);
+ len = 3;
+ SVN_ERR(svn_stream_read(translated_stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "One") == 0);
+
+ /* Seek from outside of keyword to inside of keyword. */
+ SVN_ERR(svn_stream_mark(translated_stream, &mark, pool));
+ len = 3;
+ SVN_ERR(svn_stream_read(translated_stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ /* ### The test currently fails here because the keyword isn't
+ * ### expanded correctly. buf contains "$My\0" */
+ SVN_ERR_ASSERT(strcmp(buf, "my ") == 0);
+ SVN_ERR(svn_stream_seek(stream, mark));
+ len = 3;
+ SVN_ERR(svn_stream_read(stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "my ") == 0);
+
+ /* Seek from inside of keyword to inside of keyword. */
+ SVN_ERR(svn_stream_mark(translated_stream, &mark, pool));
+ len = 3;
+ SVN_ERR(svn_stream_read(translated_stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "key") == 0);
+ SVN_ERR(svn_stream_seek(stream, mark));
+ len = 3;
+ SVN_ERR(svn_stream_read(stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "my ") == 0);
+
+ /* Seek from inside of keyword to outside of keyword. */
+ len = 22;
+ SVN_ERR(svn_stream_read(translated_stream, buf, &len));
+ SVN_ERR_ASSERT(len == 22);
+ buf[22] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "keyword was expandedTw") == 0);
+ SVN_ERR(svn_stream_mark(translated_stream, &mark2, pool));
+ SVN_ERR(svn_stream_seek(stream, mark));
+ len = 3;
+ SVN_ERR(svn_stream_read(stream, buf, &len));
+ SVN_ERR_ASSERT(len == 3);
+ buf[3] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "my ") == 0);
+ SVN_ERR(svn_stream_seek(stream, mark2));
+ len = 1;
+ SVN_ERR(svn_stream_read(stream, buf, &len));
+ SVN_ERR_ASSERT(len == 1);
+ buf[1] = '\0';
+ SVN_ERR_ASSERT(strcmp(buf, "o") == 0);
+
+ SVN_ERR(svn_stream_close(stream));
+
+ return SVN_NO_ERROR;
+}
+
+
/* The test table. */
@@ -578,5 +659,7 @@ struct svn_test_descriptor_t test_funcs[
"test stream seeking for files"),
SVN_TEST_PASS2(test_stream_seek_stringbuf,
"test stream seeking for stringbufs"),
+ SVN_TEST_XFAIL2(test_stream_seek_translated,
+ "test stream seeking for translated streams"),
SVN_TEST_NULL
};