element.c revision 362181
1/* 2 * element.c : editing trees of versioned resources 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include <assert.h> 25#include <apr_pools.h> 26 27#include "svn_types.h" 28#include "svn_error.h" 29#include "svn_string.h" 30#include "svn_props.h" 31#include "svn_dirent_uri.h" 32#include "svn_iter.h" 33#include "private/svn_sorts_private.h" 34 35#include "private/svn_element.h" 36#include "svn_private_config.h" 37 38 39void * 40svn_eid__hash_get(apr_hash_t *ht, 41 int key) 42{ 43 return apr_hash_get(ht, &key, sizeof(key)); 44} 45 46void 47svn_eid__hash_set(apr_hash_t *ht, 48 int key, 49 const void *val) 50{ 51 int *id_p = apr_pmemdup(apr_hash_pool_get(ht), &key, sizeof(key)); 52 53 apr_hash_set(ht, id_p, sizeof(key), val); 54} 55 56int 57svn_eid__hash_this_key(apr_hash_index_t *hi) 58{ 59 return *(const int *)apr_hash_this_key(hi); 60} 61 62svn_eid__hash_iter_t * 63svn_eid__hash_sorted_first(apr_pool_t *pool, 64 apr_hash_t *ht, 65 int (*comparison_func)(const svn_sort__item_t *, 66 const svn_sort__item_t *)) 67{ 68 svn_eid__hash_iter_t *hi = apr_palloc(pool, sizeof(*hi)); 69 70 if (apr_hash_count(ht) == 0) 71 return NULL; 72 73 hi->array = svn_sort__hash(ht, comparison_func, pool); 74 hi->i = 0; 75 hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i, 76 svn_sort__item_t).key); 77 hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; 78 return hi; 79} 80 81svn_eid__hash_iter_t * 82svn_eid__hash_sorted_next(svn_eid__hash_iter_t *hi) 83{ 84 hi->i++; 85 if (hi->i >= hi->array->nelts) 86 { 87 return NULL; 88 } 89 hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i, 90 svn_sort__item_t).key); 91 hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value; 92 return hi; 93} 94 95int 96svn_eid__hash_sort_compare_items_by_eid(const svn_sort__item_t *a, 97 const svn_sort__item_t *b) 98{ 99 int eid_a = *(const int *)a->key; 100 int eid_b = *(const int *)b->key; 101 102 return eid_a - eid_b; 103} 104 105 106/* 107 * =================================================================== 108 * Element payload 109 * =================================================================== 110 */ 111 112svn_boolean_t 113svn_element__payload_invariants(const svn_element__payload_t *payload) 114{ 115 if (payload->is_subbranch_root) 116 return TRUE; 117 118 /* If kind is unknown, it's a reference; otherwise it has content 119 specified and may also have a reference. */ 120 if (payload->kind == svn_node_unknown) 121 if (SVN_IS_VALID_REVNUM(payload->branch_ref.rev) 122 && payload->branch_ref.branch_id 123 && payload->branch_ref.eid != -1) 124 return TRUE; 125 if ((payload->kind == svn_node_dir 126 || payload->kind == svn_node_file 127 || payload->kind == svn_node_symlink) 128 && (payload->props 129 && ((payload->kind == svn_node_file) == !!payload->text) 130 && ((payload->kind == svn_node_symlink) == !!payload->target))) 131 return TRUE; 132 return FALSE; 133} 134 135svn_element__payload_t * 136svn_element__payload_dup(const svn_element__payload_t *old, 137 apr_pool_t *result_pool) 138{ 139 svn_element__payload_t *new_payload; 140 141 assert(! old || svn_element__payload_invariants(old)); 142 143 if (old == NULL) 144 return NULL; 145 146 new_payload = apr_pmemdup(result_pool, old, sizeof(*new_payload)); 147 if (old->branch_ref.branch_id) 148 new_payload->branch_ref.branch_id 149 = apr_pstrdup(result_pool, old->branch_ref.branch_id); 150 if (old->props) 151 new_payload->props = svn_prop_hash_dup(old->props, result_pool); 152 if (old->kind == svn_node_file && old->text) 153 new_payload->text = svn_stringbuf_dup(old->text, result_pool); 154 if (old->kind == svn_node_symlink && old->target) 155 new_payload->target = apr_pstrdup(result_pool, old->target); 156 return new_payload; 157} 158 159svn_boolean_t 160svn_element__payload_equal(const svn_element__payload_t *left, 161 const svn_element__payload_t *right, 162 apr_pool_t *scratch_pool) 163{ 164 apr_array_header_t *prop_diffs; 165 166 assert(svn_element__payload_invariants(left)); 167 assert(svn_element__payload_invariants(right)); 168 169 /* any two subbranch-root elements compare equal */ 170 if (left->is_subbranch_root && right->is_subbranch_root) 171 { 172 return TRUE; 173 } 174 else if (left->is_subbranch_root || right->is_subbranch_root) 175 { 176 return FALSE; 177 } 178 179 /* content defined only by reference is not supported */ 180 SVN_ERR_ASSERT_NO_RETURN(left->kind != svn_node_unknown 181 && right->kind != svn_node_unknown); 182 183 if (left->kind != right->kind) 184 { 185 return FALSE; 186 } 187 188 svn_error_clear(svn_prop_diffs(&prop_diffs, 189 left->props, right->props, 190 scratch_pool)); 191 192 if (prop_diffs->nelts != 0) 193 { 194 return FALSE; 195 } 196 switch (left->kind) 197 { 198 case svn_node_dir: 199 break; 200 case svn_node_file: 201 if (! svn_stringbuf_compare(left->text, right->text)) 202 { 203 return FALSE; 204 } 205 break; 206 case svn_node_symlink: 207 if (strcmp(left->target, right->target) != 0) 208 { 209 return FALSE; 210 } 211 break; 212 default: 213 break; 214 } 215 216 return TRUE; 217} 218 219svn_element__payload_t * 220svn_element__payload_create_subbranch(apr_pool_t *result_pool) 221{ 222 svn_element__payload_t *new_payload 223 = apr_pcalloc(result_pool, sizeof(*new_payload)); 224 225 new_payload->pool = result_pool; 226 new_payload->is_subbranch_root = TRUE; 227 assert(svn_element__payload_invariants(new_payload)); 228 return new_payload; 229} 230 231svn_element__payload_t * 232svn_element__payload_create_ref(svn_revnum_t rev, 233 const char *branch_id, 234 int eid, 235 apr_pool_t *result_pool) 236{ 237 svn_element__payload_t *new_payload 238 = apr_pcalloc(result_pool, sizeof(*new_payload)); 239 240 new_payload->pool = result_pool; 241 new_payload->kind = svn_node_unknown; 242 new_payload->branch_ref.rev = rev; 243 new_payload->branch_ref.branch_id = apr_pstrdup(result_pool, branch_id); 244 new_payload->branch_ref.eid = eid; 245 assert(svn_element__payload_invariants(new_payload)); 246 return new_payload; 247} 248 249svn_element__payload_t * 250svn_element__payload_create_dir(apr_hash_t *props, 251 apr_pool_t *result_pool) 252{ 253 svn_element__payload_t *new_payload 254 = apr_pcalloc(result_pool, sizeof(*new_payload)); 255 256 new_payload->pool = result_pool; 257 new_payload->kind = svn_node_dir; 258 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; 259 assert(svn_element__payload_invariants(new_payload)); 260 return new_payload; 261} 262 263svn_element__payload_t * 264svn_element__payload_create_file(apr_hash_t *props, 265 svn_stringbuf_t *text, 266 apr_pool_t *result_pool) 267{ 268 svn_element__payload_t *new_payload 269 = apr_pcalloc(result_pool, sizeof(*new_payload)); 270 271 SVN_ERR_ASSERT_NO_RETURN(text); 272 273 new_payload->pool = result_pool; 274 new_payload->kind = svn_node_file; 275 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; 276 new_payload->text = svn_stringbuf_dup(text, result_pool); 277 assert(svn_element__payload_invariants(new_payload)); 278 return new_payload; 279} 280 281svn_element__payload_t * 282svn_element__payload_create_symlink(apr_hash_t *props, 283 const char *target, 284 apr_pool_t *result_pool) 285{ 286 svn_element__payload_t *new_payload 287 = apr_pcalloc(result_pool, sizeof(*new_payload)); 288 289 SVN_ERR_ASSERT_NO_RETURN(target); 290 291 new_payload->pool = result_pool; 292 new_payload->kind = svn_node_symlink; 293 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL; 294 new_payload->target = apr_pstrdup(result_pool, target); 295 assert(svn_element__payload_invariants(new_payload)); 296 return new_payload; 297} 298 299svn_element__content_t * 300svn_element__content_create(int parent_eid, 301 const char *name, 302 const svn_element__payload_t *payload, 303 apr_pool_t *result_pool) 304{ 305 svn_element__content_t *content 306 = apr_palloc(result_pool, sizeof(*content)); 307 308 content->parent_eid = parent_eid; 309 content->name = apr_pstrdup(result_pool, name); 310 content->payload = svn_element__payload_dup(payload, result_pool); 311 return content; 312} 313 314svn_element__content_t * 315svn_element__content_dup(const svn_element__content_t *old, 316 apr_pool_t *result_pool) 317{ 318 svn_element__content_t *content 319 = apr_pmemdup(result_pool, old, sizeof(*content)); 320 321 content->name = apr_pstrdup(result_pool, old->name); 322 content->payload = svn_element__payload_dup(old->payload, result_pool); 323 return content; 324} 325 326svn_boolean_t 327svn_element__content_equal(const svn_element__content_t *content_left, 328 const svn_element__content_t *content_right, 329 apr_pool_t *scratch_pool) 330{ 331 if (!content_left && !content_right) 332 { 333 return TRUE; 334 } 335 else if (!content_left || !content_right) 336 { 337 return FALSE; 338 } 339 340 if (content_left->parent_eid != content_right->parent_eid) 341 { 342 return FALSE; 343 } 344 if (strcmp(content_left->name, content_right->name) != 0) 345 { 346 return FALSE; 347 } 348 if (! svn_element__payload_equal(content_left->payload, content_right->payload, 349 scratch_pool)) 350 { 351 return FALSE; 352 } 353 354 return TRUE; 355} 356 357svn_element__tree_t * 358svn_element__tree_create(apr_hash_t *e_map, 359 int root_eid, 360 apr_pool_t *result_pool) 361{ 362 svn_element__tree_t *element_tree 363 = apr_pcalloc(result_pool, sizeof(*element_tree)); 364 365 element_tree->e_map = e_map ? apr_hash_copy(result_pool, e_map) 366 : apr_hash_make(result_pool); 367 element_tree->root_eid = root_eid; 368 return element_tree; 369} 370 371svn_element__content_t * 372svn_element__tree_get(const svn_element__tree_t *tree, 373 int eid) 374{ 375 return svn_eid__hash_get(tree->e_map, eid); 376} 377 378void 379svn_element__tree_set(svn_element__tree_t *tree, 380 int eid, 381 const svn_element__content_t *element) 382{ 383 svn_eid__hash_set(tree->e_map, eid, element); 384} 385 386void 387svn_element__tree_purge_orphans(apr_hash_t *e_map, 388 int root_eid, 389 apr_pool_t *scratch_pool) 390{ 391 apr_hash_index_t *hi; 392 svn_boolean_t changed; 393 394 SVN_ERR_ASSERT_NO_RETURN(svn_eid__hash_get(e_map, root_eid)); 395 396 do 397 { 398 changed = FALSE; 399 400 for (hi = apr_hash_first(scratch_pool, e_map); 401 hi; hi = apr_hash_next(hi)) 402 { 403 int this_eid = svn_eid__hash_this_key(hi); 404 svn_element__content_t *this_element = apr_hash_this_val(hi); 405 406 if (this_eid != root_eid) 407 { 408 svn_element__content_t *parent_element 409 = svn_eid__hash_get(e_map, this_element->parent_eid); 410 411 /* Purge if parent is deleted */ 412 if (! parent_element) 413 { 414 svn_eid__hash_set(e_map, this_eid, NULL); 415 changed = TRUE; 416 } 417 else 418 SVN_ERR_ASSERT_NO_RETURN( 419 ! parent_element->payload->is_subbranch_root); 420 } 421 } 422 } 423 while (changed); 424} 425 426const char * 427svn_element__tree_get_path_by_eid(const svn_element__tree_t *tree, 428 int eid, 429 apr_pool_t *result_pool) 430{ 431 const char *path = ""; 432 svn_element__content_t *element; 433 434 for (; eid != tree->root_eid; eid = element->parent_eid) 435 { 436 element = svn_element__tree_get(tree, eid); 437 if (! element) 438 return NULL; 439 path = svn_relpath_join(element->name, path, result_pool); 440 } 441 SVN_ERR_ASSERT_NO_RETURN(eid == tree->root_eid); 442 return path; 443} 444 445svn_element__tree_t * 446svn_element__tree_get_subtree_at_eid(svn_element__tree_t *element_tree, 447 int eid, 448 apr_pool_t *result_pool) 449{ 450 svn_element__tree_t *new_subtree; 451 svn_element__content_t *subtree_root_element; 452 453 new_subtree = svn_element__tree_create(element_tree->e_map, eid, 454 result_pool); 455 456 /* Purge orphans */ 457 svn_element__tree_purge_orphans(new_subtree->e_map, 458 new_subtree->root_eid, result_pool); 459 460 /* Remove 'parent' and 'name' attributes from subtree root element */ 461 subtree_root_element 462 = svn_element__tree_get(new_subtree, new_subtree->root_eid); 463 svn_element__tree_set(new_subtree, new_subtree->root_eid, 464 svn_element__content_create( 465 -1, "", subtree_root_element->payload, result_pool)); 466 467 return new_subtree; 468} 469 470