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 #include <conio.h>
38 #endif
39
40 #include <apr.h> /* for STDIN_FILENO */
41 #include <apr_errno.h> /* for apr_strerror */
42 #include <apr_version.h>
43 #if APR_VERSION_AT_LEAST(1,5,0)
44 #include <apr_escape.h>
45 #else
46 #include "private/svn_dep_compat.h"
47 #endif
48 #include <apr_general.h> /* for apr_initialize/apr_terminate */
49 #include <apr_strings.h> /* for apr_snprintf */
50 #include <apr_env.h> /* for apr_env_get */
51 #include <apr_pools.h>
52 #include <apr_signal.h>
53
54 #include "svn_cmdline.h"
55 #include "svn_ctype.h"
56 #include "svn_dso.h"
57 #include "svn_dirent_uri.h"
58 #include "svn_hash.h"
59 #include "svn_path.h"
60 #include "svn_pools.h"
61 #include "svn_error.h"
62 #include "svn_nls.h"
63 #include "svn_utf.h"
64 #include "svn_auth.h"
65 #include "svn_xml.h"
66 #include "svn_base64.h"
67 #include "svn_config.h"
68 #include "svn_sorts.h"
69 #include "svn_props.h"
70 #include "svn_subst.h"
71
72 #include "private/svn_cmdline_private.h"
73 #include "private/svn_utf_private.h"
74 #include "private/svn_sorts_private.h"
75 #include "private/svn_string_private.h"
76
77 #include "svn_private_config.h"
78
79 #include "win32_crashrpt.h"
80
81 #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400)
82 /* Before Visual Studio 2005, the C runtime didn't handle encodings for the
83 for the stdio output handling. */
84 #define CMDLINE_USE_CUSTOM_ENCODING
85
86 /* The stdin encoding. If null, it's the same as the native encoding. */
87 static const char *input_encoding = NULL;
88
89 /* The stdout encoding. If null, it's the same as the native encoding. */
90 static const char *output_encoding = NULL;
91 #elif defined(WIN32) && defined(_MSC_VER)
92 /* For now limit this code to Visual C++, as the result is highly dependent
93 on the CRT implementation */
94 #define USE_WIN32_CONSOLE_SHORTCUT
95
96 /* When TRUE, stdout/stderr is directly connected to a console */
97 static svn_boolean_t shortcut_stdout_to_console = FALSE;
98 static svn_boolean_t shortcut_stderr_to_console = FALSE;
99 #endif
100
101
102 int
svn_cmdline_init(const char * progname,FILE * error_stream)103 svn_cmdline_init(const char *progname, FILE *error_stream)
104 {
105 apr_status_t status;
106 apr_pool_t *pool;
107 svn_error_t *err;
108 char prefix_buf[64]; /* 64 is probably bigger than most program names */
109
110 #ifndef WIN32
111 {
112 struct stat st;
113
114 /* The following makes sure that file descriptors 0 (stdin), 1
115 (stdout) and 2 (stderr) will not be "reused", because if
116 e.g. file descriptor 2 would be reused when opening a file, a
117 write to stderr would write to that file and most likely
118 corrupt it. */
119 if ((fstat(0, &st) == -1 && open("/dev/null", O_RDONLY) == -1) ||
120 (fstat(1, &st) == -1 && open("/dev/null", O_WRONLY) == -1) ||
121 (fstat(2, &st) == -1 && open("/dev/null", O_WRONLY) == -1))
122 {
123 if (error_stream)
124 fprintf(error_stream, "%s: error: cannot open '/dev/null'\n",
125 progname);
126 return EXIT_FAILURE;
127 }
128 }
129 #endif
130
131 /* Ignore any errors encountered while attempting to change stream
132 buffering, as the streams should retain their default buffering
133 modes. */
134 if (error_stream)
135 setvbuf(error_stream, NULL, _IONBF, 0);
136 #ifndef WIN32
137 setvbuf(stdout, NULL, _IOLBF, 0);
138 #endif
139
140 #ifdef WIN32
141 #ifdef CMDLINE_USE_CUSTOM_ENCODING
142 /* Initialize the input and output encodings. */
143 {
144 static char input_encoding_buffer[16];
145 static char output_encoding_buffer[16];
146
147 apr_snprintf(input_encoding_buffer, sizeof input_encoding_buffer,
148 "CP%u", (unsigned) GetConsoleCP());
149 input_encoding = input_encoding_buffer;
150
151 apr_snprintf(output_encoding_buffer, sizeof output_encoding_buffer,
152 "CP%u", (unsigned) GetConsoleOutputCP());
153 output_encoding = output_encoding_buffer;
154 }
155 #endif /* CMDLINE_USE_CUSTOM_ENCODING */
156
157 #ifdef SVN_USE_WIN32_CRASHHANDLER
158 if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
159 {
160 /* Attach (but don't load) the crash handler */
161 SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
162
163 #if _MSC_VER >= 1400
164 /* ### This should work for VC++ 2002 (=1300) and later */
165 /* Show the abort message on STDERR instead of a dialog to allow
166 scripts (e.g. our testsuite) to continue after an abort without
167 user intervention. Allow overriding for easier debugging. */
168 if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
169 {
170 /* In release mode: Redirect abort() errors to stderr */
171 _set_error_mode(_OUT_TO_STDERR);
172
173 /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
174 (Ignored in release builds) */
175 _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
176 _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
177 _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
178 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
179 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
180 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
181 }
182 #endif /* _MSC_VER >= 1400 */
183 }
184 #endif /* SVN_USE_WIN32_CRASHHANDLER */
185
186 #endif /* WIN32 */
187
188 /* C programs default to the "C" locale. But because svn is supposed
189 to be i18n-aware, it should inherit the default locale of its
190 environment. */
191 if (!setlocale(LC_ALL, "")
192 && !setlocale(LC_CTYPE, ""))
193 {
194 if (error_stream)
195 {
196 const char *env_vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL };
197 const char **env_var = &env_vars[0], *env_val = NULL;
198 while (*env_var)
199 {
200 env_val = getenv(*env_var);
201 if (env_val && env_val[0])
202 break;
203 ++env_var;
204 }
205
206 if (!*env_var)
207 {
208 /* Unlikely. Can setlocale fail if no env vars are set? */
209 --env_var;
210 env_val = "not set";
211 }
212
213 fprintf(error_stream,
214 "%s: warning: cannot set LC_CTYPE locale\n"
215 "%s: warning: environment variable %s is %s\n"
216 "%s: warning: please check that your locale name is correct\n",
217 progname, progname, *env_var, env_val, progname);
218 }
219 }
220
221 /* Initialize the APR subsystem, and register an atexit() function
222 to Uninitialize that subsystem at program exit. */
223 status = apr_initialize();
224 if (status)
225 {
226 if (error_stream)
227 {
228 char buf[1024];
229 apr_strerror(status, buf, sizeof(buf) - 1);
230 fprintf(error_stream,
231 "%s: error: cannot initialize APR: %s\n",
232 progname, buf);
233 }
234 return EXIT_FAILURE;
235 }
236
237 strncpy(prefix_buf, progname, sizeof(prefix_buf) - 3);
238 prefix_buf[sizeof(prefix_buf) - 3] = '\0';
239 strcat(prefix_buf, ": ");
240
241 /* DSO pool must be created before any other pools used by the
242 application so that pool cleanup doesn't unload DSOs too
243 early. See docstring of svn_dso_initialize2(). */
244 if ((err = svn_dso_initialize2()))
245 {
246 if (error_stream)
247 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
248
249 svn_error_clear(err);
250 return EXIT_FAILURE;
251 }
252
253 if (0 > atexit(apr_terminate))
254 {
255 if (error_stream)
256 fprintf(error_stream,
257 "%s: error: atexit registration failed\n",
258 progname);
259 return EXIT_FAILURE;
260 }
261
262 /* Create a pool for use by the UTF-8 routines. It will be cleaned
263 up by APR at exit time. */
264 pool = svn_pool_create(NULL);
265 svn_utf_initialize2(FALSE, pool);
266
267 if ((err = svn_nls_init()))
268 {
269 if (error_stream)
270 svn_handle_error2(err, error_stream, TRUE, prefix_buf);
271
272 svn_error_clear(err);
273 return EXIT_FAILURE;
274 }
275
276 #ifdef USE_WIN32_CONSOLE_SHORTCUT
277 if (_isatty(STDOUT_FILENO))
278 {
279 DWORD ignored;
280 HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
281
282 /* stdout is a char device handle, but is it the console? */
283 if (GetConsoleMode(stdout_handle, &ignored))
284 shortcut_stdout_to_console = TRUE;
285
286 /* Don't close stdout_handle */
287 }
288 if (_isatty(STDERR_FILENO))
289 {
290 DWORD ignored;
291 HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
292
293 /* stderr is a char device handle, but is it the console? */
294 if (GetConsoleMode(stderr_handle, &ignored))
295 shortcut_stderr_to_console = TRUE;
296
297 /* Don't close stderr_handle */
298 }
299 #endif
300
301 return EXIT_SUCCESS;
302 }
303
304
305 svn_error_t *
svn_cmdline_cstring_from_utf8(const char ** dest,const char * src,apr_pool_t * pool)306 svn_cmdline_cstring_from_utf8(const char **dest,
307 const char *src,
308 apr_pool_t *pool)
309 {
310 #ifdef CMDLINE_USE_CUSTOM_ENCODING
311 if (output_encoding != NULL)
312 return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
313 #endif
314
315 return svn_utf_cstring_from_utf8(dest, src, pool);
316 }
317
318
319 const char *
svn_cmdline_cstring_from_utf8_fuzzy(const char * src,apr_pool_t * pool)320 svn_cmdline_cstring_from_utf8_fuzzy(const char *src,
321 apr_pool_t *pool)
322 {
323 return svn_utf__cstring_from_utf8_fuzzy(src, pool,
324 svn_cmdline_cstring_from_utf8);
325 }
326
327
328 svn_error_t *
svn_cmdline_cstring_to_utf8(const char ** dest,const char * src,apr_pool_t * pool)329 svn_cmdline_cstring_to_utf8(const char **dest,
330 const char *src,
331 apr_pool_t *pool)
332 {
333 #ifdef CMDLINE_USE_CUSTOM_ENCODING
334 if (input_encoding != NULL)
335 return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
336 #endif
337
338 return svn_utf_cstring_to_utf8(dest, src, pool);
339 }
340
341
342 svn_error_t *
svn_cmdline_path_local_style_from_utf8(const char ** dest,const char * src,apr_pool_t * pool)343 svn_cmdline_path_local_style_from_utf8(const char **dest,
344 const char *src,
345 apr_pool_t *pool)
346 {
347 return svn_cmdline_cstring_from_utf8(dest,
348 svn_dirent_local_style(src, pool),
349 pool);
350 }
351
352 svn_error_t *
svn_cmdline__stdin_readline(const char ** result,apr_pool_t * result_pool,apr_pool_t * scratch_pool)353 svn_cmdline__stdin_readline(const char **result,
354 apr_pool_t *result_pool,
355 apr_pool_t *scratch_pool)
356 {
357 svn_stringbuf_t *buf = NULL;
358 svn_stream_t *stdin_stream = NULL;
359 svn_boolean_t oob = FALSE;
360
361 SVN_ERR(svn_stream_for_stdin2(&stdin_stream, TRUE, scratch_pool));
362 SVN_ERR(svn_stream_readline(stdin_stream, &buf, APR_EOL_STR, &oob, result_pool));
363
364 *result = buf->data;
365
366 return SVN_NO_ERROR;
367 }
368
369 svn_error_t *
svn_cmdline_printf(apr_pool_t * pool,const char * fmt,...)370 svn_cmdline_printf(apr_pool_t *pool, const char *fmt, ...)
371 {
372 const char *message;
373 va_list ap;
374
375 /* A note about encoding issues:
376 * APR uses the execution character set, but here we give it UTF-8 strings,
377 * both the fmt argument and any other string arguments. Since apr_pvsprintf
378 * only cares about and produces ASCII characters, this works under the
379 * assumption that all supported platforms use an execution character set
380 * with ASCII as a subset.
381 */
382
383 va_start(ap, fmt);
384 message = apr_pvsprintf(pool, fmt, ap);
385 va_end(ap);
386
387 return svn_cmdline_fputs(message, stdout, pool);
388 }
389
390 svn_error_t *
svn_cmdline_fprintf(FILE * stream,apr_pool_t * pool,const char * fmt,...)391 svn_cmdline_fprintf(FILE *stream, apr_pool_t *pool, const char *fmt, ...)
392 {
393 const char *message;
394 va_list ap;
395
396 /* See svn_cmdline_printf () for a note about character encoding issues. */
397
398 va_start(ap, fmt);
399 message = apr_pvsprintf(pool, fmt, ap);
400 va_end(ap);
401
402 return svn_cmdline_fputs(message, stream, pool);
403 }
404
405 svn_error_t *
svn_cmdline_fputs(const char * string,FILE * stream,apr_pool_t * pool)406 svn_cmdline_fputs(const char *string, FILE* stream, apr_pool_t *pool)
407 {
408 svn_error_t *err;
409 const char *out;
410
411 #ifdef USE_WIN32_CONSOLE_SHORTCUT
412 /* For legacy reasons the Visual C++ runtime converts output to the console
413 from the native 'ansi' encoding, to unicode, then back to 'ansi' and then
414 onwards to the console which is implemented as unicode.
415
416 For operations like 'svn status -v' this may cause about 70% of the total
417 processing time, with absolutely no gain.
418
419 For this specific scenario this shortcut exists. It has the nice side
420 effect of allowing full unicode output to the console.
421
422 Note that this shortcut is not used when the output is redirected, as in
423 that case the data is put on the pipe/file after the first conversion to
424 ansi. In this case the most expensive conversion is already avoided.
425 */
426 if ((stream == stdout && shortcut_stdout_to_console)
427 || (stream == stderr && shortcut_stderr_to_console))
428 {
429 WCHAR *result;
430
431 if (string[0] == '\0')
432 return SVN_NO_ERROR;
433
434 SVN_ERR(svn_cmdline_fflush(stream)); /* Flush existing output */
435
436 SVN_ERR(svn_utf__win32_utf8_to_utf16(&result, string, NULL, pool));
437
438 if (_cputws(result))
439 {
440 if (apr_get_os_error())
441 {
442 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
443 }
444 }
445
446 return SVN_NO_ERROR;
447 }
448 #endif
449
450 err = svn_cmdline_cstring_from_utf8(&out, string, pool);
451
452 if (err)
453 {
454 svn_error_clear(err);
455 out = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
456 }
457
458 /* On POSIX systems, errno will be set on an error in fputs, but this might
459 not be the case on other platforms. We reset errno and only
460 use it if it was set by the below fputs call. Else, we just return
461 a generic error. */
462 errno = 0;
463
464 if (fputs(out, stream) == EOF)
465 {
466 if (apr_get_os_error()) /* is errno on POSIX */
467 {
468 /* ### Issue #3014: Return a specific error for broken pipes,
469 * ### with a single element in the error chain. */
470 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
471 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
472 else
473 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
474 }
475 else
476 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
477 }
478
479 return SVN_NO_ERROR;
480 }
481
482 svn_error_t *
svn_cmdline_fflush(FILE * stream)483 svn_cmdline_fflush(FILE *stream)
484 {
485 /* See comment in svn_cmdline_fputs about use of errno and stdio. */
486 errno = 0;
487 if (fflush(stream) == EOF)
488 {
489 if (apr_get_os_error()) /* is errno on POSIX */
490 {
491 /* ### Issue #3014: Return a specific error for broken pipes,
492 * ### with a single element in the error chain. */
493 if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
494 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
495 else
496 return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
497 }
498 else
499 return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
500 }
501
502 return SVN_NO_ERROR;
503 }
504
svn_cmdline_output_encoding(apr_pool_t * pool)505 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
506 {
507 #ifdef CMDLINE_USE_CUSTOM_ENCODING
508 if (output_encoding)
509 return apr_pstrdup(pool, output_encoding);
510 #endif
511
512 return SVN_APR_LOCALE_CHARSET;
513 }
514
515 int
svn_cmdline_handle_exit_error(svn_error_t * err,apr_pool_t * pool,const char * prefix)516 svn_cmdline_handle_exit_error(svn_error_t *err,
517 apr_pool_t *pool,
518 const char *prefix)
519 {
520 /* Issue #3014:
521 * Don't print anything on broken pipes. The pipe was likely
522 * closed by the process at the other end. We expect that
523 * process to perform error reporting as necessary.
524 *
525 * ### This assumes that there is only one error in a chain for
526 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
527 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
528 svn_handle_error2(err, stderr, FALSE, prefix);
529 svn_error_clear(err);
530 if (pool)
531 svn_pool_destroy(pool);
532 return EXIT_FAILURE;
533 }
534
535 struct trust_server_cert_non_interactive_baton {
536 svn_boolean_t trust_server_cert_unknown_ca;
537 svn_boolean_t trust_server_cert_cn_mismatch;
538 svn_boolean_t trust_server_cert_expired;
539 svn_boolean_t trust_server_cert_not_yet_valid;
540 svn_boolean_t trust_server_cert_other_failure;
541 };
542
543 /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'.
544
545 Don't actually prompt. Instead, set *CRED_P to valid credentials
546 iff FAILURES is empty or may be accepted according to the flags
547 in BATON. If there are any other failure bits, then set *CRED_P
548 to null (that is, reject the cert).
549
550 Ignore MAY_SAVE; we don't save certs we never prompted for.
551
552 Ignore REALM and CERT_INFO,
553
554 Ignore any further films by George Lucas. */
555 static svn_error_t *
trust_server_cert_non_interactive(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)556 trust_server_cert_non_interactive(svn_auth_cred_ssl_server_trust_t **cred_p,
557 void *baton,
558 const char *realm,
559 apr_uint32_t failures,
560 const svn_auth_ssl_server_cert_info_t
561 *cert_info,
562 svn_boolean_t may_save,
563 apr_pool_t *pool)
564 {
565 struct trust_server_cert_non_interactive_baton *b = baton;
566 apr_uint32_t non_ignored_failures;
567 *cred_p = NULL;
568
569 /* Mask away bits we are instructed to ignore. */
570 non_ignored_failures = failures & ~(
571 (b->trust_server_cert_unknown_ca ? SVN_AUTH_SSL_UNKNOWNCA : 0)
572 | (b->trust_server_cert_cn_mismatch ? SVN_AUTH_SSL_CNMISMATCH : 0)
573 | (b->trust_server_cert_expired ? SVN_AUTH_SSL_EXPIRED : 0)
574 | (b->trust_server_cert_not_yet_valid ? SVN_AUTH_SSL_NOTYETVALID : 0)
575 | (b->trust_server_cert_other_failure ? SVN_AUTH_SSL_OTHER : 0)
576 );
577
578 /* If no failures remain, accept the certificate. */
579 if (non_ignored_failures == 0)
580 {
581 *cred_p = apr_pcalloc(pool, sizeof(**cred_p));
582 (*cred_p)->may_save = FALSE;
583 (*cred_p)->accepted_failures = failures;
584 }
585
586 return SVN_NO_ERROR;
587 }
588
589 svn_error_t *
svn_cmdline_create_auth_baton2(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_unknown_ca,svn_boolean_t trust_server_cert_cn_mismatch,svn_boolean_t trust_server_cert_expired,svn_boolean_t trust_server_cert_not_yet_valid,svn_boolean_t trust_server_cert_other_failure,svn_config_t * cfg,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)590 svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab,
591 svn_boolean_t non_interactive,
592 const char *auth_username,
593 const char *auth_password,
594 const char *config_dir,
595 svn_boolean_t no_auth_cache,
596 svn_boolean_t trust_server_cert_unknown_ca,
597 svn_boolean_t trust_server_cert_cn_mismatch,
598 svn_boolean_t trust_server_cert_expired,
599 svn_boolean_t trust_server_cert_not_yet_valid,
600 svn_boolean_t trust_server_cert_other_failure,
601 svn_config_t *cfg,
602 svn_cancel_func_t cancel_func,
603 void *cancel_baton,
604 apr_pool_t *pool)
605
606 {
607 svn_boolean_t store_password_val = TRUE;
608 svn_boolean_t store_auth_creds_val = TRUE;
609 svn_auth_provider_object_t *provider;
610 svn_cmdline_prompt_baton2_t *pb = NULL;
611
612 /* The whole list of registered providers */
613 apr_array_header_t *providers;
614
615 /* Populate the registered providers with the platform-specific providers */
616 SVN_ERR(svn_auth_get_platform_specific_client_providers(&providers,
617 cfg, pool));
618
619 /* If we have a cancellation function, cram it and the stuff it
620 needs into the prompt baton. */
621 if (cancel_func)
622 {
623 pb = apr_palloc(pool, sizeof(*pb));
624 pb->cancel_func = cancel_func;
625 pb->cancel_baton = cancel_baton;
626 pb->config_dir = config_dir;
627 }
628
629 if (!non_interactive)
630 {
631 /* This provider doesn't prompt the user in order to get creds;
632 it prompts the user regarding the caching of creds. */
633 svn_auth_get_simple_provider2(&provider,
634 svn_cmdline_auth_plaintext_prompt,
635 pb, pool);
636 }
637 else
638 {
639 svn_auth_get_simple_provider2(&provider, NULL, NULL, pool);
640 }
641
642 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
643 svn_auth_get_username_provider(&provider, pool);
644 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
645
646 svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
647 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
648 svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
649 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
650
651 if (!non_interactive)
652 {
653 /* This provider doesn't prompt the user in order to get creds;
654 it prompts the user regarding the caching of creds. */
655 svn_auth_get_ssl_client_cert_pw_file_provider2
656 (&provider, svn_cmdline_auth_plaintext_passphrase_prompt,
657 pb, pool);
658 }
659 else
660 {
661 svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, NULL, NULL,
662 pool);
663 }
664 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
665
666 if (!non_interactive)
667 {
668 svn_boolean_t ssl_client_cert_file_prompt;
669
670 SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
671 SVN_CONFIG_SECTION_AUTH,
672 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
673 FALSE));
674
675 /* Two basic prompt providers: username/password, and just username. */
676 svn_auth_get_simple_prompt_provider(&provider,
677 svn_cmdline_auth_simple_prompt,
678 pb,
679 2, /* retry limit */
680 pool);
681 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
682
683 svn_auth_get_username_prompt_provider
684 (&provider, svn_cmdline_auth_username_prompt, pb,
685 2, /* retry limit */ pool);
686 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
687
688 /* SSL prompt providers: server-certs and client-cert-passphrases. */
689 svn_auth_get_ssl_server_trust_prompt_provider
690 (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
691 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
692
693 svn_auth_get_ssl_client_cert_pw_prompt_provider
694 (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
695 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
696
697 /* If configuration allows, add a provider for client-cert path
698 prompting, too. */
699 if (ssl_client_cert_file_prompt)
700 {
701 svn_auth_get_ssl_client_cert_prompt_provider
702 (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
703 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
704 }
705 }
706 else if (trust_server_cert_unknown_ca || trust_server_cert_cn_mismatch ||
707 trust_server_cert_expired || trust_server_cert_not_yet_valid ||
708 trust_server_cert_other_failure)
709 {
710 struct trust_server_cert_non_interactive_baton *b;
711
712 b = apr_palloc(pool, sizeof(*b));
713 b->trust_server_cert_unknown_ca = trust_server_cert_unknown_ca;
714 b->trust_server_cert_cn_mismatch = trust_server_cert_cn_mismatch;
715 b->trust_server_cert_expired = trust_server_cert_expired;
716 b->trust_server_cert_not_yet_valid = trust_server_cert_not_yet_valid;
717 b->trust_server_cert_other_failure = trust_server_cert_other_failure;
718
719 /* Remember, only register this provider if non_interactive. */
720 svn_auth_get_ssl_server_trust_prompt_provider
721 (&provider, trust_server_cert_non_interactive, b, pool);
722 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
723 }
724
725 /* Build an authentication baton to give to libsvn_client. */
726 svn_auth_open(ab, providers, pool);
727
728 /* Place any default --username or --password credentials into the
729 auth_baton's run-time parameter hash. */
730 if (auth_username)
731 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
732 auth_username);
733 if (auth_password)
734 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
735 auth_password);
736
737 /* Same with the --non-interactive option. */
738 if (non_interactive)
739 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, "");
740
741 if (config_dir)
742 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR,
743 config_dir);
744
745 /* Determine whether storing passwords in any form is allowed.
746 * This is the deprecated location for this option, the new
747 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
748 * override the value we set here. */
749 SVN_ERR(svn_config_get_bool(cfg, &store_password_val,
750 SVN_CONFIG_SECTION_AUTH,
751 SVN_CONFIG_OPTION_STORE_PASSWORDS,
752 SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS));
753
754 if (! store_password_val)
755 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
756
757 /* Determine whether we are allowed to write to the auth/ area.
758 * This is the deprecated location for this option, the new
759 * location is SVN_CONFIG_CATEGORY_SERVERS. The RA layer may
760 * override the value we set here. */
761 SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds_val,
762 SVN_CONFIG_SECTION_AUTH,
763 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
764 SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS));
765
766 if (no_auth_cache || ! store_auth_creds_val)
767 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
768
769 #ifdef SVN_HAVE_GNOME_KEYRING
770 svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC,
771 &svn_cmdline__auth_gnome_keyring_unlock_prompt);
772 #endif /* SVN_HAVE_GNOME_KEYRING */
773
774 return SVN_NO_ERROR;
775 }
776
777 svn_error_t *
svn_cmdline__getopt_init(apr_getopt_t ** os,int argc,const char * argv[],apr_pool_t * pool)778 svn_cmdline__getopt_init(apr_getopt_t **os,
779 int argc,
780 const char *argv[],
781 apr_pool_t *pool)
782 {
783 apr_status_t apr_err = apr_getopt_init(os, pool, argc, argv);
784 if (apr_err)
785 return svn_error_wrap_apr(apr_err,
786 _("Error initializing command line arguments"));
787 return SVN_NO_ERROR;
788 }
789
790
791 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)792 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
793 const char* propname,
794 svn_string_t *propval,
795 svn_boolean_t inherited_prop,
796 apr_pool_t *pool)
797 {
798 const char *xml_safe;
799 const char *encoding = NULL;
800
801 if (*outstr == NULL)
802 *outstr = svn_stringbuf_create_empty(pool);
803
804 if (svn_xml_is_xml_safe(propval->data, propval->len))
805 {
806 svn_stringbuf_t *xml_esc = NULL;
807 svn_xml_escape_cdata_string(&xml_esc, propval, pool);
808 xml_safe = xml_esc->data;
809 }
810 else
811 {
812 const svn_string_t *base64ed = svn_base64_encode_string2(propval, TRUE,
813 pool);
814 encoding = "base64";
815 xml_safe = base64ed->data;
816 }
817
818 if (encoding)
819 svn_xml_make_open_tag(
820 outstr, pool, svn_xml_protect_pcdata,
821 inherited_prop ? "inherited_property" : "property",
822 "name", propname,
823 "encoding", encoding, SVN_VA_NULL);
824 else
825 svn_xml_make_open_tag(
826 outstr, pool, svn_xml_protect_pcdata,
827 inherited_prop ? "inherited_property" : "property",
828 "name", propname, SVN_VA_NULL);
829
830 svn_stringbuf_appendcstr(*outstr, xml_safe);
831
832 svn_xml_make_close_tag(
833 outstr, pool,
834 inherited_prop ? "inherited_property" : "property");
835
836 return;
837 }
838
839 /* Return the most similar string to NEEDLE in HAYSTACK, which contains
840 * HAYSTACK_LEN elements. Return NULL if no string is sufficiently similar.
841 */
842 /* See svn_cl__similarity_check() for a more general solution. */
843 static const char *
most_similar(const char * needle_cstr,const char ** haystack,apr_size_t haystack_len,apr_pool_t * scratch_pool)844 most_similar(const char *needle_cstr,
845 const char **haystack,
846 apr_size_t haystack_len,
847 apr_pool_t *scratch_pool)
848 {
849 const char *max_similar = NULL;
850 apr_size_t max_score = 0;
851 apr_size_t i;
852 svn_membuf_t membuf;
853 svn_string_t *needle_str = svn_string_create(needle_cstr, scratch_pool);
854
855 svn_membuf__create(&membuf, 64, scratch_pool);
856
857 for (i = 0; i < haystack_len; i++)
858 {
859 apr_size_t score;
860 svn_string_t *hay = svn_string_create(haystack[i], scratch_pool);
861
862 score = svn_string__similarity(needle_str, hay, &membuf, NULL);
863
864 /* If you update this factor, consider updating
865 * svn_cl__similarity_check(). */
866 if (score >= (2 * SVN_STRING__SIM_RANGE_MAX + 1) / 3
867 && score > max_score)
868 {
869 max_score = score;
870 max_similar = haystack[i];
871 }
872 }
873
874 return max_similar;
875 }
876
877 /* Verify that NEEDLE is in HAYSTACK, which contains HAYSTACK_LEN elements. */
878 static svn_error_t *
string_in_array(const char * needle,const char ** haystack,apr_size_t haystack_len,apr_pool_t * scratch_pool)879 string_in_array(const char *needle,
880 const char **haystack,
881 apr_size_t haystack_len,
882 apr_pool_t *scratch_pool)
883 {
884 const char *next_of_kin;
885 apr_size_t i;
886 for (i = 0; i < haystack_len; i++)
887 {
888 if (!strcmp(needle, haystack[i]))
889 return SVN_NO_ERROR;
890 }
891
892 /* Error. */
893 next_of_kin = most_similar(needle, haystack, haystack_len, scratch_pool);
894 if (next_of_kin)
895 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
896 _("Ignoring unknown value '%s'; "
897 "did you mean '%s'?"),
898 needle, next_of_kin);
899 else
900 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
901 _("Ignoring unknown value '%s'"),
902 needle);
903 }
904
905 #include "config_keys.inc"
906
907 /* Validate the FILE, SECTION, and OPTION components of CONFIG_OPTION are
908 * known. Return an error if not. (An unknown value may be either a typo
909 * or added in a newer minor version of Subversion.) */
910 static svn_error_t *
validate_config_option(svn_cmdline__config_argument_t * config_option,apr_pool_t * scratch_pool)911 validate_config_option(svn_cmdline__config_argument_t *config_option,
912 apr_pool_t *scratch_pool)
913 {
914 svn_boolean_t arbitrary_keys = FALSE;
915
916 /* TODO: some day, we could also verify that OPTION is valid for SECTION;
917 i.e., forbid invalid combinations such as config:auth:diff-extensions. */
918
919 #define ARRAYLEN(x) ( sizeof((x)) / sizeof((x)[0]) )
920
921 SVN_ERR(string_in_array(config_option->file, svn__valid_config_files,
922 ARRAYLEN(svn__valid_config_files),
923 scratch_pool));
924 SVN_ERR(string_in_array(config_option->section, svn__valid_config_sections,
925 ARRAYLEN(svn__valid_config_sections),
926 scratch_pool));
927
928 /* Don't validate option names for sections such as servers[group],
929 * config[tunnels], and config[auto-props] that permit arbitrary options. */
930 {
931 int i;
932
933 for (i = 0; i < ARRAYLEN(svn__empty_config_sections); i++)
934 {
935 if (!strcmp(config_option->section, svn__empty_config_sections[i]))
936 arbitrary_keys = TRUE;
937 }
938 }
939
940 if (! arbitrary_keys)
941 SVN_ERR(string_in_array(config_option->option, svn__valid_config_options,
942 ARRAYLEN(svn__valid_config_options),
943 scratch_pool));
944
945 #undef ARRAYLEN
946
947 return SVN_NO_ERROR;
948 }
949
950 svn_error_t *
svn_cmdline__parse_config_option(apr_array_header_t * config_options,const char * opt_arg,const char * prefix,apr_pool_t * pool)951 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
952 const char *opt_arg,
953 const char *prefix,
954 apr_pool_t *pool)
955 {
956 svn_cmdline__config_argument_t *config_option;
957 const char *first_colon, *second_colon, *equals_sign;
958 apr_size_t len = strlen(opt_arg);
959 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
960 {
961 if ((second_colon = strchr(first_colon + 1, ':')) &&
962 (second_colon != first_colon + 1))
963 {
964 if ((equals_sign = strchr(second_colon + 1, '=')) &&
965 (equals_sign != second_colon + 1))
966 {
967 svn_error_t *warning;
968
969 config_option = apr_pcalloc(pool, sizeof(*config_option));
970 config_option->file = apr_pstrndup(pool, opt_arg,
971 first_colon - opt_arg);
972 config_option->section = apr_pstrndup(pool, first_colon + 1,
973 second_colon - first_colon - 1);
974 config_option->option = apr_pstrndup(pool, second_colon + 1,
975 equals_sign - second_colon - 1);
976
977 warning = validate_config_option(config_option, pool);
978 if (warning)
979 {
980 svn_handle_warning2(stderr, warning, prefix);
981 svn_error_clear(warning);
982 }
983
984 if (! (strchr(config_option->option, ':')))
985 {
986 config_option->value = apr_pstrndup(pool, equals_sign + 1,
987 opt_arg + len - equals_sign - 1);
988 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
989 = config_option;
990 return SVN_NO_ERROR;
991 }
992 }
993 }
994 }
995 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
996 _("Invalid syntax of argument of --config-option"));
997 }
998
999 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)1000 svn_cmdline__apply_config_options(apr_hash_t *config,
1001 const apr_array_header_t *config_options,
1002 const char *prefix,
1003 const char *argument_name)
1004 {
1005 int i;
1006
1007 for (i = 0; i < config_options->nelts; i++)
1008 {
1009 svn_config_t *cfg;
1010 svn_cmdline__config_argument_t *arg =
1011 APR_ARRAY_IDX(config_options, i,
1012 svn_cmdline__config_argument_t *);
1013
1014 cfg = svn_hash_gets(config, arg->file);
1015
1016 if (cfg)
1017 {
1018 svn_config_set(cfg, arg->section, arg->option, arg->value);
1019 }
1020 else
1021 {
1022 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1023 _("Unrecognized file in argument of %s"), argument_name);
1024
1025 svn_handle_warning2(stderr, err, prefix);
1026 svn_error_clear(err);
1027 }
1028 }
1029
1030 return SVN_NO_ERROR;
1031 }
1032
1033 /* Return a copy, allocated in POOL, of the next line of text from *STR
1034 * up to and including a CR and/or an LF. Change *STR to point to the
1035 * remainder of the string after the returned part. If there are no
1036 * characters to be returned, return NULL; never return an empty string.
1037 */
1038 static const char *
next_line(const char ** str,apr_pool_t * pool)1039 next_line(const char **str, apr_pool_t *pool)
1040 {
1041 const char *start = *str;
1042 const char *p = *str;
1043
1044 /* n.b. Throughout this fn, we never read any character after a '\0'. */
1045 /* Skip over all non-EOL characters, if any. */
1046 while (*p != '\r' && *p != '\n' && *p != '\0')
1047 p++;
1048 /* Skip over \r\n or \n\r or \r or \n, if any. */
1049 if (*p == '\r' || *p == '\n')
1050 {
1051 char c = *p++;
1052
1053 if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
1054 p++;
1055 }
1056
1057 /* Now p points after at most one '\n' and/or '\r'. */
1058 *str = p;
1059
1060 if (p == start)
1061 return NULL;
1062
1063 return svn_string_ncreate(start, p - start, pool)->data;
1064 }
1065
1066 const char *
svn_cmdline__indent_string(const char * str,const char * indent,apr_pool_t * pool)1067 svn_cmdline__indent_string(const char *str,
1068 const char *indent,
1069 apr_pool_t *pool)
1070 {
1071 svn_stringbuf_t *out = svn_stringbuf_create_empty(pool);
1072 const char *line;
1073
1074 while ((line = next_line(&str, pool)))
1075 {
1076 svn_stringbuf_appendcstr(out, indent);
1077 svn_stringbuf_appendcstr(out, line);
1078 }
1079 return out->data;
1080 }
1081
1082 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)1083 svn_cmdline__print_prop_hash(svn_stream_t *out,
1084 apr_hash_t *prop_hash,
1085 svn_boolean_t names_only,
1086 apr_pool_t *pool)
1087 {
1088 apr_array_header_t *sorted_props;
1089 int i;
1090
1091 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1092 pool);
1093 for (i = 0; i < sorted_props->nelts; i++)
1094 {
1095 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1096 const char *pname = item.key;
1097 svn_string_t *propval = item.value;
1098 const char *pname_stdout;
1099
1100 if (svn_prop_needs_translation(pname))
1101 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1102 TRUE, pool));
1103
1104 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
1105
1106 if (out)
1107 {
1108 pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
1109 SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
1110 APR_EOL_STR, /* 'native' eol */
1111 FALSE, /* no repair */
1112 NULL, /* no keywords */
1113 FALSE, /* no expansion */
1114 pool));
1115
1116 SVN_ERR(svn_stream_puts(out, pname_stdout));
1117 }
1118 else
1119 {
1120 /* ### We leave these printfs for now, since if propval wasn't
1121 translated above, we don't know anything about its encoding.
1122 In fact, it might be binary data... */
1123 printf(" %s\n", pname_stdout);
1124 }
1125
1126 if (!names_only)
1127 {
1128 /* Add an extra newline to the value before indenting, so that
1129 * every line of output has the indentation whether the value
1130 * already ended in a newline or not. */
1131 const char *newval = apr_psprintf(pool, "%s\n", propval->data);
1132 const char *indented_newval = svn_cmdline__indent_string(newval,
1133 " ",
1134 pool);
1135 if (out)
1136 {
1137 SVN_ERR(svn_stream_puts(out, indented_newval));
1138 }
1139 else
1140 {
1141 printf("%s", indented_newval);
1142 }
1143 }
1144 }
1145
1146 return SVN_NO_ERROR;
1147 }
1148
1149 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)1150 svn_cmdline__print_xml_prop_hash(svn_stringbuf_t **outstr,
1151 apr_hash_t *prop_hash,
1152 svn_boolean_t names_only,
1153 svn_boolean_t inherited_props,
1154 apr_pool_t *pool)
1155 {
1156 apr_array_header_t *sorted_props;
1157 int i;
1158
1159 if (*outstr == NULL)
1160 *outstr = svn_stringbuf_create_empty(pool);
1161
1162 sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
1163 pool);
1164 for (i = 0; i < sorted_props->nelts; i++)
1165 {
1166 svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
1167 const char *pname = item.key;
1168 svn_string_t *propval = item.value;
1169
1170 if (names_only)
1171 {
1172 svn_xml_make_open_tag(
1173 outstr, pool, svn_xml_self_closing,
1174 inherited_props ? "inherited_property" : "property",
1175 "name", pname, SVN_VA_NULL);
1176 }
1177 else
1178 {
1179 const char *pname_out;
1180
1181 if (svn_prop_needs_translation(pname))
1182 SVN_ERR(svn_subst_detranslate_string(&propval, propval,
1183 TRUE, pool));
1184
1185 SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
1186
1187 svn_cmdline__print_xml_prop(outstr, pname_out, propval,
1188 inherited_props, pool);
1189 }
1190 }
1191
1192 return SVN_NO_ERROR;
1193 }
1194
1195 svn_boolean_t
svn_cmdline__stdin_is_a_terminal(void)1196 svn_cmdline__stdin_is_a_terminal(void)
1197 {
1198 #ifdef WIN32
1199 return (_isatty(STDIN_FILENO) != 0);
1200 #else
1201 return (isatty(STDIN_FILENO) != 0);
1202 #endif
1203 }
1204
1205 svn_boolean_t
svn_cmdline__stdout_is_a_terminal(void)1206 svn_cmdline__stdout_is_a_terminal(void)
1207 {
1208 #ifdef WIN32
1209 return (_isatty(STDOUT_FILENO) != 0);
1210 #else
1211 return (isatty(STDOUT_FILENO) != 0);
1212 #endif
1213 }
1214
1215 svn_boolean_t
svn_cmdline__stderr_is_a_terminal(void)1216 svn_cmdline__stderr_is_a_terminal(void)
1217 {
1218 #ifdef WIN32
1219 return (_isatty(STDERR_FILENO) != 0);
1220 #else
1221 return (isatty(STDERR_FILENO) != 0);
1222 #endif
1223 }
1224
1225 svn_boolean_t
svn_cmdline__be_interactive(svn_boolean_t non_interactive,svn_boolean_t force_interactive)1226 svn_cmdline__be_interactive(svn_boolean_t non_interactive,
1227 svn_boolean_t force_interactive)
1228 {
1229 /* If neither --non-interactive nor --force-interactive was passed,
1230 * be interactive if stdin is a terminal.
1231 * If --force-interactive was passed, always be interactive. */
1232 if (!force_interactive && !non_interactive)
1233 {
1234 return svn_cmdline__stdin_is_a_terminal();
1235 }
1236 else if (force_interactive)
1237 return TRUE;
1238
1239 return !non_interactive;
1240 }
1241
1242
1243 /* Helper for the edit_externally functions. Set *EDITOR to some path to an
1244 editor binary, in native C string on Unix/Linux platforms and in UTF-8
1245 string on Windows platform. Sources to search include: the EDITOR_CMD
1246 argument (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
1247 is not NULL), $VISUAL, $EDITOR. Return
1248 SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
1249 static svn_error_t *
find_editor_binary(const char ** editor,const char * editor_cmd,apr_hash_t * config,apr_pool_t * pool)1250 find_editor_binary(const char **editor,
1251 const char *editor_cmd,
1252 apr_hash_t *config,
1253 apr_pool_t *pool)
1254 {
1255 const char *e;
1256 const char *e_cfg;
1257 struct svn_config_t *cfg;
1258 apr_status_t status;
1259
1260 /* Use the editor specified on the command line via --editor-cmd, if any. */
1261 #ifdef WIN32
1262 /* On Windows, editor_cmd is transcoded to the system active code page
1263 because we use main() as a entry point without APR's (or our own) wrapper
1264 in command line tools. */
1265 if (editor_cmd)
1266 {
1267 SVN_ERR(svn_utf_cstring_to_utf8(&e, editor_cmd, pool));
1268 }
1269 else
1270 {
1271 e = NULL;
1272 }
1273 #else
1274 e = editor_cmd;
1275 #endif
1276
1277 /* Otherwise look for the Subversion-specific environment variable. */
1278 if (! e)
1279 {
1280 status = apr_env_get((char **)&e, "SVN_EDITOR", pool);
1281 if (status || ! *e)
1282 {
1283 e = NULL;
1284 }
1285 }
1286
1287 /* If not found then fall back on the config file. */
1288 if (! e)
1289 {
1290 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
1291 svn_config_get(cfg, &e_cfg, SVN_CONFIG_SECTION_HELPERS,
1292 SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
1293 #ifdef WIN32
1294 if (e_cfg)
1295 {
1296 /* On Windows, we assume that config values are set in system active
1297 code page, so we need transcode it here. */
1298 SVN_ERR(svn_utf_cstring_to_utf8(&e, e_cfg, pool));
1299 }
1300 #else
1301 e = e_cfg;
1302 #endif
1303 }
1304
1305 /* If not found yet then try general purpose environment variables. */
1306 if (! e)
1307 {
1308 status = apr_env_get((char**)&e, "VISUAL", pool);
1309 if (status || ! *e)
1310 {
1311 e = NULL;
1312 }
1313 }
1314 if (! e)
1315 {
1316 status = apr_env_get((char**)&e, "EDITOR", pool);
1317 if (status || ! *e)
1318 {
1319 e = NULL;
1320 }
1321 }
1322
1323 #ifdef SVN_CLIENT_EDITOR
1324 /* If still not found then fall back on the hard-coded default. */
1325 if (! e)
1326 e = SVN_CLIENT_EDITOR;
1327 #endif
1328
1329 /* Error if there is no editor specified */
1330 if (e)
1331 {
1332 const char *c;
1333
1334 for (c = e; *c; c++)
1335 if (!svn_ctype_isspace(*c))
1336 break;
1337
1338 if (! *c)
1339 return svn_error_create
1340 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1341 _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
1342 "'editor-cmd' run-time configuration option is empty or "
1343 "consists solely of whitespace. Expected a shell command."));
1344 }
1345 else
1346 return svn_error_create
1347 (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
1348 _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
1349 "set, and no 'editor-cmd' run-time configuration option was found"));
1350
1351 *editor = e;
1352 return SVN_NO_ERROR;
1353 }
1354
1355 /* Wrapper around apr_pescape_shell() which also escapes whitespace. */
1356 static const char *
escape_path(apr_pool_t * pool,const char * orig_path)1357 escape_path(apr_pool_t *pool, const char *orig_path)
1358 {
1359 apr_size_t len, esc_len;
1360 apr_status_t status;
1361
1362 len = strlen(orig_path);
1363 esc_len = 0;
1364
1365 status = apr_escape_shell(NULL, orig_path, len, &esc_len);
1366
1367 if (status == APR_NOTFOUND)
1368 {
1369 /* No special characters found by APR, so just surround it in double
1370 quotes in case there is whitespace, which APR (as of 1.6.5) doesn't
1371 consider special. */
1372 return apr_psprintf(pool, "\"%s\"", orig_path);
1373 }
1374 else
1375 {
1376 #ifdef WIN32
1377 const char *p;
1378 /* Following the advice from
1379 https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
1380 1. Surround argument with double-quotes
1381 2. Escape backslashes, if they're followed by a double-quote, and double-quotes
1382 3. Escape any metacharacter, including double-quotes, with ^ */
1383
1384 /* Use APR's buffer size as an approximation for how large the escaped
1385 string should be, plus 4 bytes for the leading/trailing ^" */
1386 svn_stringbuf_t *buf = svn_stringbuf_create_ensure(esc_len + 4, pool);
1387 svn_stringbuf_appendcstr(buf, "^\"");
1388 for (p = orig_path; *p; p++)
1389 {
1390 int nr_backslash = 0;
1391 while (*p && *p == '\\')
1392 {
1393 nr_backslash++;
1394 p++;
1395 }
1396
1397 if (!*p)
1398 /* We've reached the end of the argument, so we need 2n backslash
1399 characters. That will be interpreted as n backslashes and the
1400 final double-quote character will be interpreted as the final
1401 string delimiter. */
1402 svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2);
1403 else if (*p == '"')
1404 {
1405 /* Double-quote as part of the argument means we need to double
1406 any preceeding backslashes and then add one to escape the
1407 double-quote. */
1408 svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2 + 1);
1409 svn_stringbuf_appendbyte(buf, '^');
1410 svn_stringbuf_appendbyte(buf, *p);
1411 }
1412 else
1413 {
1414 /* Since there's no double-quote, we just insert any backslashes
1415 literally. No escaping needed. */
1416 svn_stringbuf_appendfill(buf, '\\', nr_backslash);
1417 if (strchr("()%!^<>&|", *p))
1418 svn_stringbuf_appendbyte(buf, '^');
1419 svn_stringbuf_appendbyte(buf, *p);
1420 }
1421 }
1422 svn_stringbuf_appendcstr(buf, "^\"");
1423 return buf->data;
1424 #else
1425 char *path, *p, *esc_path;
1426
1427 /* Account for whitespace, since APR doesn't */
1428 for (p = (char *)orig_path; *p; p++)
1429 if (strchr(" \t\n\r", *p))
1430 esc_len++;
1431
1432 path = apr_pcalloc(pool, esc_len);
1433 apr_escape_shell(path, orig_path, len, NULL);
1434
1435 p = esc_path = apr_pcalloc(pool, len + esc_len + 1);
1436 while (*path)
1437 {
1438 if (strchr(" \t\n\r", *path))
1439 *p++ = '\\';
1440 *p++ = *path++;
1441 }
1442
1443 return esc_path;
1444 #endif
1445 }
1446 }
1447
1448 svn_error_t *
svn_cmdline__edit_file_externally(const char * path,const char * editor_cmd,apr_hash_t * config,apr_pool_t * pool)1449 svn_cmdline__edit_file_externally(const char *path,
1450 const char *editor_cmd,
1451 apr_hash_t *config,
1452 apr_pool_t *pool)
1453 {
1454 const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
1455 const char *file_name_local;
1456 #ifdef WIN32
1457 const WCHAR *wcmd;
1458 #endif
1459 char *old_cwd;
1460 int sys_err;
1461 apr_status_t apr_err;
1462
1463 svn_dirent_split(&base_dir, &file_name, path, pool);
1464
1465 SVN_ERR(find_editor_binary(&editor, editor_cmd, config, pool));
1466
1467 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1468 if (apr_err)
1469 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1470
1471 /* APR doesn't like "" directories */
1472 if (base_dir[0] == '\0')
1473 base_dir_apr = ".";
1474 else
1475 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1476
1477 apr_err = apr_filepath_set(base_dir_apr, pool);
1478 if (apr_err)
1479 return svn_error_wrap_apr
1480 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1481
1482 SVN_ERR(svn_path_cstring_from_utf8(&file_name_local,
1483 escape_path(pool, file_name), pool));
1484 /* editor is explicitly documented as being interpreted by the user's shell,
1485 and as such should already be quoted/escaped as needed. */
1486 #ifndef WIN32
1487 cmd = apr_psprintf(pool, "%s %s", editor, file_name_local);
1488 sys_err = system(cmd);
1489 #else
1490 cmd = apr_psprintf(pool, "\"%s %s\"", editor, file_name_local);
1491 SVN_ERR(svn_utf__win32_utf8_to_utf16(&wcmd, cmd, NULL, pool));
1492 sys_err = _wsystem(wcmd);
1493 #endif
1494
1495 apr_err = apr_filepath_set(old_cwd, pool);
1496 if (apr_err)
1497 svn_handle_error2(svn_error_wrap_apr
1498 (apr_err, _("Can't restore working directory")),
1499 stderr, TRUE /* fatal */, "svn: ");
1500
1501 if (sys_err)
1502 /* Extracting any meaning from sys_err is platform specific, so just
1503 use the raw value. */
1504 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1505 _("system('%s') returned %d"), cmd, sys_err);
1506
1507 return SVN_NO_ERROR;
1508 }
1509
1510
1511 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)1512 svn_cmdline__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
1513 const char **tmpfile_left /* UTF-8! */,
1514 const char *editor_cmd,
1515 const char *base_dir /* UTF-8! */,
1516 const svn_string_t *contents /* UTF-8! */,
1517 const char *filename,
1518 apr_hash_t *config,
1519 svn_boolean_t as_text,
1520 const char *encoding,
1521 apr_pool_t *pool)
1522 {
1523 const char *editor;
1524 const char *cmd;
1525 #ifdef WIN32
1526 const WCHAR *wcmd;
1527 #endif
1528 apr_file_t *tmp_file;
1529 const char *tmpfile_name;
1530 const char *tmpfile_native;
1531 const char *base_dir_apr;
1532 svn_string_t *translated_contents;
1533 apr_status_t apr_err;
1534 apr_size_t written;
1535 apr_finfo_t finfo_before, finfo_after;
1536 svn_error_t *err = SVN_NO_ERROR;
1537 char *old_cwd;
1538 int sys_err;
1539 svn_boolean_t remove_file = TRUE;
1540
1541 SVN_ERR(find_editor_binary(&editor, editor_cmd, config, pool));
1542
1543 /* Convert file contents from UTF-8/LF if desired. */
1544 if (as_text)
1545 {
1546 const char *translated;
1547 SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
1548 APR_EOL_STR, FALSE,
1549 NULL, FALSE, pool));
1550 translated_contents = svn_string_create_empty(pool);
1551 if (encoding)
1552 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
1553 translated, encoding, pool));
1554 else
1555 SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
1556 translated, pool));
1557 translated_contents->len = strlen(translated_contents->data);
1558 }
1559 else
1560 translated_contents = svn_string_dup(contents, pool);
1561
1562 /* Move to BASE_DIR to avoid getting characters that need quoting
1563 into tmpfile_name */
1564 apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
1565 if (apr_err)
1566 return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
1567
1568 /* APR doesn't like "" directories */
1569 if (base_dir[0] == '\0')
1570 base_dir_apr = ".";
1571 else
1572 SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
1573 apr_err = apr_filepath_set(base_dir_apr, pool);
1574 if (apr_err)
1575 {
1576 return svn_error_wrap_apr
1577 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1578 }
1579
1580 /*** From here on, any problems that occur require us to cd back!! ***/
1581
1582 /* Ask the working copy for a temporary file named FILENAME-something. */
1583 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1584 "" /* dirpath */,
1585 filename,
1586 ".tmp",
1587 svn_io_file_del_none, pool, pool);
1588
1589 if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
1590 {
1591 const char *temp_dir_apr;
1592
1593 svn_error_clear(err);
1594
1595 SVN_ERR(svn_io_temp_dir(&base_dir, pool));
1596
1597 SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
1598 apr_err = apr_filepath_set(temp_dir_apr, pool);
1599 if (apr_err)
1600 {
1601 return svn_error_wrap_apr
1602 (apr_err, _("Can't change working directory to '%s'"), base_dir);
1603 }
1604
1605 err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
1606 "" /* dirpath */,
1607 filename,
1608 ".tmp",
1609 svn_io_file_del_none, pool, pool);
1610 }
1611
1612 if (err)
1613 goto cleanup2;
1614
1615 /*** From here on, any problems that occur require us to cleanup
1616 the file we just created!! ***/
1617
1618 /* Dump initial CONTENTS to TMP_FILE. */
1619 err = svn_io_file_write_full(tmp_file, translated_contents->data,
1620 translated_contents->len, &written,
1621 pool);
1622
1623 err = svn_error_compose_create(err, svn_io_file_close(tmp_file, pool));
1624
1625 /* Make sure the whole CONTENTS were written, else return an error. */
1626 if (err)
1627 goto cleanup;
1628
1629 /* Get information about the temporary file before the user has
1630 been allowed to edit its contents. */
1631 err = svn_io_stat(&finfo_before, tmpfile_name, APR_FINFO_MTIME, pool);
1632 if (err)
1633 goto cleanup;
1634
1635 /* Backdate the file a little bit in case the editor is very fast
1636 and doesn't change the size. (Use two seconds, since some
1637 filesystems have coarse granularity.) It's OK if this call
1638 fails, so we don't check its return value.*/
1639 err = svn_io_set_file_affected_time(finfo_before.mtime
1640 - apr_time_from_sec(2),
1641 tmpfile_name, pool);
1642 svn_error_clear(err);
1643
1644 /* Stat it again to get the mtime we actually set. */
1645 err = svn_io_stat(&finfo_before, tmpfile_name,
1646 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1647 if (err)
1648 goto cleanup;
1649
1650 /* Prepare the editor command line. */
1651 err = svn_utf_cstring_from_utf8(&tmpfile_native,
1652 escape_path(pool, tmpfile_name), pool);
1653 if (err)
1654 goto cleanup;
1655
1656 /* editor is explicitly documented as being interpreted by the user's shell,
1657 and as such should already be quoted/escaped as needed. */
1658 #ifndef WIN32
1659 cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
1660 #else
1661 cmd = apr_psprintf(pool, "\"%s %s\"", editor, tmpfile_native);
1662 #endif
1663
1664 /* If the caller wants us to leave the file around, return the path
1665 of the file we'll use, and make a note not to destroy it. */
1666 if (tmpfile_left)
1667 {
1668 *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
1669 remove_file = FALSE;
1670 }
1671
1672 /* Now, run the editor command line. */
1673 #ifndef WIN32
1674 sys_err = system(cmd);
1675 #else
1676 SVN_ERR(svn_utf__win32_utf8_to_utf16(&wcmd, cmd, NULL, pool));
1677 sys_err = _wsystem(wcmd);
1678 #endif
1679 if (sys_err != 0)
1680 {
1681 /* Extracting any meaning from sys_err is platform specific, so just
1682 use the raw value. */
1683 err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
1684 _("system('%s') returned %d"), cmd, sys_err);
1685 goto cleanup;
1686 }
1687
1688 /* Get information about the temporary file after the assumed editing. */
1689 err = svn_io_stat(&finfo_after, tmpfile_name,
1690 APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
1691 if (err)
1692 goto cleanup;
1693
1694 /* If the file looks changed... */
1695 if ((finfo_before.mtime != finfo_after.mtime) ||
1696 (finfo_before.size != finfo_after.size))
1697 {
1698 svn_stringbuf_t *edited_contents_s;
1699 err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
1700 if (err)
1701 goto cleanup;
1702
1703 *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
1704
1705 /* Translate back to UTF8/LF if desired. */
1706 if (as_text)
1707 {
1708 err = svn_subst_translate_string2(edited_contents, NULL, NULL,
1709 *edited_contents, encoding, FALSE,
1710 pool, pool);
1711 if (err)
1712 {
1713 err = svn_error_quick_wrap
1714 (err,
1715 _("Error normalizing edited contents to internal format"));
1716 goto cleanup;
1717 }
1718 }
1719 }
1720 else
1721 {
1722 /* No edits seem to have been made */
1723 *edited_contents = NULL;
1724 }
1725
1726 cleanup:
1727 if (remove_file)
1728 {
1729 /* Remove the file from disk. */
1730 err = svn_error_compose_create(
1731 err,
1732 svn_io_remove_file2(tmpfile_name, FALSE, pool));
1733 }
1734
1735 cleanup2:
1736 /* If we against all probability can't cd back, all further relative
1737 file references would be screwed up, so we have to abort. */
1738 apr_err = apr_filepath_set(old_cwd, pool);
1739 if (apr_err)
1740 {
1741 svn_handle_error2(svn_error_wrap_apr
1742 (apr_err, _("Can't restore working directory")),
1743 stderr, TRUE /* fatal */, "svn: ");
1744 }
1745
1746 return svn_error_trace(err);
1747 }
1748
1749 svn_error_t *
svn_cmdline__parse_trust_options(svn_boolean_t * trust_server_cert_unknown_ca,svn_boolean_t * trust_server_cert_cn_mismatch,svn_boolean_t * trust_server_cert_expired,svn_boolean_t * trust_server_cert_not_yet_valid,svn_boolean_t * trust_server_cert_other_failure,const char * opt_arg,apr_pool_t * scratch_pool)1750 svn_cmdline__parse_trust_options(
1751 svn_boolean_t *trust_server_cert_unknown_ca,
1752 svn_boolean_t *trust_server_cert_cn_mismatch,
1753 svn_boolean_t *trust_server_cert_expired,
1754 svn_boolean_t *trust_server_cert_not_yet_valid,
1755 svn_boolean_t *trust_server_cert_other_failure,
1756 const char *opt_arg,
1757 apr_pool_t *scratch_pool)
1758 {
1759 apr_array_header_t *failures;
1760 int i;
1761
1762 *trust_server_cert_unknown_ca = FALSE;
1763 *trust_server_cert_cn_mismatch = FALSE;
1764 *trust_server_cert_expired = FALSE;
1765 *trust_server_cert_not_yet_valid = FALSE;
1766 *trust_server_cert_other_failure = FALSE;
1767
1768 failures = svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, scratch_pool);
1769
1770 for (i = 0; i < failures->nelts; i++)
1771 {
1772 const char *value = APR_ARRAY_IDX(failures, i, const char *);
1773 if (!strcmp(value, "unknown-ca"))
1774 *trust_server_cert_unknown_ca = TRUE;
1775 else if (!strcmp(value, "cn-mismatch"))
1776 *trust_server_cert_cn_mismatch = TRUE;
1777 else if (!strcmp(value, "expired"))
1778 *trust_server_cert_expired = TRUE;
1779 else if (!strcmp(value, "not-yet-valid"))
1780 *trust_server_cert_not_yet_valid = TRUE;
1781 else if (!strcmp(value, "other"))
1782 *trust_server_cert_other_failure = TRUE;
1783 else
1784 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1785 _("Unknown value '%s' for %s.\n"
1786 "Supported values: %s"),
1787 value,
1788 "--trust-server-cert-failures",
1789 "unknown-ca, cn-mismatch, expired, "
1790 "not-yet-valid, other");
1791 }
1792
1793 return SVN_NO_ERROR;
1794 }
1795
1796 /* Flags to see if we've been cancelled by the client or not. */
1797 static volatile sig_atomic_t cancelled = FALSE;
1798 static volatile sig_atomic_t signum_cancelled = 0;
1799
1800 /* The signals we handle. */
1801 static int signal_map [] = {
1802 SIGINT
1803 #ifdef SIGBREAK
1804 /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
1805 , SIGBREAK
1806 #endif
1807 #ifdef SIGHUP
1808 , SIGHUP
1809 #endif
1810 #ifdef SIGTERM
1811 , SIGTERM
1812 #endif
1813 };
1814
1815 /* A signal handler to support cancellation. */
1816 static void
signal_handler(int signum)1817 signal_handler(int signum)
1818 {
1819 int i;
1820
1821 apr_signal(signum, SIG_IGN);
1822 cancelled = TRUE;
1823 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1824 if (signal_map[i] == signum)
1825 {
1826 signum_cancelled = i + 1;
1827 break;
1828 }
1829 }
1830
1831 /* An svn_cancel_func_t callback. */
1832 static svn_error_t *
check_cancel(void * baton)1833 check_cancel(void *baton)
1834 {
1835 /* Cancel baton should be always NULL in command line client. */
1836 SVN_ERR_ASSERT(baton == NULL);
1837 if (cancelled)
1838 return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
1839 else
1840 return SVN_NO_ERROR;
1841 }
1842
1843 svn_cancel_func_t
svn_cmdline__setup_cancellation_handler(void)1844 svn_cmdline__setup_cancellation_handler(void)
1845 {
1846 int i;
1847
1848 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1849 apr_signal(signal_map[i], signal_handler);
1850
1851 #ifdef SIGPIPE
1852 /* Disable SIGPIPE generation for the platforms that have it. */
1853 apr_signal(SIGPIPE, SIG_IGN);
1854 #endif
1855
1856 #ifdef SIGXFSZ
1857 /* Disable SIGXFSZ generation for the platforms that have it, otherwise
1858 * working with large files when compiled against an APR that doesn't have
1859 * large file support will crash the program, which is uncool. */
1860 apr_signal(SIGXFSZ, SIG_IGN);
1861 #endif
1862
1863 return check_cancel;
1864 }
1865
1866 void
svn_cmdline__disable_cancellation_handler(void)1867 svn_cmdline__disable_cancellation_handler(void)
1868 {
1869 int i;
1870
1871 for (i = 0; i < sizeof(signal_map)/sizeof(signal_map[0]); ++i)
1872 apr_signal(signal_map[i], SIG_DFL);
1873 }
1874
1875 void
svn_cmdline__cancellation_exit(void)1876 svn_cmdline__cancellation_exit(void)
1877 {
1878 int signum = 0;
1879
1880 if (cancelled && signum_cancelled)
1881 signum = signal_map[signum_cancelled - 1];
1882 if (signum)
1883 {
1884 #ifndef WIN32
1885 apr_signal(signum, SIG_DFL);
1886 /* No APR support for getpid() so cannot use apr_proc_kill(). */
1887 kill(getpid(), signum);
1888 #endif
1889 }
1890 }
1891