#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <apr_env.h>
#include <apr_errno.h>
#include <apr_file_info.h>
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_general.h>
#include <apr_lib.h>
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_ctype.h"
#include "svn_client.h"
#include "svn_cmdline.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_io.h"
#include "svn_utf.h"
#include "svn_subst.h"
#include "svn_config.h"
#include "svn_wc.h"
#include "svn_xml.h"
#include "svn_time.h"
#include "svn_props.h"
#include "svn_private_config.h"
#include "cl.h"
#include "private/svn_token.h"
#include "private/svn_opt_private.h"
#include "private/svn_client_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_string_private.h"
svn_error_t *
svn_cl__print_commit_info(const svn_commit_info_t *commit_info,
void *baton,
apr_pool_t *pool)
{
if (SVN_IS_VALID_REVNUM(commit_info->revision))
SVN_ERR(svn_cmdline_printf(pool, _("Committed revision %ld%s.\n"),
commit_info->revision,
commit_info->revision == 42 &&
getenv("SVN_I_LOVE_PANGALACTIC_GARGLE_BLASTERS")
? _(" (the answer to life, the universe, "
"and everything)")
: ""));
if (commit_info->post_commit_err)
SVN_ERR(svn_cmdline_printf(pool, _("\nWarning: %s\n"),
commit_info->post_commit_err));
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__merge_file_externally(const char *base_path,
const char *their_path,
const char *my_path,
const char *merged_path,
const char *wc_path,
apr_hash_t *config,
svn_boolean_t *remains_in_conflict,
apr_pool_t *pool)
{
char *merge_tool;
if (apr_env_get(&merge_tool, "SVN_MERGE", pool) != APR_SUCCESS)
{
struct svn_config_t *cfg;
merge_tool = NULL;
cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
svn_config_get(cfg, (const char **)&merge_tool,
SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_MERGE_TOOL_CMD, NULL);
}
if (merge_tool)
{
const char *c;
for (c = merge_tool; *c; c++)
if (!svn_ctype_isspace(*c))
break;
if (! *c)
return svn_error_create
(SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
_("The SVN_MERGE environment variable is empty or "
"consists solely of whitespace. Expected a shell command.\n"));
}
else
return svn_error_create
(SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
_("The environment variable SVN_MERGE and the merge-tool-cmd run-time "
"configuration option were not set.\n"));
{
const char *arguments[7] = { 0 };
char *cwd;
int exitcode;
apr_status_t status = apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, pool);
if (status != 0)
return svn_error_wrap_apr(status, NULL);
arguments[0] = merge_tool;
arguments[1] = base_path;
arguments[2] = their_path;
arguments[3] = my_path;
arguments[4] = merged_path;
arguments[5] = wc_path;
arguments[6] = NULL;
SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), merge_tool,
arguments, &exitcode, NULL, TRUE, NULL, NULL, NULL,
pool));
if (exitcode != 0 && exitcode != 1)
return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("The external merge tool '%s' exited with exit code %d."),
merge_tool, exitcode);
else if (remains_in_conflict)
*remains_in_conflict = exitcode == 1;
}
return SVN_NO_ERROR;
}
struct log_msg_baton
{
const char *editor_cmd;
const char *message;
const char *message_encoding;
const char *base_dir;
const char *tmpfile_left;
svn_boolean_t non_interactive;
apr_hash_t *config;
svn_boolean_t keep_locks;
apr_pool_t *pool;
};
svn_error_t *
svn_cl__make_log_msg_baton(void **baton,
svn_cl__opt_state_t *opt_state,
const char *base_dir ,
apr_hash_t *config,
apr_pool_t *pool)
{
struct log_msg_baton *lmb = apr_pcalloc(pool, sizeof(*lmb));
if (opt_state->filedata)
{
if (strlen(opt_state->filedata->data) < opt_state->filedata->len)
{
return svn_error_create(SVN_ERR_CL_BAD_LOG_MESSAGE, NULL,
_("Log message contains a zero byte"));
}
lmb->message = opt_state->filedata->data;
}
else
{
lmb->message = opt_state->message;
}
lmb->editor_cmd = opt_state->editor_cmd;
if (opt_state->encoding)
{
lmb->message_encoding = opt_state->encoding;
}
else if (config)
{
svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
svn_config_get(cfg, &(lmb->message_encoding),
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_LOG_ENCODING,
NULL);
}
else
lmb->message_encoding = NULL;
lmb->base_dir = base_dir;
lmb->tmpfile_left = NULL;
lmb->config = config;
lmb->keep_locks = opt_state->no_unlock;
lmb->non_interactive = opt_state->non_interactive;
lmb->pool = pool;
*baton = lmb;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__cleanup_log_msg(void *log_msg_baton,
svn_error_t *commit_err,
apr_pool_t *pool)
{
struct log_msg_baton *lmb = log_msg_baton;
svn_error_t *err;
if ((! lmb) || (! lmb->tmpfile_left))
return commit_err;
if (! commit_err)
return svn_io_remove_file2(lmb->tmpfile_left, FALSE, lmb->pool);
err = svn_error_createf(commit_err->apr_err, NULL,
_(" '%s'"),
svn_dirent_local_style(lmb->tmpfile_left, pool));
svn_error_compose(commit_err,
svn_error_create(commit_err->apr_err, err,
_("Your commit message was left in "
"a temporary file:")));
return commit_err;
}
static void
truncate_buffer_at_prefix(apr_size_t *new_len,
char *buffer,
const char *prefix)
{
char *substring = buffer;
assert(buffer && prefix);
if (new_len)
*new_len = strlen(buffer);
while (1)
{
substring = strstr(substring, prefix);
if (! substring)
return;
if ((substring == buffer)
|| (*(substring - 1) == '\r')
|| (*(substring - 1) == '\n'))
{
*substring = '\0';
if (new_len)
*new_len = substring - buffer;
}
else if (substring)
{
substring++;
}
}
}
#define EDITOR_EOF_PREFIX _("--This line, and those below, will be ignored--")
svn_error_t *
svn_cl__get_log_message(const char **log_msg,
const char **tmp_file,
const apr_array_header_t *commit_items,
void *baton,
apr_pool_t *pool)
{
svn_stringbuf_t *default_msg = NULL;
struct log_msg_baton *lmb = baton;
svn_stringbuf_t *message = NULL;
default_msg = svn_stringbuf_create(APR_EOL_STR, pool);
svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX);
svn_stringbuf_appendcstr(default_msg, APR_EOL_STR APR_EOL_STR);
*tmp_file = NULL;
if (lmb->message)
{
svn_string_t *log_msg_str = svn_string_create(lmb->message, pool);
SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, NULL, NULL,
log_msg_str, lmb->message_encoding,
FALSE, pool, pool),
_("Error normalizing log message to internal format"));
truncate_buffer_at_prefix(&(log_msg_str->len), (char *)log_msg_str->data,
EDITOR_EOF_PREFIX);
*log_msg = log_msg_str->data;
return SVN_NO_ERROR;
}
if (! commit_items->nelts)
{
*log_msg = "";
return SVN_NO_ERROR;
}
while (! message)
{
int i;
svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool);
svn_error_t *err = SVN_NO_ERROR;
svn_string_t *msg_string = svn_string_create_empty(pool);
for (i = 0; i < commit_items->nelts; i++)
{
svn_client_commit_item3_t *item
= APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
const char *path = item->path;
char text_mod = '_', prop_mod = ' ', unlock = ' ';
if (! path)
path = item->url;
else if (lmb->base_dir)
path = svn_dirent_is_child(lmb->base_dir, path, pool);
if (! path || !*path)
path = ".";
if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
&& (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
text_mod = 'R';
else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
text_mod = 'A';
else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
text_mod = 'D';
else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)
text_mod = 'M';
if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS)
prop_mod = 'M';
if (! lmb->keep_locks
&& item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)
unlock = 'U';
svn_stringbuf_appendbyte(tmp_message, text_mod);
svn_stringbuf_appendbyte(tmp_message, prop_mod);
svn_stringbuf_appendbyte(tmp_message, unlock);
if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY)
svn_stringbuf_appendcstr(tmp_message, "+ ");
else
svn_stringbuf_appendcstr(tmp_message, " ");
svn_stringbuf_appendcstr(tmp_message, path);
svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR);
}
msg_string->data = tmp_message->data;
msg_string->len = tmp_message->len;
if (! lmb->non_interactive)
{
err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left,
lmb->editor_cmd,
lmb->base_dir ? lmb->base_dir : "",
msg_string, "svn-commit",
lmb->config, TRUE,
lmb->message_encoding,
pool);
}
else
{
return svn_error_create
(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
_("Cannot invoke editor to get log message "
"when non-interactive"));
}
*tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool,
lmb->tmpfile_left);
if (err)
{
if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR)
err = svn_error_quick_wrap
(err, _("Could not use external editor to fetch log message; "
"consider setting the $SVN_EDITOR environment variable "
"or using the --message (-m) or --file (-F) options"));
return svn_error_trace(err);
}
if (msg_string)
message = svn_stringbuf_create_from_string(msg_string, pool);
if (message)
truncate_buffer_at_prefix(&message->len, message->data,
EDITOR_EOF_PREFIX);
if (message)
{
apr_size_t len;
for (len = 0; len < message->len; len++)
{
if (! svn_ctype_isspace(message->data[len]))
break;
}
if (len == message->len)
message = NULL;
}
if (! message)
{
const char *reply;
SVN_ERR(svn_cmdline_prompt_user2
(&reply,
_("\nLog message unchanged or not specified\n"
"(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool));
if (reply)
{
int letter = apr_tolower(reply[0]);
if ('a' == letter)
{
SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool));
*tmp_file = lmb->tmpfile_left = NULL;
break;
}
if ('c' == letter)
{
SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool));
*tmp_file = lmb->tmpfile_left = NULL;
message = svn_stringbuf_create_empty(pool);
}
}
}
}
*log_msg = message ? message->data : NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__may_need_force(svn_error_t *err)
{
if (err
&& (err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE ||
err->apr_err == SVN_ERR_CLIENT_MODIFIED))
{
err = svn_error_quick_wrap
(err, _("Use --force to override this restriction (local modifications "
"may be lost)"));
}
return svn_error_trace(err);
}
svn_error_t *
svn_cl__error_checked_fputs(const char *string, FILE* stream)
{
errno = 0;
if (fputs(string, stream) == EOF)
{
if (apr_get_os_error())
return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
else
return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__try(svn_error_t *err,
apr_array_header_t *errors_seen,
svn_boolean_t quiet,
...)
{
if (err)
{
apr_status_t apr_err;
va_list ap;
va_start(ap, quiet);
while ((apr_err = va_arg(ap, apr_status_t)) != APR_SUCCESS)
{
if (errors_seen)
{
int i;
svn_boolean_t add = TRUE;
for (i = 0; i < errors_seen->nelts; i++)
{
if (APR_ARRAY_IDX(errors_seen, i,
apr_status_t) == err->apr_err)
{
add = FALSE;
break;
}
}
if (add)
APR_ARRAY_PUSH(errors_seen, apr_status_t) = err->apr_err;
}
if (err->apr_err == apr_err)
{
if (! quiet)
svn_handle_warning2(stderr, err, "svn: ");
svn_error_clear(err);
return SVN_NO_ERROR;
}
}
va_end(ap);
}
return svn_error_trace(err);
}
void
svn_cl__xml_tagged_cdata(svn_stringbuf_t **sb,
apr_pool_t *pool,
const char *tagname,
const char *string)
{
if (string)
{
svn_xml_make_open_tag(sb, pool, svn_xml_protect_pcdata,
tagname, SVN_VA_NULL);
svn_xml_escape_cdata_cstring(sb, string, pool);
svn_xml_make_close_tag(sb, pool, tagname);
}
}
void
svn_cl__print_xml_commit(svn_stringbuf_t **sb,
svn_revnum_t revision,
const char *author,
const char *date,
apr_pool_t *pool)
{
svn_xml_make_open_tag(sb, pool, svn_xml_normal, "commit",
"revision",
apr_psprintf(pool, "%ld", revision), SVN_VA_NULL);
if (author)
svn_cl__xml_tagged_cdata(sb, pool, "author", author);
if (date)
svn_cl__xml_tagged_cdata(sb, pool, "date", date);
svn_xml_make_close_tag(sb, pool, "commit");
}
void
svn_cl__print_xml_lock(svn_stringbuf_t **sb,
const svn_lock_t *lock,
apr_pool_t *pool)
{
svn_xml_make_open_tag(sb, pool, svn_xml_normal, "lock", SVN_VA_NULL);
svn_cl__xml_tagged_cdata(sb, pool, "token", lock->token);
svn_cl__xml_tagged_cdata(sb, pool, "owner", lock->owner);
svn_cl__xml_tagged_cdata(sb, pool, "comment", lock->comment);
svn_cl__xml_tagged_cdata(sb, pool, "created",
svn_time_to_cstring(lock->creation_date, pool));
if (lock->expiration_date != 0)
svn_cl__xml_tagged_cdata(sb, pool, "expires",
svn_time_to_cstring(lock->expiration_date, pool));
svn_xml_make_close_tag(sb, pool, "lock");
}
svn_error_t *
svn_cl__xml_print_header(const char *tagname,
apr_pool_t *pool)
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
svn_xml_make_header2(&sb, "UTF-8", pool);
svn_xml_make_open_tag(&sb, pool, svn_xml_normal, tagname, SVN_VA_NULL);
return svn_cl__error_checked_fputs(sb->data, stdout);
}
svn_error_t *
svn_cl__xml_print_footer(const char *tagname,
apr_pool_t *pool)
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
svn_xml_make_close_tag(&sb, pool, tagname);
return svn_cl__error_checked_fputs(sb->data, stdout);
}
static const svn_token_map_t map_node_kind_xml[] =
{
{ "none", svn_node_none },
{ "file", svn_node_file },
{ "dir", svn_node_dir },
{ "", svn_node_unknown },
{ NULL, 0 }
};
static const svn_token_map_t map_node_kind_human[] =
{
{ N_("none"), svn_node_none },
{ N_("file"), svn_node_file },
{ N_("dir"), svn_node_dir },
{ "", svn_node_unknown },
{ NULL, 0 }
};
const char *
svn_cl__node_kind_str_xml(svn_node_kind_t kind)
{
return svn_token__to_word(map_node_kind_xml, kind);
}
const char *
svn_cl__node_kind_str_human_readable(svn_node_kind_t kind)
{
return _(svn_token__to_word(map_node_kind_human, kind));
}
static const svn_token_map_t map_wc_operation_xml[] =
{
{ "none", svn_wc_operation_none },
{ "update", svn_wc_operation_update },
{ "switch", svn_wc_operation_switch },
{ "merge", svn_wc_operation_merge },
{ NULL, 0 }
};
static const svn_token_map_t map_wc_operation_human[] =
{
{ N_("none"), svn_wc_operation_none },
{ N_("update"), svn_wc_operation_update },
{ N_("switch"), svn_wc_operation_switch },
{ N_("merge"), svn_wc_operation_merge },
{ NULL, 0 }
};
const char *
svn_cl__operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool)
{
return svn_token__to_word(map_wc_operation_xml, operation);
}
const char *
svn_cl__operation_str_human_readable(svn_wc_operation_t operation,
apr_pool_t *pool)
{
return _(svn_token__to_word(map_wc_operation_human, operation));
}
svn_error_t *
svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets,
apr_getopt_t *os,
const apr_array_header_t *known_targets,
svn_client_ctx_t *ctx,
svn_boolean_t keep_last_origpath_on_truepath_collision,
apr_pool_t *pool)
{
svn_error_t *err = svn_client_args_to_target_array2(targets,
os,
known_targets,
ctx,
keep_last_origpath_on_truepath_collision,
pool);
if (err)
{
if (err->apr_err == SVN_ERR_RESERVED_FILENAME_SPECIFIED)
{
svn_handle_error2(err, stderr, FALSE, "svn: Skipping argument: ");
svn_error_clear(err);
}
else
return svn_error_trace(err);
}
return SVN_NO_ERROR;
}
static svn_error_t *
changelist_receiver(void *baton,
const char *path,
const char *changelist,
apr_pool_t *pool)
{
apr_array_header_t *paths = baton;
APR_ARRAY_PUSH(paths, const char *) = apr_pstrdup(paths->pool, path);
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__changelist_paths(apr_array_header_t **paths,
const apr_array_header_t *changelists,
const apr_array_header_t *targets,
svn_depth_t depth,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *found;
apr_hash_t *paths_hash;
apr_pool_t *iterpool;
int i;
if (! (changelists && changelists->nelts))
{
*paths = (apr_array_header_t *)targets;
return SVN_NO_ERROR;
}
found = apr_array_make(scratch_pool, 8, sizeof(const char *));
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < targets->nelts; i++)
{
const char *target = APR_ARRAY_IDX(targets, i, const char *);
svn_pool_clear(iterpool);
SVN_ERR(svn_client_get_changelists(target, changelists, depth,
changelist_receiver, found,
ctx, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_hash_from_cstring_keys(&paths_hash, found, result_pool));
return svn_error_trace(svn_hash_keys(paths, paths_hash, result_pool));
}
svn_cl__show_revs_t
svn_cl__show_revs_from_word(const char *word)
{
if (strcmp(word, SVN_CL__SHOW_REVS_MERGED) == 0)
return svn_cl__show_revs_merged;
if (strcmp(word, SVN_CL__SHOW_REVS_ELIGIBLE) == 0)
return svn_cl__show_revs_eligible;
return svn_cl__show_revs_invalid;
}
svn_error_t *
svn_cl__time_cstring_to_human_cstring(const char **human_cstring,
const char *data,
apr_pool_t *pool)
{
svn_error_t *err;
apr_time_t when;
err = svn_time_from_cstring(&when, data, pool);
if (err && err->apr_err == SVN_ERR_BAD_DATE)
{
svn_error_clear(err);
*human_cstring = _("(invalid date)");
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
*human_cstring = svn_time_to_human_cstring(when, pool);
return SVN_NO_ERROR;
}
const char *
svn_cl__node_description(const svn_wc_conflict_version_t *node,
const char *wc_repos_root_URL,
apr_pool_t *pool)
{
const char *root_str = "^";
const char *path_str = "...";
if (!node)
return apr_psprintf(pool, "(%s)",
svn_cl__node_kind_str_human_readable(svn_node_none));
if (node->repos_url &&
(wc_repos_root_URL == NULL ||
strcmp(node->repos_url, wc_repos_root_URL) != 0))
root_str = node->repos_url;
if (node->path_in_repos)
path_str = node->path_in_repos;
return apr_psprintf(pool, "(%s) %s@%ld",
svn_cl__node_kind_str_human_readable(node->node_kind),
svn_path_url_add_component2(root_str, path_str, pool),
node->peg_rev);
}
svn_error_t *
svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p,
const apr_array_header_t *targets,
apr_pool_t *pool)
{
int i;
apr_array_header_t *true_targets;
true_targets = apr_array_make(pool, targets->nelts, sizeof(const char *));
for (i = 0; i < targets->nelts; i++)
{
const char *target = APR_ARRAY_IDX(targets, i, const char *);
const char *true_target, *peg;
SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg,
target, pool));
if (peg[0] && peg[1])
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s': a peg revision is not allowed here"),
target);
APR_ARRAY_PUSH(true_targets, const char *) = true_target;
}
SVN_ERR_ASSERT(true_targets_p);
*true_targets_p = true_targets;
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__assert_homogeneous_target_type(const apr_array_header_t *targets)
{
svn_error_t *err;
err = svn_client__assert_homogeneous_target_type(targets);
if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, NULL);
return err;
}
svn_error_t *
svn_cl__check_target_is_local_path(const char *target)
{
if (svn_path_is_url(target))
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'%s' is not a local path"), target);
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__check_targets_are_local_paths(const apr_array_header_t *targets)
{
int i;
for (i = 0; i < targets->nelts; i++)
{
const char *target = APR_ARRAY_IDX(targets, i, const char *);
SVN_ERR(svn_cl__check_target_is_local_path(target));
}
return SVN_NO_ERROR;
}
const char *
svn_cl__local_style_skip_ancestor(const char *parent_path,
const char *path,
apr_pool_t *pool)
{
const char *relpath = NULL;
if (parent_path)
relpath = svn_dirent_skip_ancestor(parent_path, path);
return svn_dirent_local_style(relpath ? relpath : path, pool);
}
svn_error_t *
svn_cl__propset_print_binary_mime_type_warning(apr_array_header_t *targets,
const char *propname,
const svn_string_t *propval,
apr_pool_t *scratch_pool)
{
if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
for (i = 0; i < targets->nelts; i++)
{
const char *detected_mimetype;
const char *target = APR_ARRAY_IDX(targets, i, const char *);
const char *local_abspath;
const svn_string_t *canon_propval;
svn_node_kind_t node_kind;
svn_pool_clear(iterpool);
SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
SVN_ERR(svn_io_check_path(local_abspath, &node_kind, iterpool));
if (node_kind != svn_node_file)
continue;
SVN_ERR(svn_wc_canonicalize_svn_prop(&canon_propval,
propname, propval,
local_abspath,
svn_node_file,
FALSE, NULL, NULL,
iterpool));
if (svn_mime_type_is_binary(canon_propval->data))
{
SVN_ERR(svn_io_detect_mimetype2(&detected_mimetype,
local_abspath, NULL,
iterpool));
if (detected_mimetype == NULL ||
!svn_mime_type_is_binary(detected_mimetype))
svn_error_clear(svn_cmdline_fprintf(stderr, iterpool,
_("svn: warning: '%s' is a binary mime-type but file '%s' "
"looks like text; diff, merge, blame, and other "
"operations will stop working on this file\n"),
canon_propval->data,
svn_dirent_local_style(local_abspath, iterpool)));
}
}
svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
}