aboutsummaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/config.c')
-rw-r--r--subversion/libsvn_subr/config.c206
1 files changed, 155 insertions, 51 deletions
diff --git a/subversion/libsvn_subr/config.c b/subversion/libsvn_subr/config.c
index 770d699ef020..5c3cf440a208 100644
--- a/subversion/libsvn_subr/config.c
+++ b/subversion/libsvn_subr/config.c
@@ -33,12 +33,15 @@
#include <apr_lib.h>
#include "svn_hash.h"
#include "svn_error.h"
+#include "svn_string.h"
#include "svn_pools.h"
#include "config_impl.h"
-#include "svn_private_config.h"
#include "private/svn_dep_compat.h"
#include "private/svn_subr_private.h"
+#include "private/svn_config_private.h"
+
+#include "svn_private_config.h"
@@ -55,6 +58,27 @@ struct cfg_section_t
};
+/* States that a config option value can assume. */
+typedef enum option_state_t
+{
+ /* Value still needs to be expanded.
+ This is the initial state for *all* values. */
+ option_state_needs_expanding,
+
+ /* Value is currently being expanded.
+ This transitional state allows for detecting cyclic dependencies. */
+ option_state_expanding,
+
+ /* Expanded value is available.
+ Values that never needed expanding directly go into that state
+ skipping option_state_expanding. */
+ option_state_expanded,
+
+ /* The value expansion is cyclic which results in "undefined" behavior.
+ This is to return a defined value ("") in that case. */
+ option_state_cyclic
+} option_state_t;
+
/* Option table entries. */
typedef struct cfg_option_t cfg_option_t;
struct cfg_option_t
@@ -71,10 +95,10 @@ struct cfg_option_t
/* The expanded option value. */
const char *x_value;
- /* Expansion flag. If this is TRUE, this value has already been expanded.
- In this case, if x_value is NULL, no expansions were necessary,
- and value should be used directly. */
- svn_boolean_t expanded;
+ /* Expansion state. If this is option_state_expanded, VALUE has already
+ been expanded. In this case, if x_value is NULL, no expansions were
+ necessary, and value should be used directly. */
+ option_state_t state;
};
@@ -87,7 +111,7 @@ svn_config_create2(svn_config_t **cfgp,
{
svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
- cfg->sections = apr_hash_make(result_pool);
+ cfg->sections = svn_hash__make(result_pool);
cfg->pool = result_pool;
cfg->x_pool = svn_pool_create(result_pool);
cfg->x_values = FALSE;
@@ -138,6 +162,18 @@ svn_config_read3(svn_config_t **cfgp, const char *file,
}
svn_error_t *
+svn_config__default_add_value_fn(void *baton,
+ svn_stringbuf_t *section,
+ svn_stringbuf_t *option,
+ svn_stringbuf_t *value)
+{
+ /* FIXME: We may as well propagate the known string sizes here. */
+ svn_config_set((svn_config_t *)baton, section->data,
+ option->data, value->data);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
svn_boolean_t section_names_case_sensitive,
svn_boolean_t option_names_case_sensitive,
@@ -153,7 +189,12 @@ svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
result_pool);
if (err == SVN_NO_ERROR)
- err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
+ err = svn_config__parse_stream(stream,
+ svn_config__constructor_create(
+ NULL, NULL,
+ svn_config__default_add_value_fn,
+ scratch_pool),
+ cfg, scratch_pool);
if (err == SVN_NO_ERROR)
*cfgp = cfg;
@@ -294,7 +335,7 @@ svn_config_get_config(apr_hash_t **cfg_hash,
apr_pool_t *pool)
{
svn_config_t *cfg;
- *cfg_hash = apr_hash_make(pool);
+ *cfg_hash = svn_hash__make(pool);
SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS,
pool));
@@ -312,7 +353,7 @@ svn_config__get_default_config(apr_hash_t **cfg_hash,
apr_pool_t *pool)
{
svn_config_t *empty_cfg;
- *cfg_hash = apr_hash_make(pool);
+ *cfg_hash = svn_hash__make(pool);
SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg);
@@ -338,22 +379,14 @@ for_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool,
sec_ndx != NULL;
sec_ndx = apr_hash_next(sec_ndx))
{
- void *sec_ptr;
- cfg_section_t *sec;
+ cfg_section_t *sec = apr_hash_this_val(sec_ndx);
apr_hash_index_t *opt_ndx;
- apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
- sec = sec_ptr;
-
for (opt_ndx = apr_hash_first(pool, sec->options);
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
- void *opt_ptr;
- cfg_option_t *opt;
-
- apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
- opt = opt_ptr;
+ cfg_option_t *opt = apr_hash_this_val(opt_ndx);
if (callback(baton, sec, opt))
return;
@@ -396,12 +429,13 @@ svn_config_merge(svn_config_t *cfg, const char *file,
static svn_boolean_t
rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
{
- /* Only clear the `expanded' flag if the value actually contains
+ /* Only reset the expansion state if the value actually contains
variable expansions. */
- if (option->expanded && option->x_value != NULL)
+ if ( (option->state == option_state_expanded && option->x_value != NULL)
+ || option->state == option_state_cyclic)
{
option->x_value = NULL;
- option->expanded = FALSE;
+ option->state = option_state_needs_expanding;
}
return FALSE;
@@ -482,7 +516,7 @@ find_option(svn_config_t *cfg, const char *section, const char *option,
/* Has a bi-directional dependency with make_string_from_option(). */
-static void
+static svn_boolean_t
expand_option_value(svn_config_t *cfg, cfg_section_t *section,
const char *opt_value, const char **opt_x_valuep,
apr_pool_t *x_pool);
@@ -496,7 +530,20 @@ make_string_from_option(const char **valuep, svn_config_t *cfg,
apr_pool_t* x_pool)
{
/* Expand the option value if necessary. */
- if (!opt->expanded)
+ if ( opt->state == option_state_expanding
+ || opt->state == option_state_cyclic)
+ {
+ /* Recursion is not supported. Since we can't produce an error
+ * nor should we abort the process, the next best thing is to
+ * report the recursive part as an empty string. */
+ *valuep = "";
+
+ /* Go into "value undefined" state. */
+ opt->state = option_state_cyclic;
+
+ return;
+ }
+ else if (opt->state == option_state_needs_expanding)
{
/* before attempting to expand an option, check for the placeholder.
* If none is there, there is no point in calling expand_option_value.
@@ -511,9 +558,16 @@ make_string_from_option(const char **valuep, svn_config_t *cfg,
tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
- expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
- opt->expanded = TRUE;
+ /* Expand the value. During that process, have the option marked
+ * as "expanding" to detect cycles. */
+ opt->state = option_state_expanding;
+ if (expand_option_value(cfg, section, opt->value, &opt->x_value,
+ tmp_pool))
+ opt->state = option_state_expanded;
+ else
+ opt->state = option_state_cyclic;
+ /* Ensure the expanded value is allocated in a permanent pool. */
if (x_pool != cfg->x_pool)
{
/* Grab the fully expanded value from tmp_pool before its
@@ -527,7 +581,7 @@ make_string_from_option(const char **valuep, svn_config_t *cfg,
}
else
{
- opt->expanded = TRUE;
+ opt->state = option_state_expanded;
}
}
@@ -549,8 +603,9 @@ make_string_from_option(const char **valuep, svn_config_t *cfg,
/* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
If no variable replacements are done, set *OPT_X_VALUEP to
- NULL. Allocate from X_POOL. */
-static void
+ NULL. Return TRUE if the expanded value is defined and FALSE
+ for recursive definitions. Allocate from X_POOL. */
+static svn_boolean_t
expand_option_value(svn_config_t *cfg, cfg_section_t *section,
const char *opt_value, const char **opt_x_valuep,
apr_pool_t *x_pool)
@@ -587,6 +642,18 @@ expand_option_value(svn_config_t *cfg, cfg_section_t *section,
should terminate. */
make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
+ /* Values depending on cyclic values must also be marked as
+ * "undefined" because they might themselves form cycles with
+ * the one cycle we just detected. Due to the early abort of
+ * the recursion, we won't follow and thus detect dependent
+ * cycles anymore.
+ */
+ if (x_opt->state == option_state_cyclic)
+ {
+ *opt_x_valuep = "";
+ return FALSE;
+ }
+
/* Append the plain text preceding the expansion. */
len = name_start - FMT_START_LEN - copy_from;
if (buf == NULL)
@@ -625,6 +692,9 @@ expand_option_value(svn_config_t *cfg, cfg_section_t *section,
}
else
*opt_x_valuep = NULL;
+
+ /* Expansion has a well-defined answer. */
+ return TRUE;
}
static cfg_section_t *
@@ -640,7 +710,8 @@ svn_config_addsection(svn_config_t *cfg,
hash_key = s->name;
else
hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
- s->options = apr_hash_make(cfg->pool);
+ s->options = svn_hash__make(cfg->pool);
+
svn_hash_sets(cfg->sections, hash_key, s);
return s;
@@ -664,7 +735,7 @@ svn_config_create_option(cfg_option_t **opt,
o->value = apr_pstrdup(pool, value);
o->x_value = NULL;
- o->expanded = FALSE;
+ o->state = option_state_needs_expanding;
*opt = o;
}
@@ -685,7 +756,8 @@ svn_config__is_expanded(svn_config_t *cfg,
return FALSE;
/* already expanded? */
- if (opt->expanded)
+ if ( opt->state == option_state_expanded
+ || opt->state == option_state_cyclic)
return TRUE;
/* needs expansion? */
@@ -719,8 +791,14 @@ svn_config_get(svn_config_t *cfg, const char **valuep,
{
apr_pool_t *tmp_pool = svn_pool_create(cfg->pool);
const char *x_default;
- expand_option_value(cfg, sec, default_value, &x_default, tmp_pool);
- if (x_default)
+ if (!expand_option_value(cfg, sec, default_value, &x_default,
+ tmp_pool))
+ {
+ /* Recursive definitions are not supported.
+ Normalize the answer in that case. */
+ *valuep = "";
+ }
+ else if (x_default)
{
svn_stringbuf_set(cfg->tmp_value, x_default);
*valuep = cfg->tmp_value->data;
@@ -758,7 +836,7 @@ svn_config_set(svn_config_t *cfg,
{
/* Replace the option's value. */
opt->value = apr_pstrdup(cfg->pool, value);
- opt->expanded = FALSE;
+ opt->state = option_state_needs_expanding;
return;
}
@@ -965,11 +1043,8 @@ svn_config_enumerate_sections2(svn_config_t *cfg,
sec_ndx != NULL;
sec_ndx = apr_hash_next(sec_ndx))
{
- void *sec_ptr;
- cfg_section_t *sec;
+ cfg_section_t *sec = apr_hash_this_val(sec_ndx);
- apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
- sec = sec_ptr;
++count;
svn_pool_clear(iteration_pool);
if (!callback(sec->name, baton, iteration_pool))
@@ -1001,13 +1076,9 @@ svn_config_enumerate(svn_config_t *cfg, const char *section,
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
- void *opt_ptr;
- cfg_option_t *opt;
+ cfg_option_t *opt = apr_hash_this_val(opt_ndx);
const char *temp_value;
- apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
- opt = opt_ptr;
-
++count;
make_string_from_option(&temp_value, cfg, sec, opt, NULL);
if (!callback(opt->name, temp_value, baton))
@@ -1039,13 +1110,9 @@ svn_config_enumerate2(svn_config_t *cfg, const char *section,
opt_ndx != NULL;
opt_ndx = apr_hash_next(opt_ndx))
{
- void *opt_ptr;
- cfg_option_t *opt;
+ cfg_option_t *opt = apr_hash_this_val(opt_ndx);
const char *temp_value;
- apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
- opt = opt_ptr;
-
++count;
make_string_from_option(&temp_value, cfg, sec, opt, NULL);
svn_pool_clear(iteration_pool);
@@ -1171,7 +1238,7 @@ svn_config_dup(svn_config_t **cfgp,
destopt->value = apr_pstrdup(pool, srcopt->value);
destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
- destopt->expanded = srcopt->expanded;
+ destopt->state = srcopt->state;
apr_hash_set(destsec->options,
apr_pstrdup(pool, (const char*)optkey),
optkeyLength, destopt);
@@ -1188,7 +1255,7 @@ svn_config_copy_config(apr_hash_t **cfg_hash,
{
apr_hash_index_t *cidx;
- *cfg_hash = apr_hash_make(pool);
+ *cfg_hash = svn_hash__make(pool);
for (cidx = apr_hash_first(pool, src_hash);
cidx != NULL;
cidx = apr_hash_next(cidx))
@@ -1265,3 +1332,40 @@ svn_config_has_section(svn_config_t *cfg, const char *section)
return NULL != get_hash_value(cfg->sections, cfg->tmp_key, section,
cfg->section_names_case_sensitive);
}
+
+svn_error_t *
+svn_config__write(svn_stream_t *stream,
+ const struct svn_config_t *cfg,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *section_i;
+ apr_hash_index_t *options_i;
+ apr_pool_t *section_pool = svn_pool_create(scratch_pool);
+ apr_pool_t *options_pool = svn_pool_create(scratch_pool);
+
+ for (section_i = apr_hash_first(scratch_pool, cfg->sections);
+ section_i != NULL;
+ section_i = apr_hash_next(section_i))
+ {
+ cfg_section_t *section = apr_hash_this_val(section_i);
+ svn_pool_clear(section_pool);
+ SVN_ERR(svn_stream_printf(stream, section_pool, "\n[%s]\n",
+ section->name));
+
+ for (options_i = apr_hash_first(section_pool, section->options);
+ options_i != NULL;
+ options_i = apr_hash_next(options_i))
+ {
+ cfg_option_t *option = apr_hash_this_val(options_i);
+ svn_pool_clear(options_pool);
+ SVN_ERR(svn_stream_printf(stream, options_pool, "%s=%s\n",
+ option->name, option->value));
+ }
+ }
+
+ svn_pool_destroy(section_pool);
+ svn_pool_destroy(options_pool);
+
+ return SVN_NO_ERROR;
+}
+