1 /*
2 * cmdline.c : Helpers for command-line programs.
3 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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
20 * under the License.
21 * ====================================================================
22 */
23
24
25 #include <stdlib.h> /* for atexit() */
26 #include <stdio.h> /* for setvbuf() */
27 #include <locale.h> /* for setlocale() */
28
29 #ifndef WIN32
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #else
35 #include <crtdbg.h>
36 #include <io.h>
37 #endif
38
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>
44
45 #include "svn_cmdline.h"
46 #include "svn_ctype.h"
47 #include "svn_dso.h"
48 #include "svn_dirent_uri.h"
49 #include "svn_hash.h"
50 #include "svn_path.h"
51 #include "svn_pools.h"
52 #include "svn_error.h"
53 #include "svn_nls.h"
54 #include "svn_utf.h"
55 #include "svn_auth.h"
56 #include "svn_xml.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"
62
63 #include "private/svn_cmdline_private.h"
64 #include "private/svn_utf_private.h"
65 #include "private/svn_string_private.h"
66
67 #include "svn_private_config.h"
68
69 #include "win32_crashrpt.h"
70
71 /* The stdin encoding. If null, it's the same as the native encoding. */
72 static const char *input_encoding = NULL;
73
74 /* The stdout encoding. If null, it's the same as the native encoding. */
75 static const char *output_encoding = NULL;
76
77
78 int
svn_cmdline_init(const char * progname,FILE * error_stream)79 svn_cmdline_init(const char *progname, FILE *error_stream)
80 {
81 apr_status_t status;
82 apr_pool_t *pool;
83 svn_error_t *err;
84 char prefix_buf[64]; /* 64 is probably bigger than most program names */
85
86 #ifndef WIN32
87 {
88 struct stat st;
89
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
94 corrupt it. */
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))
98 {
99 if (error_stream)
100 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
101 progname);
102 return EXIT_FAILURE;
103 }
104 }
105 #endif
106
107 /* Ignore any errors encountered while attempting to change stream
108 buffering, as the streams should retain their default buffering
109 modes. */
110 if (error_stream)
111 setvbuf(error_stream, NULL, _IONBF, 0);
112 #ifndef WIN32
113 setvbuf(stdout, NULL, _IOLBF, 0);
114 #endif
115
116 #ifdef WIN32
117 #if _MSC_VER < 1400
118 /* Initialize the input and output encodings. */
119 {
120 static char input_encoding_buffer[16];
121 static char output_encoding_buffer[16];
122
123 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer,
124 "CP%u", (unsigned) GetConsoleCP());
125 input_encoding = input_encoding_buffer;
126
127 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer,
128 "CP%u", (unsigned) GetConsoleOutputCP());
129 output_encoding = output_encoding_buffer;
130 }
131 #endif /* _MSC_VER < 1400 */
132
133 #ifdef SVN_USE_WIN32_CRASHHANDLER
134 /* Attach (but don't load) the crash handler */
135 SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
136
137 #if _MSC_VER >= 1400
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"))
143 {
144 /* In release mode: Redirect abort() errors to stderr */
145 _set_error_mode(_OUT_TO_STDERR);
146
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);
155 }
156 #endif /* _MSC_VER >= 1400 */
157
158 #endif /* SVN_USE_WIN32_CRASHHANDLER */
159
160 #endif /* WIN32 */
161
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
164 environment. */
165 if (!setlocale(LC_ALL, "")
166 && !setlocale(LC_CTYPE, ""))
167 {
168 if (error_stream)
169 {
170 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
171 const char **env_var = &env_vars[0], *env_val = NULL;
172 while (*env_var)
173 {
174 env_val = getenv(*env_var);
175 if (env_val && env_val[0])
176 break;
177 ++env_var;
178 }
179
180 if (!*env_var)
181 {
182 /* Unlikely. Can setlocale fail if no env vars are set? */
183 --env_var;
184 env_val = "not set";
185 }
186
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);
192 }
193 }
194
195 /* Initialize the APR subsystem, and register an atexit() function
196 to Uninitialize that subsystem at program exit. */
197 status = apr_initialize();
198 if (status)
199 {
200 if (error_stream)
201 {
202 char buf[1024];
203 apr_strerror(status, buf, sizeof(buf) - 1);
204 fprintf(error_stream,
205 "%s: error: cannot initialize APR: %s\n",
206 progname, buf);
207 }
208 return EXIT_FAILURE;
209 }
210
211 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3);
212 prefix_buf[sizeof(prefix_buf) - 3] = '\0';
213 strcat(prefix_buf, ": ");
214
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()))
219 {
220 if (error_stream)
221 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
222
223 svn_error_clear(err);
224 return EXIT_FAILURE;
225 }
226
227 if (0 > atexit(apr_terminate))
228 {
229 if (error_stream)
230 fprintf(error_stream,
231 "%s: error: atexit registration failed\n",
232 progname);
233 return EXIT_FAILURE;
234 }
235
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);
240
241 if ((err = svn_nls_init()))
242 {
243 if (error_stream)
244 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
245
246 svn_error_clear(err);
247 return EXIT_FAILURE;
248 }
249
250 return EXIT_SUCCESS;
251 }
252
253
254 svn_error_t *
svn_cmdline_cstring_from_utf8(const char ** dest,const char * src,apr_pool_t * pool)255 svn_cmdline_cstring_from_utf8(const char **dest,
256 const char *src,
257 apr_pool_t *pool)
258 {
259 if (output_encoding == NULL)
260 return svn_utf_cstring_from_utf8(dest, src, pool);
261 else
262 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
263 }
264
265
266 const char *
svn_cmdline_cstring_from_utf8_fuzzy(const char * src,apr_pool_t * pool)267 svn_cmdline_cstring_from_utf8_fuzzy(const char *src,
268 apr_pool_t *pool)
269 {
270 return svn_utf__cstring_from_utf8_fuzzy(src, pool,
271 svn_cmdline_cstring_from_utf8);
272 }
273
274
275 svn_error_t *
svn_cmdline_cstring_to_utf8(const char ** dest,const char * src,apr_pool_t * pool)276 svn_cmdline_cstring_to_utf8(const char **dest,
277 const char *src,
278 apr_pool_t *pool)
279 {
280 if (input_encoding == NULL)
281 return svn_utf_cstring_to_utf8(dest, src, pool);
282 else
283 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
284 }
285
286
287 svn_error_t *
svn_cmdline_path_local_style_from_utf8(const char ** dest,const char * src,apr_pool_t * pool)288 svn_cmdline_path_local_style_from_utf8(const char **dest,
289 const char *src,
290 apr_pool_t *pool)
291 {
292 return svn_cmdline_cstring_from_utf8(dest,
293 svn_dirent_local_style(src, pool),
294 pool);
295 }
296
297 svn_error_t *
svn_cmdline_printf(apr_pool_t * pool,const char * fmt,...)298 svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...)
299 {
300 const char *message;
301 va_list ap;
302
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.
309 */
310
311 va_start(ap, fmt);
312 message = apr_pvsprintf(pool, fmt, ap);
313 va_end(ap);
314
315 return svn_cmdline_fputs(message, stdout, pool);
316 }
317
318 svn_error_t *
svn_cmdline_fprintf(FILE * stream,apr_pool_t * pool,const char * fmt,...)319 svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...)
320 {
321 const char *message;
322 va_list ap;
323
324 /* See svn_cmdline_printf () for a note about character encoding issues. */
325
326 va_start(ap, fmt);
327 message = apr_pvsprintf(pool, fmt, ap);
328 va_end(ap);
329
330 return svn_cmdline_fputs(message, stream, pool);
331 }
332
333 svn_error_t *
svn_cmdline_fputs(const char * string,FILE * stream,apr_pool_t * pool)334 svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool)
335 {
336 svn_error_t *err;
337 const char *out;
338
339 err = svn_cmdline_cstring_from_utf8(&out, string, pool);
340
341 if (err)
342 {
343 svn_error_clear(err);
344 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
345 }
346
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
350 a generic error. */
351 errno = 0;
352
353 if (fputs(out, stream) == EOF)
354 {
355 if (apr_get_os_error()) /* is errno on POSIX */
356 {
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);
361 else
362 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
363 }
364 else
365 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
366 }
367
368 return SVN_NO_ERROR;
369 }
370
371 svn_error_t *
svn_cmdline_fflush(FILE * stream)372 svn_cmdline_fflush(FILE *stream)
373 {
374 /* See comment in svn_cmdline_fputs about use of errno and stdio. */
375 errno = 0;
376 if (fflush(stream) == EOF)
377 {
378 if (apr_get_os_error()) /* is errno on POSIX */
379 {
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);
384 else
385 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
386 }
387 else
388 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
389 }
390
391 return SVN_NO_ERROR;
392 }
393
svn_cmdline_output_encoding(apr_pool_t * pool)394 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
395 {
396 if (output_encoding)
397 return apr_pstrdup(pool, output_encoding);
398 else
399 return SVN_APR_LOCALE_CHARSET;
400 }
401
402 int
svn_cmdline_handle_exit_error(svn_error_t * err,apr_pool_t * pool,const char * prefix)403 svn_cmdline_handle_exit_error(svn_error_t *err,
404 apr_pool_t *pool,
405 const char *prefix)
406 {
407 /* Issue #3014:
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.
411 *
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);
417 if (pool)
418 svn_pool_destroy(pool);
419 return EXIT_FAILURE;
420 }
421
422 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'.
423
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).
428
429 Ignore MAY_SAVE; we don't save certs we never prompted for.
430
431 Ignore BATON, REALM, and CERT_INFO,
432
433 Ignore any further films by George Lucas. */
434 static svn_error_t *
ssl_trust_unknown_server_cert(svn_auth_cred_ssl_server_trust_t ** cred_p,void * baton,const char * realm,apr_uint32_t failures,const svn_auth_ssl_server_cert_info_t * cert_info,svn_boolean_t may_save,apr_pool_t * pool)435 ssl_trust_unknown_server_cert
436 (svn_auth_cred_ssl_server_trust_t **cred_p,
437 void *baton,
438 const char *realm,
439 apr_uint32_t failures,
440 const svn_auth_ssl_server_cert_info_t *cert_info,
441 svn_boolean_t may_save,
442 apr_pool_t *pool)
443 {
444 *cred_p = NULL;
445
446 if (failures == 0 || failures == SVN_AUTH_SSL_UNKNOWNCA)
447 {
448 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
449 (*cred_p)->may_save = FALSE;
450 (*cred_p)->accepted_failures = failures;
451 }
452
453 return SVN_NO_ERROR;
454 }
455
456 svn_error_t *
svn_cmdline_create_auth_baton(svn_auth_baton_t ** ab,svn_boolean_t non_interactive,const char * auth_username,const char * auth_password,const char * config_dir,svn_boolean_t no_auth_cache,svn_boolean_t trust_server_cert,svn_config_t * cfg,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)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,
464 svn_config_t *cfg,
465 svn_cancel_func_t cancel_func,
466 void *cancel_baton,
467 apr_pool_t *pool)
468 {
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;
473
474 /* The whole list of registered providers */
475 apr_array_header_t *providers;
476
477 /* Populate the registered providers with the platform-specific providers */
478 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
479 cfg, pool));
480
481 /* If we have a cancellation function, cram it and the stuff it
482 needs into the prompt baton. */
483 if (cancel_func)
484 {
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;
489 }
490
491 if (!non_interactive)
492 {
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,
497 pb, pool);
498 }
499 else
500 {
501 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool);
502 }
503
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;
507
508 /* The windows ssl server certificate CRYPTOAPI provider. */
509 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
510 "windows",
511 "ssl_server_trust",
512 pool));
513
514 if (provider)
515 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
516
517 /* The windows ssl authority certificate CRYPTOAPI provider. */
518 SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
519 "windows",
520 "ssl_server_authority",
521 pool));
522
523 if (provider)
524 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
525
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;
530
531 if (!non_interactive)
532 {
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,
537 pb, pool);
538 }
539 else
540 {
541 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
542 pool);
543 }
544 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
545
546 if (!non_interactive)
547 {
548 svn_boolean_t ssl_client_cert_file_prompt;
549
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,
553 FALSE));
554
555 /* Two basic prompt providers: username/password, and just username. */
556 svn_auth_get_simple_prompt_provider(&provider,
557 svn_cmdline_auth_simple_prompt,
558 pb,
559 2, /* retry limit */
560 pool);
561 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
562
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;
567
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;
572
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;
576
577 /* If configuration allows, add a provider for client-cert path
578 prompting, too. */
579 if (ssl_client_cert_file_prompt)
580 {
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;
584 }
585 }
586 else if (trust_server_cert)
587 {
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;
592 }
593
594 /* Build an authentication baton to give to libsvn_client. */
595 svn_auth_open(ab, providers, pool);
596
597 /* Place any default --username or --password credentials into the
598 auth_baton's run-time parameter hash. */
599 if (auth_username)
600 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
601 auth_username);
602 if (auth_password)
603 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
604 auth_password);
605
606 /* Same with the --non-interactive option. */
607 if (non_interactive)
608 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
609
610 if (config_dir)
611 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
612 config_dir);
613
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));
622
623 if (! store_password_val)
624 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
625
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));
634
635 if (no_auth_cache || ! store_auth_creds_val)
636 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
637
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 */
642
643 return SVN_NO_ERROR;
644 }
645
646 svn_error_t *
svn_cmdline__getopt_init(apr_getopt_t ** os,int argc,const char * argv[],apr_pool_t * pool)647 svn_cmdline__getopt_init(apr_getopt_t **os,
648 int argc,
649 const char *argv[],
650 apr_pool_t *pool)
651 {
652 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
653 if (apr_err)
654 return svn_error_wrap_apr(apr_err,
655 _("Error initializing command line arguments"));
656 return SVN_NO_ERROR;
657 }
658
659
660 void
svn_cmdline__print_xml_prop(svn_stringbuf_t ** outstr,const char * propname,svn_string_t * propval,svn_boolean_t inherited_prop,apr_pool_t * pool)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,
665 apr_pool_t *pool)
666 {
667 const char *xml_safe;
668 const char *encoding = NULL;
669
670 if (*outstr == NULL)
671 *outstr = svn_stringbuf_create_empty(pool);
672
673 if (svn_xml_is_xml_safe(propval->data, propval->len))
674 {
675 svn_stringbuf_t *xml_esc = NULL;
676 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
677 xml_safe = xml_esc->data;
678 }
679 else
680 {
681 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
682 pool);
683 encoding = "base64";
684 xml_safe = base64ed->data;
685 }
686
687 if (encoding)
688 svn_xml_make_open_tag(
689 outstr, pool, svn_xml_protect_pcdata,
690 inherited_prop ? "inherited_property" : "property",
691 "name", propname,
692 "encoding", encoding, NULL);
693 else
694 svn_xml_make_open_tag(
695 outstr, pool, svn_xml_protect_pcdata,
696 inherited_prop ? "inherited_property" : "property",
697 "name", propname, NULL);
698
699 svn_stringbuf_appendcstr(*outstr, xml_safe);
700
701 svn_xml_make_close_tag(
702 outstr, pool,
703 inherited_prop ? "inherited_property" : "property");
704
705 return;
706 }
707
708 svn_error_t *
svn_cmdline__parse_config_option(apr_array_header_t * config_options,const char * opt_arg,apr_pool_t * pool)709 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
710 const char *opt_arg,
711 apr_pool_t *pool)
712 {
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))
717 {
718 if ((second_colon = strchr(first_colon + 1, ':')) &&
719 (second_colon != first_colon + 1))
720 {
721 if ((equals_sign = strchr(second_colon + 1, '=')) &&
722 (equals_sign != second_colon + 1))
723 {
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);
731
732 if (! (strchr(config_option->option, ':')))
733 {
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 *)
737 = config_option;
738 return SVN_NO_ERROR;
739 }
740 }
741 }
742 }
743 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
744 _("Invalid syntax of argument of --config-option"));
745 }
746
747 svn_error_t *
svn_cmdline__apply_config_options(apr_hash_t * config,const apr_array_header_t * config_options,const char * prefix,const char * argument_name)748 svn_cmdline__apply_config_options(apr_hash_t *config,
749 const apr_array_header_t *config_options,
750 const char *prefix,
751 const char *argument_name)
752 {
753 int i;
754
755 for (i = 0; i < config_options->nelts; i++)
756 {
757 svn_config_t *cfg;
758 svn_cmdline__config_argument_t *arg =
759 APR_ARRAY_IDX(config_options, i,
760 svn_cmdline__config_argument_t *);
761
762 cfg = svn_hash_gets(config, arg->file);
763
764 if (cfg)
765 {
766 svn_config_set(cfg, arg->section, arg->option, arg->value);
767 }
768 else
769 {
770 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
771 _("Unrecognized file in argument of %s"), argument_name);
772
773 svn_handle_warning2(stderr, err, prefix);
774 svn_error_clear(err);
775 }
776 }
777
778 return SVN_NO_ERROR;
779 }
780
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.
785 */
786 static const char *
next_line(const char ** str,apr_pool_t * pool)787 next_line(const char **str, apr_pool_t *pool)
788 {
789 const char *start = *str;
790 const char *p = *str;
791
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')
795 p++;
796 /* Skip over \r\n or \n\r or \r or \n, if any. */
797 if (*p == '\r' || *p == '\n')
798 {
799 char c = *p++;
800
801 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
802 p++;
803 }
804
805 /* Now p points after at most one '\n' and/or '\r'. */
806 *str = p;
807
808 if (p == start)
809 return NULL;
810
811 return svn_string_ncreate(start, p - start, pool)->data;
812 }
813
814 const char *
svn_cmdline__indent_string(const char * str,const char * indent,apr_pool_t * pool)815 svn_cmdline__indent_string(const char *str,
816 const char *indent,
817 apr_pool_t *pool)
818 {
819 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
820 const char *line;
821
822 while ((line = next_line(&str, pool)))
823 {
824 svn_stringbuf_appendcstr(out, indent);
825 svn_stringbuf_appendcstr(out, line);
826 }
827 return out->data;
828 }
829
830 svn_error_t *
svn_cmdline__print_prop_hash(svn_stream_t * out,apr_hash_t * prop_hash,svn_boolean_t names_only,apr_pool_t * pool)831 svn_cmdline__print_prop_hash(svn_stream_t *out,
832 apr_hash_t *prop_hash,
833 svn_boolean_t names_only,
834 apr_pool_t *pool)
835 {
836 apr_array_header_t *sorted_props;
837 int i;
838
839 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
840 pool);
841 for (i = 0; i < sorted_props->nelts; i++)
842 {
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;
847
848 if (svn_prop_needs_translation(pname))
849 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
850 TRUE, pool));
851
852 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
853
854 if (out)
855 {
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 */
862 pool));
863
864 SVN_ERR(svn_stream_puts(out, pname_stdout));
865 }
866 else
867 {
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);
872 }
873
874 if (!names_only)
875 {
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,
881 " ",
882 pool);
883 if (out)
884 {
885 SVN_ERR(svn_stream_puts(out, indented_newval));
886 }
887 else
888 {
889 printf("%s", indented_newval);
890 }
891 }
892 }
893
894 return SVN_NO_ERROR;
895 }
896
897 svn_error_t *
svn_cmdline__print_xml_prop_hash(svn_stringbuf_t ** outstr,apr_hash_t * prop_hash,svn_boolean_t names_only,svn_boolean_t inherited_props,apr_pool_t * pool)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,
902 apr_pool_t *pool)
903 {
904 apr_array_header_t *sorted_props;
905 int i;
906
907 if (*outstr == NULL)
908 *outstr = svn_stringbuf_create_empty(pool);
909
910 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
911 pool);
912 for (i = 0; i < sorted_props->nelts; i++)
913 {
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;
917
918 if (names_only)
919 {
920 svn_xml_make_open_tag(
921 outstr, pool, svn_xml_self_closing,
922 inherited_props ? "inherited_property" : "property",
923 "name", pname, NULL);
924 }
925 else
926 {
927 const char *pname_out;
928
929 if (svn_prop_needs_translation(pname))
930 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
931 TRUE, pool));
932
933 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
934
935 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
936 inherited_props, pool);
937 }
938 }
939
940 return SVN_NO_ERROR;
941 }
942
943 svn_boolean_t
svn_cmdline__be_interactive(svn_boolean_t non_interactive,svn_boolean_t force_interactive)944 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
945 svn_boolean_t force_interactive)
946 {
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)
951 {
952 #ifdef WIN32
953 return (_isatty(STDIN_FILENO) != 0);
954 #else
955 return (isatty(STDIN_FILENO) != 0);
956 #endif
957 }
958 else if (force_interactive)
959 return TRUE;
960
961 return !non_interactive;
962 }
963
964
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. */
970 static svn_error_t *
find_editor_binary(const char ** editor,const char * editor_cmd,apr_hash_t * config)971 find_editor_binary(const char **editor,
972 const char *editor_cmd,
973 apr_hash_t *config)
974 {
975 const char *e;
976 struct svn_config_t *cfg;
977
978 /* Use the editor specified on the command line via --editor-cmd, if any. */
979 e = editor_cmd;
980
981 /* Otherwise look for the Subversion-specific environment variable. */
982 if (! e)
983 e = getenv("SVN_EDITOR");
984
985 /* If not found then fall back on the config file. */
986 if (! e)
987 {
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);
991 }
992
993 /* If not found yet then try general purpose environment variables. */
994 if (! e)
995 e = getenv("VISUAL");
996 if (! e)
997 e = getenv("EDITOR");
998
999 #ifdef SVN_CLIENT_EDITOR
1000 /* If still not found then fall back on the hard-coded default. */
1001 if (! e)
1002 e = SVN_CLIENT_EDITOR;
1003 #endif
1004
1005 /* Error if there is no editor specified */
1006 if (e)
1007 {
1008 const char *c;
1009
1010 for (c = e; *c; c++)
1011 if (!svn_ctype_isspace(*c))
1012 break;
1013
1014 if (! *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."));
1020 }
1021 else
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"));
1026
1027 *editor = e;
1028 return SVN_NO_ERROR;
1029 }
1030
1031
1032 svn_error_t *
svn_cmdline__edit_file_externally(const char * path,const char * editor_cmd,apr_hash_t * config,apr_pool_t * pool)1033 svn_cmdline__edit_file_externally(const char *path,
1034 const char *editor_cmd,
1035 apr_hash_t *config,
1036 apr_pool_t *pool)
1037 {
1038 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1039 char *old_cwd;
1040 int sys_err;
1041 apr_status_t apr_err;
1042
1043 svn_dirent_split(&base_dir, &file_name, path, pool);
1044
1045 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1046
1047 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1048 if (apr_err)
1049 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1050
1051 /* APR doesn't like "" directories */
1052 if (base_dir[0] == '\0')
1053 base_dir_apr = ".";
1054 else
1055 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1056
1057 apr_err = apr_filepath_set(base_dir_apr, pool);
1058 if (apr_err)
1059 return svn_error_wrap_apr
1060 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1061
1062 cmd = apr_psprintf(pool, "%s %s", editor, file_name);
1063 sys_err = system(cmd);
1064
1065 apr_err = apr_filepath_set(old_cwd, pool);
1066 if (apr_err)
1067 svn_handle_error2(svn_error_wrap_apr
1068 (apr_err, _("Can't restore working directory")),
1069 stderr, TRUE /* fatal */, "svn: ");
1070
1071 if (sys_err)
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);
1076
1077 return SVN_NO_ERROR;
1078 }
1079
1080
1081 svn_error_t *
svn_cmdline__edit_string_externally(svn_string_t ** edited_contents,const char ** tmpfile_left,const char * editor_cmd,const char * base_dir,const svn_string_t * contents,const char * filename,apr_hash_t * config,svn_boolean_t as_text,const char * encoding,apr_pool_t * pool)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,
1088 apr_hash_t *config,
1089 svn_boolean_t as_text,
1090 const char *encoding,
1091 apr_pool_t *pool)
1092 {
1093 const char *editor;
1094 const char *cmd;
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;
1101 apr_size_t written;
1102 apr_finfo_t finfo_before, finfo_after;
1103 svn_error_t *err = SVN_NO_ERROR, *err2;
1104 char *old_cwd;
1105 int sys_err;
1106 svn_boolean_t remove_file = TRUE;
1107
1108 SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
1109
1110 /* Convert file contents from UTF-8/LF if desired. */
1111 if (as_text)
1112 {
1113 const char *translated;
1114 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1115 APR_EOL_STR, FALSE,
1116 NULL, FALSE, pool));
1117 translated_contents = svn_string_create_empty(pool);
1118 if (encoding)
1119 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1120 translated, encoding, pool));
1121 else
1122 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1123 translated, pool));
1124 translated_contents->len = strlen(translated_contents->data);
1125 }
1126 else
1127 translated_contents = svn_string_dup(contents, pool);
1128
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);
1132 if (apr_err)
1133 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1134
1135 /* APR doesn't like "" directories */
1136 if (base_dir[0] == '\0')
1137 base_dir_apr = ".";
1138 else
1139 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1140 apr_err = apr_filepath_set(base_dir_apr, pool);
1141 if (apr_err)
1142 {
1143 return svn_error_wrap_apr
1144 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1145 }
1146
1147 /*** From here on, any problems that occur require us to cd back!! ***/
1148
1149 /* Ask the working copy for a temporary file named FILENAME-something. */
1150 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1151 "" /* dirpath */,
1152 filename,
1153 ".tmp",
1154 svn_io_file_del_none, pool, pool);
1155
1156 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1157 {
1158 const char *temp_dir_apr;
1159
1160 svn_error_clear(err);
1161
1162 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1163
1164 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1165 apr_err = apr_filepath_set(temp_dir_apr, pool);
1166 if (apr_err)
1167 {
1168 return svn_error_wrap_apr
1169 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1170 }
1171
1172 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1173 "" /* dirpath */,
1174 filename,
1175 ".tmp",
1176 svn_io_file_del_none, pool, pool);
1177 }
1178
1179 if (err)
1180 goto cleanup2;
1181
1182 /*** From here on, any problems that occur require us to cleanup
1183 the file we just created!! ***/
1184
1185 /* Dump initial CONTENTS to TMP_FILE. */
1186 apr_err = apr_file_write_full(tmp_file, translated_contents->data,
1187 translated_contents->len, &written);
1188
1189 apr_err2 = apr_file_close(tmp_file);
1190 if (! apr_err)
1191 apr_err = apr_err2;
1192
1193 /* Make sure the whole CONTENTS were written, else return an error. */
1194 if (apr_err)
1195 {
1196 err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
1197 tmpfile_name);
1198 goto cleanup;
1199 }
1200
1201 err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
1202 if (err)
1203 goto cleanup;
1204
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);
1209 if (apr_err)
1210 {
1211 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1212 goto cleanup;
1213 }
1214
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);
1220
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);
1224 if (apr_err)
1225 {
1226 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1227 goto cleanup;
1228 }
1229
1230 /* Prepare the editor command line. */
1231 err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
1232 if (err)
1233 goto cleanup;
1234 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
1235
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. */
1238 if (tmpfile_left)
1239 {
1240 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1241 remove_file = FALSE;
1242 }
1243
1244 /* Now, run the editor command line. */
1245 sys_err = system(cmd);
1246 if (sys_err != 0)
1247 {
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);
1252 goto cleanup;
1253 }
1254
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);
1258 if (apr_err)
1259 {
1260 err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
1261 goto cleanup;
1262 }
1263
1264 /* If the file looks changed... */
1265 if ((finfo_before.mtime != finfo_after.mtime) ||
1266 (finfo_before.size != finfo_after.size))
1267 {
1268 svn_stringbuf_t *edited_contents_s;
1269 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1270 if (err)
1271 goto cleanup;
1272
1273 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1274
1275 /* Translate back to UTF8/LF if desired. */
1276 if (as_text)
1277 {
1278 err = svn_subst_translate_string2(edited_contents, FALSE, FALSE,
1279 *edited_contents, encoding, FALSE,
1280 pool, pool);
1281 if (err)
1282 {
1283 err = svn_error_quick_wrap
1284 (err,
1285 _("Error normalizing edited contents to internal format"));
1286 goto cleanup;
1287 }
1288 }
1289 }
1290 else
1291 {
1292 /* No edits seem to have been made */
1293 *edited_contents = NULL;
1294 }
1295
1296 cleanup:
1297 if (remove_file)
1298 {
1299 /* Remove the file from disk. */
1300 err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
1301
1302 /* Only report remove error if there was no previous error. */
1303 if (! err && err2)
1304 err = err2;
1305 else
1306 svn_error_clear(err2);
1307 }
1308
1309 cleanup2:
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);
1313 if (apr_err)
1314 {
1315 svn_handle_error2(svn_error_wrap_apr
1316 (apr_err, _("Can't restore working directory")),
1317 stderr, TRUE /* fatal */, "svn: ");
1318 }
1319
1320 return svn_error_trace(err);
1321 }
1322