diff options
Diffstat (limited to 'subversion/svn/util.c')
-rw-r--r-- | subversion/svn/util.c | 514 |
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; +} + |