1/* 2 * node_tree.c: an editor for tracking repository deltas changes 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/* ==================================================================== */ 25 26 27 28 29#include <stdio.h> 30 31#define APR_WANT_STRFUNC 32#include <apr_want.h> 33#include <apr_pools.h> 34 35#include "svn_types.h" 36#include "svn_error.h" 37#include "svn_dirent_uri.h" 38#include "svn_path.h" 39#include "svn_delta.h" 40#include "svn_fs.h" 41#include "svn_repos.h" 42#include "repos.h" 43#include "svn_private_config.h" 44#include "private/svn_fspath.h" 45 46/*** NOTE: This editor is unique in that it currently is hard-coded to 47 be anchored at the root directory of the filesystem. This 48 affords us the ability to use the same paths for filesystem 49 locations and editor paths. ***/ 50 51 52 53/*** Node creation and assembly structures and routines. ***/ 54static svn_repos_node_t * 55create_node(const char *name, 56 svn_repos_node_t *parent, 57 apr_pool_t *pool) 58{ 59 svn_repos_node_t *node = apr_pcalloc(pool, sizeof(*node)); 60 node->action = 'R'; 61 node->kind = svn_node_unknown; 62 node->name = apr_pstrdup(pool, name); 63 node->parent = parent; 64 return node; 65} 66 67 68static svn_repos_node_t * 69create_sibling_node(svn_repos_node_t *elder, 70 const char *name, 71 apr_pool_t *pool) 72{ 73 svn_repos_node_t *tmp_node; 74 75 /* No ELDER sibling? That's just not gonna work out. */ 76 if (! elder) 77 return NULL; 78 79 /* Run to the end of the list of siblings of ELDER. */ 80 tmp_node = elder; 81 while (tmp_node->sibling) 82 tmp_node = tmp_node->sibling; 83 84 /* Create a new youngest sibling and return that. */ 85 return (tmp_node->sibling = create_node(name, elder->parent, pool)); 86} 87 88 89static svn_repos_node_t * 90create_child_node(svn_repos_node_t *parent, 91 const char *name, 92 apr_pool_t *pool) 93{ 94 /* No PARENT node? That's just not gonna work out. */ 95 if (! parent) 96 return NULL; 97 98 /* If PARENT has no children, create its first one and return that. */ 99 if (! parent->child) 100 return (parent->child = create_node(name, parent, pool)); 101 102 /* If PARENT already has a child, create a new sibling for its first 103 child and return that. */ 104 return create_sibling_node(parent->child, name, pool); 105} 106 107 108static svn_repos_node_t * 109find_child_by_name(svn_repos_node_t *parent, 110 const char *name) 111{ 112 svn_repos_node_t *tmp_node; 113 114 /* No PARENT node, or a barren PARENT? Nothing to find. */ 115 if ((! parent) || (! parent->child)) 116 return NULL; 117 118 /* Look through the children for a node with a matching name. */ 119 tmp_node = parent->child; 120 while (1) 121 { 122 if (! strcmp(tmp_node->name, name)) 123 { 124 return tmp_node; 125 } 126 else 127 { 128 if (tmp_node->sibling) 129 tmp_node = tmp_node->sibling; 130 else 131 break; 132 } 133 } 134 135 return NULL; 136} 137 138 139static void 140find_real_base_location(const char **path_p, 141 svn_revnum_t *rev_p, 142 svn_repos_node_t *node, 143 apr_pool_t *pool) 144{ 145 /* If NODE is an add-with-history, then its real base location is 146 the copy source. */ 147 if ((node->action == 'A') 148 && node->copyfrom_path 149 && SVN_IS_VALID_REVNUM(node->copyfrom_rev)) 150 { 151 *path_p = node->copyfrom_path; 152 *rev_p = node->copyfrom_rev; 153 return; 154 } 155 156 /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's 157 name to whatever the parent's real base path turns out to be (and 158 pass the base revision on through). */ 159 if (node->parent) 160 { 161 const char *path; 162 svn_revnum_t rev; 163 164 find_real_base_location(&path, &rev, node->parent, pool); 165 *path_p = svn_fspath__join(path, node->name, pool); 166 *rev_p = rev; 167 return; 168 } 169 170 /* Finally, if the node has no parent, then its name is "/", and it 171 has no interesting base revision. */ 172 *path_p = "/"; 173 *rev_p = SVN_INVALID_REVNUM; 174 return; 175} 176 177 178 179 180/*** Editor functions and batons. ***/ 181 182struct edit_baton 183{ 184 svn_fs_t *fs; 185 svn_fs_root_t *root; 186 svn_fs_root_t *base_root; 187 apr_pool_t *node_pool; 188 svn_repos_node_t *node; 189}; 190 191 192struct node_baton 193{ 194 struct edit_baton *edit_baton; 195 struct node_baton *parent_baton; 196 svn_repos_node_t *node; 197}; 198 199 200static svn_error_t * 201delete_entry(const char *path, 202 svn_revnum_t revision, 203 void *parent_baton, 204 apr_pool_t *pool) 205{ 206 struct node_baton *d = parent_baton; 207 struct edit_baton *eb = d->edit_baton; 208 svn_repos_node_t *node; 209 const char *name; 210 const char *base_path; 211 svn_revnum_t base_rev; 212 svn_fs_root_t *base_root; 213 svn_node_kind_t kind; 214 215 /* Get (or create) the change node and update it. */ 216 name = svn_relpath_basename(path, pool); 217 node = find_child_by_name(d->node, name); 218 if (! node) 219 node = create_child_node(d->node, name, eb->node_pool); 220 node->action = 'D'; 221 222 /* We need to look up this node's parents to see what its original 223 path in the filesystem was. Why? Because if this deletion 224 occurred underneath a copied path, the thing that was deleted 225 probably lived at a different location (relative to the copy 226 source). */ 227 find_real_base_location(&base_path, &base_rev, node, pool); 228 if (! SVN_IS_VALID_REVNUM(base_rev)) 229 { 230 /* No interesting base revision? We'll just look for the path 231 in our base root. */ 232 base_root = eb->base_root; 233 } 234 else 235 { 236 /* Oh. Perhaps some copy goodness happened somewhere? */ 237 SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool)); 238 } 239 240 /* Now figure out if this thing was a file or a dir. */ 241 SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool)); 242 if (kind == svn_node_none) 243 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 244 _("'%s' not found in filesystem"), path); 245 node->kind = kind; 246 247 return SVN_NO_ERROR; 248} 249 250 251 252static svn_error_t * 253add_open_helper(const char *path, 254 char action, 255 svn_node_kind_t kind, 256 void *parent_baton, 257 const char *copyfrom_path, 258 svn_revnum_t copyfrom_rev, 259 apr_pool_t *pool, 260 void **child_baton) 261{ 262 struct node_baton *pb = parent_baton; 263 struct edit_baton *eb = pb->edit_baton; 264 struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb)); 265 266 SVN_ERR_ASSERT(parent_baton && path); 267 268 nb->edit_baton = eb; 269 nb->parent_baton = pb; 270 271 /* Create and populate the node. */ 272 nb->node = create_child_node(pb->node, svn_relpath_basename(path, NULL), 273 eb->node_pool); 274 nb->node->kind = kind; 275 nb->node->action = action; 276 nb->node->copyfrom_rev = copyfrom_rev; 277 nb->node->copyfrom_path = 278 copyfrom_path ? apr_pstrdup(eb->node_pool, copyfrom_path) : NULL; 279 280 *child_baton = nb; 281 return SVN_NO_ERROR; 282} 283 284 285static svn_error_t * 286open_root(void *edit_baton, 287 svn_revnum_t base_revision, 288 apr_pool_t *pool, 289 void **root_baton) 290{ 291 struct edit_baton *eb = edit_baton; 292 struct node_baton *d = apr_pcalloc(pool, sizeof(*d)); 293 294 d->edit_baton = eb; 295 d->parent_baton = NULL; 296 d->node = (eb->node = create_node("", NULL, eb->node_pool)); 297 d->node->kind = svn_node_dir; 298 d->node->action = 'R'; 299 *root_baton = d; 300 301 return SVN_NO_ERROR; 302} 303 304 305static svn_error_t * 306open_directory(const char *path, 307 void *parent_baton, 308 svn_revnum_t base_revision, 309 apr_pool_t *pool, 310 void **child_baton) 311{ 312 return add_open_helper(path, 'R', svn_node_dir, parent_baton, 313 NULL, SVN_INVALID_REVNUM, 314 pool, child_baton); 315} 316 317 318static svn_error_t * 319add_directory(const char *path, 320 void *parent_baton, 321 const char *copyfrom_path, 322 svn_revnum_t copyfrom_revision, 323 apr_pool_t *pool, 324 void **child_baton) 325{ 326 return add_open_helper(path, 'A', svn_node_dir, parent_baton, 327 copyfrom_path, copyfrom_revision, 328 pool, child_baton); 329} 330 331 332static svn_error_t * 333open_file(const char *path, 334 void *parent_baton, 335 svn_revnum_t base_revision, 336 apr_pool_t *pool, 337 void **file_baton) 338{ 339 return add_open_helper(path, 'R', svn_node_file, parent_baton, 340 NULL, SVN_INVALID_REVNUM, 341 pool, file_baton); 342} 343 344 345static svn_error_t * 346add_file(const char *path, 347 void *parent_baton, 348 const char *copyfrom_path, 349 svn_revnum_t copyfrom_revision, 350 apr_pool_t *pool, 351 void **file_baton) 352{ 353 return add_open_helper(path, 'A', svn_node_file, parent_baton, 354 copyfrom_path, copyfrom_revision, 355 pool, file_baton); 356} 357 358 359static svn_error_t * 360apply_textdelta(void *file_baton, 361 const char *base_checksum, 362 apr_pool_t *pool, 363 svn_txdelta_window_handler_t *handler, 364 void **handler_baton) 365{ 366 struct node_baton *fb = file_baton; 367 fb->node->text_mod = TRUE; 368 *handler = svn_delta_noop_window_handler; 369 *handler_baton = NULL; 370 return SVN_NO_ERROR; 371} 372 373 374 375static svn_error_t * 376change_node_prop(void *node_baton, 377 const char *name, 378 const svn_string_t *value, 379 apr_pool_t *pool) 380{ 381 struct node_baton *nb = node_baton; 382 nb->node->prop_mod = TRUE; 383 return SVN_NO_ERROR; 384} 385 386 387svn_error_t * 388svn_repos_node_editor(const svn_delta_editor_t **editor, 389 void **edit_baton, 390 svn_repos_t *repos, 391 svn_fs_root_t *base_root, 392 svn_fs_root_t *root, 393 apr_pool_t *node_pool, 394 apr_pool_t *pool) 395{ 396 svn_delta_editor_t *my_editor; 397 struct edit_baton *my_edit_baton; 398 399 /* Set up the editor. */ 400 my_editor = svn_delta_default_editor(pool); 401 my_editor->open_root = open_root; 402 my_editor->delete_entry = delete_entry; 403 my_editor->add_directory = add_directory; 404 my_editor->open_directory = open_directory; 405 my_editor->add_file = add_file; 406 my_editor->open_file = open_file; 407 my_editor->apply_textdelta = apply_textdelta; 408 my_editor->change_file_prop = change_node_prop; 409 my_editor->change_dir_prop = change_node_prop; 410 411 /* Set up the edit baton. */ 412 my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton)); 413 my_edit_baton->node_pool = node_pool; 414 my_edit_baton->fs = repos->fs; 415 my_edit_baton->root = root; 416 my_edit_baton->base_root = base_root; 417 418 *editor = my_editor; 419 *edit_baton = my_edit_baton; 420 421 return SVN_NO_ERROR; 422} 423 424 425 426svn_repos_node_t * 427svn_repos_node_from_baton(void *edit_baton) 428{ 429 struct edit_baton *eb = edit_baton; 430 return eb->node; 431} 432