Reorganize the output to "svnserve --help".
[svn.git] / subversion / libsvn_client / list.c
blob66525d2b3aaf90f101a1668751d33082619cb3e3
1 /*
2 * list.c: list local and remote directory entries.
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 #include "client.h"
24 #include "svn_client.h"
25 #include "svn_path.h"
26 #include "svn_pools.h"
27 #include "svn_time.h"
28 #include "svn_sorts.h"
30 #include "svn_private_config.h"
32 /* Get the directory entries of DIR at REV (relative to the root of
33 RA_SESSION), getting at least the fields specified by DIRENT_FIELDS.
34 Use the cancellation function/baton of CTX to check for cancellation.
36 If DEPTH is svn_depth_empty, return immediately. If DEPTH is
37 svn_depth_files, invoke LIST_FUNC on the file entries with BATON;
38 if svn_depth_immediates, invoke it on file and directory entries;
39 if svn_depth_infinity, invoke it on file and directory entries and
40 recurse into the directory entries with the same depth.
42 LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t
43 objects and FS_PATH is the absolute filesystem path of the RA session.
44 Use POOL for temporary allocations.
46 static svn_error_t *
47 get_dir_contents(apr_uint32_t dirent_fields,
48 const char *dir,
49 svn_revnum_t rev,
50 svn_ra_session_t *ra_session,
51 apr_hash_t *locks,
52 const char *fs_path,
53 svn_depth_t depth,
54 svn_client_ctx_t *ctx,
55 svn_client_list_func_t list_func,
56 void *baton,
57 apr_pool_t *pool)
59 apr_hash_t *tmpdirents;
60 apr_pool_t *iterpool = svn_pool_create(pool);
61 apr_array_header_t *array;
62 int i;
64 if (depth == svn_depth_empty)
65 return SVN_NO_ERROR;
67 /* Get the directory's entries, but not its props. */
68 SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL,
69 dir, rev, dirent_fields, pool));
71 if (ctx->cancel_func)
72 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
74 /* Sort the hash, so we can call the callback in a "deterministic" order. */
75 array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically, pool);
76 for (i = 0; i < array->nelts; ++i)
78 svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
79 const char *path;
80 svn_dirent_t *the_ent = apr_hash_get(tmpdirents, item->key, item->klen);
81 svn_lock_t *lock;
83 svn_pool_clear(iterpool);
85 path = svn_path_join(dir, item->key, iterpool);
87 if (locks)
89 const char *abs_path = svn_path_join(fs_path, path, iterpool);
90 lock = apr_hash_get(locks, abs_path, APR_HASH_KEY_STRING);
92 else
93 lock = NULL;
95 if (the_ent->kind == svn_node_file
96 || depth == svn_depth_immediates
97 || depth == svn_depth_infinity)
98 SVN_ERR(list_func(baton, path, the_ent, lock, fs_path, iterpool));
100 if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
101 SVN_ERR(get_dir_contents(dirent_fields, path, rev,
102 ra_session, locks, fs_path, depth, ctx,
103 list_func, baton, iterpool));
106 svn_pool_destroy(iterpool);
107 return SVN_NO_ERROR;
110 svn_error_t *
111 svn_client_list2(const char *path_or_url,
112 const svn_opt_revision_t *peg_revision,
113 const svn_opt_revision_t *revision,
114 svn_depth_t depth,
115 apr_uint32_t dirent_fields,
116 svn_boolean_t fetch_locks,
117 svn_client_list_func_t list_func,
118 void *baton,
119 svn_client_ctx_t *ctx,
120 apr_pool_t *pool)
122 svn_ra_session_t *ra_session;
123 svn_revnum_t rev;
124 svn_dirent_t *dirent;
125 const char *url;
126 const char *repos_root;
127 const char *fs_path;
128 svn_error_t *err;
129 apr_hash_t *locks;
131 /* We use the kind field to determine if we should recurse, so we
132 always need it. */
133 dirent_fields |= SVN_DIRENT_KIND;
135 /* Get an RA plugin for this filesystem object. */
136 SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
137 &url, path_or_url, NULL,
138 peg_revision,
139 revision, ctx, pool));
141 SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
143 SVN_ERR(svn_client__path_relative_to_root(&fs_path, url, repos_root,
144 TRUE, ra_session, NULL, pool));
146 err = svn_ra_stat(ra_session, "", rev, &dirent, pool);
148 /* svnserve before 1.2 doesn't support the above, so fall back on
149 a less efficient method. */
150 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
152 svn_node_kind_t kind;
154 svn_error_clear(err);
156 SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool));
158 if (kind != svn_node_none)
160 if (strcmp(url, repos_root) != 0)
162 svn_ra_session_t *parent_session;
163 apr_hash_t *parent_ents;
164 const char *parent_url, *base_name;
166 /* Open another session to the path's parent. This server
167 doesn't support svn_ra_reparent anyway, so don't try it. */
168 svn_path_split(url, &parent_url, &base_name, pool);
170 /* 'base_name' is now the last component of an URL, but we want
171 to use it as a plain file name. Therefore, we must URI-decode
172 it. */
173 base_name = svn_path_uri_decode(base_name, pool);
175 SVN_ERR(svn_client__open_ra_session_internal(&parent_session,
176 parent_url, NULL,
177 NULL, NULL, FALSE,
178 TRUE, ctx, pool));
180 /* Get all parent's entries, no props. */
181 SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
182 NULL, "", rev, dirent_fields, pool));
184 /* Get the relevant entry. */
185 dirent = apr_hash_get(parent_ents, base_name,
186 APR_HASH_KEY_STRING);
188 else
190 /* We can't get the directory entry for the repository root,
191 but we can still get the information we want.
192 The created-rev of the repository root must, by definition,
193 be rev. */
194 dirent = apr_palloc(pool, sizeof(*dirent));
195 dirent->kind = kind;
196 dirent->size = 0;
197 if (dirent_fields & SVN_DIRENT_HAS_PROPS)
199 apr_hash_t *props;
200 SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props,
201 "", rev, 0 /* no dirent fields */,
202 pool));
203 dirent->has_props = (apr_hash_count(props) != 0);
205 dirent->created_rev = rev;
206 if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
208 apr_hash_t *props;
209 svn_string_t *val;
211 SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props,
212 pool));
213 val = apr_hash_get(props, SVN_PROP_REVISION_DATE,
214 APR_HASH_KEY_STRING);
215 if (val)
216 SVN_ERR(svn_time_from_cstring(&dirent->time, val->data,
217 pool));
218 else
219 dirent->time = 0;
221 val = apr_hash_get(props, SVN_PROP_REVISION_AUTHOR,
222 APR_HASH_KEY_STRING);
223 dirent->last_author = val ? val->data : NULL;
227 else
228 dirent = NULL;
230 else if (err)
231 return err;
233 if (! dirent)
234 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
235 _("URL '%s' non-existent in that revision"),
236 url);
238 /* Maybe get all locks under url. */
239 if (fetch_locks)
241 /* IMPORTANT: If locks are stored in a more temporary pool, we need
242 to fix store_dirent below to duplicate the locks. */
243 err = svn_ra_get_locks(ra_session, &locks, "", pool);
245 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
247 svn_error_clear(err);
248 locks = NULL;
250 else if (err)
251 return err;
253 else
254 locks = NULL;
256 /* Report the dirent for the target. */
257 SVN_ERR(list_func(baton, "", dirent, locks
258 ? (apr_hash_get(locks, fs_path,
259 APR_HASH_KEY_STRING))
260 : NULL, fs_path, pool));
262 if (dirent->kind == svn_node_dir
263 && (depth == svn_depth_files
264 || depth == svn_depth_immediates
265 || depth == svn_depth_infinity))
266 SVN_ERR(get_dir_contents(dirent_fields, "", rev, ra_session, locks,
267 fs_path, depth, ctx, list_func, baton, pool));
269 return SVN_NO_ERROR;
272 svn_error_t *
273 svn_client_list(const char *path_or_url,
274 const svn_opt_revision_t *peg_revision,
275 const svn_opt_revision_t *revision,
276 svn_boolean_t recurse,
277 apr_uint32_t dirent_fields,
278 svn_boolean_t fetch_locks,
279 svn_client_list_func_t list_func,
280 void *baton,
281 svn_client_ctx_t *ctx,
282 apr_pool_t *pool)
284 return svn_client_list2(path_or_url,
285 peg_revision,
286 revision,
287 SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse),
288 dirent_fields,
289 fetch_locks,
290 list_func,
291 baton,
292 ctx,
293 pool);
296 /* Baton used by compatibility wrapper svn_client_ls3. */
297 struct ls_baton {
298 apr_hash_t *dirents;
299 apr_hash_t *locks;
300 apr_pool_t *pool;
303 /* This implements svn_client_list_func_t. */
304 static svn_error_t *
305 store_dirent(void *baton, const char *path, const svn_dirent_t *dirent,
306 const svn_lock_t *lock, const char *abs_path, apr_pool_t *pool)
308 struct ls_baton *lb = baton;
310 /* The dirent is allocated in a temporary pool, so duplicate it into the
311 correct pool. Note, however, that the locks are stored in the correct
312 pool already. */
313 dirent = svn_dirent_dup(dirent, lb->pool);
315 /* An empty path means we are called for the target of the operation.
316 For compatibility, we only store the target if it is a file, and we
317 store it under the basename of the URL. Note that this makes it
318 impossible to differentiate between the target being a directory with a
319 child with the same basename as the target and the target being a file,
320 but that's how it was implemented. */
321 if (path[0] == '\0')
323 if (dirent->kind == svn_node_file)
325 const char *base_name = svn_path_basename(abs_path, lb->pool);
326 apr_hash_set(lb->dirents, base_name, APR_HASH_KEY_STRING, dirent);
327 if (lock)
328 apr_hash_set(lb->locks, base_name, APR_HASH_KEY_STRING, lock);
331 else
333 path = apr_pstrdup(lb->pool, path);
334 apr_hash_set(lb->dirents, path, APR_HASH_KEY_STRING, dirent);
335 if (lock)
336 apr_hash_set(lb->locks, path, APR_HASH_KEY_STRING, lock);
339 return SVN_NO_ERROR;
342 svn_error_t *
343 svn_client_ls3(apr_hash_t **dirents,
344 apr_hash_t **locks,
345 const char *path_or_url,
346 const svn_opt_revision_t *peg_revision,
347 const svn_opt_revision_t *revision,
348 svn_boolean_t recurse,
349 svn_client_ctx_t *ctx,
350 apr_pool_t *pool)
352 struct ls_baton lb;
354 *dirents = lb.dirents = apr_hash_make(pool);
355 if (locks)
356 *locks = lb.locks = apr_hash_make(pool);
357 lb.pool = pool;
359 return svn_client_list(path_or_url, peg_revision, revision, recurse,
360 SVN_DIRENT_ALL, locks != NULL,
361 store_dirent, &lb, ctx, pool);
364 svn_error_t *
365 svn_client_ls2(apr_hash_t **dirents,
366 const char *path_or_url,
367 const svn_opt_revision_t *peg_revision,
368 const svn_opt_revision_t *revision,
369 svn_boolean_t recurse,
370 svn_client_ctx_t *ctx,
371 apr_pool_t *pool)
374 return svn_client_ls3(dirents, NULL, path_or_url, peg_revision,
375 revision, recurse, ctx, pool);
379 svn_error_t *
380 svn_client_ls(apr_hash_t **dirents,
381 const char *path_or_url,
382 svn_opt_revision_t *revision,
383 svn_boolean_t recurse,
384 svn_client_ctx_t *ctx,
385 apr_pool_t *pool)
387 return svn_client_ls2(dirents, path_or_url, revision,
388 revision, recurse, ctx, pool);