#include <apr_file_io.h>
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "client.h"
#include "private/svn_client_private.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
static svn_error_t *
find_undeletables(void *baton,
const char *path,
const svn_client_status_t *status,
apr_pool_t *pool)
{
if (status->node_status == svn_wc_status_obstructed)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("'%s' is in the way of the resource "
"actually under version control"),
svn_dirent_local_style(path, pool));
else if (! status->versioned)
return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
_("'%s' is not under version control"),
svn_dirent_local_style(path, pool));
else if ((status->node_status != svn_wc_status_normal
&& status->node_status != svn_wc_status_deleted
&& status->node_status != svn_wc_status_missing)
||
(status->prop_status != svn_wc_status_none
&& status->prop_status != svn_wc_status_normal))
return svn_error_createf(SVN_ERR_CLIENT_MODIFIED, NULL,
_("'%s' has local modifications -- commit or "
"revert them first"),
svn_dirent_local_style(path, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__can_delete(const char *path,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_opt_revision_t revision;
svn_node_kind_t external_kind;
const char *defining_abspath;
const char* local_abspath;
revision.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
SVN_ERR(svn_wc__read_external_info(&external_kind, &defining_abspath, NULL,
NULL, NULL,
ctx->wc_ctx, local_abspath,
local_abspath, TRUE,
scratch_pool, scratch_pool));
if (external_kind != svn_node_none)
return svn_error_createf(SVN_ERR_WC_CANNOT_DELETE_FILE_EXTERNAL, NULL,
_("Cannot remove the external at '%s'; "
"please edit or delete the svn:externals "
"property on '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool),
svn_dirent_local_style(defining_abspath,
scratch_pool));
return svn_error_trace(svn_client_status5(NULL, ctx, path, &revision,
svn_depth_infinity, FALSE,
FALSE, FALSE, FALSE, FALSE,
NULL,
find_undeletables, NULL,
scratch_pool));
}
static svn_error_t *
path_driver_cb_func(void **dir_baton,
void *parent_baton,
void *callback_baton,
const char *path,
apr_pool_t *pool)
{
const svn_delta_editor_t *editor = callback_baton;
*dir_baton = NULL;
return editor->delete_entry(path, SVN_INVALID_REVNUM, parent_baton, pool);
}
static svn_error_t *
single_repos_delete(svn_ra_session_t *ra_session,
const char *base_uri,
const apr_array_header_t *relpaths,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const svn_delta_editor_t *editor;
apr_hash_t *commit_revprops;
void *edit_baton;
const char *log_msg;
int i;
svn_error_t *err;
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
{
svn_client_commit_item3_t *item;
const char *tmp_file;
apr_array_header_t *commit_items
= apr_array_make(pool, relpaths->nelts, sizeof(item));
for (i = 0; i < relpaths->nelts; i++)
{
const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(base_uri, relpath, pool);
item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
ctx, pool));
if (! log_msg)
return SVN_NO_ERROR;
}
else
log_msg = "";
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
log_msg, ctx, pool));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
commit_baton,
NULL, TRUE,
pool));
err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
relpaths, path_driver_cb_func,
(void *)editor, pool);
if (err)
{
return svn_error_trace(
svn_error_compose_create(err,
editor->abort_edit(edit_baton, pool)));
}
return svn_error_trace(editor->close_edit(edit_baton, pool));
}
struct repos_deletables_t
{
svn_ra_session_t *ra_session;
apr_array_header_t *target_uris;
};
static svn_error_t *
delete_urls_multi_repos(const apr_array_header_t *uris,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_hash_t *deletables = apr_hash_make(pool);
apr_pool_t *iterpool;
apr_hash_index_t *hi;
int i;
for (i = 0; i < uris->nelts; i++)
{
const char *uri = APR_ARRAY_IDX(uris, i, const char *);
struct repos_deletables_t *repos_deletables = NULL;
const char *repos_relpath;
svn_node_kind_t kind;
for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi))
{
const char *repos_root = svn__apr_hash_index_key(hi);
repos_relpath = svn_uri__is_child(repos_root, uri, pool);
if (repos_relpath)
{
repos_deletables = svn__apr_hash_index_val(hi);
APR_ARRAY_PUSH(repos_deletables->target_uris, const char *) =
apr_pstrdup(pool, uri);
break;
}
}
if (!repos_deletables)
{
svn_ra_session_t *ra_session = NULL;
const char *repos_root;
apr_array_header_t *target_uris;
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, uri,
NULL, NULL, FALSE,
TRUE, ctx, pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool));
SVN_ERR(svn_ra_reparent(ra_session, repos_root, pool));
repos_relpath = svn_uri__is_child(repos_root, uri, pool);
target_uris = apr_array_make(pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(target_uris, const char *) = apr_pstrdup(pool, uri);
repos_deletables = apr_pcalloc(pool, sizeof(*repos_deletables));
repos_deletables->ra_session = ra_session;
repos_deletables->target_uris = target_uris;
apr_hash_set(deletables, repos_root,
APR_HASH_KEY_STRING, repos_deletables);
}
if (!repos_relpath || !*repos_relpath)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
"URL '%s' not within a repository", uri);
SVN_ERR(svn_ra_check_path(repos_deletables->ra_session, repos_relpath,
SVN_INVALID_REVNUM, &kind, pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
"URL '%s' does not exist", uri);
}
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi))
{
struct repos_deletables_t *repos_deletables = svn__apr_hash_index_val(hi);
const char *base_uri;
apr_array_header_t *target_relpaths;
svn_pool_clear(iterpool);
SVN_ERR(svn_uri_condense_targets(&base_uri, &target_relpaths,
repos_deletables->target_uris,
TRUE, iterpool, iterpool));
SVN_ERR_ASSERT(!svn_path_is_empty(base_uri));
if (target_relpaths->nelts == 0)
{
const char *target_relpath;
svn_uri_split(&base_uri, &target_relpath, base_uri, iterpool);
APR_ARRAY_PUSH(target_relpaths, const char *) = target_relpath;
}
else if ((target_relpaths->nelts == 1)
&& (svn_path_is_empty(APR_ARRAY_IDX(target_relpaths, 0,
const char *))))
{
const char *target_relpath;
svn_uri_split(&base_uri, &target_relpath, base_uri, iterpool);
APR_ARRAY_IDX(target_relpaths, 0, const char *) = target_relpath;
}
SVN_ERR(svn_ra_reparent(repos_deletables->ra_session, base_uri, pool));
SVN_ERR(single_repos_delete(repos_deletables->ra_session, base_uri,
target_relpaths,
revprop_table, commit_callback,
commit_baton, ctx, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__wc_delete(const char *path,
svn_boolean_t force,
svn_boolean_t dry_run,
svn_boolean_t keep_local,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *local_abspath;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
if (!force && !keep_local)
SVN_ERR(svn_client__can_delete(local_abspath, ctx, pool));
if (!dry_run)
return svn_error_trace(svn_wc_delete4(ctx->wc_ctx, local_abspath,
keep_local, TRUE,
ctx->cancel_func, ctx->cancel_baton,
notify_func, notify_baton, pool));
return SVN_NO_ERROR;
}
struct delete_with_write_lock_baton
{
const char *path;
svn_boolean_t force;
svn_boolean_t keep_local;
svn_client_ctx_t *ctx;
};
static svn_error_t *
delete_with_write_lock_func(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct delete_with_write_lock_baton *args = baton;
return svn_client__wc_delete(args->path, args->force,
FALSE, args->keep_local,
args->ctx->notify_func2,
args->ctx->notify_baton2,
args->ctx, scratch_pool);
}
svn_error_t *
svn_client_delete4(const apr_array_header_t *paths,
svn_boolean_t force,
svn_boolean_t keep_local,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_boolean_t is_url;
if (! paths->nelts)
return SVN_NO_ERROR;
SVN_ERR(svn_client__assert_homogeneous_target_type(paths));
is_url = svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *));
if (is_url)
{
SVN_ERR(delete_urls_multi_repos(paths, revprop_table, commit_callback,
commit_baton, ctx, pool));
}
else
{
apr_pool_t *subpool = svn_pool_create(pool);
int i;
for (i = 0; i < paths->nelts; i++)
{
struct delete_with_write_lock_baton dwwlb;
const char *path = APR_ARRAY_IDX(paths, i, const char *);
const char *local_abspath;
svn_pool_clear(subpool);
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, subpool));
dwwlb.path = path;
dwwlb.force = force;
dwwlb.keep_local = keep_local;
dwwlb.ctx = ctx;
SVN_ERR(svn_wc__call_with_write_lock(delete_with_write_lock_func,
&dwwlb, ctx->wc_ctx,
local_abspath, TRUE,
pool, subpool));
}
svn_pool_destroy(subpool);
}
return SVN_NO_ERROR;
}