2 * cmdline.c : Helpers for command-line programs.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
25 #include <stdlib.h> /* for atexit() */
26 #include <stdio.h> /* for setvbuf() */
27 #include <locale.h> /* for setlocale() */
30 #include <sys/types.h>
40 #include <apr.h> /* for STDIN_FILENO */
41 #include <apr_errno.h> /* for apr_strerror */
42 #include <apr_escape.h>
43 #include <apr_general.h> /* for apr_initialize/apr_terminate */
44 #include <apr_strings.h> /* for apr_snprintf */
45 #include <apr_pools.h>
46 #include <apr_signal.h>
48 #include "svn_cmdline.h"
49 #include "svn_ctype.h"
51 #include "svn_dirent_uri.h"
54 #include "svn_pools.h"
55 #include "svn_error.h"
60 #include "svn_base64.h"
61 #include "svn_config.h"
62 #include "svn_sorts.h"
63 #include "svn_props.h"
64 #include "svn_subst.h"
66 #include "private/svn_cmdline_private.h"
67 #include "private/svn_utf_private.h"
68 #include "private/svn_sorts_private.h"
69 #include "private/svn_string_private.h"
71 #include "svn_private_config.h"
73 #include "win32_crashrpt.h"
75 #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400)
76 /* Before Visual Studio 2005, the C runtime didn't handle encodings for the
77 for the stdio output handling. */
78 #define CMDLINE_USE_CUSTOM_ENCODING
80 /* The stdin encoding. If null, it's the same as the native encoding. */
81 static const char *input_encoding = NULL;
83 /* The stdout encoding. If null, it's the same as the native encoding. */
84 static const char *output_encoding = NULL;
85 #elif defined(WIN32) && defined(_MSC_VER)
86 /* For now limit this code to Visual C++, as the result is highly dependent
87 on the CRT implementation */
88 #define USE_WIN32_CONSOLE_SHORTCUT
90 /* When TRUE, stdout/stderr is directly connected to a console */
91 static svn_boolean_t shortcut_stdout_to_console = FALSE;
92 static svn_boolean_t shortcut_stderr_to_console = FALSE;
97 svn_cmdline_init(const char *progname, FILE *error_stream)
102 char prefix_buf[64]; /* 64 is probably bigger than most program names */
108 /* The following makes sure that file descriptors 0 (stdin), 1
109 (stdout) and 2 (stderr) will not be "reused", because if
110 e.g. file descriptor 2 would be reused when opening a file, a
111 write to stderr would write to that file and most likely
113 if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) ||
114 (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) ||
115 (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1))
118 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
125 /* Ignore any errors encountered while attempting to change stream
126 buffering, as the streams should retain their default buffering
129 setvbuf(error_stream, NULL, _IONBF, 0);
131 setvbuf(stdout, NULL, _IOLBF, 0);
135 #ifdef CMDLINE_USE_CUSTOM_ENCODING
136 /* Initialize the input and output encodings. */
138 static char input_encoding_buffer[16];
139 static char output_encoding_buffer[16];
141 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer,
142 "CP%u", (unsigned) GetConsoleCP());
143 input_encoding = input_encoding_buffer;
145 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer,
146 "CP%u", (unsigned) GetConsoleOutputCP());
147 output_encoding = output_encoding_buffer;
149 #endif /* CMDLINE_USE_CUSTOM_ENCODING */
151 #ifdef SVN_USE_WIN32_CRASHHANDLER
152 if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
154 /* Attach (but don't load) the crash handler */
155 SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
158 /* ### This should work for VC++ 2002 (=1300) and later */
159 /* Show the abort message on STDERR instead of a dialog to allow
160 scripts (e.g. our testsuite) to continue after an abort without
161 user intervention. Allow overriding for easier debugging. */
162 if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
164 /* In release mode: Redirect abort() errors to stderr */
165 _set_error_mode(_OUT_TO_STDERR);
167 /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
168 (Ignored in release builds) */
169 _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
170 _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
171 _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
172 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
173 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
174 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
176 #endif /* _MSC_VER >= 1400 */
178 #endif /* SVN_USE_WIN32_CRASHHANDLER */
182 /* C programs default to the "C" locale. But because svn is supposed
183 to be i18n-aware, it should inherit the default locale of its
185 if (!setlocale(LC_ALL, "")
186 && !setlocale(LC_CTYPE, ""))
190 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
191 const char **env_var = &env_vars[0], *env_val = NULL;
194 env_val = getenv(*env_var);
195 if (env_val && env_val[0])
202 /* Unlikely. Can setlocale fail if no env vars are set? */
207 fprintf(error_stream,
208 "%s: warning: cannot set LC_CTYPE locale\n"
209 "%s: warning: environment variable %s is %s\n"
210 "%s: warning: please check that your locale name is correct\n",
211 progname, progname, *env_var, env_val, progname);
215 /* Initialize the APR subsystem, and register an atexit() function
216 to Uninitialize that subsystem at program exit. */
217 status = apr_initialize();
223 apr_strerror(status, buf, sizeof(buf) - 1);
224 fprintf(error_stream,
225 "%s: error: cannot initialize APR: %s\n",
231 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3);
232 prefix_buf[sizeof(prefix_buf) - 3] = '\0';
233 strcat(prefix_buf, ": ");
235 /* DSO pool must be created before any other pools used by the
236 application so that pool cleanup doesn't unload DSOs too
237 early. See docstring of svn_dso_initialize2(). */
238 if ((err = svn_dso_initialize2()))
241 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
243 svn_error_clear(err);
247 if (0 > atexit(apr_terminate))
250 fprintf(error_stream,
251 "%s: error: atexit registration failed\n",
256 /* Create a pool for use by the UTF-8 routines. It will be cleaned
257 up by APR at exit time. */
258 pool = svn_pool_create(NULL);
259 svn_utf_initialize2(FALSE, pool);
261 if ((err = svn_nls_init()))
264 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
266 svn_error_clear(err);
270 #ifdef USE_WIN32_CONSOLE_SHORTCUT
271 if (_isatty(STDOUT_FILENO))
274 HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
276 /* stdout is a char device handle, but is it the console? */
277 if (GetConsoleMode(stdout_handle, &ignored))
278 shortcut_stdout_to_console = TRUE;
280 /* Don't close stdout_handle */
282 if (_isatty(STDERR_FILENO))
285 HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
287 /* stderr is a char device handle, but is it the console? */
288 if (GetConsoleMode(stderr_handle, &ignored))
289 shortcut_stderr_to_console = TRUE;
291 /* Don't close stderr_handle */
300 svn_cmdline_cstring_from_utf8(const char **dest,
304 #ifdef CMDLINE_USE_CUSTOM_ENCODING
305 if (output_encoding != NULL)
306 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
309 return svn_utf_cstring_from_utf8(dest, src, pool);
314 svn_cmdline_cstring_from_utf8_fuzzy(const char *src,
317 return svn_utf__cstring_from_utf8_fuzzy(src, pool,
318 svn_cmdline_cstring_from_utf8);
323 svn_cmdline_cstring_to_utf8(const char **dest,
327 #ifdef CMDLINE_USE_CUSTOM_ENCODING
328 if (input_encoding != NULL)
329 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
332 return svn_utf_cstring_to_utf8(dest, src, pool);
337 svn_cmdline_path_local_style_from_utf8(const char **dest,
341 return svn_cmdline_cstring_from_utf8(dest,
342 svn_dirent_local_style(src, pool),
347 svn_cmdline__stdin_readline(const char **result,
348 apr_pool_t *result_pool,
349 apr_pool_t *scratch_pool)
351 svn_stringbuf_t *buf = NULL;
352 svn_stream_t *stdin_stream = NULL;
353 svn_boolean_t oob = FALSE;
355 SVN_ERR(svn_stream_for_stdin2(&stdin_stream, TRUE, scratch_pool));
356 SVN_ERR(svn_stream_readline(stdin_stream, &buf, APR_EOL_STR, &oob, result_pool));
364 svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...)
369 /* A note about encoding issues:
370 * APR uses the execution character set, but here we give it UTF-8 strings,
371 * both the fmt argument and any other string arguments. Since apr_pvsprintf
372 * only cares about and produces ASCII characters, this works under the
373 * assumption that all supported platforms use an execution character set
374 * with ASCII as a subset.
378 message = apr_pvsprintf(pool, fmt, ap);
381 return svn_cmdline_fputs(message, stdout, pool);
385 svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...)
390 /* See svn_cmdline_printf () for a note about character encoding issues. */
393 message = apr_pvsprintf(pool, fmt, ap);
396 return svn_cmdline_fputs(message, stream, pool);
400 svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool)
405 #ifdef USE_WIN32_CONSOLE_SHORTCUT
406 /* For legacy reasons the Visual C++ runtime converts output to the console
407 from the native 'ansi' encoding, to unicode, then back to 'ansi' and then
408 onwards to the console which is implemented as unicode.
410 For operations like 'svn status -v' this may cause about 70% of the total
411 processing time, with absolutely no gain.
413 For this specific scenario this shortcut exists. It has the nice side
414 effect of allowing full unicode output to the console.
416 Note that this shortcut is not used when the output is redirected, as in
417 that case the data is put on the pipe/file after the first conversion to
418 ansi. In this case the most expensive conversion is already avoided.
420 if ((stream == stdout && shortcut_stdout_to_console)
421 || (stream == stderr && shortcut_stderr_to_console))
425 if (string[0] == '\0')
428 SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */
430 SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool));
434 if (apr_get_os_error())
436 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
444 err = svn_cmdline_cstring_from_utf8(&out, string, pool);
448 svn_error_clear(err);
449 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
452 /* On POSIX systems, errno will be set on an error in fputs, but this might
453 not be the case on other platforms. We reset errno and only
454 use it if it was set by the below fputs call. Else, we just return
458 if (fputs(out, stream) == EOF)
460 if (apr_get_os_error()) /* is errno on POSIX */
462 /* ### Issue #3014: Return a specific error for broken pipes,
463 * ### with a single element in the error chain. */
464 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
465 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
467 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
470 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
477 svn_cmdline_fflush(FILE *stream)
479 /* See comment in svn_cmdline_fputs about use of errno and stdio. */
481 if (fflush(stream) == EOF)
483 if (apr_get_os_error()) /* is errno on POSIX */
485 /* ### Issue #3014: Return a specific error for broken pipes,
486 * ### with a single element in the error chain. */
487 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
488 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
490 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
493 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
499 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
501 #ifdef CMDLINE_USE_CUSTOM_ENCODING
503 return apr_pstrdup(pool, output_encoding);
506 return SVN_APR_LOCALE_CHARSET;
510 svn_cmdline_handle_exit_error(svn_error_t *err,
515 * Don't print anything on broken pipes. The pipe was likely
516 * closed by the process at the other end. We expect that
517 * process to perform error reporting as necessary.
519 * ### This assumes that there is only one error in a chain for
520 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
521 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
522 svn_handle_error2(err, stderr, FALSE, prefix);
523 svn_error_clear(err);
525 svn_pool_destroy(pool);
529 struct trust_server_cert_non_interactive_baton {
530 svn_boolean_t trust_server_cert_unknown_ca;
531 svn_boolean_t trust_server_cert_cn_mismatch;
532 svn_boolean_t trust_server_cert_expired;
533 svn_boolean_t trust_server_cert_not_yet_valid;
534 svn_boolean_t trust_server_cert_other_failure;
537 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'.
539 Don't actually prompt. Instead, set *CRED_P to valid credentials
540 iff FAILURES is empty or may be accepted according to the flags
541 in BATON. If there are any other failure bits, then set *CRED_P
542 to null (that is, reject the cert).
544 Ignore MAY_SAVE; we don't save certs we never prompted for.
546 Ignore REALM and CERT_INFO,
548 Ignore any further films by George Lucas. */
550 trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p,
553 apr_uint32_t failures,
554 const svn_auth_ssl_server_cert_info_t
556 svn_boolean_t may_save,
559 struct trust_server_cert_non_interactive_baton *b = baton;
560 apr_uint32_t non_ignored_failures;
563 /* Mask away bits we are instructed to ignore. */
564 non_ignored_failures = failures & ~(
565 (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0)
566 | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0)
567 | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0)
568 | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0)
569 | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0)
572 /* If no failures remain, accept the certificate. */
573 if (non_ignored_failures == 0)
575 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
576 (*cred_p)->may_save = FALSE;
577 (*cred_p)->accepted_failures = failures;
584 svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab,
585 svn_boolean_t non_interactive,
586 const char *auth_username,
587 const char *auth_password,
588 const char *config_dir,
589 svn_boolean_t no_auth_cache,
590 svn_boolean_t trust_server_cert_unknown_ca,
591 svn_boolean_t trust_server_cert_cn_mismatch,
592 svn_boolean_t trust_server_cert_expired,
593 svn_boolean_t trust_server_cert_not_yet_valid,
594 svn_boolean_t trust_server_cert_other_failure,
596 svn_cancel_func_t cancel_func,
601 svn_boolean_t store_password_val = TRUE;
602 svn_boolean_t store_auth_creds_val = TRUE;
603 svn_auth_provider_object_t *provider;
604 svn_cmdline_prompt_baton2_t *pb = NULL;
606 /* The whole list of registered providers */
607 apr_array_header_t *providers;
609 /* Populate the registered providers with the platform-specific providers */
610 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
613 /* If we have a cancellation function, cram it and the stuff it
614 needs into the prompt baton. */
617 pb = apr_palloc(pool, sizeof(*pb));
618 pb->cancel_func = cancel_func;
619 pb->cancel_baton = cancel_baton;
620 pb->config_dir = config_dir;
623 if (!non_interactive)
625 /* This provider doesn't prompt the user in order to get creds;
626 it prompts the user regarding the caching of creds. */
627 svn_auth_get_simple_provider2(&provider,
628 svn_cmdline_auth_plaintext_prompt,
633 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool);
636 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
637 svn_auth_get_username_provider(&provider, pool);
638 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
640 svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
641 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
642 svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
643 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
645 if (!non_interactive)
647 /* This provider doesn't prompt the user in order to get creds;
648 it prompts the user regarding the caching of creds. */
649 svn_auth_get_ssl_client_cert_pw_file_provider2
650 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt,
655 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
658 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
660 if (!non_interactive)
662 svn_boolean_t ssl_client_cert_file_prompt;
664 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
665 SVN_CONFIG_SECTION_AUTH,
666 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
669 /* Two basic prompt providers: username/password, and just username. */
670 svn_auth_get_simple_prompt_provider(&provider,
671 svn_cmdline_auth_simple_prompt,
675 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
677 svn_auth_get_username_prompt_provider
678 (&provider, svn_cmdline_auth_username_prompt, pb,
679 2, /* retry limit */ pool);
680 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
682 /* SSL prompt providers: server-certs and client-cert-passphrases. */
683 svn_auth_get_ssl_server_trust_prompt_provider
684 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
685 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
687 svn_auth_get_ssl_client_cert_pw_prompt_provider
688 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
689 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
691 /* If configuration allows, add a provider for client-cert path
693 if (ssl_client_cert_file_prompt)
695 svn_auth_get_ssl_client_cert_prompt_provider
696 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
697 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
700 else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch ||
701 trust_server_cert_expired || trust_server_cert_not_yet_valid ||
702 trust_server_cert_other_failure)
704 struct trust_server_cert_non_interactive_baton *b;
706 b = apr_palloc(pool, sizeof(*b));
707 b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca;
708 b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch;
709 b->trust_server_cert_expired = trust_server_cert_expired;
710 b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid;
711 b->trust_server_cert_other_failure = trust_server_cert_other_failure;
713 /* Remember, only register this provider if non_interactive. */
714 svn_auth_get_ssl_server_trust_prompt_provider
715 (&provider, trust_server_cert_non_interactive, b, pool);
716 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
719 /* Build an authentication baton to give to libsvn_client. */
720 svn_auth_open(ab, providers, pool);
722 /* Place any default --username or --password credentials into the
723 auth_baton's run-time parameter hash. */
725 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
728 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
731 /* Same with the --non-interactive option. */
733 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
736 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
739 /* Determine whether storing passwords in any form is allowed.
740 * This is the deprecated location for this option, the new
741 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
742 * override the value we set here. */
743 SVN_ERR(svn_config_get_bool(cfg, &store_password_val,
744 SVN_CONFIG_SECTION_AUTH,
745 SVN_CONFIG_OPTION_STORE_PASSWORDS,
746 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS));
748 if (! store_password_val)
749 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
751 /* Determine whether we are allowed to write to the auth/ area.
752 * This is the deprecated location for this option, the new
753 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
754 * override the value we set here. */
755 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val,
756 SVN_CONFIG_SECTION_AUTH,
757 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
758 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS));
760 if (no_auth_cache || ! store_auth_creds_val)
761 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
763 #ifdef SVN_HAVE_GNOME_KEYRING
764 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC,
765 &svn_cmdline__auth_gnome_keyring_unlock_prompt);
766 #endif /* SVN_HAVE_GNOME_KEYRING */
772 svn_cmdline__getopt_init(apr_getopt_t **os,
777 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
779 return svn_error_wrap_apr(apr_err,
780 _("Error initializing command line arguments"));
786 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
787 const char* propname,
788 svn_string_t *propval,
789 svn_boolean_t inherited_prop,
792 const char *xml_safe;
793 const char *encoding = NULL;
796 *outstr = svn_stringbuf_create_empty(pool);
798 if (svn_xml_is_xml_safe(propval->data, propval->len))
800 svn_stringbuf_t *xml_esc = NULL;
801 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
802 xml_safe = xml_esc->data;
806 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
809 xml_safe = base64ed->data;
813 svn_xml_make_open_tag(
814 outstr, pool, svn_xml_protect_pcdata,
815 inherited_prop ? "inherited_property" : "property",
817 "encoding", encoding, SVN_VA_NULL);
819 svn_xml_make_open_tag(
820 outstr, pool, svn_xml_protect_pcdata,
821 inherited_prop ? "inherited_property" : "property",
822 "name", propname, SVN_VA_NULL);
824 svn_stringbuf_appendcstr(*outstr, xml_safe);
826 svn_xml_make_close_tag(
828 inherited_prop ? "inherited_property" : "property");
833 /* Return the most similar string to NEEDLE in HAYSTACK, which contains
834 * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar.
836 /* See svn_cl__similarity_check() for a more general solution. */
838 most_similar(const char *needle_cstr,
839 const char **haystack,
840 apr_size_t haystack_len,
841 apr_pool_t *scratch_pool)
843 const char *max_similar = NULL;
844 apr_size_t max_score = 0;
847 svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool);
849 svn_membuf__create(&membuf, 64, scratch_pool);
851 for (i = 0; i < haystack_len; i++)
854 svn_string_t *hay = svn_string_create(haystack[i], scratch_pool);
856 score = svn_string__similarity(needle_str, hay, &membuf, NULL);
858 /* If you update this factor, consider updating
859 * svn_cl__similarity_check(). */
860 if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3
861 && score > max_score)
864 max_similar = haystack[i];
871 /* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */
873 string_in_array(const char *needle,
874 const char **haystack,
875 apr_size_t haystack_len,
876 apr_pool_t *scratch_pool)
878 const char *next_of_kin;
880 for (i = 0; i < haystack_len; i++)
882 if (!strcmp(needle, haystack[i]))
887 next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool);
889 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
890 _("Ignoring unknown value '%s'; "
891 "did you mean '%s'?"),
892 needle, next_of_kin);
894 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
895 _("Ignoring unknown value '%s'"),
899 #include "config_keys.inc"
901 /* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are
902 * known. Return an error if not. (An unknown value may be either a typo
903 * or added in a newer minor version of Subversion.) */
905 validate_config_option(svn_cmdline__config_argument_t *config_option,
906 apr_pool_t *scratch_pool)
908 svn_boolean_t arbitrary_keys = FALSE;
910 /* TODO: some day, we could also verify that OPTION is valid for SECTION;
911 i.e., forbid invalid combinations such as config:auth:diff-extensions. */
913 #define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) )
915 SVN_ERR(string_in_array(config_option->file, svn__valid_config_files,
916 ARRAYLEN(svn__valid_config_files),
918 SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections,
919 ARRAYLEN(svn__valid_config_sections),
922 /* Don't validate option names for sections such as servers[group],
923 * config[tunnels], and config[auto-props] that permit arbitrary options. */
927 for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++)
929 if (!strcmp(config_option->section, svn__empty_config_sections[i]))
930 arbitrary_keys = TRUE;
934 if (! arbitrary_keys)
935 SVN_ERR(string_in_array(config_option->option, svn__valid_config_options,
936 ARRAYLEN(svn__valid_config_options),
945 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
950 svn_cmdline__config_argument_t *config_option;
951 const char *first_colon, *second_colon, *equals_sign;
952 apr_size_t len = strlen(opt_arg);
953 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
955 if ((second_colon = strchr(first_colon + 1, ':')) &&
956 (second_colon != first_colon + 1))
958 if ((equals_sign = strchr(second_colon + 1, '=')) &&
959 (equals_sign != second_colon + 1))
961 svn_error_t *warning;
963 config_option = apr_pcalloc(pool, sizeof(*config_option));
964 config_option->file = apr_pstrndup(pool, opt_arg,
965 first_colon - opt_arg);
966 config_option->section = apr_pstrndup(pool, first_colon + 1,
967 second_colon - first_colon - 1);
968 config_option->option = apr_pstrndup(pool, second_colon + 1,
969 equals_sign - second_colon - 1);
971 warning = validate_config_option(config_option, pool);
974 svn_handle_warning2(stderr, warning, prefix);
975 svn_error_clear(warning);
978 if (! (strchr(config_option->option, ':')))
980 config_option->value = apr_pstrndup(pool, equals_sign + 1,
981 opt_arg + len - equals_sign - 1);
982 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
989 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
990 _("Invalid syntax of argument of --config-option"));
994 svn_cmdline__apply_config_options(apr_hash_t *config,
995 const apr_array_header_t *config_options,
997 const char *argument_name)
1001 for (i = 0; i < config_options->nelts; i++)
1004 svn_cmdline__config_argument_t *arg =
1005 APR_ARRAY_IDX(config_options, i,
1006 svn_cmdline__config_argument_t *);
1008 cfg = svn_hash_gets(config, arg->file);
1012 svn_config_set(cfg, arg->section, arg->option, arg->value);
1016 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1017 _("Unrecognized file in argument of %s"), argument_name);
1019 svn_handle_warning2(stderr, err, prefix);
1020 svn_error_clear(err);
1024 return SVN_NO_ERROR;
1027 /* Return a copy, allocated in POOL, of the next line of text from *STR
1028 * up to and including a CR and/or an LF. Change *STR to point to the
1029 * remainder of the string after the returned part. If there are no
1030 * characters to be returned, return NULL; never return an empty string.
1033 next_line(const char **str, apr_pool_t *pool)
1035 const char *start = *str;
1036 const char *p = *str;
1038 /* n.b. Throughout this fn, we never read any character after a '\0'. */
1039 /* Skip over all non-EOL characters, if any. */
1040 while (*p != '\r' && *p != '\n' && *p != '\0')
1042 /* Skip over \r\n or \n\r or \r or \n, if any. */
1043 if (*p == '\r' || *p == '\n')
1047 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
1051 /* Now p points after at most one '\n' and/or '\r'. */
1057 return svn_string_ncreate(start, p - start, pool)->data;
1061 svn_cmdline__indent_string(const char *str,
1065 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
1068 while ((line = next_line(&str, pool)))
1070 svn_stringbuf_appendcstr(out, indent);
1071 svn_stringbuf_appendcstr(out, line);
1077 svn_cmdline__print_prop_hash(svn_stream_t *out,
1078 apr_hash_t *prop_hash,
1079 svn_boolean_t names_only,
1082 apr_array_header_t *sorted_props;
1085 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1087 for (i = 0; i < sorted_props->nelts; i++)
1089 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1090 const char *pname = item.key;
1091 svn_string_t *propval = item.value;
1092 const char *pname_stdout;
1094 if (svn_prop_needs_translation(pname))
1095 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1098 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
1102 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
1103 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
1104 APR_EOL_STR, /* 'native' eol */
1105 FALSE, /* no repair */
1106 NULL, /* no keywords */
1107 FALSE, /* no expansion */
1110 SVN_ERR(svn_stream_puts(out, pname_stdout));
1114 /* ### We leave these printfs for now, since if propval wasn't
1115 translated above, we don't know anything about its encoding.
1116 In fact, it might be binary data... */
1117 printf(" %s\n", pname_stdout);
1122 /* Add an extra newline to the value before indenting, so that
1123 * every line of output has the indentation whether the value
1124 * already ended in a newline or not. */
1125 const char *newval = apr_psprintf(pool, "%s\n", propval->data);
1126 const char *indented_newval = svn_cmdline__indent_string(newval,
1131 SVN_ERR(svn_stream_puts(out, indented_newval));
1135 printf("%s", indented_newval);
1140 return SVN_NO_ERROR;
1144 svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr,
1145 apr_hash_t *prop_hash,
1146 svn_boolean_t names_only,
1147 svn_boolean_t inherited_props,
1150 apr_array_header_t *sorted_props;
1153 if (*outstr == NULL)
1154 *outstr = svn_stringbuf_create_empty(pool);
1156 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1158 for (i = 0; i < sorted_props->nelts; i++)
1160 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1161 const char *pname = item.key;
1162 svn_string_t *propval = item.value;
1166 svn_xml_make_open_tag(
1167 outstr, pool, svn_xml_self_closing,
1168 inherited_props ? "inherited_property" : "property",
1169 "name", pname, SVN_VA_NULL);
1173 const char *pname_out;
1175 if (svn_prop_needs_translation(pname))
1176 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1179 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
1181 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
1182 inherited_props, pool);
1186 return SVN_NO_ERROR;
1190 svn_cmdline__stdin_is_a_terminal(void)
1193 return (_isatty(STDIN_FILENO) != 0);
1195 return (isatty(STDIN_FILENO) != 0);
1200 svn_cmdline__stdout_is_a_terminal(void)
1203 return (_isatty(STDOUT_FILENO) != 0);
1205 return (isatty(STDOUT_FILENO) != 0);
1210 svn_cmdline__stderr_is_a_terminal(void)
1213 return (_isatty(STDERR_FILENO) != 0);
1215 return (isatty(STDERR_FILENO) != 0);
1220 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
1221 svn_boolean_t force_interactive)
1223 /* If neither --non-interactive nor --force-interactive was passed,
1224 * be interactive if stdin is a terminal.
1225 * If --force-interactive was passed, always be interactive. */
1226 if (!force_interactive && !non_interactive)
1228 return svn_cmdline__stdin_is_a_terminal();
1230 else if (force_interactive)
1233 return !non_interactive;
1237 /* Helper for the edit_externally functions. Set *EDITOR to some path to an
1238 editor binary. Sources to search include: the EDITOR_CMD argument
1239 (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
1240 is not NULL), $VISUAL, $EDITOR. Return
1241 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
1242 static svn_error_t *
1243 find_editor_binary(const char **editor,
1244 const char *editor_cmd,
1248 struct svn_config_t *cfg;
1250 /* Use the editor specified on the command line via --editor-cmd, if any. */
1253 /* Otherwise look for the Subversion-specific environment variable. */
1255 e = getenv("SVN_EDITOR");
1257 /* If not found then fall back on the config file. */
1260 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
1261 svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
1262 SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
1265 /* If not found yet then try general purpose environment variables. */
1267 e = getenv("VISUAL");
1269 e = getenv("EDITOR");
1271 #ifdef SVN_CLIENT_EDITOR
1272 /* If still not found then fall back on the hard-coded default. */
1274 e = SVN_CLIENT_EDITOR;
1277 /* Error if there is no editor specified */
1282 for (c = e; *c; c++)
1283 if (!svn_ctype_isspace(*c))
1287 return svn_error_create
1288 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1289 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
1290 "'editor-cmd' run-time configuration option is empty or "
1291 "consists solely of whitespace. Expected a shell command."));
1294 return svn_error_create
1295 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1296 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
1297 "set, and no 'editor-cmd' run-time configuration option was found"));
1300 return SVN_NO_ERROR;
1303 /* Wrapper around apr_pescape_shell() which also escapes whitespace. */
1305 escape_path(apr_pool_t *pool, const char *orig_path)
1307 apr_size_t len, esc_len;
1308 apr_status_t status;
1310 len = strlen(orig_path);
1313 status = apr_escape_shell(NULL, orig_path, len, &esc_len);
1315 if (status == APR_NOTFOUND)
1317 /* No special characters found by APR, so just surround it in double
1318 quotes in case there is whitespace, which APR (as of 1.6.5) doesn't
1319 consider special. */
1320 return apr_psprintf(pool, "\"%s\"", orig_path);
1326 /* Following the advice from
1327 https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
1328 1. Surround argument with double-quotes
1329 2. Escape backslashes, if they're followed by a double-quote, and double-quotes
1330 3. Escape any metacharacter, including double-quotes, with ^ */
1332 /* Use APR's buffer size as an approximation for how large the escaped
1333 string should be, plus 4 bytes for the leading/trailing ^" */
1334 svn_stringbuf_t *buf = svn_stringbuf_create_ensure(esc_len + 4, pool);
1335 svn_stringbuf_appendcstr(buf, "^\"");
1336 for (p = orig_path; *p; p++)
1338 int nr_backslash = 0;
1339 while (*p && *p == '\\')
1346 /* We've reached the end of the argument, so we need 2n backslash
1347 characters. That will be interpreted as n backslashes and the
1348 final double-quote character will be interpreted as the final
1349 string delimiter. */
1350 svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2);
1353 /* Double-quote as part of the argument means we need to double
1354 any preceeding backslashes and then add one to escape the
1356 svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2 + 1);
1357 svn_stringbuf_appendbyte(buf, '^');
1358 svn_stringbuf_appendbyte(buf, *p);
1362 /* Since there's no double-quote, we just insert any backslashes
1363 literally. No escaping needed. */
1364 svn_stringbuf_appendfill(buf, '\\', nr_backslash);
1365 if (strchr("()%!^<>&|", *p))
1366 svn_stringbuf_appendbyte(buf, '^');
1367 svn_stringbuf_appendbyte(buf, *p);
1370 svn_stringbuf_appendcstr(buf, "^\"");
1373 char *path, *p, *esc_path;
1375 /* Account for whitespace, since APR doesn't */
1376 for (p = (char *)orig_path; *p; p++)
1377 if (strchr(" \t\n\r", *p))
1380 path = apr_pcalloc(pool, esc_len);
1381 apr_escape_shell(path, orig_path, len, NULL);
1383 p = esc_path = apr_pcalloc(pool, len + esc_len + 1);
1386 if (strchr(" \t\n\r", *path))
1397 svn_cmdline__edit_file_externally(const char *path,
1398 const char *editor_cmd,
1402 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1405 apr_status_t apr_err;
1407 svn_dirent_split(&base_dir, &file_name, path, pool);
1409 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1411 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1413 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1415 /* APR doesn't like "" directories */
1416 if (base_dir[0] == '\0')
1419 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1421 apr_err = apr_filepath_set(base_dir_apr, pool);
1423 return svn_error_wrap_apr
1424 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1426 /* editor is explicitly documented as being interpreted by the user's shell,
1427 and as such should already be quoted/escaped as needed. */
1428 cmd = apr_psprintf(pool, "%s %s", editor, escape_path(pool, file_name));
1429 sys_err = system(cmd);
1431 apr_err = apr_filepath_set(old_cwd, pool);
1433 svn_handle_error2(svn_error_wrap_apr
1434 (apr_err, _("Can't restore working directory")),
1435 stderr, TRUE /* fatal */, "svn: ");
1438 /* Extracting any meaning from sys_err is platform specific, so just
1439 use the raw value. */
1440 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1441 _("system('%s') returned %d"), cmd, sys_err);
1443 return SVN_NO_ERROR;
1448 svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
1449 const char **tmpfile_left /* UTF-8! */,
1450 const char *editor_cmd,
1451 const char *base_dir /* UTF-8! */,
1452 const svn_string_t *contents /* UTF-8! */,
1453 const char *filename,
1455 svn_boolean_t as_text,
1456 const char *encoding,
1461 apr_file_t *tmp_file;
1462 const char *tmpfile_name;
1463 const char *tmpfile_native;
1464 const char *base_dir_apr;
1465 svn_string_t *translated_contents;
1466 apr_status_t apr_err;
1468 apr_finfo_t finfo_before, finfo_after;
1469 svn_error_t *err = SVN_NO_ERROR;
1472 svn_boolean_t remove_file = TRUE;
1474 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1476 /* Convert file contents from UTF-8/LF if desired. */
1479 const char *translated;
1480 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1482 NULL, FALSE, pool));
1483 translated_contents = svn_string_create_empty(pool);
1485 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1486 translated, encoding, pool));
1488 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1490 translated_contents->len = strlen(translated_contents->data);
1493 translated_contents = svn_string_dup(contents, pool);
1495 /* Move to BASE_DIR to avoid getting characters that need quoting
1496 into tmpfile_name */
1497 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1499 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1501 /* APR doesn't like "" directories */
1502 if (base_dir[0] == '\0')
1505 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1506 apr_err = apr_filepath_set(base_dir_apr, pool);
1509 return svn_error_wrap_apr
1510 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1513 /*** From here on, any problems that occur require us to cd back!! ***/
1515 /* Ask the working copy for a temporary file named FILENAME-something. */
1516 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1520 svn_io_file_del_none, pool, pool);
1522 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1524 const char *temp_dir_apr;
1526 svn_error_clear(err);
1528 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1530 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1531 apr_err = apr_filepath_set(temp_dir_apr, pool);
1534 return svn_error_wrap_apr
1535 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1538 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1542 svn_io_file_del_none, pool, pool);
1548 /*** From here on, any problems that occur require us to cleanup
1549 the file we just created!! ***/
1551 /* Dump initial CONTENTS to TMP_FILE. */
1552 err = svn_io_file_write_full(tmp_file, translated_contents->data,
1553 translated_contents->len, &written,
1556 err = svn_error_compose_create(err, svn_io_file_close(tmp_file, pool));
1558 /* Make sure the whole CONTENTS were written, else return an error. */
1562 /* Get information about the temporary file before the user has
1563 been allowed to edit its contents. */
1564 err = svn_io_stat(&finfo_before, tmpfile_name, APR_FINFO_MTIME, pool);
1568 /* Backdate the file a little bit in case the editor is very fast
1569 and doesn't change the size. (Use two seconds, since some
1570 filesystems have coarse granularity.) It's OK if this call
1571 fails, so we don't check its return value.*/
1572 err = svn_io_set_file_affected_time(finfo_before.mtime
1573 - apr_time_from_sec(2),
1574 tmpfile_name, pool);
1575 svn_error_clear(err);
1577 /* Stat it again to get the mtime we actually set. */
1578 err = svn_io_stat(&finfo_before, tmpfile_name,
1579 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1583 /* Prepare the editor command line. */
1584 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
1588 /* editor is explicitly documented as being interpreted by the user's shell,
1589 and as such should already be quoted/escaped as needed. */
1590 cmd = apr_psprintf(pool, "%s %s", editor, escape_path(pool, tmpfile_native));
1592 /* If the caller wants us to leave the file around, return the path
1593 of the file we'll use, and make a note not to destroy it. */
1596 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1597 remove_file = FALSE;
1600 /* Now, run the editor command line. */
1601 sys_err = system(cmd);
1604 /* Extracting any meaning from sys_err is platform specific, so just
1605 use the raw value. */
1606 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1607 _("system('%s') returned %d"), cmd, sys_err);
1611 /* Get information about the temporary file after the assumed editing. */
1612 err = svn_io_stat(&finfo_after, tmpfile_name,
1613 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1617 /* If the file looks changed... */
1618 if ((finfo_before.mtime != finfo_after.mtime) ||
1619 (finfo_before.size != finfo_after.size))
1621 svn_stringbuf_t *edited_contents_s;
1622 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1626 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1628 /* Translate back to UTF8/LF if desired. */
1631 err = svn_subst_translate_string2(edited_contents, NULL, NULL,
1632 *edited_contents, encoding, FALSE,
1636 err = svn_error_quick_wrap
1638 _("Error normalizing edited contents to internal format"));
1645 /* No edits seem to have been made */
1646 *edited_contents = NULL;
1652 /* Remove the file from disk. */
1653 err = svn_error_compose_create(
1655 svn_io_remove_file2(tmpfile_name, FALSE, pool));
1659 /* If we against all probability can't cd back, all further relative
1660 file references would be screwed up, so we have to abort. */
1661 apr_err = apr_filepath_set(old_cwd, pool);
1664 svn_handle_error2(svn_error_wrap_apr
1665 (apr_err, _("Can't restore working directory")),
1666 stderr, TRUE /* fatal */, "svn: ");
1669 return svn_error_trace(err);
1673 svn_cmdline__parse_trust_options(
1674 svn_boolean_t *trust_server_cert_unknown_ca,
1675 svn_boolean_t *trust_server_cert_cn_mismatch,
1676 svn_boolean_t *trust_server_cert_expired,
1677 svn_boolean_t *trust_server_cert_not_yet_valid,
1678 svn_boolean_t *trust_server_cert_other_failure,
1679 const char *opt_arg,
1680 apr_pool_t *scratch_pool)
1682 apr_array_header_t *failures;
1685 *trust_server_cert_unknown_ca = FALSE;
1686 *trust_server_cert_cn_mismatch = FALSE;
1687 *trust_server_cert_expired = FALSE;
1688 *trust_server_cert_not_yet_valid = FALSE;
1689 *trust_server_cert_other_failure = FALSE;
1691 failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool);
1693 for (i = 0; i < failures->nelts; i++)
1695 const char *value = APR_ARRAY_IDX(failures, i, const char *);
1696 if (!strcmp(value, "unknown-ca"))
1697 *trust_server_cert_unknown_ca = TRUE;
1698 else if (!strcmp(value, "cn-mismatch"))
1699 *trust_server_cert_cn_mismatch = TRUE;
1700 else if (!strcmp(value, "expired"))
1701 *trust_server_cert_expired = TRUE;
1702 else if (!strcmp(value, "not-yet-valid"))
1703 *trust_server_cert_not_yet_valid = TRUE;
1704 else if (!strcmp(value, "other"))
1705 *trust_server_cert_other_failure = TRUE;
1707 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1708 _("Unknown value '%s' for %s.\n"
1709 "Supported values: %s"),
1711 "--trust-server-cert-failures",
1712 "unknown-ca, cn-mismatch, expired, "
1713 "not-yet-valid, other");
1716 return SVN_NO_ERROR;
1719 /* Flags to see if we've been cancelled by the client or not. */
1720 static volatile sig_atomic_t cancelled = FALSE;
1721 static volatile sig_atomic_t signum_cancelled = 0;
1723 /* The signals we handle. */
1724 static int signal_map [] = {
1727 /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
1738 /* A signal handler to support cancellation. */
1740 signal_handler(int signum)
1744 apr_signal(signum, SIG_IGN);
1746 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1747 if (signal_map[i] == signum)
1749 signum_cancelled = i + 1;
1754 /* An svn_cancel_func_t callback. */
1755 static svn_error_t *
1756 check_cancel(void *baton)
1758 /* Cancel baton should be always NULL in command line client. */
1759 SVN_ERR_ASSERT(baton == NULL);
1761 return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
1763 return SVN_NO_ERROR;
1767 svn_cmdline__setup_cancellation_handler(void)
1771 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1772 apr_signal(signal_map[i], signal_handler);
1775 /* Disable SIGPIPE generation for the platforms that have it. */
1776 apr_signal(SIGPIPE, SIG_IGN);
1780 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
1781 * working with large files when compiled against an APR that doesn't have
1782 * large file support will crash the program, which is uncool. */
1783 apr_signal(SIGXFSZ, SIG_IGN);
1786 return check_cancel;
1790 svn_cmdline__disable_cancellation_handler(void)
1794 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1795 apr_signal(signal_map[i], SIG_DFL);
1799 svn_cmdline__cancellation_exit(void)
1803 if (cancelled && signum_cancelled)
1804 signum = signal_map[signum_cancelled - 1];
1808 apr_signal(signum, SIG_DFL);
1809 /* No APR support for getpid() so cannot use apr_proc_kill(). */
1810 kill(getpid(), signum);