summaryrefslogtreecommitdiff
path: root/subversion/svn/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/util.c')
-rw-r--r--subversion/svn/util.c514
1 files changed, 81 insertions, 433 deletions
diff --git a/subversion/svn/util.c b/subversion/svn/util.c
index be8de9a..092bc7e 100644
--- a/subversion/svn/util.c
+++ b/subversion/svn/util.c
@@ -54,14 +54,17 @@
#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"
@@ -93,125 +96,6 @@ svn_cl__print_commit_info(const svn_commit_info_t *commit_info,
}
-/* Helper for the next two functions. Set *EDITOR to some path to an
- editor binary. Sources to search include: the EDITOR_CMD argument
- (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
- is not NULL), $VISUAL, $EDITOR. Return
- SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
-static svn_error_t *
-find_editor_binary(const char **editor,
- const char *editor_cmd,
- apr_hash_t *config)
-{
- const char *e;
- struct svn_config_t *cfg;
-
- /* Use the editor specified on the command line via --editor-cmd, if any. */
- e = editor_cmd;
-
- /* Otherwise look for the Subversion-specific environment variable. */
- if (! e)
- e = getenv("SVN_EDITOR");
-
- /* If not found then fall back on the config file. */
- if (! e)
- {
- cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING) : NULL;
- svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
- SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
- }
-
- /* If not found yet then try general purpose environment variables. */
- if (! e)
- e = getenv("VISUAL");
- if (! e)
- e = getenv("EDITOR");
-
-#ifdef SVN_CLIENT_EDITOR
- /* If still not found then fall back on the hard-coded default. */
- if (! e)
- e = SVN_CLIENT_EDITOR;
-#endif
-
- /* Error if there is no editor specified */
- if (e)
- {
- const char *c;
-
- for (c = e; *c; c++)
- if (!svn_ctype_isspace(*c))
- break;
-
- if (! *c)
- return svn_error_create
- (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
- _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
- "'editor-cmd' run-time configuration option is empty or "
- "consists solely of whitespace. Expected a shell command."));
- }
- else
- return svn_error_create
- (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
- _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
- "set, and no 'editor-cmd' run-time configuration option was found"));
-
- *editor = e;
- return SVN_NO_ERROR;
-}
-
-
-/* Use the visual editor to edit files. This requires that the file name itself
- be shell-safe, although the path to reach that file need not be shell-safe.
- */
-svn_error_t *
-svn_cl__edit_file_externally(const char *path,
- const char *editor_cmd,
- apr_hash_t *config,
- apr_pool_t *pool)
-{
- const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
- char *old_cwd;
- int sys_err;
- apr_status_t apr_err;
-
- svn_dirent_split(&base_dir, &file_name, path, pool);
-
- SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
-
- apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
- if (apr_err)
- return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
-
- /* APR doesn't like "" directories */
- if (base_dir[0] == '\0')
- base_dir_apr = ".";
- else
- SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
-
- apr_err = apr_filepath_set(base_dir_apr, pool);
- if (apr_err)
- return svn_error_wrap_apr
- (apr_err, _("Can't change working directory to '%s'"), base_dir);
-
- cmd = apr_psprintf(pool, "%s %s", editor, file_name);
- sys_err = system(cmd);
-
- apr_err = apr_filepath_set(old_cwd, pool);
- if (apr_err)
- svn_handle_error2(svn_error_wrap_apr
- (apr_err, _("Can't restore working directory")),
- stderr, TRUE /* fatal */, "svn: ");
-
- if (sys_err)
- /* Extracting any meaning from sys_err is platform specific, so just
- use the raw value. */
- return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
- _("system('%s') returned %d"), cmd, sys_err);
-
- return SVN_NO_ERROR;
-}
-
svn_error_t *
svn_cl__merge_file_externally(const char *base_path,
const char *their_path,
@@ -228,8 +112,7 @@ svn_cl__merge_file_externally(const char *base_path,
{
struct svn_config_t *cfg;
merge_tool = NULL;
- cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING) : NULL;
+ cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
/* apr_env_get wants char **, this wants const char ** */
svn_config_get(cfg, (const char **)&merge_tool,
SVN_CONFIG_SECTION_HELPERS,
@@ -290,248 +173,6 @@ svn_cl__merge_file_externally(const char *base_path,
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_cl__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
- const char **tmpfile_left /* UTF-8! */,
- const char *editor_cmd,
- const char *base_dir /* UTF-8! */,
- const svn_string_t *contents /* UTF-8! */,
- const char *filename,
- apr_hash_t *config,
- svn_boolean_t as_text,
- const char *encoding,
- apr_pool_t *pool)
-{
- const char *editor;
- const char *cmd;
- apr_file_t *tmp_file;
- const char *tmpfile_name;
- const char *tmpfile_native;
- const char *tmpfile_apr, *base_dir_apr;
- svn_string_t *translated_contents;
- apr_status_t apr_err, apr_err2;
- apr_size_t written;
- apr_finfo_t finfo_before, finfo_after;
- svn_error_t *err = SVN_NO_ERROR, *err2;
- char *old_cwd;
- int sys_err;
- svn_boolean_t remove_file = TRUE;
-
- SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
-
- /* Convert file contents from UTF-8/LF if desired. */
- if (as_text)
- {
- const char *translated;
- SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
- APR_EOL_STR, FALSE,
- NULL, FALSE, pool));
- translated_contents = svn_string_create("", pool);
- if (encoding)
- SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
- translated, encoding, pool));
- else
- SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
- translated, pool));
- translated_contents->len = strlen(translated_contents->data);
- }
- else
- translated_contents = svn_string_dup(contents, pool);
-
- /* Move to BASE_DIR to avoid getting characters that need quoting
- into tmpfile_name */
- apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
- if (apr_err)
- return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
-
- /* APR doesn't like "" directories */
- if (base_dir[0] == '\0')
- base_dir_apr = ".";
- else
- SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
- apr_err = apr_filepath_set(base_dir_apr, pool);
- if (apr_err)
- {
- return svn_error_wrap_apr
- (apr_err, _("Can't change working directory to '%s'"), base_dir);
- }
-
- /*** From here on, any problems that occur require us to cd back!! ***/
-
- /* Ask the working copy for a temporary file named FILENAME-something. */
- err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
- "" /* dirpath */,
- filename,
- ".tmp",
- svn_io_file_del_none, pool, pool);
-
- if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
- {
- const char *temp_dir_apr;
-
- svn_error_clear(err);
-
- SVN_ERR(svn_io_temp_dir(&base_dir, pool));
-
- SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
- apr_err = apr_filepath_set(temp_dir_apr, pool);
- if (apr_err)
- {
- return svn_error_wrap_apr
- (apr_err, _("Can't change working directory to '%s'"), base_dir);
- }
-
- err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
- "" /* dirpath */,
- filename,
- ".tmp",
- svn_io_file_del_none, pool, pool);
- }
-
- if (err)
- goto cleanup2;
-
- /*** From here on, any problems that occur require us to cleanup
- the file we just created!! ***/
-
- /* Dump initial CONTENTS to TMP_FILE. */
- apr_err = apr_file_write_full(tmp_file, translated_contents->data,
- translated_contents->len, &written);
-
- apr_err2 = apr_file_close(tmp_file);
- if (! apr_err)
- apr_err = apr_err2;
-
- /* Make sure the whole CONTENTS were written, else return an error. */
- if (apr_err)
- {
- err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
- tmpfile_name);
- goto cleanup;
- }
-
- err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
- if (err)
- goto cleanup;
-
- /* Get information about the temporary file before the user has
- been allowed to edit its contents. */
- apr_err = apr_stat(&finfo_before, tmpfile_apr,
- APR_FINFO_MTIME, pool);
- if (apr_err)
- {
- err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
- goto cleanup;
- }
-
- /* Backdate the file a little bit in case the editor is very fast
- and doesn't change the size. (Use two seconds, since some
- filesystems have coarse granularity.) It's OK if this call
- fails, so we don't check its return value.*/
- apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool);
-
- /* Stat it again to get the mtime we actually set. */
- apr_err = apr_stat(&finfo_before, tmpfile_apr,
- APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
- if (apr_err)
- {
- err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
- goto cleanup;
- }
-
- /* Prepare the editor command line. */
- err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
- if (err)
- goto cleanup;
- cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
-
- /* If the caller wants us to leave the file around, return the path
- of the file we'll use, and make a note not to destroy it. */
- if (tmpfile_left)
- {
- *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
- remove_file = FALSE;
- }
-
- /* Now, run the editor command line. */
- sys_err = system(cmd);
- if (sys_err != 0)
- {
- /* Extracting any meaning from sys_err is platform specific, so just
- use the raw value. */
- err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
- _("system('%s') returned %d"), cmd, sys_err);
- goto cleanup;
- }
-
- /* Get information about the temporary file after the assumed editing. */
- apr_err = apr_stat(&finfo_after, tmpfile_apr,
- APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
- if (apr_err)
- {
- err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
- goto cleanup;
- }
-
- /* If the file looks changed... */
- if ((finfo_before.mtime != finfo_after.mtime) ||
- (finfo_before.size != finfo_after.size))
- {
- svn_stringbuf_t *edited_contents_s;
- err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
- if (err)
- goto cleanup;
-
- *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
-
- /* Translate back to UTF8/LF if desired. */
- if (as_text)
- {
- err = svn_subst_translate_string2(edited_contents, FALSE, FALSE,
- *edited_contents, encoding, FALSE,
- pool, pool);
- if (err)
- {
- err = svn_error_quick_wrap
- (err,
- _("Error normalizing edited contents to internal format"));
- goto cleanup;
- }
- }
- }
- else
- {
- /* No edits seem to have been made */
- *edited_contents = NULL;
- }
-
- cleanup:
- if (remove_file)
- {
- /* Remove the file from disk. */
- err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
-
- /* Only report remove error if there was no previous error. */
- if (! err && err2)
- err = err2;
- else
- svn_error_clear(err2);
- }
-
- cleanup2:
- /* If we against all probability can't cd back, all further relative
- file references would be screwed up, so we have to abort. */
- apr_err = apr_filepath_set(old_cwd, pool);
- if (apr_err)
- {
- svn_handle_error2(svn_error_wrap_apr
- (apr_err, _("Can't restore working directory")),
- stderr, TRUE /* fatal */, "svn: ");
- }
-
- return svn_error_trace(err);
-}
-
/* A svn_client_ctx_t's log_msg_baton3, for use with
svn_cl__make_log_msg_baton(). */
@@ -583,8 +224,7 @@ svn_cl__make_log_msg_baton(void **baton,
}
else if (config)
{
- svn_config_t *cfg = apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING);
+ 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,
@@ -732,12 +372,12 @@ svn_cl__get_log_message(const char **log_msg,
while (! message)
{
/* We still don't have a valid commit message. Use $EDITOR to
- get one. Note that svn_cl__edit_externally will still return
- a UTF-8'ized log message. */
+ get one. Note that svn_cl__edit_string_externally will still
+ return a UTF-8'ized log 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("", pool);
+ svn_string_t *msg_string = svn_string_create_empty(pool);
for (i = 0; i < commit_items->nelts; i++)
{
@@ -793,12 +433,12 @@ svn_cl__get_log_message(const char **log_msg,
/* Use the external edit to get a log message. */
if (! lmb->non_interactive)
{
- err = svn_cl__edit_string_externally(&msg_string, &lmb->tmpfile_left,
- lmb->editor_cmd, lmb->base_dir,
- msg_string, "svn-commit",
- lmb->config, TRUE,
- lmb->message_encoding,
- pool);
+ err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left,
+ lmb->editor_cmd, lmb->base_dir,
+ msg_string, "svn-commit",
+ lmb->config, TRUE,
+ lmb->message_encoding,
+ pool);
}
else /* non_interactive flag says we can't pop up an editor, so error */
{
@@ -876,7 +516,7 @@ svn_cl__get_log_message(const char **log_msg,
{
SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool));
*tmp_file = lmb->tmpfile_left = NULL;
- message = svn_stringbuf_create("", pool);
+ message = svn_stringbuf_create_empty(pool);
}
/* If the user chooses anything else, the loop will
@@ -947,7 +587,7 @@ svn_cl__try(svn_error_t *err,
va_list ap;
va_start(ap, quiet);
- while ((apr_err = va_arg(ap, apr_status_t)) != SVN_NO_ERROR)
+ while ((apr_err = va_arg(ap, apr_status_t)) != APR_SUCCESS)
{
if (errors_seen)
{
@@ -1058,7 +698,7 @@ svn_error_t *
svn_cl__xml_print_header(const char *tagname,
apr_pool_t *pool)
{
- svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
/* <?xml version="1.0" encoding="UTF-8"?> */
svn_xml_make_header2(&sb, "UTF-8", pool);
@@ -1074,7 +714,7 @@ svn_error_t *
svn_cl__xml_print_footer(const char *tagname,
apr_pool_t *pool)
{
- svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
/* "</TAGNAME>" */
svn_xml_make_close_tag(&sb, pool, tagname);
@@ -1264,56 +904,6 @@ svn_cl__time_cstring_to_human_cstring(const char **human_cstring,
return SVN_NO_ERROR;
}
-
-/* Return a copy, allocated in POOL, of the next line of text from *STR
- * up to and including a CR and/or an LF. Change *STR to point to the
- * remainder of the string after the returned part. If there are no
- * characters to be returned, return NULL; never return an empty string.
- */
-static const char *
-next_line(const char **str, apr_pool_t *pool)
-{
- const char *start = *str;
- const char *p = *str;
-
- /* n.b. Throughout this fn, we never read any character after a '\0'. */
- /* Skip over all non-EOL characters, if any. */
- while (*p != '\r' && *p != '\n' && *p != '\0')
- p++;
- /* Skip over \r\n or \n\r or \r or \n, if any. */
- if (*p == '\r' || *p == '\n')
- {
- char c = *p++;
-
- if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
- p++;
- }
-
- /* Now p points after at most one '\n' and/or '\r'. */
- *str = p;
-
- if (p == start)
- return NULL;
-
- return svn_string_ncreate(start, p - start, pool)->data;
-}
-
-const char *
-svn_cl__indent_string(const char *str,
- const char *indent,
- apr_pool_t *pool)
-{
- svn_stringbuf_t *out = svn_stringbuf_create("", pool);
- const char *line;
-
- while ((line = next_line(&str, pool)))
- {
- svn_stringbuf_appendcstr(out, indent);
- svn_stringbuf_appendcstr(out, line);
- }
- return out->data;
-}
-
const char *
svn_cl__node_description(const svn_wc_conflict_version_t *node,
const char *wc_repos_root_URL,
@@ -1358,10 +948,14 @@ svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p,
for (i = 0; i < targets->nelts; i++)
{
const char *target = APR_ARRAY_IDX(targets, i, const char *);
- const char *true_target;
+ const char *true_target, *peg;
- SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, NULL,
+ 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;
}
@@ -1378,9 +972,7 @@ svn_cl__assert_homogeneous_target_type(const apr_array_header_t *targets)
err = svn_client__assert_homogeneous_target_type(targets);
if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
- return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
- _("Cannot mix repository and working copy "
- "targets"));
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err, NULL);
return err;
}
@@ -1419,3 +1011,59 @@ svn_cl__local_style_skip_ancestor(const char *parent_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;
+}
+