1251881Speter/*
2251881Speter * config.c :  reading configuration information
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26289180Speter#include <assert.h>
27289180Speter
28251881Speter#define APR_WANT_STRFUNC
29251881Speter#define APR_WANT_MEMFUNC
30251881Speter#include <apr_want.h>
31251881Speter
32251881Speter#include <apr_general.h>
33251881Speter#include <apr_lib.h>
34251881Speter#include "svn_hash.h"
35251881Speter#include "svn_error.h"
36362181Sdim#include "svn_string.h"
37251881Speter#include "svn_pools.h"
38251881Speter#include "config_impl.h"
39251881Speter
40251881Speter#include "private/svn_dep_compat.h"
41289180Speter#include "private/svn_subr_private.h"
42362181Sdim#include "private/svn_config_private.h"
43251881Speter
44362181Sdim#include "svn_private_config.h"
45362181Sdim
46251881Speter
47251881Speter
48251881Speter
49251881Speter/* Section table entries. */
50251881Spetertypedef struct cfg_section_t cfg_section_t;
51251881Speterstruct cfg_section_t
52251881Speter{
53251881Speter  /* The section name. */
54251881Speter  const char *name;
55251881Speter
56251881Speter  /* Table of cfg_option_t's. */
57251881Speter  apr_hash_t *options;
58251881Speter};
59251881Speter
60251881Speter
61362181Sdim/* States that a config option value can assume. */
62362181Sdimtypedef enum option_state_t
63362181Sdim{
64362181Sdim  /* Value still needs to be expanded.
65362181Sdim     This is the initial state for *all* values. */
66362181Sdim  option_state_needs_expanding,
67362181Sdim
68362181Sdim  /* Value is currently being expanded.
69362181Sdim     This transitional state allows for detecting cyclic dependencies. */
70362181Sdim  option_state_expanding,
71362181Sdim
72362181Sdim  /* Expanded value is available.
73362181Sdim     Values that never needed expanding directly go into that state
74362181Sdim     skipping option_state_expanding. */
75362181Sdim  option_state_expanded,
76362181Sdim
77362181Sdim  /* The value expansion is cyclic which results in "undefined" behavior.
78362181Sdim     This is to return a defined value ("") in that case. */
79362181Sdim  option_state_cyclic
80362181Sdim} option_state_t;
81362181Sdim
82251881Speter/* Option table entries. */
83251881Spetertypedef struct cfg_option_t cfg_option_t;
84251881Speterstruct cfg_option_t
85251881Speter{
86251881Speter  /* The option name. */
87251881Speter  const char *name;
88251881Speter
89251881Speter  /* The option name, converted into a hash key. */
90251881Speter  const char *hash_key;
91251881Speter
92251881Speter  /* The unexpanded option value. */
93251881Speter  const char *value;
94251881Speter
95251881Speter  /* The expanded option value. */
96251881Speter  const char *x_value;
97251881Speter
98362181Sdim  /* Expansion state. If this is option_state_expanded, VALUE has already
99362181Sdim     been expanded.  In this case, if x_value is NULL, no expansions were
100362181Sdim     necessary, and value should be used directly. */
101362181Sdim  option_state_t state;
102251881Speter};
103251881Speter
104251881Speter
105251881Speter
106251881Spetersvn_error_t *
107251881Spetersvn_config_create2(svn_config_t **cfgp,
108251881Speter                   svn_boolean_t section_names_case_sensitive,
109251881Speter                   svn_boolean_t option_names_case_sensitive,
110251881Speter                   apr_pool_t *result_pool)
111251881Speter{
112251881Speter  svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
113251881Speter
114362181Sdim  cfg->sections = svn_hash__make(result_pool);
115251881Speter  cfg->pool = result_pool;
116251881Speter  cfg->x_pool = svn_pool_create(result_pool);
117251881Speter  cfg->x_values = FALSE;
118251881Speter  cfg->tmp_key = svn_stringbuf_create_empty(result_pool);
119251881Speter  cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
120251881Speter  cfg->section_names_case_sensitive = section_names_case_sensitive;
121251881Speter  cfg->option_names_case_sensitive = option_names_case_sensitive;
122289180Speter  cfg->read_only = FALSE;
123251881Speter
124251881Speter  *cfgp = cfg;
125251881Speter  return SVN_NO_ERROR;
126251881Speter}
127251881Speter
128251881Spetersvn_error_t *
129251881Spetersvn_config_read3(svn_config_t **cfgp, const char *file,
130251881Speter                 svn_boolean_t must_exist,
131251881Speter                 svn_boolean_t section_names_case_sensitive,
132251881Speter                 svn_boolean_t option_names_case_sensitive,
133251881Speter                 apr_pool_t *result_pool)
134251881Speter{
135251881Speter  svn_config_t *cfg;
136251881Speter  svn_error_t *err;
137251881Speter
138251881Speter  SVN_ERR(svn_config_create2(&cfg,
139251881Speter                             section_names_case_sensitive,
140251881Speter                             option_names_case_sensitive,
141251881Speter                             result_pool));
142251881Speter
143251881Speter  /* Yes, this is platform-specific code in Subversion, but there's no
144251881Speter     practical way to migrate it into APR, as it's simultaneously
145251881Speter     Subversion-specific and Windows-specific.  Even if we eventually
146251881Speter     want to have APR offer a generic config-reading interface, it
147251881Speter     makes sense to test it here first and migrate it later. */
148251881Speter#ifdef WIN32
149251881Speter  if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN))
150251881Speter    err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN,
151251881Speter                                     must_exist, result_pool);
152251881Speter  else
153251881Speter#endif /* WIN32 */
154251881Speter    err = svn_config__parse_file(cfg, file, must_exist, result_pool);
155251881Speter
156251881Speter  if (err != SVN_NO_ERROR)
157251881Speter    return err;
158251881Speter  else
159251881Speter    *cfgp = cfg;
160251881Speter
161251881Speter  return SVN_NO_ERROR;
162251881Speter}
163251881Speter
164251881Spetersvn_error_t *
165362181Sdimsvn_config__default_add_value_fn(void *baton,
166362181Sdim                                 svn_stringbuf_t *section,
167362181Sdim                                 svn_stringbuf_t *option,
168362181Sdim                                 svn_stringbuf_t *value)
169362181Sdim{
170362181Sdim  /* FIXME: We may as well propagate the known string sizes here. */
171362181Sdim  svn_config_set((svn_config_t *)baton, section->data,
172362181Sdim                 option->data, value->data);
173362181Sdim  return SVN_NO_ERROR;
174362181Sdim}
175362181Sdim
176362181Sdimsvn_error_t *
177251881Spetersvn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
178251881Speter                 svn_boolean_t section_names_case_sensitive,
179251881Speter                 svn_boolean_t option_names_case_sensitive,
180251881Speter                 apr_pool_t *result_pool)
181251881Speter{
182251881Speter  svn_config_t *cfg;
183251881Speter  svn_error_t *err;
184251881Speter  apr_pool_t *scratch_pool = svn_pool_create(result_pool);
185251881Speter
186251881Speter  err = svn_config_create2(&cfg,
187251881Speter                           section_names_case_sensitive,
188251881Speter                           option_names_case_sensitive,
189251881Speter                           result_pool);
190251881Speter
191251881Speter  if (err == SVN_NO_ERROR)
192362181Sdim    err = svn_config__parse_stream(stream,
193362181Sdim                                   svn_config__constructor_create(
194362181Sdim                                       NULL, NULL,
195362181Sdim                                       svn_config__default_add_value_fn,
196362181Sdim                                       scratch_pool),
197362181Sdim                                   cfg, scratch_pool);
198251881Speter
199251881Speter  if (err == SVN_NO_ERROR)
200251881Speter    *cfgp = cfg;
201251881Speter
202251881Speter  svn_pool_destroy(scratch_pool);
203251881Speter
204251881Speter  return err;
205251881Speter}
206251881Speter
207251881Speter/* Read various configuration sources into *CFGP, in this order, with
208251881Speter * later reads overriding the results of earlier ones:
209251881Speter *
210251881Speter *    1. SYS_REGISTRY_PATH   (only on Win32, but ignored if NULL)
211251881Speter *
212251881Speter *    2. SYS_FILE_PATH       (everywhere, but ignored if NULL)
213251881Speter *
214251881Speter *    3. USR_REGISTRY_PATH   (only on Win32, but ignored if NULL)
215251881Speter *
216251881Speter *    4. USR_FILE_PATH       (everywhere, but ignored if NULL)
217251881Speter *
218251881Speter * Allocate *CFGP in POOL.  Even if no configurations are read,
219251881Speter * allocate an empty *CFGP.
220251881Speter */
221251881Speterstatic svn_error_t *
222251881Speterread_all(svn_config_t **cfgp,
223251881Speter         const char *sys_registry_path,
224251881Speter         const char *usr_registry_path,
225251881Speter         const char *sys_file_path,
226251881Speter         const char *usr_file_path,
227251881Speter         apr_pool_t *pool)
228251881Speter{
229251881Speter  svn_boolean_t red_config = FALSE;  /* "red" is the past tense of "read" */
230251881Speter
231251881Speter  /*** Read system-wide configurations first... ***/
232251881Speter
233251881Speter#ifdef WIN32
234251881Speter  if (sys_registry_path)
235251881Speter    {
236289180Speter      SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE,
237289180Speter                               pool));
238251881Speter      red_config = TRUE;
239251881Speter    }
240251881Speter#endif /* WIN32 */
241251881Speter
242251881Speter  if (sys_file_path)
243251881Speter    {
244251881Speter      if (red_config)
245251881Speter        SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE));
246251881Speter      else
247251881Speter        {
248251881Speter          SVN_ERR(svn_config_read3(cfgp, sys_file_path,
249251881Speter                                   FALSE, FALSE, FALSE, pool));
250251881Speter          red_config = TRUE;
251251881Speter        }
252251881Speter    }
253251881Speter
254251881Speter  /*** ...followed by per-user configurations. ***/
255251881Speter
256251881Speter#ifdef WIN32
257251881Speter  if (usr_registry_path)
258251881Speter    {
259251881Speter      if (red_config)
260251881Speter        SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
261251881Speter      else
262251881Speter        {
263289180Speter          SVN_ERR(svn_config_read3(cfgp, usr_registry_path,
264289180Speter                                   FALSE, FALSE, FALSE, pool));
265251881Speter          red_config = TRUE;
266251881Speter        }
267251881Speter    }
268251881Speter#endif /* WIN32 */
269251881Speter
270251881Speter  if (usr_file_path)
271251881Speter    {
272251881Speter      if (red_config)
273251881Speter        SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE));
274251881Speter      else
275251881Speter        {
276251881Speter          SVN_ERR(svn_config_read3(cfgp, usr_file_path,
277251881Speter                                   FALSE, FALSE, FALSE, pool));
278251881Speter          red_config = TRUE;
279251881Speter        }
280251881Speter    }
281251881Speter
282251881Speter  if (! red_config)
283251881Speter    SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
284251881Speter
285251881Speter  return SVN_NO_ERROR;
286251881Speter}
287251881Speter
288251881Speter
289251881Speter/* CONFIG_DIR provides an override for the default behavior of reading
290251881Speter   the default set of overlay files described by read_all()'s doc
291289180Speter   string.  Returns non-NULL *CFG or an error. */
292251881Speterstatic svn_error_t *
293251881Speterget_category_config(svn_config_t **cfg,
294251881Speter                    const char *config_dir,
295251881Speter                    const char *category,
296251881Speter                    apr_pool_t *pool)
297251881Speter{
298251881Speter  const char *usr_reg_path = NULL, *sys_reg_path = NULL;
299251881Speter  const char *usr_cfg_path, *sys_cfg_path;
300251881Speter  svn_error_t *err = NULL;
301251881Speter
302251881Speter  *cfg = NULL;
303251881Speter
304251881Speter  if (! config_dir)
305251881Speter    {
306251881Speter#ifdef WIN32
307251881Speter      sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH,
308289180Speter                                 category, SVN_VA_NULL);
309251881Speter      usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH,
310289180Speter                                 category, SVN_VA_NULL);
311251881Speter#endif /* WIN32 */
312251881Speter
313251881Speter      err = svn_config__sys_config_path(&sys_cfg_path, category, pool);
314251881Speter      if ((err) && (err->apr_err == SVN_ERR_BAD_FILENAME))
315251881Speter        {
316251881Speter          sys_cfg_path = NULL;
317251881Speter          svn_error_clear(err);
318251881Speter        }
319251881Speter      else if (err)
320251881Speter        return err;
321251881Speter    }
322251881Speter  else
323251881Speter    sys_cfg_path = NULL;
324251881Speter
325251881Speter  SVN_ERR(svn_config_get_user_config_path(&usr_cfg_path, config_dir, category,
326251881Speter                                          pool));
327251881Speter  return read_all(cfg, sys_reg_path, usr_reg_path,
328251881Speter                  sys_cfg_path, usr_cfg_path, pool);
329251881Speter}
330251881Speter
331251881Speter
332251881Spetersvn_error_t *
333251881Spetersvn_config_get_config(apr_hash_t **cfg_hash,
334251881Speter                      const char *config_dir,
335251881Speter                      apr_pool_t *pool)
336251881Speter{
337251881Speter  svn_config_t *cfg;
338362181Sdim  *cfg_hash = svn_hash__make(pool);
339251881Speter
340251881Speter  SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS,
341251881Speter                              pool));
342289180Speter  svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, cfg);
343251881Speter
344251881Speter  SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_CONFIG,
345251881Speter                              pool));
346289180Speter  svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, cfg);
347251881Speter
348251881Speter  return SVN_NO_ERROR;
349251881Speter}
350251881Speter
351289180Spetersvn_error_t *
352289180Spetersvn_config__get_default_config(apr_hash_t **cfg_hash,
353289180Speter                               apr_pool_t *pool)
354289180Speter{
355289180Speter  svn_config_t *empty_cfg;
356362181Sdim  *cfg_hash = svn_hash__make(pool);
357251881Speter
358289180Speter  SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
359289180Speter  svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg);
360289180Speter
361289180Speter  SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
362289180Speter  svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, empty_cfg);
363289180Speter
364289180Speter  return SVN_NO_ERROR;
365289180Speter}
366289180Speter
367289180Speter
368251881Speter
369251881Speter/* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION)
370251881Speter   pair.  Stop if CALLBACK returns TRUE.  Allocate from POOL. */
371251881Speterstatic void
372251881Speterfor_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool,
373251881Speter                svn_boolean_t callback(void *same_baton,
374251881Speter                                       cfg_section_t *section,
375251881Speter                                       cfg_option_t *option))
376251881Speter{
377251881Speter  apr_hash_index_t *sec_ndx;
378251881Speter  for (sec_ndx = apr_hash_first(pool, cfg->sections);
379251881Speter       sec_ndx != NULL;
380251881Speter       sec_ndx = apr_hash_next(sec_ndx))
381251881Speter    {
382362181Sdim      cfg_section_t *sec = apr_hash_this_val(sec_ndx);
383251881Speter      apr_hash_index_t *opt_ndx;
384251881Speter
385251881Speter      for (opt_ndx = apr_hash_first(pool, sec->options);
386251881Speter           opt_ndx != NULL;
387251881Speter           opt_ndx = apr_hash_next(opt_ndx))
388251881Speter        {
389362181Sdim          cfg_option_t *opt = apr_hash_this_val(opt_ndx);
390251881Speter
391251881Speter          if (callback(baton, sec, opt))
392251881Speter            return;
393251881Speter        }
394251881Speter    }
395251881Speter}
396251881Speter
397251881Speter
398251881Speter
399251881Speterstatic svn_boolean_t
400251881Spetermerge_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
401251881Speter{
402251881Speter  svn_config_set(baton, section->name, option->name, option->value);
403251881Speter  return FALSE;
404251881Speter}
405251881Speter
406251881Spetersvn_error_t *
407251881Spetersvn_config_merge(svn_config_t *cfg, const char *file,
408251881Speter                 svn_boolean_t must_exist)
409251881Speter{
410251881Speter  /* The original config hash shouldn't change if there's an error
411251881Speter     while reading the confguration, so read into a temporary table.
412251881Speter     ### We could use a tmp subpool for this, since merge_cfg is going
413251881Speter     to be tossed afterwards.  Premature optimization, though? */
414251881Speter  svn_config_t *merge_cfg;
415251881Speter  SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist,
416251881Speter                           cfg->section_names_case_sensitive,
417251881Speter                           cfg->option_names_case_sensitive,
418251881Speter                           cfg->pool));
419251881Speter
420251881Speter  /* Now copy the new options into the original table. */
421251881Speter  for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback);
422251881Speter  return SVN_NO_ERROR;
423251881Speter}
424251881Speter
425251881Speter
426251881Speter
427251881Speter/* Remove variable expansions from CFG.  Walk through the options tree,
428251881Speter   killing all expanded values, then clear the expanded value pool. */
429251881Speterstatic svn_boolean_t
430251881Speterrmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
431251881Speter{
432362181Sdim  /* Only reset the expansion state if the value actually contains
433251881Speter     variable expansions. */
434362181Sdim  if (   (option->state == option_state_expanded && option->x_value != NULL)
435362181Sdim      || option->state == option_state_cyclic)
436251881Speter    {
437251881Speter      option->x_value = NULL;
438362181Sdim      option->state = option_state_needs_expanding;
439251881Speter    }
440251881Speter
441251881Speter  return FALSE;
442251881Speter}
443251881Speter
444251881Speterstatic void
445251881Speterremove_expansions(svn_config_t *cfg)
446251881Speter{
447251881Speter  if (!cfg->x_values)
448251881Speter    return;
449251881Speter
450251881Speter  for_each_option(cfg, NULL, cfg->x_pool, rmex_callback);
451251881Speter  svn_pool_clear(cfg->x_pool);
452251881Speter  cfg->x_values = FALSE;
453251881Speter}
454251881Speter
455251881Speter
456251881Speter
457251881Speter/* Canonicalize a string for hashing.  Modifies KEY in place. */
458251881Speterstatic APR_INLINE char *
459251881Spetermake_hash_key(char *key)
460251881Speter{
461251881Speter  register char *p;
462251881Speter  for (p = key; *p != 0; ++p)
463251881Speter    *p = (char)apr_tolower(*p);
464251881Speter  return key;
465251881Speter}
466251881Speter
467289180Speter/* Return the value for KEY in HASH.  If CASE_SENSITIVE is FALSE,
468289180Speter   BUFFER will be used to construct the normalized hash key. */
469289180Speterstatic void *
470289180Speterget_hash_value(apr_hash_t *hash,
471289180Speter               svn_stringbuf_t *buffer,
472289180Speter               const char *key,
473289180Speter               svn_boolean_t case_sensitive)
474289180Speter{
475289180Speter  apr_size_t i;
476289180Speter  apr_size_t len = strlen(key);
477251881Speter
478289180Speter  if (case_sensitive)
479289180Speter    return apr_hash_get(hash, key, len);
480289180Speter
481289180Speter  svn_stringbuf_ensure(buffer, len);
482289180Speter  for (i = 0; i < len; ++i)
483289180Speter    buffer->data[i] = (char)apr_tolower(key[i]);
484289180Speter
485289180Speter  return apr_hash_get(hash, buffer->data, len);
486289180Speter}
487289180Speter
488251881Speter/* Return a pointer to an option in CFG, or NULL if it doesn't exist.
489251881Speter   if SECTIONP is non-null, return a pointer to the option's section.
490251881Speter   OPTION may be NULL. */
491251881Speterstatic cfg_option_t *
492251881Speterfind_option(svn_config_t *cfg, const char *section, const char *option,
493251881Speter            cfg_section_t **sectionp)
494251881Speter{
495289180Speter  void *sec_ptr = get_hash_value(cfg->sections, cfg->tmp_key, section,
496289180Speter                                 cfg->section_names_case_sensitive);
497251881Speter  if (sectionp != NULL)
498251881Speter    *sectionp = sec_ptr;
499251881Speter
500251881Speter  if (sec_ptr != NULL && option != NULL)
501251881Speter    {
502251881Speter      cfg_section_t *sec = sec_ptr;
503289180Speter      cfg_option_t *opt = get_hash_value(sec->options, cfg->tmp_key, option,
504289180Speter                                         cfg->option_names_case_sensitive);
505251881Speter      /* NOTE: ConfigParser's sections are case sensitive. */
506251881Speter      if (opt == NULL
507251881Speter          && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0)
508251881Speter        /* Options which aren't found in the requested section are
509251881Speter           also sought after in the default section. */
510251881Speter        opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec);
511251881Speter      return opt;
512251881Speter    }
513251881Speter
514251881Speter  return NULL;
515251881Speter}
516251881Speter
517251881Speter
518251881Speter/* Has a bi-directional dependency with make_string_from_option(). */
519362181Sdimstatic svn_boolean_t
520251881Speterexpand_option_value(svn_config_t *cfg, cfg_section_t *section,
521251881Speter                    const char *opt_value, const char **opt_x_valuep,
522251881Speter                    apr_pool_t *x_pool);
523251881Speter
524251881Speter
525251881Speter/* Set *VALUEP according to the OPT's value.  A value for X_POOL must
526251881Speter   only ever be passed into this function by expand_option_value(). */
527251881Speterstatic void
528251881Spetermake_string_from_option(const char **valuep, svn_config_t *cfg,
529251881Speter                        cfg_section_t *section, cfg_option_t *opt,
530251881Speter                        apr_pool_t* x_pool)
531251881Speter{
532251881Speter  /* Expand the option value if necessary. */
533362181Sdim  if (   opt->state == option_state_expanding
534362181Sdim      || opt->state == option_state_cyclic)
535251881Speter    {
536362181Sdim      /* Recursion is not supported.  Since we can't produce an error
537362181Sdim       * nor should we abort the process, the next best thing is to
538362181Sdim       * report the recursive part as an empty string. */
539362181Sdim      *valuep = "";
540362181Sdim
541362181Sdim      /* Go into "value undefined" state. */
542362181Sdim      opt->state = option_state_cyclic;
543362181Sdim
544362181Sdim      return;
545362181Sdim    }
546362181Sdim  else if (opt->state == option_state_needs_expanding)
547362181Sdim    {
548251881Speter      /* before attempting to expand an option, check for the placeholder.
549251881Speter       * If none is there, there is no point in calling expand_option_value.
550251881Speter       */
551251881Speter      if (opt->value && strchr(opt->value, '%'))
552251881Speter        {
553289180Speter          apr_pool_t *tmp_pool;
554251881Speter
555289180Speter          /* setting read-only mode should have expanded all values
556289180Speter           * automatically. */
557289180Speter          assert(!cfg->read_only);
558289180Speter
559289180Speter          tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
560289180Speter
561362181Sdim          /* Expand the value. During that process, have the option marked
562362181Sdim           * as "expanding" to detect cycles. */
563362181Sdim          opt->state = option_state_expanding;
564362181Sdim          if (expand_option_value(cfg, section, opt->value, &opt->x_value,
565362181Sdim                                  tmp_pool))
566362181Sdim            opt->state = option_state_expanded;
567362181Sdim          else
568362181Sdim            opt->state = option_state_cyclic;
569251881Speter
570362181Sdim          /* Ensure the expanded value is allocated in a permanent pool. */
571286506Speter          if (x_pool != cfg->x_pool)
572251881Speter            {
573251881Speter              /* Grab the fully expanded value from tmp_pool before its
574251881Speter                 disappearing act. */
575251881Speter              if (opt->x_value)
576251881Speter                opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
577251881Speter                                              strlen(opt->x_value));
578286506Speter              if (!x_pool)
579286506Speter                svn_pool_destroy(tmp_pool);
580251881Speter            }
581251881Speter        }
582251881Speter      else
583251881Speter        {
584362181Sdim          opt->state = option_state_expanded;
585251881Speter        }
586251881Speter    }
587251881Speter
588251881Speter  if (opt->x_value)
589251881Speter    *valuep = opt->x_value;
590251881Speter  else
591251881Speter    *valuep = opt->value;
592251881Speter}
593251881Speter
594251881Speter
595251881Speter/* Start of variable-replacement placeholder */
596251881Speter#define FMT_START     "%("
597251881Speter#define FMT_START_LEN (sizeof(FMT_START) - 1)
598251881Speter
599251881Speter/* End of variable-replacement placeholder */
600251881Speter#define FMT_END       ")s"
601251881Speter#define FMT_END_LEN   (sizeof(FMT_END) - 1)
602251881Speter
603251881Speter
604251881Speter/* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
605251881Speter   If no variable replacements are done, set *OPT_X_VALUEP to
606362181Sdim   NULL.  Return TRUE if the expanded value is defined and FALSE
607362181Sdim   for recursive definitions.  Allocate from X_POOL. */
608362181Sdimstatic svn_boolean_t
609251881Speterexpand_option_value(svn_config_t *cfg, cfg_section_t *section,
610251881Speter                    const char *opt_value, const char **opt_x_valuep,
611251881Speter                    apr_pool_t *x_pool)
612251881Speter{
613251881Speter  svn_stringbuf_t *buf = NULL;
614251881Speter  const char *parse_from = opt_value;
615251881Speter  const char *copy_from = parse_from;
616251881Speter  const char *name_start, *name_end;
617251881Speter
618251881Speter  while (parse_from != NULL
619251881Speter         && *parse_from != '\0'
620251881Speter         && (name_start = strstr(parse_from, FMT_START)) != NULL)
621251881Speter    {
622251881Speter      name_start += FMT_START_LEN;
623251881Speter      if (*name_start == '\0')
624251881Speter        /* FMT_START at end of opt_value. */
625251881Speter        break;
626251881Speter
627251881Speter      name_end = strstr(name_start, FMT_END);
628251881Speter      if (name_end != NULL)
629251881Speter        {
630251881Speter          cfg_option_t *x_opt;
631251881Speter          apr_size_t len = name_end - name_start;
632251881Speter          char *name = apr_pstrmemdup(x_pool, name_start, len);
633251881Speter
634251881Speter          x_opt = find_option(cfg, section->name, name, NULL);
635251881Speter
636251881Speter          if (x_opt != NULL)
637251881Speter            {
638251881Speter              const char *cstring;
639251881Speter
640251881Speter              /* Pass back the sub-pool originally provided by
641251881Speter                 make_string_from_option() as an indication of when it
642251881Speter                 should terminate. */
643251881Speter              make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
644251881Speter
645362181Sdim              /* Values depending on cyclic values must also be marked as
646362181Sdim               * "undefined" because they might themselves form cycles with
647362181Sdim               * the one cycle we just detected.  Due to the early abort of
648362181Sdim               * the recursion, we won't follow and thus detect dependent
649362181Sdim               * cycles anymore.
650362181Sdim               */
651362181Sdim              if (x_opt->state == option_state_cyclic)
652362181Sdim                {
653362181Sdim                  *opt_x_valuep = "";
654362181Sdim                  return FALSE;
655362181Sdim                }
656362181Sdim
657251881Speter              /* Append the plain text preceding the expansion. */
658251881Speter              len = name_start - FMT_START_LEN - copy_from;
659251881Speter              if (buf == NULL)
660251881Speter                {
661251881Speter                  buf = svn_stringbuf_ncreate(copy_from, len, x_pool);
662251881Speter                  cfg->x_values = TRUE;
663251881Speter                }
664251881Speter              else
665251881Speter                svn_stringbuf_appendbytes(buf, copy_from, len);
666251881Speter
667251881Speter              /* Append the expansion and adjust parse pointers. */
668251881Speter              svn_stringbuf_appendcstr(buf, cstring);
669251881Speter              parse_from = name_end + FMT_END_LEN;
670251881Speter              copy_from = parse_from;
671251881Speter            }
672251881Speter          else
673251881Speter            /* Though ConfigParser considers the failure to resolve
674251881Speter               the requested expansion an exception condition, we
675251881Speter               consider it to be plain text, and look for the start of
676251881Speter               the next one. */
677251881Speter            parse_from = name_end + FMT_END_LEN;
678251881Speter        }
679251881Speter      else
680251881Speter        /* Though ConfigParser treats unterminated format specifiers
681251881Speter           as an exception condition, we consider them to be plain
682251881Speter           text.  The fact that there are no more format specifier
683251881Speter           endings means we're done parsing. */
684251881Speter        parse_from = NULL;
685251881Speter    }
686251881Speter
687251881Speter  if (buf != NULL)
688251881Speter    {
689251881Speter      /* Copy the remainder of the plain text. */
690251881Speter      svn_stringbuf_appendcstr(buf, copy_from);
691251881Speter      *opt_x_valuep = buf->data;
692251881Speter    }
693251881Speter  else
694251881Speter    *opt_x_valuep = NULL;
695362181Sdim
696362181Sdim  /* Expansion has a well-defined answer. */
697362181Sdim  return TRUE;
698251881Speter}
699251881Speter
700251881Speterstatic cfg_section_t *
701251881Spetersvn_config_addsection(svn_config_t *cfg,
702251881Speter                      const char *section)
703251881Speter{
704251881Speter  cfg_section_t *s;
705251881Speter  const char *hash_key;
706251881Speter
707251881Speter  s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
708251881Speter  s->name = apr_pstrdup(cfg->pool, section);
709251881Speter  if(cfg->section_names_case_sensitive)
710251881Speter    hash_key = s->name;
711251881Speter  else
712251881Speter    hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
713362181Sdim  s->options = svn_hash__make(cfg->pool);
714362181Sdim
715251881Speter  svn_hash_sets(cfg->sections, hash_key, s);
716251881Speter
717251881Speter  return s;
718251881Speter}
719251881Speter
720251881Speterstatic void
721251881Spetersvn_config_create_option(cfg_option_t **opt,
722251881Speter                         const char *option,
723251881Speter                         const char *value,
724251881Speter                         svn_boolean_t option_names_case_sensitive,
725251881Speter                         apr_pool_t *pool)
726251881Speter{
727251881Speter  cfg_option_t *o;
728251881Speter
729251881Speter  o = apr_palloc(pool, sizeof(cfg_option_t));
730251881Speter  o->name = apr_pstrdup(pool, option);
731251881Speter  if(option_names_case_sensitive)
732251881Speter    o->hash_key = o->name;
733251881Speter  else
734251881Speter    o->hash_key = make_hash_key(apr_pstrdup(pool, option));
735251881Speter
736251881Speter  o->value = apr_pstrdup(pool, value);
737251881Speter  o->x_value = NULL;
738362181Sdim  o->state = option_state_needs_expanding;
739251881Speter
740251881Speter  *opt = o;
741251881Speter}
742251881Speter
743289180Spetersvn_boolean_t
744289180Spetersvn_config__is_expanded(svn_config_t *cfg,
745289180Speter                        const char *section,
746289180Speter                        const char *option)
747289180Speter{
748289180Speter  cfg_option_t *opt;
749289180Speter
750289180Speter  if (cfg == NULL)
751289180Speter    return FALSE;
752289180Speter
753289180Speter  /* does the option even exist? */
754289180Speter  opt = find_option(cfg, section, option, NULL);
755289180Speter  if (opt == NULL)
756289180Speter    return FALSE;
757289180Speter
758289180Speter  /* already expanded? */
759362181Sdim  if (   opt->state == option_state_expanded
760362181Sdim      || opt->state == option_state_cyclic)
761289180Speter    return TRUE;
762289180Speter
763289180Speter  /* needs expansion? */
764289180Speter  if (opt->value && strchr(opt->value, '%'))
765289180Speter    return FALSE;
766289180Speter
767289180Speter  /* no expansion necessary */
768289180Speter  return TRUE;
769289180Speter}
770289180Speter
771251881Speter
772251881Spetervoid
773251881Spetersvn_config_get(svn_config_t *cfg, const char **valuep,
774251881Speter               const char *section, const char *option,
775251881Speter               const char *default_value)
776251881Speter{
777251881Speter  *valuep = default_value;
778251881Speter  if (cfg)
779251881Speter    {
780251881Speter      cfg_section_t *sec;
781251881Speter      cfg_option_t *opt = find_option(cfg, section, option, &sec);
782251881Speter      if (opt != NULL)
783251881Speter        {
784251881Speter          make_string_from_option(valuep, cfg, sec, opt, NULL);
785251881Speter        }
786251881Speter      else
787251881Speter        /* before attempting to expand an option, check for the placeholder.
788289180Speter         * If there is none, there is no point in calling expand_option_value.
789251881Speter         */
790251881Speter        if (default_value && strchr(default_value, '%'))
791251881Speter          {
792289180Speter            apr_pool_t *tmp_pool = svn_pool_create(cfg->pool);
793251881Speter            const char *x_default;
794362181Sdim            if (!expand_option_value(cfg, sec, default_value, &x_default,
795362181Sdim                                     tmp_pool))
796251881Speter              {
797362181Sdim                /* Recursive definitions are not supported.
798362181Sdim                   Normalize the answer in that case. */
799362181Sdim                *valuep = "";
800362181Sdim              }
801362181Sdim            else if (x_default)
802362181Sdim              {
803251881Speter                svn_stringbuf_set(cfg->tmp_value, x_default);
804251881Speter                *valuep = cfg->tmp_value->data;
805251881Speter              }
806251881Speter            svn_pool_destroy(tmp_pool);
807251881Speter          }
808251881Speter    }
809251881Speter}
810251881Speter
811251881Speter
812251881Speter
813251881Spetervoid
814251881Spetersvn_config_set(svn_config_t *cfg,
815251881Speter               const char *section, const char *option,
816251881Speter               const char *value)
817251881Speter{
818251881Speter  cfg_section_t *sec;
819251881Speter  cfg_option_t *opt;
820251881Speter
821289180Speter  /* Ignore write attempts to r/o configurations.
822289180Speter   *
823289180Speter   * Since we should never try to modify r/o data, trigger an assertion
824289180Speter   * in debug mode.
825289180Speter   */
826289180Speter#ifdef SVN_DEBUG
827289180Speter  SVN_ERR_ASSERT_NO_RETURN(!cfg->read_only);
828289180Speter#endif
829289180Speter  if (cfg->read_only)
830289180Speter    return;
831289180Speter
832251881Speter  remove_expansions(cfg);
833251881Speter
834251881Speter  opt = find_option(cfg, section, option, &sec);
835251881Speter  if (opt != NULL)
836251881Speter    {
837251881Speter      /* Replace the option's value. */
838251881Speter      opt->value = apr_pstrdup(cfg->pool, value);
839362181Sdim      opt->state = option_state_needs_expanding;
840251881Speter      return;
841251881Speter    }
842251881Speter
843251881Speter  /* Create a new option */
844251881Speter  svn_config_create_option(&opt, option, value,
845251881Speter                           cfg->option_names_case_sensitive,
846251881Speter                           cfg->pool);
847251881Speter
848251881Speter  if (sec == NULL)
849251881Speter    {
850251881Speter      /* Even the section doesn't exist. Create it. */
851251881Speter      sec = svn_config_addsection(cfg, section);
852251881Speter    }
853251881Speter
854251881Speter  svn_hash_sets(sec->options, opt->hash_key, opt);
855251881Speter}
856251881Speter
857251881Speter
858251881Speter
859251881Speter/* Set *BOOLP to true or false depending (case-insensitively) on INPUT.
860251881Speter   If INPUT is null, set *BOOLP to DEFAULT_VALUE.
861251881Speter
862251881Speter   INPUT is a string indicating truth or falsehood in any of the usual
863251881Speter   ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc.
864251881Speter
865251881Speter   If INPUT is neither NULL nor a recognized string, return an error
866251881Speter   with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in
867251881Speter   constructing the error string. */
868251881Speterstatic svn_error_t *
869251881Speterget_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value,
870251881Speter         const char *section, const char *option)
871251881Speter{
872251881Speter  svn_tristate_t value = svn_tristate__from_word(input);
873251881Speter
874251881Speter  if (value == svn_tristate_true)
875251881Speter    *boolp = TRUE;
876251881Speter  else if (value == svn_tristate_false)
877251881Speter    *boolp = FALSE;
878251881Speter  else if (input == NULL) /* no value provided */
879251881Speter    *boolp = default_value;
880251881Speter
881251881Speter  else if (section) /* unrecognized value */
882251881Speter    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
883251881Speter                             _("Config error: invalid boolean "
884251881Speter                               "value '%s' for '[%s] %s'"),
885251881Speter                             input, section, option);
886251881Speter  else
887251881Speter    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
888251881Speter                             _("Config error: invalid boolean "
889251881Speter                               "value '%s' for '%s'"),
890251881Speter                             input, option);
891251881Speter
892251881Speter  return SVN_NO_ERROR;
893251881Speter}
894251881Speter
895251881Speter
896251881Spetersvn_error_t *
897251881Spetersvn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep,
898251881Speter                    const char *section, const char *option,
899251881Speter                    svn_boolean_t default_value)
900251881Speter{
901251881Speter  const char *tmp_value;
902251881Speter  svn_config_get(cfg, &tmp_value, section, option, NULL);
903251881Speter  return get_bool(valuep, tmp_value, default_value, section, option);
904251881Speter}
905251881Speter
906251881Speter
907251881Speter
908251881Spetervoid
909251881Spetersvn_config_set_bool(svn_config_t *cfg,
910251881Speter                    const char *section, const char *option,
911251881Speter                    svn_boolean_t value)
912251881Speter{
913251881Speter  svn_config_set(cfg, section, option,
914251881Speter                 (value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE));
915251881Speter}
916251881Speter
917251881Spetersvn_error_t *
918251881Spetersvn_config_get_int64(svn_config_t *cfg,
919251881Speter                     apr_int64_t *valuep,
920251881Speter                     const char *section,
921251881Speter                     const char *option,
922251881Speter                     apr_int64_t default_value)
923251881Speter{
924251881Speter  const char *tmp_value;
925251881Speter  svn_config_get(cfg, &tmp_value, section, option, NULL);
926251881Speter  if (tmp_value)
927251881Speter    return svn_cstring_strtoi64(valuep, tmp_value,
928251881Speter                                APR_INT64_MIN, APR_INT64_MAX, 10);
929251881Speter
930251881Speter  *valuep = default_value;
931251881Speter  return SVN_NO_ERROR;
932251881Speter}
933251881Speter
934251881Spetervoid
935251881Spetersvn_config_set_int64(svn_config_t *cfg,
936251881Speter                     const char *section,
937251881Speter                     const char *option,
938251881Speter                     apr_int64_t value)
939251881Speter{
940251881Speter  svn_config_set(cfg, section, option,
941251881Speter                 apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value));
942251881Speter}
943251881Speter
944251881Spetersvn_error_t *
945251881Spetersvn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep,
946251881Speter                          const char *section, const char *option,
947251881Speter                          const char* default_value)
948251881Speter{
949251881Speter  const char *tmp_value;
950251881Speter
951251881Speter  svn_config_get(cfg, &tmp_value, section, option, NULL);
952251881Speter
953251881Speter  if (! tmp_value)
954251881Speter    tmp_value = default_value;
955251881Speter
956251881Speter  if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK)))
957251881Speter    {
958251881Speter      *valuep = SVN_CONFIG_ASK;
959251881Speter    }
960251881Speter  else
961251881Speter    {
962251881Speter      svn_boolean_t bool_val;
963251881Speter      /* We already incorporated default_value into tmp_value if
964251881Speter         necessary, so the FALSE below will be ignored unless the
965251881Speter         caller is doing something it shouldn't be doing. */
966251881Speter      SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
967251881Speter      *valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE;
968251881Speter    }
969251881Speter
970251881Speter  return SVN_NO_ERROR;
971251881Speter}
972251881Speter
973251881Spetersvn_error_t *
974251881Spetersvn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep,
975251881Speter                        const char *section, const char *option,
976251881Speter                        const char *unknown_value,
977251881Speter                        svn_tristate_t default_value)
978251881Speter{
979251881Speter  const char *tmp_value;
980251881Speter
981251881Speter  svn_config_get(cfg, &tmp_value, section, option, NULL);
982251881Speter
983251881Speter  if (! tmp_value)
984251881Speter    {
985251881Speter      *valuep = default_value;
986251881Speter    }
987251881Speter  else if (0 == svn_cstring_casecmp(tmp_value, unknown_value))
988251881Speter    {
989251881Speter      *valuep = svn_tristate_unknown;
990251881Speter    }
991251881Speter  else
992251881Speter    {
993251881Speter      svn_boolean_t bool_val;
994251881Speter      /* We already incorporated default_value into tmp_value if
995251881Speter         necessary, so the FALSE below will be ignored unless the
996251881Speter         caller is doing something it shouldn't be doing. */
997251881Speter      SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
998251881Speter      *valuep = bool_val ? svn_tristate_true : svn_tristate_false;
999251881Speter    }
1000251881Speter
1001251881Speter  return SVN_NO_ERROR;
1002251881Speter}
1003251881Speter
1004251881Speterint
1005251881Spetersvn_config_enumerate_sections(svn_config_t *cfg,
1006251881Speter                              svn_config_section_enumerator_t callback,
1007251881Speter                              void *baton)
1008251881Speter{
1009251881Speter  apr_hash_index_t *sec_ndx;
1010251881Speter  int count = 0;
1011251881Speter  apr_pool_t *subpool = svn_pool_create(cfg->x_pool);
1012251881Speter
1013251881Speter  for (sec_ndx = apr_hash_first(subpool, cfg->sections);
1014251881Speter       sec_ndx != NULL;
1015251881Speter       sec_ndx = apr_hash_next(sec_ndx))
1016251881Speter    {
1017251881Speter      void *sec_ptr;
1018251881Speter      cfg_section_t *sec;
1019251881Speter
1020251881Speter      apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
1021251881Speter      sec = sec_ptr;
1022251881Speter      ++count;
1023251881Speter      if (!callback(sec->name, baton))
1024251881Speter        break;
1025251881Speter    }
1026251881Speter
1027251881Speter  svn_pool_destroy(subpool);
1028251881Speter  return count;
1029251881Speter}
1030251881Speter
1031251881Speter
1032251881Speterint
1033251881Spetersvn_config_enumerate_sections2(svn_config_t *cfg,
1034251881Speter                               svn_config_section_enumerator2_t callback,
1035251881Speter                               void *baton, apr_pool_t *pool)
1036251881Speter{
1037251881Speter  apr_hash_index_t *sec_ndx;
1038251881Speter  apr_pool_t *iteration_pool;
1039251881Speter  int count = 0;
1040251881Speter
1041251881Speter  iteration_pool = svn_pool_create(pool);
1042251881Speter  for (sec_ndx = apr_hash_first(pool, cfg->sections);
1043251881Speter       sec_ndx != NULL;
1044251881Speter       sec_ndx = apr_hash_next(sec_ndx))
1045251881Speter    {
1046362181Sdim      cfg_section_t *sec = apr_hash_this_val(sec_ndx);
1047251881Speter
1048251881Speter      ++count;
1049251881Speter      svn_pool_clear(iteration_pool);
1050251881Speter      if (!callback(sec->name, baton, iteration_pool))
1051251881Speter        break;
1052251881Speter    }
1053251881Speter  svn_pool_destroy(iteration_pool);
1054251881Speter
1055251881Speter  return count;
1056251881Speter}
1057251881Speter
1058251881Speter
1059251881Speter
1060251881Speterint
1061251881Spetersvn_config_enumerate(svn_config_t *cfg, const char *section,
1062251881Speter                     svn_config_enumerator_t callback, void *baton)
1063251881Speter{
1064251881Speter  cfg_section_t *sec;
1065251881Speter  apr_hash_index_t *opt_ndx;
1066251881Speter  int count;
1067251881Speter  apr_pool_t *subpool;
1068251881Speter
1069251881Speter  find_option(cfg, section, NULL, &sec);
1070251881Speter  if (sec == NULL)
1071251881Speter    return 0;
1072251881Speter
1073289180Speter  subpool = svn_pool_create(cfg->pool);
1074251881Speter  count = 0;
1075251881Speter  for (opt_ndx = apr_hash_first(subpool, sec->options);
1076251881Speter       opt_ndx != NULL;
1077251881Speter       opt_ndx = apr_hash_next(opt_ndx))
1078251881Speter    {
1079362181Sdim      cfg_option_t *opt = apr_hash_this_val(opt_ndx);
1080251881Speter      const char *temp_value;
1081251881Speter
1082251881Speter      ++count;
1083251881Speter      make_string_from_option(&temp_value, cfg, sec, opt, NULL);
1084251881Speter      if (!callback(opt->name, temp_value, baton))
1085251881Speter        break;
1086251881Speter    }
1087251881Speter
1088251881Speter  svn_pool_destroy(subpool);
1089251881Speter  return count;
1090251881Speter}
1091251881Speter
1092251881Speter
1093251881Speterint
1094251881Spetersvn_config_enumerate2(svn_config_t *cfg, const char *section,
1095251881Speter                      svn_config_enumerator2_t callback, void *baton,
1096251881Speter                      apr_pool_t *pool)
1097251881Speter{
1098251881Speter  cfg_section_t *sec;
1099251881Speter  apr_hash_index_t *opt_ndx;
1100251881Speter  apr_pool_t *iteration_pool;
1101251881Speter  int count;
1102251881Speter
1103251881Speter  find_option(cfg, section, NULL, &sec);
1104251881Speter  if (sec == NULL)
1105251881Speter    return 0;
1106251881Speter
1107251881Speter  iteration_pool = svn_pool_create(pool);
1108251881Speter  count = 0;
1109251881Speter  for (opt_ndx = apr_hash_first(pool, sec->options);
1110251881Speter       opt_ndx != NULL;
1111251881Speter       opt_ndx = apr_hash_next(opt_ndx))
1112251881Speter    {
1113362181Sdim      cfg_option_t *opt = apr_hash_this_val(opt_ndx);
1114251881Speter      const char *temp_value;
1115251881Speter
1116251881Speter      ++count;
1117251881Speter      make_string_from_option(&temp_value, cfg, sec, opt, NULL);
1118251881Speter      svn_pool_clear(iteration_pool);
1119251881Speter      if (!callback(opt->name, temp_value, baton, iteration_pool))
1120251881Speter        break;
1121251881Speter    }
1122251881Speter  svn_pool_destroy(iteration_pool);
1123251881Speter
1124251881Speter  return count;
1125251881Speter}
1126251881Speter
1127251881Speter
1128251881Speter
1129251881Speter/* Baton for search_groups() */
1130251881Speterstruct search_groups_baton
1131251881Speter{
1132251881Speter  const char *key;          /* Provided by caller of svn_config_find_group */
1133251881Speter  const char *match;        /* Filled in by search_groups */
1134251881Speter  apr_pool_t *pool;
1135251881Speter};
1136251881Speter
1137251881Speter
1138251881Speter/* This is an `svn_config_enumerator_t' function, and BATON is a
1139251881Speter * `struct search_groups_baton *'.
1140251881Speter */
1141251881Speterstatic svn_boolean_t search_groups(const char *name,
1142251881Speter                                   const char *value,
1143251881Speter                                   void *baton,
1144251881Speter                                   apr_pool_t *pool)
1145251881Speter{
1146251881Speter  struct search_groups_baton *b = baton;
1147251881Speter  apr_array_header_t *list;
1148251881Speter
1149251881Speter  list = svn_cstring_split(value, ",", TRUE, pool);
1150251881Speter  if (svn_cstring_match_glob_list(b->key, list))
1151251881Speter    {
1152251881Speter      /* Fill in the match and return false, to stop enumerating. */
1153251881Speter      b->match = apr_pstrdup(b->pool, name);
1154251881Speter      return FALSE;
1155251881Speter    }
1156251881Speter  else
1157251881Speter    return TRUE;
1158251881Speter}
1159251881Speter
1160251881Speter
1161251881Speterconst char *svn_config_find_group(svn_config_t *cfg, const char *key,
1162251881Speter                                  const char *master_section,
1163251881Speter                                  apr_pool_t *pool)
1164251881Speter{
1165251881Speter  struct search_groups_baton gb;
1166251881Speter
1167251881Speter  gb.key = key;
1168251881Speter  gb.match = NULL;
1169251881Speter  gb.pool = pool;
1170251881Speter  (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
1171251881Speter  return gb.match;
1172251881Speter}
1173251881Speter
1174251881Speter
1175251881Speterconst char*
1176251881Spetersvn_config_get_server_setting(svn_config_t *cfg,
1177251881Speter                              const char* server_group,
1178251881Speter                              const char* option_name,
1179251881Speter                              const char* default_value)
1180251881Speter{
1181251881Speter  const char *retval;
1182251881Speter  svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL,
1183251881Speter                 option_name, default_value);
1184251881Speter  if (server_group)
1185251881Speter    {
1186251881Speter      svn_config_get(cfg, &retval, server_group, option_name, retval);
1187251881Speter    }
1188251881Speter  return retval;
1189251881Speter}
1190251881Speter
1191251881Speter
1192251881Spetersvn_error_t *
1193251881Spetersvn_config_dup(svn_config_t **cfgp,
1194289180Speter               const svn_config_t *src,
1195251881Speter               apr_pool_t *pool)
1196251881Speter{
1197251881Speter  apr_hash_index_t *sectidx;
1198251881Speter  apr_hash_index_t *optidx;
1199251881Speter
1200251881Speter  *cfgp = 0;
1201251881Speter  SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
1202251881Speter
1203251881Speter  (*cfgp)->x_values = src->x_values;
1204251881Speter  (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
1205251881Speter  (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
1206251881Speter
1207251881Speter  for (sectidx = apr_hash_first(pool, src->sections);
1208251881Speter       sectidx != NULL;
1209251881Speter       sectidx = apr_hash_next(sectidx))
1210251881Speter  {
1211251881Speter    const void *sectkey;
1212251881Speter    void *sectval;
1213251881Speter    apr_ssize_t sectkeyLength;
1214251881Speter    cfg_section_t * srcsect;
1215251881Speter    cfg_section_t * destsec;
1216251881Speter
1217251881Speter    apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
1218251881Speter    srcsect = sectval;
1219251881Speter
1220251881Speter    destsec = svn_config_addsection(*cfgp, srcsect->name);
1221251881Speter
1222251881Speter    for (optidx = apr_hash_first(pool, srcsect->options);
1223251881Speter         optidx != NULL;
1224251881Speter         optidx = apr_hash_next(optidx))
1225251881Speter    {
1226251881Speter      const void *optkey;
1227251881Speter      void *optval;
1228251881Speter      apr_ssize_t optkeyLength;
1229251881Speter      cfg_option_t *srcopt;
1230251881Speter      cfg_option_t *destopt;
1231251881Speter
1232251881Speter      apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
1233251881Speter      srcopt = optval;
1234251881Speter
1235251881Speter      svn_config_create_option(&destopt, srcopt->name, srcopt->value,
1236251881Speter                               (*cfgp)->option_names_case_sensitive,
1237251881Speter                               pool);
1238251881Speter
1239251881Speter      destopt->value = apr_pstrdup(pool, srcopt->value);
1240251881Speter      destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
1241362181Sdim      destopt->state = srcopt->state;
1242251881Speter      apr_hash_set(destsec->options,
1243251881Speter                   apr_pstrdup(pool, (const char*)optkey),
1244251881Speter                   optkeyLength, destopt);
1245251881Speter    }
1246251881Speter  }
1247251881Speter
1248251881Speter  return SVN_NO_ERROR;
1249251881Speter}
1250251881Speter
1251251881Spetersvn_error_t *
1252251881Spetersvn_config_copy_config(apr_hash_t **cfg_hash,
1253251881Speter                       apr_hash_t *src_hash,
1254251881Speter                       apr_pool_t *pool)
1255251881Speter{
1256251881Speter  apr_hash_index_t *cidx;
1257251881Speter
1258362181Sdim  *cfg_hash = svn_hash__make(pool);
1259251881Speter  for (cidx = apr_hash_first(pool, src_hash);
1260251881Speter       cidx != NULL;
1261251881Speter       cidx = apr_hash_next(cidx))
1262251881Speter  {
1263251881Speter    const void *ckey;
1264251881Speter    void *cval;
1265251881Speter    apr_ssize_t ckeyLength;
1266251881Speter    svn_config_t * srcconfig;
1267251881Speter    svn_config_t * destconfig;
1268251881Speter
1269251881Speter    apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
1270251881Speter    srcconfig = cval;
1271251881Speter
1272251881Speter    SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
1273251881Speter
1274251881Speter    apr_hash_set(*cfg_hash,
1275251881Speter                 apr_pstrdup(pool, (const char*)ckey),
1276251881Speter                 ckeyLength, destconfig);
1277251881Speter  }
1278251881Speter
1279251881Speter  return SVN_NO_ERROR;
1280251881Speter}
1281251881Speter
1282251881Spetersvn_error_t*
1283251881Spetersvn_config_get_server_setting_int(svn_config_t *cfg,
1284251881Speter                                  const char *server_group,
1285251881Speter                                  const char *option_name,
1286251881Speter                                  apr_int64_t default_value,
1287251881Speter                                  apr_int64_t *result_value,
1288251881Speter                                  apr_pool_t *pool)
1289251881Speter{
1290251881Speter  const char* tmp_value;
1291251881Speter  char *end_pos;
1292251881Speter
1293251881Speter  tmp_value = svn_config_get_server_setting(cfg, server_group,
1294251881Speter                                            option_name, NULL);
1295251881Speter  if (tmp_value == NULL)
1296251881Speter    *result_value = default_value;
1297251881Speter  else
1298251881Speter    {
1299251881Speter      /* read tmp_value as an int now */
1300251881Speter      *result_value = apr_strtoi64(tmp_value, &end_pos, 0);
1301251881Speter
1302251881Speter      if (*end_pos != 0)
1303251881Speter        {
1304251881Speter          return svn_error_createf
1305251881Speter            (SVN_ERR_BAD_CONFIG_VALUE, NULL,
1306251881Speter             _("Config error: invalid integer value '%s'"),
1307251881Speter             tmp_value);
1308251881Speter        }
1309251881Speter    }
1310251881Speter
1311251881Speter  return SVN_NO_ERROR;
1312251881Speter}
1313251881Speter
1314251881Spetersvn_error_t *
1315251881Spetersvn_config_get_server_setting_bool(svn_config_t *cfg,
1316251881Speter                                   svn_boolean_t *valuep,
1317251881Speter                                   const char *server_group,
1318251881Speter                                   const char *option_name,
1319251881Speter                                   svn_boolean_t default_value)
1320251881Speter{
1321251881Speter  const char* tmp_value;
1322251881Speter  tmp_value = svn_config_get_server_setting(cfg, server_group,
1323251881Speter                                            option_name, NULL);
1324251881Speter  return get_bool(valuep, tmp_value, default_value,
1325251881Speter                  server_group, option_name);
1326251881Speter}
1327251881Speter
1328251881Speter
1329251881Spetersvn_boolean_t
1330251881Spetersvn_config_has_section(svn_config_t *cfg, const char *section)
1331251881Speter{
1332289180Speter  return NULL != get_hash_value(cfg->sections, cfg->tmp_key, section,
1333289180Speter                                cfg->section_names_case_sensitive);
1334251881Speter}
1335362181Sdim
1336362181Sdimsvn_error_t *
1337362181Sdimsvn_config__write(svn_stream_t *stream,
1338362181Sdim                  const struct svn_config_t *cfg,
1339362181Sdim                  apr_pool_t *scratch_pool)
1340362181Sdim{
1341362181Sdim  apr_hash_index_t *section_i;
1342362181Sdim  apr_hash_index_t *options_i;
1343362181Sdim  apr_pool_t *section_pool = svn_pool_create(scratch_pool);
1344362181Sdim  apr_pool_t *options_pool = svn_pool_create(scratch_pool);
1345362181Sdim
1346362181Sdim  for (section_i = apr_hash_first(scratch_pool, cfg->sections);
1347362181Sdim       section_i != NULL;
1348362181Sdim       section_i = apr_hash_next(section_i))
1349362181Sdim    {
1350362181Sdim      cfg_section_t *section = apr_hash_this_val(section_i);
1351362181Sdim      svn_pool_clear(section_pool);
1352362181Sdim      SVN_ERR(svn_stream_printf(stream, section_pool, "\n[%s]\n",
1353362181Sdim                                section->name));
1354362181Sdim
1355362181Sdim      for (options_i = apr_hash_first(section_pool, section->options);
1356362181Sdim           options_i != NULL;
1357362181Sdim           options_i = apr_hash_next(options_i))
1358362181Sdim        {
1359362181Sdim          cfg_option_t *option = apr_hash_this_val(options_i);
1360362181Sdim          svn_pool_clear(options_pool);
1361362181Sdim          SVN_ERR(svn_stream_printf(stream, options_pool, "%s=%s\n",
1362362181Sdim                                    option->name, option->value));
1363362181Sdim        }
1364362181Sdim    }
1365362181Sdim
1366362181Sdim  svn_pool_destroy(section_pool);
1367362181Sdim  svn_pool_destroy(options_pool);
1368362181Sdim
1369362181Sdim  return SVN_NO_ERROR;
1370362181Sdim}
1371362181Sdim
1372