#include <apr_strings.h>
#include <apr_pools.h>
#include "svn_pools.h"
#include "client.h"
#include "svn_path.h"
#include "svn_dirent_uri.h"
#include "svn_delta.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_client_private.h"
struct status_baton
{
svn_boolean_t deleted_in_repos;
apr_hash_t *changelist_hash;
svn_client_status_func_t real_status_func;
void *real_status_baton;
const char *anchor_abspath;
const char *anchor_relpath;
svn_wc_context_t *wc_ctx;
};
static svn_error_t *
tweak_status(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *scratch_pool)
{
struct status_baton *sb = baton;
const char *path = local_abspath;
svn_client_status_t *cst;
if (sb->anchor_abspath)
path = svn_dirent_join(sb->anchor_relpath,
svn_dirent_skip_ancestor(sb->anchor_abspath, path),
scratch_pool);
if (sb->changelist_hash
&& (! status->changelist
|| ! apr_hash_get(sb->changelist_hash, status->changelist,
APR_HASH_KEY_STRING)))
{
return SVN_NO_ERROR;
}
if (sb->deleted_in_repos)
{
svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
new_status->repos_node_status = svn_wc_status_deleted;
status = new_status;
}
SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status,
scratch_pool, scratch_pool));
return sb->real_status_func(sb->real_status_baton, path, cst,
scratch_pool);
}
typedef struct report_baton_t {
const svn_ra_reporter3_t* wrapped_reporter;
void *wrapped_report_baton;
char *ancestor;
void *set_locks_baton;
svn_depth_t depth;
svn_client_ctx_t *ctx;
apr_pool_t *pool;
} report_baton_t;
static svn_error_t *
reporter_set_path(void *report_baton, const char *path,
svn_revnum_t revision, svn_depth_t depth,
svn_boolean_t start_empty, const char *lock_token,
apr_pool_t *pool)
{
report_baton_t *rb = report_baton;
return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
revision, depth, start_empty,
lock_token, pool);
}
static svn_error_t *
reporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool)
{
report_baton_t *rb = report_baton;
return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path,
pool);
}
static svn_error_t *
reporter_link_path(void *report_baton, const char *path, const char *url,
svn_revnum_t revision, svn_depth_t depth,
svn_boolean_t start_empty,
const char *lock_token, apr_pool_t *pool)
{
report_baton_t *rb = report_baton;
if (!svn_uri__is_ancestor(rb->ancestor, url))
{
const char *ancestor;
ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool);
rb->ancestor[strlen(ancestor)] = '\0';
rb->depth = svn_depth_infinity;
}
return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
revision, depth, start_empty,
lock_token, pool);
}
static svn_error_t *
reporter_finish_report(void *report_baton, apr_pool_t *pool)
{
report_baton_t *rb = report_baton;
svn_ra_session_t *ras;
apr_hash_t *locks;
const char *repos_root;
apr_pool_t *subpool = svn_pool_create(pool);
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR(svn_client__open_ra_session_internal(&ras, NULL, rb->ancestor, NULL,
NULL, FALSE, TRUE,
rb->ctx, subpool));
err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
|| (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
locks = apr_hash_make(rb->pool);
}
SVN_ERR(err);
SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool));
svn_pool_destroy(subpool);
SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks,
repos_root, rb->pool));
return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool);
}
static svn_error_t *
reporter_abort_report(void *report_baton, apr_pool_t *pool)
{
report_baton_t *rb = report_baton;
return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool);
}
static svn_ra_reporter3_t lock_fetch_reporter = {
reporter_set_path,
reporter_delete_path,
reporter_link_path,
reporter_finish_report,
reporter_abort_report
};
svn_error_t *
svn_client_status5(svn_revnum_t *result_rev,
svn_client_ctx_t *ctx,
const char *path,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t get_all,
svn_boolean_t update,
svn_boolean_t no_ignore,
svn_boolean_t ignore_externals,
svn_boolean_t depth_as_sticky,
const apr_array_header_t *changelists,
svn_client_status_func_t status_func,
void *status_baton,
apr_pool_t *pool)
{
struct status_baton sb;
const char *dir, *dir_abspath;
const char *target_abspath;
const char *target_basename;
apr_array_header_t *ignores;
svn_error_t *err;
apr_hash_t *changelist_hash = NULL;
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a local path"), path);
if (changelists && changelists->nelts)
SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
if (result_rev)
*result_rev = SVN_INVALID_REVNUM;
sb.real_status_func = status_func;
sb.real_status_baton = status_baton;
sb.deleted_in_repos = FALSE;
sb.changelist_hash = changelist_hash;
sb.wc_ctx = ctx->wc_ctx;
SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));
{
svn_node_kind_t kind;
SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, target_abspath, FALSE, pool));
if (kind == svn_node_dir)
{
dir_abspath = target_abspath;
target_basename = "";
dir = path;
}
else
{
dir_abspath = svn_dirent_dirname(target_abspath, pool);
target_basename = svn_dirent_basename(target_abspath, NULL);
dir = svn_dirent_dirname(path, pool);
if (kind == svn_node_file)
{
if (depth == svn_depth_empty)
depth = svn_depth_files;
}
else
{
err = svn_wc_read_kind(&kind, ctx->wc_ctx, dir_abspath, FALSE,
pool);
svn_error_clear(err);
if (err || kind != svn_node_dir)
{
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(path, pool));
}
}
}
}
if (svn_dirent_is_absolute(dir))
{
sb.anchor_abspath = NULL;
sb.anchor_relpath = NULL;
}
else
{
sb.anchor_abspath = dir_abspath;
sb.anchor_relpath = dir;
}
SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
if (update)
{
svn_ra_session_t *ra_session;
const char *URL;
svn_node_kind_t kind;
svn_boolean_t server_supports_depth;
const svn_delta_editor_t *editor;
void *edit_baton, *set_locks_baton;
svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
pool, pool));
if (!URL)
return svn_error_createf
(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("Entry '%s' has no URL"),
svn_dirent_local_style(dir, pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
dir_abspath,
NULL, FALSE, TRUE,
ctx, pool));
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
SVN_RA_CAPABILITY_DEPTH, pool));
SVN_ERR(svn_wc_get_status_editor5(&editor, &edit_baton, &set_locks_baton,
&edit_revision, ctx->wc_ctx,
dir_abspath, target_basename,
depth, get_all,
no_ignore, depth_as_sticky,
server_supports_depth,
ignores, tweak_status, &sb,
ctx->cancel_func, ctx->cancel_baton,
pool, pool));
SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
&kind, pool));
if (kind == svn_node_none)
{
svn_boolean_t added;
SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
dir_abspath, pool));
if (! added)
sb.deleted_in_repos = TRUE;
SVN_ERR(editor->close_edit(edit_baton, pool));
}
else
{
svn_revnum_t revnum;
report_baton_t rb;
svn_depth_t status_depth;
if (revision->kind == svn_opt_revision_head)
{
revnum = SVN_INVALID_REVNUM;
}
else
{
SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
ctx->wc_ctx,
target_abspath,
ra_session, revision,
pool));
}
if (depth_as_sticky || !server_supports_depth)
status_depth = depth;
else
status_depth = svn_depth_unknown;
SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
&rb.wrapped_report_baton,
target_basename, revnum, status_depth,
editor, edit_baton, pool));
rb.ancestor = apr_pstrdup(pool, URL);
rb.set_locks_baton = set_locks_baton;
rb.ctx = ctx;
rb.pool = pool;
if (depth == svn_depth_unknown)
rb.depth = svn_depth_infinity;
else
rb.depth = depth;
SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
target_abspath,
&lock_fetch_reporter, &rb,
FALSE ,
depth, (! depth_as_sticky),
(! server_supports_depth),
FALSE ,
ctx->cancel_func, ctx->cancel_baton,
NULL, NULL, pool));
}
if (ctx->notify_func2)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(target_abspath,
svn_wc_notify_status_completed, pool);
notify->revision = edit_revision;
(ctx->notify_func2)(ctx->notify_baton2, notify, pool);
}
if (result_rev)
*result_rev = edit_revision;
}
else
{
err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
depth, get_all, no_ignore, FALSE, ignores,
tweak_status, &sb,
ctx->cancel_func, ctx->cancel_baton,
pool);
if (err && err->apr_err == SVN_ERR_WC_MISSING)
{
svn_error_clear(err);
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(path, pool));
}
SVN_ERR(err);
}
if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
{
apr_hash_t *external_map;
SVN_ERR(svn_wc__externals_defined_below(&external_map,
ctx->wc_ctx, target_abspath,
pool, pool));
SVN_ERR(svn_client__do_external_status(ctx, external_map,
depth, get_all,
update, no_ignore,
sb.anchor_abspath, sb.anchor_relpath,
status_func, status_baton, pool));
}
return SVN_NO_ERROR;
}
svn_client_status_t *
svn_client_status_dup(const svn_client_status_t *status,
apr_pool_t *result_pool)
{
svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st));
*st = *status;
if (status->local_abspath)
st->local_abspath = apr_pstrdup(result_pool, status->local_abspath);
if (status->repos_root_url)
st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url);
if (status->repos_uuid)
st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid);
if (status->repos_relpath)
st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath);
if (status->changed_author)
st->changed_author = apr_pstrdup(result_pool, status->changed_author);
if (status->lock)
st->lock = svn_lock_dup(status->lock, result_pool);
if (status->changelist)
st->changelist = apr_pstrdup(result_pool, status->changelist);
if (status->ood_changed_author)
st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author);
if (status->repos_lock)
st->repos_lock = svn_lock_dup(status->repos_lock, result_pool);
if (status->backwards_compatibility_baton)
{
const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton;
st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st,
result_pool);
}
return st;
}
svn_error_t *
svn_client__create_status(svn_client_status_t **cst,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*cst = apr_pcalloc(result_pool, sizeof(**cst));
(*cst)->kind = status->kind;
(*cst)->local_abspath = local_abspath;
(*cst)->filesize = status->filesize;
(*cst)->versioned = status->versioned;
(*cst)->conflicted = status->conflicted;
(*cst)->node_status = status->node_status;
(*cst)->text_status = status->text_status;
(*cst)->prop_status = status->prop_status;
if (status->kind == svn_node_dir)
(*cst)->wc_is_locked = status->locked;
(*cst)->copied = status->copied;
(*cst)->revision = status->revision;
(*cst)->changed_rev = status->changed_rev;
(*cst)->changed_date = status->changed_date;
(*cst)->changed_author = status->changed_author;
(*cst)->repos_root_url = status->repos_root_url;
(*cst)->repos_uuid = status->repos_uuid;
(*cst)->repos_relpath = status->repos_relpath;
(*cst)->switched = status->switched;
(*cst)->file_external = FALSE;
if (
(status->versioned
&& status->switched
&& status->kind == svn_node_file))
{
svn_node_kind_t external_kind;
SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
NULL, wc_ctx,
local_abspath ,
local_abspath, TRUE,
scratch_pool, scratch_pool));
if (external_kind == svn_node_file)
{
(*cst)->file_external = TRUE;
(*cst)->switched = FALSE;
(*cst)->node_status = (*cst)->text_status;
}
}
(*cst)->lock = status->lock;
(*cst)->changelist = status->changelist;
(*cst)->depth = status->depth;
(*cst)->ood_kind = status->ood_kind;
(*cst)->repos_node_status = status->repos_node_status;
(*cst)->repos_text_status = status->repos_text_status;
(*cst)->repos_prop_status = status->repos_prop_status;
(*cst)->repos_lock = status->repos_lock;
(*cst)->ood_changed_rev = status->ood_changed_rev;
(*cst)->ood_changed_date = status->ood_changed_date;
(*cst)->ood_changed_author = status->ood_changed_author;
(*cst)->backwards_compatibility_baton = status;
if (status->versioned && status->conflicted)
{
svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted,
&tree_conflicted, wc_ctx, local_abspath,
scratch_pool));
if (text_conflicted)
(*cst)->text_status = svn_wc_status_conflicted;
if (prop_conflicted)
(*cst)->prop_status = svn_wc_status_conflicted;
if (text_conflicted || prop_conflicted)
(*cst)->node_status = svn_wc_status_conflicted;
}
return SVN_NO_ERROR;
}