settings: Add support to enumerate sections and key/value pairs with fallbacks

This commit is contained in:
Tobias Brunner
2014-02-12 14:34:33 +01:00
parent f4da1989cd
commit 9925eeabd2
3 changed files with 188 additions and 46 deletions
+25 -11
View File
@@ -714,14 +714,23 @@ START_TEST(test_add_fallback)
verify_string("subval2", "main.sub1.key2");
verify_string("subsubval1", "main.sub1.subsub.subkey1");
/* fallbacks currently have no effect on section & key/value enumerators */
keys = linked_list_create_with_items(NULL);
keys = linked_list_create_with_items("sub1", NULL);
verify_sections(keys, "main");
keys = linked_list_create_with_items("subsub", NULL);
verify_sections(keys, "main.sub1");
keys = linked_list_create_with_items("key1", NULL);
values = linked_list_create_with_items("val1", NULL);
verify_key_values(keys, values, "main");
keys = linked_list_create_with_items("key1", "key2", NULL);
values = linked_list_create_with_items("val1", "subval2", NULL);
verify_key_values(keys, values, "main.sub1");
keys = linked_list_create_with_items("subkey1", NULL);
values = linked_list_create_with_items("subsubval1", NULL);
verify_key_values(keys, values, "main.sub1.subsub");
settings->add_fallback(settings, "main", "base");
verify_string("val1", "main.key1");
verify_string("baseval2", "main.key2");
@@ -732,18 +741,23 @@ START_TEST(test_add_fallback)
verify_string("subbase3", "main.sub1.key3");
verify_string("subbase4", "main.sub2.key4");
keys = linked_list_create_with_items(NULL);
verify_sections(keys, "main.sub1");
keys = linked_list_create_with_items("sub1", NULL);
verify_sections(keys, "main");
keys = linked_list_create_with_items("key1", NULL);
values = linked_list_create_with_items("val1", NULL);
keys = linked_list_create_with_items("sub1", "sub2", NULL);
verify_sections(keys, "main");
keys = linked_list_create_with_items("subsub", NULL);
verify_sections(keys, "main.sub1");
keys = linked_list_create_with_items("key1", "key2", NULL);
values = linked_list_create_with_items("val1", "baseval2", NULL);
verify_key_values(keys, values, "main");
keys = linked_list_create_with_items("key1", "key2", "key3", NULL);
values = linked_list_create_with_items("val1", "subval2", "subbase3", NULL);
verify_key_values(keys, values, "main.sub1");
keys = linked_list_create_with_items("key1", NULL);
values = linked_list_create_with_items("val1", NULL);
verify_key_values(keys, values, "main");
keys = linked_list_create_with_items("subkey1", "subkey2", NULL);
values = linked_list_create_with_items("subsubval1", "subsubbaseval2", NULL);
verify_key_values(keys, values, "main.sub1.subsub");
settings->set_str(settings, "main.sub1.key2", "val2");
verify_string("val2", "main.sub1.key2");
+157 -28
View File
@@ -32,6 +32,7 @@
#include "settings.h"
#include "collections/array.h"
#include "collections/hashtable.h"
#include "collections/linked_list.h"
#include "threading/rwlock.h"
#include "utils/debug.h"
@@ -307,23 +308,67 @@ static section_t *find_section_buffered(section_t *section,
}
/**
* Find a section by a given key (thread-safe).
* Find all sections via a given key considering fallbacks, using buffered key,
* reusable buffer.
*/
static section_t *find_section(private_settings_t *this, section_t *section,
char *key, va_list args)
static void find_sections_buffered(section_t *section, char *start, char *key,
va_list args, char *buf, int len, array_t **sections)
{
char buf[128], keybuf[512];
section_t *found;
section_t *found = NULL, *fallback;
char *pos;
int i;
if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
if (!section)
{
return NULL;
return;
}
pos = strchr(key, '.');
if (pos)
{
*pos = '\0';
}
if (!print_key(buf, len, start, key, args))
{
return;
}
if (pos)
{ /* restore so we can follow fallbacks */
*pos = '.';
}
if (!strlen(buf))
{
found = section;
}
else
{
array_bsearch(section->sections, buf, section_find, &found);
}
if (found)
{
if (pos)
{
find_sections_buffered(found, start, pos+1, args, buf, len,
sections);
}
else
{
array_insert_create(sections, ARRAY_TAIL, found);
for (i = 0; i < array_count(found->fallbacks); i++)
{
array_get(found->fallbacks, i, &fallback);
array_insert_create(sections, ARRAY_TAIL, fallback);
}
}
}
if (section->fallbacks)
{
for (i = 0; i < array_count(section->fallbacks); i++)
{
array_get(section->fallbacks, i, &fallback);
find_sections_buffered(fallback, start, key, args, buf, len,
sections);
}
}
this->lock->read_lock(this->lock);
found = find_section_buffered(section, keybuf, keybuf, args, buf,
sizeof(buf), FALSE);
this->lock->unlock(this->lock);
return found;
}
/**
@@ -347,6 +392,26 @@ static section_t *ensure_section(private_settings_t *this, section_t *section,
return found;
}
/**
* Find a section by a given key with its fallbacks (not thread-safe!).
* Sections are returned in depth-first order (array is allocated). NULL is
* returned if no sections are found.
*/
static array_t *find_sections(private_settings_t *this, section_t *section,
char *key, va_list args)
{
char buf[128], keybuf[512];
array_t *sections = NULL;
if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
{
return NULL;
}
find_sections_buffered(section, keybuf, keybuf, args, buf,
sizeof(buf), &sections);
return sections;
}
/**
* Check if the given fallback section already exists
*/
@@ -785,64 +850,128 @@ METHOD(settings_t, set_default_str, bool,
return FALSE;
}
/**
* Data for enumerators
*/
typedef struct {
/** settings_t instance */
private_settings_t *settings;
/** sections to enumerate */
array_t *sections;
/** sections/keys that were already enumerated */
hashtable_t *seen;
} enumerator_data_t;
/**
* Destroy enumerator data
*/
static void enumerator_destroy(enumerator_data_t *this)
{
this->settings->lock->unlock(this->settings->lock);
this->seen->destroy(this->seen);
array_destroy(this->sections);
free(this);
}
/**
* Enumerate section names, not sections
*/
static bool section_filter(void *null, section_t **in, char **out)
static bool section_filter(hashtable_t *seen, section_t **in, char **out)
{
*out = (*in)->name;
if (seen->get(seen, *out))
{
return FALSE;
}
seen->put(seen, *out, *out);
return TRUE;
}
/**
* Enumerate sections of the given section
*/
static enumerator_t *section_enumerator(section_t *section,
enumerator_data_t *data)
{
return enumerator_create_filter(array_create_enumerator(section->sections),
(void*)section_filter, data->seen, NULL);
}
METHOD(settings_t, create_section_enumerator, enumerator_t*,
private_settings_t *this, char *key, ...)
{
section_t *section;
enumerator_data_t *data;
array_t *sections;
va_list args;
this->lock->read_lock(this->lock);
va_start(args, key);
section = find_section(this, this->top, key, args);
sections = find_sections(this, this->top, key, args);
va_end(args);
if (!section)
if (!sections)
{
this->lock->unlock(this->lock);
return enumerator_create_empty();
}
this->lock->read_lock(this->lock);
return enumerator_create_filter(
array_create_enumerator(section->sections),
(void*)section_filter, this->lock, (void*)this->lock->unlock);
INIT(data,
.settings = this,
.sections = sections,
.seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
);
return enumerator_create_nested(array_create_enumerator(sections),
(void*)section_enumerator, data, (void*)enumerator_destroy);
}
/**
* Enumerate key and values, not kv_t entries
*/
static bool kv_filter(void *null, kv_t **in, char **key,
static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
void *none, char **value)
{
*key = (*in)->key;
if (seen->get(seen, *key))
{
return FALSE;
}
*value = (*in)->value;
seen->put(seen, *key, *key);
return TRUE;
}
/**
* Enumerate key/value pairs of the given section
*/
static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
{
return enumerator_create_filter(array_create_enumerator(section->kv),
(void*)kv_filter, data->seen, NULL);
}
METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
private_settings_t *this, char *key, ...)
{
section_t *section;
enumerator_data_t *data;
array_t *sections;
va_list args;
this->lock->read_lock(this->lock);
va_start(args, key);
section = find_section(this, this->top, key, args);
sections = find_sections(this, this->top, key, args);
va_end(args);
if (!section)
if (!sections)
{
this->lock->unlock(this->lock);
return enumerator_create_empty();
}
this->lock->read_lock(this->lock);
return enumerator_create_filter(
array_create_enumerator(section->kv),
(void*)kv_filter, this->lock, (void*)this->lock->unlock);
INIT(data,
.settings = this,
.sections = sections,
.seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
);
return enumerator_create_nested(array_create_enumerator(sections),
(void*)kv_enumerator, data, (void*)enumerator_destroy);
}
METHOD(settings_t, add_fallback, void,
+6 -7
View File
@@ -276,13 +276,12 @@ struct settings_t {
* 'section-one.two' will result in a lookup for the same section/key
* in 'section-two'.
*
* @note This has only an effect on single value lookups. Enumerators for
* sections and key/value-pairs are not affected. The reason is that it is
* rather tricky to implement such a merge without requiring allocations for
* each lookup. For instance, if charon.tls has libtls as fallback and
* charon has libstrongswan as fallback, the key/value-enumerator for
* charon.tls had to enumerate the libtls and libstrongswan.tls sections
* too, but it would have to keep track of the already enumerated keys.
* @note Lookups are depth-first and currently strictly top-down.
* For instance, if app.sec had lib1.sec as fallback and lib1 had lib2 as
* fallback the keys/sections in lib2.sec would not be considered. But if
* app had lib3 as fallback the contents of lib3.sec would (as app is passed
* during the initial lookup). In the last example the order during
* enumerations would be app.sec, lib1.sec, lib3.sec.
*
* @note Additional arguments will be applied to both section format
* strings so they must be compatible.