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 (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 (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 server-cert, client-cert, and client-cert-password providers. */
509 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
515 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
517 svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
518 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
519 svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
520 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
522 if (!non_interactive)
524 /* This provider doesn't prompt the user in order to get creds;
525 it prompts the user regarding the caching of creds. */
526 svn_auth_get_ssl_client_cert_pw_file_provider2
527 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt,
532 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
535 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
537 if (!non_interactive)
539 svn_boolean_t ssl_client_cert_file_prompt;
541 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
542 SVN_CONFIG_SECTION_AUTH,
543 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
546 /* Two basic prompt providers: username/password, and just username. */
547 svn_auth_get_simple_prompt_provider(&provider,
548 svn_cmdline_auth_simple_prompt,
552 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
554 svn_auth_get_username_prompt_provider
555 (&provider, svn_cmdline_auth_username_prompt, pb,
556 2, /* retry limit */ pool);
557 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
559 /* SSL prompt providers: server-certs and client-cert-passphrases. */
560 svn_auth_get_ssl_server_trust_prompt_provider
561 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
562 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
564 svn_auth_get_ssl_client_cert_pw_prompt_provider
565 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
566 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
568 /* If configuration allows, add a provider for client-cert path
570 if (ssl_client_cert_file_prompt)
572 svn_auth_get_ssl_client_cert_prompt_provider
573 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
574 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
577 else if (trust_server_cert)
579 /* Remember, only register this provider if non_interactive. */
580 svn_auth_get_ssl_server_trust_prompt_provider
581 (&provider, ssl_trust_unknown_server_cert, NULL, pool);
582 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
585 /* Build an authentication baton to give to libsvn_client. */
586 svn_auth_open(ab, providers, pool);
588 /* Place any default --username or --password credentials into the
589 auth_baton's run-time parameter hash. */
591 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
594 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
597 /* Same with the --non-interactive option. */
599 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
602 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
605 /* Determine whether storing passwords in any form is allowed.
606 * This is the deprecated location for this option, the new
607 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
608 * override the value we set here. */
609 SVN_ERR(svn_config_get_bool(cfg, &store_password_val,
610 SVN_CONFIG_SECTION_AUTH,
611 SVN_CONFIG_OPTION_STORE_PASSWORDS,
612 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS));
614 if (! store_password_val)
615 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
617 /* Determine whether we are allowed to write to the auth/ area.
618 * This is the deprecated location for this option, the new
619 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
620 * override the value we set here. */
621 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val,
622 SVN_CONFIG_SECTION_AUTH,
623 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
624 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS));
626 if (no_auth_cache || ! store_auth_creds_val)
627 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
629 #ifdef SVN_HAVE_GNOME_KEYRING
630 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC,
631 &svn_cmdline__auth_gnome_keyring_unlock_prompt);
632 #endif /* SVN_HAVE_GNOME_KEYRING */
638 svn_cmdline__getopt_init(apr_getopt_t **os,
643 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
645 return svn_error_wrap_apr(apr_err,
646 _("Error initializing command line arguments"));
652 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
653 const char* propname,
654 svn_string_t *propval,
655 svn_boolean_t inherited_prop,
658 const char *xml_safe;
659 const char *encoding = NULL;
662 *outstr = svn_stringbuf_create_empty(pool);
664 if (svn_xml_is_xml_safe(propval->data, propval->len))
666 svn_stringbuf_t *xml_esc = NULL;
667 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
668 xml_safe = xml_esc->data;
672 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
675 xml_safe = base64ed->data;
679 svn_xml_make_open_tag(
680 outstr, pool, svn_xml_protect_pcdata,
681 inherited_prop ? "inherited_property" : "property",
683 "encoding", encoding, NULL);
685 svn_xml_make_open_tag(
686 outstr, pool, svn_xml_protect_pcdata,
687 inherited_prop ? "inherited_property" : "property",
688 "name", propname, NULL);
690 svn_stringbuf_appendcstr(*outstr, xml_safe);
692 svn_xml_make_close_tag(
694 inherited_prop ? "inherited_property" : "property");
700 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
704 svn_cmdline__config_argument_t *config_option;
705 const char *first_colon, *second_colon, *equals_sign;
706 apr_size_t len = strlen(opt_arg);
707 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
709 if ((second_colon = strchr(first_colon + 1, ':')) &&
710 (second_colon != first_colon + 1))
712 if ((equals_sign = strchr(second_colon + 1, '=')) &&
713 (equals_sign != second_colon + 1))
715 config_option = apr_pcalloc(pool, sizeof(*config_option));
716 config_option->file = apr_pstrndup(pool, opt_arg,
717 first_colon - opt_arg);
718 config_option->section = apr_pstrndup(pool, first_colon + 1,
719 second_colon - first_colon - 1);
720 config_option->option = apr_pstrndup(pool, second_colon + 1,
721 equals_sign - second_colon - 1);
723 if (! (strchr(config_option->option, ':')))
725 config_option->value = apr_pstrndup(pool, equals_sign + 1,
726 opt_arg + len - equals_sign - 1);
727 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
734 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
735 _("Invalid syntax of argument of --config-option"));
739 svn_cmdline__apply_config_options(apr_hash_t *config,
740 const apr_array_header_t *config_options,
742 const char *argument_name)
746 for (i = 0; i < config_options->nelts; i++)
749 svn_cmdline__config_argument_t *arg =
750 APR_ARRAY_IDX(config_options, i,
751 svn_cmdline__config_argument_t *);
753 cfg = svn_hash_gets(config, arg->file);
757 svn_config_set(cfg, arg->section, arg->option, arg->value);
761 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
762 _("Unrecognized file in argument of %s"), argument_name);
764 svn_handle_warning2(stderr, err, prefix);
765 svn_error_clear(err);
772 /* Return a copy, allocated in POOL, of the next line of text from *STR
773 * up to and including a CR and/or an LF. Change *STR to point to the
774 * remainder of the string after the returned part. If there are no
775 * characters to be returned, return NULL; never return an empty string.
778 next_line(const char **str, apr_pool_t *pool)
780 const char *start = *str;
781 const char *p = *str;
783 /* n.b. Throughout this fn, we never read any character after a '\0'. */
784 /* Skip over all non-EOL characters, if any. */
785 while (*p != '\r' && *p != '\n' && *p != '\0')
787 /* Skip over \r\n or \n\r or \r or \n, if any. */
788 if (*p == '\r' || *p == '\n')
792 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
796 /* Now p points after at most one '\n' and/or '\r'. */
802 return svn_string_ncreate(start, p - start, pool)->data;
806 svn_cmdline__indent_string(const char *str,
810 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
813 while ((line = next_line(&str, pool)))
815 svn_stringbuf_appendcstr(out, indent);
816 svn_stringbuf_appendcstr(out, line);
822 svn_cmdline__print_prop_hash(svn_stream_t *out,
823 apr_hash_t *prop_hash,
824 svn_boolean_t names_only,
827 apr_array_header_t *sorted_props;
830 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
832 for (i = 0; i < sorted_props->nelts; i++)
834 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
835 const char *pname = item.key;
836 svn_string_t *propval = item.value;
837 const char *pname_stdout;
839 if (svn_prop_needs_translation(pname))
840 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
843 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
847 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
848 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
849 APR_EOL_STR, /* 'native' eol */
850 FALSE, /* no repair */
851 NULL, /* no keywords */
852 FALSE, /* no expansion */
855 SVN_ERR(svn_stream_puts(out, pname_stdout));
859 /* ### We leave these printfs for now, since if propval wasn't
860 translated above, we don't know anything about its encoding.
861 In fact, it might be binary data... */
862 printf(" %s\n", pname_stdout);
867 /* Add an extra newline to the value before indenting, so that
868 * every line of output has the indentation whether the value
869 * already ended in a newline or not. */
870 const char *newval = apr_psprintf(pool, "%s\n", propval->data);
871 const char *indented_newval = svn_cmdline__indent_string(newval,
876 SVN_ERR(svn_stream_puts(out, indented_newval));
880 printf("%s", indented_newval);
889 svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr,
890 apr_hash_t *prop_hash,
891 svn_boolean_t names_only,
892 svn_boolean_t inherited_props,
895 apr_array_header_t *sorted_props;
899 *outstr = svn_stringbuf_create_empty(pool);
901 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
903 for (i = 0; i < sorted_props->nelts; i++)
905 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
906 const char *pname = item.key;
907 svn_string_t *propval = item.value;
911 svn_xml_make_open_tag(
912 outstr, pool, svn_xml_self_closing,
913 inherited_props ? "inherited_property" : "property",
914 "name", pname, NULL);
918 const char *pname_out;
920 if (svn_prop_needs_translation(pname))
921 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
924 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
926 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
927 inherited_props, pool);
935 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
936 svn_boolean_t force_interactive)
938 /* If neither --non-interactive nor --force-interactive was passed,
939 * be interactive if stdin is a terminal.
940 * If --force-interactive was passed, always be interactive. */
941 if (!force_interactive && !non_interactive)
944 return (_isatty(STDIN_FILENO) != 0);
946 return (isatty(STDIN_FILENO) != 0);
949 else if (force_interactive)
952 return !non_interactive;
956 /* Helper for the next two functions. Set *EDITOR to some path to an
957 editor binary. Sources to search include: the EDITOR_CMD argument
958 (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
959 is not NULL), $VISUAL, $EDITOR. Return
960 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
962 find_editor_binary(const char **editor,
963 const char *editor_cmd,
967 struct svn_config_t *cfg;
969 /* Use the editor specified on the command line via --editor-cmd, if any. */
972 /* Otherwise look for the Subversion-specific environment variable. */
974 e = getenv("SVN_EDITOR");
976 /* If not found then fall back on the config file. */
979 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
980 svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
981 SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
984 /* If not found yet then try general purpose environment variables. */
986 e = getenv("VISUAL");
988 e = getenv("EDITOR");
990 #ifdef SVN_CLIENT_EDITOR
991 /* If still not found then fall back on the hard-coded default. */
993 e = SVN_CLIENT_EDITOR;
996 /* Error if there is no editor specified */
1001 for (c = e; *c; c++)
1002 if (!svn_ctype_isspace(*c))
1006 return svn_error_create
1007 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1008 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
1009 "'editor-cmd' run-time configuration option is empty or "
1010 "consists solely of whitespace. Expected a shell command."));
1013 return svn_error_create
1014 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1015 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
1016 "set, and no 'editor-cmd' run-time configuration option was found"));
1019 return SVN_NO_ERROR;
1024 svn_cmdline__edit_file_externally(const char *path,
1025 const char *editor_cmd,
1029 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1032 apr_status_t apr_err;
1034 svn_dirent_split(&base_dir, &file_name, path, pool);
1036 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1038 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1040 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1042 /* APR doesn't like "" directories */
1043 if (base_dir[0] == '\0')
1046 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1048 apr_err = apr_filepath_set(base_dir_apr, pool);
1050 return svn_error_wrap_apr
1051 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1053 cmd = apr_psprintf(pool, "%s %s", editor, file_name);
1054 sys_err = system(cmd);
1056 apr_err = apr_filepath_set(old_cwd, pool);
1058 svn_handle_error2(svn_error_wrap_apr
1059 (apr_err, _("Can't restore working directory")),
1060 stderr, TRUE /* fatal */, "svn: ");
1063 /* Extracting any meaning from sys_err is platform specific, so just
1064 use the raw value. */
1065 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1066 _("system('%s') returned %d"), cmd, sys_err);
1068 return SVN_NO_ERROR;
1073 svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
1074 const char **tmpfile_left /* UTF-8! */,
1075 const char *editor_cmd,
1076 const char *base_dir /* UTF-8! */,
1077 const svn_string_t *contents /* UTF-8! */,
1078 const char *filename,
1080 svn_boolean_t as_text,
1081 const char *encoding,
1086 apr_file_t *tmp_file;
1087 const char *tmpfile_name;
1088 const char *tmpfile_native;
1089 const char *tmpfile_apr, *base_dir_apr;
1090 svn_string_t *translated_contents;
1091 apr_status_t apr_err, apr_err2;
1093 apr_finfo_t finfo_before, finfo_after;
1094 svn_error_t *err = SVN_NO_ERROR, *err2;
1097 svn_boolean_t remove_file = TRUE;
1099 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1101 /* Convert file contents from UTF-8/LF if desired. */
1104 const char *translated;
1105 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1107 NULL, FALSE, pool));
1108 translated_contents = svn_string_create_empty(pool);
1110 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1111 translated, encoding, pool));
1113 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1115 translated_contents->len = strlen(translated_contents->data);
1118 translated_contents = svn_string_dup(contents, pool);
1120 /* Move to BASE_DIR to avoid getting characters that need quoting
1121 into tmpfile_name */
1122 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1124 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1126 /* APR doesn't like "" directories */
1127 if (base_dir[0] == '\0')
1130 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1131 apr_err = apr_filepath_set(base_dir_apr, pool);
1134 return svn_error_wrap_apr
1135 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1138 /*** From here on, any problems that occur require us to cd back!! ***/
1140 /* Ask the working copy for a temporary file named FILENAME-something. */
1141 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1145 svn_io_file_del_none, pool, pool);
1147 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1149 const char *temp_dir_apr;
1151 svn_error_clear(err);
1153 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1155 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1156 apr_err = apr_filepath_set(temp_dir_apr, pool);
1159 return svn_error_wrap_apr
1160 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1163 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1167 svn_io_file_del_none, pool, pool);
1173 /*** From here on, any problems that occur require us to cleanup
1174 the file we just created!! ***/
1176 /* Dump initial CONTENTS to TMP_FILE. */
1177 apr_err = apr_file_write_full(tmp_file, translated_contents->data,
1178 translated_contents->len, &written);
1180 apr_err2 = apr_file_close(tmp_file);
1184 /* Make sure the whole CONTENTS were written, else return an error. */
1187 err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
1192 err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
1196 /* Get information about the temporary file before the user has
1197 been allowed to edit its contents. */
1198 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1199 APR_FINFO_MTIME, pool);
1202 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1206 /* Backdate the file a little bit in case the editor is very fast
1207 and doesn't change the size. (Use two seconds, since some
1208 filesystems have coarse granularity.) It's OK if this call
1209 fails, so we don't check its return value.*/
1210 apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool);
1212 /* Stat it again to get the mtime we actually set. */
1213 apr_err = apr_stat(&finfo_before, tmpfile_apr,
1214 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1217 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1221 /* Prepare the editor command line. */
1222 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
1225 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
1227 /* If the caller wants us to leave the file around, return the path
1228 of the file we'll use, and make a note not to destroy it. */
1231 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1232 remove_file = FALSE;
1235 /* Now, run the editor command line. */
1236 sys_err = system(cmd);
1239 /* Extracting any meaning from sys_err is platform specific, so just
1240 use the raw value. */
1241 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1242 _("system('%s') returned %d"), cmd, sys_err);
1246 /* Get information about the temporary file after the assumed editing. */
1247 apr_err = apr_stat(&finfo_after, tmpfile_apr,
1248 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1251 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1255 /* If the file looks changed... */
1256 if ((finfo_before.mtime != finfo_after.mtime) ||
1257 (finfo_before.size != finfo_after.size))
1259 svn_stringbuf_t *edited_contents_s;
1260 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1264 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1266 /* Translate back to UTF8/LF if desired. */
1269 err = svn_subst_translate_string2(edited_contents, FALSE, FALSE,
1270 *edited_contents, encoding, FALSE,
1274 err = svn_error_quick_wrap
1276 _("Error normalizing edited contents to internal format"));
1283 /* No edits seem to have been made */
1284 *edited_contents = NULL;
1290 /* Remove the file from disk. */
1291 err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
1293 /* Only report remove error if there was no previous error. */
1297 svn_error_clear(err2);
1301 /* If we against all probability can't cd back, all further relative
1302 file references would be screwed up, so we have to abort. */
1303 apr_err = apr_filepath_set(old_cwd, pool);
1306 svn_handle_error2(svn_error_wrap_apr
1307 (apr_err, _("Can't restore working directory")),
1308 stderr, TRUE /* fatal */, "svn: ");
1311 return svn_error_trace(err);