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