2
* path_driver.c -- drive an editor across a set of paths
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
* ====================================================================
21
#include <apr_pools.h>
22
#include <apr_strings.h>
24
#include "svn_types.h"
25
#include "svn_delta.h"
26
#include "svn_pools.h"
28
#include "svn_sorts.h"
31
/*** Helper functions. ***/
33
typedef struct dir_stack_t
35
void *dir_baton; /* the dir baton. */
36
apr_pool_t *pool; /* the pool associated with the dir baton. */
41
/* Call EDITOR's open_directory() function with the PATH and REVISION
42
* arguments, and then add the resulting dir baton to the dir baton
46
open_dir (apr_array_header_t *db_stack,
47
const svn_delta_editor_t *editor,
49
svn_revnum_t revision,
56
/* Assert that we are in a stable state. */
57
assert (db_stack && db_stack->nelts);
59
/* Get the parent dir baton. */
60
item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, void *);
61
parent_db = item->dir_baton;
63
/* Call the EDITOR's open_directory function to get a new directory
65
subpool = svn_pool_create (pool);
66
SVN_ERR (editor->open_directory (path, parent_db, revision, subpool, &db));
68
/* Now add the dir baton to the stack. */
69
item = apr_pcalloc (subpool, sizeof (*item));
72
APR_ARRAY_PUSH (db_stack, dir_stack_t *) = item;
78
/* Pop a directory from the dir baton stack and update the stack
81
* This function calls the EDITOR's close_directory() function.
84
pop_stack (apr_array_header_t *db_stack,
85
const svn_delta_editor_t *editor)
89
/* Assert that we are in a stable state. */
90
assert (db_stack && db_stack->nelts);
92
/* Close the most recent directory pushed to the stack. */
93
item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, dir_stack_t *);
94
(void) apr_array_pop (db_stack);
95
SVN_ERR (editor->close_directory (item->dir_baton, item->pool));
96
svn_pool_destroy (item->pool);
102
/* Count the number of path components in PATH. */
104
count_components (const char *path)
107
const char *instance = path;
109
if ((strlen (path) == 1) && (path[0] == '/'))
115
instance = strchr (instance, '/');
126
/*** Public interfaces ***/
128
svn_delta_path_driver (const svn_delta_editor_t *editor,
130
svn_revnum_t revision,
131
apr_array_header_t *paths,
132
svn_delta_path_driver_cb_func_t callback_func,
133
void *callback_baton,
136
apr_array_header_t *db_stack = apr_array_make (pool, 4, sizeof (void *));
137
const char *last_path = NULL;
139
void *parent_db = NULL, *db = NULL;
141
apr_pool_t *subpool = svn_pool_create (pool);
142
apr_pool_t *iterpool = svn_pool_create (pool);
143
dir_stack_t *item = apr_pcalloc (subpool, sizeof (*item));
145
/* Do nothing if there are no paths. */
149
/* Sort the paths in a depth-first directory-ish order. */
150
qsort (paths->elts, paths->nelts, paths->elt_size, svn_sort_compare_paths);
152
/* If the root of the edit is also a target path, we want to call
153
the callback function to let the user open the root directory and
154
do what needs to be done. Otherwise, we'll do the open_root()
156
path = APR_ARRAY_IDX (paths, 0, const char *);
157
if (svn_path_is_empty (path))
159
SVN_ERR (callback_func (&db, NULL, callback_baton, path, subpool));
165
SVN_ERR (editor->open_root (edit_baton, revision, subpool, &db));
167
item->pool = subpool;
168
item->dir_baton = db;
169
APR_ARRAY_PUSH (db_stack, void *) = item;
171
/* Now, loop over the commit items, traversing the URL tree and
172
driving the editor. */
173
for (; i < paths->nelts; i++)
175
const char *pdir, *bname;
176
const char *common = "";
179
/* Clear the iteration pool. */
180
svn_pool_clear (iterpool);
182
/* Get the next path. */
183
path = APR_ARRAY_IDX (paths, i, const char *);
185
/*** Step A - Find the common ancestor of the last path and the
186
current one. For the first iteration, this is just the
189
common = svn_path_get_longest_ancestor (last_path, path, iterpool);
190
common_len = strlen (common);
192
/*** Step B - Close any directories between the last path and
193
the new common ancestor, if any need to be closed.
194
Sometimes there is nothing to do here (like, for the first
195
iteration, or when the last path was an ancestor of the
197
if ((i > 0) && (strlen (last_path) > common_len))
199
const char *rel = last_path + (common_len ? (common_len + 1) : 0);
200
int count = count_components (rel);
203
SVN_ERR (pop_stack (db_stack, editor));
207
/*** Step C - Open any directories between the common ancestor
208
and the parent of the current path. ***/
209
svn_path_split (path, &pdir, &bname, iterpool);
210
if (strlen (pdir) > common_len)
212
const char *piece = pdir + common_len + 1;
216
const char *rel = pdir;
218
/* Find the first separator. */
219
piece = strchr (piece, '/');
221
/* Calculate REL as the portion of PDIR up to (but not
222
including) the location to which PIECE is pointing. */
224
rel = apr_pstrmemdup (iterpool, pdir, piece - pdir);
226
/* Open the subdirectory. */
227
SVN_ERR (open_dir (db_stack, editor, rel, revision, pool));
229
/* If we found a '/', advance our PIECE pointer to
230
character just after that '/'. Otherwise, we're
239
/*** Step D - Tell our caller to handle the current path. ***/
240
item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, void *);
241
parent_db = item->dir_baton;
242
subpool = svn_pool_create (pool);
243
SVN_ERR (callback_func (&db, parent_db, callback_baton, path, subpool));
246
item = apr_pcalloc (subpool, sizeof (*item));
247
item->dir_baton = db;
248
item->pool = subpool;
249
APR_ARRAY_PUSH (db_stack, void *) = item;
253
svn_pool_destroy (subpool);
256
/*** Step E - Save our state for the next iteration. If our
257
caller opened or added PATH as a directory, that becomes
258
our LAST_PATH. Otherwise, we use PATH's parent
261
/* NOTE: The variable LAST_PATH needs to outlive the loop. */
263
last_path = path; /* lives in a pool outside our control. */
265
last_path = apr_pstrdup (pool, pdir); /* duping into POOL. */
268
/* Destroy the iteration subpool. */
269
svn_pool_destroy (iterpool);
271
/* Close down any remaining open directory batons. */
272
while (db_stack->nelts)
274
SVN_ERR (pop_stack (db_stack, editor));