summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_serf/replay.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_serf/replay.c')
-rw-r--r--subversion/libsvn_ra_serf/replay.c955
1 files changed, 409 insertions, 546 deletions
diff --git a/subversion/libsvn_ra_serf/replay.c b/subversion/libsvn_ra_serf/replay.c
index 66e2f58..8d2da69 100644
--- a/subversion/libsvn_ra_serf/replay.c
+++ b/subversion/libsvn_ra_serf/replay.c
@@ -29,6 +29,7 @@
#include "svn_pools.h"
#include "svn_ra.h"
#include "svn_dav.h"
+#include "svn_hash.h"
#include "svn_xml.h"
#include "../libsvn_ra/ra_loader.h"
#include "svn_config.h"
@@ -46,55 +47,93 @@
* This enum represents the current state of our XML parsing.
*/
typedef enum replay_state_e {
- NONE = 0,
- REPORT,
- OPEN_DIR,
- ADD_DIR,
- OPEN_FILE,
- ADD_FILE,
- DELETE_ENTRY,
- APPLY_TEXTDELTA,
- CHANGE_PROP
+ INITIAL = XML_STATE_INITIAL,
+
+ REPLAY_REPORT,
+ REPLAY_TARGET_REVISION,
+ REPLAY_OPEN_ROOT,
+ REPLAY_OPEN_DIRECTORY,
+ REPLAY_OPEN_FILE,
+ REPLAY_ADD_DIRECTORY,
+ REPLAY_ADD_FILE,
+ REPLAY_DELETE_ENTRY,
+ REPLAY_CLOSE_FILE,
+ REPLAY_CLOSE_DIRECTORY,
+ REPLAY_CHANGE_DIRECTORY_PROP,
+ REPLAY_CHANGE_FILE_PROP,
+ REPLAY_APPLY_TEXTDELTA
} replay_state_e;
-typedef struct replay_info_t replay_info_t;
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t replay_ttable[] = {
+ { INITIAL, S_, "editor-report", REPLAY_REPORT,
+ FALSE, { NULL }, TRUE },
-struct replay_info_t {
- apr_pool_t *pool;
+ /* Replay just throws every operation as xml element directly
+ in the replay report, so we can't really use the nice exit
+ handling of the transition parser to handle clean callbacks */
- void *baton;
- svn_stream_t *stream;
+ { REPLAY_REPORT, S_, "target-revision", REPLAY_TARGET_REVISION,
+ FALSE, { "rev", NULL }, TRUE },
- replay_info_t *parent;
-};
+ { REPLAY_REPORT, S_, "open-root", REPLAY_OPEN_ROOT,
+ FALSE, { "rev", NULL }, TRUE },
-typedef svn_error_t *
-(*change_prop_t)(void *baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool);
+ { REPLAY_REPORT, S_, "open-directory", REPLAY_OPEN_DIRECTORY,
+ FALSE, { "name", "rev", NULL }, TRUE },
-typedef struct prop_info_t {
- apr_pool_t *pool;
+ { REPLAY_REPORT, S_, "open-file", REPLAY_OPEN_FILE,
+ FALSE, { "name", "rev", NULL }, TRUE },
- change_prop_t change;
+ { REPLAY_REPORT, S_, "add-directory", REPLAY_ADD_DIRECTORY,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
- const char *name;
- svn_boolean_t del_prop;
+ { REPLAY_REPORT, S_, "add-file", REPLAY_ADD_FILE,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
- svn_stringbuf_t *prop_value;
+ { REPLAY_REPORT, S_, "delete-entry", REPLAY_DELETE_ENTRY,
+ FALSE, { "name", "rev", NULL }, TRUE },
- replay_info_t *parent;
-} prop_info_t;
+ { REPLAY_REPORT, S_, "close-file", REPLAY_CLOSE_FILE,
+ FALSE, { "?checksum", NULL }, TRUE },
-typedef struct replay_context_t {
- apr_pool_t *src_rev_pool;
- apr_pool_t *dst_rev_pool;
+ { REPLAY_REPORT, S_, "close-directory", REPLAY_CLOSE_DIRECTORY,
+ FALSE, { NULL }, TRUE },
- /* Are we done fetching this file? */
- svn_boolean_t done;
- svn_ra_serf__list_t **done_list;
- svn_ra_serf__list_t done_item;
+ { REPLAY_REPORT, S_, "change-dir-prop", REPLAY_CHANGE_DIRECTORY_PROP,
+ TRUE, { "name", "?del", NULL }, TRUE },
+
+ { REPLAY_REPORT, S_, "change-file-prop", REPLAY_CHANGE_FILE_PROP,
+ TRUE, { "name", "?del", NULL }, TRUE },
+
+ { REPLAY_REPORT, S_, "apply-textdelta", REPLAY_APPLY_TEXTDELTA,
+ FALSE, { "?checksum", NULL }, TRUE },
+
+ { 0 }
+};
+
+/* Per directory/file state */
+typedef struct replay_node_t {
+ apr_pool_t *pool; /* pool allocating this node's data */
+ svn_boolean_t file; /* file or dir */
+
+ void *baton; /* node baton */
+ svn_stream_t *stream; /* stream while handling txdata */
+
+ struct replay_node_t *parent; /* parent node or NULL */
+} replay_node_t;
+
+/* Per revision replay report state */
+typedef struct revision_report_t {
+ apr_pool_t *pool; /* per revision pool */
+
+ struct replay_node_t *current_node;
+ struct replay_node_t *root_node;
+
+ /* Are we done fetching this file?
+ Handles book-keeping in multi-report case */
+ svn_boolean_t *done;
+ int *replay_reports; /* NULL or number of outstanding reports */
/* callback to get an editor */
svn_ra_replay_revstart_callback_t revstart_func;
@@ -121,479 +160,324 @@ typedef struct replay_context_t {
svn_revnum_t revprop_rev;
/* Revision properties for this revision. */
- apr_hash_t *revs_props;
- apr_hash_t *props;
-
- /* Keep a reference to the XML parser ctx to report any errors. */
- svn_ra_serf__xml_parser_t *parser_ctx;
+ apr_hash_t *rev_props;
/* Handlers for the PROPFIND and REPORT for the current revision. */
svn_ra_serf__handler_t *propfind_handler;
- svn_ra_serf__handler_t *report_handler;
-
-} replay_context_t;
-
-
-static void *
-push_state(svn_ra_serf__xml_parser_t *parser,
- replay_context_t *replay_ctx,
- replay_state_e state)
-{
- svn_ra_serf__xml_push_state(parser, state);
-
- if (state == OPEN_DIR || state == ADD_DIR ||
- state == OPEN_FILE || state == ADD_FILE)
- {
- replay_info_t *info;
- apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
-
- info = apr_palloc(pool, sizeof(*info));
-
- info->pool = pool;
- info->parent = parser->state->private;
- info->baton = NULL;
- info->stream = NULL;
+ svn_ra_serf__handler_t *report_handler; /* For done handler */
- parser->state->private = info;
- }
- else if (state == CHANGE_PROP)
- {
- prop_info_t *info;
- apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
-
- info = apr_pcalloc(pool, sizeof(*info));
-
- info->pool = pool;
- info->parent = parser->state->private;
- info->prop_value = svn_stringbuf_create_empty(pool);
-
- parser->state->private = info;
- }
-
- return parser->state->private;
-}
+} revision_report_t;
+/* Conforms to svn_ra_serf__xml_opened_t */
static svn_error_t *
-start_replay(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool)
+replay_opened(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int entered_state,
+ const svn_ra_serf__dav_props_t *tag,
+ apr_pool_t *scratch_pool)
{
- replay_context_t *ctx = parser->user_data;
- replay_state_e state;
+ struct revision_report_t *ctx = baton;
- state = parser->state->current_state;
-
- if (state == NONE &&
- strcmp(name.name, "editor-report") == 0)
+ if (entered_state == REPLAY_REPORT)
{
- push_state(parser, ctx, REPORT);
-
/* Before we can continue, we need the revision properties. */
SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done);
- /* Create a pool for the commit editor. */
- ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
-
- SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
- ctx->revprop_target,
- ctx->revprop_rev,
- ctx->revs_props,
- ctx->dst_rev_pool,
- scratch_pool));
+ svn_ra_serf__keep_only_regular_props(ctx->rev_props, scratch_pool);
if (ctx->revstart_func)
{
SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
&ctx->editor, &ctx->editor_baton,
- ctx->props,
- ctx->dst_rev_pool));
+ ctx->rev_props,
+ ctx->pool));
}
}
- else if (state == REPORT &&
- strcmp(name.name, "target-revision") == 0)
+ else if (entered_state == REPLAY_APPLY_TEXTDELTA)
{
- const char *rev;
-
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in target-revision element"));
- }
-
- SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
- SVN_STR_TO_REV(rev),
- scratch_pool));
+ struct replay_node_t *node = ctx->current_node;
+ apr_hash_t *attrs;
+ const char *checksum;
+ svn_txdelta_window_handler_t handler;
+ void *handler_baton;
+
+ if (! node || ! node->file || node->stream)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ /* ### Is there a better way to access a specific attr here? */
+ attrs = svn_ra_serf__xml_gather_since(xes, REPLAY_APPLY_TEXTDELTA);
+ checksum = svn_hash_gets(attrs, "checksum");
+
+ SVN_ERR(ctx->editor->apply_textdelta(node->baton, checksum, node->pool,
+ &handler, &handler_baton));
+
+ if (handler != svn_delta_noop_window_handler)
+ {
+ node->stream = svn_base64_decode(
+ svn_txdelta_parse_svndiff(handler,
+ handler_baton,
+ TRUE,
+ node->pool),
+ node->pool);
+ }
}
- else if (state == REPORT &&
- strcmp(name.name, "open-root") == 0)
- {
- const char *rev;
- replay_info_t *info;
- rev = svn_xml_get_attr_value("rev", attrs);
-
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-root element"));
- }
+ return SVN_NO_ERROR;
+}
- info = push_state(parser, ctx, OPEN_DIR);
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+replay_closed(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int leaving_state,
+ const svn_string_t *cdata,
+ apr_hash_t *attrs,
+ apr_pool_t *scratch_pool)
+{
+ struct revision_report_t *ctx = baton;
- SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
- SVN_STR_TO_REV(rev),
- ctx->dst_rev_pool,
- &info->baton));
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "delete-entry") == 0)
+ if (leaving_state == REPLAY_REPORT)
{
- const char *file_name, *rev;
- replay_info_t *info;
+ if (ctx->current_node)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in delete-entry element"));
- }
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
+ if (ctx->revfinish_func)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in delete-entry element"));
+ SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
+ ctx->editor, ctx->editor_baton,
+ ctx->rev_props, scratch_pool));
}
+ }
+ else if (leaving_state == REPLAY_TARGET_REVISION)
+ {
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
- info = push_state(parser, ctx, DELETE_ENTRY);
-
- SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
- info->baton, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
+ (svn_revnum_t)rev,
+ scratch_pool));
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-directory") == 0)
+ else if (leaving_state == REPLAY_OPEN_ROOT)
{
- const char *rev, *dir_name;
- replay_info_t *info;
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
+ apr_pool_t *root_pool = svn_pool_create(ctx->pool);
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-directory element"));
- }
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
+ if (ctx->current_node || ctx->root_node)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- info = push_state(parser, ctx, OPEN_DIR);
+ ctx->root_node = apr_pcalloc(root_pool, sizeof(*ctx->root_node));
+ ctx->root_node->pool = root_pool;
- SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
- SVN_STR_TO_REV(rev),
- ctx->dst_rev_pool, &info->baton));
+ ctx->current_node = ctx->root_node;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->open_root(ctx->editor_baton, (svn_revnum_t)rev,
+ root_pool,
+ &ctx->current_node->baton));
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-directory") == 0)
+ else if (leaving_state == REPLAY_OPEN_DIRECTORY
+ || leaving_state == REPLAY_OPEN_FILE
+ || leaving_state == REPLAY_ADD_DIRECTORY
+ || leaving_state == REPLAY_ADD_FILE)
{
- const char *dir_name, *copyfrom, *copyrev;
- svn_revnum_t rev;
- replay_info_t *info;
-
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
+ struct replay_node_t *node;
+ apr_pool_t *node_pool;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *rev_str;
+ apr_int64_t rev;
+
+ if (!ctx->current_node || ctx->current_node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ node_pool = svn_pool_create(ctx->current_node->pool);
+ node = apr_pcalloc(node_pool, sizeof(*node));
+ node->pool = node_pool;
+ node->parent = ctx->current_node;
+
+ if (leaving_state == REPLAY_OPEN_DIRECTORY
+ || leaving_state == REPLAY_OPEN_FILE)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-directory element"));
+ rev_str = svn_hash_gets(attrs, "rev");
}
- copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
- copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
+ else
+ rev_str = svn_hash_gets(attrs, "copyfrom-rev");
- if (copyrev)
- rev = SVN_STR_TO_REV(copyrev);
+ if (rev_str)
+ SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
else
rev = SVN_INVALID_REVNUM;
- info = push_state(parser, ctx, ADD_DIR);
-
- SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
- copyfrom, rev,
- ctx->dst_rev_pool, &info->baton));
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "close-directory") == 0)
- {
- replay_info_t *info = parser->state->private;
-
- SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-file") == 0)
- {
- const char *file_name, *rev;
- replay_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
+ switch (leaving_state)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-file element"));
+ case REPLAY_OPEN_DIRECTORY:
+ node->file = FALSE;
+ SVN_ERR(ctx->editor->open_directory(name,
+ ctx->current_node->baton,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_OPEN_FILE:
+ node->file = TRUE;
+ SVN_ERR(ctx->editor->open_file(name,
+ ctx->current_node->baton,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_ADD_DIRECTORY:
+ node->file = FALSE;
+ SVN_ERR(ctx->editor->add_directory(
+ name,
+ ctx->current_node->baton,
+ SVN_IS_VALID_REVNUM(rev)
+ ? svn_hash_gets(attrs, "copyfrom-path")
+ : NULL,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_ADD_FILE:
+ node->file = TRUE;
+ SVN_ERR(ctx->editor->add_file(
+ name,
+ ctx->current_node->baton,
+ SVN_IS_VALID_REVNUM(rev)
+ ? svn_hash_gets(attrs, "copyfrom-path")
+ : NULL,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ /* default: unreachable */
}
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-file element"));
- }
-
- info = push_state(parser, ctx, OPEN_FILE);
-
- SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton,
- SVN_STR_TO_REV(rev),
- info->pool, &info->baton));
+ ctx->current_node = node;
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-file") == 0)
+ else if (leaving_state == REPLAY_CLOSE_FILE)
{
- const char *file_name, *copyfrom, *copyrev;
- svn_revnum_t rev;
- replay_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-file element"));
- }
- copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
- copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
- info = push_state(parser, ctx, ADD_FILE);
+ struct replay_node_t *node = ctx->current_node;
- if (copyrev)
- rev = SVN_STR_TO_REV(copyrev);
- else
- rev = SVN_INVALID_REVNUM;
+ if (! node || ! node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
- copyfrom, rev,
- info->pool, &info->baton));
+ SVN_ERR(ctx->editor->close_file(node->baton,
+ svn_hash_gets(attrs, "checksum"),
+ node->pool));
+ ctx->current_node = node->parent;
+ svn_pool_destroy(node->pool);
}
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "apply-textdelta") == 0)
+ else if (leaving_state == REPLAY_CLOSE_DIRECTORY)
{
- const char *checksum;
- replay_info_t *info;
- svn_txdelta_window_handler_t textdelta;
- void *textdelta_baton;
- svn_stream_t *delta_stream;
-
- info = push_state(parser, ctx, APPLY_TEXTDELTA);
-
- checksum = svn_xml_get_attr_value("checksum", attrs);
- if (checksum)
- {
- checksum = apr_pstrdup(info->pool, checksum);
- }
+ struct replay_node_t *node = ctx->current_node;
- SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
- info->pool,
- &textdelta,
- &textdelta_baton));
+ if (! node || node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
- TRUE, info->pool);
- info->stream = svn_base64_decode(delta_stream, info->pool);
+ SVN_ERR(ctx->editor->close_directory(node->baton, node->pool));
+ ctx->current_node = node->parent;
+ svn_pool_destroy(node->pool);
}
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "close-file") == 0)
+ else if (leaving_state == REPLAY_DELETE_ENTRY)
{
- replay_info_t *info = parser->state->private;
- const char *checksum;
-
- checksum = svn_xml_get_attr_value("checksum", attrs);
-
- SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
+ struct replay_node_t *parent_node = ctx->current_node;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
+
+ if (! parent_node || parent_node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->delete_entry(name,
+ (svn_revnum_t)rev,
+ parent_node->baton,
+ scratch_pool));
}
- else if (((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "change-file-prop") == 0) ||
- ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "change-dir-prop") == 0))
+ else if (leaving_state == REPLAY_CHANGE_FILE_PROP
+ || leaving_state == REPLAY_CHANGE_DIRECTORY_PROP)
{
- const char *prop_name;
- prop_info_t *info;
+ struct replay_node_t *node = ctx->current_node;
+ const char *name;
+ const svn_string_t *value;
- prop_name = svn_xml_get_attr_value("name", attrs);
- if (!prop_name)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in %s element"),
- name.name);
- }
-
- info = push_state(parser, ctx, CHANGE_PROP);
+ if (! node || node->file != (leaving_state == REPLAY_CHANGE_FILE_PROP))
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+ name = svn_hash_gets(attrs, "name");
- if (svn_xml_get_attr_value("del", attrs))
- info->del_prop = TRUE;
+ if (svn_hash_gets(attrs, "del"))
+ value = NULL;
else
- info->del_prop = FALSE;
+ value = svn_base64_decode_string(cdata, scratch_pool);
- info->name = apr_pstrdup(info->pool, prop_name);
- if (state == OPEN_FILE || state == ADD_FILE)
+ if (node->file)
{
- info->change = ctx->editor->change_file_prop;
+ SVN_ERR(ctx->editor->change_file_prop(node->baton, name, value,
+ scratch_pool));
}
else
{
- info->change = ctx->editor->change_dir_prop;
- }
-
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-end_replay(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- replay_context_t *ctx = parser->user_data;
- replay_state_e state;
-
- state = parser->state->current_state;
-
- if (state == REPORT &&
- strcmp(name.name, "editor-report") == 0)
- {
- svn_ra_serf__xml_pop_state(parser);
- if (ctx->revfinish_func)
- {
- SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
- ctx->editor, ctx->editor_baton,
- ctx->props,
- ctx->dst_rev_pool));
+ SVN_ERR(ctx->editor->change_dir_prop(node->baton, name, value,
+ scratch_pool));
}
- svn_pool_destroy(ctx->dst_rev_pool);
- }
- else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
- {
- /* Don't do anything. */
- }
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "close-file") == 0)
- {
- /* Don't do anything. */
- }
- else if ((state == APPLY_TEXTDELTA) &&
- strcmp(name.name, "apply-textdelta") == 0)
- {
- replay_info_t *info = parser->state->private;
- SVN_ERR(svn_stream_close(info->stream));
- svn_ra_serf__xml_pop_state(parser);
}
- else if (state == CHANGE_PROP &&
- (strcmp(name.name, "change-file-prop") == 0 ||
- strcmp(name.name, "change-dir-prop") == 0))
+ else if (leaving_state == REPLAY_APPLY_TEXTDELTA)
{
- prop_info_t *info = parser->state->private;
- const svn_string_t *prop_val;
+ struct replay_node_t *node = ctx->current_node;
- if (info->del_prop)
- {
- prop_val = NULL;
- }
- else
- {
- const svn_string_t *morph;
-
- morph = svn_stringbuf__morph_into_string(info->prop_value);
-#ifdef SVN_DEBUG
- info->prop_value = NULL; /* morph killed the stringbuf. */
-#endif
+ if (! node || ! node->file || ! node->stream)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- prop_val = svn_base64_decode_string(morph, info->pool);
- }
+ SVN_ERR(svn_stream_close(node->stream));
- SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
- info->parent->pool));
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
+ node->stream = NULL;
}
-
return SVN_NO_ERROR;
}
+/* Conforms to svn_ra_serf__xml_cdata_t */
static svn_error_t *
-cdata_replay(svn_ra_serf__xml_parser_t *parser,
+replay_cdata(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int current_state,
const char *data,
apr_size_t len,
apr_pool_t *scratch_pool)
{
- replay_context_t *replay_ctx = parser->user_data;
- replay_state_e state;
-
- UNUSED_CTX(replay_ctx);
+ struct revision_report_t *ctx = baton;
- state = parser->state->current_state;
-
- if (state == APPLY_TEXTDELTA)
+ if (current_state == REPLAY_APPLY_TEXTDELTA)
{
- replay_info_t *info = parser->state->private;
- apr_size_t written;
-
- written = len;
+ struct replay_node_t *node = ctx->current_node;
- SVN_ERR(svn_stream_write(info->stream, data, &written));
+ if (! node || ! node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- if (written != len)
- return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
- _("Error writing stream: unexpected EOF"));
- }
- else if (state == CHANGE_PROP)
- {
- prop_info_t *info = parser->state->private;
+ if (node->stream)
+ {
+ apr_size_t written = len;
- svn_stringbuf_appendbytes(info->prop_value, data, len);
+ SVN_ERR(svn_stream_write(node->stream, data, &written));
+ if (written != len)
+ return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ _("Error writing stream: unexpected EOF"));
+ }
}
return SVN_NO_ERROR;
}
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_replay_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
- replay_context_t *ctx = baton;
+ struct revision_report_t *ctx = baton;
serf_bucket_t *body_bkt;
body_bkt = serf_bucket_aggregate_create(alloc);
@@ -601,7 +485,7 @@ create_replay_body(serf_bucket_t **bkt,
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"S:replay-report",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
/* If we have a non-NULL include path, we add it to the body and
omit the revision; otherwise, the reverse. */
@@ -616,17 +500,17 @@ create_replay_body(serf_bucket_t **bkt,
{
svn_ra_serf__add_tag_buckets(body_bkt,
"S:revision",
- apr_ltoa(ctx->src_rev_pool, ctx->revision),
+ apr_ltoa(pool, ctx->revision),
alloc);
}
svn_ra_serf__add_tag_buckets(body_bkt,
"S:low-water-mark",
- apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
+ apr_ltoa(pool, ctx->low_water_mark),
alloc);
svn_ra_serf__add_tag_buckets(body_bkt,
"S:send-deltas",
- apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
+ apr_ltoa(pool, ctx->send_deltas),
alloc);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
@@ -642,65 +526,49 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session,
svn_boolean_t send_deltas,
const svn_delta_editor_t *editor,
void *edit_baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- replay_context_t *replay_ctx;
+ struct revision_report_t ctx = { NULL };
svn_ra_serf__session_t *session = ra_session->priv;
svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
- svn_error_t *err;
+ svn_ra_serf__xml_context_t *xmlctx;
const char *report_target;
- SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, session,
+ scratch_pool));
+
+ ctx.pool = svn_pool_create(scratch_pool);
+ ctx.editor = editor;
+ ctx.editor_baton = edit_baton;
+ ctx.done = FALSE;
+ ctx.revision = revision;
+ ctx.low_water_mark = low_water_mark;
+ ctx.send_deltas = send_deltas;
+ ctx.rev_props = apr_hash_make(scratch_pool);
- replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
- replay_ctx->src_rev_pool = pool;
- replay_ctx->editor = editor;
- replay_ctx->editor_baton = edit_baton;
- replay_ctx->done = FALSE;
- replay_ctx->revision = revision;
- replay_ctx->low_water_mark = low_water_mark;
- replay_ctx->send_deltas = send_deltas;
- replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+ xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+ replay_opened, replay_closed,
+ replay_cdata,
+ &ctx,
+ scratch_pool);
- handler = apr_pcalloc(pool, sizeof(*handler));
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ scratch_pool);
- handler->handler_pool = pool;
handler->method = "REPORT";
handler->path = session->session_url.path;
handler->body_delegate = create_replay_body;
- handler->body_delegate_baton = replay_ctx;
+ handler->body_delegate_baton = &ctx;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
- parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
+ /* Not setting up done handler as we don't use a global context */
- parser_ctx->pool = pool;
- parser_ctx->user_data = replay_ctx;
- parser_ctx->start = start_replay;
- parser_ctx->end = end_replay;
- parser_ctx->cdata = cdata_replay;
- parser_ctx->done = &replay_ctx->done;
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
-
- /* This is only needed to handle errors during XML parsing. */
- replay_ctx->parser_ctx = parser_ctx;
- replay_ctx->report_handler = handler; /* unused */
-
- svn_ra_serf__request_create(handler);
-
- err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
-
- SVN_ERR(svn_error_compose_create(
+ return svn_error_trace(
svn_ra_serf__error_on_status(handler->sline,
handler->path,
- handler->location),
- err));
-
- return SVN_NO_ERROR;
+ handler->location));
}
/* The maximum number of outstanding requests at any time. When this
@@ -734,6 +602,33 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session,
*/
#define MAX_OUTSTANDING_REQUESTS 50
+/* Implements svn_ra_serf__response_done_delegate_t for svn_ra_serf__replay_range */
+static svn_error_t *
+replay_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct revision_report_t *ctx = baton;
+ svn_ra_serf__handler_t *handler = ctx->report_handler;
+
+ if (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, scratch_pool);
+ else if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ *ctx->done = TRUE; /* Breaks out svn_ra_serf__context_run_wait */
+
+ /* Are re replaying multiple revisions? */
+ if (ctx->replay_reports)
+ {
+ (*ctx->replay_reports)--;
+ }
+
+ svn_pool_destroy(ctx->pool); /* Destroys handler and request! */
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
svn_revnum_t start_revision,
@@ -743,15 +638,17 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
svn_ra_replay_revstart_callback_t revstart_func,
svn_ra_replay_revfinish_callback_t revfinish_func,
void *replay_baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
svn_revnum_t rev = start_revision;
const char *report_target;
int active_reports = 0;
const char *include_path;
+ svn_boolean_t done;
- SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, session,
+ scratch_pool));
/* Prior to 1.8, mod_dav_svn expect to get replay REPORT requests
aimed at the session URL. But that's incorrect -- these reports
@@ -774,8 +671,7 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
{
SVN_ERR(svn_ra_serf__get_relative_path(&include_path,
session->session_url.path,
- session, session->conns[0],
- pool));
+ session, scratch_pool));
}
else
{
@@ -784,10 +680,6 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
while (active_reports || rev <= end_revision)
{
- svn_ra_serf__list_t *done_list;
- svn_ra_serf__list_t *done_reports = NULL;
- replay_context_t *replay_ctx;
-
if (session->cancel_func)
SVN_ERR(session->cancel_func(session->cancel_baton));
@@ -795,54 +687,56 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
requests to MAX_OUTSTANDING_REQUESTS. */
if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS)
{
+ struct revision_report_t *rev_ctx;
svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
- apr_pool_t *ctx_pool = svn_pool_create(pool);
+ apr_pool_t *rev_pool = svn_pool_create(scratch_pool);
+ svn_ra_serf__xml_context_t *xmlctx;
const char *replay_target;
- replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
- replay_ctx->src_rev_pool = ctx_pool;
- replay_ctx->revstart_func = revstart_func;
- replay_ctx->revfinish_func = revfinish_func;
- replay_ctx->replay_baton = replay_baton;
- replay_ctx->done = FALSE;
- replay_ctx->include_path = include_path;
- replay_ctx->revision = rev;
- replay_ctx->low_water_mark = low_water_mark;
- replay_ctx->send_deltas = send_deltas;
- replay_ctx->done_item.data = replay_ctx;
+ rev_ctx = apr_pcalloc(rev_pool, sizeof(*rev_ctx));
+ rev_ctx->pool = rev_pool;
+ rev_ctx->revstart_func = revstart_func;
+ rev_ctx->revfinish_func = revfinish_func;
+ rev_ctx->replay_baton = replay_baton;
+ rev_ctx->done = &done;
+ rev_ctx->replay_reports = &active_reports;
+ rev_ctx->include_path = include_path;
+ rev_ctx->revision = rev;
+ rev_ctx->low_water_mark = low_water_mark;
+ rev_ctx->send_deltas = send_deltas;
/* Request all properties of a certain revision. */
- replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+ rev_ctx->rev_props = apr_hash_make(rev_ctx->pool);
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
- replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld",
- session->rev_stub, rev);
- replay_ctx->revprop_rev = SVN_INVALID_REVNUM;
+ rev_ctx->revprop_target = apr_psprintf(rev_pool, "%s/%ld",
+ session->rev_stub, rev);
+ rev_ctx->revprop_rev = SVN_INVALID_REVNUM;
}
else
{
- replay_ctx->revprop_target = report_target;
- replay_ctx->revprop_rev = rev;
+ rev_ctx->revprop_target = report_target;
+ rev_ctx->revprop_rev = rev;
}
- SVN_ERR(svn_ra_serf__deliver_props(&replay_ctx->propfind_handler,
- replay_ctx->revs_props, session,
- session->conns[0],
- replay_ctx->revprop_target,
- replay_ctx->revprop_rev,
- "0", all_props,
- NULL,
- replay_ctx->src_rev_pool));
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &rev_ctx->propfind_handler,
+ session,
+ rev_ctx->revprop_target,
+ rev_ctx->revprop_rev,
+ "0", all_props,
+ svn_ra_serf__deliver_svn_props,
+ rev_ctx->rev_props,
+ rev_pool));
/* Spin up the serf request for the PROPFIND. */
- svn_ra_serf__request_create(replay_ctx->propfind_handler);
+ svn_ra_serf__request_create(rev_ctx->propfind_handler);
/* Send the replay REPORT request. */
if (session->supports_rev_rsrc_replay)
{
- replay_target = apr_psprintf(pool, "%s/%ld",
+ replay_target = apr_psprintf(rev_pool, "%s/%ld",
session->rev_stub, rev);
}
else
@@ -850,41 +744,24 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
replay_target = session->session_url.path;
}
- handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
+ xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+ replay_opened, replay_closed,
+ replay_cdata, rev_ctx,
+ rev_pool);
+
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ rev_pool);
- handler->handler_pool = replay_ctx->src_rev_pool;
handler->method = "REPORT";
handler->path = replay_target;
handler->body_delegate = create_replay_body;
- handler->body_delegate_baton = replay_ctx;
- handler->conn = session->conns[0];
- handler->session = session;
-
- parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
- sizeof(*parser_ctx));
-
- /* Setup the XML parser context.
- Because we have not one but a list of requests, the 'done' property
- on the replay_ctx is not of much use. Instead, use 'done_list'.
- On each handled response (succesfully or not), the parser will add
- done_item to done_list, so by keeping track of the state of
- done_list we know how many requests have been handled completely.
- */
- parser_ctx->pool = replay_ctx->src_rev_pool;
- parser_ctx->user_data = replay_ctx;
- parser_ctx->start = start_replay;
- parser_ctx->end = end_replay;
- parser_ctx->cdata = cdata_replay;
- parser_ctx->done = &replay_ctx->done;
- parser_ctx->done_list = &done_reports;
- parser_ctx->done_item = &replay_ctx->done_item;
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
- replay_ctx->report_handler = handler;
-
- /* This is only needed to handle errors during XML parsing. */
- replay_ctx->parser_ctx = parser_ctx;
+ handler->body_delegate_baton = rev_ctx;
+ handler->body_type = "text/xml";
+
+ handler->done_delegate = replay_done;
+ handler->done_delegate_baton = rev_ctx;
+ rev_ctx->report_handler = handler;
svn_ra_serf__request_create(handler);
rev++;
@@ -892,26 +769,12 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
}
/* Run the serf loop. */
- SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
-
- /* Substract the number of completely handled responses from our
- total nr. of open requests', so we'll know when to stop this loop.
- Since the message is completely handled, we can destroy its pool. */
- done_list = done_reports;
- while (done_list)
- {
- replay_context_t *ctx = (replay_context_t *)done_list->data;
- svn_ra_serf__handler_t *done_handler = ctx->report_handler;
-
- done_list = done_list->next;
- SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
- done_handler->path,
- done_handler->location));
- svn_pool_destroy(ctx->src_rev_pool);
- active_reports--;
- }
+ done = FALSE;
+ SVN_ERR(svn_ra_serf__context_run_wait(&done, session, scratch_pool));
- done_reports = NULL;
+ /* The done handler of reports decrements active_reports when a report
+ is done. This same handler reports (fatal) report errors, so we can
+ just loop here. */
}
return SVN_NO_ERROR;