| /* |
| * 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 <assert.h> |
| #include <apr_pools.h> |
| |
| #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); |
| } |
| |
| void |
| 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); |
| } |
| |
| 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; |
| } |
| |