1/* 2 * config_file.c : parsing configuration files 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 26#include <apr_lib.h> 27#include <apr_env.h> 28#include "config_impl.h" 29#include "svn_io.h" 30#include "svn_types.h" 31#include "svn_dirent_uri.h" 32#include "svn_auth.h" 33#include "svn_subst.h" 34#include "svn_utf.h" 35#include "svn_pools.h" 36#include "svn_user.h" 37#include "svn_ctype.h" 38 39#include "svn_private_config.h" 40 41#ifdef __HAIKU__ 42# include <FindDirectory.h> 43# include <StorageDefs.h> 44#endif 45 46/* Used to terminate lines in large multi-line string literals. */ 47#define NL APR_EOL_STR 48 49 50/* File parsing context */ 51typedef struct parse_context_t 52{ 53 /* This config struct */ 54 svn_config_t *cfg; 55 56 /* The stream struct */ 57 svn_stream_t *stream; 58 59 /* The current line in the file */ 60 int line; 61 62 /* Emulate an ungetc */ 63 int ungotten_char; 64 65 /* Temporary strings */ 66 svn_stringbuf_t *section; 67 svn_stringbuf_t *option; 68 svn_stringbuf_t *value; 69 70 /* Parser buffer for getc() to avoid call overhead into several libraries 71 for every character */ 72 char parser_buffer[SVN_STREAM_CHUNK_SIZE]; /* Larger than most config files */ 73 size_t buffer_pos; /* Current position within parser_buffer */ 74 size_t buffer_size; /* parser_buffer contains this many bytes */ 75} parse_context_t; 76 77 78 79/* Emulate getc() because streams don't support it. 80 * 81 * In order to be able to ungetc(), use the CXT instead of the stream 82 * to be able to store the 'ungotton' character. 83 * 84 */ 85static APR_INLINE svn_error_t * 86parser_getc(parse_context_t *ctx, int *c) 87{ 88 do 89 { 90 if (ctx->ungotten_char != EOF) 91 { 92 *c = ctx->ungotten_char; 93 ctx->ungotten_char = EOF; 94 } 95 else if (ctx->buffer_pos < ctx->buffer_size) 96 { 97 *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos]; 98 ctx->buffer_pos++; 99 } 100 else 101 { 102 ctx->buffer_pos = 0; 103 ctx->buffer_size = sizeof(ctx->parser_buffer); 104 105 SVN_ERR(svn_stream_read(ctx->stream, ctx->parser_buffer, 106 &(ctx->buffer_size))); 107 108 if (ctx->buffer_pos < ctx->buffer_size) 109 { 110 *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos]; 111 ctx->buffer_pos++; 112 } 113 else 114 *c = EOF; 115 } 116 } 117 while (*c == '\r'); 118 119 return SVN_NO_ERROR; 120} 121 122/* Simplified version of parser_getc() to be used inside skipping loops. 123 * It will not check for 'ungotton' chars and may or may not ignore '\r'. 124 * 125 * In a 'while(cond) getc();' loop, the first iteration must call 126 * parser_getc to handle all the special cases. Later iterations should 127 * use parser_getc_plain for maximum performance. 128 */ 129static APR_INLINE svn_error_t * 130parser_getc_plain(parse_context_t *ctx, int *c) 131{ 132 if (ctx->buffer_pos < ctx->buffer_size) 133 { 134 *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos]; 135 ctx->buffer_pos++; 136 137 return SVN_NO_ERROR; 138 } 139 140 return parser_getc(ctx, c); 141} 142 143/* Emulate ungetc() because streams don't support it. 144 * 145 * Use CTX to store the ungotten character C. 146 */ 147static APR_INLINE svn_error_t * 148parser_ungetc(parse_context_t *ctx, int c) 149{ 150 ctx->ungotten_char = c; 151 152 return SVN_NO_ERROR; 153} 154 155/* Eat chars from STREAM until encounter non-whitespace, newline, or EOF. 156 Set *PCOUNT to the number of characters eaten, not counting the 157 last one, and return the last char read (the one that caused the 158 break). */ 159static APR_INLINE svn_error_t * 160skip_whitespace(parse_context_t *ctx, int *c, int *pcount) 161{ 162 int ch = 0; 163 int count = 0; 164 165 SVN_ERR(parser_getc(ctx, &ch)); 166 while (svn_ctype_isspace(ch) && ch != '\n' && ch != EOF) 167 { 168 ++count; 169 SVN_ERR(parser_getc_plain(ctx, &ch)); 170 } 171 *pcount = count; 172 *c = ch; 173 return SVN_NO_ERROR; 174} 175 176 177/* Skip to the end of the line (or file). Returns the char that ended 178 the line; the char is either EOF or newline. */ 179static APR_INLINE svn_error_t * 180skip_to_eoln(parse_context_t *ctx, int *c) 181{ 182 int ch; 183 184 SVN_ERR(parser_getc(ctx, &ch)); 185 while (ch != '\n' && ch != EOF) 186 SVN_ERR(parser_getc_plain(ctx, &ch)); 187 188 *c = ch; 189 return SVN_NO_ERROR; 190} 191 192/* Skip a UTF-8 Byte Order Mark if found. */ 193static APR_INLINE svn_error_t * 194skip_bom(parse_context_t *ctx) 195{ 196 int ch; 197 198 SVN_ERR(parser_getc(ctx, &ch)); 199 if (ch == 0xEF) 200 { 201 const unsigned char *buf = (unsigned char *)ctx->parser_buffer; 202 /* This makes assumptions about the implementation of parser_getc and 203 * the use of skip_bom. Specifically that parser_getc() will get all 204 * of the BOM characters into the parse_context_t buffer. This can 205 * safely be assumed as long as we only try to use skip_bom() at the 206 * start of the stream and the buffer is longer than 3 characters. */ 207 SVN_ERR_ASSERT(ctx->buffer_size > ctx->buffer_pos + 1); 208 if (buf[ctx->buffer_pos] == 0xBB && buf[ctx->buffer_pos + 1] == 0xBF) 209 ctx->buffer_pos += 2; 210 else 211 SVN_ERR(parser_ungetc(ctx, ch)); 212 } 213 else 214 SVN_ERR(parser_ungetc(ctx, ch)); 215 216 return SVN_NO_ERROR; 217} 218 219/* Parse a single option value */ 220static svn_error_t * 221parse_value(int *pch, parse_context_t *ctx) 222{ 223 svn_boolean_t end_of_val = FALSE; 224 int ch; 225 226 /* Read the first line of the value */ 227 svn_stringbuf_setempty(ctx->value); 228 SVN_ERR(parser_getc(ctx, &ch)); 229 while (ch != EOF && ch != '\n') 230 /* last ch seen was ':' or '=' in parse_option. */ 231 { 232 const char char_from_int = (char)ch; 233 svn_stringbuf_appendbyte(ctx->value, char_from_int); 234 SVN_ERR(parser_getc(ctx, &ch)); 235 } 236 /* Leading and trailing whitespace is ignored. */ 237 svn_stringbuf_strip_whitespace(ctx->value); 238 239 /* Look for any continuation lines. */ 240 for (;;) 241 { 242 243 if (ch == EOF || end_of_val) 244 { 245 /* At end of file. The value is complete, there can't be 246 any continuation lines. */ 247 svn_config_set(ctx->cfg, ctx->section->data, 248 ctx->option->data, ctx->value->data); 249 break; 250 } 251 else 252 { 253 int count; 254 ++ctx->line; 255 SVN_ERR(skip_whitespace(ctx, &ch, &count)); 256 257 switch (ch) 258 { 259 case '\n': 260 /* The next line was empty. Ergo, it can't be a 261 continuation line. */ 262 ++ctx->line; 263 end_of_val = TRUE; 264 continue; 265 266 case EOF: 267 /* This is also an empty line. */ 268 end_of_val = TRUE; 269 continue; 270 271 default: 272 if (count == 0) 273 { 274 /* This line starts in the first column. That means 275 it's either a section, option or comment. Put 276 the char back into the stream, because it doesn't 277 belong to us. */ 278 SVN_ERR(parser_ungetc(ctx, ch)); 279 end_of_val = TRUE; 280 } 281 else 282 { 283 /* This is a continuation line. Read it. */ 284 svn_stringbuf_appendbyte(ctx->value, ' '); 285 286 while (ch != EOF && ch != '\n') 287 { 288 const char char_from_int = (char)ch; 289 svn_stringbuf_appendbyte(ctx->value, char_from_int); 290 SVN_ERR(parser_getc(ctx, &ch)); 291 } 292 /* Trailing whitespace is ignored. */ 293 svn_stringbuf_strip_whitespace(ctx->value); 294 } 295 } 296 } 297 } 298 299 *pch = ch; 300 return SVN_NO_ERROR; 301} 302 303 304/* Parse a single option */ 305static svn_error_t * 306parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool) 307{ 308 svn_error_t *err = SVN_NO_ERROR; 309 int ch; 310 311 svn_stringbuf_setempty(ctx->option); 312 ch = *pch; /* Yes, the first char is relevant. */ 313 while (ch != EOF && ch != ':' && ch != '=' && ch != '\n') 314 { 315 const char char_from_int = (char)ch; 316 svn_stringbuf_appendbyte(ctx->option, char_from_int); 317 SVN_ERR(parser_getc(ctx, &ch)); 318 } 319 320 if (ch != ':' && ch != '=') 321 { 322 ch = EOF; 323 err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 324 "line %d: Option must end with ':' or '='", 325 ctx->line); 326 } 327 else 328 { 329 /* Whitespace around the name separator is ignored. */ 330 svn_stringbuf_strip_whitespace(ctx->option); 331 err = parse_value(&ch, ctx); 332 } 333 334 *pch = ch; 335 return err; 336} 337 338 339/* Read chars until enounter ']', then skip everything to the end of 340 * the line. Set *PCH to the character that ended the line (either 341 * newline or EOF), and set CTX->section to the string of characters 342 * seen before ']'. 343 * 344 * This is meant to be called immediately after reading the '[' that 345 * starts a section name. 346 */ 347static svn_error_t * 348parse_section_name(int *pch, parse_context_t *ctx, 349 apr_pool_t *scratch_pool) 350{ 351 svn_error_t *err = SVN_NO_ERROR; 352 int ch; 353 354 svn_stringbuf_setempty(ctx->section); 355 SVN_ERR(parser_getc(ctx, &ch)); 356 while (ch != EOF && ch != ']' && ch != '\n') 357 { 358 const char char_from_int = (char)ch; 359 svn_stringbuf_appendbyte(ctx->section, char_from_int); 360 SVN_ERR(parser_getc(ctx, &ch)); 361 } 362 363 if (ch != ']') 364 { 365 ch = EOF; 366 err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 367 "line %d: Section header must end with ']'", 368 ctx->line); 369 } 370 else 371 { 372 /* Everything from the ']' to the end of the line is ignored. */ 373 SVN_ERR(skip_to_eoln(ctx, &ch)); 374 if (ch != EOF) 375 ++ctx->line; 376 } 377 378 *pch = ch; 379 return err; 380} 381 382 383svn_error_t * 384svn_config__sys_config_path(const char **path_p, 385 const char *fname, 386 apr_pool_t *pool) 387{ 388 *path_p = NULL; 389 390 /* Note that even if fname is null, svn_dirent_join_many will DTRT. */ 391 392#ifdef WIN32 393 { 394 const char *folder; 395 SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool)); 396 *path_p = svn_dirent_join_many(pool, folder, 397 SVN_CONFIG__SUBDIRECTORY, fname, NULL); 398 } 399 400#elif defined(__HAIKU__) 401 { 402 char folder[B_PATH_NAME_LENGTH]; 403 404 status_t error = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, false, 405 folder, sizeof(folder)); 406 if (error) 407 return SVN_NO_ERROR; 408 409 *path_p = svn_dirent_join_many(pool, folder, 410 SVN_CONFIG__SYS_DIRECTORY, fname, NULL); 411 } 412#else /* ! WIN32 && !__HAIKU__ */ 413 414 *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, NULL); 415 416#endif /* WIN32 */ 417 418 return SVN_NO_ERROR; 419} 420 421 422/*** Exported interfaces. ***/ 423 424 425svn_error_t * 426svn_config__parse_file(svn_config_t *cfg, const char *file, 427 svn_boolean_t must_exist, apr_pool_t *result_pool) 428{ 429 svn_error_t *err = SVN_NO_ERROR; 430 svn_stream_t *stream; 431 apr_pool_t *scratch_pool = svn_pool_create(result_pool); 432 433 err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool); 434 435 if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err)) 436 { 437 svn_error_clear(err); 438 svn_pool_destroy(scratch_pool); 439 return SVN_NO_ERROR; 440 } 441 else 442 SVN_ERR(err); 443 444 err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool); 445 446 if (err != SVN_NO_ERROR) 447 { 448 /* Add the filename to the error stack. */ 449 err = svn_error_createf(err->apr_err, err, 450 "Error while parsing config file: %s:", 451 svn_dirent_local_style(file, scratch_pool)); 452 } 453 454 /* Close the streams (and other cleanup): */ 455 svn_pool_destroy(scratch_pool); 456 457 return err; 458} 459 460svn_error_t * 461svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream, 462 apr_pool_t *result_pool, apr_pool_t *scratch_pool) 463{ 464 parse_context_t *ctx; 465 int ch, count; 466 467 ctx = apr_palloc(scratch_pool, sizeof(*ctx)); 468 469 ctx->cfg = cfg; 470 ctx->stream = stream; 471 ctx->line = 1; 472 ctx->ungotten_char = EOF; 473 ctx->section = svn_stringbuf_create_empty(scratch_pool); 474 ctx->option = svn_stringbuf_create_empty(scratch_pool); 475 ctx->value = svn_stringbuf_create_empty(scratch_pool); 476 ctx->buffer_pos = 0; 477 ctx->buffer_size = 0; 478 479 SVN_ERR(skip_bom(ctx)); 480 481 do 482 { 483 SVN_ERR(skip_whitespace(ctx, &ch, &count)); 484 485 switch (ch) 486 { 487 case '[': /* Start of section header */ 488 if (count == 0) 489 SVN_ERR(parse_section_name(&ch, ctx, scratch_pool)); 490 else 491 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 492 "line %d: Section header" 493 " must start in the first column", 494 ctx->line); 495 break; 496 497 case '#': /* Comment */ 498 if (count == 0) 499 { 500 SVN_ERR(skip_to_eoln(ctx, &ch)); 501 ++(ctx->line); 502 } 503 else 504 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 505 "line %d: Comment" 506 " must start in the first column", 507 ctx->line); 508 break; 509 510 case '\n': /* Empty line */ 511 ++(ctx->line); 512 break; 513 514 case EOF: /* End of file or read error */ 515 break; 516 517 default: 518 if (svn_stringbuf_isempty(ctx->section)) 519 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 520 "line %d: Section header expected", 521 ctx->line); 522 else if (count != 0) 523 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 524 "line %d: Option expected", 525 ctx->line); 526 else 527 SVN_ERR(parse_option(&ch, ctx, scratch_pool)); 528 break; 529 } 530 } 531 while (ch != EOF); 532 533 return SVN_NO_ERROR; 534} 535 536 537/* Helper for ensure_auth_dirs: create SUBDIR under AUTH_DIR, iff 538 SUBDIR does not already exist, but ignore any errors. Use POOL for 539 temporary allocation. */ 540static void 541ensure_auth_subdir(const char *auth_dir, 542 const char *subdir, 543 apr_pool_t *pool) 544{ 545 svn_error_t *err; 546 const char *subdir_full_path; 547 svn_node_kind_t kind; 548 549 subdir_full_path = svn_dirent_join(auth_dir, subdir, pool); 550 err = svn_io_check_path(subdir_full_path, &kind, pool); 551 if (err || kind == svn_node_none) 552 { 553 svn_error_clear(err); 554 svn_error_clear(svn_io_dir_make(subdir_full_path, APR_OS_DEFAULT, pool)); 555 } 556} 557 558/* Helper for svn_config_ensure: see if ~/.subversion/auth/ and its 559 subdirs exist, try to create them, but don't throw errors on 560 failure. PATH is assumed to be a path to the user's private config 561 directory. */ 562static void 563ensure_auth_dirs(const char *path, 564 apr_pool_t *pool) 565{ 566 svn_node_kind_t kind; 567 const char *auth_dir; 568 svn_error_t *err; 569 570 /* Ensure ~/.subversion/auth/ */ 571 auth_dir = svn_dirent_join(path, SVN_CONFIG__AUTH_SUBDIR, pool); 572 err = svn_io_check_path(auth_dir, &kind, pool); 573 if (err || kind == svn_node_none) 574 { 575 svn_error_clear(err); 576 /* 'chmod 700' permissions: */ 577 err = svn_io_dir_make(auth_dir, 578 (APR_UREAD | APR_UWRITE | APR_UEXECUTE), 579 pool); 580 if (err) 581 { 582 /* Don't try making subdirs if we can't make the top-level dir. */ 583 svn_error_clear(err); 584 return; 585 } 586 } 587 588 /* If a provider exists that wants to store credentials in 589 ~/.subversion, a subdirectory for the cred_kind must exist. */ 590 ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SIMPLE, pool); 591 ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_USERNAME, pool); 592 ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_SERVER_TRUST, pool); 593 ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, pool); 594} 595 596 597svn_error_t * 598svn_config_ensure(const char *config_dir, apr_pool_t *pool) 599{ 600 const char *path; 601 svn_node_kind_t kind; 602 svn_error_t *err; 603 604 /* Ensure that the user-specific config directory exists. */ 605 SVN_ERR(svn_config_get_user_config_path(&path, config_dir, NULL, pool)); 606 607 if (! path) 608 return SVN_NO_ERROR; 609 610 err = svn_io_check_resolved_path(path, &kind, pool); 611 if (err) 612 { 613 /* Don't throw an error, but don't continue. */ 614 svn_error_clear(err); 615 return SVN_NO_ERROR; 616 } 617 618 if (kind == svn_node_none) 619 { 620 err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); 621 if (err) 622 { 623 /* Don't throw an error, but don't continue. */ 624 svn_error_clear(err); 625 return SVN_NO_ERROR; 626 } 627 } 628 else if (kind == svn_node_file) 629 { 630 /* Somebody put a file where the config directory should be. 631 Wacky. Let's bail. */ 632 return SVN_NO_ERROR; 633 } 634 635 /* Else, there's a configuration directory. */ 636 637 /* If we get errors trying to do things below, just stop and return 638 success. There's no _need_ to init a config directory if 639 something's preventing it. */ 640 641 /** If non-existent, try to create a number of auth/ subdirectories. */ 642 ensure_auth_dirs(path, pool); 643 644 /** Ensure that the `README.txt' file exists. **/ 645 SVN_ERR(svn_config_get_user_config_path 646 (&path, config_dir, SVN_CONFIG__USR_README_FILE, pool)); 647 648 if (! path) /* highly unlikely, since a previous call succeeded */ 649 return SVN_NO_ERROR; 650 651 err = svn_io_check_path(path, &kind, pool); 652 if (err) 653 { 654 svn_error_clear(err); 655 return SVN_NO_ERROR; 656 } 657 658 if (kind == svn_node_none) 659 { 660 apr_file_t *f; 661 const char *contents = 662 "This directory holds run-time configuration information for Subversion" NL 663 "clients. The configuration files all share the same syntax, but you" NL 664 "should examine a particular file to learn what configuration" NL 665 "directives are valid for that file." NL 666 "" NL 667 "The syntax is standard INI format:" NL 668 "" NL 669 " - Empty lines, and lines starting with '#', are ignored." NL 670 " The first significant line in a file must be a section header." NL 671 "" NL 672 " - A section starts with a section header, which must start in" NL 673 " the first column:" NL 674 "" NL 675 " [section-name]" NL 676 "" NL 677 " - An option, which must always appear within a section, is a pair" NL 678 " (name, value). There are two valid forms for defining an" NL 679 " option, both of which must start in the first column:" NL 680 "" NL 681 " name: value" NL 682 " name = value" NL 683 "" NL 684 " Whitespace around the separator (:, =) is optional." NL 685 "" NL 686 " - Section and option names are case-insensitive, but case is" NL 687 " preserved." NL 688 "" NL 689 " - An option's value may be broken into several lines. The value" NL 690 " continuation lines must start with at least one whitespace." NL 691 " Trailing whitespace in the previous line, the newline character" NL 692 " and the leading whitespace in the continuation line is compressed" NL 693 " into a single space character." NL 694 "" NL 695 " - All leading and trailing whitespace around a value is trimmed," NL 696 " but the whitespace within a value is preserved, with the" NL 697 " exception of whitespace around line continuations, as" NL 698 " described above." NL 699 "" NL 700 " - When a value is a boolean, any of the following strings are" NL 701 " recognised as truth values (case does not matter):" NL 702 "" NL 703 " true false" NL 704 " yes no" NL 705 " on off" NL 706 " 1 0" NL 707 "" NL 708 " - When a value is a list, it is comma-separated. Again, the" NL 709 " whitespace around each element of the list is trimmed." NL 710 "" NL 711 " - Option values may be expanded within a value by enclosing the" NL 712 " option name in parentheses, preceded by a percent sign and" NL 713 " followed by an 's':" NL 714 "" NL 715 " %(name)s" NL 716 "" NL 717 " The expansion is performed recursively and on demand, during" NL 718 " svn_option_get. The name is first searched for in the same" NL 719 " section, then in the special [DEFAULT] section. If the name" NL 720 " is not found, the whole '%(name)s' placeholder is left" NL 721 " unchanged." NL 722 "" NL 723 " Any modifications to the configuration data invalidate all" NL 724 " previously expanded values, so that the next svn_option_get" NL 725 " will take the modifications into account." NL 726 "" NL 727 "The syntax of the configuration files is a subset of the one used by" NL 728 "Python's ConfigParser module; see" NL 729 "" NL 730 " http://www.python.org/doc/current/lib/module-ConfigParser.html" NL 731 "" NL 732 "Configuration data in the Windows registry" NL 733 "==========================================" NL 734 "" NL 735 "On Windows, configuration data may also be stored in the registry. The" NL 736 "functions svn_config_read and svn_config_merge will read from the" NL 737 "registry when passed file names of the form:" NL 738 "" NL 739 " REGISTRY:<hive>/path/to/config-key" NL 740 "" NL 741 "The REGISTRY: prefix must be in upper case. The <hive> part must be" NL 742 "one of:" NL 743 "" NL 744 " HKLM for HKEY_LOCAL_MACHINE" NL 745 " HKCU for HKEY_CURRENT_USER" NL 746 "" NL 747 "The values in config-key represent the options in the [DEFAULT] section."NL 748 "The keys below config-key represent other sections, and their values" NL 749 "represent the options. Only values of type REG_SZ whose name doesn't" NL 750 "start with a '#' will be used; other values, as well as the keys'" NL 751 "default values, will be ignored." NL 752 "" NL 753 "" NL 754 "File locations" NL 755 "==============" NL 756 "" NL 757 "Typically, Subversion uses two config directories, one for site-wide" NL 758 "configuration," NL 759 "" NL 760 " Unix:" NL 761 " /etc/subversion/servers" NL 762 " /etc/subversion/config" NL 763 " /etc/subversion/hairstyles" NL 764 " Windows:" NL 765 " %ALLUSERSPROFILE%\\Application Data\\Subversion\\servers" NL 766 " %ALLUSERSPROFILE%\\Application Data\\Subversion\\config" NL 767 " %ALLUSERSPROFILE%\\Application Data\\Subversion\\hairstyles" NL 768 " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Servers" NL 769 " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Config" NL 770 " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Hairstyles" NL 771 "" NL 772 "and one for per-user configuration:" NL 773 "" NL 774 " Unix:" NL 775 " ~/.subversion/servers" NL 776 " ~/.subversion/config" NL 777 " ~/.subversion/hairstyles" NL 778 " Windows:" NL 779 " %APPDATA%\\Subversion\\servers" NL 780 " %APPDATA%\\Subversion\\config" NL 781 " %APPDATA%\\Subversion\\hairstyles" NL 782 " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Servers" NL 783 " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Config" NL 784 " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Hairstyles" NL 785 "" NL; 786 787 err = svn_io_file_open(&f, path, 788 (APR_WRITE | APR_CREATE | APR_EXCL), 789 APR_OS_DEFAULT, 790 pool); 791 792 if (! err) 793 { 794 SVN_ERR(svn_io_file_write_full(f, contents, 795 strlen(contents), NULL, pool)); 796 SVN_ERR(svn_io_file_close(f, pool)); 797 } 798 799 svn_error_clear(err); 800 } 801 802 /** Ensure that the `servers' file exists. **/ 803 SVN_ERR(svn_config_get_user_config_path 804 (&path, config_dir, SVN_CONFIG_CATEGORY_SERVERS, pool)); 805 806 if (! path) /* highly unlikely, since a previous call succeeded */ 807 return SVN_NO_ERROR; 808 809 err = svn_io_check_path(path, &kind, pool); 810 if (err) 811 { 812 svn_error_clear(err); 813 return SVN_NO_ERROR; 814 } 815 816 if (kind == svn_node_none) 817 { 818 apr_file_t *f; 819 const char *contents = 820 "### This file specifies server-specific parameters," NL 821 "### including HTTP proxy information, HTTP timeout settings," NL 822 "### and authentication settings." NL 823 "###" NL 824 "### The currently defined server options are:" NL 825 "### http-proxy-host Proxy host for HTTP connection" NL 826 "### http-proxy-port Port number of proxy host service" NL 827 "### http-proxy-username Username for auth to proxy service"NL 828 "### http-proxy-password Password for auth to proxy service"NL 829 "### http-proxy-exceptions List of sites that do not use proxy" 830 NL 831 "### http-timeout Timeout for HTTP requests in seconds" 832 NL 833 "### http-compression Whether to compress HTTP requests" NL 834 "### http-max-connections Maximum number of parallel server" NL 835 "### connections to use for any given" NL 836 "### HTTP operation." NL 837 "### http-chunked-requests Whether to use chunked transfer" NL 838 "### encoding for HTTP requests body." NL 839 "### neon-debug-mask Debug mask for Neon HTTP library" NL 840 "### ssl-authority-files List of files, each of a trusted CA" 841 NL 842 "### ssl-trust-default-ca Trust the system 'default' CAs" NL 843 "### ssl-client-cert-file PKCS#12 format client certificate file" 844 NL 845 "### ssl-client-cert-password Client Key password, if needed." NL 846 "### ssl-pkcs11-provider Name of PKCS#11 provider to use." NL 847 "### http-library Which library to use for http/https" 848 NL 849 "### connections." NL 850 "### http-bulk-updates Whether to request bulk update" NL 851 "### responses or to fetch each file" NL 852 "### in an individual request. " NL 853 "### store-passwords Specifies whether passwords used" NL 854 "### to authenticate against a" NL 855 "### Subversion server may be cached" NL 856 "### to disk in any way." NL 857#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 858 "### store-plaintext-passwords Specifies whether passwords may" NL 859 "### be cached on disk unencrypted." NL 860#endif 861 "### store-ssl-client-cert-pp Specifies whether passphrase used" NL 862 "### to authenticate against a client" NL 863 "### certificate may be cached to disk" NL 864 "### in any way" NL 865#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 866 "### store-ssl-client-cert-pp-plaintext" NL 867 "### Specifies whether client cert" NL 868 "### passphrases may be cached on disk" NL 869 "### unencrypted (i.e., as plaintext)." NL 870#endif 871 "### store-auth-creds Specifies whether any auth info" NL 872 "### (passwords, server certs, etc.)" NL 873 "### may be cached to disk." NL 874 "### username Specifies the default username." NL 875 "###" NL 876 "### Set store-passwords to 'no' to avoid storing passwords on disk" NL 877 "### in any way, including in password stores. It defaults to" NL 878 "### 'yes', but Subversion will never save your password to disk in" NL 879 "### plaintext unless explicitly configured to do so." NL 880 "### Note that this option only prevents saving of *new* passwords;" NL 881 "### it doesn't invalidate existing passwords. (To do that, remove" NL 882 "### the cache files by hand as described in the Subversion book.)" NL 883 "###" NL 884#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 885 "### Set store-plaintext-passwords to 'no' to avoid storing" NL 886 "### passwords in unencrypted form in the auth/ area of your config" NL 887 "### directory. Set it to 'yes' to allow Subversion to store" NL 888 "### unencrypted passwords in the auth/ area. The default is" NL 889 "### 'ask', which means that Subversion will ask you before" NL 890 "### saving a password to disk in unencrypted form. Note that" NL 891 "### this option has no effect if either 'store-passwords' or " NL 892 "### 'store-auth-creds' is set to 'no'." NL 893 "###" NL 894#endif 895 "### Set store-ssl-client-cert-pp to 'no' to avoid storing ssl" NL 896 "### client certificate passphrases in the auth/ area of your" NL 897 "### config directory. It defaults to 'yes', but Subversion will" NL 898 "### never save your passphrase to disk in plaintext unless" NL 899 "### explicitly configured to do so." NL 900 "###" NL 901 "### Note store-ssl-client-cert-pp only prevents the saving of *new*"NL 902 "### passphrases; it doesn't invalidate existing passphrases. To do"NL 903 "### that, remove the cache files by hand as described in the" NL 904 "### Subversion book at http://svnbook.red-bean.com/nightly/en/\\" NL 905 "### svn.serverconfig.netmodel.html\\" NL 906 "### #svn.serverconfig.netmodel.credcache" NL 907 "###" NL 908#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 909 "### Set store-ssl-client-cert-pp-plaintext to 'no' to avoid storing"NL 910 "### passphrases in unencrypted form in the auth/ area of your" NL 911 "### config directory. Set it to 'yes' to allow Subversion to" NL 912 "### store unencrypted passphrases in the auth/ area. The default" NL 913 "### is 'ask', which means that Subversion will prompt before" NL 914 "### saving a passphrase to disk in unencrypted form. Note that" NL 915 "### this option has no effect if either 'store-auth-creds' or " NL 916 "### 'store-ssl-client-cert-pp' is set to 'no'." NL 917 "###" NL 918#endif 919 "### Set store-auth-creds to 'no' to avoid storing any Subversion" NL 920 "### credentials in the auth/ area of your config directory." NL 921 "### Note that this includes SSL server certificates." NL 922 "### It defaults to 'yes'. Note that this option only prevents" NL 923 "### saving of *new* credentials; it doesn't invalidate existing" NL 924 "### caches. (To do that, remove the cache files by hand.)" NL 925 "###" NL 926 "### HTTP timeouts, if given, are specified in seconds. A timeout" NL 927 "### of 0, i.e. zero, causes a builtin default to be used." NL 928 "###" NL 929 "### Most users will not need to explicitly set the http-library" NL 930 "### option, but valid values for the option include:" NL 931 "### 'serf': Serf-based module (Subversion 1.5 - present)" NL 932 "### 'neon': Neon-based module (Subversion 1.0 - 1.7)" NL 933 "### Availability of these modules may depend on your specific" NL 934 "### Subversion distribution." NL 935 "###" NL 936 "### The commented-out examples below are intended only to" NL 937 "### demonstrate how to use this file; any resemblance to actual" NL 938 "### servers, living or dead, is entirely coincidental." NL 939 "" NL 940 "### In the 'groups' section, the URL of the repository you're" NL 941 "### trying to access is matched against the patterns on the right." NL 942 "### If a match is found, the server options are taken from the" NL 943 "### section with the corresponding name on the left." NL 944 "" NL 945 "[groups]" NL 946 "# group1 = *.collab.net" NL 947 "# othergroup = repository.blarggitywhoomph.com" NL 948 "# thirdgroup = *.example.com" NL 949 "" NL 950 "### Information for the first group:" NL 951 "# [group1]" NL 952 "# http-proxy-host = proxy1.some-domain-name.com" NL 953 "# http-proxy-port = 80" NL 954 "# http-proxy-username = blah" NL 955 "# http-proxy-password = doubleblah" NL 956 "# http-timeout = 60" NL 957 "# neon-debug-mask = 130" NL 958#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 959 "# store-plaintext-passwords = no" NL 960#endif 961 "# username = harry" NL 962 "" NL 963 "### Information for the second group:" NL 964 "# [othergroup]" NL 965 "# http-proxy-host = proxy2.some-domain-name.com" NL 966 "# http-proxy-port = 9000" NL 967 "# No username and password for the proxy, so use the defaults below." 968 NL 969 "" NL 970 "### You can set default parameters in the 'global' section." NL 971 "### These parameters apply if no corresponding parameter is set in" NL 972 "### a specifically matched group as shown above. Thus, if you go" NL 973 "### through the same proxy server to reach every site on the" NL 974 "### Internet, you probably just want to put that server's" NL 975 "### information in the 'global' section and not bother with" NL 976 "### 'groups' or any other sections." NL 977 "###" NL 978 "### Most people might want to configure password caching" NL 979 "### parameters here, but you can also configure them per server" NL 980 "### group (per-group settings override global settings)." NL 981 "###" NL 982 "### If you go through a proxy for all but a few sites, you can" NL 983 "### list those exceptions under 'http-proxy-exceptions'. This only"NL 984 "### overrides defaults, not explicitly matched server names." NL 985 "###" NL 986 "### 'ssl-authority-files' is a semicolon-delimited list of files," NL 987 "### each pointing to a PEM-encoded Certificate Authority (CA) " NL 988 "### SSL certificate. See details above for overriding security " NL 989 "### due to SSL." NL 990 "[global]" NL 991 "# http-proxy-exceptions = *.exception.com, www.internal-site.org" NL 992 "# http-proxy-host = defaultproxy.whatever.com" NL 993 "# http-proxy-port = 7000" NL 994 "# http-proxy-username = defaultusername" NL 995 "# http-proxy-password = defaultpassword" NL 996 "# http-compression = no" NL 997 "# No http-timeout, so just use the builtin default." NL 998 "# No neon-debug-mask, so neon debugging is disabled." NL 999 "# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem" NL 1000 "#" NL 1001 "# Password / passphrase caching parameters:" NL 1002 "# store-passwords = no" NL 1003 "# store-ssl-client-cert-pp = no" NL 1004#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE 1005 "# store-plaintext-passwords = no" NL 1006 "# store-ssl-client-cert-pp-plaintext = no" NL 1007#endif 1008 ; 1009 1010 err = svn_io_file_open(&f, path, 1011 (APR_WRITE | APR_CREATE | APR_EXCL), 1012 APR_OS_DEFAULT, 1013 pool); 1014 1015 if (! err) 1016 { 1017 SVN_ERR(svn_io_file_write_full(f, contents, 1018 strlen(contents), NULL, pool)); 1019 SVN_ERR(svn_io_file_close(f, pool)); 1020 } 1021 1022 svn_error_clear(err); 1023 } 1024 1025 /** Ensure that the `config' file exists. **/ 1026 SVN_ERR(svn_config_get_user_config_path 1027 (&path, config_dir, SVN_CONFIG_CATEGORY_CONFIG, pool)); 1028 1029 if (! path) /* highly unlikely, since a previous call succeeded */ 1030 return SVN_NO_ERROR; 1031 1032 err = svn_io_check_path(path, &kind, pool); 1033 if (err) 1034 { 1035 svn_error_clear(err); 1036 return SVN_NO_ERROR; 1037 } 1038 1039 if (kind == svn_node_none) 1040 { 1041 apr_file_t *f; 1042 const char *contents = 1043 "### This file configures various client-side behaviors." NL 1044 "###" NL 1045 "### The commented-out examples below are intended to demonstrate" NL 1046 "### how to use this file." NL 1047 "" NL 1048 "### Section for authentication and authorization customizations." NL 1049 "[auth]" NL 1050 "### Set password stores used by Subversion. They should be" NL 1051 "### delimited by spaces or commas. The order of values determines" NL 1052 "### the order in which password stores are used." NL 1053 "### Valid password stores:" NL 1054 "### gnome-keyring (Unix-like systems)" NL 1055 "### kwallet (Unix-like systems)" NL 1056 "### gpg-agent (Unix-like systems)" NL 1057 "### keychain (Mac OS X)" NL 1058 "### windows-cryptoapi (Windows)" NL 1059#ifdef SVN_HAVE_KEYCHAIN_SERVICES 1060 "# password-stores = keychain" NL 1061#elif defined(WIN32) && !defined(__MINGW32__) 1062 "# password-stores = windows-cryptoapi" NL 1063#else 1064 "# password-stores = gpg-agent,gnome-keyring,kwallet" NL 1065#endif 1066 "### To disable all password stores, use an empty list:" NL 1067 "# password-stores =" NL 1068#ifdef SVN_HAVE_KWALLET 1069 "###" NL 1070 "### Set KWallet wallet used by Subversion. If empty or unset," NL 1071 "### then the default network wallet will be used." NL 1072 "# kwallet-wallet =" NL 1073 "###" NL 1074 "### Include PID (Process ID) in Subversion application name when" NL 1075 "### using KWallet. It defaults to 'no'." NL 1076 "# kwallet-svn-application-name-with-pid = yes" NL 1077#endif 1078 "###" NL 1079 "### Set ssl-client-cert-file-prompt to 'yes' to cause the client" NL 1080 "### to prompt for a path to a client cert file when the server" NL 1081 "### requests a client cert but no client cert file is found in the" NL 1082 "### expected place (see the 'ssl-client-cert-file' option in the" NL 1083 "### 'servers' configuration file). Defaults to 'no'." NL 1084 "# ssl-client-cert-file-prompt = no" NL 1085 "###" NL 1086 "### The rest of the [auth] section in this file has been deprecated." 1087 NL 1088 "### Both 'store-passwords' and 'store-auth-creds' can now be" NL 1089 "### specified in the 'servers' file in your config directory" NL 1090 "### and are documented there. Anything specified in this section " NL 1091 "### is overridden by settings specified in the 'servers' file." NL 1092 "# store-passwords = no" NL 1093 "# store-auth-creds = no" NL 1094 "" NL 1095 "### Section for configuring external helper applications." NL 1096 "[helpers]" NL 1097 "### Set editor-cmd to the command used to invoke your text editor." NL 1098 "### This will override the environment variables that Subversion" NL 1099 "### examines by default to find this information ($EDITOR, " NL 1100 "### et al)." NL 1101 "# editor-cmd = editor (vi, emacs, notepad, etc.)" NL 1102 "### Set diff-cmd to the absolute path of your 'diff' program." NL 1103 "### This will override the compile-time default, which is to use" NL 1104 "### Subversion's internal diff implementation." NL 1105 "# diff-cmd = diff_program (diff, gdiff, etc.)" NL 1106 "### Diff-extensions are arguments passed to an external diff" NL 1107 "### program or to Subversion's internal diff implementation." NL 1108 "### Set diff-extensions to override the default arguments ('-u')." NL 1109 "# diff-extensions = -u -p" NL 1110 "### Set diff3-cmd to the absolute path of your 'diff3' program." NL 1111 "### This will override the compile-time default, which is to use" NL 1112 "### Subversion's internal diff3 implementation." NL 1113 "# diff3-cmd = diff3_program (diff3, gdiff3, etc.)" NL 1114 "### Set diff3-has-program-arg to 'yes' if your 'diff3' program" NL 1115 "### accepts the '--diff-program' option." NL 1116 "# diff3-has-program-arg = [yes | no]" NL 1117 "### Set merge-tool-cmd to the command used to invoke your external" NL 1118 "### merging tool of choice. Subversion will pass 5 arguments to" NL 1119 "### the specified command: base theirs mine merged wcfile" NL 1120 "# merge-tool-cmd = merge_command" NL 1121 "" NL 1122 "### Section for configuring tunnel agents." NL 1123 "[tunnels]" NL 1124 "### Configure svn protocol tunnel schemes here. By default, only" NL 1125 "### the 'ssh' scheme is defined. You can define other schemes to" NL 1126 "### be used with 'svn+scheme://hostname/path' URLs. A scheme" NL 1127 "### definition is simply a command, optionally prefixed by an" NL 1128 "### environment variable name which can override the command if it" NL 1129 "### is defined. The command (or environment variable) may contain" NL 1130 "### arguments, using standard shell quoting for arguments with" NL 1131 "### spaces. The command will be invoked as:" NL 1132 "### <command> <hostname> svnserve -t" NL 1133 "### (If the URL includes a username, then the hostname will be" NL 1134 "### passed to the tunnel agent as <user>@<hostname>.) If the" NL 1135 "### built-in ssh scheme were not predefined, it could be defined" NL 1136 "### as:" NL 1137 "# ssh = $SVN_SSH ssh -q" NL 1138 "### If you wanted to define a new 'rsh' scheme, to be used with" NL 1139 "### 'svn+rsh:' URLs, you could do so as follows:" NL 1140 "# rsh = rsh" NL 1141 "### Or, if you wanted to specify a full path and arguments:" NL 1142 "# rsh = /path/to/rsh -l myusername" NL 1143 "### On Windows, if you are specifying a full path to a command," NL 1144 "### use a forward slash (/) or a paired backslash (\\\\) as the" NL 1145 "### path separator. A single backslash will be treated as an" NL 1146 "### escape for the following character." NL 1147 "" NL 1148 "### Section for configuring miscellaneous Subversion options." NL 1149 "[miscellany]" NL 1150 "### Set global-ignores to a set of whitespace-delimited globs" NL 1151 "### which Subversion will ignore in its 'status' output, and" NL 1152 "### while importing or adding files and directories." NL 1153 "### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'." NL 1154 "# global-ignores = " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_1 NL 1155 "# " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_2 NL 1156 "### Set log-encoding to the default encoding for log messages" NL 1157 "# log-encoding = latin1" NL 1158 "### Set use-commit-times to make checkout/update/switch/revert" NL 1159 "### put last-committed timestamps on every file touched." NL 1160 "# use-commit-times = yes" NL 1161 "### Set no-unlock to prevent 'svn commit' from automatically" NL 1162 "### releasing locks on files." NL 1163 "# no-unlock = yes" NL 1164 "### Set mime-types-file to a MIME type registry file, used to" NL 1165 "### provide hints to Subversion's MIME type auto-detection" NL 1166 "### algorithm." NL 1167 "# mime-types-file = /path/to/mime.types" NL 1168 "### Set preserved-conflict-file-exts to a whitespace-delimited" NL 1169 "### list of patterns matching file extensions which should be" NL 1170 "### preserved in generated conflict file names. By default," NL 1171 "### conflict files use custom extensions." NL 1172 "# preserved-conflict-file-exts = doc ppt xls od?" NL 1173 "### Set enable-auto-props to 'yes' to enable automatic properties" NL 1174 "### for 'svn add' and 'svn import', it defaults to 'no'." NL 1175 "### Automatic properties are defined in the section 'auto-props'." NL 1176 "# enable-auto-props = yes" NL 1177 "### Set interactive-conflicts to 'no' to disable interactive" NL 1178 "### conflict resolution prompting. It defaults to 'yes'." NL 1179 "# interactive-conflicts = no" NL 1180 "### Set memory-cache-size to define the size of the memory cache" NL 1181 "### used by the client when accessing a FSFS repository via" NL 1182 "### ra_local (the file:// scheme). The value represents the number" NL 1183 "### of MB used by the cache." NL 1184 "# memory-cache-size = 16" NL 1185 "" NL 1186 "### Section for configuring automatic properties." NL 1187 "[auto-props]" NL 1188 "### The format of the entries is:" NL 1189 "### file-name-pattern = propname[=value][;propname[=value]...]" NL 1190 "### The file-name-pattern can contain wildcards (such as '*' and" NL 1191 "### '?'). All entries which match (case-insensitively) will be" NL 1192 "### applied to the file. Note that auto-props functionality" NL 1193 "### must be enabled, which is typically done by setting the" NL 1194 "### 'enable-auto-props' option." NL 1195 "# *.c = svn:eol-style=native" NL 1196 "# *.cpp = svn:eol-style=native" NL 1197 "# *.h = svn:keywords=Author Date Id Rev URL;svn:eol-style=native" NL 1198 "# *.dsp = svn:eol-style=CRLF" NL 1199 "# *.dsw = svn:eol-style=CRLF" NL 1200 "# *.sh = svn:eol-style=native;svn:executable" NL 1201 "# *.txt = svn:eol-style=native;svn:keywords=Author Date Id Rev URL;"NL 1202 "# *.png = svn:mime-type=image/png" NL 1203 "# *.jpg = svn:mime-type=image/jpeg" NL 1204 "# Makefile = svn:eol-style=native" NL 1205 "" NL 1206 "### Section for configuring working copies." NL 1207 "[working-copy]" NL 1208 "### Set to a list of the names of specific clients that should use" NL 1209 "### exclusive SQLite locking of working copies. This increases the"NL 1210 "### performance of the client but prevents concurrent access by" NL 1211 "### other clients. Third-party clients may also support this" NL 1212 "### option." NL 1213 "### Possible values:" NL 1214 "### svn (the command line client)" NL 1215 "# exclusive-locking-clients =" NL 1216 "### Set to true to enable exclusive SQLite locking of working" NL 1217 "### copies by all clients using the 1.8 APIs. Enabling this may" NL 1218 "### cause some clients to fail to work properly. This does not have"NL 1219 "### to be set for exclusive-locking-clients to work." NL 1220 "# exclusive-locking = false" NL; 1221 1222 err = svn_io_file_open(&f, path, 1223 (APR_WRITE | APR_CREATE | APR_EXCL), 1224 APR_OS_DEFAULT, 1225 pool); 1226 1227 if (! err) 1228 { 1229 SVN_ERR(svn_io_file_write_full(f, contents, 1230 strlen(contents), NULL, pool)); 1231 SVN_ERR(svn_io_file_close(f, pool)); 1232 } 1233 1234 svn_error_clear(err); 1235 } 1236 1237 return SVN_NO_ERROR; 1238} 1239 1240svn_error_t * 1241svn_config_get_user_config_path(const char **path, 1242 const char *config_dir, 1243 const char *fname, 1244 apr_pool_t *pool) 1245{ 1246 *path= NULL; 1247 1248 /* Note that even if fname is null, svn_dirent_join_many will DTRT. */ 1249 1250 if (config_dir) 1251 { 1252 *path = svn_dirent_join_many(pool, config_dir, fname, NULL); 1253 return SVN_NO_ERROR; 1254 } 1255 1256#ifdef WIN32 1257 { 1258 const char *folder; 1259 SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool)); 1260 *path = svn_dirent_join_many(pool, folder, 1261 SVN_CONFIG__SUBDIRECTORY, fname, NULL); 1262 } 1263 1264#elif defined(__HAIKU__) 1265 { 1266 char folder[B_PATH_NAME_LENGTH]; 1267 1268 status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, 1269 folder, sizeof(folder)); 1270 if (error) 1271 return SVN_NO_ERROR; 1272 1273 *path = svn_dirent_join_many(pool, folder, 1274 SVN_CONFIG__USR_DIRECTORY, fname, NULL); 1275 } 1276#else /* ! WIN32 && !__HAIKU__ */ 1277 1278 { 1279 const char *homedir = svn_user_get_homedir(pool); 1280 if (! homedir) 1281 return SVN_NO_ERROR; 1282 *path = svn_dirent_join_many(pool, 1283 svn_dirent_canonicalize(homedir, pool), 1284 SVN_CONFIG__USR_DIRECTORY, fname, NULL); 1285 } 1286#endif /* WIN32 */ 1287 1288 return SVN_NO_ERROR; 1289} 1290 1291