/* * element.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 #include #include "svn_types.h" #include "svn_error.h" #include "svn_string.h" #include "svn_props.h" #include "svn_dirent_uri.h" #include "svn_iter.h" #include "private/svn_sorts_private.h" #include "private/svn_element.h" #include "svn_private_config.h" void * svn_eid__hash_get(apr_hash_t *ht, int key) { return apr_hash_get(ht, &key, sizeof(key)); } void svn_eid__hash_set(apr_hash_t *ht, int key, const void *val) { int *id_p = apr_pmemdup(apr_hash_pool_get(ht), &key, sizeof(key)); apr_hash_set(ht, id_p, sizeof(key), val); } int svn_eid__hash_this_key(apr_hash_index_t *hi) { return *(const int *)apr_hash_this_key(hi); } svn_eid__hash_iter_t * svn_eid__hash_sorted_first(apr_pool_t *pool, apr_hash_t *ht, int (*comparison_func)(const svn_sort__item_t *, const svn_sort__item_t *)) { svn_eid__hash_iter_t *hi = apr_palloc(pool, sizeof(*hi)); if (apr_hash_count(ht) == 0) return NULL; hi->array = svn_sort__hash(ht, comparison_func, pool); hi->i = 0; hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).key); hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; return hi; } svn_eid__hash_iter_t * svn_eid__hash_sorted_next(svn_eid__hash_iter_t *hi) { hi->i++; if (hi->i >= hi->array->nelts) { return NULL; } hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).key); hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; return hi; } int svn_eid__hash_sort_compare_items_by_eid(const svn_sort__item_t *a, const svn_sort__item_t *b) { int eid_a = *(const int *)a->key; int eid_b = *(const int *)b->key; return eid_a - eid_b; } /* * =================================================================== * Element payload * =================================================================== */ svn_boolean_t svn_element__payload_invariants(const svn_element__payload_t *payload) { if (payload->is_subbranch_root) return TRUE; /* If kind is unknown, it's a reference; otherwise it has content specified and may also have a reference. */ if (payload->kind == svn_node_unknown) if (SVN_IS_VALID_REVNUM(payload->branch_ref.rev) && payload->branch_ref.branch_id && payload->branch_ref.eid != -1) return TRUE; if ((payload->kind == svn_node_dir || payload->kind == svn_node_file || payload->kind == svn_node_symlink) && (payload->props && ((payload->kind == svn_node_file) == !!payload->text) && ((payload->kind == svn_node_symlink) == !!payload->target))) return TRUE; return FALSE; } svn_element__payload_t * svn_element__payload_dup(const svn_element__payload_t *old, apr_pool_t *result_pool) { svn_element__payload_t *new_payload; assert(! old || svn_element__payload_invariants(old)); if (old == NULL) return NULL; new_payload = apr_pmemdup(result_pool, old, sizeof(*new_payload)); if (old->branch_ref.branch_id) new_payload->branch_ref.branch_id = apr_pstrdup(result_pool, old->branch_ref.branch_id); if (old->props) new_payload->props = svn_prop_hash_dup(old->props, result_pool); if (old->kind == svn_node_file && old->text) new_payload->text = svn_stringbuf_dup(old->text, result_pool); if (old->kind == svn_node_symlink && old->target) new_payload->target = apr_pstrdup(result_pool, old->target); return new_payload; } svn_boolean_t svn_element__payload_equal(const svn_element__payload_t *left, const svn_element__payload_t *right, apr_pool_t *scratch_pool) { apr_array_header_t *prop_diffs; assert(svn_element__payload_invariants(left)); assert(svn_element__payload_invariants(right)); /* any two subbranch-root elements compare equal */ if (left->is_subbranch_root && right->is_subbranch_root) { return TRUE; } else if (left->is_subbranch_root || right->is_subbranch_root) { return FALSE; } /* content defined only by reference is not supported */ SVN_ERR_ASSERT_NO_RETURN(left->kind != svn_node_unknown && right->kind != svn_node_unknown); if (left->kind != right->kind) { return FALSE; } svn_error_clear(svn_prop_diffs(&prop_diffs, left->props, right->props, scratch_pool)); if (prop_diffs->nelts != 0) { return FALSE; } switch (left->kind) { case svn_node_dir: break; case svn_node_file: if (! svn_stringbuf_compare(left->text, right->text)) { return FALSE; } break; case svn_node_symlink: if (strcmp(left->target, right->target) != 0) { return FALSE; } break; default: break; } return TRUE; } svn_element__payload_t * svn_element__payload_create_subbranch(apr_pool_t *result_pool) { svn_element__payload_t *new_payload = apr_pcalloc(result_pool, sizeof(*new_payload)); new_payload->pool = result_pool; new_payload->is_subbranch_root = TRUE; assert(svn_element__payload_invariants(new_payload)); return new_payload; } svn_element__payload_t * svn_element__payload_create_ref(svn_revnum_t rev, const char *branch_id, int eid, apr_pool_t *result_pool) { svn_element__payload_t *new_payload = apr_pcalloc(result_pool, sizeof(*new_payload)); new_payload->pool = result_pool; new_payload->kind = svn_node_unknown; new_payload->branch_ref.rev = rev; new_payload->branch_ref.branch_id = apr_pstrdup(result_pool, branch_id); new_payload->branch_ref.eid = eid; assert(svn_element__payload_invariants(new_payload)); return new_payload; } svn_element__payload_t * svn_element__payload_create_dir(apr_hash_t *props, apr_pool_t *result_pool) { svn_element__payload_t *new_payload = apr_pcalloc(result_pool, sizeof(*new_payload)); new_payload->pool = result_pool; new_payload->kind = svn_node_dir; new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; assert(svn_element__payload_invariants(new_payload)); return new_payload; } svn_element__payload_t * svn_element__payload_create_file(apr_hash_t *props, svn_stringbuf_t *text, apr_pool_t *result_pool) { svn_element__payload_t *new_payload = apr_pcalloc(result_pool, sizeof(*new_payload)); SVN_ERR_ASSERT_NO_RETURN(text); new_payload->pool = result_pool; new_payload->kind = svn_node_file; new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; new_payload->text = svn_stringbuf_dup(text, result_pool); assert(svn_element__payload_invariants(new_payload)); return new_payload; } svn_element__payload_t * svn_element__payload_create_symlink(apr_hash_t *props, const char *target, apr_pool_t *result_pool) { svn_element__payload_t *new_payload = apr_pcalloc(result_pool, sizeof(*new_payload)); SVN_ERR_ASSERT_NO_RETURN(target); new_payload->pool = result_pool; new_payload->kind = svn_node_symlink; new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; new_payload->target = apr_pstrdup(result_pool, target); assert(svn_element__payload_invariants(new_payload)); return new_payload; } svn_element__content_t * svn_element__content_create(int parent_eid, const char *name, const svn_element__payload_t *payload, apr_pool_t *result_pool) { svn_element__content_t *content = apr_palloc(result_pool, sizeof(*content)); content->parent_eid = parent_eid; content->name = apr_pstrdup(result_pool, name); content->payload = svn_element__payload_dup(payload, result_pool); return content; } svn_element__content_t * svn_element__content_dup(const svn_element__content_t *old, apr_pool_t *result_pool) { svn_element__content_t *content = apr_pmemdup(result_pool, old, sizeof(*content)); content->name = apr_pstrdup(result_pool, old->name); content->payload = svn_element__payload_dup(old->payload, result_pool); return content; } svn_boolean_t svn_element__content_equal(const svn_element__content_t *content_left, const svn_element__content_t *content_right, apr_pool_t *scratch_pool) { if (!content_left && !content_right) { return TRUE; } else if (!content_left || !content_right) { return FALSE; } if (content_left->parent_eid != content_right->parent_eid) { return FALSE; } if (strcmp(content_left->name, content_right->name) != 0) { return FALSE; } if (! svn_element__payload_equal(content_left->payload, content_right->payload, scratch_pool)) { return FALSE; } return TRUE; } svn_element__tree_t * svn_element__tree_create(apr_hash_t *e_map, int root_eid, apr_pool_t *result_pool) { svn_element__tree_t *element_tree = apr_pcalloc(result_pool, sizeof(*element_tree)); element_tree->e_map = e_map ? apr_hash_copy(result_pool, e_map) : apr_hash_make(result_pool); element_tree->root_eid = root_eid; return element_tree; } svn_element__content_t * svn_element__tree_get(const svn_element__tree_t *tree, int eid) { return svn_eid__hash_get(tree->e_map, eid); } svn_error_t * svn_element__tree_set(svn_element__tree_t *tree, int eid, const svn_element__content_t *element) { svn_eid__hash_set(tree->e_map, eid, element); return SVN_NO_ERROR; } void svn_element__tree_purge_orphans(apr_hash_t *e_map, int root_eid, apr_pool_t *scratch_pool) { apr_hash_index_t *hi; svn_boolean_t changed; SVN_ERR_ASSERT_NO_RETURN(svn_eid__hash_get(e_map, root_eid)); do { changed = FALSE; for (hi = apr_hash_first(scratch_pool, e_map); hi; hi = apr_hash_next(hi)) { int this_eid = svn_eid__hash_this_key(hi); svn_element__content_t *this_element = apr_hash_this_val(hi); if (this_eid != root_eid) { svn_element__content_t *parent_element = svn_eid__hash_get(e_map, this_element->parent_eid); /* Purge if parent is deleted */ if (! parent_element) { svn_eid__hash_set(e_map, this_eid, NULL); changed = TRUE; } else SVN_ERR_ASSERT_NO_RETURN( ! parent_element->payload->is_subbranch_root); } } } while (changed); } const char * svn_element__tree_get_path_by_eid(const svn_element__tree_t *tree, int eid, apr_pool_t *result_pool) { const char *path = ""; svn_element__content_t *element; for (; eid != tree->root_eid; eid = element->parent_eid) { element = svn_element__tree_get(tree, eid); if (! element) return NULL; path = svn_relpath_join(element->name, path, result_pool); } SVN_ERR_ASSERT_NO_RETURN(eid == tree->root_eid); return path; } svn_element__tree_t * svn_element__tree_get_subtree_at_eid(svn_element__tree_t *element_tree, int eid, apr_pool_t *result_pool) { svn_element__tree_t *new_subtree; svn_element__content_t *subtree_root_element; new_subtree = svn_element__tree_create(element_tree->e_map, eid, result_pool); /* Purge orphans */ svn_element__tree_purge_orphans(new_subtree->e_map, new_subtree->root_eid, result_pool); /* Remove 'parent' and 'name' attributes from subtree root element */ subtree_root_element = svn_element__tree_get(new_subtree, new_subtree->root_eid); svn_element__tree_set(new_subtree, new_subtree->root_eid, svn_element__content_create( -1, "", subtree_root_element->payload, result_pool)); return new_subtree; }