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