In the command-line client, forbid
[svn.git] / subversion / libsvn_repos / node_tree.c
blob810dfffb56371bc943f733934cf3f08c5e91b9ef
1 /*
2 * node_tree.c: an editor for tracking repository deltas changes
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
24 #include <stdio.h>
25 #include <assert.h>
27 #define APR_WANT_STRFUNC
28 #include <apr_want.h>
29 #include <apr_pools.h>
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_path.h"
34 #include "svn_delta.h"
35 #include "svn_fs.h"
36 #include "svn_repos.h"
37 #include "repos.h"
38 #include "svn_private_config.h"
40 /*** NOTE: This editor is unique in that it currently is hard-coded to
41 be anchored at the root directory of the filesystem. This
42 affords us the ability to use the same paths for filesystem
43 locations and editor paths. ***/
47 /*** Node creation and assembly structures and routines. ***/
48 static svn_repos_node_t *
49 create_node(const char *name,
50 svn_repos_node_t *parent,
51 apr_pool_t *pool)
53 svn_repos_node_t *node = apr_pcalloc(pool, sizeof(*node));
54 node->action = 'R';
55 node->kind = svn_node_unknown;
56 node->name = apr_pstrdup(pool, name);
57 node->parent = parent;
58 return node;
62 static svn_repos_node_t *
63 create_sibling_node(svn_repos_node_t *elder,
64 const char *name,
65 apr_pool_t *pool)
67 svn_repos_node_t *tmp_node;
69 /* No ELDER sibling? That's just not gonna work out. */
70 if (! elder)
71 return NULL;
73 /* Run to the end of the list of siblings of ELDER. */
74 tmp_node = elder;
75 while (tmp_node->sibling)
76 tmp_node = tmp_node->sibling;
78 /* Create a new youngest sibling and return that. */
79 return (tmp_node->sibling = create_node(name, elder->parent, pool));
83 static svn_repos_node_t *
84 create_child_node(svn_repos_node_t *parent,
85 const char *name,
86 apr_pool_t *pool)
88 /* No PARENT node? That's just not gonna work out. */
89 if (! parent)
90 return NULL;
92 /* If PARENT has no children, create its first one and return that. */
93 if (! parent->child)
94 return (parent->child = create_node(name, parent, pool));
96 /* If PARENT already has a child, create a new sibling for its first
97 child and return that. */
98 return create_sibling_node(parent->child, name, pool);
102 static svn_repos_node_t *
103 find_child_by_name(svn_repos_node_t *parent,
104 const char *name)
106 svn_repos_node_t *tmp_node;
108 /* No PARENT node, or a barren PARENT? Nothing to find. */
109 if ((! parent) || (! parent->child))
110 return NULL;
112 /* Look through the children for a node with a matching name. */
113 tmp_node = parent->child;
114 while (1)
116 if (! strcmp(tmp_node->name, name))
118 return tmp_node;
120 else
122 if (tmp_node->sibling)
123 tmp_node = tmp_node->sibling;
124 else
125 break;
129 return NULL;
133 static void
134 find_real_base_location(const char **path_p,
135 svn_revnum_t *rev_p,
136 svn_repos_node_t *node,
137 apr_pool_t *pool)
139 /* If NODE is an add-with-history, then its real base location is
140 the copy source. */
141 if ((node->action == 'A')
142 && node->copyfrom_path
143 && SVN_IS_VALID_REVNUM(node->copyfrom_rev))
145 *path_p = node->copyfrom_path;
146 *rev_p = node->copyfrom_rev;
147 return;
150 /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's
151 name to whatever the parent's real base path turns out to be (and
152 pass the base revision on through). */
153 if (node->parent)
155 const char *path;
156 svn_revnum_t rev;
158 find_real_base_location(&path, &rev, node->parent, pool);
159 *path_p = svn_path_join(path, node->name, pool);
160 *rev_p = rev;
161 return;
164 /* Finally, if the node has no parent, then its name is "/", and it
165 has no interesting base revision. */
166 *path_p = "/";
167 *rev_p = SVN_INVALID_REVNUM;
168 return;
174 /*** Editor functions and batons. ***/
176 struct edit_baton
178 svn_fs_t *fs;
179 svn_fs_root_t *root;
180 svn_fs_root_t *base_root;
181 apr_pool_t *node_pool;
182 svn_repos_node_t *node;
186 struct node_baton
188 struct edit_baton *edit_baton;
189 struct node_baton *parent_baton;
190 svn_repos_node_t *node;
194 static svn_error_t *
195 delete_entry(const char *path,
196 svn_revnum_t revision,
197 void *parent_baton,
198 apr_pool_t *pool)
200 struct node_baton *d = parent_baton;
201 struct edit_baton *eb = d->edit_baton;
202 svn_repos_node_t *node;
203 const char *name;
204 const char *base_path;
205 svn_revnum_t base_rev;
206 svn_fs_root_t *base_root;
207 svn_node_kind_t kind;
209 /* Get (or create) the change node and update it. */
210 name = svn_path_basename(path, pool);
211 node = find_child_by_name(d->node, name);
212 if (! node)
213 node = create_child_node(d->node, name, eb->node_pool);
214 node->action = 'D';
216 /* We need to look up this node's parents to see what its original
217 path in the filesystem was. Why? Because if this deletion
218 occurred underneath a copied path, the thing that was deleted
219 probably lived at a different location (relative to the copy
220 source). */
221 find_real_base_location(&base_path, &base_rev, node, pool);
222 if (! SVN_IS_VALID_REVNUM(base_rev))
224 /* No interesting base revision? We'll just look for the path
225 in our base root. */
226 base_root = eb->base_root;
228 else
230 /* Oh. Perhaps some copy goodness happened somewhere? */
231 SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool));
234 /* Now figure out if this thing was a file or a dir. */
235 SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool));
236 if (kind == svn_node_none)
237 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
238 _("'%s' not found in filesystem"), path);
239 node->kind = kind;
241 return SVN_NO_ERROR;
246 static svn_error_t *
247 add_open_helper(const char *path,
248 char action,
249 svn_node_kind_t kind,
250 void *parent_baton,
251 const char *copyfrom_path,
252 svn_revnum_t copyfrom_rev,
253 apr_pool_t *pool,
254 void **child_baton)
256 struct node_baton *pb = parent_baton;
257 struct edit_baton *eb = pb->edit_baton;
258 struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
260 assert(parent_baton && path);
262 nb->edit_baton = eb;
263 nb->parent_baton = pb;
265 /* Create and populate the node. */
266 nb->node = create_child_node(pb->node, svn_path_basename(path, pool),
267 eb->node_pool);
268 nb->node->kind = kind;
269 nb->node->action = action;
270 nb->node->copyfrom_rev = copyfrom_rev;
271 nb->node->copyfrom_path =
272 copyfrom_path ? apr_pstrdup(eb->node_pool, copyfrom_path) : NULL;
274 *child_baton = nb;
275 return SVN_NO_ERROR;
279 static svn_error_t *
280 open_root(void *edit_baton,
281 svn_revnum_t base_revision,
282 apr_pool_t *pool,
283 void **root_baton)
285 struct edit_baton *eb = edit_baton;
286 struct node_baton *d = apr_pcalloc(pool, sizeof(*d));
288 d->edit_baton = eb;
289 d->parent_baton = NULL;
290 d->node = (eb->node = create_node("", NULL, eb->node_pool));
291 d->node->kind = svn_node_dir;
292 d->node->action = 'R';
293 *root_baton = d;
295 return SVN_NO_ERROR;
299 static svn_error_t *
300 open_directory(const char *path,
301 void *parent_baton,
302 svn_revnum_t base_revision,
303 apr_pool_t *pool,
304 void **child_baton)
306 SVN_ERR(add_open_helper(path, 'R', svn_node_dir, parent_baton,
307 NULL, SVN_INVALID_REVNUM,
308 pool, child_baton));
309 return SVN_NO_ERROR;
313 static svn_error_t *
314 add_directory(const char *path,
315 void *parent_baton,
316 const char *copyfrom_path,
317 svn_revnum_t copyfrom_revision,
318 apr_pool_t *pool,
319 void **child_baton)
321 SVN_ERR(add_open_helper(path, 'A', svn_node_dir, parent_baton,
322 copyfrom_path, copyfrom_revision,
323 pool, child_baton));
324 return SVN_NO_ERROR;
328 static svn_error_t *
329 open_file(const char *path,
330 void *parent_baton,
331 svn_revnum_t base_revision,
332 apr_pool_t *pool,
333 void **file_baton)
335 SVN_ERR(add_open_helper(path, 'R', svn_node_file, parent_baton,
336 NULL, SVN_INVALID_REVNUM,
337 pool, file_baton));
338 return SVN_NO_ERROR;
342 static svn_error_t *
343 add_file(const char *path,
344 void *parent_baton,
345 const char *copyfrom_path,
346 svn_revnum_t copyfrom_revision,
347 apr_pool_t *pool,
348 void **file_baton)
350 SVN_ERR(add_open_helper(path, 'A', svn_node_file, parent_baton,
351 copyfrom_path, copyfrom_revision,
352 pool, file_baton));
353 return SVN_NO_ERROR;
357 static svn_error_t *
358 apply_textdelta(void *file_baton,
359 const char *base_checksum,
360 apr_pool_t *pool,
361 svn_txdelta_window_handler_t *handler,
362 void **handler_baton)
364 struct node_baton *fb = file_baton;
365 fb->node->text_mod = TRUE;
366 *handler = svn_delta_noop_window_handler;
367 *handler_baton = NULL;
368 return SVN_NO_ERROR;
373 static svn_error_t *
374 change_node_prop(void *node_baton,
375 const char *name,
376 const svn_string_t *value,
377 apr_pool_t *pool)
379 struct node_baton *nb = node_baton;
380 nb->node->prop_mod = TRUE;
381 return SVN_NO_ERROR;
385 svn_error_t *
386 svn_repos_node_editor(const svn_delta_editor_t **editor,
387 void **edit_baton,
388 svn_repos_t *repos,
389 svn_fs_root_t *base_root,
390 svn_fs_root_t *root,
391 apr_pool_t *node_pool,
392 apr_pool_t *pool)
394 svn_delta_editor_t *my_editor;
395 struct edit_baton *my_edit_baton;
397 /* Set up the editor. */
398 my_editor = svn_delta_default_editor(pool);
399 my_editor->open_root = open_root;
400 my_editor->delete_entry = delete_entry;
401 my_editor->add_directory = add_directory;
402 my_editor->open_directory = open_directory;
403 my_editor->add_file = add_file;
404 my_editor->open_file = open_file;
405 my_editor->apply_textdelta = apply_textdelta;
406 my_editor->change_file_prop = change_node_prop;
407 my_editor->change_dir_prop = change_node_prop;
409 /* Set up the edit baton. */
410 my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton));
411 my_edit_baton->node_pool = node_pool;
412 my_edit_baton->fs = repos->fs;
413 my_edit_baton->root = root;
414 my_edit_baton->base_root = base_root;
416 *editor = my_editor;
417 *edit_baton = my_edit_baton;
419 return SVN_NO_ERROR;
424 svn_repos_node_t *
425 svn_repos_node_from_baton(void *edit_baton)
427 struct edit_baton *eb = edit_baton;
428 return eb->node;