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_general.h> /* for apr_initialize/apr_terminate */
43 #include <apr_strings.h> /* for apr_snprintf */
44 #include <apr_pools.h>
46 #include "svn_cmdline.h"
47 #include "svn_ctype.h"
49 #include "svn_dirent_uri.h"
52 #include "svn_pools.h"
53 #include "svn_error.h"
58 #include "svn_base64.h"
59 #include "svn_config.h"
60 #include "svn_sorts.h"
61 #include "svn_props.h"
62 #include "svn_subst.h"
64 #include "private/svn_cmdline_private.h"
65 #include "private/svn_utf_private.h"
66 #include "private/svn_sorts_private.h"
67 #include "private/svn_string_private.h"
69 #include "svn_private_config.h"
71 #include "win32_crashrpt.h"
73 #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400)
74 /* Before Visual Studio 2005, the C runtime didn't handle encodings for the
75 for the stdio output handling. */
76 #define CMDLINE_USE_CUSTOM_ENCODING
78 /* The stdin encoding. If null, it's the same as the native encoding. */
79 static const char *input_encoding = NULL;
81 /* The stdout encoding. If null, it's the same as the native encoding. */
82 static const char *output_encoding = NULL;
83 #elif defined(WIN32) && defined(_MSC_VER)
84 /* For now limit this code to Visual C++, as the result is highly dependent
85 on the CRT implementation */
86 #define USE_WIN32_CONSOLE_SHORTCUT
88 /* When TRUE, stdout/stderr is directly connected to a console */
89 static svn_boolean_t shortcut_stdout_to_console = FALSE;
90 static svn_boolean_t shortcut_stderr_to_console = FALSE;
95 svn_cmdline_init(const char *progname, FILE *error_stream)
100 char prefix_buf[64]; /* 64 is probably bigger than most program names */
106 /* The following makes sure that file descriptors 0 (stdin), 1
107 (stdout) and 2 (stderr) will not be "reused", because if
108 e.g. file descriptor 2 would be reused when opening a file, a
109 write to stderr would write to that file and most likely
111 if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) ||
112 (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) ||
113 (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1))
116 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
123 /* Ignore any errors encountered while attempting to change stream
124 buffering, as the streams should retain their default buffering
127 setvbuf(error_stream, NULL, _IONBF, 0);
129 setvbuf(stdout, NULL, _IOLBF, 0);
133 #ifdef CMDLINE_USE_CUSTOM_ENCODING
134 /* Initialize the input and output encodings. */
136 static char input_encoding_buffer[16];
137 static char output_encoding_buffer[16];
139 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer,
140 "CP%u", (unsigned) GetConsoleCP());
141 input_encoding = input_encoding_buffer;
143 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer,
144 "CP%u", (unsigned) GetConsoleOutputCP());
145 output_encoding = output_encoding_buffer;
147 #endif /* CMDLINE_USE_CUSTOM_ENCODING */
149 #ifdef SVN_USE_WIN32_CRASHHANDLER
150 if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
152 /* Attach (but don't load) the crash handler */
153 SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
156 /* ### This should work for VC++ 2002 (=1300) and later */
157 /* Show the abort message on STDERR instead of a dialog to allow
158 scripts (e.g. our testsuite) to continue after an abort without
159 user intervention. Allow overriding for easier debugging. */
160 if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
162 /* In release mode: Redirect abort() errors to stderr */
163 _set_error_mode(_OUT_TO_STDERR);
165 /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
166 (Ignored in release builds) */
167 _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
168 _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
169 _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
170 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
171 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
172 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
174 #endif /* _MSC_VER >= 1400 */
176 #endif /* SVN_USE_WIN32_CRASHHANDLER */
180 /* C programs default to the "C" locale. But because svn is supposed
181 to be i18n-aware, it should inherit the default locale of its
183 if (!setlocale(LC_ALL, "")
184 && !setlocale(LC_CTYPE, ""))
188 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
189 const char **env_var = &env_vars[0], *env_val = NULL;
192 env_val = getenv(*env_var);
193 if (env_val && env_val[0])
200 /* Unlikely. Can setlocale fail if no env vars are set? */
205 fprintf(error_stream,
206 "%s: warning: cannot set LC_CTYPE locale\n"
207 "%s: warning: environment variable %s is %s\n"
208 "%s: warning: please check that your locale name is correct\n",
209 progname, progname, *env_var, env_val, progname);
213 /* Initialize the APR subsystem, and register an atexit() function
214 to Uninitialize that subsystem at program exit. */
215 status = apr_initialize();
221 apr_strerror(status, buf, sizeof(buf) - 1);
222 fprintf(error_stream,
223 "%s: error: cannot initialize APR: %s\n",
229 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3);
230 prefix_buf[sizeof(prefix_buf) - 3] = '\0';
231 strcat(prefix_buf, ": ");
233 /* DSO pool must be created before any other pools used by the
234 application so that pool cleanup doesn't unload DSOs too
235 early. See docstring of svn_dso_initialize2(). */
236 if ((err = svn_dso_initialize2()))
239 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
241 svn_error_clear(err);
245 if (0 > atexit(apr_terminate))
248 fprintf(error_stream,
249 "%s: error: atexit registration failed\n",
254 /* Create a pool for use by the UTF-8 routines. It will be cleaned
255 up by APR at exit time. */
256 pool = svn_pool_create(NULL);
257 svn_utf_initialize2(FALSE, pool);
259 if ((err = svn_nls_init()))
262 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
264 svn_error_clear(err);
268 #ifdef USE_WIN32_CONSOLE_SHORTCUT
269 if (_isatty(STDOUT_FILENO))
272 HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
274 /* stdout is a char device handle, but is it the console? */
275 if (GetConsoleMode(stdout_handle, &ignored))
276 shortcut_stdout_to_console = TRUE;
278 /* Don't close stdout_handle */
280 if (_isatty(STDERR_FILENO))
283 HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
285 /* stderr is a char device handle, but is it the console? */
286 if (GetConsoleMode(stderr_handle, &ignored))
287 shortcut_stderr_to_console = TRUE;
289 /* Don't close stderr_handle */
298 svn_cmdline_cstring_from_utf8(const char **dest,
302 #ifdef CMDLINE_USE_CUSTOM_ENCODING
303 if (output_encoding != NULL)
304 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
307 return svn_utf_cstring_from_utf8(dest, src, pool);
312 svn_cmdline_cstring_from_utf8_fuzzy(const char *src,
315 return svn_utf__cstring_from_utf8_fuzzy(src, pool,
316 svn_cmdline_cstring_from_utf8);
321 svn_cmdline_cstring_to_utf8(const char **dest,
325 #ifdef CMDLINE_USE_CUSTOM_ENCODING
326 if (input_encoding != NULL)
327 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
330 return svn_utf_cstring_to_utf8(dest, src, pool);
335 svn_cmdline_path_local_style_from_utf8(const char **dest,
339 return svn_cmdline_cstring_from_utf8(dest,
340 svn_dirent_local_style(src, pool),
345 svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...)
350 /* A note about encoding issues:
351 * APR uses the execution character set, but here we give it UTF-8 strings,
352 * both the fmt argument and any other string arguments. Since apr_pvsprintf
353 * only cares about and produces ASCII characters, this works under the
354 * assumption that all supported platforms use an execution character set
355 * with ASCII as a subset.
359 message = apr_pvsprintf(pool, fmt, ap);
362 return svn_cmdline_fputs(message, stdout, pool);
366 svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...)
371 /* See svn_cmdline_printf () for a note about character encoding issues. */
374 message = apr_pvsprintf(pool, fmt, ap);
377 return svn_cmdline_fputs(message, stream, pool);
381 svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool)
386 #ifdef USE_WIN32_CONSOLE_SHORTCUT
387 /* For legacy reasons the Visual C++ runtime converts output to the console
388 from the native 'ansi' encoding, to unicode, then back to 'ansi' and then
389 onwards to the console which is implemented as unicode.
391 For operations like 'svn status -v' this may cause about 70% of the total
392 processing time, with absolutely no gain.
394 For this specific scenario this shortcut exists. It has the nice side
395 effect of allowing full unicode output to the console.
397 Note that this shortcut is not used when the output is redirected, as in
398 that case the data is put on the pipe/file after the first conversion to
399 ansi. In this case the most expensive conversion is already avoided.
401 if ((stream == stdout && shortcut_stdout_to_console)
402 || (stream == stderr && shortcut_stderr_to_console))
406 if (string[0] == '\0')
409 SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */
411 SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool));
415 if (apr_get_os_error())
417 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
425 err = svn_cmdline_cstring_from_utf8(&out, string, pool);
429 svn_error_clear(err);
430 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
433 /* On POSIX systems, errno will be set on an error in fputs, but this might
434 not be the case on other platforms. We reset errno and only
435 use it if it was set by the below fputs call. Else, we just return
439 if (fputs(out, stream) == EOF)
441 if (apr_get_os_error()) /* is errno on POSIX */
443 /* ### Issue #3014: Return a specific error for broken pipes,
444 * ### with a single element in the error chain. */
445 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
446 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
448 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
451 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
458 svn_cmdline_fflush(FILE *stream)
460 /* See comment in svn_cmdline_fputs about use of errno and stdio. */
462 if (fflush(stream) == EOF)
464 if (apr_get_os_error()) /* is errno on POSIX */
466 /* ### Issue #3014: Return a specific error for broken pipes,
467 * ### with a single element in the error chain. */
468 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
469 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
471 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
474 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
480 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
482 #ifdef CMDLINE_USE_CUSTOM_ENCODING
484 return apr_pstrdup(pool, output_encoding);
487 return SVN_APR_LOCALE_CHARSET;
491 svn_cmdline_handle_exit_error(svn_error_t *err,
496 * Don't print anything on broken pipes. The pipe was likely
497 * closed by the process at the other end. We expect that
498 * process to perform error reporting as necessary.
500 * ### This assumes that there is only one error in a chain for
501 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
502 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
503 svn_handle_error2(err, stderr, FALSE, prefix);
504 svn_error_clear(err);
506 svn_pool_destroy(pool);
510 struct trust_server_cert_non_interactive_baton {
511 svn_boolean_t trust_server_cert_unknown_ca;
512 svn_boolean_t trust_server_cert_cn_mismatch;
513 svn_boolean_t trust_server_cert_expired;
514 svn_boolean_t trust_server_cert_not_yet_valid;
515 svn_boolean_t trust_server_cert_other_failure;
518 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'.
520 Don't actually prompt. Instead, set *CRED_P to valid credentials
521 iff FAILURES is empty or may be accepted according to the flags
522 in BATON. If there are any other failure bits, then set *CRED_P
523 to null (that is, reject the cert).
525 Ignore MAY_SAVE; we don't save certs we never prompted for.
527 Ignore REALM and CERT_INFO,
529 Ignore any further films by George Lucas. */
531 trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p,
534 apr_uint32_t failures,
535 const svn_auth_ssl_server_cert_info_t
537 svn_boolean_t may_save,
540 struct trust_server_cert_non_interactive_baton *b = baton;
541 apr_uint32_t non_ignored_failures;
544 /* Mask away bits we are instructed to ignore. */
545 non_ignored_failures = failures & ~(
546 (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0)
547 | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0)
548 | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0)
549 | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0)
550 | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0)
553 /* If no failures remain, accept the certificate. */
554 if (non_ignored_failures == 0)
556 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
557 (*cred_p)->may_save = FALSE;
558 (*cred_p)->accepted_failures = failures;
565 svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab,
566 svn_boolean_t non_interactive,
567 const char *auth_username,
568 const char *auth_password,
569 const char *config_dir,
570 svn_boolean_t no_auth_cache,
571 svn_boolean_t trust_server_cert_unknown_ca,
572 svn_boolean_t trust_server_cert_cn_mismatch,
573 svn_boolean_t trust_server_cert_expired,
574 svn_boolean_t trust_server_cert_not_yet_valid,
575 svn_boolean_t trust_server_cert_other_failure,
577 svn_cancel_func_t cancel_func,
582 svn_boolean_t store_password_val = TRUE;
583 svn_boolean_t store_auth_creds_val = TRUE;
584 svn_auth_provider_object_t *provider;
585 svn_cmdline_prompt_baton2_t *pb = NULL;
587 /* The whole list of registered providers */
588 apr_array_header_t *providers;
590 /* Populate the registered providers with the platform-specific providers */
591 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
594 /* If we have a cancellation function, cram it and the stuff it
595 needs into the prompt baton. */
598 pb = apr_palloc(pool, sizeof(*pb));
599 pb->cancel_func = cancel_func;
600 pb->cancel_baton = cancel_baton;
601 pb->config_dir = config_dir;
604 if (!non_interactive)
606 /* This provider doesn't prompt the user in order to get creds;
607 it prompts the user regarding the caching of creds. */
608 svn_auth_get_simple_provider2(&provider,
609 svn_cmdline_auth_plaintext_prompt,
614 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool);
617 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
618 svn_auth_get_username_provider(&provider, pool);
619 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
621 svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
622 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
623 svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
624 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
626 if (!non_interactive)
628 /* This provider doesn't prompt the user in order to get creds;
629 it prompts the user regarding the caching of creds. */
630 svn_auth_get_ssl_client_cert_pw_file_provider2
631 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt,
636 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
639 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
641 if (!non_interactive)
643 svn_boolean_t ssl_client_cert_file_prompt;
645 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
646 SVN_CONFIG_SECTION_AUTH,
647 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
650 /* Two basic prompt providers: username/password, and just username. */
651 svn_auth_get_simple_prompt_provider(&provider,
652 svn_cmdline_auth_simple_prompt,
656 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
658 svn_auth_get_username_prompt_provider
659 (&provider, svn_cmdline_auth_username_prompt, pb,
660 2, /* retry limit */ pool);
661 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
663 /* SSL prompt providers: server-certs and client-cert-passphrases. */
664 svn_auth_get_ssl_server_trust_prompt_provider
665 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
666 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
668 svn_auth_get_ssl_client_cert_pw_prompt_provider
669 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
670 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
672 /* If configuration allows, add a provider for client-cert path
674 if (ssl_client_cert_file_prompt)
676 svn_auth_get_ssl_client_cert_prompt_provider
677 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
678 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
681 else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch ||
682 trust_server_cert_expired || trust_server_cert_not_yet_valid ||
683 trust_server_cert_other_failure)
685 struct trust_server_cert_non_interactive_baton *b;
687 b = apr_palloc(pool, sizeof(*b));
688 b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca;
689 b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch;
690 b->trust_server_cert_expired = trust_server_cert_expired;
691 b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid;
692 b->trust_server_cert_other_failure = trust_server_cert_other_failure;
694 /* Remember, only register this provider if non_interactive. */
695 svn_auth_get_ssl_server_trust_prompt_provider
696 (&provider, trust_server_cert_non_interactive, b, pool);
697 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
700 /* Build an authentication baton to give to libsvn_client. */
701 svn_auth_open(ab, providers, pool);
703 /* Place any default --username or --password credentials into the
704 auth_baton's run-time parameter hash. */
706 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
709 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
712 /* Same with the --non-interactive option. */
714 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
717 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
720 /* Determine whether storing passwords in any form is allowed.
721 * This is the deprecated location for this option, the new
722 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
723 * override the value we set here. */
724 SVN_ERR(svn_config_get_bool(cfg, &store_password_val,
725 SVN_CONFIG_SECTION_AUTH,
726 SVN_CONFIG_OPTION_STORE_PASSWORDS,
727 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS));
729 if (! store_password_val)
730 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
732 /* Determine whether we are allowed to write to the auth/ area.
733 * This is the deprecated location for this option, the new
734 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
735 * override the value we set here. */
736 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val,
737 SVN_CONFIG_SECTION_AUTH,
738 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
739 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS));
741 if (no_auth_cache || ! store_auth_creds_val)
742 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
744 #ifdef SVN_HAVE_GNOME_KEYRING
745 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC,
746 &svn_cmdline__auth_gnome_keyring_unlock_prompt);
747 #endif /* SVN_HAVE_GNOME_KEYRING */
753 svn_cmdline__getopt_init(apr_getopt_t **os,
758 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
760 return svn_error_wrap_apr(apr_err,
761 _("Error initializing command line arguments"));
767 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
768 const char* propname,
769 svn_string_t *propval,
770 svn_boolean_t inherited_prop,
773 const char *xml_safe;
774 const char *encoding = NULL;
777 *outstr = svn_stringbuf_create_empty(pool);
779 if (svn_xml_is_xml_safe(propval->data, propval->len))
781 svn_stringbuf_t *xml_esc = NULL;
782 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
783 xml_safe = xml_esc->data;
787 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
790 xml_safe = base64ed->data;
794 svn_xml_make_open_tag(
795 outstr, pool, svn_xml_protect_pcdata,
796 inherited_prop ? "inherited_property" : "property",
798 "encoding", encoding, SVN_VA_NULL);
800 svn_xml_make_open_tag(
801 outstr, pool, svn_xml_protect_pcdata,
802 inherited_prop ? "inherited_property" : "property",
803 "name", propname, SVN_VA_NULL);
805 svn_stringbuf_appendcstr(*outstr, xml_safe);
807 svn_xml_make_close_tag(
809 inherited_prop ? "inherited_property" : "property");
814 /* Return the most similar string to NEEDLE in HAYSTACK, which contains
815 * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar.
817 /* See svn_cl__similarity_check() for a more general solution. */
819 most_similar(const char *needle_cstr,
820 const char **haystack,
821 apr_size_t haystack_len,
822 apr_pool_t *scratch_pool)
824 const char *max_similar;
825 apr_size_t max_score = 0;
828 svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool);
830 svn_membuf__create(&membuf, 64, scratch_pool);
832 for (i = 0; i < haystack_len; i++)
835 svn_string_t *hay = svn_string_create(haystack[i], scratch_pool);
837 score = svn_string__similarity(needle_str, hay, &membuf, NULL);
839 /* If you update this factor, consider updating
840 * svn_cl__similarity_check(). */
841 if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3
842 && score > max_score)
845 max_similar = haystack[i];
855 /* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */
857 string_in_array(const char *needle,
858 const char **haystack,
859 apr_size_t haystack_len,
860 apr_pool_t *scratch_pool)
862 const char *next_of_kin;
864 for (i = 0; i < haystack_len; i++)
866 if (!strcmp(needle, haystack[i]))
871 next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool);
873 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
874 _("Ignoring unknown value '%s'; "
875 "did you mean '%s'?"),
876 needle, next_of_kin);
878 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
879 _("Ignoring unknown value '%s'"),
883 #include "config_keys.inc"
885 /* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are
886 * known. Warn to stderr if not. (An unknown value may be either a typo
887 * or added in a newer minor version of Subversion.) */
889 validate_config_option(svn_cmdline__config_argument_t *config_option,
890 apr_pool_t *scratch_pool)
892 svn_boolean_t arbitrary_keys = FALSE;
894 /* TODO: some day, we could also verify that OPTION is valid for SECTION;
895 i.e., forbid invalid combinations such as config:auth:diff-extensions. */
897 #define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) )
899 SVN_ERR(string_in_array(config_option->file, svn__valid_config_files,
900 ARRAYLEN(svn__valid_config_files),
902 SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections,
903 ARRAYLEN(svn__valid_config_sections),
906 /* Don't validate option names for sections such as servers[group],
907 * config[tunnels], and config[auto-props] that permit arbitrary options. */
911 for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++)
913 if (!strcmp(config_option->section, svn__empty_config_sections[i]))
914 arbitrary_keys = TRUE;
918 if (! arbitrary_keys)
919 SVN_ERR(string_in_array(config_option->option, svn__valid_config_options,
920 ARRAYLEN(svn__valid_config_options),
929 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
934 svn_cmdline__config_argument_t *config_option;
935 const char *first_colon, *second_colon, *equals_sign;
936 apr_size_t len = strlen(opt_arg);
937 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
939 if ((second_colon = strchr(first_colon + 1, ':')) &&
940 (second_colon != first_colon + 1))
942 if ((equals_sign = strchr(second_colon + 1, '=')) &&
943 (equals_sign != second_colon + 1))
945 svn_error_t *warning;
947 config_option = apr_pcalloc(pool, sizeof(*config_option));
948 config_option->file = apr_pstrndup(pool, opt_arg,
949 first_colon - opt_arg);
950 config_option->section = apr_pstrndup(pool, first_colon + 1,
951 second_colon - first_colon - 1);
952 config_option->option = apr_pstrndup(pool, second_colon + 1,
953 equals_sign - second_colon - 1);
955 warning = validate_config_option(config_option, pool);
958 svn_handle_warning2(stderr, warning, prefix);
959 svn_error_clear(warning);
962 if (! (strchr(config_option->option, ':')))
964 config_option->value = apr_pstrndup(pool, equals_sign + 1,
965 opt_arg + len - equals_sign - 1);
966 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
973 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
974 _("Invalid syntax of argument of --config-option"));
978 svn_cmdline__apply_config_options(apr_hash_t *config,
979 const apr_array_header_t *config_options,
981 const char *argument_name)
985 for (i = 0; i < config_options->nelts; i++)
988 svn_cmdline__config_argument_t *arg =
989 APR_ARRAY_IDX(config_options, i,
990 svn_cmdline__config_argument_t *);
992 cfg = svn_hash_gets(config, arg->file);
996 svn_config_set(cfg, arg->section, arg->option, arg->value);
1000 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1001 _("Unrecognized file in argument of %s"), argument_name);
1003 svn_handle_warning2(stderr, err, prefix);
1004 svn_error_clear(err);
1008 return SVN_NO_ERROR;
1011 /* Return a copy, allocated in POOL, of the next line of text from *STR
1012 * up to and including a CR and/or an LF. Change *STR to point to the
1013 * remainder of the string after the returned part. If there are no
1014 * characters to be returned, return NULL; never return an empty string.
1017 next_line(const char **str, apr_pool_t *pool)
1019 const char *start = *str;
1020 const char *p = *str;
1022 /* n.b. Throughout this fn, we never read any character after a '\0'. */
1023 /* Skip over all non-EOL characters, if any. */
1024 while (*p != '\r' && *p != '\n' && *p != '\0')
1026 /* Skip over \r\n or \n\r or \r or \n, if any. */
1027 if (*p == '\r' || *p == '\n')
1031 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
1035 /* Now p points after at most one '\n' and/or '\r'. */
1041 return svn_string_ncreate(start, p - start, pool)->data;
1045 svn_cmdline__indent_string(const char *str,
1049 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
1052 while ((line = next_line(&str, pool)))
1054 svn_stringbuf_appendcstr(out, indent);
1055 svn_stringbuf_appendcstr(out, line);
1061 svn_cmdline__print_prop_hash(svn_stream_t *out,
1062 apr_hash_t *prop_hash,
1063 svn_boolean_t names_only,
1066 apr_array_header_t *sorted_props;
1069 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1071 for (i = 0; i < sorted_props->nelts; i++)
1073 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1074 const char *pname = item.key;
1075 svn_string_t *propval = item.value;
1076 const char *pname_stdout;
1078 if (svn_prop_needs_translation(pname))
1079 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1082 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
1086 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
1087 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
1088 APR_EOL_STR, /* 'native' eol */
1089 FALSE, /* no repair */
1090 NULL, /* no keywords */
1091 FALSE, /* no expansion */
1094 SVN_ERR(svn_stream_puts(out, pname_stdout));
1098 /* ### We leave these printfs for now, since if propval wasn't
1099 translated above, we don't know anything about its encoding.
1100 In fact, it might be binary data... */
1101 printf(" %s\n", pname_stdout);
1106 /* Add an extra newline to the value before indenting, so that
1107 * every line of output has the indentation whether the value
1108 * already ended in a newline or not. */
1109 const char *newval = apr_psprintf(pool, "%s\n", propval->data);
1110 const char *indented_newval = svn_cmdline__indent_string(newval,
1115 SVN_ERR(svn_stream_puts(out, indented_newval));
1119 printf("%s", indented_newval);
1124 return SVN_NO_ERROR;
1128 svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr,
1129 apr_hash_t *prop_hash,
1130 svn_boolean_t names_only,
1131 svn_boolean_t inherited_props,
1134 apr_array_header_t *sorted_props;
1137 if (*outstr == NULL)
1138 *outstr = svn_stringbuf_create_empty(pool);
1140 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1142 for (i = 0; i < sorted_props->nelts; i++)
1144 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1145 const char *pname = item.key;
1146 svn_string_t *propval = item.value;
1150 svn_xml_make_open_tag(
1151 outstr, pool, svn_xml_self_closing,
1152 inherited_props ? "inherited_property" : "property",
1153 "name", pname, SVN_VA_NULL);
1157 const char *pname_out;
1159 if (svn_prop_needs_translation(pname))
1160 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1163 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
1165 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
1166 inherited_props, pool);
1170 return SVN_NO_ERROR;
1174 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
1175 svn_boolean_t force_interactive)
1177 /* If neither --non-interactive nor --force-interactive was passed,
1178 * be interactive if stdin is a terminal.
1179 * If --force-interactive was passed, always be interactive. */
1180 if (!force_interactive && !non_interactive)
1183 return (_isatty(STDIN_FILENO) != 0);
1185 return (isatty(STDIN_FILENO) != 0);
1188 else if (force_interactive)
1191 return !non_interactive;
1195 /* Helper for the next two functions. Set *EDITOR to some path to an
1196 editor binary. Sources to search include: the EDITOR_CMD argument
1197 (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
1198 is not NULL), $VISUAL, $EDITOR. Return
1199 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
1200 static svn_error_t *
1201 find_editor_binary(const char **editor,
1202 const char *editor_cmd,
1206 struct svn_config_t *cfg;
1208 /* Use the editor specified on the command line via --editor-cmd, if any. */
1211 /* Otherwise look for the Subversion-specific environment variable. */
1213 e = getenv("SVN_EDITOR");
1215 /* If not found then fall back on the config file. */
1218 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
1219 svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
1220 SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
1223 /* If not found yet then try general purpose environment variables. */
1225 e = getenv("VISUAL");
1227 e = getenv("EDITOR");
1229 #ifdef SVN_CLIENT_EDITOR
1230 /* If still not found then fall back on the hard-coded default. */
1232 e = SVN_CLIENT_EDITOR;
1235 /* Error if there is no editor specified */
1240 for (c = e; *c; c++)
1241 if (!svn_ctype_isspace(*c))
1245 return svn_error_create
1246 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1247 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
1248 "'editor-cmd' run-time configuration option is empty or "
1249 "consists solely of whitespace. Expected a shell command."));
1252 return svn_error_create
1253 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1254 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
1255 "set, and no 'editor-cmd' run-time configuration option was found"));
1258 return SVN_NO_ERROR;
1263 svn_cmdline__edit_file_externally(const char *path,
1264 const char *editor_cmd,
1268 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1271 apr_status_t apr_err;
1273 svn_dirent_split(&base_dir, &file_name, path, pool);
1275 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1277 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1279 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1281 /* APR doesn't like "" directories */
1282 if (base_dir[0] == '\0')
1285 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1287 apr_err = apr_filepath_set(base_dir_apr, pool);
1289 return svn_error_wrap_apr
1290 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1292 cmd = apr_psprintf(pool, "%s %s", editor, file_name);
1293 sys_err = system(cmd);
1295 apr_err = apr_filepath_set(old_cwd, pool);
1297 svn_handle_error2(svn_error_wrap_apr
1298 (apr_err, _("Can't restore working directory")),
1299 stderr, TRUE /* fatal */, "svn: ");
1302 /* Extracting any meaning from sys_err is platform specific, so just
1303 use the raw value. */
1304 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1305 _("system('%s') returned %d"), cmd, sys_err);
1307 return SVN_NO_ERROR;
1312 svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
1313 const char **tmpfile_left /* UTF-8! */,
1314 const char *editor_cmd,
1315 const char *base_dir /* UTF-8! */,
1316 const svn_string_t *contents /* UTF-8! */,
1317 const char *filename,
1319 svn_boolean_t as_text,
1320 const char *encoding,
1325 apr_file_t *tmp_file;
1326 const char *tmpfile_name;
1327 const char *tmpfile_native;
1328 const char *tmpfile_apr, *base_dir_apr;
1329 svn_string_t *translated_contents;
1330 apr_status_t apr_err, apr_err2;
1332 apr_finfo_t finfo_before, finfo_after;
1333 svn_error_t *err = SVN_NO_ERROR, *err2;
1336 svn_boolean_t remove_file = TRUE;
1338 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1340 /* Convert file contents from UTF-8/LF if desired. */
1343 const char *translated;
1344 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1346 NULL, FALSE, pool));
1347 translated_contents = svn_string_create_empty(pool);
1349 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1350 translated, encoding, pool));
1352 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1354 translated_contents->len = strlen(translated_contents->data);
1357 translated_contents = svn_string_dup(contents, pool);
1359 /* Move to BASE_DIR to avoid getting characters that need quoting
1360 into tmpfile_name */
1361 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1363 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1365 /* APR doesn't like "" directories */
1366 if (base_dir[0] == '\0')
1369 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1370 apr_err = apr_filepath_set(base_dir_apr, pool);
1373 return svn_error_wrap_apr
1374 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1377 /*** From here on, any problems that occur require us to cd back!! ***/
1379 /* Ask the working copy for a temporary file named FILENAME-something. */
1380 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1384 svn_io_file_del_none, pool, pool);
1386 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1388 const char *temp_dir_apr;
1390 svn_error_clear(err);
1392 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1394 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1395 apr_err = apr_filepath_set(temp_dir_apr, pool);
1398 return svn_error_wrap_apr
1399 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1402 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1406 svn_io_file_del_none, pool, pool);
1412 /*** From here on, any problems that occur require us to cleanup
1413 the file we just created!! ***/
1415 /* Dump initial CONTENTS to TMP_FILE. */
1416 apr_err = apr_file_write_full(tmp_file, translated_contents->data,
1417 translated_contents->len, &written);
1419 apr_err2 = apr_file_close(tmp_file);
1423 /* Make sure the whole CONTENTS were written, else return an error. */
1426 err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
1431 err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
1435 /* Get information about the temporary file before the user has
1436 been allowed to edit its contents. */
1437 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1438 APR_FINFO_MTIME, pool);
1441 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1445 /* Backdate the file a little bit in case the editor is very fast
1446 and doesn't change the size. (Use two seconds, since some
1447 filesystems have coarse granularity.) It's OK if this call
1448 fails, so we don't check its return value.*/
1449 apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool);
1451 /* Stat it again to get the mtime we actually set. */
1452 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1453 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1456 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1460 /* Prepare the editor command line. */
1461 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
1464 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
1466 /* If the caller wants us to leave the file around, return the path
1467 of the file we'll use, and make a note not to destroy it. */
1470 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1471 remove_file = FALSE;
1474 /* Now, run the editor command line. */
1475 sys_err = system(cmd);
1478 /* Extracting any meaning from sys_err is platform specific, so just
1479 use the raw value. */
1480 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1481 _("system('%s') returned %d"), cmd, sys_err);
1485 /* Get information about the temporary file after the assumed editing. */
1486 apr_err = apr_stat(&finfo_after, tmpfile_apr,
1487 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1490 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1494 /* If the file looks changed... */
1495 if ((finfo_before.mtime != finfo_after.mtime) ||
1496 (finfo_before.size != finfo_after.size))
1498 svn_stringbuf_t *edited_contents_s;
1499 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1503 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1505 /* Translate back to UTF8/LF if desired. */
1508 err = svn_subst_translate_string2(edited_contents, NULL, NULL,
1509 *edited_contents, encoding, FALSE,
1513 err = svn_error_quick_wrap
1515 _("Error normalizing edited contents to internal format"));
1522 /* No edits seem to have been made */
1523 *edited_contents = NULL;
1529 /* Remove the file from disk. */
1530 err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
1532 /* Only report remove error if there was no previous error. */
1536 svn_error_clear(err2);
1540 /* If we against all probability can't cd back, all further relative
1541 file references would be screwed up, so we have to abort. */
1542 apr_err = apr_filepath_set(old_cwd, pool);
1545 svn_handle_error2(svn_error_wrap_apr
1546 (apr_err, _("Can't restore working directory")),
1547 stderr, TRUE /* fatal */, "svn: ");
1550 return svn_error_trace(err);
1554 svn_cmdline__parse_trust_options(
1555 svn_boolean_t *trust_server_cert_unknown_ca,
1556 svn_boolean_t *trust_server_cert_cn_mismatch,
1557 svn_boolean_t *trust_server_cert_expired,
1558 svn_boolean_t *trust_server_cert_not_yet_valid,
1559 svn_boolean_t *trust_server_cert_other_failure,
1560 const char *opt_arg,
1561 apr_pool_t *scratch_pool)
1563 apr_array_header_t *failures;
1566 *trust_server_cert_unknown_ca = FALSE;
1567 *trust_server_cert_cn_mismatch = FALSE;
1568 *trust_server_cert_expired = FALSE;
1569 *trust_server_cert_not_yet_valid = FALSE;
1570 *trust_server_cert_other_failure = FALSE;
1572 failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool);
1574 for (i = 0; i < failures->nelts; i++)
1576 const char *value = APR_ARRAY_IDX(failures, i, const char *);
1577 if (!strcmp(value, "unknown-ca"))
1578 *trust_server_cert_unknown_ca = TRUE;
1579 else if (!strcmp(value, "cn-mismatch"))
1580 *trust_server_cert_cn_mismatch = TRUE;
1581 else if (!strcmp(value, "expired"))
1582 *trust_server_cert_expired = TRUE;
1583 else if (!strcmp(value, "not-yet-valid"))
1584 *trust_server_cert_not_yet_valid = TRUE;
1585 else if (!strcmp(value, "other"))
1586 *trust_server_cert_other_failure = TRUE;
1588 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1589 _("Unknown value '%s' for %s.\n"
1590 "Supported values: %s"),
1592 "--trust-server-cert-failures",
1593 "unknown-ca, cn-mismatch, expired, "
1594 "not-yet-valid, other");
1597 return SVN_NO_ERROR;