diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-18 13:33:26 +0000 |
---|---|---|
committer | <> | 2015-07-08 14:41:01 +0000 |
commit | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (patch) | |
tree | 98bae10dde41c746c51ae97ec4f879e330415aa7 /subversion/libsvn_client/list.c | |
parent | 239dfafe71711b2f4c43d7b90a1228d7bdc5195e (diff) | |
download | subversion-tarball-subversion-1.8.13.tar.gz |
Imported from /home/lorry/working-area/delta_subversion-tarball/subversion-1.8.13.tar.gz.subversion-1.8.13
Diffstat (limited to 'subversion/libsvn_client/list.c')
-rw-r--r-- | subversion/libsvn_client/list.c | 439 |
1 files changed, 369 insertions, 70 deletions
diff --git a/subversion/libsvn_client/list.c b/subversion/libsvn_client/list.c index 15250eb..4093893 100644 --- a/subversion/libsvn_client/list.c +++ b/subversion/libsvn_client/list.c @@ -23,6 +23,7 @@ #include "svn_client.h" #include "svn_dirent_uri.h" +#include "svn_hash.h" #include "svn_path.h" #include "svn_pools.h" #include "svn_time.h" @@ -32,8 +33,37 @@ #include "client.h" #include "private/svn_fspath.h" +#include "private/svn_ra_private.h" +#include "private/svn_wc_private.h" #include "svn_private_config.h" +/* Prototypes for referencing before declaration */ +static svn_error_t * +list_externals(apr_hash_t *externals, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +static svn_error_t * +list_internal(const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_boolean_t include_externals, + const char *external_parent_url, + const char *external_target, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + + /* Get the directory entries of DIR at REV (relative to the root of RA_SESSION), getting at least the fields specified by DIRENT_FIELDS. Use the cancellation function/baton of CTX to check for cancellation. @@ -46,7 +76,15 @@ LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t objects and FS_PATH is the absolute filesystem path of the RA session. - Use POOL for temporary allocations. + Use SCRATCH_POOL for temporary allocations. + + If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS + hash table whose keys are URLs of the directory which has externals + definitions, and whose values are the externals description text. + Allocate the hash's keys and values in RESULT_POOL. + + EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items + are listed, otherwise both are set to NULL by the caller. */ static svn_error_t * get_dir_contents(apr_uint32_t dirent_fields, @@ -57,23 +95,31 @@ get_dir_contents(apr_uint32_t dirent_fields, const char *fs_path, svn_depth_t depth, svn_client_ctx_t *ctx, - svn_client_list_func_t list_func, + apr_hash_t *externals, + const char *external_parent_url, + const char *external_target, + svn_client_list_func2_t list_func, void *baton, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_hash_t *tmpdirents; - apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_array_header_t *array; svn_error_t *err; + apr_hash_t *prop_hash = NULL; + const svn_string_t *prop_val = NULL; int i; if (depth == svn_depth_empty) return SVN_NO_ERROR; - /* Get the directory's entries, but not its props. Ignore any - not-authorized errors. */ - err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL, - dir, rev, dirent_fields, pool); + /* Get the directory's entries. If externals hash is non-NULL, get its + properties also. Ignore any not-authorized errors. */ + err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL, + externals ? &prop_hash : NULL, + dir, rev, dirent_fields, scratch_pool); + if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) || (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN))) { @@ -82,16 +128,31 @@ get_dir_contents(apr_uint32_t dirent_fields, } SVN_ERR(err); + /* Filter out svn:externals from all properties hash. */ + if (prop_hash) + prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS); + if (prop_val) + { + const char *url; + + SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool)); + + svn_hash_sets(externals, + svn_path_url_add_component2(url, dir, result_pool), + svn_string_dup(prop_val, result_pool)); + } + if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Sort the hash, so we can call the callback in a "deterministic" order. */ - array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically, pool); + array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically, + scratch_pool); for (i = 0; i < array->nelts; ++i) { svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); const char *path; - svn_dirent_t *the_ent = apr_hash_get(tmpdirents, item->key, item->klen); + svn_dirent_t *the_ent = item->value; svn_lock_t *lock; svn_pool_clear(iterpool); @@ -101,7 +162,7 @@ get_dir_contents(apr_uint32_t dirent_fields, if (locks) { const char *abs_path = svn_fspath__join(fs_path, path, iterpool); - lock = apr_hash_get(locks, abs_path, APR_HASH_KEY_STRING); + lock = svn_hash_gets(locks, abs_path); } else lock = NULL; @@ -109,91 +170,83 @@ get_dir_contents(apr_uint32_t dirent_fields, if (the_ent->kind == svn_node_file || depth == svn_depth_immediates || depth == svn_depth_infinity) - SVN_ERR(list_func(baton, path, the_ent, lock, fs_path, iterpool)); + SVN_ERR(list_func(baton, path, the_ent, lock, fs_path, + external_parent_url, external_target, iterpool)); + /* If externals is non-NULL, populate the externals hash table + recursively for all directory entries. */ if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir) SVN_ERR(get_dir_contents(dirent_fields, path, rev, ra_session, locks, fs_path, depth, ctx, - list_func, baton, iterpool)); + externals, external_parent_url, + external_target, list_func, baton, + result_pool, iterpool)); } svn_pool_destroy(iterpool); return SVN_NO_ERROR; } +/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */ +/* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */ svn_error_t * -svn_client_list2(const char *path_or_url, - const svn_opt_revision_t *peg_revision, - const svn_opt_revision_t *revision, - svn_depth_t depth, - apr_uint32_t dirent_fields, - svn_boolean_t fetch_locks, - svn_client_list_func_t list_func, - void *baton, - svn_client_ctx_t *ctx, - apr_pool_t *pool) +svn_client__ra_stat_compatible(svn_ra_session_t *ra_session, + svn_revnum_t rev, + svn_dirent_t **dirent_p, + apr_uint32_t dirent_fields, + svn_client_ctx_t *ctx, + apr_pool_t *pool) { - svn_ra_session_t *ra_session; - svn_revnum_t rev; - svn_dirent_t *dirent; - const char *url; - const char *repos_root; - const char *fs_path; svn_error_t *err; - apr_hash_t *locks; - - /* We use the kind field to determine if we should recurse, so we - always need it. */ - dirent_fields |= SVN_DIRENT_KIND; - - /* Get an RA plugin for this filesystem object. */ - SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev, - &url, path_or_url, NULL, - peg_revision, - revision, ctx, pool)); - - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool)); - SVN_ERR(svn_client__path_relative_to_root(&fs_path, ctx->wc_ctx, url, - repos_root, TRUE, ra_session, - pool, pool)); - - err = svn_ra_stat(ra_session, "", rev, &dirent, pool); + err = svn_ra_stat(ra_session, "", rev, dirent_p, pool); /* svnserve before 1.2 doesn't support the above, so fall back on a less efficient method. */ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { + const char *repos_root_url; + const char *session_url; svn_node_kind_t kind; + svn_dirent_t *dirent; svn_error_clear(err); + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); + SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool)); + SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool)); if (kind != svn_node_none) { - if (strcmp(url, repos_root) != 0) + if (strcmp(session_url, repos_root_url) != 0) { svn_ra_session_t *parent_session; apr_hash_t *parent_ents; const char *parent_url, *base_name; + apr_pool_t *subpool = svn_pool_create(pool); /* Open another session to the path's parent. This server doesn't support svn_ra_reparent anyway, so don't try it. */ - svn_uri_split(&parent_url, &base_name, url, pool); + svn_uri_split(&parent_url, &base_name, session_url, subpool); - SVN_ERR(svn_client__open_ra_session_internal(&parent_session, - NULL, parent_url, - NULL, NULL, FALSE, - TRUE, ctx, pool)); + SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url, + NULL, ctx, + subpool, subpool)); /* Get all parent's entries, no props. */ SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL, - NULL, "", rev, dirent_fields, pool)); + NULL, "", rev, dirent_fields, subpool)); /* Get the relevant entry. */ - dirent = apr_hash_get(parent_ents, base_name, - APR_HASH_KEY_STRING); + dirent = svn_hash_gets(parent_ents, base_name); + + if (dirent) + *dirent_p = svn_dirent_dup(dirent, pool); + else + *dirent_p = NULL; + + svn_pool_destroy(subpool); /* Close RA session */ } else { @@ -203,7 +256,7 @@ svn_client_list2(const char *path_or_url, be rev. */ dirent = apr_palloc(pool, sizeof(*dirent)); dirent->kind = kind; - dirent->size = 0; + dirent->size = SVN_INVALID_FILESIZE; if (dirent_fields & SVN_DIRENT_HAS_PROPS) { apr_hash_t *props; @@ -220,30 +273,108 @@ svn_client_list2(const char *path_or_url, SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props, pool)); - val = apr_hash_get(props, SVN_PROP_REVISION_DATE, - APR_HASH_KEY_STRING); + val = svn_hash_gets(props, SVN_PROP_REVISION_DATE); if (val) SVN_ERR(svn_time_from_cstring(&dirent->time, val->data, pool)); else dirent->time = 0; - val = apr_hash_get(props, SVN_PROP_REVISION_AUTHOR, - APR_HASH_KEY_STRING); + val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR); dirent->last_author = val ? val->data : NULL; } + + *dirent_p = dirent; } } else - dirent = NULL; + *dirent_p = NULL; } - else if (err) - return svn_error_trace(err); + else + SVN_ERR(err); + + return SVN_NO_ERROR; +} + +/* List the file/directory entries for PATH_OR_URL at REVISION. + The actual node revision selected is determined by the path as + it exists in PEG_REVISION. + + If DEPTH is svn_depth_infinity, then list all file and directory entries + recursively. Else if DEPTH is svn_depth_files, list all files under + PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is + svn_depth_immediates, list all files and include immediate + subdirectories (at svn_depth_empty). Else if DEPTH is + svn_depth_empty, just list PATH_OR_URL with none of its entries. + + DIRENT_FIELDS controls which fields in the svn_dirent_t's are + filled in. To have them totally filled in use SVN_DIRENT_ALL, + otherwise simply bitwise OR together the combination of SVN_DIRENT_* + fields you care about. + + If FETCH_LOCKS is TRUE, include locks when reporting directory entries. + + If INCLUDE_EXTERNALS is TRUE, also list all external items + reached by recursion. DEPTH value passed to the original list target + applies for the externals also. EXTERNAL_PARENT_URL is url of the + directory which has the externals definitions. EXTERNAL_TARGET is the + target subdirectory of externals definitions. + + Report directory entries by invoking LIST_FUNC/BATON. + Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external + items are listed, otherwise both are set to NULL. + + Use authentication baton cached in CTX to authenticate against the + repository. + Use POOL for all allocations. +*/ +static svn_error_t * +list_internal(const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_boolean_t include_externals, + const char *external_parent_url, + const char *external_target, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_client__pathrev_t *loc; + svn_dirent_t *dirent; + const char *fs_path; + svn_error_t *err; + apr_hash_t *locks; + apr_hash_t *externals; + + if (include_externals) + externals = apr_hash_make(pool); + else + externals = NULL; + + /* We use the kind field to determine if we should recurse, so we + always need it. */ + dirent_fields |= SVN_DIRENT_KIND; + + /* Get an RA plugin for this filesystem object. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, + path_or_url, NULL, + peg_revision, + revision, ctx, pool)); + + fs_path = svn_client__pathrev_fspath(loc, pool); + + SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent, + dirent_fields, ctx, pool)); if (! dirent) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("URL '%s' non-existent in revision %ld"), - url, rev); + loc->url, loc->rev); /* Maybe get all locks under url. */ if (fetch_locks) @@ -265,16 +396,184 @@ svn_client_list2(const char *path_or_url, /* Report the dirent for the target. */ SVN_ERR(list_func(baton, "", dirent, locks - ? (apr_hash_get(locks, fs_path, - APR_HASH_KEY_STRING)) - : NULL, fs_path, pool)); + ? (svn_hash_gets(locks, fs_path)) + : NULL, fs_path, external_parent_url, + external_target, pool)); if (dirent->kind == svn_node_dir && (depth == svn_depth_files || depth == svn_depth_immediates || depth == svn_depth_infinity)) - SVN_ERR(get_dir_contents(dirent_fields, "", rev, ra_session, locks, - fs_path, depth, ctx, list_func, baton, pool)); + SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks, + fs_path, depth, ctx, externals, + external_parent_url, external_target, list_func, + baton, pool, pool)); + + /* We handle externals after listing entries under path_or_url, so that + handling external items (and any errors therefrom) doesn't delay + the primary operation. */ + if (include_externals && apr_hash_count(externals)) + { + /* The 'externals' hash populated by get_dir_contents() is processed + here. */ + SVN_ERR(list_externals(externals, depth, dirent_fields, + fetch_locks, list_func, baton, + ctx, pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +wrap_list_error(const svn_client_ctx_t *ctx, + const char *target_abspath, + svn_error_t *err, + apr_pool_t *scratch_pool) +{ + if (err && err->apr_err != SVN_ERR_CANCELLED) + { + if (ctx->notify_func2) + { + svn_wc_notify_t *notifier = svn_wc_create_notify( + target_abspath, + svn_wc_notify_failed_external, + scratch_pool); + notifier->err = err; + ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool); + } + svn_error_clear(err); + return SVN_NO_ERROR; + } + + return err; +} + + +/* Walk through all the external items and list them. */ +static svn_error_t * +list_external_items(apr_array_header_t *external_items, + const char *externals_parent_url, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *externals_parent_repos_root_url; + apr_pool_t *iterpool; + int i; + + SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url, + NULL /* uuid */, + externals_parent_url, ctx, + scratch_pool, scratch_pool)); + + iterpool = svn_pool_create(scratch_pool); + + for (i = 0; i < external_items->nelts; i++) + { + const char *resolved_url; + + svn_wc_external_item2_t *item = + APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *); + + svn_pool_clear(iterpool); + + SVN_ERR(svn_wc__resolve_relative_external_url( + &resolved_url, + item, + externals_parent_repos_root_url, + externals_parent_url, + iterpool, iterpool)); + + /* List the external */ + SVN_ERR(wrap_list_error(ctx, item->target_dir, + list_internal(resolved_url, + &item->peg_revision, + &item->revision, + depth, dirent_fields, + fetch_locks, + TRUE, + externals_parent_url, + item->target_dir, + list_func, baton, ctx, + iterpool), + iterpool)); + + } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* List external items defined on each external in EXTERNALS, a const char * + externals_parent_url(url of the directory which has the externals + definitions) of all externals mapping to the svn_string_t * externals_desc + (externals description text). All other options are the same as those + passed to svn_client_list(). */ +static svn_error_t * +list_externals(apr_hash_t *externals, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, externals); + hi; + hi = apr_hash_next(hi)) + { + const char *externals_parent_url = svn__apr_hash_index_key(hi); + svn_string_t *externals_desc = svn__apr_hash_index_val(hi); + apr_array_header_t *external_items; + + svn_pool_clear(iterpool); + + SVN_ERR(svn_wc_parse_externals_description3(&external_items, + externals_parent_url, + externals_desc->data, + FALSE, iterpool)); + + if (! external_items->nelts) + continue; + + SVN_ERR(list_external_items(external_items, externals_parent_url, depth, + dirent_fields, fetch_locks, list_func, + baton, ctx, iterpool)); + + } + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } + + +svn_error_t * +svn_client_list3(const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_depth_t depth, + apr_uint32_t dirent_fields, + svn_boolean_t fetch_locks, + svn_boolean_t include_externals, + svn_client_list_func2_t list_func, + void *baton, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + + return svn_error_trace(list_internal(path_or_url, peg_revision, + revision, + depth, dirent_fields, + fetch_locks, + include_externals, + NULL, NULL, list_func, + baton, ctx, pool)); +} |