Index: subversion/include/svn_client.h =================================================================== --- subversion/include/svn_client.h (revision 15654) +++ subversion/include/svn_client.h (working copy) @@ -1842,7 +1842,7 @@ /** - * Set @a *dirents to a newly allocated hash of entries for @a + * Set @a *dirents and *locks to a newly allocated hash of entries for @a * path_or_url at @a revision. The actual node revision selected is * determined by the path as it exists in @a peg_revision. If @a * peg_revision is @c svn_opt_revision_unspecified, then it defaults @@ -1853,18 +1853,37 @@ * @a path_or_url is a file, return only the dirent for the file. If @a * path_or_url is non-existent, return @c SVN_ERR_FS_NOT_FOUND. * - * The hash maps entry names (const char *) to @c svn_dirent_t *'s. - * Do all allocation in @a pool. + * The @a dirents hash maps entry names (const char *) to + * @c svn_dirent_t *'s. Do all allocation in @a pool. * + * If @a locks is not @c NULL, set @a *locks to a hash table mapping + * entry names (const char *) to @c svn_lock_t *'s, + * allocating both @a *locks and everything inside it in @a pool. + * * Use authentication baton cached in @a ctx to authenticate against the * repository. * * If @a recurse is true (and @a path_or_url is a directory) this will * be a recursive operation. * - * @since New in 1.2. + * @since New in 1.3. */ svn_error_t * +svn_client_ls3 (apr_hash_t **dirents, + apr_hash_t **locks, + const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_boolean_t recurse, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** + * Same as svn_client_ls3(), but always passes a NULL lock hash. + * + * @deprecated Provided for backward compatibility with the 1.2 API. + */ +svn_error_t * svn_client_ls2 (apr_hash_t **dirents, const char *path_or_url, const svn_opt_revision_t *peg_revision, Index: subversion/libsvn_client/ls.c =================================================================== --- subversion/libsvn_client/ls.c (revision 15654) +++ subversion/libsvn_client/ls.c (working copy) @@ -23,6 +23,7 @@ #include "client.h" #include "svn_client.h" #include "svn_path.h" +#include "svn_pools.h" #include "svn_private_config.h" @@ -71,7 +72,8 @@ } svn_error_t * -svn_client_ls2 (apr_hash_t **dirents, +svn_client_ls3 (apr_hash_t **dirents, + apr_hash_t **locks, const char *path_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, @@ -83,12 +85,22 @@ svn_revnum_t rev; svn_node_kind_t url_kind; const char *url; + const char *repos_root; + const char *rel_path; + apr_pool_t *subpool; + apr_hash_t *new_locks; + apr_hash_index_t *hi; /* Get an RA plugin for this filesystem object. */ SVN_ERR (svn_client__ra_session_from_path (&ra_session, &rev, &url, path_or_url, peg_revision, revision, ctx, pool)); + /* Getting repository root. */ + SVN_ERR (svn_ra_get_repos_root (ra_session, &repos_root, pool)); + /* Getting relative path respective to repository root. */ + rel_path = svn_path_is_child (repos_root, url, pool); + /* Decide if the URL is a file or directory. */ SVN_ERR (svn_ra_check_path (ra_session, "", rev, &url_kind, pool)); @@ -107,14 +119,14 @@ /* Re-open the session to the file's parent instead. */ svn_path_split (url, &parent_url, &base_name, pool); + /* 'base_name' is now the last component of an URL, but we want to use it as a plain file name. Therefore, we must URI-decode it. */ base_name = svn_path_uri_decode(base_name, pool); SVN_ERR (svn_client__open_ra_session_internal (&ra_session, parent_url, - NULL, - NULL, NULL, FALSE, TRUE, - ctx, pool)); + NULL, NULL, NULL, FALSE, + TRUE, ctx, pool)); /* Get all parent's entries, no props. */ SVN_ERR (svn_ra_get_dir (ra_session, "", rev, &parent_ents, @@ -127,6 +139,7 @@ return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL, _("URL '%s' non-existent in that revision"), url); + svn_path_split (rel_path, &rel_path, NULL, pool); apr_hash_set (*dirents, base_name, APR_HASH_KEY_STRING, the_ent); } @@ -135,10 +148,57 @@ _("URL '%s' non-existent in that revision"), url); + if (locks == NULL) + return SVN_NO_ERROR; + + if (rel_path == NULL || rel_path[0] == 0) + rel_path = "/"; + else + rel_path = apr_psprintf (pool, "/%s/", rel_path); + + subpool = svn_pool_create (pool); + + /* Get lock. */ + SVN_ERR (svn_ra_get_locks (ra_session, locks, "", subpool)); + + new_locks = apr_hash_make (pool); + for (hi = apr_hash_first (subpool, *locks); + hi; + hi = apr_hash_next (hi)) + { + const void *key; + void *val; + const char *newkey; + + apr_hash_this (hi, &key, NULL, &val); + newkey = svn_path_is_child (svn_path_canonicalize (rel_path, subpool), + svn_path_canonicalize (key, subpool), + pool); + if (newkey) + apr_hash_set (new_locks, newkey, APR_HASH_KEY_STRING, val); + } + + apr_pool_destroy (subpool); + *locks = new_locks; + return SVN_NO_ERROR; } svn_error_t * +svn_client_ls2 (apr_hash_t **dirents, + const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_boolean_t recurse, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + + return svn_client_ls3 (dirents, NULL, path_or_url, peg_revision, + revision, recurse, ctx, pool); +} + +svn_error_t * svn_client_ls (apr_hash_t **dirents, const char *path_or_url, svn_opt_revision_t *revision, Index: subversion/clients/cmdline/ls-cmd.c =================================================================== --- subversion/clients/cmdline/ls-cmd.c (revision 15654) +++ subversion/clients/cmdline/ls-cmd.c (working copy) @@ -37,6 +37,7 @@ static svn_error_t * print_dirents (apr_hash_t *dirents, + apr_hash_t *locks, svn_boolean_t verbose, svn_client_ctx_t *ctx, apr_pool_t *pool) @@ -51,6 +52,7 @@ { const char *utf8_entryname; svn_dirent_t *dirent; + svn_lock_t *lock; svn_sort__item_t *item; svn_pool_clear (subpool); @@ -63,6 +65,7 @@ utf8_entryname = item->key; dirent = apr_hash_get (dirents, utf8_entryname, item->klen); + lock = apr_hash_get (locks, utf8_entryname, item->klen); if (verbose) { @@ -70,8 +73,8 @@ apr_time_exp_t exp_time; apr_status_t apr_err; apr_size_t size; - char timestr[20]; - const char *sizestr, *utf8_timestr; + char timestr[20], lock_timestr[20]; + const char *sizestr, *utf8_timestr, *utf8_lock_timestr; /* svn_time_to_human_cstring gives us something *way* too long to use for this, so we have to roll our own. We include @@ -98,13 +101,42 @@ sizestr = apr_psprintf (subpool, "%" SVN_FILESIZE_T_FMT, dirent->size); + if (lock) + { + apr_time_exp_lt (&exp_time, lock->creation_date); + if (apr_time_sec + (now - lock->creation_date) < (365 * 86400 / 2) + && apr_time_sec + (lock->creation_date - now) < (365 * 86400 / 2)) + { + apr_err = apr_strftime (lock_timestr, &size, + sizeof (lock_timestr), + "%b %d %H:%M", &exp_time); + } + else + { + apr_err = apr_strftime (lock_timestr, &size, + sizeof (timestr), + "%b %d %Y", &exp_time); + } + /* if that failed, just zero out the string and print nothing */ + if (apr_err) + lock_timestr[0] = '\0'; + + /* we need it in UTF-8. */ + SVN_ERR (svn_utf_cstring_to_utf8 + (&utf8_lock_timestr, lock_timestr, subpool)); + } + SVN_ERR (svn_cmdline_printf - (subpool, "%7ld %-8.8s %10s %12s %s%s\n", + (subpool, "%7ld %-12s %10s %12s %-12s %12s %s%s\n", dirent->created_rev, dirent->last_author ? dirent->last_author : " ? ", (dirent->kind == svn_node_file) ? sizestr : "", utf8_timestr, + lock ? lock->owner : "", + lock ? utf8_lock_timestr : "", utf8_entryname, (dirent->kind == svn_node_dir) ? "/" : "")); } @@ -139,6 +171,7 @@ static svn_error_t * print_dirents_xml (apr_hash_t *dirents, + apr_hash_t *locks, const char *path, svn_client_ctx_t *ctx, apr_pool_t *pool) @@ -164,6 +197,7 @@ const char *utf8_entryname; svn_dirent_t *dirent; svn_sort__item_t *item; + svn_lock_t *lock; svn_pool_clear (subpool); @@ -175,6 +209,7 @@ utf8_entryname = item->key; dirent = apr_hash_get (dirents, utf8_entryname, item->klen); + lock = apr_hash_get (locks, utf8_entryname, APR_HASH_KEY_STRING); sb = svn_stringbuf_create ("", subpool); @@ -208,6 +243,49 @@ /* "" */ svn_xml_make_close_tag (&sb, subpool, "commit"); + /* "" */ + if (lock) + { + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "lock", NULL); + + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "token", NULL); + svn_xml_escape_cdata_cstring (&sb, lock->token, pool); + svn_xml_make_close_tag (&sb, pool, "token"); + + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "owner", NULL); + svn_xml_escape_cdata_cstring (&sb, lock->owner, pool); + svn_xml_make_close_tag (&sb, pool, "owner"); + + if (lock->comment) + { + svn_xml_make_open_tag (&sb, pool, svn_xml_normal, + "comment", NULL); + svn_xml_escape_cdata_cstring (&sb, lock->comment, pool); + svn_xml_make_close_tag (&sb, pool, "comment"); + } + + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "created", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (lock->creation_date, pool), + pool); + svn_xml_make_close_tag (&sb, pool, "created"); + + if (lock->expiration_date != 0) + { + svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, + "expires", NULL); + svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring + (lock->expiration_date, pool), + pool); + svn_xml_make_close_tag (&sb, pool, "expires"); + } + + /* "" */ + svn_xml_make_close_tag (&sb, subpool, "lock"); + } /* "" */ svn_xml_make_close_tag (&sb, subpool, "entry"); @@ -281,6 +359,7 @@ for (i = 0; i < targets->nelts; i++) { apr_hash_t *dirents; + apr_hash_t *locks; const char *target = ((const char **) (targets->elts))[i]; const char *truepath; svn_opt_revision_t peg_revision; @@ -292,14 +371,15 @@ /* Get peg revisions. */ SVN_ERR (svn_opt_parse_path (&peg_revision, &truepath, target, subpool)); - SVN_ERR (svn_client_ls2 (&dirents, truepath, &peg_revision, + + SVN_ERR (svn_client_ls3 (&dirents, &locks, truepath, &peg_revision, &(opt_state->start_revision), opt_state->recursive, ctx, subpool)); if (opt_state->xml) - SVN_ERR (print_dirents_xml (dirents, truepath, ctx, subpool)); + SVN_ERR (print_dirents_xml (dirents, locks, truepath, ctx, subpool)); else - SVN_ERR (print_dirents (dirents, opt_state->verbose, ctx, subpool)); + SVN_ERR (print_dirents (dirents, locks, opt_state->verbose, ctx, subpool)); } svn_pool_destroy (subpool); Index: subversion/clients/cmdline/main.c =================================================================== --- subversion/clients/cmdline/main.c (revision 15654) +++ subversion/clients/cmdline/main.c (working copy) @@ -389,12 +389,14 @@ "current\n" " working directory.\n" "\n" - " With --verbose, the following fields show the status of the item:\n" + " With --verbose, the following fields will be shown for each item:\n" "\n" " Revision number of the last commit\n" " Author of the last commit\n" " Size (in bytes)\n" - " Date and time of the last commit\n"), + " Date and time of the last commit\n" + " Date and time of the lock creation\n" + " Owner of the lock\n"), {'r', 'v', 'R', svn_cl__incremental_opt, svn_cl__xml_opt, SVN_CL__AUTH_OPTIONS, svn_cl__config_dir_opt} }, Index: subversion/clients/cmdline/dtd/list.dtd =================================================================== --- subversion/clients/cmdline/dtd/list.dtd (revision 15654) +++ subversion/clients/cmdline/dtd/list.dtd (working copy) @@ -8,9 +8,16 @@ - + + + + + + + +