config.c revision 289166
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#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
41
42
43/* Section table entries. */
44typedef struct cfg_section_t cfg_section_t;
45struct 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. */
56typedef struct cfg_option_t cfg_option_t;
57struct 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
79svn_error_t *
80svn_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
100svn_error_t *
101svn_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
136svn_error_t *
137svn_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 */
176static svn_error_t *
177read_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. */
246static svn_error_t *
247get_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
286svn_error_t *
287svn_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
313/* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION)
314   pair.  Stop if CALLBACK returns TRUE.  Allocate from POOL. */
315static void
316for_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
351static svn_boolean_t
352merge_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
358svn_error_t *
359svn_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
379/* Remove variable expansions from CFG.  Walk through the options tree,
380   killing all expanded values, then clear the expanded value pool. */
381static svn_boolean_t
382rmex_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
395static void
396remove_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
408/* Canonicalize a string for hashing.  Modifies KEY in place. */
409static APR_INLINE char *
410make_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. */
422static cfg_option_t *
423find_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(). */
464static void
465expand_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(). */
472static void
473make_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. */
526static void
527expand_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
603static cfg_section_t *
604svn_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
622static void
623svn_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
646void
647svn_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
681void
682svn_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
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. */
725static svn_error_t *
726get_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
753svn_error_t *
754svn_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
765void
766svn_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
774svn_error_t *
775svn_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
791void
792svn_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
801svn_error_t *
802svn_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
830svn_error_t *
831svn_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
861int
862svn_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
889int
890svn_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
920int
921svn_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
957int
958svn_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
997/* Baton for search_groups() */
998struct 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 */
1009static 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
1029const 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
1043const char*
1044svn_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
1060svn_error_t *
1061svn_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
1119svn_error_t *
1120svn_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
1150svn_error_t*
1151svn_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
1182svn_error_t *
1183svn_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
1197svn_boolean_t
1198svn_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}
1210