1251881Speter/* 2251881Speter * list.c: list local and remote directory entries. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include "svn_client.h" 25251881Speter#include "svn_dirent_uri.h" 26251881Speter#include "svn_hash.h" 27251881Speter#include "svn_path.h" 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_time.h" 30251881Speter#include "svn_sorts.h" 31251881Speter#include "svn_props.h" 32251881Speter 33251881Speter#include "client.h" 34251881Speter 35251881Speter#include "private/svn_fspath.h" 36251881Speter#include "private/svn_ra_private.h" 37251881Speter#include "private/svn_wc_private.h" 38251881Speter#include "svn_private_config.h" 39251881Speter 40251881Speter/* Prototypes for referencing before declaration */ 41251881Speterstatic svn_error_t * 42251881Speterlist_externals(apr_hash_t *externals, 43251881Speter svn_depth_t depth, 44251881Speter apr_uint32_t dirent_fields, 45251881Speter svn_boolean_t fetch_locks, 46251881Speter svn_client_list_func2_t list_func, 47251881Speter void *baton, 48251881Speter svn_client_ctx_t *ctx, 49251881Speter apr_pool_t *scratch_pool); 50251881Speter 51251881Speterstatic svn_error_t * 52251881Speterlist_internal(const char *path_or_url, 53251881Speter const svn_opt_revision_t *peg_revision, 54251881Speter const svn_opt_revision_t *revision, 55251881Speter svn_depth_t depth, 56251881Speter apr_uint32_t dirent_fields, 57251881Speter svn_boolean_t fetch_locks, 58251881Speter svn_boolean_t include_externals, 59251881Speter const char *external_parent_url, 60251881Speter const char *external_target, 61251881Speter svn_client_list_func2_t list_func, 62251881Speter void *baton, 63251881Speter svn_client_ctx_t *ctx, 64251881Speter apr_pool_t *pool); 65251881Speter 66251881Speter 67251881Speter/* Get the directory entries of DIR at REV (relative to the root of 68251881Speter RA_SESSION), getting at least the fields specified by DIRENT_FIELDS. 69251881Speter Use the cancellation function/baton of CTX to check for cancellation. 70251881Speter 71251881Speter If DEPTH is svn_depth_empty, return immediately. If DEPTH is 72251881Speter svn_depth_files, invoke LIST_FUNC on the file entries with BATON; 73251881Speter if svn_depth_immediates, invoke it on file and directory entries; 74251881Speter if svn_depth_infinity, invoke it on file and directory entries and 75251881Speter recurse into the directory entries with the same depth. 76251881Speter 77251881Speter LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t 78251881Speter objects and FS_PATH is the absolute filesystem path of the RA session. 79251881Speter Use SCRATCH_POOL for temporary allocations. 80251881Speter 81251881Speter If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS 82251881Speter hash table whose keys are URLs of the directory which has externals 83251881Speter definitions, and whose values are the externals description text. 84251881Speter Allocate the hash's keys and values in RESULT_POOL. 85251881Speter 86251881Speter EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items 87251881Speter are listed, otherwise both are set to NULL by the caller. 88251881Speter*/ 89251881Speterstatic svn_error_t * 90251881Speterget_dir_contents(apr_uint32_t dirent_fields, 91251881Speter const char *dir, 92251881Speter svn_revnum_t rev, 93251881Speter svn_ra_session_t *ra_session, 94251881Speter apr_hash_t *locks, 95251881Speter const char *fs_path, 96251881Speter svn_depth_t depth, 97251881Speter svn_client_ctx_t *ctx, 98251881Speter apr_hash_t *externals, 99251881Speter const char *external_parent_url, 100251881Speter const char *external_target, 101251881Speter svn_client_list_func2_t list_func, 102251881Speter void *baton, 103251881Speter apr_pool_t *result_pool, 104251881Speter apr_pool_t *scratch_pool) 105251881Speter{ 106251881Speter apr_hash_t *tmpdirents; 107251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 108251881Speter apr_array_header_t *array; 109251881Speter svn_error_t *err; 110251881Speter apr_hash_t *prop_hash = NULL; 111251881Speter const svn_string_t *prop_val = NULL; 112251881Speter int i; 113251881Speter 114251881Speter if (depth == svn_depth_empty) 115251881Speter return SVN_NO_ERROR; 116251881Speter 117251881Speter /* Get the directory's entries. If externals hash is non-NULL, get its 118251881Speter properties also. Ignore any not-authorized errors. */ 119251881Speter err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL, 120251881Speter externals ? &prop_hash : NULL, 121251881Speter dir, rev, dirent_fields, scratch_pool); 122251881Speter 123251881Speter if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) || 124251881Speter (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN))) 125251881Speter { 126251881Speter svn_error_clear(err); 127251881Speter return SVN_NO_ERROR; 128251881Speter } 129251881Speter SVN_ERR(err); 130251881Speter 131251881Speter /* Filter out svn:externals from all properties hash. */ 132251881Speter if (prop_hash) 133251881Speter prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS); 134251881Speter if (prop_val) 135251881Speter { 136251881Speter const char *url; 137251881Speter 138251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool)); 139251881Speter 140251881Speter svn_hash_sets(externals, 141251881Speter svn_path_url_add_component2(url, dir, result_pool), 142251881Speter svn_string_dup(prop_val, result_pool)); 143251881Speter } 144251881Speter 145251881Speter if (ctx->cancel_func) 146251881Speter SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 147251881Speter 148251881Speter /* Sort the hash, so we can call the callback in a "deterministic" order. */ 149251881Speter array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically, 150251881Speter scratch_pool); 151251881Speter for (i = 0; i < array->nelts; ++i) 152251881Speter { 153251881Speter svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); 154251881Speter const char *path; 155251881Speter svn_dirent_t *the_ent = item->value; 156251881Speter svn_lock_t *lock; 157251881Speter 158251881Speter svn_pool_clear(iterpool); 159251881Speter 160251881Speter path = svn_relpath_join(dir, item->key, iterpool); 161251881Speter 162251881Speter if (locks) 163251881Speter { 164251881Speter const char *abs_path = svn_fspath__join(fs_path, path, iterpool); 165251881Speter lock = svn_hash_gets(locks, abs_path); 166251881Speter } 167251881Speter else 168251881Speter lock = NULL; 169251881Speter 170251881Speter if (the_ent->kind == svn_node_file 171251881Speter || depth == svn_depth_immediates 172251881Speter || depth == svn_depth_infinity) 173251881Speter SVN_ERR(list_func(baton, path, the_ent, lock, fs_path, 174251881Speter external_parent_url, external_target, iterpool)); 175251881Speter 176251881Speter /* If externals is non-NULL, populate the externals hash table 177251881Speter recursively for all directory entries. */ 178251881Speter if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir) 179251881Speter SVN_ERR(get_dir_contents(dirent_fields, path, rev, 180251881Speter ra_session, locks, fs_path, depth, ctx, 181251881Speter externals, external_parent_url, 182251881Speter external_target, list_func, baton, 183251881Speter result_pool, iterpool)); 184251881Speter } 185251881Speter 186251881Speter svn_pool_destroy(iterpool); 187251881Speter return SVN_NO_ERROR; 188251881Speter} 189251881Speter 190251881Speter/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */ 191251881Speter/* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */ 192251881Spetersvn_error_t * 193251881Spetersvn_client__ra_stat_compatible(svn_ra_session_t *ra_session, 194251881Speter svn_revnum_t rev, 195251881Speter svn_dirent_t **dirent_p, 196251881Speter apr_uint32_t dirent_fields, 197251881Speter svn_client_ctx_t *ctx, 198251881Speter apr_pool_t *pool) 199251881Speter{ 200251881Speter svn_error_t *err; 201251881Speter 202251881Speter err = svn_ra_stat(ra_session, "", rev, dirent_p, pool); 203251881Speter 204251881Speter /* svnserve before 1.2 doesn't support the above, so fall back on 205251881Speter a less efficient method. */ 206251881Speter if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) 207251881Speter { 208251881Speter const char *repos_root_url; 209251881Speter const char *session_url; 210251881Speter svn_node_kind_t kind; 211251881Speter svn_dirent_t *dirent; 212251881Speter 213251881Speter svn_error_clear(err); 214251881Speter 215251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); 216251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool)); 217251881Speter 218251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool)); 219251881Speter 220251881Speter if (kind != svn_node_none) 221251881Speter { 222251881Speter if (strcmp(session_url, repos_root_url) != 0) 223251881Speter { 224251881Speter svn_ra_session_t *parent_session; 225251881Speter apr_hash_t *parent_ents; 226251881Speter const char *parent_url, *base_name; 227251881Speter apr_pool_t *subpool = svn_pool_create(pool); 228251881Speter 229251881Speter /* Open another session to the path's parent. This server 230251881Speter doesn't support svn_ra_reparent anyway, so don't try it. */ 231251881Speter svn_uri_split(&parent_url, &base_name, session_url, subpool); 232251881Speter 233251881Speter SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url, 234251881Speter NULL, ctx, 235251881Speter subpool, subpool)); 236251881Speter 237251881Speter /* Get all parent's entries, no props. */ 238251881Speter SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL, 239251881Speter NULL, "", rev, dirent_fields, subpool)); 240251881Speter 241251881Speter /* Get the relevant entry. */ 242251881Speter dirent = svn_hash_gets(parent_ents, base_name); 243251881Speter 244251881Speter if (dirent) 245251881Speter *dirent_p = svn_dirent_dup(dirent, pool); 246251881Speter else 247251881Speter *dirent_p = NULL; 248251881Speter 249251881Speter svn_pool_destroy(subpool); /* Close RA session */ 250251881Speter } 251251881Speter else 252251881Speter { 253251881Speter /* We can't get the directory entry for the repository root, 254251881Speter but we can still get the information we want. 255251881Speter The created-rev of the repository root must, by definition, 256251881Speter be rev. */ 257251881Speter dirent = apr_palloc(pool, sizeof(*dirent)); 258251881Speter dirent->kind = kind; 259251881Speter dirent->size = SVN_INVALID_FILESIZE; 260251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 261251881Speter { 262251881Speter apr_hash_t *props; 263251881Speter SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props, 264251881Speter "", rev, 0 /* no dirent fields */, 265251881Speter pool)); 266251881Speter dirent->has_props = (apr_hash_count(props) != 0); 267251881Speter } 268251881Speter dirent->created_rev = rev; 269251881Speter if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR)) 270251881Speter { 271251881Speter apr_hash_t *props; 272251881Speter svn_string_t *val; 273251881Speter 274251881Speter SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props, 275251881Speter pool)); 276251881Speter val = svn_hash_gets(props, SVN_PROP_REVISION_DATE); 277251881Speter if (val) 278251881Speter SVN_ERR(svn_time_from_cstring(&dirent->time, val->data, 279251881Speter pool)); 280251881Speter else 281251881Speter dirent->time = 0; 282251881Speter 283251881Speter val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR); 284251881Speter dirent->last_author = val ? val->data : NULL; 285251881Speter } 286251881Speter 287251881Speter *dirent_p = dirent; 288251881Speter } 289251881Speter } 290251881Speter else 291251881Speter *dirent_p = NULL; 292251881Speter } 293251881Speter else 294251881Speter SVN_ERR(err); 295251881Speter 296251881Speter return SVN_NO_ERROR; 297251881Speter} 298251881Speter 299251881Speter/* List the file/directory entries for PATH_OR_URL at REVISION. 300251881Speter The actual node revision selected is determined by the path as 301251881Speter it exists in PEG_REVISION. 302251881Speter 303251881Speter If DEPTH is svn_depth_infinity, then list all file and directory entries 304251881Speter recursively. Else if DEPTH is svn_depth_files, list all files under 305251881Speter PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is 306251881Speter svn_depth_immediates, list all files and include immediate 307251881Speter subdirectories (at svn_depth_empty). Else if DEPTH is 308251881Speter svn_depth_empty, just list PATH_OR_URL with none of its entries. 309251881Speter 310251881Speter DIRENT_FIELDS controls which fields in the svn_dirent_t's are 311251881Speter filled in. To have them totally filled in use SVN_DIRENT_ALL, 312251881Speter otherwise simply bitwise OR together the combination of SVN_DIRENT_* 313251881Speter fields you care about. 314251881Speter 315251881Speter If FETCH_LOCKS is TRUE, include locks when reporting directory entries. 316251881Speter 317251881Speter If INCLUDE_EXTERNALS is TRUE, also list all external items 318251881Speter reached by recursion. DEPTH value passed to the original list target 319251881Speter applies for the externals also. EXTERNAL_PARENT_URL is url of the 320251881Speter directory which has the externals definitions. EXTERNAL_TARGET is the 321251881Speter target subdirectory of externals definitions. 322251881Speter 323251881Speter Report directory entries by invoking LIST_FUNC/BATON. 324251881Speter Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external 325251881Speter items are listed, otherwise both are set to NULL. 326251881Speter 327251881Speter Use authentication baton cached in CTX to authenticate against the 328251881Speter repository. 329251881Speter 330251881Speter Use POOL for all allocations. 331251881Speter*/ 332251881Speterstatic svn_error_t * 333251881Speterlist_internal(const char *path_or_url, 334251881Speter const svn_opt_revision_t *peg_revision, 335251881Speter const svn_opt_revision_t *revision, 336251881Speter svn_depth_t depth, 337251881Speter apr_uint32_t dirent_fields, 338251881Speter svn_boolean_t fetch_locks, 339251881Speter svn_boolean_t include_externals, 340251881Speter const char *external_parent_url, 341251881Speter const char *external_target, 342251881Speter svn_client_list_func2_t list_func, 343251881Speter void *baton, 344251881Speter svn_client_ctx_t *ctx, 345251881Speter apr_pool_t *pool) 346251881Speter{ 347251881Speter svn_ra_session_t *ra_session; 348251881Speter svn_client__pathrev_t *loc; 349251881Speter svn_dirent_t *dirent; 350251881Speter const char *fs_path; 351251881Speter svn_error_t *err; 352251881Speter apr_hash_t *locks; 353251881Speter apr_hash_t *externals; 354251881Speter 355251881Speter if (include_externals) 356251881Speter externals = apr_hash_make(pool); 357251881Speter else 358251881Speter externals = NULL; 359251881Speter 360251881Speter /* We use the kind field to determine if we should recurse, so we 361251881Speter always need it. */ 362251881Speter dirent_fields |= SVN_DIRENT_KIND; 363251881Speter 364251881Speter /* Get an RA plugin for this filesystem object. */ 365251881Speter SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, 366251881Speter path_or_url, NULL, 367251881Speter peg_revision, 368251881Speter revision, ctx, pool)); 369251881Speter 370251881Speter fs_path = svn_client__pathrev_fspath(loc, pool); 371251881Speter 372251881Speter SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent, 373251881Speter dirent_fields, ctx, pool)); 374251881Speter if (! dirent) 375251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 376251881Speter _("URL '%s' non-existent in revision %ld"), 377251881Speter loc->url, loc->rev); 378251881Speter 379251881Speter /* Maybe get all locks under url. */ 380251881Speter if (fetch_locks) 381251881Speter { 382251881Speter /* IMPORTANT: If locks are stored in a more temporary pool, we need 383251881Speter to fix store_dirent below to duplicate the locks. */ 384251881Speter err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool); 385251881Speter 386251881Speter if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) 387251881Speter { 388251881Speter svn_error_clear(err); 389251881Speter locks = NULL; 390251881Speter } 391251881Speter else if (err) 392251881Speter return svn_error_trace(err); 393251881Speter } 394251881Speter else 395251881Speter locks = NULL; 396251881Speter 397251881Speter /* Report the dirent for the target. */ 398251881Speter SVN_ERR(list_func(baton, "", dirent, locks 399251881Speter ? (svn_hash_gets(locks, fs_path)) 400251881Speter : NULL, fs_path, external_parent_url, 401251881Speter external_target, pool)); 402251881Speter 403251881Speter if (dirent->kind == svn_node_dir 404251881Speter && (depth == svn_depth_files 405251881Speter || depth == svn_depth_immediates 406251881Speter || depth == svn_depth_infinity)) 407251881Speter SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks, 408251881Speter fs_path, depth, ctx, externals, 409251881Speter external_parent_url, external_target, list_func, 410251881Speter baton, pool, pool)); 411251881Speter 412251881Speter /* We handle externals after listing entries under path_or_url, so that 413251881Speter handling external items (and any errors therefrom) doesn't delay 414251881Speter the primary operation. */ 415251881Speter if (include_externals && apr_hash_count(externals)) 416251881Speter { 417251881Speter /* The 'externals' hash populated by get_dir_contents() is processed 418251881Speter here. */ 419251881Speter SVN_ERR(list_externals(externals, depth, dirent_fields, 420251881Speter fetch_locks, list_func, baton, 421251881Speter ctx, pool)); 422251881Speter } 423251881Speter 424251881Speter return SVN_NO_ERROR; 425251881Speter} 426251881Speter 427251881Speterstatic svn_error_t * 428251881Speterwrap_list_error(const svn_client_ctx_t *ctx, 429251881Speter const char *target_abspath, 430251881Speter svn_error_t *err, 431251881Speter apr_pool_t *scratch_pool) 432251881Speter{ 433251881Speter if (err && err->apr_err != SVN_ERR_CANCELLED) 434251881Speter { 435251881Speter if (ctx->notify_func2) 436251881Speter { 437251881Speter svn_wc_notify_t *notifier = svn_wc_create_notify( 438251881Speter target_abspath, 439251881Speter svn_wc_notify_failed_external, 440251881Speter scratch_pool); 441251881Speter notifier->err = err; 442251881Speter ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool); 443251881Speter } 444251881Speter svn_error_clear(err); 445251881Speter return SVN_NO_ERROR; 446251881Speter } 447251881Speter 448251881Speter return err; 449251881Speter} 450251881Speter 451251881Speter 452251881Speter/* Walk through all the external items and list them. */ 453251881Speterstatic svn_error_t * 454251881Speterlist_external_items(apr_array_header_t *external_items, 455251881Speter const char *externals_parent_url, 456251881Speter svn_depth_t depth, 457251881Speter apr_uint32_t dirent_fields, 458251881Speter svn_boolean_t fetch_locks, 459251881Speter svn_client_list_func2_t list_func, 460251881Speter void *baton, 461251881Speter svn_client_ctx_t *ctx, 462251881Speter apr_pool_t *scratch_pool) 463251881Speter{ 464251881Speter const char *externals_parent_repos_root_url; 465251881Speter apr_pool_t *iterpool; 466251881Speter int i; 467251881Speter 468251881Speter SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url, 469251881Speter NULL /* uuid */, 470251881Speter externals_parent_url, ctx, 471251881Speter scratch_pool, scratch_pool)); 472251881Speter 473251881Speter iterpool = svn_pool_create(scratch_pool); 474251881Speter 475251881Speter for (i = 0; i < external_items->nelts; i++) 476251881Speter { 477251881Speter const char *resolved_url; 478251881Speter 479251881Speter svn_wc_external_item2_t *item = 480251881Speter APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *); 481251881Speter 482251881Speter svn_pool_clear(iterpool); 483251881Speter 484251881Speter SVN_ERR(svn_wc__resolve_relative_external_url( 485251881Speter &resolved_url, 486251881Speter item, 487251881Speter externals_parent_repos_root_url, 488251881Speter externals_parent_url, 489251881Speter iterpool, iterpool)); 490251881Speter 491251881Speter /* List the external */ 492251881Speter SVN_ERR(wrap_list_error(ctx, item->target_dir, 493251881Speter list_internal(resolved_url, 494251881Speter &item->peg_revision, 495251881Speter &item->revision, 496251881Speter depth, dirent_fields, 497251881Speter fetch_locks, 498251881Speter TRUE, 499251881Speter externals_parent_url, 500251881Speter item->target_dir, 501251881Speter list_func, baton, ctx, 502251881Speter iterpool), 503251881Speter iterpool)); 504251881Speter 505251881Speter } 506251881Speter svn_pool_destroy(iterpool); 507251881Speter 508251881Speter return SVN_NO_ERROR; 509251881Speter} 510251881Speter 511251881Speter/* List external items defined on each external in EXTERNALS, a const char * 512251881Speter externals_parent_url(url of the directory which has the externals 513251881Speter definitions) of all externals mapping to the svn_string_t * externals_desc 514251881Speter (externals description text). All other options are the same as those 515251881Speter passed to svn_client_list(). */ 516251881Speterstatic svn_error_t * 517251881Speterlist_externals(apr_hash_t *externals, 518251881Speter svn_depth_t depth, 519251881Speter apr_uint32_t dirent_fields, 520251881Speter svn_boolean_t fetch_locks, 521251881Speter svn_client_list_func2_t list_func, 522251881Speter void *baton, 523251881Speter svn_client_ctx_t *ctx, 524251881Speter apr_pool_t *scratch_pool) 525251881Speter{ 526251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 527251881Speter apr_hash_index_t *hi; 528251881Speter 529251881Speter for (hi = apr_hash_first(scratch_pool, externals); 530251881Speter hi; 531251881Speter hi = apr_hash_next(hi)) 532251881Speter { 533251881Speter const char *externals_parent_url = svn__apr_hash_index_key(hi); 534251881Speter svn_string_t *externals_desc = svn__apr_hash_index_val(hi); 535251881Speter apr_array_header_t *external_items; 536251881Speter 537251881Speter svn_pool_clear(iterpool); 538251881Speter 539251881Speter SVN_ERR(svn_wc_parse_externals_description3(&external_items, 540251881Speter externals_parent_url, 541251881Speter externals_desc->data, 542251881Speter FALSE, iterpool)); 543251881Speter 544251881Speter if (! external_items->nelts) 545251881Speter continue; 546251881Speter 547251881Speter SVN_ERR(list_external_items(external_items, externals_parent_url, depth, 548251881Speter dirent_fields, fetch_locks, list_func, 549251881Speter baton, ctx, iterpool)); 550251881Speter 551251881Speter } 552251881Speter svn_pool_destroy(iterpool); 553251881Speter 554251881Speter return SVN_NO_ERROR; 555251881Speter} 556251881Speter 557251881Speter 558251881Spetersvn_error_t * 559251881Spetersvn_client_list3(const char *path_or_url, 560251881Speter const svn_opt_revision_t *peg_revision, 561251881Speter const svn_opt_revision_t *revision, 562251881Speter svn_depth_t depth, 563251881Speter apr_uint32_t dirent_fields, 564251881Speter svn_boolean_t fetch_locks, 565251881Speter svn_boolean_t include_externals, 566251881Speter svn_client_list_func2_t list_func, 567251881Speter void *baton, 568251881Speter svn_client_ctx_t *ctx, 569251881Speter apr_pool_t *pool) 570251881Speter{ 571251881Speter 572251881Speter return svn_error_trace(list_internal(path_or_url, peg_revision, 573251881Speter revision, 574251881Speter depth, dirent_fields, 575251881Speter fetch_locks, 576251881Speter include_externals, 577251881Speter NULL, NULL, list_func, 578251881Speter baton, ctx, pool)); 579251881Speter} 580