svn_error_t * svn_opt__arg_canonicalize_path(const char **path_out, const char *path_in, apr_pool_t *pool) { const char *apr_target; char *truenamed_target; /* APR-encoded */ apr_status_t apr_err; /* canonicalize case, and change all separators to '/'. */ SVN_ERR(svn_path_cstring_from_utf8(&apr_target, path_in, pool)); apr_err = apr_filepath_merge(&truenamed_target, "", apr_target, APR_FILEPATH_TRUENAME, pool); if (!apr_err) /* We have a canonicalized APR-encoded target now. */ apr_target = truenamed_target; else if (APR_STATUS_IS_ENOENT(apr_err)) /* It's okay for the file to not exist, that just means we have to accept the case given to the client. We'll use the original APR-encoded target. */ ; else return svn_error_createf(apr_err, NULL, _("Error resolving case of '%s'"), svn_dirent_local_style(path_in, pool)); /* convert back to UTF-8. */ SVN_ERR(svn_path_cstring_to_utf8(path_out, apr_target, pool)); *path_out = svn_dirent_canonicalize(*path_out, pool); 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__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); }