1/* 2 * config.c : reading configuration information 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 <assert.h> 27 28#define APR_WANT_STRFUNC 29#define APR_WANT_MEMFUNC 30#include <apr_want.h> 31 32#include <apr_general.h> 33#include <apr_lib.h> 34#include "svn_hash.h" 35#include "svn_error.h" 36#include "svn_string.h" 37#include "svn_pools.h" 38#include "config_impl.h" 39 40#include "private/svn_dep_compat.h" 41#include "private/svn_subr_private.h" 42#include "private/svn_config_private.h" 43 44#include "svn_private_config.h" 45 46 47 48 49/* Section table entries. */ 50typedef struct cfg_section_t cfg_section_t; 51struct cfg_section_t 52{ 53 /* The section name. */ 54 const char *name; 55 56 /* Table of cfg_option_t's. */ 57 apr_hash_t *options; 58}; 59 60 61/* States that a config option value can assume. */ 62typedef enum option_state_t 63{ 64 /* Value still needs to be expanded. 65 This is the initial state for *all* values. */ 66 option_state_needs_expanding, 67 68 /* Value is currently being expanded. 69 This transitional state allows for detecting cyclic dependencies. */ 70 option_state_expanding, 71 72 /* Expanded value is available. 73 Values that never needed expanding directly go into that state 74 skipping option_state_expanding. */ 75 option_state_expanded, 76 77 /* The value expansion is cyclic which results in "undefined" behavior. 78 This is to return a defined value ("") in that case. */ 79 option_state_cyclic 80} option_state_t; 81 82/* Option table entries. */ 83typedef struct cfg_option_t cfg_option_t; 84struct cfg_option_t 85{ 86 /* The option name. */ 87 const char *name; 88 89 /* The option name, converted into a hash key. */ 90 const char *hash_key; 91 92 /* The unexpanded option value. */ 93 const char *value; 94 95 /* The expanded option value. */ 96 const char *x_value; 97 98 /* Expansion state. If this is option_state_expanded, VALUE has already 99 been expanded. In this case, if x_value is NULL, no expansions were 100 necessary, and value should be used directly. */ 101 option_state_t state; 102}; 103 104 105 106svn_error_t * 107svn_config_create2(svn_config_t **cfgp, 108 svn_boolean_t section_names_case_sensitive, 109 svn_boolean_t option_names_case_sensitive, 110 apr_pool_t *result_pool) 111{ 112 svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg)); 113 114 cfg->sections = svn_hash__make(result_pool); 115 cfg->pool = result_pool; 116 cfg->x_pool = svn_pool_create(result_pool); 117 cfg->x_values = FALSE; 118 cfg->tmp_key = svn_stringbuf_create_empty(result_pool); 119 cfg->tmp_value = svn_stringbuf_create_empty(result_pool); 120 cfg->section_names_case_sensitive = section_names_case_sensitive; 121 cfg->option_names_case_sensitive = option_names_case_sensitive; 122 cfg->read_only = FALSE; 123 124 *cfgp = cfg; 125 return SVN_NO_ERROR; 126} 127 128svn_error_t * 129svn_config_read3(svn_config_t **cfgp, const char *file, 130 svn_boolean_t must_exist, 131 svn_boolean_t section_names_case_sensitive, 132 svn_boolean_t option_names_case_sensitive, 133 apr_pool_t *result_pool) 134{ 135 svn_config_t *cfg; 136 svn_error_t *err; 137 138 SVN_ERR(svn_config_create2(&cfg, 139 section_names_case_sensitive, 140 option_names_case_sensitive, 141 result_pool)); 142 143 /* Yes, this is platform-specific code in Subversion, but there's no 144 practical way to migrate it into APR, as it's simultaneously 145 Subversion-specific and Windows-specific. Even if we eventually 146 want to have APR offer a generic config-reading interface, it 147 makes sense to test it here first and migrate it later. */ 148#ifdef WIN32 149 if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN)) 150 err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN, 151 must_exist, result_pool); 152 else 153#endif /* WIN32 */ 154 err = svn_config__parse_file(cfg, file, must_exist, result_pool); 155 156 if (err != SVN_NO_ERROR) 157 return err; 158 else 159 *cfgp = cfg; 160 161 return SVN_NO_ERROR; 162} 163 164svn_error_t * 165svn_config__default_add_value_fn(void *baton, 166 svn_stringbuf_t *section, 167 svn_stringbuf_t *option, 168 svn_stringbuf_t *value) 169{ 170 /* FIXME: We may as well propagate the known string sizes here. */ 171 svn_config_set((svn_config_t *)baton, section->data, 172 option->data, value->data); 173 return SVN_NO_ERROR; 174} 175 176svn_error_t * 177svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream, 178 svn_boolean_t section_names_case_sensitive, 179 svn_boolean_t option_names_case_sensitive, 180 apr_pool_t *result_pool) 181{ 182 svn_config_t *cfg; 183 svn_error_t *err; 184 apr_pool_t *scratch_pool = svn_pool_create(result_pool); 185 186 err = svn_config_create2(&cfg, 187 section_names_case_sensitive, 188 option_names_case_sensitive, 189 result_pool); 190 191 if (err == SVN_NO_ERROR) 192 err = svn_config__parse_stream(stream, 193 svn_config__constructor_create( 194 NULL, NULL, 195 svn_config__default_add_value_fn, 196 scratch_pool), 197 cfg, scratch_pool); 198 199 if (err == SVN_NO_ERROR) 200 *cfgp = cfg; 201 202 svn_pool_destroy(scratch_pool); 203 204 return err; 205} 206 207/* Read various configuration sources into *CFGP, in this order, with 208 * later reads overriding the results of earlier ones: 209 * 210 * 1. SYS_REGISTRY_PATH (only on Win32, but ignored if NULL) 211 * 212 * 2. SYS_FILE_PATH (everywhere, but ignored if NULL) 213 * 214 * 3. USR_REGISTRY_PATH (only on Win32, but ignored if NULL) 215 * 216 * 4. USR_FILE_PATH (everywhere, but ignored if NULL) 217 * 218 * Allocate *CFGP in POOL. Even if no configurations are read, 219 * allocate an empty *CFGP. 220 */ 221static svn_error_t * 222read_all(svn_config_t **cfgp, 223 const char *sys_registry_path, 224 const char *usr_registry_path, 225 const char *sys_file_path, 226 const char *usr_file_path, 227 apr_pool_t *pool) 228{ 229 svn_boolean_t red_config = FALSE; /* "red" is the past tense of "read" */ 230 231 /*** Read system-wide configurations first... ***/ 232 233#ifdef WIN32 234 if (sys_registry_path) 235 { 236 SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE, 237 pool)); 238 red_config = TRUE; 239 } 240#endif /* WIN32 */ 241 242 if (sys_file_path) 243 { 244 if (red_config) 245 SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE)); 246 else 247 { 248 SVN_ERR(svn_config_read3(cfgp, sys_file_path, 249 FALSE, FALSE, FALSE, pool)); 250 red_config = TRUE; 251 } 252 } 253 254 /*** ...followed by per-user configurations. ***/ 255 256#ifdef WIN32 257 if (usr_registry_path) 258 { 259 if (red_config) 260 SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE)); 261 else 262 { 263 SVN_ERR(svn_config_read3(cfgp, usr_registry_path, 264 FALSE, FALSE, FALSE, pool)); 265 red_config = TRUE; 266 } 267 } 268#endif /* WIN32 */ 269 270 if (usr_file_path) 271 { 272 if (red_config) 273 SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE)); 274 else 275 { 276 SVN_ERR(svn_config_read3(cfgp, usr_file_path, 277 FALSE, FALSE, FALSE, pool)); 278 red_config = TRUE; 279 } 280 } 281 282 if (! red_config) 283 SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool)); 284 285 return SVN_NO_ERROR; 286} 287 288 289/* CONFIG_DIR provides an override for the default behavior of reading 290 the default set of overlay files described by read_all()'s doc 291 string. Returns non-NULL *CFG or an error. */ 292static svn_error_t * 293get_category_config(svn_config_t **cfg, 294 const char *config_dir, 295 const char *category, 296 apr_pool_t *pool) 297{ 298 const char *usr_reg_path = NULL, *sys_reg_path = NULL; 299 const char *usr_cfg_path, *sys_cfg_path; 300 svn_error_t *err = NULL; 301 302 *cfg = NULL; 303 304 if (! config_dir) 305 { 306#ifdef WIN32 307 sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH, 308 category, SVN_VA_NULL); 309 usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH, 310 category, SVN_VA_NULL); 311#endif /* WIN32 */ 312 313 err = svn_config__sys_config_path(&sys_cfg_path, category, pool); 314 if ((err) && (err->apr_err == SVN_ERR_BAD_FILENAME)) 315 { 316 sys_cfg_path = NULL; 317 svn_error_clear(err); 318 } 319 else if (err) 320 return err; 321 } 322 else 323 sys_cfg_path = NULL; 324 325 SVN_ERR(svn_config_get_user_config_path(&usr_cfg_path, config_dir, category, 326 pool)); 327 return read_all(cfg, sys_reg_path, usr_reg_path, 328 sys_cfg_path, usr_cfg_path, pool); 329} 330 331 332svn_error_t * 333svn_config_get_config(apr_hash_t **cfg_hash, 334 const char *config_dir, 335 apr_pool_t *pool) 336{ 337 svn_config_t *cfg; 338 *cfg_hash = svn_hash__make(pool); 339 340 SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS, 341 pool)); 342 svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, cfg); 343 344 SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_CONFIG, 345 pool)); 346 svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, cfg); 347 348 return SVN_NO_ERROR; 349} 350 351svn_error_t * 352svn_config__get_default_config(apr_hash_t **cfg_hash, 353 apr_pool_t *pool) 354{ 355 svn_config_t *empty_cfg; 356 *cfg_hash = svn_hash__make(pool); 357 358 SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool)); 359 svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg); 360 361 SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool)); 362 svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, empty_cfg); 363 364 return SVN_NO_ERROR; 365} 366 367 368 369/* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION) 370 pair. Stop if CALLBACK returns TRUE. Allocate from POOL. */ 371static void 372for_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool, 373 svn_boolean_t callback(void *same_baton, 374 cfg_section_t *section, 375 cfg_option_t *option)) 376{ 377 apr_hash_index_t *sec_ndx; 378 for (sec_ndx = apr_hash_first(pool, cfg->sections); 379 sec_ndx != NULL; 380 sec_ndx = apr_hash_next(sec_ndx)) 381 { 382 cfg_section_t *sec = apr_hash_this_val(sec_ndx); 383 apr_hash_index_t *opt_ndx; 384 385 for (opt_ndx = apr_hash_first(pool, sec->options); 386 opt_ndx != NULL; 387 opt_ndx = apr_hash_next(opt_ndx)) 388 { 389 cfg_option_t *opt = apr_hash_this_val(opt_ndx); 390 391 if (callback(baton, sec, opt)) 392 return; 393 } 394 } 395} 396 397 398 399static svn_boolean_t 400merge_callback(void *baton, cfg_section_t *section, cfg_option_t *option) 401{ 402 svn_config_set(baton, section->name, option->name, option->value); 403 return FALSE; 404} 405 406svn_error_t * 407svn_config_merge(svn_config_t *cfg, const char *file, 408 svn_boolean_t must_exist) 409{ 410 /* The original config hash shouldn't change if there's an error 411 while reading the confguration, so read into a temporary table. 412 ### We could use a tmp subpool for this, since merge_cfg is going 413 to be tossed afterwards. Premature optimization, though? */ 414 svn_config_t *merge_cfg; 415 SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist, 416 cfg->section_names_case_sensitive, 417 cfg->option_names_case_sensitive, 418 cfg->pool)); 419 420 /* Now copy the new options into the original table. */ 421 for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback); 422 return SVN_NO_ERROR; 423} 424 425 426 427/* Remove variable expansions from CFG. Walk through the options tree, 428 killing all expanded values, then clear the expanded value pool. */ 429static svn_boolean_t 430rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option) 431{ 432 /* Only reset the expansion state if the value actually contains 433 variable expansions. */ 434 if ( (option->state == option_state_expanded && option->x_value != NULL) 435 || option->state == option_state_cyclic) 436 { 437 option->x_value = NULL; 438 option->state = option_state_needs_expanding; 439 } 440 441 return FALSE; 442} 443 444static void 445remove_expansions(svn_config_t *cfg) 446{ 447 if (!cfg->x_values) 448 return; 449 450 for_each_option(cfg, NULL, cfg->x_pool, rmex_callback); 451 svn_pool_clear(cfg->x_pool); 452 cfg->x_values = FALSE; 453} 454 455 456 457/* Canonicalize a string for hashing. Modifies KEY in place. */ 458static APR_INLINE char * 459make_hash_key(char *key) 460{ 461 register char *p; 462 for (p = key; *p != 0; ++p) 463 *p = (char)apr_tolower(*p); 464 return key; 465} 466 467/* Return the value for KEY in HASH. If CASE_SENSITIVE is FALSE, 468 BUFFER will be used to construct the normalized hash key. */ 469static void * 470get_hash_value(apr_hash_t *hash, 471 svn_stringbuf_t *buffer, 472 const char *key, 473 svn_boolean_t case_sensitive) 474{ 475 apr_size_t i; 476 apr_size_t len = strlen(key); 477 478 if (case_sensitive) 479 return apr_hash_get(hash, key, len); 480 481 svn_stringbuf_ensure(buffer, len); 482 for (i = 0; i < len; ++i) 483 buffer->data[i] = (char)apr_tolower(key[i]); 484 485 return apr_hash_get(hash, buffer->data, len); 486} 487 488/* Return a pointer to an option in CFG, or NULL if it doesn't exist. 489 if SECTIONP is non-null, return a pointer to the option's section. 490 OPTION may be NULL. */ 491static cfg_option_t * 492find_option(svn_config_t *cfg, const char *section, const char *option, 493 cfg_section_t **sectionp) 494{ 495 void *sec_ptr = get_hash_value(cfg->sections, cfg->tmp_key, section, 496 cfg->section_names_case_sensitive); 497 if (sectionp != NULL) 498 *sectionp = sec_ptr; 499 500 if (sec_ptr != NULL && option != NULL) 501 { 502 cfg_section_t *sec = sec_ptr; 503 cfg_option_t *opt = get_hash_value(sec->options, cfg->tmp_key, option, 504 cfg->option_names_case_sensitive); 505 /* NOTE: ConfigParser's sections are case sensitive. */ 506 if (opt == NULL 507 && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0) 508 /* Options which aren't found in the requested section are 509 also sought after in the default section. */ 510 opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec); 511 return opt; 512 } 513 514 return NULL; 515} 516 517 518/* Has a bi-directional dependency with make_string_from_option(). */ 519static svn_boolean_t 520expand_option_value(svn_config_t *cfg, cfg_section_t *section, 521 const char *opt_value, const char **opt_x_valuep, 522 apr_pool_t *x_pool); 523 524 525/* Set *VALUEP according to the OPT's value. A value for X_POOL must 526 only ever be passed into this function by expand_option_value(). */ 527static void 528make_string_from_option(const char **valuep, svn_config_t *cfg, 529 cfg_section_t *section, cfg_option_t *opt, 530 apr_pool_t* x_pool) 531{ 532 /* Expand the option value if necessary. */ 533 if ( opt->state == option_state_expanding 534 || opt->state == option_state_cyclic) 535 { 536 /* Recursion is not supported. Since we can't produce an error 537 * nor should we abort the process, the next best thing is to 538 * report the recursive part as an empty string. */ 539 *valuep = ""; 540 541 /* Go into "value undefined" state. */ 542 opt->state = option_state_cyclic; 543 544 return; 545 } 546 else if (opt->state == option_state_needs_expanding) 547 { 548 /* before attempting to expand an option, check for the placeholder. 549 * If none is there, there is no point in calling expand_option_value. 550 */ 551 if (opt->value && strchr(opt->value, '%')) 552 { 553 apr_pool_t *tmp_pool; 554 555 /* setting read-only mode should have expanded all values 556 * automatically. */ 557 assert(!cfg->read_only); 558 559 tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool)); 560 561 /* Expand the value. During that process, have the option marked 562 * as "expanding" to detect cycles. */ 563 opt->state = option_state_expanding; 564 if (expand_option_value(cfg, section, opt->value, &opt->x_value, 565 tmp_pool)) 566 opt->state = option_state_expanded; 567 else 568 opt->state = option_state_cyclic; 569 570 /* Ensure the expanded value is allocated in a permanent pool. */ 571 if (x_pool != cfg->x_pool) 572 { 573 /* Grab the fully expanded value from tmp_pool before its 574 disappearing act. */ 575 if (opt->x_value) 576 opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value, 577 strlen(opt->x_value)); 578 if (!x_pool) 579 svn_pool_destroy(tmp_pool); 580 } 581 } 582 else 583 { 584 opt->state = option_state_expanded; 585 } 586 } 587 588 if (opt->x_value) 589 *valuep = opt->x_value; 590 else 591 *valuep = opt->value; 592} 593 594 595/* Start of variable-replacement placeholder */ 596#define FMT_START "%(" 597#define FMT_START_LEN (sizeof(FMT_START) - 1) 598 599/* End of variable-replacement placeholder */ 600#define FMT_END ")s" 601#define FMT_END_LEN (sizeof(FMT_END) - 1) 602 603 604/* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP. 605 If no variable replacements are done, set *OPT_X_VALUEP to 606 NULL. Return TRUE if the expanded value is defined and FALSE 607 for recursive definitions. Allocate from X_POOL. */ 608static svn_boolean_t 609expand_option_value(svn_config_t *cfg, cfg_section_t *section, 610 const char *opt_value, const char **opt_x_valuep, 611 apr_pool_t *x_pool) 612{ 613 svn_stringbuf_t *buf = NULL; 614 const char *parse_from = opt_value; 615 const char *copy_from = parse_from; 616 const char *name_start, *name_end; 617 618 while (parse_from != NULL 619 && *parse_from != '\0' 620 && (name_start = strstr(parse_from, FMT_START)) != NULL) 621 { 622 name_start += FMT_START_LEN; 623 if (*name_start == '\0') 624 /* FMT_START at end of opt_value. */ 625 break; 626 627 name_end = strstr(name_start, FMT_END); 628 if (name_end != NULL) 629 { 630 cfg_option_t *x_opt; 631 apr_size_t len = name_end - name_start; 632 char *name = apr_pstrmemdup(x_pool, name_start, len); 633 634 x_opt = find_option(cfg, section->name, name, NULL); 635 636 if (x_opt != NULL) 637 { 638 const char *cstring; 639 640 /* Pass back the sub-pool originally provided by 641 make_string_from_option() as an indication of when it 642 should terminate. */ 643 make_string_from_option(&cstring, cfg, section, x_opt, x_pool); 644 645 /* Values depending on cyclic values must also be marked as 646 * "undefined" because they might themselves form cycles with 647 * the one cycle we just detected. Due to the early abort of 648 * the recursion, we won't follow and thus detect dependent 649 * cycles anymore. 650 */ 651 if (x_opt->state == option_state_cyclic) 652 { 653 *opt_x_valuep = ""; 654 return FALSE; 655 } 656 657 /* Append the plain text preceding the expansion. */ 658 len = name_start - FMT_START_LEN - copy_from; 659 if (buf == NULL) 660 { 661 buf = svn_stringbuf_ncreate(copy_from, len, x_pool); 662 cfg->x_values = TRUE; 663 } 664 else 665 svn_stringbuf_appendbytes(buf, copy_from, len); 666 667 /* Append the expansion and adjust parse pointers. */ 668 svn_stringbuf_appendcstr(buf, cstring); 669 parse_from = name_end + FMT_END_LEN; 670 copy_from = parse_from; 671 } 672 else 673 /* Though ConfigParser considers the failure to resolve 674 the requested expansion an exception condition, we 675 consider it to be plain text, and look for the start of 676 the next one. */ 677 parse_from = name_end + FMT_END_LEN; 678 } 679 else 680 /* Though ConfigParser treats unterminated format specifiers 681 as an exception condition, we consider them to be plain 682 text. The fact that there are no more format specifier 683 endings means we're done parsing. */ 684 parse_from = NULL; 685 } 686 687 if (buf != NULL) 688 { 689 /* Copy the remainder of the plain text. */ 690 svn_stringbuf_appendcstr(buf, copy_from); 691 *opt_x_valuep = buf->data; 692 } 693 else 694 *opt_x_valuep = NULL; 695 696 /* Expansion has a well-defined answer. */ 697 return TRUE; 698} 699 700static cfg_section_t * 701svn_config_addsection(svn_config_t *cfg, 702 const char *section) 703{ 704 cfg_section_t *s; 705 const char *hash_key; 706 707 s = apr_palloc(cfg->pool, sizeof(cfg_section_t)); 708 s->name = apr_pstrdup(cfg->pool, section); 709 if(cfg->section_names_case_sensitive) 710 hash_key = s->name; 711 else 712 hash_key = make_hash_key(apr_pstrdup(cfg->pool, section)); 713 s->options = svn_hash__make(cfg->pool); 714 715 svn_hash_sets(cfg->sections, hash_key, s); 716 717 return s; 718} 719 720static void 721svn_config_create_option(cfg_option_t **opt, 722 const char *option, 723 const char *value, 724 svn_boolean_t option_names_case_sensitive, 725 apr_pool_t *pool) 726{ 727 cfg_option_t *o; 728 729 o = apr_palloc(pool, sizeof(cfg_option_t)); 730 o->name = apr_pstrdup(pool, option); 731 if(option_names_case_sensitive) 732 o->hash_key = o->name; 733 else 734 o->hash_key = make_hash_key(apr_pstrdup(pool, option)); 735 736 o->value = apr_pstrdup(pool, value); 737 o->x_value = NULL; 738 o->state = option_state_needs_expanding; 739 740 *opt = o; 741} 742 743svn_boolean_t 744svn_config__is_expanded(svn_config_t *cfg, 745 const char *section, 746 const char *option) 747{ 748 cfg_option_t *opt; 749 750 if (cfg == NULL) 751 return FALSE; 752 753 /* does the option even exist? */ 754 opt = find_option(cfg, section, option, NULL); 755 if (opt == NULL) 756 return FALSE; 757 758 /* already expanded? */ 759 if ( opt->state == option_state_expanded 760 || opt->state == option_state_cyclic) 761 return TRUE; 762 763 /* needs expansion? */ 764 if (opt->value && strchr(opt->value, '%')) 765 return FALSE; 766 767 /* no expansion necessary */ 768 return TRUE; 769} 770 771 772void 773svn_config_get(svn_config_t *cfg, const char **valuep, 774 const char *section, const char *option, 775 const char *default_value) 776{ 777 *valuep = default_value; 778 if (cfg) 779 { 780 cfg_section_t *sec; 781 cfg_option_t *opt = find_option(cfg, section, option, &sec); 782 if (opt != NULL) 783 { 784 make_string_from_option(valuep, cfg, sec, opt, NULL); 785 } 786 else 787 /* before attempting to expand an option, check for the placeholder. 788 * If there is none, there is no point in calling expand_option_value. 789 */ 790 if (default_value && strchr(default_value, '%')) 791 { 792 apr_pool_t *tmp_pool = svn_pool_create(cfg->pool); 793 const char *x_default; 794 if (!expand_option_value(cfg, sec, default_value, &x_default, 795 tmp_pool)) 796 { 797 /* Recursive definitions are not supported. 798 Normalize the answer in that case. */ 799 *valuep = ""; 800 } 801 else if (x_default) 802 { 803 svn_stringbuf_set(cfg->tmp_value, x_default); 804 *valuep = cfg->tmp_value->data; 805 } 806 svn_pool_destroy(tmp_pool); 807 } 808 } 809} 810 811 812 813void 814svn_config_set(svn_config_t *cfg, 815 const char *section, const char *option, 816 const char *value) 817{ 818 cfg_section_t *sec; 819 cfg_option_t *opt; 820 821 /* Ignore write attempts to r/o configurations. 822 * 823 * Since we should never try to modify r/o data, trigger an assertion 824 * in debug mode. 825 */ 826#ifdef SVN_DEBUG 827 SVN_ERR_ASSERT_NO_RETURN(!cfg->read_only); 828#endif 829 if (cfg->read_only) 830 return; 831 832 remove_expansions(cfg); 833 834 opt = find_option(cfg, section, option, &sec); 835 if (opt != NULL) 836 { 837 /* Replace the option's value. */ 838 opt->value = apr_pstrdup(cfg->pool, value); 839 opt->state = option_state_needs_expanding; 840 return; 841 } 842 843 /* Create a new option */ 844 svn_config_create_option(&opt, option, value, 845 cfg->option_names_case_sensitive, 846 cfg->pool); 847 848 if (sec == NULL) 849 { 850 /* Even the section doesn't exist. Create it. */ 851 sec = svn_config_addsection(cfg, section); 852 } 853 854 svn_hash_sets(sec->options, opt->hash_key, opt); 855} 856 857 858 859/* Set *BOOLP to true or false depending (case-insensitively) on INPUT. 860 If INPUT is null, set *BOOLP to DEFAULT_VALUE. 861 862 INPUT is a string indicating truth or falsehood in any of the usual 863 ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc. 864 865 If INPUT is neither NULL nor a recognized string, return an error 866 with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in 867 constructing the error string. */ 868static svn_error_t * 869get_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value, 870 const char *section, const char *option) 871{ 872 svn_tristate_t value = svn_tristate__from_word(input); 873 874 if (value == svn_tristate_true) 875 *boolp = TRUE; 876 else if (value == svn_tristate_false) 877 *boolp = FALSE; 878 else if (input == NULL) /* no value provided */ 879 *boolp = default_value; 880 881 else if (section) /* unrecognized value */ 882 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 883 _("Config error: invalid boolean " 884 "value '%s' for '[%s] %s'"), 885 input, section, option); 886 else 887 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 888 _("Config error: invalid boolean " 889 "value '%s' for '%s'"), 890 input, option); 891 892 return SVN_NO_ERROR; 893} 894 895 896svn_error_t * 897svn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep, 898 const char *section, const char *option, 899 svn_boolean_t default_value) 900{ 901 const char *tmp_value; 902 svn_config_get(cfg, &tmp_value, section, option, NULL); 903 return get_bool(valuep, tmp_value, default_value, section, option); 904} 905 906 907 908void 909svn_config_set_bool(svn_config_t *cfg, 910 const char *section, const char *option, 911 svn_boolean_t value) 912{ 913 svn_config_set(cfg, section, option, 914 (value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE)); 915} 916 917svn_error_t * 918svn_config_get_int64(svn_config_t *cfg, 919 apr_int64_t *valuep, 920 const char *section, 921 const char *option, 922 apr_int64_t default_value) 923{ 924 const char *tmp_value; 925 svn_config_get(cfg, &tmp_value, section, option, NULL); 926 if (tmp_value) 927 return svn_cstring_strtoi64(valuep, tmp_value, 928 APR_INT64_MIN, APR_INT64_MAX, 10); 929 930 *valuep = default_value; 931 return SVN_NO_ERROR; 932} 933 934void 935svn_config_set_int64(svn_config_t *cfg, 936 const char *section, 937 const char *option, 938 apr_int64_t value) 939{ 940 svn_config_set(cfg, section, option, 941 apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value)); 942} 943 944svn_error_t * 945svn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep, 946 const char *section, const char *option, 947 const char* default_value) 948{ 949 const char *tmp_value; 950 951 svn_config_get(cfg, &tmp_value, section, option, NULL); 952 953 if (! tmp_value) 954 tmp_value = default_value; 955 956 if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK))) 957 { 958 *valuep = SVN_CONFIG_ASK; 959 } 960 else 961 { 962 svn_boolean_t bool_val; 963 /* We already incorporated default_value into tmp_value if 964 necessary, so the FALSE below will be ignored unless the 965 caller is doing something it shouldn't be doing. */ 966 SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option)); 967 *valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE; 968 } 969 970 return SVN_NO_ERROR; 971} 972 973svn_error_t * 974svn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep, 975 const char *section, const char *option, 976 const char *unknown_value, 977 svn_tristate_t default_value) 978{ 979 const char *tmp_value; 980 981 svn_config_get(cfg, &tmp_value, section, option, NULL); 982 983 if (! tmp_value) 984 { 985 *valuep = default_value; 986 } 987 else if (0 == svn_cstring_casecmp(tmp_value, unknown_value)) 988 { 989 *valuep = svn_tristate_unknown; 990 } 991 else 992 { 993 svn_boolean_t bool_val; 994 /* We already incorporated default_value into tmp_value if 995 necessary, so the FALSE below will be ignored unless the 996 caller is doing something it shouldn't be doing. */ 997 SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option)); 998 *valuep = bool_val ? svn_tristate_true : svn_tristate_false; 999 } 1000 1001 return SVN_NO_ERROR; 1002} 1003 1004int 1005svn_config_enumerate_sections(svn_config_t *cfg, 1006 svn_config_section_enumerator_t callback, 1007 void *baton) 1008{ 1009 apr_hash_index_t *sec_ndx; 1010 int count = 0; 1011 apr_pool_t *subpool = svn_pool_create(cfg->x_pool); 1012 1013 for (sec_ndx = apr_hash_first(subpool, cfg->sections); 1014 sec_ndx != NULL; 1015 sec_ndx = apr_hash_next(sec_ndx)) 1016 { 1017 void *sec_ptr; 1018 cfg_section_t *sec; 1019 1020 apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr); 1021 sec = sec_ptr; 1022 ++count; 1023 if (!callback(sec->name, baton)) 1024 break; 1025 } 1026 1027 svn_pool_destroy(subpool); 1028 return count; 1029} 1030 1031 1032int 1033svn_config_enumerate_sections2(svn_config_t *cfg, 1034 svn_config_section_enumerator2_t callback, 1035 void *baton, apr_pool_t *pool) 1036{ 1037 apr_hash_index_t *sec_ndx; 1038 apr_pool_t *iteration_pool; 1039 int count = 0; 1040 1041 iteration_pool = svn_pool_create(pool); 1042 for (sec_ndx = apr_hash_first(pool, cfg->sections); 1043 sec_ndx != NULL; 1044 sec_ndx = apr_hash_next(sec_ndx)) 1045 { 1046 cfg_section_t *sec = apr_hash_this_val(sec_ndx); 1047 1048 ++count; 1049 svn_pool_clear(iteration_pool); 1050 if (!callback(sec->name, baton, iteration_pool)) 1051 break; 1052 } 1053 svn_pool_destroy(iteration_pool); 1054 1055 return count; 1056} 1057 1058 1059 1060int 1061svn_config_enumerate(svn_config_t *cfg, const char *section, 1062 svn_config_enumerator_t callback, void *baton) 1063{ 1064 cfg_section_t *sec; 1065 apr_hash_index_t *opt_ndx; 1066 int count; 1067 apr_pool_t *subpool; 1068 1069 find_option(cfg, section, NULL, &sec); 1070 if (sec == NULL) 1071 return 0; 1072 1073 subpool = svn_pool_create(cfg->pool); 1074 count = 0; 1075 for (opt_ndx = apr_hash_first(subpool, sec->options); 1076 opt_ndx != NULL; 1077 opt_ndx = apr_hash_next(opt_ndx)) 1078 { 1079 cfg_option_t *opt = apr_hash_this_val(opt_ndx); 1080 const char *temp_value; 1081 1082 ++count; 1083 make_string_from_option(&temp_value, cfg, sec, opt, NULL); 1084 if (!callback(opt->name, temp_value, baton)) 1085 break; 1086 } 1087 1088 svn_pool_destroy(subpool); 1089 return count; 1090} 1091 1092 1093int 1094svn_config_enumerate2(svn_config_t *cfg, const char *section, 1095 svn_config_enumerator2_t callback, void *baton, 1096 apr_pool_t *pool) 1097{ 1098 cfg_section_t *sec; 1099 apr_hash_index_t *opt_ndx; 1100 apr_pool_t *iteration_pool; 1101 int count; 1102 1103 find_option(cfg, section, NULL, &sec); 1104 if (sec == NULL) 1105 return 0; 1106 1107 iteration_pool = svn_pool_create(pool); 1108 count = 0; 1109 for (opt_ndx = apr_hash_first(pool, sec->options); 1110 opt_ndx != NULL; 1111 opt_ndx = apr_hash_next(opt_ndx)) 1112 { 1113 cfg_option_t *opt = apr_hash_this_val(opt_ndx); 1114 const char *temp_value; 1115 1116 ++count; 1117 make_string_from_option(&temp_value, cfg, sec, opt, NULL); 1118 svn_pool_clear(iteration_pool); 1119 if (!callback(opt->name, temp_value, baton, iteration_pool)) 1120 break; 1121 } 1122 svn_pool_destroy(iteration_pool); 1123 1124 return count; 1125} 1126 1127 1128 1129/* Baton for search_groups() */ 1130struct search_groups_baton 1131{ 1132 const char *key; /* Provided by caller of svn_config_find_group */ 1133 const char *match; /* Filled in by search_groups */ 1134 apr_pool_t *pool; 1135}; 1136 1137 1138/* This is an `svn_config_enumerator_t' function, and BATON is a 1139 * `struct search_groups_baton *'. 1140 */ 1141static svn_boolean_t search_groups(const char *name, 1142 const char *value, 1143 void *baton, 1144 apr_pool_t *pool) 1145{ 1146 struct search_groups_baton *b = baton; 1147 apr_array_header_t *list; 1148 1149 list = svn_cstring_split(value, ",", TRUE, pool); 1150 if (svn_cstring_match_glob_list(b->key, list)) 1151 { 1152 /* Fill in the match and return false, to stop enumerating. */ 1153 b->match = apr_pstrdup(b->pool, name); 1154 return FALSE; 1155 } 1156 else 1157 return TRUE; 1158} 1159 1160 1161const char *svn_config_find_group(svn_config_t *cfg, const char *key, 1162 const char *master_section, 1163 apr_pool_t *pool) 1164{ 1165 struct search_groups_baton gb; 1166 1167 gb.key = key; 1168 gb.match = NULL; 1169 gb.pool = pool; 1170 (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool); 1171 return gb.match; 1172} 1173 1174 1175const char* 1176svn_config_get_server_setting(svn_config_t *cfg, 1177 const char* server_group, 1178 const char* option_name, 1179 const char* default_value) 1180{ 1181 const char *retval; 1182 svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL, 1183 option_name, default_value); 1184 if (server_group) 1185 { 1186 svn_config_get(cfg, &retval, server_group, option_name, retval); 1187 } 1188 return retval; 1189} 1190 1191 1192svn_error_t * 1193svn_config_dup(svn_config_t **cfgp, 1194 const svn_config_t *src, 1195 apr_pool_t *pool) 1196{ 1197 apr_hash_index_t *sectidx; 1198 apr_hash_index_t *optidx; 1199 1200 *cfgp = 0; 1201 SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool)); 1202 1203 (*cfgp)->x_values = src->x_values; 1204 (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive; 1205 (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive; 1206 1207 for (sectidx = apr_hash_first(pool, src->sections); 1208 sectidx != NULL; 1209 sectidx = apr_hash_next(sectidx)) 1210 { 1211 const void *sectkey; 1212 void *sectval; 1213 apr_ssize_t sectkeyLength; 1214 cfg_section_t * srcsect; 1215 cfg_section_t * destsec; 1216 1217 apr_hash_this(sectidx, §key, §keyLength, §val); 1218 srcsect = sectval; 1219 1220 destsec = svn_config_addsection(*cfgp, srcsect->name); 1221 1222 for (optidx = apr_hash_first(pool, srcsect->options); 1223 optidx != NULL; 1224 optidx = apr_hash_next(optidx)) 1225 { 1226 const void *optkey; 1227 void *optval; 1228 apr_ssize_t optkeyLength; 1229 cfg_option_t *srcopt; 1230 cfg_option_t *destopt; 1231 1232 apr_hash_this(optidx, &optkey, &optkeyLength, &optval); 1233 srcopt = optval; 1234 1235 svn_config_create_option(&destopt, srcopt->name, srcopt->value, 1236 (*cfgp)->option_names_case_sensitive, 1237 pool); 1238 1239 destopt->value = apr_pstrdup(pool, srcopt->value); 1240 destopt->x_value = apr_pstrdup(pool, srcopt->x_value); 1241 destopt->state = srcopt->state; 1242 apr_hash_set(destsec->options, 1243 apr_pstrdup(pool, (const char*)optkey), 1244 optkeyLength, destopt); 1245 } 1246 } 1247 1248 return SVN_NO_ERROR; 1249} 1250 1251svn_error_t * 1252svn_config_copy_config(apr_hash_t **cfg_hash, 1253 apr_hash_t *src_hash, 1254 apr_pool_t *pool) 1255{ 1256 apr_hash_index_t *cidx; 1257 1258 *cfg_hash = svn_hash__make(pool); 1259 for (cidx = apr_hash_first(pool, src_hash); 1260 cidx != NULL; 1261 cidx = apr_hash_next(cidx)) 1262 { 1263 const void *ckey; 1264 void *cval; 1265 apr_ssize_t ckeyLength; 1266 svn_config_t * srcconfig; 1267 svn_config_t * destconfig; 1268 1269 apr_hash_this(cidx, &ckey, &ckeyLength, &cval); 1270 srcconfig = cval; 1271 1272 SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool)); 1273 1274 apr_hash_set(*cfg_hash, 1275 apr_pstrdup(pool, (const char*)ckey), 1276 ckeyLength, destconfig); 1277 } 1278 1279 return SVN_NO_ERROR; 1280} 1281 1282svn_error_t* 1283svn_config_get_server_setting_int(svn_config_t *cfg, 1284 const char *server_group, 1285 const char *option_name, 1286 apr_int64_t default_value, 1287 apr_int64_t *result_value, 1288 apr_pool_t *pool) 1289{ 1290 const char* tmp_value; 1291 char *end_pos; 1292 1293 tmp_value = svn_config_get_server_setting(cfg, server_group, 1294 option_name, NULL); 1295 if (tmp_value == NULL) 1296 *result_value = default_value; 1297 else 1298 { 1299 /* read tmp_value as an int now */ 1300 *result_value = apr_strtoi64(tmp_value, &end_pos, 0); 1301 1302 if (*end_pos != 0) 1303 { 1304 return svn_error_createf 1305 (SVN_ERR_BAD_CONFIG_VALUE, NULL, 1306 _("Config error: invalid integer value '%s'"), 1307 tmp_value); 1308 } 1309 } 1310 1311 return SVN_NO_ERROR; 1312} 1313 1314svn_error_t * 1315svn_config_get_server_setting_bool(svn_config_t *cfg, 1316 svn_boolean_t *valuep, 1317 const char *server_group, 1318 const char *option_name, 1319 svn_boolean_t default_value) 1320{ 1321 const char* tmp_value; 1322 tmp_value = svn_config_get_server_setting(cfg, server_group, 1323 option_name, NULL); 1324 return get_bool(valuep, tmp_value, default_value, 1325 server_group, option_name); 1326} 1327 1328 1329svn_boolean_t 1330svn_config_has_section(svn_config_t *cfg, const char *section) 1331{ 1332 return NULL != get_hash_value(cfg->sections, cfg->tmp_key, section, 1333 cfg->section_names_case_sensitive); 1334} 1335 1336svn_error_t * 1337svn_config__write(svn_stream_t *stream, 1338 const struct svn_config_t *cfg, 1339 apr_pool_t *scratch_pool) 1340{ 1341 apr_hash_index_t *section_i; 1342 apr_hash_index_t *options_i; 1343 apr_pool_t *section_pool = svn_pool_create(scratch_pool); 1344 apr_pool_t *options_pool = svn_pool_create(scratch_pool); 1345 1346 for (section_i = apr_hash_first(scratch_pool, cfg->sections); 1347 section_i != NULL; 1348 section_i = apr_hash_next(section_i)) 1349 { 1350 cfg_section_t *section = apr_hash_this_val(section_i); 1351 svn_pool_clear(section_pool); 1352 SVN_ERR(svn_stream_printf(stream, section_pool, "\n[%s]\n", 1353 section->name)); 1354 1355 for (options_i = apr_hash_first(section_pool, section->options); 1356 options_i != NULL; 1357 options_i = apr_hash_next(options_i)) 1358 { 1359 cfg_option_t *option = apr_hash_this_val(options_i); 1360 svn_pool_clear(options_pool); 1361 SVN_ERR(svn_stream_printf(stream, options_pool, "%s=%s\n", 1362 option->name, option->value)); 1363 } 1364 } 1365 1366 svn_pool_destroy(section_pool); 1367 svn_pool_destroy(options_pool); 1368 1369 return SVN_NO_ERROR; 1370} 1371 1372