#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#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>
#include <apr_errno.h>
#include <apr_general.h>
#include <apr_strings.h>
#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)
#define CMDLINE_USE_CUSTOM_ENCODING
static const char *input_encoding = NULL;
static const char *output_encoding = NULL;
#elif defined(WIN32) && defined(_MSC_VER)
#define USE_WIN32_CONSOLE_SHORTCUT
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];
#ifndef WIN32
{
struct stat st;
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
if (error_stream)
setvbuf(error_stream, NULL, _IONBF, 0);
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
#ifdef WIN32
#ifdef CMDLINE_USE_CUSTOM_ENCODING
{
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
#ifdef SVN_USE_WIN32_CRASHHANDLER
if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
{
SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
#if _MSC_VER >= 1400
if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
{
_set_error_mode(_OUT_TO_STDERR);
_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
}
#endif
#endif
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)
{
--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);
}
}
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, ": ");
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;
}
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);
if (GetConsoleMode(stdout_handle, &ignored))
shortcut_stdout_to_console = TRUE;
}
if (_isatty(STDERR_FILENO))
{
DWORD ignored;
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
if (GetConsoleMode(stderr_handle, &ignored))
shortcut_stderr_to_console = TRUE;
}
#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;
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;
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
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));
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);
}
errno = 0;
if (fputs(out, stream) == EOF)
{
if (apr_get_os_error())
{
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)
{
errno = 0;
if (fflush(stream) == EOF)
{
if (apr_get_os_error())
{
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)
{
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;
};
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;
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 (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;
apr_array_header_t *providers;
SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
cfg, pool));
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)
{
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)
{
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));
svn_auth_get_simple_prompt_provider(&provider,
svn_cmdline_auth_simple_prompt,
pb,
2,
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, pool);
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
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 (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;
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;
}
svn_auth_open(ab, providers, pool);
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);
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);
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, "");
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
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;
}
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 (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3
&& score > max_score)
{
max_score = score;
max_similar = haystack[i];
}
}
return max_similar;
}
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;
}
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"
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;
#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));
{
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;
}
static const char *
next_line(const char **str, apr_pool_t *pool)
{
const char *start = *str;
const char *p = *str;
while (*p != '\r' && *p != '\n' && *p != '\0')
p++;
if (*p == '\r' || *p == '\n')
{
char c = *p++;
if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
p++;
}
*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,
FALSE,
NULL,
FALSE,
pool));
SVN_ERR(svn_stream_puts(out, pname_stdout));
}
else
{
printf(" %s\n", pname_stdout);
}
if (!names_only)
{
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 (!force_interactive && !non_interactive)
{
return svn_cmdline__stdin_is_a_terminal();
}
else if (force_interactive)
return TRUE;
return !non_interactive;
}
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;
e = editor_cmd;
if (! e)
e = getenv("SVN_EDITOR");
if (! e)
{
cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
}
if (! e)
e = getenv("VISUAL");
if (! e)
e = getenv("EDITOR");
#ifdef SVN_CLIENT_EDITOR
if (! e)
e = SVN_CLIENT_EDITOR;
#endif
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;
}
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;
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"));
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 , "svn: ");
if (sys_err)
return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("system('%s') returned %d"), cmd, sys_err);
return SVN_NO_ERROR;
}
svn_error_t *
svn_cmdline__edit_string_externally(svn_string_t **edited_contents ,
const char **tmpfile_left ,
const char *editor_cmd,
const char *base_dir ,
const svn_string_t *contents ,
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 *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));
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);
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"));
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);
}
err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
"" ,
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,
"" ,
filename,
".tmp",
svn_io_file_del_none, pool, pool);
}
if (err)
goto cleanup2;
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));
if (err)
goto cleanup;
err = svn_io_stat(&finfo_before, tmpfile_name, APR_FINFO_MTIME, pool);
if (err)
goto cleanup;
err = svn_io_set_file_affected_time(finfo_before.mtime
- apr_time_from_sec(2),
tmpfile_name, pool);
svn_error_clear(err);
err = svn_io_stat(&finfo_before, tmpfile_name,
APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
if (err)
goto cleanup;
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 (tmpfile_left)
{
*tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
remove_file = FALSE;
}
sys_err = system(cmd);
if (sys_err != 0)
{
err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
_("system('%s') returned %d"), cmd, sys_err);
goto cleanup;
}
err = svn_io_stat(&finfo_after, tmpfile_name,
APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
if (err)
goto cleanup;
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);
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
{
*edited_contents = NULL;
}
cleanup:
if (remove_file)
{
err = svn_error_compose_create(
err,
svn_io_remove_file2(tmpfile_name, FALSE, pool));
}
cleanup2:
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 , "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;
}
static volatile sig_atomic_t cancelled = FALSE;
static volatile sig_atomic_t signum_cancelled = 0;
static int signal_map [] = {
SIGINT
#ifdef SIGBREAK
, SIGBREAK
#endif
#ifdef SIGHUP
, SIGHUP
#endif
#ifdef SIGTERM
, SIGTERM
#endif
};
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;
}
}
static svn_error_t *
check_cancel(void *baton)
{
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
apr_signal(SIGPIPE, SIG_IGN);
#endif
#ifdef SIGXFSZ
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);
kill(getpid(), signum);
#endif
}
}