1251881Speter/*
2251881Speter * node_tree.c:  an editor for tracking repository deltas changes
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter
29251881Speter#include <stdio.h>
30251881Speter
31251881Speter#define APR_WANT_STRFUNC
32251881Speter#include <apr_want.h>
33251881Speter#include <apr_pools.h>
34251881Speter
35251881Speter#include "svn_types.h"
36251881Speter#include "svn_error.h"
37251881Speter#include "svn_dirent_uri.h"
38251881Speter#include "svn_path.h"
39251881Speter#include "svn_delta.h"
40251881Speter#include "svn_fs.h"
41251881Speter#include "svn_repos.h"
42251881Speter#include "repos.h"
43251881Speter#include "svn_private_config.h"
44251881Speter#include "private/svn_fspath.h"
45251881Speter
46251881Speter/*** NOTE: This editor is unique in that it currently is hard-coded to
47251881Speter     be anchored at the root directory of the filesystem.  This
48251881Speter     affords us the ability to use the same paths for filesystem
49251881Speter     locations and editor paths.  ***/
50251881Speter
51251881Speter
52251881Speter
53251881Speter/*** Node creation and assembly structures and routines. ***/
54251881Speterstatic svn_repos_node_t *
55251881Spetercreate_node(const char *name,
56251881Speter            svn_repos_node_t *parent,
57251881Speter            apr_pool_t *pool)
58251881Speter{
59251881Speter  svn_repos_node_t *node = apr_pcalloc(pool, sizeof(*node));
60251881Speter  node->action = 'R';
61251881Speter  node->kind = svn_node_unknown;
62251881Speter  node->name = apr_pstrdup(pool, name);
63251881Speter  node->parent = parent;
64251881Speter  return node;
65251881Speter}
66251881Speter
67251881Speter
68251881Speterstatic svn_repos_node_t *
69251881Spetercreate_sibling_node(svn_repos_node_t *elder,
70251881Speter                    const char *name,
71251881Speter                    apr_pool_t *pool)
72251881Speter{
73251881Speter  svn_repos_node_t *tmp_node;
74251881Speter
75251881Speter  /* No ELDER sibling?  That's just not gonna work out. */
76251881Speter  if (! elder)
77251881Speter    return NULL;
78251881Speter
79251881Speter  /* Run to the end of the list of siblings of ELDER. */
80251881Speter  tmp_node = elder;
81251881Speter  while (tmp_node->sibling)
82251881Speter    tmp_node = tmp_node->sibling;
83251881Speter
84251881Speter  /* Create a new youngest sibling and return that. */
85251881Speter  return (tmp_node->sibling = create_node(name, elder->parent, pool));
86251881Speter}
87251881Speter
88251881Speter
89251881Speterstatic svn_repos_node_t *
90251881Spetercreate_child_node(svn_repos_node_t *parent,
91251881Speter                  const char *name,
92251881Speter                  apr_pool_t *pool)
93251881Speter{
94251881Speter  /* No PARENT node?  That's just not gonna work out. */
95251881Speter  if (! parent)
96251881Speter    return NULL;
97251881Speter
98251881Speter  /* If PARENT has no children, create its first one and return that. */
99251881Speter  if (! parent->child)
100251881Speter    return (parent->child = create_node(name, parent, pool));
101251881Speter
102251881Speter  /* If PARENT already has a child, create a new sibling for its first
103251881Speter     child and return that. */
104251881Speter  return create_sibling_node(parent->child, name, pool);
105251881Speter}
106251881Speter
107251881Speter
108251881Speterstatic svn_repos_node_t *
109251881Speterfind_child_by_name(svn_repos_node_t *parent,
110251881Speter                   const char *name)
111251881Speter{
112251881Speter  svn_repos_node_t *tmp_node;
113251881Speter
114251881Speter  /* No PARENT node, or a barren PARENT?  Nothing to find. */
115251881Speter  if ((! parent) || (! parent->child))
116251881Speter    return NULL;
117251881Speter
118251881Speter  /* Look through the children for a node with a matching name. */
119251881Speter  tmp_node = parent->child;
120251881Speter  while (1)
121251881Speter    {
122251881Speter      if (! strcmp(tmp_node->name, name))
123251881Speter        {
124251881Speter          return tmp_node;
125251881Speter        }
126251881Speter      else
127251881Speter        {
128251881Speter          if (tmp_node->sibling)
129251881Speter            tmp_node = tmp_node->sibling;
130251881Speter          else
131251881Speter            break;
132251881Speter        }
133251881Speter    }
134251881Speter
135251881Speter  return NULL;
136251881Speter}
137251881Speter
138251881Speter
139251881Speterstatic void
140251881Speterfind_real_base_location(const char **path_p,
141251881Speter                        svn_revnum_t *rev_p,
142251881Speter                        svn_repos_node_t *node,
143251881Speter                        apr_pool_t *pool)
144251881Speter{
145251881Speter  /* If NODE is an add-with-history, then its real base location is
146251881Speter     the copy source. */
147251881Speter  if ((node->action == 'A')
148251881Speter      && node->copyfrom_path
149251881Speter      && SVN_IS_VALID_REVNUM(node->copyfrom_rev))
150251881Speter    {
151251881Speter      *path_p = node->copyfrom_path;
152251881Speter      *rev_p = node->copyfrom_rev;
153251881Speter      return;
154251881Speter    }
155251881Speter
156251881Speter  /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's
157251881Speter     name to whatever the parent's real base path turns out to be (and
158251881Speter     pass the base revision on through). */
159251881Speter  if (node->parent)
160251881Speter    {
161251881Speter      const char *path;
162251881Speter      svn_revnum_t rev;
163251881Speter
164251881Speter      find_real_base_location(&path, &rev, node->parent, pool);
165251881Speter      *path_p = svn_fspath__join(path, node->name, pool);
166251881Speter      *rev_p = rev;
167251881Speter      return;
168251881Speter    }
169251881Speter
170251881Speter  /* Finally, if the node has no parent, then its name is "/", and it
171251881Speter     has no interesting base revision.  */
172251881Speter  *path_p = "/";
173251881Speter  *rev_p = SVN_INVALID_REVNUM;
174251881Speter  return;
175251881Speter}
176251881Speter
177251881Speter
178251881Speter
179251881Speter
180251881Speter/*** Editor functions and batons. ***/
181251881Speter
182251881Speterstruct edit_baton
183251881Speter{
184251881Speter  svn_fs_t *fs;
185251881Speter  svn_fs_root_t *root;
186251881Speter  svn_fs_root_t *base_root;
187251881Speter  apr_pool_t *node_pool;
188251881Speter  svn_repos_node_t *node;
189251881Speter};
190251881Speter
191251881Speter
192251881Speterstruct node_baton
193251881Speter{
194251881Speter  struct edit_baton *edit_baton;
195251881Speter  struct node_baton *parent_baton;
196251881Speter  svn_repos_node_t *node;
197251881Speter};
198251881Speter
199251881Speter
200251881Speterstatic svn_error_t *
201251881Speterdelete_entry(const char *path,
202251881Speter             svn_revnum_t revision,
203251881Speter             void *parent_baton,
204251881Speter             apr_pool_t *pool)
205251881Speter{
206251881Speter  struct node_baton *d = parent_baton;
207251881Speter  struct edit_baton *eb = d->edit_baton;
208251881Speter  svn_repos_node_t *node;
209251881Speter  const char *name;
210251881Speter  const char *base_path;
211251881Speter  svn_revnum_t base_rev;
212251881Speter  svn_fs_root_t *base_root;
213251881Speter  svn_node_kind_t kind;
214251881Speter
215251881Speter  /* Get (or create) the change node and update it. */
216251881Speter  name = svn_relpath_basename(path, pool);
217251881Speter  node = find_child_by_name(d->node, name);
218251881Speter  if (! node)
219251881Speter    node = create_child_node(d->node, name, eb->node_pool);
220251881Speter  node->action = 'D';
221251881Speter
222251881Speter  /* We need to look up this node's parents to see what its original
223251881Speter     path in the filesystem was.  Why?  Because if this deletion
224251881Speter     occurred underneath a copied path, the thing that was deleted
225251881Speter     probably lived at a different location (relative to the copy
226251881Speter     source). */
227251881Speter  find_real_base_location(&base_path, &base_rev, node, pool);
228251881Speter  if (! SVN_IS_VALID_REVNUM(base_rev))
229251881Speter    {
230251881Speter      /* No interesting base revision?  We'll just look for the path
231251881Speter         in our base root.  */
232251881Speter      base_root = eb->base_root;
233251881Speter    }
234251881Speter  else
235251881Speter    {
236251881Speter      /* Oh.  Perhaps some copy goodness happened somewhere? */
237251881Speter      SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool));
238251881Speter    }
239251881Speter
240251881Speter  /* Now figure out if this thing was a file or a dir. */
241251881Speter  SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool));
242251881Speter  if (kind == svn_node_none)
243251881Speter    return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
244251881Speter                             _("'%s' not found in filesystem"), path);
245251881Speter  node->kind = kind;
246251881Speter
247251881Speter  return SVN_NO_ERROR;
248251881Speter}
249251881Speter
250251881Speter
251251881Speter
252251881Speterstatic svn_error_t *
253251881Speteradd_open_helper(const char *path,
254251881Speter                char action,
255251881Speter                svn_node_kind_t kind,
256251881Speter                void *parent_baton,
257251881Speter                const char *copyfrom_path,
258251881Speter                svn_revnum_t copyfrom_rev,
259251881Speter                apr_pool_t *pool,
260251881Speter                void **child_baton)
261251881Speter{
262251881Speter  struct node_baton *pb = parent_baton;
263251881Speter  struct edit_baton *eb = pb->edit_baton;
264251881Speter  struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
265251881Speter
266251881Speter  SVN_ERR_ASSERT(parent_baton && path);
267251881Speter
268251881Speter  nb->edit_baton = eb;
269251881Speter  nb->parent_baton = pb;
270251881Speter
271251881Speter  /* Create and populate the node. */
272251881Speter  nb->node = create_child_node(pb->node, svn_relpath_basename(path, NULL),
273251881Speter                               eb->node_pool);
274251881Speter  nb->node->kind = kind;
275251881Speter  nb->node->action = action;
276251881Speter  nb->node->copyfrom_rev = copyfrom_rev;
277251881Speter  nb->node->copyfrom_path =
278251881Speter    copyfrom_path ? apr_pstrdup(eb->node_pool, copyfrom_path) : NULL;
279251881Speter
280251881Speter  *child_baton = nb;
281251881Speter  return SVN_NO_ERROR;
282251881Speter}
283251881Speter
284251881Speter
285251881Speterstatic svn_error_t *
286251881Speteropen_root(void *edit_baton,
287251881Speter          svn_revnum_t base_revision,
288251881Speter          apr_pool_t *pool,
289251881Speter          void **root_baton)
290251881Speter{
291251881Speter  struct edit_baton *eb = edit_baton;
292251881Speter  struct node_baton *d = apr_pcalloc(pool, sizeof(*d));
293251881Speter
294251881Speter  d->edit_baton = eb;
295251881Speter  d->parent_baton = NULL;
296251881Speter  d->node = (eb->node = create_node("", NULL, eb->node_pool));
297251881Speter  d->node->kind = svn_node_dir;
298251881Speter  d->node->action = 'R';
299251881Speter  *root_baton = d;
300251881Speter
301251881Speter  return SVN_NO_ERROR;
302251881Speter}
303251881Speter
304251881Speter
305251881Speterstatic svn_error_t *
306251881Speteropen_directory(const char *path,
307251881Speter               void *parent_baton,
308251881Speter               svn_revnum_t base_revision,
309251881Speter               apr_pool_t *pool,
310251881Speter               void **child_baton)
311251881Speter{
312251881Speter  return add_open_helper(path, 'R', svn_node_dir, parent_baton,
313251881Speter                         NULL, SVN_INVALID_REVNUM,
314251881Speter                         pool, child_baton);
315251881Speter}
316251881Speter
317251881Speter
318251881Speterstatic svn_error_t *
319251881Speteradd_directory(const char *path,
320251881Speter              void *parent_baton,
321251881Speter              const char *copyfrom_path,
322251881Speter              svn_revnum_t copyfrom_revision,
323251881Speter              apr_pool_t *pool,
324251881Speter              void **child_baton)
325251881Speter{
326251881Speter  return add_open_helper(path, 'A', svn_node_dir, parent_baton,
327251881Speter                         copyfrom_path, copyfrom_revision,
328251881Speter                         pool, child_baton);
329251881Speter}
330251881Speter
331251881Speter
332251881Speterstatic svn_error_t *
333251881Speteropen_file(const char *path,
334251881Speter          void *parent_baton,
335251881Speter          svn_revnum_t base_revision,
336251881Speter          apr_pool_t *pool,
337251881Speter          void **file_baton)
338251881Speter{
339251881Speter  return add_open_helper(path, 'R', svn_node_file, parent_baton,
340251881Speter                         NULL, SVN_INVALID_REVNUM,
341251881Speter                         pool, file_baton);
342251881Speter}
343251881Speter
344251881Speter
345251881Speterstatic svn_error_t *
346251881Speteradd_file(const char *path,
347251881Speter         void *parent_baton,
348251881Speter         const char *copyfrom_path,
349251881Speter         svn_revnum_t copyfrom_revision,
350251881Speter         apr_pool_t *pool,
351251881Speter         void **file_baton)
352251881Speter{
353251881Speter  return add_open_helper(path, 'A', svn_node_file, parent_baton,
354251881Speter                         copyfrom_path, copyfrom_revision,
355251881Speter                         pool, file_baton);
356251881Speter}
357251881Speter
358251881Speter
359251881Speterstatic svn_error_t *
360251881Speterapply_textdelta(void *file_baton,
361251881Speter                const char *base_checksum,
362251881Speter                apr_pool_t *pool,
363251881Speter                svn_txdelta_window_handler_t *handler,
364251881Speter                void **handler_baton)
365251881Speter{
366251881Speter  struct node_baton *fb = file_baton;
367251881Speter  fb->node->text_mod = TRUE;
368251881Speter  *handler = svn_delta_noop_window_handler;
369251881Speter  *handler_baton = NULL;
370251881Speter  return SVN_NO_ERROR;
371251881Speter}
372251881Speter
373251881Speter
374251881Speter
375251881Speterstatic svn_error_t *
376251881Speterchange_node_prop(void *node_baton,
377251881Speter                 const char *name,
378251881Speter                 const svn_string_t *value,
379251881Speter                 apr_pool_t *pool)
380251881Speter{
381251881Speter  struct node_baton *nb = node_baton;
382251881Speter  nb->node->prop_mod = TRUE;
383251881Speter  return SVN_NO_ERROR;
384251881Speter}
385251881Speter
386251881Speter
387251881Spetersvn_error_t *
388251881Spetersvn_repos_node_editor(const svn_delta_editor_t **editor,
389251881Speter                      void **edit_baton,
390251881Speter                      svn_repos_t *repos,
391251881Speter                      svn_fs_root_t *base_root,
392251881Speter                      svn_fs_root_t *root,
393251881Speter                      apr_pool_t *node_pool,
394251881Speter                      apr_pool_t *pool)
395251881Speter{
396251881Speter  svn_delta_editor_t *my_editor;
397251881Speter  struct edit_baton *my_edit_baton;
398251881Speter
399251881Speter  /* Set up the editor. */
400251881Speter  my_editor = svn_delta_default_editor(pool);
401251881Speter  my_editor->open_root           = open_root;
402251881Speter  my_editor->delete_entry        = delete_entry;
403251881Speter  my_editor->add_directory       = add_directory;
404251881Speter  my_editor->open_directory      = open_directory;
405251881Speter  my_editor->add_file            = add_file;
406251881Speter  my_editor->open_file           = open_file;
407251881Speter  my_editor->apply_textdelta     = apply_textdelta;
408251881Speter  my_editor->change_file_prop    = change_node_prop;
409251881Speter  my_editor->change_dir_prop     = change_node_prop;
410251881Speter
411251881Speter  /* Set up the edit baton. */
412251881Speter  my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton));
413251881Speter  my_edit_baton->node_pool = node_pool;
414251881Speter  my_edit_baton->fs = repos->fs;
415251881Speter  my_edit_baton->root = root;
416251881Speter  my_edit_baton->base_root = base_root;
417251881Speter
418251881Speter  *editor = my_editor;
419251881Speter  *edit_baton = my_edit_baton;
420251881Speter
421251881Speter  return SVN_NO_ERROR;
422251881Speter}
423251881Speter
424251881Speter
425251881Speter
426251881Spetersvn_repos_node_t *
427251881Spetersvn_repos_node_from_baton(void *edit_baton)
428251881Speter{
429251881Speter  struct edit_baton *eb = edit_baton;
430251881Speter  return eb->node;
431251881Speter}
432