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