summaryrefslogtreecommitdiff
path: root/subversion/libsvn_delta/path_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_delta/path_driver.c')
-rw-r--r--subversion/libsvn_delta/path_driver.c313
1 files changed, 186 insertions, 127 deletions
diff --git a/subversion/libsvn_delta/path_driver.c b/subversion/libsvn_delta/path_driver.c
index c1f3e07b818a7..5840cc7f29099 100644
--- a/subversion/libsvn_delta/path_driver.c
+++ b/subversion/libsvn_delta/path_driver.c
@@ -31,7 +31,6 @@
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_sorts.h"
-#include "private/svn_fspath.h"
#include "private/svn_sorts_private.h"
@@ -45,6 +44,22 @@ typedef struct dir_stack_t
} dir_stack_t;
+/* Push onto dir_stack a new item allocated in POOL and containing
+ * DIR_BATON and POOL.
+ */
+static void
+push_dir_stack_item(apr_array_header_t *db_stack,
+ void *dir_baton,
+ apr_pool_t *pool)
+{
+ dir_stack_t *item = apr_pcalloc(pool, sizeof(*item));
+
+ item->dir_baton = dir_baton;
+ item->pool = pool;
+ APR_ARRAY_PUSH(db_stack, dir_stack_t *) = item;
+}
+
+
/* Call EDITOR's open_directory() function with the PATH argument, then
* add the resulting dir baton to the dir baton stack.
*/
@@ -72,10 +87,7 @@ open_dir(apr_array_header_t *db_stack,
&db));
/* Now add the dir baton to the stack. */
- item = apr_pcalloc(subpool, sizeof(*item));
- item->dir_baton = db;
- item->pool = subpool;
- APR_ARRAY_PUSH(db_stack, dir_stack_t *) = item;
+ push_dir_stack_item(db_stack, db, subpool);
return SVN_NO_ERROR;
}
@@ -131,168 +143,215 @@ count_components(const char *path)
/*** Public interfaces ***/
svn_error_t *
-svn_delta_path_driver2(const svn_delta_editor_t *editor,
+svn_delta_path_driver3(const svn_delta_editor_t *editor,
void *edit_baton,
- const apr_array_header_t *paths,
+ const apr_array_header_t *relpaths,
svn_boolean_t sort_paths,
- svn_delta_path_driver_cb_func_t callback_func,
+ svn_delta_path_driver_cb_func2_t callback_func,
void *callback_baton,
apr_pool_t *pool)
{
- apr_array_header_t *db_stack = apr_array_make(pool, 4, sizeof(void *));
- const char *last_path = NULL;
- int i = 0;
- void *parent_db = NULL, *db = NULL;
- const char *path;
+ svn_delta_path_driver_state_t *state;
+ int i;
apr_pool_t *subpool, *iterpool;
- dir_stack_t *item;
/* Do nothing if there are no paths. */
- if (! paths->nelts)
+ if (! relpaths->nelts)
return SVN_NO_ERROR;
subpool = svn_pool_create(pool);
iterpool = svn_pool_create(pool);
/* sort paths if necessary */
- if (sort_paths && paths->nelts > 1)
+ if (sort_paths && relpaths->nelts > 1)
{
- apr_array_header_t *sorted = apr_array_copy(subpool, paths);
+ apr_array_header_t *sorted = apr_array_copy(subpool, relpaths);
svn_sort__array(sorted, svn_sort_compare_paths);
- paths = sorted;
+ relpaths = sorted;
}
- item = apr_pcalloc(subpool, sizeof(*item));
-
- /* If the root of the edit is also a target path, we want to call
- the callback function to let the user open the root directory and
- do what needs to be done. Otherwise, we'll do the open_root()
- ourselves. */
- path = APR_ARRAY_IDX(paths, 0, const char *);
- if (svn_path_is_empty(path))
- {
- SVN_ERR(callback_func(&db, NULL, callback_baton, path, subpool));
- last_path = path;
- i++;
- }
- else
- {
- SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, subpool, &db));
- }
- item->pool = subpool;
- item->dir_baton = db;
- APR_ARRAY_PUSH(db_stack, void *) = item;
+ SVN_ERR(svn_delta_path_driver_start(&state,
+ editor, edit_baton,
+ callback_func, callback_baton,
+ pool));
/* Now, loop over the commit items, traversing the URL tree and
driving the editor. */
- for (; i < paths->nelts; i++)
+ for (i = 0; i < relpaths->nelts; i++)
{
- const char *pdir;
- const char *common = "";
- size_t common_len;
+ const char *relpath;
/* Clear the iteration pool. */
svn_pool_clear(iterpool);
/* Get the next path. */
- path = APR_ARRAY_IDX(paths, i, const char *);
-
- /*** Step A - Find the common ancestor of the last path and the
- current one. For the first iteration, this is just the
- empty string. ***/
- if (i > 0)
- common = (last_path[0] == '/')
- ? svn_fspath__get_longest_ancestor(last_path, path, iterpool)
- : svn_relpath_get_longest_ancestor(last_path, path, iterpool);
- common_len = strlen(common);
-
- /*** Step B - Close any directories between the last path and
- the new common ancestor, if any need to be closed.
- Sometimes there is nothing to do here (like, for the first
- iteration, or when the last path was an ancestor of the
- current one). ***/
- if ((i > 0) && (strlen(last_path) > common_len))
- {
- const char *rel = last_path + (common_len ? (common_len + 1) : 0);
- int count = count_components(rel);
- while (count--)
- {
- SVN_ERR(pop_stack(db_stack, editor));
- }
- }
+ relpath = APR_ARRAY_IDX(relpaths, i, const char *);
- /*** Step C - Open any directories between the common ancestor
- and the parent of the current path. ***/
- if (*path == '/')
- pdir = svn_fspath__dirname(path, iterpool);
- else
- pdir = svn_relpath_dirname(path, iterpool);
+ SVN_ERR(svn_delta_path_driver_step(state, relpath, iterpool));
+ }
- if (strlen(pdir) > common_len)
- {
- const char *piece = pdir + common_len + 1;
-
- while (1)
- {
- const char *rel = pdir;
-
- /* Find the first separator. */
- piece = strchr(piece, '/');
-
- /* Calculate REL as the portion of PDIR up to (but not
- including) the location to which PIECE is pointing. */
- if (piece)
- rel = apr_pstrmemdup(iterpool, pdir, piece - pdir);
-
- /* Open the subdirectory. */
- SVN_ERR(open_dir(db_stack, editor, rel, pool));
-
- /* If we found a '/', advance our PIECE pointer to
- character just after that '/'. Otherwise, we're
- done. */
- if (piece)
- piece++;
- else
- break;
- }
- }
+ /* Destroy the iteration subpool. */
+ svn_pool_destroy(iterpool);
- /*** Step D - Tell our caller to handle the current path. ***/
- item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, void *);
- parent_db = item->dir_baton;
- subpool = svn_pool_create(pool);
- SVN_ERR(callback_func(&db, parent_db, callback_baton, path, subpool));
- if (db)
+ SVN_ERR(svn_delta_path_driver_finish(state, pool));
+
+ return SVN_NO_ERROR;
+}
+
+struct svn_delta_path_driver_state_t
+{
+ const svn_delta_editor_t *editor;
+ void *edit_baton;
+ svn_delta_path_driver_cb_func2_t callback_func;
+ void *callback_baton;
+ apr_array_header_t *db_stack;
+ const char *last_path;
+ apr_pool_t *pool; /* at least the lifetime of the entire drive */
+};
+
+svn_error_t *
+svn_delta_path_driver_start(svn_delta_path_driver_state_t **state_p,
+ const svn_delta_editor_t *editor,
+ void *edit_baton,
+ svn_delta_path_driver_cb_func2_t callback_func,
+ void *callback_baton,
+ apr_pool_t *pool)
+{
+ svn_delta_path_driver_state_t *state = apr_pcalloc(pool, sizeof(*state));
+
+ state->editor = editor;
+ state->edit_baton = edit_baton;
+ state->callback_func = callback_func;
+ state->callback_baton = callback_baton;
+ state->db_stack = apr_array_make(pool, 4, sizeof(void *));
+ state->last_path = NULL;
+ state->pool = pool;
+
+ *state_p = state;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta_path_driver_step(svn_delta_path_driver_state_t *state,
+ const char *relpath,
+ apr_pool_t *scratch_pool)
+{
+ const char *pdir;
+ const char *common = "";
+ size_t common_len;
+ apr_pool_t *subpool;
+ dir_stack_t *item;
+ void *parent_db, *db;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ /* If the first target path is not the root of the edit, we must first
+ call open_root() ourselves. (If the first target path is the root of
+ the edit, then we expect the user's callback to do so.) */
+ if (!state->last_path && !svn_path_is_empty(relpath))
+ {
+ subpool = svn_pool_create(state->pool);
+ SVN_ERR(state->editor->open_root(state->edit_baton, SVN_INVALID_REVNUM,
+ subpool, &db));
+ push_dir_stack_item(state->db_stack, db, subpool);
+ }
+
+ /*** Step A - Find the common ancestor of the last path and the
+ current one. For the first iteration, this is just the
+ empty string. ***/
+ if (state->last_path)
+ common = svn_relpath_get_longest_ancestor(state->last_path, relpath,
+ scratch_pool);
+ common_len = strlen(common);
+
+ /*** Step B - Close any directories between the last path and
+ the new common ancestor, if any need to be closed.
+ Sometimes there is nothing to do here (like, for the first
+ iteration, or when the last path was an ancestor of the
+ current one). ***/
+ if ((state->last_path) && (strlen(state->last_path) > common_len))
+ {
+ const char *rel = state->last_path + (common_len ? (common_len + 1) : 0);
+ int count = count_components(rel);
+ while (count--)
{
- item = apr_pcalloc(subpool, sizeof(*item));
- item->dir_baton = db;
- item->pool = subpool;
- APR_ARRAY_PUSH(db_stack, void *) = item;
+ SVN_ERR(pop_stack(state->db_stack, state->editor));
}
- else
+ }
+
+ /*** Step C - Open any directories between the common ancestor
+ and the parent of the current path. ***/
+ pdir = svn_relpath_dirname(relpath, scratch_pool);
+
+ if (strlen(pdir) > common_len)
+ {
+ const char *piece = pdir + common_len + 1;
+
+ while (1)
{
- svn_pool_destroy(subpool);
+ const char *rel = pdir;
+
+ /* Find the first separator. */
+ piece = strchr(piece, '/');
+
+ /* Calculate REL as the portion of PDIR up to (but not
+ including) the location to which PIECE is pointing. */
+ if (piece)
+ rel = apr_pstrmemdup(scratch_pool, pdir, piece - pdir);
+
+ /* Open the subdirectory. */
+ SVN_ERR(open_dir(state->db_stack, state->editor, rel, state->pool));
+
+ /* If we found a '/', advance our PIECE pointer to
+ character just after that '/'. Otherwise, we're
+ done. */
+ if (piece)
+ piece++;
+ else
+ break;
}
+ }
- /*** Step E - Save our state for the next iteration. If our
- caller opened or added PATH as a directory, that becomes
- our LAST_PATH. Otherwise, we use PATH's parent
- directory. ***/
-
- /* NOTE: The variable LAST_PATH needs to outlive the loop. */
- if (db)
- last_path = path; /* lives in a pool outside our control. */
- else
- last_path = apr_pstrdup(pool, pdir); /* duping into POOL. */
+ /*** Step D - Tell our caller to handle the current path. ***/
+ if (state->db_stack->nelts)
+ {
+ item = APR_ARRAY_IDX(state->db_stack, state->db_stack->nelts - 1, void *);
+ parent_db = item->dir_baton;
+ }
+ else
+ parent_db = NULL;
+ db = NULL; /* predictable behaviour for callbacks that don't set it */
+ subpool = svn_pool_create(state->pool);
+ SVN_ERR(state->callback_func(&db,
+ state->editor, state->edit_baton, parent_db,
+ state->callback_baton,
+ relpath, subpool));
+ if (db)
+ {
+ push_dir_stack_item(state->db_stack, db, subpool);
+ }
+ else
+ {
+ svn_pool_destroy(subpool);
}
- /* Destroy the iteration subpool. */
- svn_pool_destroy(iterpool);
+ /*** Step E - Save our state for the next iteration. If our
+ caller opened or added PATH as a directory, that becomes
+ our LAST_PATH. Otherwise, we use PATH's parent
+ directory. ***/
+ state->last_path = apr_pstrdup(state->pool, db ? relpath : pdir);
+
+ return SVN_NO_ERROR;
+}
+svn_error_t *
+svn_delta_path_driver_finish(svn_delta_path_driver_state_t *state,
+ apr_pool_t *scratch_pool)
+{
/* Close down any remaining open directory batons. */
- while (db_stack->nelts)
+ while (state->db_stack->nelts)
{
- SVN_ERR(pop_stack(db_stack, editor));
+ SVN_ERR(pop_stack(state->db_stack, state->editor));
}
return SVN_NO_ERROR;