diff options
Diffstat (limited to 'subversion/libsvn_ra_serf/replay.c')
-rw-r--r-- | subversion/libsvn_ra_serf/replay.c | 955 |
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; |