/*
* editor.c : editing trees of versioned resources
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_pools.h>
#include "svn_types.h"
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "private/svn_editor.h"
#ifdef SVN_DEBUG
/* This enables runtime checks of the editor API constraints. This may
introduce additional memory and runtime overhead, and should not be used
in production builds.
### Remove before release?
### Disabled for now. If I call svn_editor_alter_directory(A) then
svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD.
If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child)
then if I call svn_editor_alter_directory(A) followed by
svn_editor_alter_directory(A/B/C) the latter fails on
VERIFY_PARENT_MAY_EXIST. */
#if 0
#define ENABLE_ORDERING_CHECK
#endif
#endif
struct svn_editor_t
{
void *baton;
/* Standard cancellation function. Called before each callback. */
svn_cancel_func_t cancel_func;
void *cancel_baton;
/* Our callback functions match that of the set-many structure, so
just use that. */
svn_editor_cb_many_t funcs;
/* This pool is used as the scratch_pool for all callbacks. */
apr_pool_t *scratch_pool;
#ifdef ENABLE_ORDERING_CHECK
svn_boolean_t within_callback;
apr_hash_t *pending_incomplete_children;
apr_hash_t *completed_nodes;
svn_boolean_t finished;
apr_pool_t *state_pool;
#endif
};
#ifdef ENABLE_ORDERING_CHECK
#define START_CALLBACK(editor) \
do { \
svn_editor_t *editor__tmp_e = (editor); \
SVN_ERR_ASSERT(!editor__tmp_e->within_callback); \
editor__tmp_e->within_callback = TRUE; \
} while (0)
#define END_CALLBACK(editor) ((editor)->within_callback = FALSE)
/* Marker to indicate no further changes are allowed on this node. */
static const int marker_done = 0;
#define MARKER_DONE (&marker_done)
/* Marker indicating that add_* may be called for this path, or that it
can be the destination of a copy or move. For copy/move, the path
will switch to MARKER_ALLOW_ALTER, to enable further tweaks. */
static const int marker_allow_add = 0;
#define MARKER_ALLOW_ADD (&marker_allow_add)
/* Marker indicating that alter_* may be called for this path. */
static const int marker_allow_alter = 0;
#define MARKER_ALLOW_ALTER (&marker_allow_alter)
/* Just like MARKER_DONE, but also indicates that the node was created
via add_directory(). This allows us to verify that the CHILDREN param
was comprehensive. */
static const int marker_added_dir = 0;
#define MARKER_ADDED_DIR (&marker_added_dir)
#define MARK_FINISHED(editor) ((editor)->finished = TRUE)
#define SHOULD_NOT_BE_FINISHED(editor) SVN_ERR_ASSERT(!(editor)->finished)
#define CLEAR_INCOMPLETE(editor, relpath) \
svn_hash_sets((editor)->pending_incomplete_children, relpath, NULL);
#define MARK_RELPATH(editor, relpath, value) \
svn_hash_sets((editor)->completed_nodes, \
apr_pstrdup((editor)->state_pool, relpath), value)
#define MARK_COMPLETED(editor, relpath) \
MARK_RELPATH(editor, relpath, MARKER_DONE)
#define SHOULD_NOT_BE_COMPLETED(editor, relpath) \
SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, relpath) == NULL)
#define MARK_ALLOW_ADD(editor, relpath) \
MARK_RELPATH(editor, relpath, MARKER_ALLOW_ADD)
#define SHOULD_ALLOW_ADD(editor, relpath) \
SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ADD, NULL))
#define MARK_ALLOW_ALTER(editor, relpath) \
MARK_RELPATH(editor, relpath, MARKER_ALLOW_ALTER)
#define SHOULD_ALLOW_ALTER(editor, relpath) \
SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ALTER, NULL))
#define MARK_ADDED_DIR(editor, relpath) \
MARK_RELPATH(editor, relpath, MARKER_ADDED_DIR)
#define CHECK_UNKNOWN_CHILD(editor, relpath) \
SVN_ERR_ASSERT(check_unknown_child(editor, relpath))
/* When a child is changed in some way, mark the parent directory as needing
to be "stable" (no future structural changes). IOW, only allow "alter" on
the parent. Prevents parent-add/delete/move after any child operation. */
#define MARK_PARENT_STABLE(editor, relpath) \
mark_parent_stable(editor, relpath)
/* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we
know it does not exist. All other cases: it might exist. */
#define VERIFY_PARENT_MAY_EXIST(editor, relpath) \
SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, \
svn_relpath_dirname(relpath, \
(editor)->scratch_pool)) \
!= MARKER_ALLOW_ADD)
/* If the parent is MARKER_ADDED_DIR, then we should not be deleting
children(*). If the parent is MARKER_ALLOW_ADD, then it has been
moved-away, so children cannot exist. That leaves MARKER_DONE,
MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that
we didn't get either of the bad ones.
(*) if the child as added via add_*(), then it would have been marked
as completed and delete/move-away already test against completed nodes.
This test is to beware of trying to delete "children" that are not
actually (and can't possibly be) present. */
#define CHILD_DELETIONS_ALLOWED(editor, relpath) \
SVN_ERR_ASSERT(!allow_either(editor, \
svn_relpath_dirname(relpath, \
(editor)->scratch_pool), \
MARKER_ADDED_DIR, MARKER_ALLOW_ADD))
static svn_boolean_t
allow_either(const svn_editor_t *editor,
const char *relpath,
const void *marker1,
const void *marker2)
{
void *value = svn_hash_gets(editor->completed_nodes, relpath);
return value == marker1 || value == marker2;
}
static svn_boolean_t
check_unknown_child(const svn_editor_t *editor,
const char *relpath)
{
const char *parent;
/* If we already know about the new child, then exit early. */
if (svn_hash_gets(editor->pending_incomplete_children, relpath) != NULL)
return TRUE;
parent = svn_relpath_dirname(relpath, editor->scratch_pool);
/* Was this parent created via svn_editor_add_directory() ? */
if (svn_hash_gets(editor->completed_nodes, parent)
== MARKER_ADDED_DIR)
{
/* Whoops. This child should have been listed in that add call,
and placed into ->pending_incomplete_children. */
return FALSE;
}
/* The parent was not added in this drive. */
return TRUE;
}
static void
mark_parent_stable(const svn_editor_t *editor,
const char *relpath)
{
const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool);
const void *marker = svn_hash_gets(editor->completed_nodes, parent);
/* If RELPATH has already been marked (to disallow adds, or that it
has been fully-completed), then do nothing. */
if (marker == MARKER_ALLOW_ALTER
|| marker == MARKER_DONE
|| marker == MARKER_ADDED_DIR)
return;
/* If the marker is MARKER_ALLOW_ADD, then that means the parent was
moved away. There is no way to work on a child. That should have
been tested before we got here by VERIFY_PARENT_MAY_EXIST(). */
SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD);
/* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER. */
MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER);
}
#else
/* Be wary with the definition of these macros so that we don't
end up with "statement with no effect" warnings. Obviously, this
depends upon particular usage, which is easy to verify. */
#define START_CALLBACK(editor) /* empty */
#define END_CALLBACK(editor) /* empty */
#define MARK_FINISHED(editor) /* empty */
#define SHOULD_NOT_BE_FINISHED(editor) /* empty */
#define CLEAR_INCOMPLETE(editor, relpath) /* empty */
#define MARK_COMPLETED(editor, relpath) /* empty */
#define SHOULD_NOT_BE_COMPLETED(editor, relpath) /* empty */
#define MARK_ALLOW_ADD(editor, relpath) /* empty */
#define SHOULD_ALLOW_ADD(editor, relpath) /* empty */
#define MARK_ALLOW_ALTER(editor, relpath) /* empty */
#define SHOULD_ALLOW_ALTER(editor, relpath) /* empty */
#define MARK_ADDED_DIR(editor, relpath) /* empty */
#define CHECK_UNKNOWN_CHILD(editor, relpath) /* empty */
#define MARK_PARENT_STABLE(editor, relpath) /* empty */
#define VERIFY_PARENT_MAY_EXIST(editor, relpath) /* empty */
#define CHILD_DELETIONS_ALLOWED(editor, relpath) /* empty */
#endif /* ENABLE_ORDERING_CHECK */
svn_error_t *
svn_editor_create(svn_editor_t **editor,
void *editor_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*editor = apr_pcalloc(result_pool, sizeof(**editor));
(*editor)->baton = editor_baton;
(*editor)->cancel_func = cancel_func;
(*editor)->cancel_baton = cancel_baton;
(*editor)->scratch_pool = svn_pool_create(result_pool);
#ifdef ENABLE_ORDERING_CHECK
(*editor)->pending_incomplete_children = apr_hash_make(result_pool);
(*editor)->completed_nodes = apr_hash_make(result_pool);
(*editor)->finished = FALSE;
(*editor)->state_pool = result_pool;
#endif
return SVN_NO_ERROR;
}
void *
svn_editor_get_baton(const svn_editor_t *editor)
{
return editor->baton;
}
svn_error_t *
svn_editor_setcb_add_directory(svn_editor_t *editor,
svn_editor_cb_add_directory_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_add_directory = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_add_file(svn_editor_t *editor,
svn_editor_cb_add_file_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_add_file = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_add_symlink(svn_editor_t *editor,
svn_editor_cb_add_symlink_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_add_symlink = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_add_absent(svn_editor_t *editor,
svn_editor_cb_add_absent_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_add_absent = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_alter_directory(svn_editor_t *editor,
svn_editor_cb_alter_directory_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_alter_directory = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_alter_file(svn_editor_t *editor,
svn_editor_cb_alter_file_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_alter_file = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_alter_symlink(svn_editor_t *editor,
svn_editor_cb_alter_symlink_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_alter_symlink = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_delete(svn_editor_t *editor,
svn_editor_cb_delete_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_delete = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_copy(svn_editor_t *editor,
svn_editor_cb_copy_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_copy = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_move(svn_editor_t *editor,
svn_editor_cb_move_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_move = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_complete(svn_editor_t *editor,
svn_editor_cb_complete_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_complete = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_abort(svn_editor_t *editor,
svn_editor_cb_abort_t callback,
apr_pool_t *scratch_pool)
{
editor->funcs.cb_abort = callback;
return SVN_NO_ERROR;
}
svn_error_t *
svn_editor_setcb_many(svn_editor_t *editor,
const svn_editor_cb_many_t *many,
apr_pool_t *scratch_pool)
{
#define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
COPY_CALLBACK(cb_add_directory);
COPY_CALLBACK(cb_add_file);
COPY_CALLBACK(cb_add_symlink);
COPY_CALLBACK(cb_add_absent);
COPY_CALLBACK(cb_alter_directory);
COPY_CALLBACK(cb_alter_file);
COPY_CALLBACK(cb_alter_symlink);
COPY_CALLBACK(cb_delete);
COPY_CALLBACK(cb_copy);
COPY_CALLBACK(cb_move);
COPY_CALLBACK(cb_complete);
COPY_CALLBACK(cb_abort);
#undef COPY_CALLBACK
return SVN_NO_ERROR;
}
static svn_error_t *
check_cancel(svn_editor_t *editor)
{
svn_error_t *err = NULL;
if (editor->cancel_func)
{
START_CALLBACK(editor);
err = editor->cancel_func(editor->cancel_baton);
END_CALLBACK(editor);
}
return svn_error_trace(err);
}
svn_error_t *
svn_editor_add_directory(svn_editor_t *editor,
const char *relpath,
const apr_array_header_t *children,
apr_hash_t *props,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT(children != NULL);
SVN_ERR_ASSERT(props != NULL);
/* ### validate children are just basenames? */
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_add_directory)
{
START_CALLBACK(editor);
err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
props, replaces_rev,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_ADDED_DIR(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
#ifdef ENABLE_ORDERING_CHECK
{
int i;
for (i = 0; i < children->nelts; i++)
{
const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
const char *child = svn_relpath_join(relpath, child_basename,
editor->state_pool);
svn_hash_sets(editor->pending_incomplete_children, child, "");
}
}
#endif
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_add_file(svn_editor_t *editor,
const char *relpath,
const svn_checksum_t *checksum,
svn_stream_t *contents,
apr_hash_t *props,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT(checksum != NULL
&& checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
SVN_ERR_ASSERT(contents != NULL);
SVN_ERR_ASSERT(props != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_add_file)
{
START_CALLBACK(editor);
err = editor->funcs.cb_add_file(editor->baton, relpath,
checksum, contents, props,
replaces_rev, editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_add_symlink(svn_editor_t *editor,
const char *relpath,
const char *target,
apr_hash_t *props,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT(props != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_add_symlink)
{
START_CALLBACK(editor);
err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
replaces_rev, editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_add_absent(svn_editor_t *editor,
const char *relpath,
svn_node_kind_t kind,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHECK_UNKNOWN_CHILD(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_add_absent)
{
START_CALLBACK(editor);
err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
replaces_rev, editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
CLEAR_INCOMPLETE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_alter_directory(svn_editor_t *editor,
const char *relpath,
svn_revnum_t revision,
const apr_array_header_t *children,
apr_hash_t *props)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT(children != NULL || props != NULL);
/* ### validate children are just basenames? */
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_alter_directory)
{
START_CALLBACK(editor);
err = editor->funcs.cb_alter_directory(editor->baton,
relpath, revision,
children, props,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
#ifdef ENABLE_ORDERING_CHECK
/* ### this is not entirely correct. we probably need to adjust the
### check_unknown_child() function for this scenario. */
#if 0
{
int i;
for (i = 0; i < children->nelts; i++)
{
const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
const char *child = svn_relpath_join(relpath, child_basename,
editor->state_pool);
apr_hash_set(editor->pending_incomplete_children, child,
APR_HASH_KEY_STRING, "");
/* Perhaps MARK_ALLOW_ADD(editor, child); ? */
}
}
#endif
#endif
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_alter_file(svn_editor_t *editor,
const char *relpath,
svn_revnum_t revision,
const svn_checksum_t *checksum,
svn_stream_t *contents,
apr_hash_t *props)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
|| (checksum == NULL && contents == NULL));
SVN_ERR_ASSERT(props != NULL || checksum != NULL);
if (checksum)
SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_alter_file)
{
START_CALLBACK(editor);
err = editor->funcs.cb_alter_file(editor->baton,
relpath, revision,
checksum, contents, props,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_alter_symlink(svn_editor_t *editor,
const char *relpath,
svn_revnum_t revision,
const char *target,
apr_hash_t *props)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SVN_ERR_ASSERT(props != NULL || target != NULL);
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ALTER(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_alter_symlink)
{
START_CALLBACK(editor);
err = editor->funcs.cb_alter_symlink(editor->baton,
relpath, revision,
target, props,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_delete(svn_editor_t *editor,
const char *relpath,
svn_revnum_t revision)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_NOT_BE_COMPLETED(editor, relpath);
VERIFY_PARENT_MAY_EXIST(editor, relpath);
CHILD_DELETIONS_ALLOWED(editor, relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_delete)
{
START_CALLBACK(editor);
err = editor->funcs.cb_delete(editor->baton, relpath, revision,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_COMPLETED(editor, relpath);
MARK_PARENT_STABLE(editor, relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_copy(svn_editor_t *editor,
const char *src_relpath,
svn_revnum_t src_revision,
const char *dst_relpath,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_ALLOW_ADD(editor, dst_relpath);
VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_copy)
{
START_CALLBACK(editor);
err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
dst_relpath, replaces_rev,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_ALLOW_ALTER(editor, dst_relpath);
MARK_PARENT_STABLE(editor, dst_relpath);
CLEAR_INCOMPLETE(editor, dst_relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_move(svn_editor_t *editor,
const char *src_relpath,
svn_revnum_t src_revision,
const char *dst_relpath,
svn_revnum_t replaces_rev)
{
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
SHOULD_NOT_BE_FINISHED(editor);
SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
SHOULD_ALLOW_ADD(editor, dst_relpath);
VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
CHILD_DELETIONS_ALLOWED(editor, src_relpath);
VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
SVN_ERR(check_cancel(editor));
if (editor->funcs.cb_move)
{
START_CALLBACK(editor);
err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
dst_relpath, replaces_rev,
editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_ALLOW_ADD(editor, src_relpath);
MARK_PARENT_STABLE(editor, src_relpath);
MARK_ALLOW_ALTER(editor, dst_relpath);
MARK_PARENT_STABLE(editor, dst_relpath);
CLEAR_INCOMPLETE(editor, dst_relpath);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_complete(svn_editor_t *editor)
{
svn_error_t *err = SVN_NO_ERROR;
SHOULD_NOT_BE_FINISHED(editor);
#ifdef ENABLE_ORDERING_CHECK
SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
#endif
if (editor->funcs.cb_complete)
{
START_CALLBACK(editor);
err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_FINISHED(editor);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}
svn_error_t *
svn_editor_abort(svn_editor_t *editor)
{
svn_error_t *err = SVN_NO_ERROR;
SHOULD_NOT_BE_FINISHED(editor);
if (editor->funcs.cb_abort)
{
START_CALLBACK(editor);
err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
END_CALLBACK(editor);
}
MARK_FINISHED(editor);
svn_pool_clear(editor->scratch_pool);
return svn_error_trace(err);
}