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)
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              svn_pool_destroy(tmp_pool);
498            }
499        }
500      else
501        {
502          opt->expanded = TRUE;
503        }
504    }
505
506  if (opt->x_value)
507    *valuep = opt->x_value;
508  else
509    *valuep = opt->value;
510}
511
512
513/* Start of variable-replacement placeholder */
514#define FMT_START     "%("
515#define FMT_START_LEN (sizeof(FMT_START) - 1)
516
517/* End of variable-replacement placeholder */
518#define FMT_END       ")s"
519#define FMT_END_LEN   (sizeof(FMT_END) - 1)
520
521
522/* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
523   If no variable replacements are done, set *OPT_X_VALUEP to
524   NULL. Allocate from X_POOL. */
525static void
526expand_option_value(svn_config_t *cfg, cfg_section_t *section,
527                    const char *opt_value, const char **opt_x_valuep,
528                    apr_pool_t *x_pool)
529{
530  svn_stringbuf_t *buf = NULL;
531  const char *parse_from = opt_value;
532  const char *copy_from = parse_from;
533  const char *name_start, *name_end;
534
535  while (parse_from != NULL
536         && *parse_from != '\0'
537         && (name_start = strstr(parse_from, FMT_START)) != NULL)
538    {
539      name_start += FMT_START_LEN;
540      if (*name_start == '\0')
541        /* FMT_START at end of opt_value. */
542        break;
543
544      name_end = strstr(name_start, FMT_END);
545      if (name_end != NULL)
546        {
547          cfg_option_t *x_opt;
548          apr_size_t len = name_end - name_start;
549          char *name = apr_pstrmemdup(x_pool, name_start, len);
550
551          x_opt = find_option(cfg, section->name, name, NULL);
552
553          if (x_opt != NULL)
554            {
555              const char *cstring;
556
557              /* Pass back the sub-pool originally provided by
558                 make_string_from_option() as an indication of when it
559                 should terminate. */
560              make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
561
562              /* Append the plain text preceding the expansion. */
563              len = name_start - FMT_START_LEN - copy_from;
564              if (buf == NULL)
565                {
566                  buf = svn_stringbuf_ncreate(copy_from, len, x_pool);
567                  cfg->x_values = TRUE;
568                }
569              else
570                svn_stringbuf_appendbytes(buf, copy_from, len);
571
572              /* Append the expansion and adjust parse pointers. */
573              svn_stringbuf_appendcstr(buf, cstring);
574              parse_from = name_end + FMT_END_LEN;
575              copy_from = parse_from;
576            }
577          else
578            /* Though ConfigParser considers the failure to resolve
579               the requested expansion an exception condition, we
580               consider it to be plain text, and look for the start of
581               the next one. */
582            parse_from = name_end + FMT_END_LEN;
583        }
584      else
585        /* Though ConfigParser treats unterminated format specifiers
586           as an exception condition, we consider them to be plain
587           text.  The fact that there are no more format specifier
588           endings means we're done parsing. */
589        parse_from = NULL;
590    }
591
592  if (buf != NULL)
593    {
594      /* Copy the remainder of the plain text. */
595      svn_stringbuf_appendcstr(buf, copy_from);
596      *opt_x_valuep = buf->data;
597    }
598  else
599    *opt_x_valuep = NULL;
600}
601
602static cfg_section_t *
603svn_config_addsection(svn_config_t *cfg,
604                      const char *section)
605{
606  cfg_section_t *s;
607  const char *hash_key;
608
609  s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
610  s->name = apr_pstrdup(cfg->pool, section);
611  if(cfg->section_names_case_sensitive)
612    hash_key = s->name;
613  else
614    hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
615  s->options = apr_hash_make(cfg->pool);
616  svn_hash_sets(cfg->sections, hash_key, s);
617
618  return s;
619}
620
621static void
622svn_config_create_option(cfg_option_t **opt,
623                         const char *option,
624                         const char *value,
625                         svn_boolean_t option_names_case_sensitive,
626                         apr_pool_t *pool)
627{
628  cfg_option_t *o;
629
630  o = apr_palloc(pool, sizeof(cfg_option_t));
631  o->name = apr_pstrdup(pool, option);
632  if(option_names_case_sensitive)
633    o->hash_key = o->name;
634  else
635    o->hash_key = make_hash_key(apr_pstrdup(pool, option));
636
637  o->value = apr_pstrdup(pool, value);
638  o->x_value = NULL;
639  o->expanded = FALSE;
640
641  *opt = o;
642}
643
644
645void
646svn_config_get(svn_config_t *cfg, const char **valuep,
647               const char *section, const char *option,
648               const char *default_value)
649{
650  *valuep = default_value;
651  if (cfg)
652    {
653      cfg_section_t *sec;
654      cfg_option_t *opt = find_option(cfg, section, option, &sec);
655      if (opt != NULL)
656        {
657          make_string_from_option(valuep, cfg, sec, opt, NULL);
658        }
659      else
660        /* before attempting to expand an option, check for the placeholder.
661         * If none is there, there is no point in calling expand_option_value.
662         */
663        if (default_value && strchr(default_value, '%'))
664          {
665            apr_pool_t *tmp_pool = svn_pool_create(cfg->x_pool);
666            const char *x_default;
667            expand_option_value(cfg, sec, default_value, &x_default, tmp_pool);
668            if (x_default)
669              {
670                svn_stringbuf_set(cfg->tmp_value, x_default);
671                *valuep = cfg->tmp_value->data;
672              }
673            svn_pool_destroy(tmp_pool);
674          }
675    }
676}
677
678
679
680void
681svn_config_set(svn_config_t *cfg,
682               const char *section, const char *option,
683               const char *value)
684{
685  cfg_section_t *sec;
686  cfg_option_t *opt;
687
688  remove_expansions(cfg);
689
690  opt = find_option(cfg, section, option, &sec);
691  if (opt != NULL)
692    {
693      /* Replace the option's value. */
694      opt->value = apr_pstrdup(cfg->pool, value);
695      opt->expanded = FALSE;
696      return;
697    }
698
699  /* Create a new option */
700  svn_config_create_option(&opt, option, value,
701                           cfg->option_names_case_sensitive,
702                           cfg->pool);
703
704  if (sec == NULL)
705    {
706      /* Even the section doesn't exist. Create it. */
707      sec = svn_config_addsection(cfg, section);
708    }
709
710  svn_hash_sets(sec->options, opt->hash_key, opt);
711}
712
713
714
715/* Set *BOOLP to true or false depending (case-insensitively) on INPUT.
716   If INPUT is null, set *BOOLP to DEFAULT_VALUE.
717
718   INPUT is a string indicating truth or falsehood in any of the usual
719   ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc.
720
721   If INPUT is neither NULL nor a recognized string, return an error
722   with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in
723   constructing the error string. */
724static svn_error_t *
725get_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value,
726         const char *section, const char *option)
727{
728  svn_tristate_t value = svn_tristate__from_word(input);
729
730  if (value == svn_tristate_true)
731    *boolp = TRUE;
732  else if (value == svn_tristate_false)
733    *boolp = FALSE;
734  else if (input == NULL) /* no value provided */
735    *boolp = default_value;
736
737  else if (section) /* unrecognized value */
738    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
739                             _("Config error: invalid boolean "
740                               "value '%s' for '[%s] %s'"),
741                             input, section, option);
742  else
743    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
744                             _("Config error: invalid boolean "
745                               "value '%s' for '%s'"),
746                             input, option);
747
748  return SVN_NO_ERROR;
749}
750
751
752svn_error_t *
753svn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep,
754                    const char *section, const char *option,
755                    svn_boolean_t default_value)
756{
757  const char *tmp_value;
758  svn_config_get(cfg, &tmp_value, section, option, NULL);
759  return get_bool(valuep, tmp_value, default_value, section, option);
760}
761
762
763
764void
765svn_config_set_bool(svn_config_t *cfg,
766                    const char *section, const char *option,
767                    svn_boolean_t value)
768{
769  svn_config_set(cfg, section, option,
770                 (value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE));
771}
772
773svn_error_t *
774svn_config_get_int64(svn_config_t *cfg,
775                     apr_int64_t *valuep,
776                     const char *section,
777                     const char *option,
778                     apr_int64_t default_value)
779{
780  const char *tmp_value;
781  svn_config_get(cfg, &tmp_value, section, option, NULL);
782  if (tmp_value)
783    return svn_cstring_strtoi64(valuep, tmp_value,
784                                APR_INT64_MIN, APR_INT64_MAX, 10);
785
786  *valuep = default_value;
787  return SVN_NO_ERROR;
788}
789
790void
791svn_config_set_int64(svn_config_t *cfg,
792                     const char *section,
793                     const char *option,
794                     apr_int64_t value)
795{
796  svn_config_set(cfg, section, option,
797                 apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value));
798}
799
800svn_error_t *
801svn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep,
802                          const char *section, const char *option,
803                          const char* default_value)
804{
805  const char *tmp_value;
806
807  svn_config_get(cfg, &tmp_value, section, option, NULL);
808
809  if (! tmp_value)
810    tmp_value = default_value;
811
812  if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK)))
813    {
814      *valuep = SVN_CONFIG_ASK;
815    }
816  else
817    {
818      svn_boolean_t bool_val;
819      /* We already incorporated default_value into tmp_value if
820         necessary, so the FALSE below will be ignored unless the
821         caller is doing something it shouldn't be doing. */
822      SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
823      *valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE;
824    }
825
826  return SVN_NO_ERROR;
827}
828
829svn_error_t *
830svn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep,
831                        const char *section, const char *option,
832                        const char *unknown_value,
833                        svn_tristate_t default_value)
834{
835  const char *tmp_value;
836
837  svn_config_get(cfg, &tmp_value, section, option, NULL);
838
839  if (! tmp_value)
840    {
841      *valuep = default_value;
842    }
843  else if (0 == svn_cstring_casecmp(tmp_value, unknown_value))
844    {
845      *valuep = svn_tristate_unknown;
846    }
847  else
848    {
849      svn_boolean_t bool_val;
850      /* We already incorporated default_value into tmp_value if
851         necessary, so the FALSE below will be ignored unless the
852         caller is doing something it shouldn't be doing. */
853      SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
854      *valuep = bool_val ? svn_tristate_true : svn_tristate_false;
855    }
856
857  return SVN_NO_ERROR;
858}
859
860int
861svn_config_enumerate_sections(svn_config_t *cfg,
862                              svn_config_section_enumerator_t callback,
863                              void *baton)
864{
865  apr_hash_index_t *sec_ndx;
866  int count = 0;
867  apr_pool_t *subpool = svn_pool_create(cfg->x_pool);
868
869  for (sec_ndx = apr_hash_first(subpool, cfg->sections);
870       sec_ndx != NULL;
871       sec_ndx = apr_hash_next(sec_ndx))
872    {
873      void *sec_ptr;
874      cfg_section_t *sec;
875
876      apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
877      sec = sec_ptr;
878      ++count;
879      if (!callback(sec->name, baton))
880        break;
881    }
882
883  svn_pool_destroy(subpool);
884  return count;
885}
886
887
888int
889svn_config_enumerate_sections2(svn_config_t *cfg,
890                               svn_config_section_enumerator2_t callback,
891                               void *baton, apr_pool_t *pool)
892{
893  apr_hash_index_t *sec_ndx;
894  apr_pool_t *iteration_pool;
895  int count = 0;
896
897  iteration_pool = svn_pool_create(pool);
898  for (sec_ndx = apr_hash_first(pool, cfg->sections);
899       sec_ndx != NULL;
900       sec_ndx = apr_hash_next(sec_ndx))
901    {
902      void *sec_ptr;
903      cfg_section_t *sec;
904
905      apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
906      sec = sec_ptr;
907      ++count;
908      svn_pool_clear(iteration_pool);
909      if (!callback(sec->name, baton, iteration_pool))
910        break;
911    }
912  svn_pool_destroy(iteration_pool);
913
914  return count;
915}
916
917
918
919int
920svn_config_enumerate(svn_config_t *cfg, const char *section,
921                     svn_config_enumerator_t callback, void *baton)
922{
923  cfg_section_t *sec;
924  apr_hash_index_t *opt_ndx;
925  int count;
926  apr_pool_t *subpool;
927
928  find_option(cfg, section, NULL, &sec);
929  if (sec == NULL)
930    return 0;
931
932  subpool = svn_pool_create(cfg->x_pool);
933  count = 0;
934  for (opt_ndx = apr_hash_first(subpool, sec->options);
935       opt_ndx != NULL;
936       opt_ndx = apr_hash_next(opt_ndx))
937    {
938      void *opt_ptr;
939      cfg_option_t *opt;
940      const char *temp_value;
941
942      apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
943      opt = opt_ptr;
944
945      ++count;
946      make_string_from_option(&temp_value, cfg, sec, opt, NULL);
947      if (!callback(opt->name, temp_value, baton))
948        break;
949    }
950
951  svn_pool_destroy(subpool);
952  return count;
953}
954
955
956int
957svn_config_enumerate2(svn_config_t *cfg, const char *section,
958                      svn_config_enumerator2_t callback, void *baton,
959                      apr_pool_t *pool)
960{
961  cfg_section_t *sec;
962  apr_hash_index_t *opt_ndx;
963  apr_pool_t *iteration_pool;
964  int count;
965
966  find_option(cfg, section, NULL, &sec);
967  if (sec == NULL)
968    return 0;
969
970  iteration_pool = svn_pool_create(pool);
971  count = 0;
972  for (opt_ndx = apr_hash_first(pool, sec->options);
973       opt_ndx != NULL;
974       opt_ndx = apr_hash_next(opt_ndx))
975    {
976      void *opt_ptr;
977      cfg_option_t *opt;
978      const char *temp_value;
979
980      apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
981      opt = opt_ptr;
982
983      ++count;
984      make_string_from_option(&temp_value, cfg, sec, opt, NULL);
985      svn_pool_clear(iteration_pool);
986      if (!callback(opt->name, temp_value, baton, iteration_pool))
987        break;
988    }
989  svn_pool_destroy(iteration_pool);
990
991  return count;
992}
993
994
995
996/* Baton for search_groups() */
997struct search_groups_baton
998{
999  const char *key;          /* Provided by caller of svn_config_find_group */
1000  const char *match;        /* Filled in by search_groups */
1001  apr_pool_t *pool;
1002};
1003
1004
1005/* This is an `svn_config_enumerator_t' function, and BATON is a
1006 * `struct search_groups_baton *'.
1007 */
1008static svn_boolean_t search_groups(const char *name,
1009                                   const char *value,
1010                                   void *baton,
1011                                   apr_pool_t *pool)
1012{
1013  struct search_groups_baton *b = baton;
1014  apr_array_header_t *list;
1015
1016  list = svn_cstring_split(value, ",", TRUE, pool);
1017  if (svn_cstring_match_glob_list(b->key, list))
1018    {
1019      /* Fill in the match and return false, to stop enumerating. */
1020      b->match = apr_pstrdup(b->pool, name);
1021      return FALSE;
1022    }
1023  else
1024    return TRUE;
1025}
1026
1027
1028const char *svn_config_find_group(svn_config_t *cfg, const char *key,
1029                                  const char *master_section,
1030                                  apr_pool_t *pool)
1031{
1032  struct search_groups_baton gb;
1033
1034  gb.key = key;
1035  gb.match = NULL;
1036  gb.pool = pool;
1037  (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
1038  return gb.match;
1039}
1040
1041
1042const char*
1043svn_config_get_server_setting(svn_config_t *cfg,
1044                              const char* server_group,
1045                              const char* option_name,
1046                              const char* default_value)
1047{
1048  const char *retval;
1049  svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL,
1050                 option_name, default_value);
1051  if (server_group)
1052    {
1053      svn_config_get(cfg, &retval, server_group, option_name, retval);
1054    }
1055  return retval;
1056}
1057
1058
1059svn_error_t *
1060svn_config_dup(svn_config_t **cfgp,
1061               svn_config_t *src,
1062               apr_pool_t *pool)
1063{
1064  apr_hash_index_t *sectidx;
1065  apr_hash_index_t *optidx;
1066
1067  *cfgp = 0;
1068  SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
1069
1070  (*cfgp)->x_values = src->x_values;
1071  (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
1072  (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
1073
1074  for (sectidx = apr_hash_first(pool, src->sections);
1075       sectidx != NULL;
1076       sectidx = apr_hash_next(sectidx))
1077  {
1078    const void *sectkey;
1079    void *sectval;
1080    apr_ssize_t sectkeyLength;
1081    cfg_section_t * srcsect;
1082    cfg_section_t * destsec;
1083
1084    apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
1085    srcsect = sectval;
1086
1087    destsec = svn_config_addsection(*cfgp, srcsect->name);
1088
1089    for (optidx = apr_hash_first(pool, srcsect->options);
1090         optidx != NULL;
1091         optidx = apr_hash_next(optidx))
1092    {
1093      const void *optkey;
1094      void *optval;
1095      apr_ssize_t optkeyLength;
1096      cfg_option_t *srcopt;
1097      cfg_option_t *destopt;
1098
1099      apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
1100      srcopt = optval;
1101
1102      svn_config_create_option(&destopt, srcopt->name, srcopt->value,
1103                               (*cfgp)->option_names_case_sensitive,
1104                               pool);
1105
1106      destopt->value = apr_pstrdup(pool, srcopt->value);
1107      destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
1108      destopt->expanded = srcopt->expanded;
1109      apr_hash_set(destsec->options,
1110                   apr_pstrdup(pool, (const char*)optkey),
1111                   optkeyLength, destopt);
1112    }
1113  }
1114
1115  return SVN_NO_ERROR;
1116}
1117
1118svn_error_t *
1119svn_config_copy_config(apr_hash_t **cfg_hash,
1120                       apr_hash_t *src_hash,
1121                       apr_pool_t *pool)
1122{
1123  apr_hash_index_t *cidx;
1124
1125  *cfg_hash = apr_hash_make(pool);
1126  for (cidx = apr_hash_first(pool, src_hash);
1127       cidx != NULL;
1128       cidx = apr_hash_next(cidx))
1129  {
1130    const void *ckey;
1131    void *cval;
1132    apr_ssize_t ckeyLength;
1133    svn_config_t * srcconfig;
1134    svn_config_t * destconfig;
1135
1136    apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
1137    srcconfig = cval;
1138
1139    SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
1140
1141    apr_hash_set(*cfg_hash,
1142                 apr_pstrdup(pool, (const char*)ckey),
1143                 ckeyLength, destconfig);
1144  }
1145
1146  return SVN_NO_ERROR;
1147}
1148
1149svn_error_t*
1150svn_config_get_server_setting_int(svn_config_t *cfg,
1151                                  const char *server_group,
1152                                  const char *option_name,
1153                                  apr_int64_t default_value,
1154                                  apr_int64_t *result_value,
1155                                  apr_pool_t *pool)
1156{
1157  const char* tmp_value;
1158  char *end_pos;
1159
1160  tmp_value = svn_config_get_server_setting(cfg, server_group,
1161                                            option_name, NULL);
1162  if (tmp_value == NULL)
1163    *result_value = default_value;
1164  else
1165    {
1166      /* read tmp_value as an int now */
1167      *result_value = apr_strtoi64(tmp_value, &end_pos, 0);
1168
1169      if (*end_pos != 0)
1170        {
1171          return svn_error_createf
1172            (SVN_ERR_BAD_CONFIG_VALUE, NULL,
1173             _("Config error: invalid integer value '%s'"),
1174             tmp_value);
1175        }
1176    }
1177
1178  return SVN_NO_ERROR;
1179}
1180
1181svn_error_t *
1182svn_config_get_server_setting_bool(svn_config_t *cfg,
1183                                   svn_boolean_t *valuep,
1184                                   const char *server_group,
1185                                   const char *option_name,
1186                                   svn_boolean_t default_value)
1187{
1188  const char* tmp_value;
1189  tmp_value = svn_config_get_server_setting(cfg, server_group,
1190                                            option_name, NULL);
1191  return get_bool(valuep, tmp_value, default_value,
1192                  server_group, option_name);
1193}
1194
1195
1196svn_boolean_t
1197svn_config_has_section(svn_config_t *cfg, const char *section)
1198{
1199  cfg_section_t *sec;
1200
1201  /* Canonicalize the hash key */
1202  svn_stringbuf_set(cfg->tmp_key, section);
1203  if (! cfg->section_names_case_sensitive)
1204    make_hash_key(cfg->tmp_key->data);
1205
1206  sec = svn_hash_gets(cfg->sections, cfg->tmp_key->data);
1207  return sec != NULL;
1208}
1209