Author: rinrab Date: Wed May 28 19:56:36 2025 New Revision: 1925917 URL: http://svn.apache.org/viewvc?rev=1925917&view=rev Log: Move svn-editor stuff into a separate file, cmdline_editor.c.
* subversion/libsvn_subr/cmdline.c (find_editor_binary, escape_path, svn_cmdline__edit_file_externally, svn_cmdline__edit_string_externally): Removed. * subversion/libsvn_subr/cmdline_editor.c: svn-copied from cmdline.c, keeping only required things. (includes): cleanup. (find_editor_binary, escape_path, svn_cmdline__edit_file_externally, svn_cmdline__edit_string_externally): Copied from cmdline.c. No functional changes. Let GHA workflows verify it. Added: subversion/trunk/subversion/libsvn_subr/cmdline_editor.c - copied, changed from r1925915, subversion/trunk/subversion/libsvn_subr/cmdline.c Modified: subversion/trunk/subversion/libsvn_subr/cmdline.c Modified: subversion/trunk/subversion/libsvn_subr/cmdline.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cmdline.c?rev=1925917&r1=1925916&r2=1925917&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_subr/cmdline.c (original) +++ subversion/trunk/subversion/libsvn_subr/cmdline.c Wed May 28 19:56:36 2025 @@ -1240,522 +1240,6 @@ svn_cmdline__be_interactive(svn_boolean_ } -/* Helper for the edit_externally functions. Set *EDITOR to some path to an - editor binary, in native C string on Unix/Linux platforms and in UTF-8 - string on Windows platform. 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, - apr_pool_t *pool) -{ - const char *e; - const char *e_cfg; - struct svn_config_t *cfg; - apr_status_t status; - - /* Use the editor specified on the command line via --editor-cmd, if any. */ -#ifdef WIN32 - /* On Windows, editor_cmd is transcoded to the system active code page - because we use main() as a entry point without APR's (or our own) wrapper - in command line tools. */ - if (editor_cmd) - { - SVN_ERR(svn_utf_cstring_to_utf8(&e, editor_cmd, pool)); - } - else - { - e = NULL; - } -#else - e = editor_cmd; -#endif - - /* Otherwise look for the Subversion-specific environment variable. */ - if (! e) - { - status = apr_env_get((char **)&e, "SVN_EDITOR", pool); - if (status || ! *e) - { - e = NULL; - } - } - - /* If not found then fall back on the config file. */ - if (! e) - { - cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; - svn_config_get(cfg, &e_cfg, SVN_CONFIG_SECTION_HELPERS, - SVN_CONFIG_OPTION_EDITOR_CMD, NULL); -#ifdef WIN32 - if (e_cfg) - { - /* On Windows, we assume that config values are set in system active - code page, so we need transcode it here. */ - SVN_ERR(svn_utf_cstring_to_utf8(&e, e_cfg, pool)); - } -#else - e = e_cfg; -#endif - } - - /* If not found yet then try general purpose environment variables. */ - if (! e) - { - status = apr_env_get((char**)&e, "VISUAL", pool); - if (status || ! *e) - { - e = NULL; - } - } - if (! e) - { - status = apr_env_get((char**)&e, "EDITOR", pool); - if (status || ! *e) - { - e = NULL; - } - } - -#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; -} - -/* Wrapper around apr_pescape_shell() which also escapes whitespace. */ -static const char * -escape_path(apr_pool_t *pool, const char *orig_path) -{ - apr_size_t len, esc_len; - apr_status_t status; - - len = strlen(orig_path); - esc_len = 0; - - status = apr_escape_shell(NULL, orig_path, len, &esc_len); - - if (status == APR_NOTFOUND) - { - /* No special characters found by APR, so just surround it in double - quotes in case there is whitespace, which APR (as of 1.6.5) doesn't - consider special. */ - return apr_psprintf(pool, "\"%s\"", orig_path); - } - else - { -#ifdef WIN32 - const char *p; - /* Following the advice from - https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way - 1. Surround argument with double-quotes - 2. Escape backslashes, if they're followed by a double-quote, and double-quotes - 3. Escape any metacharacter, including double-quotes, with ^ */ - - /* Use APR's buffer size as an approximation for how large the escaped - string should be, plus 4 bytes for the leading/trailing ^" */ - svn_stringbuf_t *buf = svn_stringbuf_create_ensure(esc_len + 4, pool); - svn_stringbuf_appendcstr(buf, "^\""); - for (p = orig_path; *p; p++) - { - int nr_backslash = 0; - while (*p && *p == '\\') - { - nr_backslash++; - p++; - } - - if (!*p) - /* We've reached the end of the argument, so we need 2n backslash - characters. That will be interpreted as n backslashes and the - final double-quote character will be interpreted as the final - string delimiter. */ - svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2); - else if (*p == '"') - { - /* Double-quote as part of the argument means we need to double - any preceding backslashes and then add one to escape the - double-quote. */ - svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2 + 1); - svn_stringbuf_appendbyte(buf, '^'); - svn_stringbuf_appendbyte(buf, *p); - } - else - { - /* Since there's no double-quote, we just insert any backslashes - literally. No escaping needed. */ - svn_stringbuf_appendfill(buf, '\\', nr_backslash); - if (strchr("()%!^<>&|", *p)) - svn_stringbuf_appendbyte(buf, '^'); - svn_stringbuf_appendbyte(buf, *p); - } - } - svn_stringbuf_appendcstr(buf, "^\""); - return buf->data; -#else - char *path, *p, *esc_path; - - /* Account for whitespace, since APR doesn't */ - for (p = (char *)orig_path; *p; p++) - if (strchr(" \t\n\r", *p)) - esc_len++; - - path = apr_pcalloc(pool, esc_len); - apr_escape_shell(path, orig_path, len, NULL); - - p = esc_path = apr_pcalloc(pool, len + esc_len + 1); - while (*path) - { - if (strchr(" \t\n\r", *path)) - *p++ = '\\'; - *p++ = *path++; - } - - return esc_path; -#endif - } -} - -svn_error_t * -svn_cmdline__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; - const char *file_name_local; -#ifdef WIN32 - const WCHAR *wcmd; -#endif - 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, pool)); - - 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); - - SVN_ERR(svn_path_cstring_from_utf8(&file_name_local, - escape_path(pool, file_name), pool)); - /* editor is explicitly documented as being interpreted by the user's shell, - and as such should already be quoted/escaped as needed. */ -#ifndef WIN32 - cmd = apr_psprintf(pool, "%s %s", editor, file_name_local); - sys_err = system(cmd); -#else - cmd = apr_psprintf(pool, "\"%s %s\"", editor, file_name_local); - SVN_ERR(svn_utf__win32_utf8_to_utf16(&wcmd, cmd, NULL, pool)); - sys_err = _wsystem(wcmd); -#endif - - 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) - { - const char *cmd_utf8; - - /* Extracting any meaning from sys_err is platform specific, so just - use the raw value. */ - SVN_ERR(svn_path_cstring_to_utf8(&cmd_utf8, cmd, pool)); - return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, - _("system('%s') returned %d"), - cmd_utf8, sys_err); - } - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_cmdline__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; -#ifdef WIN32 - const WCHAR *wcmd; -#endif - apr_file_t *tmp_file; - const char *tmpfile_name; - const char *tmpfile_native; - const char *base_dir_apr; - svn_string_t *translated_contents; - apr_status_t apr_err; - apr_size_t written; - apr_finfo_t finfo_before, finfo_after; - svn_error_t *err = SVN_NO_ERROR; - char *old_cwd; - int sys_err; - svn_boolean_t remove_file = TRUE; - - SVN_ERR(find_editor_binary(&editor, editor_cmd, config, pool)); - - /* 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_empty(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. */ - err = svn_io_file_write_full(tmp_file, translated_contents->data, - translated_contents->len, &written, - pool); - - err = svn_error_compose_create(err, svn_io_file_close(tmp_file, pool)); - - /* Make sure the whole CONTENTS were written, else return an error. */ - if (err) - goto cleanup; - - /* Get information about the temporary file before the user has - been allowed to edit its contents. */ - err = svn_io_stat(&finfo_before, tmpfile_name, APR_FINFO_MTIME, pool); - if (err) - 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.*/ - err = svn_io_set_file_affected_time(finfo_before.mtime - - apr_time_from_sec(2), - tmpfile_name, pool); - svn_error_clear(err); - - /* Stat it again to get the mtime we actually set. */ - err = svn_io_stat(&finfo_before, tmpfile_name, - APR_FINFO_MTIME | APR_FINFO_SIZE, pool); - if (err) - goto cleanup; - - /* Prepare the editor command line. */ - err = svn_path_cstring_from_utf8(&tmpfile_native, - escape_path(pool, tmpfile_name), pool); - if (err) - goto cleanup; - - /* editor is explicitly documented as being interpreted by the user's shell, - and as such should already be quoted/escaped as needed. */ -#ifndef WIN32 - cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native); -#else - cmd = apr_psprintf(pool, "\"%s %s\"", editor, tmpfile_native); -#endif - - /* 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. */ -#ifndef WIN32 - sys_err = system(cmd); -#else - SVN_ERR(svn_utf__win32_utf8_to_utf16(&wcmd, cmd, NULL, pool)); - sys_err = _wsystem(wcmd); -#endif - if (sys_err != 0) - { - const char *cmd_utf8; - - /* Extracting any meaning from sys_err is platform specific, so just - use the raw value. */ - SVN_ERR(svn_path_cstring_to_utf8(&cmd_utf8, cmd, pool)); - err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, - _("system('%s') returned %d"), - cmd_utf8, sys_err); - goto cleanup; - } - - /* Get information about the temporary file after the assumed editing. */ - err = svn_io_stat(&finfo_after, tmpfile_name, - APR_FINFO_MTIME | APR_FINFO_SIZE, pool); - if (err) - 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, NULL, NULL, - *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. */ - err = svn_error_compose_create( - err, - svn_io_remove_file2(tmpfile_name, FALSE, pool)); - } - - 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); -} - svn_error_t * svn_cmdline__parse_trust_options( svn_boolean_t *trust_server_cert_unknown_ca, Copied: subversion/trunk/subversion/libsvn_subr/cmdline_editor.c (from r1925915, subversion/trunk/subversion/libsvn_subr/cmdline.c) URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cmdline_editor.c?p2=subversion/trunk/subversion/libsvn_subr/cmdline_editor.c&p1=subversion/trunk/subversion/libsvn_subr/cmdline.c&r1=1925915&r2=1925917&rev=1925917&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_subr/cmdline.c (original) +++ subversion/trunk/subversion/libsvn_subr/cmdline_editor.c Wed May 28 19:56:36 2025 @@ -1,5 +1,5 @@ /* - * cmdline.c : Helpers for command-line programs. + * cmdline_editor.c : svn editor implementation. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -21,1225 +21,31 @@ * ==================================================================== */ - -#include <stdlib.h> /* for atexit() */ -#include <stdio.h> /* for setvbuf() */ -#include <locale.h> /* for setlocale() */ - -#ifndef WIN32 -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#else -#include <crtdbg.h> -#include <io.h> -#include <conio.h> -#endif - -#include <apr.h> /* for STDIN_FILENO */ -#include <apr_errno.h> /* for apr_strerror */ #include <apr_version.h> #if APR_VERSION_AT_LEAST(1,5,0) #include <apr_escape.h> #else #include "private/svn_dep_compat.h" #endif -#include <apr_general.h> /* for apr_initialize/apr_terminate */ -#include <apr_strings.h> /* for apr_snprintf */ #include <apr_env.h> /* for apr_env_get */ #include <apr_pools.h> -#include <apr_signal.h> #include "svn_cmdline.h" #include "svn_ctype.h" -#include "svn_dso.h" #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_path.h" #include "svn_pools.h" #include "svn_error.h" -#include "svn_nls.h" #include "svn_utf.h" -#include "svn_auth.h" -#include "svn_xml.h" -#include "svn_base64.h" #include "svn_config.h" -#include "svn_sorts.h" -#include "svn_props.h" #include "svn_subst.h" #include "private/svn_cmdline_private.h" #include "private/svn_utf_private.h" -#include "private/svn_sorts_private.h" -#include "private/svn_string_private.h" #include "svn_private_config.h" -#include "win32_crashrpt.h" - -#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400) -/* Before Visual Studio 2005, the C runtime didn't handle encodings for the - stdio output handling. */ -#define CMDLINE_USE_CUSTOM_ENCODING - -/* The stdin encoding. If null, it's the same as the native encoding. */ -static const char *input_encoding = NULL; - -/* The stdout encoding. If null, it's the same as the native encoding. */ -static const char *output_encoding = NULL; -#elif defined(WIN32) && defined(_MSC_VER) -/* For now limit this code to Visual C++, as the result is highly dependent - on the CRT implementation */ -#define USE_WIN32_CONSOLE_SHORTCUT - -/* When TRUE, stdout/stderr is directly connected to a console */ -static svn_boolean_t shortcut_stdout_to_console = FALSE; -static svn_boolean_t shortcut_stderr_to_console = FALSE; -#endif - - -int -svn_cmdline_init(const char *progname, FILE *error_stream) -{ - apr_status_t status; - apr_pool_t *pool; - svn_error_t *err; - char prefix_buf[64]; /* 64 is probably bigger than most program names */ - -#ifndef WIN32 - { - struct stat st; - - /* The following makes sure that file descriptors 0 (stdin), 1 - (stdout) and 2 (stderr) will not be "reused", because if - e.g. file descriptor 2 would be reused when opening a file, a - write to stderr would write to that file and most likely - corrupt it. */ - if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) || - (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) || - (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1)) - { - if (error_stream) - fprintf(error_stream, "%s: error: cannot open '/dev/null'\n", - progname); - return EXIT_FAILURE; - } - } -#endif - - /* Ignore any errors encountered while attempting to change stream - buffering, as the streams should retain their default buffering - modes. */ - if (error_stream) - setvbuf(error_stream, NULL, _IONBF, 0); -#ifndef WIN32 - setvbuf(stdout, NULL, _IOLBF, 0); -#endif - -#ifdef WIN32 -#ifdef CMDLINE_USE_CUSTOM_ENCODING - /* Initialize the input and output encodings. */ - { - static char input_encoding_buffer[16]; - static char output_encoding_buffer[16]; - - apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer, - "CP%u", (unsigned) GetConsoleCP()); - input_encoding = input_encoding_buffer; - - apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer, - "CP%u", (unsigned) GetConsoleOutputCP()); - output_encoding = output_encoding_buffer; - } -#endif /* CMDLINE_USE_CUSTOM_ENCODING */ - -#ifdef SVN_USE_WIN32_CRASHHANDLER - if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER")) - { - /* Attach (but don't load) the crash handler */ - SetUnhandledExceptionFilter(svn__unhandled_exception_filter); - -#if _MSC_VER >= 1400 - /* ### This should work for VC++ 2002 (=1300) and later */ - /* Show the abort message on STDERR instead of a dialog to allow - scripts (e.g. our testsuite) to continue after an abort without - user intervention. Allow overriding for easier debugging. */ - if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT")) - { - /* In release mode: Redirect abort() errors to stderr */ - _set_error_mode(_OUT_TO_STDERR); - - /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. - (Ignored in release builds) */ - _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - } -#endif /* _MSC_VER >= 1400 */ - } -#endif /* SVN_USE_WIN32_CRASHHANDLER */ - -#endif /* WIN32 */ - - /* C programs default to the "C" locale. But because svn is supposed - to be i18n-aware, it should inherit the default locale of its - environment. */ - if (!setlocale(LC_ALL, "") - && !setlocale(LC_CTYPE, "")) - { - if (error_stream) - { - const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL }; - const char **env_var = &env_vars[0], *env_val = NULL; - while (*env_var) - { - env_val = getenv(*env_var); - if (env_val && env_val[0]) - break; - ++env_var; - } - - if (!*env_var) - { - /* Unlikely. Can setlocale fail if no env vars are set? */ - --env_var; - env_val = "not set"; - } - - fprintf(error_stream, - "%s: warning: cannot set LC_CTYPE locale\n" - "%s: warning: environment variable %s is %s\n" - "%s: warning: please check that your locale name is correct\n", - progname, progname, *env_var, env_val, progname); - } - } - - /* Initialize the APR subsystem, and register an atexit() function - to Uninitialize that subsystem at program exit. */ - status = apr_initialize(); - if (status) - { - if (error_stream) - { - char buf[1024]; - apr_strerror(status, buf, sizeof(buf) - 1); - fprintf(error_stream, - "%s: error: cannot initialize APR: %s\n", - progname, buf); - } - return EXIT_FAILURE; - } - - strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3); - prefix_buf[sizeof(prefix_buf) - 3] = '\0'; - strcat(prefix_buf, ": "); - - /* DSO pool must be created before any other pools used by the - application so that pool cleanup doesn't unload DSOs too - early. See docstring of svn_dso_initialize2(). */ - if ((err = svn_dso_initialize2())) - { - if (error_stream) - svn_handle_error2(err, error_stream, TRUE, prefix_buf); - - svn_error_clear(err); - return EXIT_FAILURE; - } - - if (0 > atexit(apr_terminate)) - { - if (error_stream) - fprintf(error_stream, - "%s: error: atexit registration failed\n", - progname); - return EXIT_FAILURE; - } - - /* Create a pool for use by the UTF-8 routines. It will be cleaned - up by APR at exit time. */ - pool = svn_pool_create(NULL); - svn_utf_initialize2(FALSE, pool); - - if ((err = svn_nls_init())) - { - if (error_stream) - svn_handle_error2(err, error_stream, TRUE, prefix_buf); - - svn_error_clear(err); - return EXIT_FAILURE; - } - -#ifdef USE_WIN32_CONSOLE_SHORTCUT - if (_isatty(STDOUT_FILENO)) - { - DWORD ignored; - HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - - /* stdout is a char device handle, but is it the console? */ - if (GetConsoleMode(stdout_handle, &ignored)) - shortcut_stdout_to_console = TRUE; - - /* Don't close stdout_handle */ - } - if (_isatty(STDERR_FILENO)) - { - DWORD ignored; - HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); - - /* stderr is a char device handle, but is it the console? */ - if (GetConsoleMode(stderr_handle, &ignored)) - shortcut_stderr_to_console = TRUE; - - /* Don't close stderr_handle */ - } -#endif - - return EXIT_SUCCESS; -} - - -svn_error_t * -svn_cmdline_cstring_from_utf8(const char **dest, - const char *src, - apr_pool_t *pool) -{ -#ifdef CMDLINE_USE_CUSTOM_ENCODING - if (output_encoding != NULL) - return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool); -#endif - - return svn_utf_cstring_from_utf8(dest, src, pool); -} - - -const char * -svn_cmdline_cstring_from_utf8_fuzzy(const char *src, - apr_pool_t *pool) -{ - return svn_utf__cstring_from_utf8_fuzzy(src, pool, - svn_cmdline_cstring_from_utf8); -} - - -svn_error_t * -svn_cmdline_cstring_to_utf8(const char **dest, - const char *src, - apr_pool_t *pool) -{ -#ifdef CMDLINE_USE_CUSTOM_ENCODING - if (input_encoding != NULL) - return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool); -#endif - - return svn_utf_cstring_to_utf8(dest, src, pool); -} - - -svn_error_t * -svn_cmdline_path_local_style_from_utf8(const char **dest, - const char *src, - apr_pool_t *pool) -{ - return svn_cmdline_cstring_from_utf8(dest, - svn_dirent_local_style(src, pool), - pool); -} - -svn_error_t * -svn_cmdline__stdin_readline(const char **result, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_stringbuf_t *buf = NULL; - svn_stream_t *stdin_stream = NULL; - svn_boolean_t oob = FALSE; - - SVN_ERR(svn_stream_for_stdin2(&stdin_stream, TRUE, scratch_pool)); - SVN_ERR(svn_stream_readline(stdin_stream, &buf, APR_EOL_STR, &oob, result_pool)); - - *result = buf->data; - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...) -{ - const char *message; - va_list ap; - - /* A note about encoding issues: - * APR uses the execution character set, but here we give it UTF-8 strings, - * both the fmt argument and any other string arguments. Since apr_pvsprintf - * only cares about and produces ASCII characters, this works under the - * assumption that all supported platforms use an execution character set - * with ASCII as a subset. - */ - - va_start(ap, fmt); - message = apr_pvsprintf(pool, fmt, ap); - va_end(ap); - - return svn_cmdline_fputs(message, stdout, pool); -} - -svn_error_t * -svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...) -{ - const char *message; - va_list ap; - - /* See svn_cmdline_printf () for a note about character encoding issues. */ - - va_start(ap, fmt); - message = apr_pvsprintf(pool, fmt, ap); - va_end(ap); - - return svn_cmdline_fputs(message, stream, pool); -} - -svn_error_t * -svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool) -{ - svn_error_t *err; - const char *out; - -#ifdef USE_WIN32_CONSOLE_SHORTCUT - /* For legacy reasons the Visual C++ runtime converts output to the console - from the native 'ansi' encoding, to unicode, then back to 'ansi' and then - onwards to the console which is implemented as unicode. - - For operations like 'svn status -v' this may cause about 70% of the total - processing time, with absolutely no gain. - - For this specific scenario this shortcut exists. It has the nice side - effect of allowing full unicode output to the console. - - Note that this shortcut is not used when the output is redirected, as in - that case the data is put on the pipe/file after the first conversion to - ansi. In this case the most expensive conversion is already avoided. - */ - if ((stream == stdout && shortcut_stdout_to_console) - || (stream == stderr && shortcut_stderr_to_console)) - { - WCHAR *result; - - if (string[0] == '\0') - return SVN_NO_ERROR; - - SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */ - - SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool)); - - if (_cputws(result)) - { - if (apr_get_os_error()) - { - return svn_error_wrap_apr(apr_get_os_error(), _("Write error")); - } - } - - return SVN_NO_ERROR; - } -#endif - - err = svn_cmdline_cstring_from_utf8(&out, string, pool); - - if (err) - { - svn_error_clear(err); - out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool); - } - - /* On POSIX systems, errno will be set on an error in fputs, but this might - not be the case on other platforms. We reset errno and only - use it if it was set by the below fputs call. Else, we just return - a generic error. */ - errno = 0; - - if (fputs(out, stream) == EOF) - { - if (apr_get_os_error()) /* is errno on POSIX */ - { - /* ### Issue #3014: Return a specific error for broken pipes, - * ### with a single element in the error chain. */ - if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error())) - return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); - else - 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_cmdline_fflush(FILE *stream) -{ - /* See comment in svn_cmdline_fputs about use of errno and stdio. */ - errno = 0; - if (fflush(stream) == EOF) - { - if (apr_get_os_error()) /* is errno on POSIX */ - { - /* ### Issue #3014: Return a specific error for broken pipes, - * ### with a single element in the error chain. */ - if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error())) - return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); - else - 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; -} - -const char *svn_cmdline_output_encoding(apr_pool_t *pool) -{ -#ifdef CMDLINE_USE_CUSTOM_ENCODING - if (output_encoding) - return apr_pstrdup(pool, output_encoding); -#endif - - return SVN_APR_LOCALE_CHARSET; -} - -int -svn_cmdline_handle_exit_error(svn_error_t *err, - apr_pool_t *pool, - const char *prefix) -{ - /* Issue #3014: - * Don't print anything on broken pipes. The pipe was likely - * closed by the process at the other end. We expect that - * process to perform error reporting as necessary. - * - * ### This assumes that there is only one error in a chain for - * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */ - if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR) - svn_handle_error2(err, stderr, FALSE, prefix); - svn_error_clear(err); - if (pool) - svn_pool_destroy(pool); - return EXIT_FAILURE; -} - -struct trust_server_cert_non_interactive_baton { - svn_boolean_t trust_server_cert_unknown_ca; - svn_boolean_t trust_server_cert_cn_mismatch; - svn_boolean_t trust_server_cert_expired; - svn_boolean_t trust_server_cert_not_yet_valid; - svn_boolean_t trust_server_cert_other_failure; -}; - -/* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. - - Don't actually prompt. Instead, set *CRED_P to valid credentials - iff FAILURES is empty or may be accepted according to the flags - in BATON. If there are any other failure bits, then set *CRED_P - to null (that is, reject the cert). - - Ignore MAY_SAVE; we don't save certs we never prompted for. - - Ignore REALM and CERT_INFO, - - Ignore any further films by George Lucas. */ -static svn_error_t * -trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p, - void *baton, - const char *realm, - apr_uint32_t failures, - const svn_auth_ssl_server_cert_info_t - *cert_info, - svn_boolean_t may_save, - apr_pool_t *pool) -{ - struct trust_server_cert_non_interactive_baton *b = baton; - apr_uint32_t non_ignored_failures; - *cred_p = NULL; - - /* Mask away bits we are instructed to ignore. */ - non_ignored_failures = failures & ~( - (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0) - | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0) - | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0) - | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0) - | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0) - ); - - /* If no failures remain, accept the certificate. */ - if (non_ignored_failures == 0) - { - *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); - (*cred_p)->may_save = FALSE; - (*cred_p)->accepted_failures = failures; - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab, - svn_boolean_t non_interactive, - const char *auth_username, - const char *auth_password, - const char *config_dir, - svn_boolean_t no_auth_cache, - svn_boolean_t trust_server_cert_unknown_ca, - svn_boolean_t trust_server_cert_cn_mismatch, - svn_boolean_t trust_server_cert_expired, - svn_boolean_t trust_server_cert_not_yet_valid, - svn_boolean_t trust_server_cert_other_failure, - svn_config_t *cfg, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *pool) - -{ - svn_boolean_t store_password_val = TRUE; - svn_boolean_t store_auth_creds_val = TRUE; - svn_auth_provider_object_t *provider; - svn_cmdline_prompt_baton2_t *pb = NULL; - - /* The whole list of registered providers */ - apr_array_header_t *providers; - - /* Populate the registered providers with the platform-specific providers */ - SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers, - cfg, pool)); - - /* If we have a cancellation function, cram it and the stuff it - needs into the prompt baton. */ - if (cancel_func) - { - pb = apr_palloc(pool, sizeof(*pb)); - pb->cancel_func = cancel_func; - pb->cancel_baton = cancel_baton; - pb->config_dir = config_dir; - } - - if (!non_interactive) - { - /* This provider doesn't prompt the user in order to get creds; - it prompts the user regarding the caching of creds. */ - svn_auth_get_simple_provider2(&provider, - svn_cmdline_auth_plaintext_prompt, - pb, pool); - } - else - { - svn_auth_get_simple_provider2(&provider, NULL, NULL, pool); - } - - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_username_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - svn_auth_get_ssl_server_trust_file_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_ssl_client_cert_file_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - if (!non_interactive) - { - /* This provider doesn't prompt the user in order to get creds; - it prompts the user regarding the caching of creds. */ - svn_auth_get_ssl_client_cert_pw_file_provider2 - (&provider, svn_cmdline_auth_plaintext_passphrase_prompt, - pb, pool); - } - else - { - svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL, - pool); - } - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - if (!non_interactive) - { - svn_boolean_t ssl_client_cert_file_prompt; - - SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt, - SVN_CONFIG_SECTION_AUTH, - SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT, - FALSE)); - - /* Two basic prompt providers: username/password, and just username. */ - svn_auth_get_simple_prompt_provider(&provider, - svn_cmdline_auth_simple_prompt, - pb, - 2, /* retry limit */ - pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - svn_auth_get_username_prompt_provider - (&provider, svn_cmdline_auth_username_prompt, pb, - 2, /* retry limit */ pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* SSL prompt providers: server-certs and client-cert-passphrases. */ - svn_auth_get_ssl_server_trust_prompt_provider - (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - svn_auth_get_ssl_client_cert_pw_prompt_provider - (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* If configuration allows, add a provider for client-cert path - prompting, too. */ - if (ssl_client_cert_file_prompt) - { - svn_auth_get_ssl_client_cert_prompt_provider - (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - } - } - else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch || - trust_server_cert_expired || trust_server_cert_not_yet_valid || - trust_server_cert_other_failure) - { - struct trust_server_cert_non_interactive_baton *b; - - b = apr_palloc(pool, sizeof(*b)); - b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca; - b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch; - b->trust_server_cert_expired = trust_server_cert_expired; - b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid; - b->trust_server_cert_other_failure = trust_server_cert_other_failure; - - /* Remember, only register this provider if non_interactive. */ - svn_auth_get_ssl_server_trust_prompt_provider - (&provider, trust_server_cert_non_interactive, b, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - } - - /* Build an authentication baton to give to libsvn_client. */ - svn_auth_open(ab, providers, pool); - - /* Place any default --username or --password credentials into the - auth_baton's run-time parameter hash. */ - if (auth_username) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, - auth_username); - if (auth_password) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, - auth_password); - - /* Same with the --non-interactive option. */ - if (non_interactive) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, ""); - - if (config_dir) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR, - config_dir); - - /* Determine whether storing passwords in any form is allowed. - * This is the deprecated location for this option, the new - * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may - * override the value we set here. */ - SVN_ERR(svn_config_get_bool(cfg, &store_password_val, - SVN_CONFIG_SECTION_AUTH, - SVN_CONFIG_OPTION_STORE_PASSWORDS, - SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS)); - - if (! store_password_val) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); - - /* Determine whether we are allowed to write to the auth/ area. - * This is the deprecated location for this option, the new - * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may - * override the value we set here. */ - SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val, - SVN_CONFIG_SECTION_AUTH, - SVN_CONFIG_OPTION_STORE_AUTH_CREDS, - SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS)); - - if (no_auth_cache || ! store_auth_creds_val) - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); - -#ifdef SVN_HAVE_GNOME_KEYRING - svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC, - &svn_cmdline__auth_gnome_keyring_unlock_prompt); -#endif /* SVN_HAVE_GNOME_KEYRING */ - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cmdline__getopt_init(apr_getopt_t **os, - int argc, - const char *argv[], - apr_pool_t *pool) -{ - apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv); - if (apr_err) - return svn_error_wrap_apr(apr_err, - _("Error initializing command line arguments")); - return SVN_NO_ERROR; -} - - -void -svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr, - const char* propname, - svn_string_t *propval, - svn_boolean_t inherited_prop, - apr_pool_t *pool) -{ - const char *xml_safe; - const char *encoding = NULL; - - if (*outstr == NULL) - *outstr = svn_stringbuf_create_empty(pool); - - if (svn_xml_is_xml_safe(propval->data, propval->len)) - { - svn_stringbuf_t *xml_esc = NULL; - svn_xml_escape_cdata_string(&xml_esc, propval, pool); - xml_safe = xml_esc->data; - } - else - { - const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE, - pool); - encoding = "base64"; - xml_safe = base64ed->data; - } - - if (encoding) - svn_xml_make_open_tag( - outstr, pool, svn_xml_protect_pcdata, - inherited_prop ? "inherited_property" : "property", - "name", propname, - "encoding", encoding, SVN_VA_NULL); - else - svn_xml_make_open_tag( - outstr, pool, svn_xml_protect_pcdata, - inherited_prop ? "inherited_property" : "property", - "name", propname, SVN_VA_NULL); - - svn_stringbuf_appendcstr(*outstr, xml_safe); - - svn_xml_make_close_tag( - outstr, pool, - inherited_prop ? "inherited_property" : "property"); - - return; -} - -/* Return the most similar string to NEEDLE in HAYSTACK, which contains - * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar. - */ -/* See svn_cl__similarity_check() for a more general solution. */ -static const char * -most_similar(const char *needle_cstr, - const char **haystack, - apr_size_t haystack_len, - apr_pool_t *scratch_pool) -{ - const char *max_similar = NULL; - apr_size_t max_score = 0; - apr_size_t i; - svn_membuf_t membuf; - svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool); - - svn_membuf__create(&membuf, 64, scratch_pool); - - for (i = 0; i < haystack_len; i++) - { - apr_size_t score; - svn_string_t *hay = svn_string_create(haystack[i], scratch_pool); - - score = svn_string__similarity(needle_str, hay, &membuf, NULL); - - /* If you update this factor, consider updating - * svn_cl__similarity_check(). */ - if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3 - && score > max_score) - { - max_score = score; - max_similar = haystack[i]; - } - } - - return max_similar; -} - -/* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */ -static svn_error_t * -string_in_array(const char *needle, - const char **haystack, - apr_size_t haystack_len, - apr_pool_t *scratch_pool) -{ - const char *next_of_kin; - apr_size_t i; - for (i = 0; i < haystack_len; i++) - { - if (!strcmp(needle, haystack[i])) - return SVN_NO_ERROR; - } - - /* Error. */ - next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool); - if (next_of_kin) - return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Ignoring unknown value '%s'; " - "did you mean '%s'?"), - needle, next_of_kin); - else - return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Ignoring unknown value '%s'"), - needle); -} - -#include "config_keys.inc" - -/* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are - * known. Return an error if not. (An unknown value may be either a typo - * or added in a newer minor version of Subversion.) */ -static svn_error_t * -validate_config_option(svn_cmdline__config_argument_t *config_option, - apr_pool_t *scratch_pool) -{ - svn_boolean_t arbitrary_keys = FALSE; - - /* TODO: some day, we could also verify that OPTION is valid for SECTION; - i.e., forbid invalid combinations such as config:auth:diff-extensions. */ - -#define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) ) - - SVN_ERR(string_in_array(config_option->file, svn__valid_config_files, - ARRAYLEN(svn__valid_config_files), - scratch_pool)); - SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections, - ARRAYLEN(svn__valid_config_sections), - scratch_pool)); - - /* Don't validate option names for sections such as servers[group], - * config[tunnels], and config[auto-props] that permit arbitrary options. */ - { - int i; - - for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++) - { - if (!strcmp(config_option->section, svn__empty_config_sections[i])) - arbitrary_keys = TRUE; - } - } - - if (! arbitrary_keys) - SVN_ERR(string_in_array(config_option->option, svn__valid_config_options, - ARRAYLEN(svn__valid_config_options), - scratch_pool)); - -#undef ARRAYLEN - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cmdline__parse_config_option(apr_array_header_t *config_options, - const char *opt_arg, - const char *prefix, - apr_pool_t *pool) -{ - svn_cmdline__config_argument_t *config_option; - const char *first_colon, *second_colon, *equals_sign; - apr_size_t len = strlen(opt_arg); - if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg)) - { - if ((second_colon = strchr(first_colon + 1, ':')) && - (second_colon != first_colon + 1)) - { - if ((equals_sign = strchr(second_colon + 1, '=')) && - (equals_sign != second_colon + 1)) - { - svn_error_t *warning; - - config_option = apr_pcalloc(pool, sizeof(*config_option)); - config_option->file = apr_pstrndup(pool, opt_arg, - first_colon - opt_arg); - config_option->section = apr_pstrndup(pool, first_colon + 1, - second_colon - first_colon - 1); - config_option->option = apr_pstrndup(pool, second_colon + 1, - equals_sign - second_colon - 1); - - warning = validate_config_option(config_option, pool); - if (warning) - { - svn_handle_warning2(stderr, warning, prefix); - svn_error_clear(warning); - } - - if (! (strchr(config_option->option, ':'))) - { - config_option->value = apr_pstrndup(pool, equals_sign + 1, - opt_arg + len - equals_sign - 1); - APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *) - = config_option; - return SVN_NO_ERROR; - } - } - } - } - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Invalid syntax of argument of --config-option")); -} - -svn_error_t * -svn_cmdline__apply_config_options(apr_hash_t *config, - const apr_array_header_t *config_options, - const char *prefix, - const char *argument_name) -{ - int i; - - for (i = 0; i < config_options->nelts; i++) - { - svn_config_t *cfg; - svn_cmdline__config_argument_t *arg = - APR_ARRAY_IDX(config_options, i, - svn_cmdline__config_argument_t *); - - cfg = svn_hash_gets(config, arg->file); - - if (cfg) - { - svn_config_set(cfg, arg->section, arg->option, arg->value); - } - else - { - svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Unrecognized file in argument of %s"), argument_name); - - svn_handle_warning2(stderr, err, prefix); - svn_error_clear(err); - } - } - - 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_cmdline__indent_string(const char *str, - const char *indent, - apr_pool_t *pool) -{ - svn_stringbuf_t *out = svn_stringbuf_create_empty(pool); - const char *line; - - while ((line = next_line(&str, pool))) - { - svn_stringbuf_appendcstr(out, indent); - svn_stringbuf_appendcstr(out, line); - } - return out->data; -} - -svn_error_t * -svn_cmdline__print_prop_hash(svn_stream_t *out, - apr_hash_t *prop_hash, - svn_boolean_t names_only, - apr_pool_t *pool) -{ - apr_array_header_t *sorted_props; - int i; - - sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, - pool); - for (i = 0; i < sorted_props->nelts; i++) - { - svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); - const char *pname = item.key; - svn_string_t *propval = item.value; - const char *pname_stdout; - - if (svn_prop_needs_translation(pname)) - SVN_ERR(svn_subst_detranslate_string(&propval, propval, - TRUE, pool)); - - SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool)); - - if (out) - { - pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout); - SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout, - APR_EOL_STR, /* 'native' eol */ - FALSE, /* no repair */ - NULL, /* no keywords */ - FALSE, /* no expansion */ - pool)); - - SVN_ERR(svn_stream_puts(out, pname_stdout)); - } - else - { - /* ### We leave these printfs for now, since if propval wasn't - translated above, we don't know anything about its encoding. - In fact, it might be binary data... */ - printf(" %s\n", pname_stdout); - } - - if (!names_only) - { - /* Add an extra newline to the value before indenting, so that - * every line of output has the indentation whether the value - * already ended in a newline or not. */ - const char *newval = apr_psprintf(pool, "%s\n", propval->data); - const char *indented_newval = svn_cmdline__indent_string(newval, - " ", - pool); - if (out) - { - SVN_ERR(svn_stream_puts(out, indented_newval)); - } - else - { - printf("%s", indented_newval); - } - } - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr, - apr_hash_t *prop_hash, - svn_boolean_t names_only, - svn_boolean_t inherited_props, - apr_pool_t *pool) -{ - apr_array_header_t *sorted_props; - int i; - - if (*outstr == NULL) - *outstr = svn_stringbuf_create_empty(pool); - - sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically, - pool); - for (i = 0; i < sorted_props->nelts; i++) - { - svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t); - const char *pname = item.key; - svn_string_t *propval = item.value; - - if (names_only) - { - svn_xml_make_open_tag( - outstr, pool, svn_xml_self_closing, - inherited_props ? "inherited_property" : "property", - "name", pname, SVN_VA_NULL); - } - else - { - const char *pname_out; - - if (svn_prop_needs_translation(pname)) - SVN_ERR(svn_subst_detranslate_string(&propval, propval, - TRUE, pool)); - - SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool)); - - svn_cmdline__print_xml_prop(outstr, pname_out, propval, - inherited_props, pool); - } - } - - return SVN_NO_ERROR; -} - -svn_boolean_t -svn_cmdline__stdin_is_a_terminal(void) -{ -#ifdef WIN32 - return (_isatty(STDIN_FILENO) != 0); -#else - return (isatty(STDIN_FILENO) != 0); -#endif -} - -svn_boolean_t -svn_cmdline__stdout_is_a_terminal(void) -{ -#ifdef WIN32 - return (_isatty(STDOUT_FILENO) != 0); -#else - return (isatty(STDOUT_FILENO) != 0); -#endif -} - -svn_boolean_t -svn_cmdline__stderr_is_a_terminal(void) -{ -#ifdef WIN32 - return (_isatty(STDERR_FILENO) != 0); -#else - return (isatty(STDERR_FILENO) != 0); -#endif -} - -svn_boolean_t -svn_cmdline__be_interactive(svn_boolean_t non_interactive, - svn_boolean_t force_interactive) -{ - /* If neither --non-interactive nor --force-interactive was passed, - * be interactive if stdin is a terminal. - * If --force-interactive was passed, always be interactive. */ - if (!force_interactive && !non_interactive) - { - return svn_cmdline__stdin_is_a_terminal(); - } - else if (force_interactive) - return TRUE; - - return !non_interactive; -} - - /* Helper for the edit_externally functions. Set *EDITOR to some path to an editor binary, in native C string on Unix/Linux platforms and in UTF-8 string on Windows platform. Sources to search include: the EDITOR_CMD @@ -1755,203 +561,3 @@ svn_cmdline__edit_string_externally(svn_ return svn_error_trace(err); } - -svn_error_t * -svn_cmdline__parse_trust_options( - svn_boolean_t *trust_server_cert_unknown_ca, - svn_boolean_t *trust_server_cert_cn_mismatch, - svn_boolean_t *trust_server_cert_expired, - svn_boolean_t *trust_server_cert_not_yet_valid, - svn_boolean_t *trust_server_cert_other_failure, - const char *opt_arg, - apr_pool_t *scratch_pool) -{ - apr_array_header_t *failures; - int i; - - *trust_server_cert_unknown_ca = FALSE; - *trust_server_cert_cn_mismatch = FALSE; - *trust_server_cert_expired = FALSE; - *trust_server_cert_not_yet_valid = FALSE; - *trust_server_cert_other_failure = FALSE; - - failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool); - - for (i = 0; i < failures->nelts; i++) - { - const char *value = APR_ARRAY_IDX(failures, i, const char *); - if (!strcmp(value, "unknown-ca")) - *trust_server_cert_unknown_ca = TRUE; - else if (!strcmp(value, "cn-mismatch")) - *trust_server_cert_cn_mismatch = TRUE; - else if (!strcmp(value, "expired")) - *trust_server_cert_expired = TRUE; - else if (!strcmp(value, "not-yet-valid")) - *trust_server_cert_not_yet_valid = TRUE; - else if (!strcmp(value, "other")) - *trust_server_cert_other_failure = TRUE; - else - return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Unknown value '%s' for %s.\n" - "Supported values: %s"), - value, - "--trust-server-cert-failures", - "unknown-ca, cn-mismatch, expired, " - "not-yet-valid, other"); - } - - return SVN_NO_ERROR; -} - -/* Flags to see if we've been cancelled by the client or not. */ -static volatile sig_atomic_t cancelled = FALSE; -static volatile sig_atomic_t signum_cancelled = 0; - -/* The signals we handle. */ -static int signal_map [] = { - SIGINT -#ifdef SIGBREAK - /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */ - , SIGBREAK -#endif -#ifdef SIGHUP - , SIGHUP -#endif -#ifdef SIGTERM - , SIGTERM -#endif -}; - -/* A signal handler to support cancellation. */ -static void -signal_handler(int signum) -{ - int i; - - apr_signal(signum, SIG_IGN); - cancelled = TRUE; - for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) - if (signal_map[i] == signum) - { - signum_cancelled = i + 1; - break; - } -} - -/* An svn_cancel_func_t callback. */ -static svn_error_t * -check_cancel(void *baton) -{ - /* Cancel baton should be always NULL in command line client. */ - SVN_ERR_ASSERT(baton == NULL); - if (cancelled) - return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal")); - else - return SVN_NO_ERROR; -} - -svn_cancel_func_t -svn_cmdline__setup_cancellation_handler(void) -{ - int i; - - for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) - apr_signal(signal_map[i], signal_handler); - -#ifdef SIGPIPE - /* Disable SIGPIPE generation for the platforms that have it. */ - apr_signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef SIGXFSZ - /* Disable SIGXFSZ generation for the platforms that have it, otherwise - * working with large files when compiled against an APR that doesn't have - * large file support will crash the program, which is uncool. */ - apr_signal(SIGXFSZ, SIG_IGN); -#endif - - return check_cancel; -} - -void -svn_cmdline__disable_cancellation_handler(void) -{ - int i; - - for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i) - apr_signal(signal_map[i], SIG_DFL); -} - -void -svn_cmdline__cancellation_exit(void) -{ - int signum = 0; - - if (cancelled && signum_cancelled) - signum = signal_map[signum_cancelled - 1]; - if (signum) - { -#ifndef WIN32 - apr_signal(signum, SIG_DFL); - /* No APR support for getpid() so cannot use apr_proc_kill(). */ - kill(getpid(), signum); -#endif - } -} - -#if defined(WIN32) - -svn_error_t * -svn_cmdline__win32_get_cstring_argv(const char **cstring_argv_p[], - int argc, - const wchar_t *argv[], - apr_pool_t *result_pool) -{ - apr_array_header_t *cstring_argv; - int i; - - cstring_argv = apr_array_make(result_pool, argc + 1, sizeof(const char *)); - - for (i = 0; i < argc; i++) - { - const wchar_t *arg = argv[i]; - char *cstring_arg; - int rv; - - /* Passing -1 for the string length guarantees that the returned length - will account for a terminating null character. */ - rv = WideCharToMultiByte(CP_ACP, 0, arg, -1, NULL, 0, NULL, NULL); - if (rv <= 0) - { - return svn_error_wrap_apr(apr_get_os_error(), - _("Conversion from UTF-16 failed")); - } - - cstring_arg = apr_palloc(result_pool, rv); - rv = WideCharToMultiByte(CP_ACP, 0, arg, -1, cstring_arg, rv, NULL, NULL); - if (rv <= 0) - { - return svn_error_wrap_apr(apr_get_os_error(), - _("Conversion from UTF-16 failed")); - } - - APR_ARRAY_PUSH(cstring_argv, const char *) = cstring_arg; - } - - APR_ARRAY_PUSH(cstring_argv, const char *) = NULL; - - *cstring_argv_p = (const char **)cstring_argv->elts; - return SVN_NO_ERROR; -} - -#endif - -svn_error_t * -svn_cmdline__default_get_cstring_argv(const char **cstring_argv_p[], - int argc, - const char *argv[], - apr_pool_t *result_pool) -{ - *cstring_argv_p = argv; - return SVN_NO_ERROR; -}