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