diff options
Diffstat (limited to 'subversion/libsvn_client/list.c')
-rw-r--r-- | subversion/libsvn_client/list.c | 160 |
1 files changed, 135 insertions, 25 deletions
diff --git a/subversion/libsvn_client/list.c b/subversion/libsvn_client/list.c index c29b8bd68dc44..78433c337e58b 100644 --- a/subversion/libsvn_client/list.c +++ b/subversion/libsvn_client/list.c @@ -21,6 +21,8 @@ * ==================================================================== */ +#include <apr_fnmatch.h> + #include "svn_client.h" #include "svn_dirent_uri.h" #include "svn_hash.h" @@ -35,12 +37,14 @@ #include "private/svn_fspath.h" #include "private/svn_ra_private.h" #include "private/svn_sorts_private.h" +#include "private/svn_utf_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, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -53,6 +57,7 @@ static svn_error_t * list_internal(const char *path_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -64,6 +69,18 @@ list_internal(const char *path_or_url, svn_client_ctx_t *ctx, apr_pool_t *pool); +/* Return TRUE if S matches any of the const char * in PATTERNS. + * Note that any S will match if PATTERNS is empty. + * Use SCRATCH_BUFFER for temporary string contents. */ +static svn_boolean_t +match_patterns(const char *s, + const apr_array_header_t *patterns, + svn_membuf_t *scratch_buffer) +{ + return patterns + ? svn_utf__fuzzy_glob_match(s, patterns, scratch_buffer) + : TRUE; +} /* Get the directory entries of DIR at REV (relative to the root of RA_SESSION), getting at least the fields specified by DIRENT_FIELDS. @@ -75,6 +92,10 @@ list_internal(const char *path_or_url, if svn_depth_infinity, invoke it on file and directory entries and recurse into the directory entries with the same depth. + If PATTERNS is not empty, the last path segments must match at least + one of const char * patterns in it or the respective dirent will not + be reported. + 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 SCRATCH_POOL for temporary allocations. @@ -86,6 +107,8 @@ list_internal(const char *path_or_url, EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items are listed, otherwise both are set to NULL by the caller. + + Use SCRATCH_BUFFER for temporary string contents. */ static svn_error_t * get_dir_contents(apr_uint32_t dirent_fields, @@ -94,6 +117,7 @@ get_dir_contents(apr_uint32_t dirent_fields, svn_ra_session_t *ra_session, apr_hash_t *locks, const char *fs_path, + const apr_array_header_t *patterns, svn_depth_t depth, svn_client_ctx_t *ctx, apr_hash_t *externals, @@ -101,6 +125,7 @@ get_dir_contents(apr_uint32_t dirent_fields, const char *external_target, svn_client_list_func2_t list_func, void *baton, + svn_membuf_t *scratch_buffer, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -175,23 +200,71 @@ 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, - external_parent_url, external_target, iterpool)); + if (match_patterns(item->key, patterns, scratch_buffer)) + 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, + SVN_ERR(get_dir_contents(dirent_fields, path, rev, ra_session, + locks, fs_path, patterns, depth, ctx, externals, external_parent_url, external_target, list_func, baton, - result_pool, iterpool)); + scratch_buffer, result_pool, iterpool)); } svn_pool_destroy(iterpool); return SVN_NO_ERROR; } +/* Baton type to be used with list_receiver. */ +typedef struct receiver_baton_t +{ + /* Wrapped callback function to invoke. */ + svn_client_list_func2_t list_func; + + /* Baton to be used with LIST_FUNC. */ + void *list_baton; + + /* Client context providing cancellation support. */ + svn_client_ctx_t *ctx; + + /* All locks found for the whole tree; pick yours. */ + apr_hash_t *locks; + + /* Start path of the operation. */ + const char *fs_base_path; +} receiver_baton_t; + +/* Implement svn_ra_dirent_receiver_t. + The BATON type must be a receiver_baton_t. */ +static svn_error_t * +list_receiver(const char *rel_path, + svn_dirent_t *dirent, + void *baton, + apr_pool_t *pool) +{ + receiver_baton_t *b = baton; + const svn_lock_t *lock = NULL; + + /* We only report the path relative to the start path. */ + rel_path = svn_dirent_skip_ancestor(b->fs_base_path, rel_path); + + if (b->locks) + { + const char *abs_path = svn_dirent_join(b->fs_base_path, rel_path, pool); + lock = svn_hash_gets(b->locks, abs_path); + } + + if (b->ctx->cancel_func) + SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); + + SVN_ERR(b->list_func(b->list_baton, rel_path, dirent, lock, + b->fs_base_path, NULL, NULL, pool)); + + 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 @@ -204,6 +277,10 @@ get_dir_contents(apr_uint32_t dirent_fields, subdirectories (at svn_depth_empty). Else if DEPTH is svn_depth_empty, just list PATH_OR_URL with none of its entries. + If PATTERNS is not NULL, the last path segments must match at least + one of const char * patterns in it or the respective dirent will not + be reported. + 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_* @@ -230,6 +307,7 @@ static svn_error_t * list_internal(const char *path_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -248,6 +326,7 @@ list_internal(const char *path_or_url, svn_error_t *err; apr_hash_t *locks; apr_hash_t *externals; + svn_membuf_t scratch_buffer; if (include_externals) externals = apr_hash_make(pool); @@ -266,12 +345,6 @@ list_internal(const char *path_or_url, fs_path = svn_client__pathrev_fspath(loc, pool); - SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool)); - if (! dirent) - return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, - _("URL '%s' non-existent in revision %ld"), - loc->url, loc->rev); - /* Maybe get all locks under url. */ if (fetch_locks) { @@ -290,20 +363,52 @@ list_internal(const char *path_or_url, else locks = NULL; + /* Try to use the efficient and fully authz-filtered code path. */ + if (!include_externals) + { + receiver_baton_t receiver_baton; + receiver_baton.list_baton = baton; + receiver_baton.ctx = ctx; + receiver_baton.list_func = list_func; + receiver_baton.locks = locks; + receiver_baton.fs_base_path = fs_path; + + err = svn_ra_list(ra_session, "", loc->rev, patterns, depth, + dirent_fields, list_receiver, &receiver_baton, pool); + + if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE)) + svn_error_clear(err); + else + return svn_error_trace(err); + } + + /* Stat for the file / directory node itself. */ + SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool)); + if (! dirent) + return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("URL '%s' non-existent in revision %ld"), + loc->url, loc->rev); + + /* We need a scratch buffer for temporary string data. + * Create one with a reasonable initial size. */ + svn_membuf__create(&scratch_buffer, 256, pool); + /* Report the dirent for the target. */ - SVN_ERR(list_func(baton, "", dirent, locks - ? (svn_hash_gets(locks, fs_path)) - : NULL, fs_path, external_parent_url, - external_target, pool)); + if (match_patterns(svn_dirent_dirname(fs_path, pool), patterns, + &scratch_buffer)) + SVN_ERR(list_func(baton, "", dirent, locks + ? (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, "", loc->rev, ra_session, locks, - fs_path, depth, ctx, externals, + fs_path, patterns, depth, ctx, externals, external_parent_url, external_target, list_func, - baton, pool, pool)); + baton, &scratch_buffer, pool, pool)); /* We handle externals after listing entries under path_or_url, so that handling external items (and any errors therefrom) doesn't delay @@ -312,7 +417,7 @@ list_internal(const char *path_or_url, { /* The 'externals' hash populated by get_dir_contents() is processed here. */ - SVN_ERR(list_externals(externals, depth, dirent_fields, + SVN_ERR(list_externals(externals, patterns, depth, dirent_fields, fetch_locks, list_func, baton, ctx, pool)); } @@ -349,6 +454,7 @@ wrap_list_error(const svn_client_ctx_t *ctx, static svn_error_t * list_external_items(apr_array_header_t *external_items, const char *externals_parent_url, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -389,6 +495,7 @@ list_external_items(apr_array_header_t *external_items, list_internal(resolved_url, &item->peg_revision, &item->revision, + patterns, depth, dirent_fields, fetch_locks, TRUE, @@ -411,6 +518,7 @@ list_external_items(apr_array_header_t *external_items, passed to svn_client_list(). */ static svn_error_t * list_externals(apr_hash_t *externals, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -440,9 +548,10 @@ list_externals(apr_hash_t *externals, 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_ERR(list_external_items(external_items, externals_parent_url, + patterns, depth, dirent_fields, + fetch_locks, list_func, baton, ctx, + iterpool)); } svn_pool_destroy(iterpool); @@ -452,9 +561,10 @@ list_externals(apr_hash_t *externals, svn_error_t * -svn_client_list3(const char *path_or_url, +svn_client_list4(const char *path_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, + const apr_array_header_t *patterns, svn_depth_t depth, apr_uint32_t dirent_fields, svn_boolean_t fetch_locks, @@ -462,14 +572,14 @@ svn_client_list3(const char *path_or_url, svn_client_list_func2_t list_func, void *baton, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { return svn_error_trace(list_internal(path_or_url, peg_revision, - revision, + revision, patterns, depth, dirent_fields, fetch_locks, include_externals, NULL, NULL, list_func, - baton, ctx, pool)); + baton, ctx, scratch_pool)); } |