cmdline.c revision 262253
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. */ 72static const char *input_encoding = NULL; 73 74/* The stdout encoding. If null, it's the same as the native encoding. */ 75static const char *output_encoding = NULL; 76 77 78int 79svn_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 254svn_error_t * 255svn_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 266const char * 267svn_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 275svn_error_t * 276svn_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 287svn_error_t * 288svn_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 297svn_error_t * 298svn_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 318svn_error_t * 319svn_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 333svn_error_t * 334svn_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 371svn_error_t * 372svn_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 394const 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 402int 403svn_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. */ 434static svn_error_t * 435ssl_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 456svn_error_t * 457svn_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 646svn_error_t * 647svn_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 660void 661svn_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 708svn_error_t * 709svn_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 747svn_error_t * 748svn_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 */ 786static const char * 787next_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 814const char * 815svn_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 830svn_error_t * 831svn_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 897svn_error_t * 898svn_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 943svn_boolean_t 944svn_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. */ 970static svn_error_t * 971find_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 1032svn_error_t * 1033svn_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 1081svn_error_t * 1082svn_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