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