diff options
Diffstat (limited to 'subversion/libsvn_repos/node_tree.c')
-rw-r--r-- | subversion/libsvn_repos/node_tree.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/subversion/libsvn_repos/node_tree.c b/subversion/libsvn_repos/node_tree.c new file mode 100644 index 0000000..bd947b0 --- /dev/null +++ b/subversion/libsvn_repos/node_tree.c @@ -0,0 +1,431 @@ +/* + * node_tree.c: an editor for tracking repository deltas changes + * + * ==================================================================== + * 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 <stdio.h> + +#define APR_WANT_STRFUNC +#include <apr_want.h> +#include <apr_pools.h> + +#include "svn_types.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_delta.h" +#include "svn_fs.h" +#include "svn_repos.h" +#include "repos.h" +#include "svn_private_config.h" +#include "private/svn_fspath.h" + +/*** NOTE: This editor is unique in that it currently is hard-coded to + be anchored at the root directory of the filesystem. This + affords us the ability to use the same paths for filesystem + locations and editor paths. ***/ + + + +/*** Node creation and assembly structures and routines. ***/ +static svn_repos_node_t * +create_node(const char *name, + svn_repos_node_t *parent, + apr_pool_t *pool) +{ + svn_repos_node_t *node = apr_pcalloc(pool, sizeof(*node)); + node->action = 'R'; + node->kind = svn_node_unknown; + node->name = apr_pstrdup(pool, name); + node->parent = parent; + return node; +} + + +static svn_repos_node_t * +create_sibling_node(svn_repos_node_t *elder, + const char *name, + apr_pool_t *pool) +{ + svn_repos_node_t *tmp_node; + + /* No ELDER sibling? That's just not gonna work out. */ + if (! elder) + return NULL; + + /* Run to the end of the list of siblings of ELDER. */ + tmp_node = elder; + while (tmp_node->sibling) + tmp_node = tmp_node->sibling; + + /* Create a new youngest sibling and return that. */ + return (tmp_node->sibling = create_node(name, elder->parent, pool)); +} + + +static svn_repos_node_t * +create_child_node(svn_repos_node_t *parent, + const char *name, + apr_pool_t *pool) +{ + /* No PARENT node? That's just not gonna work out. */ + if (! parent) + return NULL; + + /* If PARENT has no children, create its first one and return that. */ + if (! parent->child) + return (parent->child = create_node(name, parent, pool)); + + /* If PARENT already has a child, create a new sibling for its first + child and return that. */ + return create_sibling_node(parent->child, name, pool); +} + + +static svn_repos_node_t * +find_child_by_name(svn_repos_node_t *parent, + const char *name) +{ + svn_repos_node_t *tmp_node; + + /* No PARENT node, or a barren PARENT? Nothing to find. */ + if ((! parent) || (! parent->child)) + return NULL; + + /* Look through the children for a node with a matching name. */ + tmp_node = parent->child; + while (1) + { + if (! strcmp(tmp_node->name, name)) + { + return tmp_node; + } + else + { + if (tmp_node->sibling) + tmp_node = tmp_node->sibling; + else + break; + } + } + + return NULL; +} + + +static void +find_real_base_location(const char **path_p, + svn_revnum_t *rev_p, + svn_repos_node_t *node, + apr_pool_t *pool) +{ + /* If NODE is an add-with-history, then its real base location is + the copy source. */ + if ((node->action == 'A') + && node->copyfrom_path + && SVN_IS_VALID_REVNUM(node->copyfrom_rev)) + { + *path_p = node->copyfrom_path; + *rev_p = node->copyfrom_rev; + return; + } + + /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's + name to whatever the parent's real base path turns out to be (and + pass the base revision on through). */ + if (node->parent) + { + const char *path; + svn_revnum_t rev; + + find_real_base_location(&path, &rev, node->parent, pool); + *path_p = svn_fspath__join(path, node->name, pool); + *rev_p = rev; + return; + } + + /* Finally, if the node has no parent, then its name is "/", and it + has no interesting base revision. */ + *path_p = "/"; + *rev_p = SVN_INVALID_REVNUM; + return; +} + + + + +/*** Editor functions and batons. ***/ + +struct edit_baton +{ + svn_fs_t *fs; + svn_fs_root_t *root; + svn_fs_root_t *base_root; + apr_pool_t *node_pool; + svn_repos_node_t *node; +}; + + +struct node_baton +{ + struct edit_baton *edit_baton; + struct node_baton *parent_baton; + svn_repos_node_t *node; +}; + + +static svn_error_t * +delete_entry(const char *path, + svn_revnum_t revision, + void *parent_baton, + apr_pool_t *pool) +{ + struct node_baton *d = parent_baton; + struct edit_baton *eb = d->edit_baton; + svn_repos_node_t *node; + const char *name; + const char *base_path; + svn_revnum_t base_rev; + svn_fs_root_t *base_root; + svn_node_kind_t kind; + + /* Get (or create) the change node and update it. */ + name = svn_relpath_basename(path, pool); + node = find_child_by_name(d->node, name); + if (! node) + node = create_child_node(d->node, name, eb->node_pool); + node->action = 'D'; + + /* We need to look up this node's parents to see what its original + path in the filesystem was. Why? Because if this deletion + occurred underneath a copied path, the thing that was deleted + probably lived at a different location (relative to the copy + source). */ + find_real_base_location(&base_path, &base_rev, node, pool); + if (! SVN_IS_VALID_REVNUM(base_rev)) + { + /* No interesting base revision? We'll just look for the path + in our base root. */ + base_root = eb->base_root; + } + else + { + /* Oh. Perhaps some copy goodness happened somewhere? */ + SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool)); + } + + /* Now figure out if this thing was a file or a dir. */ + SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool)); + if (kind == svn_node_none) + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("'%s' not found in filesystem"), path); + node->kind = kind; + + return SVN_NO_ERROR; +} + + + +static svn_error_t * +add_open_helper(const char *path, + char action, + svn_node_kind_t kind, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev, + apr_pool_t *pool, + void **child_baton) +{ + struct node_baton *pb = parent_baton; + struct edit_baton *eb = pb->edit_baton; + struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb)); + + SVN_ERR_ASSERT(parent_baton && path); + + nb->edit_baton = eb; + nb->parent_baton = pb; + + /* Create and populate the node. */ + nb->node = create_child_node(pb->node, svn_relpath_basename(path, NULL), + eb->node_pool); + nb->node->kind = kind; + nb->node->action = action; + nb->node->copyfrom_rev = copyfrom_rev; + nb->node->copyfrom_path = + copyfrom_path ? apr_pstrdup(eb->node_pool, copyfrom_path) : NULL; + + *child_baton = nb; + return SVN_NO_ERROR; +} + + +static svn_error_t * +open_root(void *edit_baton, + svn_revnum_t base_revision, + apr_pool_t *pool, + void **root_baton) +{ + struct edit_baton *eb = edit_baton; + struct node_baton *d = apr_pcalloc(pool, sizeof(*d)); + + d->edit_baton = eb; + d->parent_baton = NULL; + d->node = (eb->node = create_node("", NULL, eb->node_pool)); + d->node->kind = svn_node_dir; + d->node->action = 'R'; + *root_baton = d; + + return SVN_NO_ERROR; +} + + +static svn_error_t * +open_directory(const char *path, + void *parent_baton, + svn_revnum_t base_revision, + apr_pool_t *pool, + void **child_baton) +{ + return add_open_helper(path, 'R', svn_node_dir, parent_baton, + NULL, SVN_INVALID_REVNUM, + pool, child_baton); +} + + +static svn_error_t * +add_directory(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *pool, + void **child_baton) +{ + return add_open_helper(path, 'A', svn_node_dir, parent_baton, + copyfrom_path, copyfrom_revision, + pool, child_baton); +} + + +static svn_error_t * +open_file(const char *path, + void *parent_baton, + svn_revnum_t base_revision, + apr_pool_t *pool, + void **file_baton) +{ + return add_open_helper(path, 'R', svn_node_file, parent_baton, + NULL, SVN_INVALID_REVNUM, + pool, file_baton); +} + + +static svn_error_t * +add_file(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *pool, + void **file_baton) +{ + return add_open_helper(path, 'A', svn_node_file, parent_baton, + copyfrom_path, copyfrom_revision, + pool, file_baton); +} + + +static svn_error_t * +apply_textdelta(void *file_baton, + const char *base_checksum, + apr_pool_t *pool, + svn_txdelta_window_handler_t *handler, + void **handler_baton) +{ + struct node_baton *fb = file_baton; + fb->node->text_mod = TRUE; + *handler = svn_delta_noop_window_handler; + *handler_baton = NULL; + return SVN_NO_ERROR; +} + + + +static svn_error_t * +change_node_prop(void *node_baton, + const char *name, + const svn_string_t *value, + apr_pool_t *pool) +{ + struct node_baton *nb = node_baton; + nb->node->prop_mod = TRUE; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_repos_node_editor(const svn_delta_editor_t **editor, + void **edit_baton, + svn_repos_t *repos, + svn_fs_root_t *base_root, + svn_fs_root_t *root, + apr_pool_t *node_pool, + apr_pool_t *pool) +{ + svn_delta_editor_t *my_editor; + struct edit_baton *my_edit_baton; + + /* Set up the editor. */ + my_editor = svn_delta_default_editor(pool); + my_editor->open_root = open_root; + my_editor->delete_entry = delete_entry; + my_editor->add_directory = add_directory; + my_editor->open_directory = open_directory; + my_editor->add_file = add_file; + my_editor->open_file = open_file; + my_editor->apply_textdelta = apply_textdelta; + my_editor->change_file_prop = change_node_prop; + my_editor->change_dir_prop = change_node_prop; + + /* Set up the edit baton. */ + my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton)); + my_edit_baton->node_pool = node_pool; + my_edit_baton->fs = repos->fs; + my_edit_baton->root = root; + my_edit_baton->base_root = base_root; + + *editor = my_editor; + *edit_baton = my_edit_baton; + + return SVN_NO_ERROR; +} + + + +svn_repos_node_t * +svn_repos_node_from_baton(void *edit_baton) +{ + struct edit_baton *eb = edit_baton; + return eb->node; +} |