diff options
Diffstat (limited to 'subversion/libsvn_delta/path_driver.c')
-rw-r--r-- | subversion/libsvn_delta/path_driver.c | 313 |
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; |