diff options
Diffstat (limited to 'subversion/libsvn_delta/editor.c')
-rw-r--r-- | subversion/libsvn_delta/editor.c | 956 |
1 files changed, 956 insertions, 0 deletions
diff --git a/subversion/libsvn_delta/editor.c b/subversion/libsvn_delta/editor.c new file mode 100644 index 0000000..1dc94b2 --- /dev/null +++ b/subversion/libsvn_delta/editor.c @@ -0,0 +1,956 @@ +/* + * 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_rotate(svn_editor_t *editor, + svn_editor_cb_rotate_t callback, + apr_pool_t *scratch_pool) +{ + editor->funcs.cb_rotate = 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_rotate); + 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, + apr_hash_t *props, + const svn_checksum_t *checksum, + svn_stream_t *contents) +{ + 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, props, + checksum, contents, + 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, + apr_hash_t *props, + const char *target) +{ + 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, props, + target, + 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_rotate(svn_editor_t *editor, + const apr_array_header_t *relpaths, + const apr_array_header_t *revisions) +{ + svn_error_t *err = SVN_NO_ERROR; + + SHOULD_NOT_BE_FINISHED(editor); +#ifdef ENABLE_ORDERING_CHECK + { + int i; + for (i = 0; i < relpaths->nelts; i++) + { + const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); + + SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); + SHOULD_NOT_BE_COMPLETED(editor, relpath); + VERIFY_PARENT_MAY_EXIST(editor, relpath); + CHILD_DELETIONS_ALLOWED(editor, relpath); + } + } +#endif + + SVN_ERR(check_cancel(editor)); + + if (editor->funcs.cb_rotate) + { + START_CALLBACK(editor); + err = editor->funcs.cb_rotate(editor->baton, relpaths, revisions, + editor->scratch_pool); + END_CALLBACK(editor); + } + +#ifdef ENABLE_ORDERING_CHECK + { + int i; + for (i = 0; i < relpaths->nelts; i++) + { + const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); + MARK_ALLOW_ALTER(editor, relpath); + MARK_PARENT_STABLE(editor, relpath); + } + } +#endif + + 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); +} |