xref: /freebsd-13-stable/contrib/subversion/subversion/libsvn_subr/config.c (revision 7725780a600dd28ae2c61f2d51c76d41a070958b)
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. */
50 typedef struct cfg_section_t cfg_section_t;
51 struct 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. */
62 typedef 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. */
83 typedef struct cfg_option_t cfg_option_t;
84 struct 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 
106 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)107 svn_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 
128 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)129 svn_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 
164 svn_error_t *
svn_config__default_add_value_fn(void * baton,svn_stringbuf_t * section,svn_stringbuf_t * option,svn_stringbuf_t * value)165 svn_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 
176 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)177 svn_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  */
221 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)222 read_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. */
292 static svn_error_t *
get_category_config(svn_config_t ** cfg,const char * config_dir,const char * category,apr_pool_t * pool)293 get_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 
332 svn_error_t *
svn_config_get_config(apr_hash_t ** cfg_hash,const char * config_dir,apr_pool_t * pool)333 svn_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 
351 svn_error_t *
svn_config__get_default_config(apr_hash_t ** cfg_hash,apr_pool_t * pool)352 svn_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. */
371 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))372 for_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 
399 static svn_boolean_t
merge_callback(void * baton,cfg_section_t * section,cfg_option_t * option)400 merge_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 
406 svn_error_t *
svn_config_merge(svn_config_t * cfg,const char * file,svn_boolean_t must_exist)407 svn_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. */
429 static svn_boolean_t
rmex_callback(void * baton,cfg_section_t * section,cfg_option_t * option)430 rmex_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 
444 static void
remove_expansions(svn_config_t * cfg)445 remove_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. */
458 static APR_INLINE char *
make_hash_key(char * key)459 make_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. */
469 static void *
get_hash_value(apr_hash_t * hash,svn_stringbuf_t * buffer,const char * key,svn_boolean_t case_sensitive)470 get_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. */
491 static cfg_option_t *
find_option(svn_config_t * cfg,const char * section,const char * option,cfg_section_t ** sectionp)492 find_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(). */
519 static svn_boolean_t
520 expand_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(). */
527 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)528 make_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. */
608 static svn_boolean_t
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)609 expand_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 
700 static cfg_section_t *
svn_config_addsection(svn_config_t * cfg,const char * section)701 svn_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 
720 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)721 svn_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 
743 svn_boolean_t
svn_config__is_expanded(svn_config_t * cfg,const char * section,const char * option)744 svn_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 
772 void
svn_config_get(svn_config_t * cfg,const char ** valuep,const char * section,const char * option,const char * default_value)773 svn_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 
813 void
svn_config_set(svn_config_t * cfg,const char * section,const char * option,const char * value)814 svn_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. */
868 static svn_error_t *
get_bool(svn_boolean_t * boolp,const char * input,svn_boolean_t default_value,const char * section,const char * option)869 get_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 
896 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)897 svn_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 
908 void
svn_config_set_bool(svn_config_t * cfg,const char * section,const char * option,svn_boolean_t value)909 svn_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 
917 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)918 svn_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 
934 void
svn_config_set_int64(svn_config_t * cfg,const char * section,const char * option,apr_int64_t value)935 svn_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 
944 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)945 svn_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 
973 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)974 svn_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 
1004 int
svn_config_enumerate_sections(svn_config_t * cfg,svn_config_section_enumerator_t callback,void * baton)1005 svn_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 
1032 int
svn_config_enumerate_sections2(svn_config_t * cfg,svn_config_section_enumerator2_t callback,void * baton,apr_pool_t * pool)1033 svn_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 
1060 int
svn_config_enumerate(svn_config_t * cfg,const char * section,svn_config_enumerator_t callback,void * baton)1061 svn_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 
1093 int
svn_config_enumerate2(svn_config_t * cfg,const char * section,svn_config_enumerator2_t callback,void * baton,apr_pool_t * pool)1094 svn_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() */
1130 struct 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  */
search_groups(const char * name,const char * value,void * baton,apr_pool_t * pool)1141 static 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 
svn_config_find_group(svn_config_t * cfg,const char * key,const char * master_section,apr_pool_t * pool)1161 const 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 
1175 const char*
svn_config_get_server_setting(svn_config_t * cfg,const char * server_group,const char * option_name,const char * default_value)1176 svn_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 
1192 svn_error_t *
svn_config_dup(svn_config_t ** cfgp,const svn_config_t * src,apr_pool_t * pool)1193 svn_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, &sectkey, &sectkeyLength, &sectval);
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 
1251 svn_error_t *
svn_config_copy_config(apr_hash_t ** cfg_hash,apr_hash_t * src_hash,apr_pool_t * pool)1252 svn_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 
1282 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)1283 svn_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 
1314 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)1315 svn_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 
1329 svn_boolean_t
svn_config_has_section(svn_config_t * cfg,const char * section)1330 svn_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 
1336 svn_error_t *
svn_config__write(svn_stream_t * stream,const struct svn_config_t * cfg,apr_pool_t * scratch_pool)1337 svn_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