diff options
| author | Peter Wemm <peter@FreeBSD.org> | 2015-10-12 08:54:49 +0000 | 
|---|---|---|
| committer | Peter Wemm <peter@FreeBSD.org> | 2015-10-12 08:54:49 +0000 | 
| commit | dc5d469d6574e9fb03bdd793658bb371315b306a (patch) | |
| tree | 013c2e6845398e5a9ca4901dcc077769c7520e1d /subversion/libsvn_ra_serf/replay.c | |
| parent | 58218291fa73a17020ef0447398e9e8a78f9e8c7 (diff) | |
Diffstat (limited to 'subversion/libsvn_ra_serf/replay.c')
| -rw-r--r-- | subversion/libsvn_ra_serf/replay.c | 954 | 
1 files changed, 408 insertions, 546 deletions
| diff --git a/subversion/libsvn_ra_serf/replay.c b/subversion/libsvn_ra_serf/replay.c index 66e2f584d53f..d9a2ed6992b2 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,23 @@ 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->done_delegate = replay_done; +          handler->done_delegate_baton = rev_ctx; +          rev_ctx->report_handler = handler;            svn_ra_serf__request_create(handler);            rev++; @@ -892,26 +768,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; | 
