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>
39 #include <apr.h> /* for STDIN_FILENO */
40 #include <apr_errno.h> /* for apr_strerror */
41 #include <apr_general.h> /* for apr_initialize/apr_terminate */
42 #include <apr_strings.h> /* for apr_snprintf */
43 #include <apr_pools.h>
45 #include "svn_cmdline.h"
46 #include "svn_ctype.h"
48 #include "svn_dirent_uri.h"
51 #include "svn_pools.h"
52 #include "svn_error.h"
57 #include "svn_base64.h"
58 #include "svn_config.h"
59 #include "svn_sorts.h"
60 #include "svn_props.h"
61 #include "svn_subst.h"
63 #include "private/svn_cmdline_private.h"
64 #include "private/svn_utf_private.h"
65 #include "private/svn_string_private.h"
67 #include "svn_private_config.h"
69 #include "win32_crashrpt.h"
71 /* The stdin encoding. If null, it's the same as the native encoding. */
72 static const char *input_encoding = NULL;
74 /* The stdout encoding. If null, it's the same as the native encoding. */
75 static const char *output_encoding = NULL;
79 svn_cmdline_init(const char *progname, FILE *error_stream)
84 char prefix_buf[64]; /* 64 is probably bigger than most program names */
90 /* The following makes sure that file descriptors 0 (stdin), 1
91 (stdout) and 2 (stderr) will not be "reused", because if
92 e.g. file descriptor 2 would be reused when opening a file, a
93 write to stderr would write to that file and most likely
95 if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) ||
96 (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) ||
97 (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1))
100 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
107 /* Ignore any errors encountered while attempting to change stream
108 buffering, as the streams should retain their default buffering
111 setvbuf(error_stream, NULL, _IONBF, 0);
113 setvbuf(stdout, NULL, _IOLBF, 0);
118 /* Initialize the input and output encodings. */
120 static char input_encoding_buffer[16];
121 static char output_encoding_buffer[16];
123 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer,
124 "CP%u", (unsigned) GetConsoleCP());
125 input_encoding = input_encoding_buffer;
127 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer,
128 "CP%u", (unsigned) GetConsoleOutputCP());
129 output_encoding = output_encoding_buffer;
131 #endif /* _MSC_VER < 1400 */
133 #ifdef SVN_USE_WIN32_CRASHHANDLER
134 /* Attach (but don't load) the crash handler */
135 SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
138 /* ### This should work for VC++ 2002 (=1300) and later */
139 /* Show the abort message on STDERR instead of a dialog to allow
140 scripts (e.g. our testsuite) to continue after an abort without
141 user intervention. Allow overriding for easier debugging. */
142 if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
144 /* In release mode: Redirect abort() errors to stderr */
145 _set_error_mode(_OUT_TO_STDERR);
147 /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
148 (Ignored in release builds) */
149 _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
150 _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
151 _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
152 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
153 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
154 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
156 #endif /* _MSC_VER >= 1400 */
158 #endif /* SVN_USE_WIN32_CRASHHANDLER */
162 /* C programs default to the "C" locale. But because svn is supposed
163 to be i18n-aware, it should inherit the default locale of its
165 if (!setlocale(LC_ALL, "")
166 && !setlocale(LC_CTYPE, ""))
170 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
171 const char **env_var = &env_vars[0], *env_val = NULL;
174 env_val = getenv(*env_var);
175 if (env_val && env_val[0])
182 /* Unlikely. Can setlocale fail if no env vars are set? */
187 fprintf(error_stream,
188 "%s: warning: cannot set LC_CTYPE locale\n"
189 "%s: warning: environment variable %s is %s\n"
190 "%s: warning: please check that your locale name is correct\n",
191 progname, progname, *env_var, env_val, progname);
195 /* Initialize the APR subsystem, and register an atexit() function
196 to Uninitialize that subsystem at program exit. */
197 status = apr_initialize();
203 apr_strerror(status, buf, sizeof(buf) - 1);
204 fprintf(error_stream,
205 "%s: error: cannot initialize APR: %s\n",
211 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3);
212 prefix_buf[sizeof(prefix_buf) - 3] = '\0';
213 strcat(prefix_buf, ": ");
215 /* DSO pool must be created before any other pools used by the
216 application so that pool cleanup doesn't unload DSOs too
217 early. See docstring of svn_dso_initialize2(). */
218 if ((err = svn_dso_initialize2()))
221 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
223 svn_error_clear(err);
227 if (0 > atexit(apr_terminate))
230 fprintf(error_stream,
231 "%s: error: atexit registration failed\n",
236 /* Create a pool for use by the UTF-8 routines. It will be cleaned
237 up by APR at exit time. */
238 pool = svn_pool_create(NULL);
239 svn_utf_initialize2(FALSE, pool);
241 if ((err = svn_nls_init()))
244 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
246 svn_error_clear(err);
255 svn_cmdline_cstring_from_utf8(const char **dest,
259 if (output_encoding == NULL)
260 return svn_utf_cstring_from_utf8(dest, src, pool);
262 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
267 svn_cmdline_cstring_from_utf8_fuzzy(const char *src,
270 return svn_utf__cstring_from_utf8_fuzzy(src, pool,
271 svn_cmdline_cstring_from_utf8);
276 svn_cmdline_cstring_to_utf8(const char **dest,
280 if (input_encoding == NULL)
281 return svn_utf_cstring_to_utf8(dest, src, pool);
283 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
288 svn_cmdline_path_local_style_from_utf8(const char **dest,
292 return svn_cmdline_cstring_from_utf8(dest,
293 svn_dirent_local_style(src, pool),
298 svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...)
303 /* A note about encoding issues:
304 * APR uses the execution character set, but here we give it UTF-8 strings,
305 * both the fmt argument and any other string arguments. Since apr_pvsprintf
306 * only cares about and produces ASCII characters, this works under the
307 * assumption that all supported platforms use an execution character set
308 * with ASCII as a subset.
312 message = apr_pvsprintf(pool, fmt, ap);
315 return svn_cmdline_fputs(message, stdout, pool);
319 svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...)
324 /* See svn_cmdline_printf () for a note about character encoding issues. */
327 message = apr_pvsprintf(pool, fmt, ap);
330 return svn_cmdline_fputs(message, stream, pool);
334 svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool)
339 err = svn_cmdline_cstring_from_utf8(&out, string, pool);
343 svn_error_clear(err);
344 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
347 /* On POSIX systems, errno will be set on an error in fputs, but this might
348 not be the case on other platforms. We reset errno and only
349 use it if it was set by the below fputs call. Else, we just return
353 if (fputs(out, stream) == EOF)
355 if (apr_get_os_error()) /* is errno on POSIX */
357 /* ### Issue #3014: Return a specific error for broken pipes,
358 * ### with a single element in the error chain. */
359 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
360 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
362 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
365 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
372 svn_cmdline_fflush(FILE *stream)
374 /* See comment in svn_cmdline_fputs about use of errno and stdio. */
376 if (fflush(stream) == EOF)
378 if (apr_get_os_error()) /* is errno on POSIX */
380 /* ### Issue #3014: Return a specific error for broken pipes,
381 * ### with a single element in the error chain. */
382 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
383 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
385 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
388 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
394 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
397 return apr_pstrdup(pool, output_encoding);
399 return SVN_APR_LOCALE_CHARSET;
403 svn_cmdline_handle_exit_error(svn_error_t *err,
408 * Don't print anything on broken pipes. The pipe was likely
409 * closed by the process at the other end. We expect that
410 * process to perform error reporting as necessary.
412 * ### This assumes that there is only one error in a chain for
413 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
414 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
415 svn_handle_error2(err, stderr, FALSE, prefix);
416 svn_error_clear(err);
418 svn_pool_destroy(pool);
422 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'.
424 Don't actually prompt. Instead, set *CRED_P to valid credentials
425 iff FAILURES is empty or is exactly SVN_AUTH_SSL_UNKNOWNCA. If
426 there are any other failure bits, then set *CRED_P to null (that
427 is, reject the cert).
429 Ignore MAY_SAVE; we don't save certs we never prompted for.
431 Ignore BATON, REALM, and CERT_INFO,
433 Ignore any further films by George Lucas. */
435 ssl_trust_unknown_server_cert
436 (svn_auth_cred_ssl_server_trust_t **cred_p,
439 apr_uint32_t failures,
440 const svn_auth_ssl_server_cert_info_t *cert_info,
441 svn_boolean_t may_save,
446 if (failures == 0 || failures == SVN_AUTH_SSL_UNKNOWNCA)
448 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
449 (*cred_p)->may_save = FALSE;
450 (*cred_p)->accepted_failures = failures;
457 svn_cmdline_create_auth_baton(svn_auth_baton_t **ab,
458 svn_boolean_t non_interactive,
459 const char *auth_username,
460 const char *auth_password,
461 const char *config_dir,
462 svn_boolean_t no_auth_cache,
463 svn_boolean_t trust_server_cert,
465 svn_cancel_func_t cancel_func,
469 svn_boolean_t store_password_val = TRUE;
470 svn_boolean_t store_auth_creds_val = TRUE;
471 svn_auth_provider_object_t *provider;
472 svn_cmdline_prompt_baton2_t *pb = NULL;
474 /* The whole list of registered providers */
475 apr_array_header_t *providers;
477 /* Populate the registered providers with the platform-specific providers */
478 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
481 /* If we have a cancellation function, cram it and the stuff it
482 needs into the prompt baton. */
485 pb = apr_palloc(pool, sizeof(*pb));
486 pb->cancel_func = cancel_func;
487 pb->cancel_baton = cancel_baton;
488 pb->config_dir = config_dir;
491 if (!non_interactive)
493 /* This provider doesn't prompt the user in order to get creds;
494 it prompts the user regarding the caching of creds. */
495 svn_auth_get_simple_provider2(&provider,
496 svn_cmdline_auth_plaintext_prompt,
501 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool);
504 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
505 svn_auth_get_username_provider(&provider, pool);
506 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
508 /* The windows ssl server certificate CRYPTOAPI provider. */
509 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
515 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
517 /* The windows ssl authority certificate CRYPTOAPI provider. */
518 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
520 "ssl_server_authority",
524 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
526 svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
527 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
528 svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
529 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
531 if (!non_interactive)
533 /* This provider doesn't prompt the user in order to get creds;
534 it prompts the user regarding the caching of creds. */
535 svn_auth_get_ssl_client_cert_pw_file_provider2
536 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt,
541 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
544 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
546 if (!non_interactive)
548 svn_boolean_t ssl_client_cert_file_prompt;
550 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
551 SVN_CONFIG_SECTION_AUTH,
552 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
555 /* Two basic prompt providers: username/password, and just username. */
556 svn_auth_get_simple_prompt_provider(&provider,
557 svn_cmdline_auth_simple_prompt,
561 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
563 svn_auth_get_username_prompt_provider
564 (&provider, svn_cmdline_auth_username_prompt, pb,
565 2, /* retry limit */ pool);
566 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
568 /* SSL prompt providers: server-certs and client-cert-passphrases. */
569 svn_auth_get_ssl_server_trust_prompt_provider
570 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
571 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
573 svn_auth_get_ssl_client_cert_pw_prompt_provider
574 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
575 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
577 /* If configuration allows, add a provider for client-cert path
579 if (ssl_client_cert_file_prompt)
581 svn_auth_get_ssl_client_cert_prompt_provider
582 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
583 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
586 else if (trust_server_cert)
588 /* Remember, only register this provider if non_interactive. */
589 svn_auth_get_ssl_server_trust_prompt_provider
590 (&provider, ssl_trust_unknown_server_cert, NULL, pool);
591 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
594 /* Build an authentication baton to give to libsvn_client. */
595 svn_auth_open(ab, providers, pool);
597 /* Place any default --username or --password credentials into the
598 auth_baton's run-time parameter hash. */
600 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
603 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
606 /* Same with the --non-interactive option. */
608 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
611 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
614 /* Determine whether storing passwords in any form is allowed.
615 * This is the deprecated location for this option, the new
616 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
617 * override the value we set here. */
618 SVN_ERR(svn_config_get_bool(cfg, &store_password_val,
619 SVN_CONFIG_SECTION_AUTH,
620 SVN_CONFIG_OPTION_STORE_PASSWORDS,
621 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS));
623 if (! store_password_val)
624 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
626 /* Determine whether we are allowed to write to the auth/ area.
627 * This is the deprecated location for this option, the new
628 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
629 * override the value we set here. */
630 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val,
631 SVN_CONFIG_SECTION_AUTH,
632 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
633 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS));
635 if (no_auth_cache || ! store_auth_creds_val)
636 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
638 #ifdef SVN_HAVE_GNOME_KEYRING
639 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC,
640 &svn_cmdline__auth_gnome_keyring_unlock_prompt);
641 #endif /* SVN_HAVE_GNOME_KEYRING */
647 svn_cmdline__getopt_init(apr_getopt_t **os,
652 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
654 return svn_error_wrap_apr(apr_err,
655 _("Error initializing command line arguments"));
661 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
662 const char* propname,
663 svn_string_t *propval,
664 svn_boolean_t inherited_prop,
667 const char *xml_safe;
668 const char *encoding = NULL;
671 *outstr = svn_stringbuf_create_empty(pool);
673 if (svn_xml_is_xml_safe(propval->data, propval->len))
675 svn_stringbuf_t *xml_esc = NULL;
676 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
677 xml_safe = xml_esc->data;
681 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
684 xml_safe = base64ed->data;
688 svn_xml_make_open_tag(
689 outstr, pool, svn_xml_protect_pcdata,
690 inherited_prop ? "inherited_property" : "property",
692 "encoding", encoding, NULL);
694 svn_xml_make_open_tag(
695 outstr, pool, svn_xml_protect_pcdata,
696 inherited_prop ? "inherited_property" : "property",
697 "name", propname, NULL);
699 svn_stringbuf_appendcstr(*outstr, xml_safe);
701 svn_xml_make_close_tag(
703 inherited_prop ? "inherited_property" : "property");
709 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
713 svn_cmdline__config_argument_t *config_option;
714 const char *first_colon, *second_colon, *equals_sign;
715 apr_size_t len = strlen(opt_arg);
716 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
718 if ((second_colon = strchr(first_colon + 1, ':')) &&
719 (second_colon != first_colon + 1))
721 if ((equals_sign = strchr(second_colon + 1, '=')) &&
722 (equals_sign != second_colon + 1))
724 config_option = apr_pcalloc(pool, sizeof(*config_option));
725 config_option->file = apr_pstrndup(pool, opt_arg,
726 first_colon - opt_arg);
727 config_option->section = apr_pstrndup(pool, first_colon + 1,
728 second_colon - first_colon - 1);
729 config_option->option = apr_pstrndup(pool, second_colon + 1,
730 equals_sign - second_colon - 1);
732 if (! (strchr(config_option->option, ':')))
734 config_option->value = apr_pstrndup(pool, equals_sign + 1,
735 opt_arg + len - equals_sign - 1);
736 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
743 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
744 _("Invalid syntax of argument of --config-option"));
748 svn_cmdline__apply_config_options(apr_hash_t *config,
749 const apr_array_header_t *config_options,
751 const char *argument_name)
755 for (i = 0; i < config_options->nelts; i++)
758 svn_cmdline__config_argument_t *arg =
759 APR_ARRAY_IDX(config_options, i,
760 svn_cmdline__config_argument_t *);
762 cfg = svn_hash_gets(config, arg->file);
766 svn_config_set(cfg, arg->section, arg->option, arg->value);
770 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
771 _("Unrecognized file in argument of %s"), argument_name);
773 svn_handle_warning2(stderr, err, prefix);
774 svn_error_clear(err);
781 /* Return a copy, allocated in POOL, of the next line of text from *STR
782 * up to and including a CR and/or an LF. Change *STR to point to the
783 * remainder of the string after the returned part. If there are no
784 * characters to be returned, return NULL; never return an empty string.
787 next_line(const char **str, apr_pool_t *pool)
789 const char *start = *str;
790 const char *p = *str;
792 /* n.b. Throughout this fn, we never read any character after a '\0'. */
793 /* Skip over all non-EOL characters, if any. */
794 while (*p != '\r' && *p != '\n' && *p != '\0')
796 /* Skip over \r\n or \n\r or \r or \n, if any. */
797 if (*p == '\r' || *p == '\n')
801 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
805 /* Now p points after at most one '\n' and/or '\r'. */
811 return svn_string_ncreate(start, p - start, pool)->data;
815 svn_cmdline__indent_string(const char *str,
819 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
822 while ((line = next_line(&str, pool)))
824 svn_stringbuf_appendcstr(out, indent);
825 svn_stringbuf_appendcstr(out, line);
831 svn_cmdline__print_prop_hash(svn_stream_t *out,
832 apr_hash_t *prop_hash,
833 svn_boolean_t names_only,
836 apr_array_header_t *sorted_props;
839 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
841 for (i = 0; i < sorted_props->nelts; i++)
843 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
844 const char *pname = item.key;
845 svn_string_t *propval = item.value;
846 const char *pname_stdout;
848 if (svn_prop_needs_translation(pname))
849 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
852 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
856 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
857 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
858 APR_EOL_STR, /* 'native' eol */
859 FALSE, /* no repair */
860 NULL, /* no keywords */
861 FALSE, /* no expansion */
864 SVN_ERR(svn_stream_puts(out, pname_stdout));
868 /* ### We leave these printfs for now, since if propval wasn't
869 translated above, we don't know anything about its encoding.
870 In fact, it might be binary data... */
871 printf(" %s\n", pname_stdout);
876 /* Add an extra newline to the value before indenting, so that
877 * every line of output has the indentation whether the value
878 * already ended in a newline or not. */
879 const char *newval = apr_psprintf(pool, "%s\n", propval->data);
880 const char *indented_newval = svn_cmdline__indent_string(newval,
885 SVN_ERR(svn_stream_puts(out, indented_newval));
889 printf("%s", indented_newval);
898 svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr,
899 apr_hash_t *prop_hash,
900 svn_boolean_t names_only,
901 svn_boolean_t inherited_props,
904 apr_array_header_t *sorted_props;
908 *outstr = svn_stringbuf_create_empty(pool);
910 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
912 for (i = 0; i < sorted_props->nelts; i++)
914 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
915 const char *pname = item.key;
916 svn_string_t *propval = item.value;
920 svn_xml_make_open_tag(
921 outstr, pool, svn_xml_self_closing,
922 inherited_props ? "inherited_property" : "property",
923 "name", pname, NULL);
927 const char *pname_out;
929 if (svn_prop_needs_translation(pname))
930 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
933 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
935 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
936 inherited_props, pool);
944 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
945 svn_boolean_t force_interactive)
947 /* If neither --non-interactive nor --force-interactive was passed,
948 * be interactive if stdin is a terminal.
949 * If --force-interactive was passed, always be interactive. */
950 if (!force_interactive && !non_interactive)
953 return (_isatty(STDIN_FILENO) != 0);
955 return (isatty(STDIN_FILENO) != 0);
958 else if (force_interactive)
961 return !non_interactive;
965 /* Helper for the next two functions. Set *EDITOR to some path to an
966 editor binary. Sources to search include: the EDITOR_CMD argument
967 (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
968 is not NULL), $VISUAL, $EDITOR. Return
969 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
971 find_editor_binary(const char **editor,
972 const char *editor_cmd,
976 struct svn_config_t *cfg;
978 /* Use the editor specified on the command line via --editor-cmd, if any. */
981 /* Otherwise look for the Subversion-specific environment variable. */
983 e = getenv("SVN_EDITOR");
985 /* If not found then fall back on the config file. */
988 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
989 svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
990 SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
993 /* If not found yet then try general purpose environment variables. */
995 e = getenv("VISUAL");
997 e = getenv("EDITOR");
999 #ifdef SVN_CLIENT_EDITOR
1000 /* If still not found then fall back on the hard-coded default. */
1002 e = SVN_CLIENT_EDITOR;
1005 /* Error if there is no editor specified */
1010 for (c = e; *c; c++)
1011 if (!svn_ctype_isspace(*c))
1015 return svn_error_create
1016 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1017 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
1018 "'editor-cmd' run-time configuration option is empty or "
1019 "consists solely of whitespace. Expected a shell command."));
1022 return svn_error_create
1023 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1024 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
1025 "set, and no 'editor-cmd' run-time configuration option was found"));
1028 return SVN_NO_ERROR;
1033 svn_cmdline__edit_file_externally(const char *path,
1034 const char *editor_cmd,
1038 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1041 apr_status_t apr_err;
1043 svn_dirent_split(&base_dir, &file_name, path, pool);
1045 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1047 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1049 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1051 /* APR doesn't like "" directories */
1052 if (base_dir[0] == '\0')
1055 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1057 apr_err = apr_filepath_set(base_dir_apr, pool);
1059 return svn_error_wrap_apr
1060 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1062 cmd = apr_psprintf(pool, "%s %s", editor, file_name);
1063 sys_err = system(cmd);
1065 apr_err = apr_filepath_set(old_cwd, pool);
1067 svn_handle_error2(svn_error_wrap_apr
1068 (apr_err, _("Can't restore working directory")),
1069 stderr, TRUE /* fatal */, "svn: ");
1072 /* Extracting any meaning from sys_err is platform specific, so just
1073 use the raw value. */
1074 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1075 _("system('%s') returned %d"), cmd, sys_err);
1077 return SVN_NO_ERROR;
1082 svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
1083 const char **tmpfile_left /* UTF-8! */,
1084 const char *editor_cmd,
1085 const char *base_dir /* UTF-8! */,
1086 const svn_string_t *contents /* UTF-8! */,
1087 const char *filename,
1089 svn_boolean_t as_text,
1090 const char *encoding,
1095 apr_file_t *tmp_file;
1096 const char *tmpfile_name;
1097 const char *tmpfile_native;
1098 const char *tmpfile_apr, *base_dir_apr;
1099 svn_string_t *translated_contents;
1100 apr_status_t apr_err, apr_err2;
1102 apr_finfo_t finfo_before, finfo_after;
1103 svn_error_t *err = SVN_NO_ERROR, *err2;
1106 svn_boolean_t remove_file = TRUE;
1108 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1110 /* Convert file contents from UTF-8/LF if desired. */
1113 const char *translated;
1114 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1116 NULL, FALSE, pool));
1117 translated_contents = svn_string_create_empty(pool);
1119 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1120 translated, encoding, pool));
1122 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1124 translated_contents->len = strlen(translated_contents->data);
1127 translated_contents = svn_string_dup(contents, pool);
1129 /* Move to BASE_DIR to avoid getting characters that need quoting
1130 into tmpfile_name */
1131 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1133 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1135 /* APR doesn't like "" directories */
1136 if (base_dir[0] == '\0')
1139 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1140 apr_err = apr_filepath_set(base_dir_apr, pool);
1143 return svn_error_wrap_apr
1144 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1147 /*** From here on, any problems that occur require us to cd back!! ***/
1149 /* Ask the working copy for a temporary file named FILENAME-something. */
1150 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1154 svn_io_file_del_none, pool, pool);
1156 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1158 const char *temp_dir_apr;
1160 svn_error_clear(err);
1162 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1164 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1165 apr_err = apr_filepath_set(temp_dir_apr, pool);
1168 return svn_error_wrap_apr
1169 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1172 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1176 svn_io_file_del_none, pool, pool);
1182 /*** From here on, any problems that occur require us to cleanup
1183 the file we just created!! ***/
1185 /* Dump initial CONTENTS to TMP_FILE. */
1186 apr_err = apr_file_write_full(tmp_file, translated_contents->data,
1187 translated_contents->len, &written);
1189 apr_err2 = apr_file_close(tmp_file);
1193 /* Make sure the whole CONTENTS were written, else return an error. */
1196 err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
1201 err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
1205 /* Get information about the temporary file before the user has
1206 been allowed to edit its contents. */
1207 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1208 APR_FINFO_MTIME, pool);
1211 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1215 /* Backdate the file a little bit in case the editor is very fast
1216 and doesn't change the size. (Use two seconds, since some
1217 filesystems have coarse granularity.) It's OK if this call
1218 fails, so we don't check its return value.*/
1219 apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool);
1221 /* Stat it again to get the mtime we actually set. */
1222 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1223 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1226 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1230 /* Prepare the editor command line. */
1231 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
1234 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
1236 /* If the caller wants us to leave the file around, return the path
1237 of the file we'll use, and make a note not to destroy it. */
1240 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1241 remove_file = FALSE;
1244 /* Now, run the editor command line. */
1245 sys_err = system(cmd);
1248 /* Extracting any meaning from sys_err is platform specific, so just
1249 use the raw value. */
1250 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1251 _("system('%s') returned %d"), cmd, sys_err);
1255 /* Get information about the temporary file after the assumed editing. */
1256 apr_err = apr_stat(&finfo_after, tmpfile_apr,
1257 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1260 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1264 /* If the file looks changed... */
1265 if ((finfo_before.mtime != finfo_after.mtime) ||
1266 (finfo_before.size != finfo_after.size))
1268 svn_stringbuf_t *edited_contents_s;
1269 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1273 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1275 /* Translate back to UTF8/LF if desired. */
1278 err = svn_subst_translate_string2(edited_contents, FALSE, FALSE,
1279 *edited_contents, encoding, FALSE,
1283 err = svn_error_quick_wrap
1285 _("Error normalizing edited contents to internal format"));
1292 /* No edits seem to have been made */
1293 *edited_contents = NULL;
1299 /* Remove the file from disk. */
1300 err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
1302 /* Only report remove error if there was no previous error. */
1306 svn_error_clear(err2);
1310 /* If we against all probability can't cd back, all further relative
1311 file references would be screwed up, so we have to abort. */
1312 apr_err = apr_filepath_set(old_cwd, pool);
1315 svn_handle_error2(svn_error_wrap_apr
1316 (apr_err, _("Can't restore working directory")),
1317 stderr, TRUE /* fatal */, "svn: ");
1320 return svn_error_trace(err);