settings: Move to a separate folder
authorTobias Brunner <tobias@strongswan.org>
Tue, 4 Mar 2014 13:18:42 +0000 (14:18 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 15 May 2014 09:28:05 +0000 (11:28 +0200)
src/libstrongswan/Android.mk
src/libstrongswan/Makefile.am
src/libstrongswan/library.h
src/libstrongswan/settings/settings.c [new file with mode: 0644]
src/libstrongswan/settings/settings.h [new file with mode: 0644]
src/libstrongswan/tests/suites/test_settings.c
src/libstrongswan/utils/settings.c [deleted file]
src/libstrongswan/utils/settings.h [deleted file]

index 24f0bc5..5f22bf1 100644 (file)
@@ -32,12 +32,12 @@ networking/streams/stream_service.c networking/streams/stream_manager.c \
 pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
 processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
 processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
-selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
+selectors/traffic_selector.c settings/settings.c \
+threading/thread.c threading/thread_value.c \
 threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
 utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
 utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
-utils/printf_hook/printf_hook_builtin.c utils/settings.c utils/test.c \
-utils/utils/strerror.c
+utils/printf_hook/printf_hook_builtin.c utils/test.c utils/utils/strerror.c
 
 # adding the plugin source files
 
index 3462d2f..d202b02 100644 (file)
@@ -30,12 +30,12 @@ networking/streams/stream_service.c networking/streams/stream_manager.c \
 pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
 processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
 processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
-selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
+selectors/traffic_selector.c settings/settings.c \
+threading/thread.c threading/thread_value.c \
 threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
 utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
 utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
-utils/settings.c utils/test.c \
-utils/utils/strerror.c
+utils/test.c utils/utils/strerror.c
 
 if USE_DEV_HEADERS
 strongswan_includedir = ${dev_headers}
@@ -75,15 +75,14 @@ resolver/rr.h resolver/resolver_manager.h \
 plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
 processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
 processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \
-threading/thread.h threading/thread_value.h \
+settings/settings.h threading/thread.h threading/thread_value.h \
 threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
 threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.h \
 utils/utils.h utils/chunk.h utils/debug.h utils/enum.h utils/identification.h \
 utils/lexparser.h utils/optionsfrom.h utils/capabilities.h utils/backtrace.h \
 utils/leak_detective.h utils/printf_hook/printf_hook.h \
 utils/printf_hook/printf_hook_vstr.h utils/printf_hook/printf_hook_builtin.h \
-utils/settings.h utils/test.h utils/integrity_checker.h \
-utils/utils/strerror.h
+utils/test.h utils/integrity_checker.h utils/utils/strerror.h
 endif
 
 library.lo :   $(top_builddir)/config.status
index 4125328..37a83fa 100644 (file)
 #include "utils/capabilities.h"
 #include "utils/integrity_checker.h"
 #include "utils/leak_detective.h"
-#include "utils/settings.h"
 #include "plugins/plugin_loader.h"
+#include "settings/settings.h"
 
 typedef struct library_t library_t;
 
diff --git a/src/libstrongswan/settings/settings.c b/src/libstrongswan/settings/settings.c
new file mode 100644 (file)
index 0000000..cf34fd1
--- /dev/null
@@ -0,0 +1,1520 @@
+/*
+ * Copyright (C) 2010-2014 Tobias Brunner
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif /* HAVE_GLOB_H */
+
+#include "settings.h"
+
+#include "collections/array.h"
+#include "collections/hashtable.h"
+#include "collections/linked_list.h"
+#include "threading/rwlock.h"
+#include "utils/debug.h"
+
+#define MAX_INCLUSION_LEVEL            10
+
+typedef struct private_settings_t private_settings_t;
+typedef struct section_t section_t;
+typedef struct kv_t kv_t;
+
+/**
+ * private data of settings
+ */
+struct private_settings_t {
+
+       /**
+        * public functions
+        */
+       settings_t public;
+
+       /**
+        * top level section
+        */
+       section_t *top;
+
+       /**
+        * contents of loaded files and in-memory settings (char*)
+        */
+       linked_list_t *contents;
+
+       /**
+        * lock to safely access the settings
+        */
+       rwlock_t *lock;
+};
+
+/**
+ * section containing subsections and key value pairs
+ */
+struct section_t {
+
+       /**
+        * name of the section
+        */
+       char *name;
+
+       /**
+        * fallback sections, as section_t
+        */
+       array_t *fallbacks;
+
+       /**
+        * subsections, as section_t
+        */
+       array_t *sections;
+
+       /**
+        * key value pairs, as kv_t
+        */
+       array_t *kv;
+};
+
+/**
+ * Key value pair
+ */
+struct kv_t {
+
+       /**
+        * key string, relative
+        */
+       char *key;
+
+       /**
+        * value as string
+        */
+       char *value;
+};
+
+/**
+ * create a key/value pair
+ */
+static kv_t *kv_create(char *key, char *value)
+{
+       kv_t *this;
+       INIT(this,
+               .key = strdup(key),
+               .value = value,
+       );
+       return this;
+}
+
+/**
+ * destroy a key/value pair
+ */
+static void kv_destroy(kv_t *this)
+{
+       free(this->key);
+       free(this);
+}
+
+/**
+ * create a section with the given name
+ */
+static section_t *section_create(char *name)
+{
+       section_t *this;
+       INIT(this,
+               .name = strdupnull(name),
+       );
+       return this;
+}
+
+/**
+ * destroy a section
+ */
+static void section_destroy(section_t *this)
+{
+       array_destroy_function(this->sections, (void*)section_destroy, NULL);
+       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
+       array_destroy(this->fallbacks);
+       free(this->name);
+       free(this);
+}
+
+/**
+ * Purge contents of a section, returns if section can be safely removed.
+ */
+static bool section_purge(section_t *this)
+{
+       section_t *current;
+       int i;
+
+       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
+       this->kv = NULL;
+       /* we ensure sections used as fallback, or configured with fallbacks (or
+        * having any such subsections) are not removed */
+       for (i = array_count(this->sections) - 1; i >= 0; i--)
+       {
+               array_get(this->sections, i, &current);
+               if (section_purge(current))
+               {
+                       array_remove(this->sections, i, NULL);
+                       section_destroy(current);
+               }
+       }
+       return !this->fallbacks && !array_count(this->sections);
+}
+
+/**
+ * callback to find a section by name
+ */
+static int section_find(const void *a, const void *b)
+{
+       const char *key = a;
+       const section_t *item = b;
+       return strcmp(key, item->name);
+}
+
+/**
+ * callback to sort sections by name
+ */
+static int section_sort(const void *a, const void *b, void *user)
+{
+       const section_t *sa = a, *sb = b;
+       return strcmp(sa->name, sb->name);
+}
+
+/**
+ * callback to find a kv pair by key
+ */
+static int kv_find(const void *a, const void *b)
+{
+       const char *key = a;
+       const kv_t *item = b;
+       return strcmp(key, item->key);
+}
+
+/**
+ * callback to sort kv pairs by key
+ */
+static int kv_sort(const void *a, const void *b, void *user)
+{
+       const kv_t *kva = a, *kvb = b;
+       return strcmp(kva->key, kvb->key);
+}
+
+/**
+ * Print a format key, but consume already processed arguments
+ */
+static bool print_key(char *buf, int len, char *start, char *key, va_list args)
+{
+       va_list copy;
+       char *pos = start;
+       bool res;
+
+       va_copy(copy, args);
+       while (TRUE)
+       {
+               pos = memchr(pos, '%', key - pos);
+               if (!pos)
+               {
+                       break;
+               }
+               pos++;
+               switch (*pos)
+               {
+                       case 'd':
+                               va_arg(copy, int);
+                               break;
+                       case 's':
+                               va_arg(copy, char*);
+                               break;
+                       case 'N':
+                               va_arg(copy, enum_name_t*);
+                               va_arg(copy, int);
+                               break;
+                       case '%':
+                               break;
+                       default:
+                               DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
+                               break;
+               }
+               pos++;
+       }
+       res = vsnprintf(buf, len, key, copy) < len;
+       va_end(copy);
+       return res;
+}
+
+/**
+ * Find a section by a given key, using buffered key, reusable buffer.
+ * If "ensure" is TRUE, the sections are created if they don't exist.
+ */
+static section_t *find_section_buffered(section_t *section,
+                                       char *start, char *key, va_list args, char *buf, int len,
+                                       bool ensure)
+{
+       char *pos;
+       section_t *found = NULL;
+
+       if (section == NULL)
+       {
+               return NULL;
+       }
+       pos = strchr(key, '.');
+       if (pos)
+       {
+               *pos = '\0';
+               pos++;
+       }
+       if (!print_key(buf, len, start, key, args))
+       {
+               return NULL;
+       }
+       if (!strlen(buf))
+       {
+               found = section;
+       }
+       else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
+       {
+               if (ensure)
+               {
+                       found = section_create(buf);
+                       array_insert_create(&section->sections, ARRAY_TAIL, found);
+                       array_sort(section->sections, section_sort, NULL);
+               }
+       }
+       if (found && pos)
+       {
+               return find_section_buffered(found, start, pos, args, buf, len, ensure);
+       }
+       return found;
+}
+
+/**
+ * Find all sections via a given key considering fallbacks, using buffered key,
+ * reusable buffer.
+ */
+static void find_sections_buffered(section_t *section, char *start, char *key,
+                                               va_list args, char *buf, int len, array_t **sections)
+{
+       section_t *found = NULL, *fallback;
+       char *pos;
+       int i;
+
+       if (!section)
+       {
+               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);
+               }
+       }
+}
+
+/**
+ * Ensure that the section with the given key exists (thread-safe).
+ */
+static section_t *ensure_section(private_settings_t *this, section_t *section,
+                                                                const char *key, va_list args)
+{
+       char buf[128], keybuf[512];
+       section_t *found;
+
+       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+       {
+               return NULL;
+       }
+       /* we might have to change the tree */
+       this->lock->write_lock(this->lock);
+       found = find_section_buffered(section, keybuf, keybuf, args, buf,
+                                                                 sizeof(buf), TRUE);
+       this->lock->unlock(this->lock);
+       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
+ */
+static bool fallback_exists(section_t *section, section_t *fallback)
+{
+       if (section == fallback)
+       {
+               return TRUE;
+       }
+       else if (section->fallbacks)
+       {
+               section_t *existing;
+               int i;
+
+               for (i = 0; i < array_count(section->fallbacks); i++)
+               {
+                       array_get(section->fallbacks, i, &existing);
+                       if (existing == fallback)
+                       {
+                               return TRUE;
+                       }
+               }
+       }
+       return FALSE;
+}
+
+/**
+ * Ensure that the section with the given key exists and add the given fallback
+ * section (thread-safe).
+ */
+static void add_fallback_to_section(private_settings_t *this,
+                                                       section_t *section, const char *key, va_list args,
+                                                       section_t *fallback)
+{
+       char buf[128], keybuf[512];
+       section_t *found;
+
+       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+       {
+               return;
+       }
+       this->lock->write_lock(this->lock);
+       found = find_section_buffered(section, keybuf, keybuf, args, buf,
+                                                                 sizeof(buf), TRUE);
+       if (!fallback_exists(found, fallback))
+       {
+               /* to ensure sections referred to as fallback are not purged, we create
+                * the array there too */
+               if (!fallback->fallbacks)
+               {
+                       fallback->fallbacks = array_create(0, 0);
+               }
+               array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback);
+       }
+       this->lock->unlock(this->lock);
+}
+
+/**
+ * Find the key/value pair for a key, using buffered key, reusable buffer
+ * If "ensure" is TRUE, the sections (and key/value pair) are created if they
+ * don't exist.
+ * Fallbacks are only considered if "ensure" is FALSE.
+ */
+static kv_t *find_value_buffered(section_t *section, char *start, char *key,
+                                                                va_list args, char *buf, int len, bool ensure)
+{
+       int i;
+       char *pos;
+       kv_t *kv = NULL;
+       section_t *found = NULL;
+
+       if (section == NULL)
+       {
+               return NULL;
+       }
+
+       pos = strchr(key, '.');
+       if (pos)
+       {
+               *pos = '\0';
+               if (!print_key(buf, len, start, key, args))
+               {
+                       return NULL;
+               }
+               /* restore so we can retry for fallbacks */
+               *pos = '.';
+               if (!strlen(buf))
+               {
+                       found = section;
+               }
+               else if (array_bsearch(section->sections, buf, section_find,
+                                                          &found) == -1)
+               {
+                       if (ensure)
+                       {
+                               found = section_create(buf);
+                               array_insert_create(&section->sections, ARRAY_TAIL, found);
+                               array_sort(section->sections, section_sort, NULL);
+                       }
+               }
+               if (found)
+               {
+                       kv = find_value_buffered(found, start, pos+1, args, buf, len,
+                                                                        ensure);
+               }
+               if (!kv && !ensure && section->fallbacks)
+               {
+                       for (i = 0; !kv && i < array_count(section->fallbacks); i++)
+                       {
+                               array_get(section->fallbacks, i, &found);
+                               kv = find_value_buffered(found, start, key, args, buf, len,
+                                                                                ensure);
+                       }
+               }
+       }
+       else
+       {
+               if (!print_key(buf, len, start, key, args))
+               {
+                       return NULL;
+               }
+               if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
+               {
+                       if (ensure)
+                       {
+                               kv = kv_create(buf, NULL);
+                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
+                               array_sort(section->kv, kv_sort, NULL);
+                       }
+                       else if (section->fallbacks)
+                       {
+                               for (i = 0; !kv && i < array_count(section->fallbacks); i++)
+                               {
+                                       array_get(section->fallbacks, i, &found);
+                                       kv = find_value_buffered(found, start, key, args, buf, len,
+                                                                                        ensure);
+                               }
+                       }
+               }
+       }
+       return kv;
+}
+
+/**
+ * Find the string value for a key (thread-safe).
+ */
+static char *find_value(private_settings_t *this, section_t *section,
+                                               char *key, va_list args)
+{
+       char buf[128], keybuf[512], *value = NULL;
+       kv_t *kv;
+
+       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+       {
+               return NULL;
+       }
+       this->lock->read_lock(this->lock);
+       kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
+                                                        FALSE);
+       if (kv)
+       {
+               value = kv->value;
+       }
+       this->lock->unlock(this->lock);
+       return value;
+}
+
+/**
+ * Set a value to a copy of the given string (thread-safe).
+ */
+static void set_value(private_settings_t *this, section_t *section,
+                                         char *key, va_list args, char *value)
+{
+       char buf[128], keybuf[512];
+       kv_t *kv;
+
+       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+       {
+               return;
+       }
+       this->lock->write_lock(this->lock);
+       kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
+                                                        TRUE);
+       if (kv)
+       {
+               if (!value)
+               {
+                       kv->value = NULL;
+               }
+               else if (kv->value && (strlen(value) <= strlen(kv->value)))
+               {       /* overwrite in-place, if possible */
+                       strcpy(kv->value, value);
+               }
+               else
+               {       /* otherwise clone the string and store it in the cache */
+                       kv->value = strdup(value);
+                       this->contents->insert_last(this->contents, kv->value);
+               }
+       }
+       this->lock->unlock(this->lock);
+}
+
+METHOD(settings_t, get_str, char*,
+       private_settings_t *this, char *key, char *def, ...)
+{
+       char *value;
+       va_list args;
+
+       va_start(args, def);
+       value = find_value(this, this->top, key, args);
+       va_end(args);
+       if (value)
+       {
+               return value;
+       }
+       return def;
+}
+
+/**
+ * Described in header
+ */
+inline bool settings_value_as_bool(char *value, bool def)
+{
+       if (value)
+       {
+               if (strcaseeq(value, "1") ||
+                       strcaseeq(value, "yes") ||
+                       strcaseeq(value, "true") ||
+                       strcaseeq(value, "enabled"))
+               {
+                       return TRUE;
+               }
+               else if (strcaseeq(value, "0") ||
+                                strcaseeq(value, "no") ||
+                                strcaseeq(value, "false") ||
+                                strcaseeq(value, "disabled"))
+               {
+                       return FALSE;
+               }
+       }
+       return def;
+}
+
+METHOD(settings_t, get_bool, bool,
+       private_settings_t *this, char *key, bool def, ...)
+{
+       char *value;
+       va_list args;
+
+       va_start(args, def);
+       value = find_value(this, this->top, key, args);
+       va_end(args);
+       return settings_value_as_bool(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline int settings_value_as_int(char *value, int def)
+{
+       int intval;
+       if (value)
+       {
+               errno = 0;
+               intval = strtol(value, NULL, 10);
+               if (errno == 0)
+               {
+                       return intval;
+               }
+       }
+       return def;
+}
+
+METHOD(settings_t, get_int, int,
+       private_settings_t *this, char *key, int def, ...)
+{
+       char *value;
+       va_list args;
+
+       va_start(args, def);
+       value = find_value(this, this->top, key, args);
+       va_end(args);
+       return settings_value_as_int(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline double settings_value_as_double(char *value, double def)
+{
+       double dval;
+       if (value)
+       {
+               errno = 0;
+               dval = strtod(value, NULL);
+               if (errno == 0)
+               {
+                       return dval;
+               }
+       }
+       return def;
+}
+
+METHOD(settings_t, get_double, double,
+       private_settings_t *this, char *key, double def, ...)
+{
+       char *value;
+       va_list args;
+
+       va_start(args, def);
+       value = find_value(this, this->top, key, args);
+       va_end(args);
+       return settings_value_as_double(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
+{
+       char *endptr;
+       u_int32_t timeval;
+       if (value)
+       {
+               errno = 0;
+               timeval = strtoul(value, &endptr, 10);
+               if (errno == 0)
+               {
+                       switch (*endptr)
+                       {
+                               case 'd':               /* time in days */
+                                       timeval *= 24 * 3600;
+                                       break;
+                               case 'h':               /* time in hours */
+                                       timeval *= 3600;
+                                       break;
+                               case 'm':               /* time in minutes */
+                                       timeval *= 60;
+                                       break;
+                               case 's':               /* time in seconds */
+                               default:
+                                       break;
+                       }
+                       return timeval;
+               }
+       }
+       return def;
+}
+
+METHOD(settings_t, get_time, u_int32_t,
+       private_settings_t *this, char *key, u_int32_t def, ...)
+{
+       char *value;
+       va_list args;
+
+       va_start(args, def);
+       value = find_value(this, this->top, key, args);
+       va_end(args);
+       return settings_value_as_time(value, def);
+}
+
+METHOD(settings_t, set_str, void,
+       private_settings_t *this, char *key, char *value, ...)
+{
+       va_list args;
+       va_start(args, value);
+       set_value(this, this->top, key, args, value);
+       va_end(args);
+}
+
+METHOD(settings_t, set_bool, void,
+       private_settings_t *this, char *key, bool value, ...)
+{
+       va_list args;
+       va_start(args, value);
+       set_value(this, this->top, key, args, value ? "1" : "0");
+       va_end(args);
+}
+
+METHOD(settings_t, set_int, void,
+       private_settings_t *this, char *key, int value, ...)
+{
+       char val[16];
+       va_list args;
+       va_start(args, value);
+       if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
+       {
+               set_value(this, this->top, key, args, val);
+       }
+       va_end(args);
+}
+
+METHOD(settings_t, set_double, void,
+       private_settings_t *this, char *key, double value, ...)
+{
+       char val[64];
+       va_list args;
+       va_start(args, value);
+       if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
+       {
+               set_value(this, this->top, key, args, val);
+       }
+       va_end(args);
+}
+
+METHOD(settings_t, set_time, void,
+       private_settings_t *this, char *key, u_int32_t value, ...)
+{
+       char val[16];
+       va_list args;
+       va_start(args, value);
+       if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
+       {
+               set_value(this, this->top, key, args, val);
+       }
+       va_end(args);
+}
+
+METHOD(settings_t, set_default_str, bool,
+       private_settings_t *this, char *key, char *value, ...)
+{
+       char *old;
+       va_list args;
+
+       va_start(args, value);
+       old = find_value(this, this->top, key, args);
+       va_end(args);
+
+       if (!old)
+       {
+               va_start(args, value);
+               set_value(this, this->top, key, args, value);
+               va_end(args);
+               return TRUE;
+       }
+       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(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, ...)
+{
+       enumerator_data_t *data;
+       array_t *sections;
+       va_list args;
+
+       this->lock->read_lock(this->lock);
+       va_start(args, key);
+       sections = find_sections(this, this->top, key, args);
+       va_end(args);
+
+       if (!sections)
+       {
+               this->lock->unlock(this->lock);
+               return enumerator_create_empty();
+       }
+       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(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, ...)
+{
+       enumerator_data_t *data;
+       array_t *sections;
+       va_list args;
+
+       this->lock->read_lock(this->lock);
+       va_start(args, key);
+       sections = find_sections(this, this->top, key, args);
+       va_end(args);
+
+       if (!sections)
+       {
+               this->lock->unlock(this->lock);
+               return enumerator_create_empty();
+       }
+       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,
+       private_settings_t *this, const char *key, const char *fallback, ...)
+{
+       section_t *section;
+       va_list args;
+
+       /* find/create the fallback */
+       va_start(args, fallback);
+       section = ensure_section(this, this->top, fallback, args);
+       va_end(args);
+
+       va_start(args, fallback);
+       add_fallback_to_section(this, this->top, key, args, section);
+       va_end(args);
+}
+
+/**
+ * parse text, truncate "skip" chars, delimited by term respecting brackets.
+ *
+ * Chars in "skip" are truncated at the beginning and the end of the resulting
+ * token. "term" contains a list of characters to read up to (first match),
+ * while "br" contains bracket counterparts found in "term" to skip.
+ */
+static char parse(char **text, char *skip, char *term, char *br, char **token)
+{
+       char *best = NULL;
+       char best_term = '\0';
+
+       /* skip leading chars */
+       while (strchr(skip, **text))
+       {
+               (*text)++;
+               if (!**text)
+               {
+                       return 0;
+               }
+       }
+       /* mark begin of subtext */
+       *token = *text;
+       while (*term)
+       {
+               char *pos = *text;
+               int level = 1;
+
+               /* find terminator */
+               while (*pos)
+               {
+                       if (*pos == *term)
+                       {
+                               level--;
+                       }
+                       else if (br && *pos == *br)
+                       {
+                               level++;
+                       }
+                       if (level == 0)
+                       {
+                               if (best == NULL || best > pos)
+                               {
+                                       best = pos;
+                                       best_term = *term;
+                               }
+                               break;
+                       }
+                       pos++;
+               }
+               /* try next terminator */
+               term++;
+               if (br)
+               {
+                       br++;
+               }
+       }
+       if (best)
+       {
+               /* update input */
+               *text = best;
+               /* null trailing bytes */
+               do
+               {
+                       *best = '\0';
+                       best--;
+               }
+               while (best >= *token && strchr(skip, *best));
+               /* return found terminator */
+               return best_term;
+       }
+       return 0;
+}
+
+/**
+ * Check if "text" starts with "pattern".
+ * Characters in "skip" are skipped first. If found, TRUE is returned and "text"
+ * is modified to point to the character right after "pattern".
+ */
+static bool starts_with(char **text, char *skip, char *pattern)
+{
+       char *pos = *text;
+       int len = strlen(pattern);
+       while (strchr(skip, *pos))
+       {
+               pos++;
+               if (!*pos)
+               {
+                       return FALSE;
+               }
+       }
+       if (strlen(pos) < len || !strneq(pos, pattern, len))
+       {
+               return FALSE;
+       }
+       *text = pos + len;
+       return TRUE;
+}
+
+/**
+ * Check if what follows in "text" is an include statement.
+ * If this function returns TRUE, "text" will point to the character right after
+ * the include pattern, which is returned in "pattern".
+ */
+static bool parse_include(char **text, char **pattern)
+{
+       char *pos = *text;
+       if (!starts_with(&pos, "\n\t ", "include"))
+       {
+               return FALSE;
+       }
+       if (starts_with(&pos, "\t ", "="))
+       {       /* ignore "include = value" */
+               return FALSE;
+       }
+       *text = pos;
+       return parse(text, "\t ", "\n", NULL, pattern) != 0;
+}
+
+/**
+ * Forward declaration.
+ */
+static bool parse_files(linked_list_t *contents, char *file, int level,
+                                               char *pattern, section_t *section);
+
+/**
+ * Parse a section
+ */
+static bool parse_section(linked_list_t *contents, char *file, int level,
+                                                 char **text, section_t *section)
+{
+       bool finished = FALSE;
+       char *key, *value, *inner;
+
+       while (!finished)
+       {
+               if (parse_include(text, &value))
+               {
+                       if (!parse_files(contents, file, level, value, section))
+                       {
+                               DBG1(DBG_LIB, "failed to include '%s'", value);
+                               return FALSE;
+                       }
+                       continue;
+               }
+               switch (parse(text, "\t\n ", "{=#", NULL, &key))
+               {
+                       case '{':
+                               if (parse(text, "\t ", "}", "{", &inner))
+                               {
+                                       section_t *sub;
+                                       if (!strlen(key))
+                                       {
+                                               DBG1(DBG_LIB, "skipping section without name in '%s'",
+                                                        section->name);
+                                               continue;
+                                       }
+                                       if (array_bsearch(section->sections, key, section_find,
+                                                                         &sub) == -1)
+                                       {
+                                               sub = section_create(key);
+                                               if (parse_section(contents, file, level, &inner, sub))
+                                               {
+                                                       array_insert_create(&section->sections, ARRAY_TAIL,
+                                                                                               sub);
+                                                       array_sort(section->sections, section_sort, NULL);
+                                                       continue;
+                                               }
+                                               section_destroy(sub);
+                                       }
+                                       else
+                                       {       /* extend the existing section */
+                                               if (parse_section(contents, file, level, &inner, sub))
+                                               {
+                                                       continue;
+                                               }
+                                       }
+                                       DBG1(DBG_LIB, "parsing subsection '%s' failed", key);
+                                       break;
+                               }
+                               DBG1(DBG_LIB, "matching '}' not found near %s", *text);
+                               break;
+                       case '=':
+                               if (parse(text, "\t ", "\n", NULL, &value))
+                               {
+                                       kv_t *kv;
+                                       if (!strlen(key))
+                                       {
+                                               DBG1(DBG_LIB, "skipping value without key in '%s'",
+                                                        section->name);
+                                               continue;
+                                       }
+                                       if (array_bsearch(section->kv, key, kv_find, &kv) == -1)
+                                       {
+                                               kv = kv_create(key, value);
+                                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
+                                               array_sort(section->kv, kv_sort, NULL);
+                                       }
+                                       else
+                                       {       /* replace with the most recently read value */
+                                               kv->value = value;
+                                       }
+                                       continue;
+                               }
+                               DBG1(DBG_LIB, "parsing value failed near %s", *text);
+                               break;
+                       case '#':
+                               parse(text, "", "\n", NULL, &value);
+                               continue;
+                       default:
+                               finished = TRUE;
+                               continue;
+               }
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Parse a file and add the settings to the given section.
+ */
+static bool parse_file(linked_list_t *contents, char *file, int level,
+                                          section_t *section)
+{
+       bool success;
+       char *text, *pos;
+       struct stat st;
+       FILE *fd;
+       int len;
+
+       DBG2(DBG_LIB, "loading config file '%s'", file);
+       if (stat(file, &st) == -1)
+       {
+               if (errno == ENOENT)
+               {
+#ifdef STRONGSWAN_CONF
+                       if (streq(file, STRONGSWAN_CONF))
+                       {
+                               DBG2(DBG_LIB, "'%s' does not exist, ignored", file);
+                       }
+                       else
+#endif
+                       {
+                               DBG1(DBG_LIB, "'%s' does not exist, ignored", file);
+                       }
+                       return TRUE;
+               }
+               DBG1(DBG_LIB, "failed to stat '%s': %s", file, strerror(errno));
+               return FALSE;
+       }
+       else if (!S_ISREG(st.st_mode))
+       {
+               DBG1(DBG_LIB, "'%s' is not a regular file", file);
+               return FALSE;
+       }
+       fd = fopen(file, "r");
+       if (fd == NULL)
+       {
+               DBG1(DBG_LIB, "'%s' is not readable", file);
+               return FALSE;
+       }
+       fseek(fd, 0, SEEK_END);
+       len = ftell(fd);
+       rewind(fd);
+       text = malloc(len + 2);
+       text[len] = text[len + 1] = '\0';
+       if (fread(text, 1, len, fd) != len)
+       {
+               free(text);
+               fclose(fd);
+               return FALSE;
+       }
+       fclose(fd);
+
+       pos = text;
+       success = parse_section(contents, file, level, &pos, section);
+       if (!success)
+       {
+               free(text);
+       }
+       else
+       {
+               contents->insert_last(contents, text);
+       }
+       return success;
+}
+
+/**
+ * Load the files matching "pattern", which is resolved with glob(3), if
+ * available.
+ * If the pattern is relative, the directory of "file" is used as base.
+ */
+static bool parse_files(linked_list_t *contents, char *file, int level,
+                                               char *pattern, section_t *section)
+{
+       bool success = TRUE;
+       char pat[PATH_MAX];
+
+       if (level > MAX_INCLUSION_LEVEL)
+       {
+               DBG1(DBG_LIB, "maximum level of %d includes reached, ignored",
+                        MAX_INCLUSION_LEVEL);
+               return TRUE;
+       }
+
+       if (!strlen(pattern))
+       {
+               DBG1(DBG_LIB, "empty include pattern, ignored");
+               return TRUE;
+       }
+
+       if (!file || pattern[0] == '/')
+       {       /* absolute path */
+               if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
+               {
+                       DBG1(DBG_LIB, "include pattern too long, ignored");
+                       return TRUE;
+               }
+       }
+       else
+       {       /* base relative paths to the directory of the current file */
+               char *dir = path_dirname(file);
+               if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat))
+               {
+                       DBG1(DBG_LIB, "include pattern too long, ignored");
+                       free(dir);
+                       return TRUE;
+               }
+               free(dir);
+       }
+#ifdef HAVE_GLOB_H
+       {
+               int status;
+               glob_t buf;
+
+               status = glob(pat, GLOB_ERR, NULL, &buf);
+               if (status == GLOB_NOMATCH)
+               {
+                       DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
+               }
+               else if (status != 0)
+               {
+                       DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
+                       success = FALSE;
+               }
+               else
+               {
+                       char **expanded;
+                       for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
+                       {
+                               success &= parse_file(contents, *expanded, level + 1, section);
+                               if (!success)
+                               {
+                                       break;
+                               }
+                       }
+               }
+               globfree(&buf);
+       }
+#else /* HAVE_GLOB_H */
+       /* if glob(3) is not available, try to load pattern directly */
+       success = parse_file(contents, pat, level + 1, section);
+#endif /* HAVE_GLOB_H */
+       return success;
+}
+
+/**
+ * Recursivly extends "base" with "extension".
+ */
+static void section_extend(section_t *base, section_t *extension)
+{
+       enumerator_t *enumerator;
+       section_t *sec;
+       kv_t *kv;
+
+       enumerator = array_create_enumerator(extension->sections);
+       while (enumerator->enumerate(enumerator, (void**)&sec))
+       {
+               section_t *found;
+               if (array_bsearch(base->sections, sec->name, section_find,
+                       &found) != -1)
+               {
+                       section_extend(found, sec);
+               }
+               else
+               {
+                       array_remove_at(extension->sections, enumerator);
+                       array_insert_create(&base->sections, ARRAY_TAIL, sec);
+                       array_sort(base->sections, section_sort, NULL);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = array_create_enumerator(extension->kv);
+       while (enumerator->enumerate(enumerator, (void**)&kv))
+       {
+               kv_t *found;
+               if (array_bsearch(base->kv, kv->key, kv_find, &found) != -1)
+               {
+                       found->value = kv->value;
+               }
+               else
+               {
+                       array_remove_at(extension->kv, enumerator);
+                       array_insert_create(&base->kv, ARRAY_TAIL, kv);
+                       array_sort(base->kv, kv_sort, NULL);
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * Load settings from files matching the given file pattern.
+ * All sections and values are added relative to "parent".
+ * All files (even included ones) have to be loaded successfully.
+ */
+static bool load_files_internal(private_settings_t *this, section_t *parent,
+                                                               char *pattern, bool merge)
+{
+       char *text;
+       linked_list_t *contents;
+       section_t *section;
+
+       if (pattern == NULL)
+       {
+#ifdef STRONGSWAN_CONF
+               pattern = STRONGSWAN_CONF;
+#else
+               return FALSE;
+#endif
+       }
+
+       contents = linked_list_create();
+       section = section_create(NULL);
+
+       if (!parse_files(contents, NULL, 0, pattern, section))
+       {
+               contents->destroy_function(contents, (void*)free);
+               section_destroy(section);
+               return FALSE;
+       }
+
+       this->lock->write_lock(this->lock);
+       if (!merge)
+       {
+               section_purge(parent);
+       }
+       /* extend parent section */
+       section_extend(parent, section);
+       /* move contents of loaded files to main store */
+       while (contents->remove_first(contents, (void**)&text) == SUCCESS)
+       {
+               this->contents->insert_last(this->contents, text);
+       }
+       this->lock->unlock(this->lock);
+
+       section_destroy(section);
+       contents->destroy(contents);
+       return TRUE;
+}
+
+METHOD(settings_t, load_files, bool,
+       private_settings_t *this, char *pattern, bool merge)
+{
+       return load_files_internal(this, this->top, pattern, merge);
+}
+
+METHOD(settings_t, load_files_section, bool,
+       private_settings_t *this, char *pattern, bool merge, char *key, ...)
+{
+       section_t *section;
+       va_list args;
+
+       va_start(args, key);
+       section = ensure_section(this, this->top, key, args);
+       va_end(args);
+
+       if (!section)
+       {
+               return FALSE;
+       }
+       return load_files_internal(this, section, pattern, merge);
+}
+
+METHOD(settings_t, destroy, void,
+       private_settings_t *this)
+{
+       section_destroy(this->top);
+       this->contents->destroy_function(this->contents, (void*)free);
+       this->lock->destroy(this->lock);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+settings_t *settings_create(char *file)
+{
+       private_settings_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_str = _get_str,
+                       .get_int = _get_int,
+                       .get_double = _get_double,
+                       .get_time = _get_time,
+                       .get_bool = _get_bool,
+                       .set_str = _set_str,
+                       .set_int = _set_int,
+                       .set_double = _set_double,
+                       .set_time = _set_time,
+                       .set_bool = _set_bool,
+                       .set_default_str = _set_default_str,
+                       .create_section_enumerator = _create_section_enumerator,
+                       .create_key_value_enumerator = _create_key_value_enumerator,
+                       .add_fallback = _add_fallback,
+                       .load_files = _load_files,
+                       .load_files_section = _load_files_section,
+                       .destroy = _destroy,
+               },
+               .top = section_create(NULL),
+               .contents = linked_list_create(),
+               .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+       );
+
+       load_files(this, file, FALSE);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/settings/settings.h b/src/libstrongswan/settings/settings.h
new file mode 100644 (file)
index 0000000..23f3a8e
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2010 Tobias Brunner
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup settings settings
+ * @ingroup libstrongswan
+ *
+ * @defgroup settings_t settings
+ * @{ @ingroup settings
+ */
+
+#ifndef SETTINGS_H_
+#define SETTINGS_H_
+
+typedef struct settings_t settings_t;
+
+#include "utils/utils.h"
+#include "collections/enumerator.h"
+
+/**
+ * Convert a string value returned by a key/value enumerator to a boolean.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_bool()
+ * @param value                        the string value
+ * @param def                  the default value, if value is NULL or invalid
+ */
+bool settings_value_as_bool(char *value, bool def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to an integer.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_int()
+ * @param value                        the string value
+ * @param def                  the default value, if value is NULL or invalid
+ */
+int settings_value_as_int(char *value, int def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to a double.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_double()
+ * @param value                        the string value
+ * @param def                  the default value, if value is NULL or invalid
+ */
+double settings_value_as_double(char *value, double def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to a time value.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_time()
+ * @param value                        the string value
+ * @param def                  the default value, if value is NULL or invalid
+ */
+u_int32_t settings_value_as_time(char *value, u_int32_t def);
+
+/**
+ * Generic configuration options read from a config file.
+ *
+ * The syntax is quite simple:
+ * @code
+ * settings := (section|keyvalue)*
+ * section  := name { settings }
+ * keyvalue := key = value\n
+ * @endcode
+ * E.g.:
+ * @code
+       a = b
+       section-one {
+               somevalue = asdf
+               subsection {
+                       othervalue = xxx
+               }
+               yetanother = zz
+       }
+       section-two {
+       }
+       @endcode
+ *
+ * The values are accessed using the get() functions using dotted keys, e.g.
+ *   section-one.subsection.othervalue
+ *
+ * Currently only a limited set of printf format specifiers are supported
+ * (namely %s, %d and %N, see implementation for details).
+ *
+ * \section includes Including other files
+ * Other files can be included, using the include statement e.g.
+ * @code
+ *   include /somepath/subconfig.conf
+ * @endcode
+ * Shell patterns like *.conf are possible.
+ *
+ * If the path is relative, the directory of the file containing the include
+ * statement is used as base.
+ *
+ * Sections loaded from included files extend previously loaded sections,
+ * already existing values are replaced.
+ *
+ * All settings included from files are added relative to the section the
+ * include statement is in.
+ *
+ * The following files result in the same final config as above:
+ *
+ * @code
+       a = b
+       section-one {
+               somevalue = before include
+               include include.conf
+       }
+       include two.conf
+       @endcode
+ * include.conf
+ * @code
+       somevalue = asdf
+       subsection {
+               othervalue = yyy
+       }
+       yetanother = zz
+       @endcode
+ * two.conf
+ * @code
+       section-one {
+               subsection {
+                       othervalue = xxx
+               }
+       }
+       section-two {
+       }
+       @endcode
+ */
+struct settings_t {
+
+       /**
+        * Get a settings value as a string.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value returned if key not found
+        * @param ...           argument list for key
+        * @return                      value pointing to internal string
+        */
+       char* (*get_str)(settings_t *this, char *key, char *def, ...);
+
+       /**
+        * Get a boolean yes|no, true|false value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value returned if key not found
+        * @param ...           argument list for key
+        * @return                      value of the key
+        */
+       bool (*get_bool)(settings_t *this, char *key, bool def, ...);
+
+       /**
+        * Get an integer value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value returned if key not found
+        * @param ...           argument list for key
+        * @return                      value of the key
+        */
+       int (*get_int)(settings_t *this, char *key, int def, ...);
+
+       /**
+        * Get an double value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value returned if key not found
+        * @param ...           argument list for key
+        * @return                      value of the key
+        */
+       double (*get_double)(settings_t *this, char *key, double def, ...);
+
+       /**
+        * Get a time value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value returned if key not found
+        * @param ...           argument list for key
+        * @return                      value of the key (in seconds)
+        */
+       u_int32_t (*get_time)(settings_t *this, char *key, u_int32_t def, ...);
+
+       /**
+        * Set a string value.
+        *
+        * @param key           key including sections, printf style format
+        * @param value         value to set (gets cloned)
+        * @param ...           argument list for key
+        */
+       void (*set_str)(settings_t *this, char *key, char *value, ...);
+
+       /**
+        * Set a boolean value.
+        *
+        * @param key           key including sections, printf style format
+        * @param value         value to set
+        * @param ...           argument list for key
+        */
+       void (*set_bool)(settings_t *this, char *key, bool value, ...);
+
+       /**
+        * Set an integer value.
+        *
+        * @param key           key including sections, printf style format
+        * @param value         value to set
+        * @param ...           argument list for key
+        */
+       void (*set_int)(settings_t *this, char *key, int value, ...);
+
+       /**
+        * Set an double value.
+        *
+        * @param key           key including sections, printf style format
+        * @param value         value to set
+        * @param ...           argument list for key
+        */
+       void (*set_double)(settings_t *this, char *key, double value, ...);
+
+       /**
+        * Set a time value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value to set
+        * @param ...           argument list for key
+        */
+       void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
+
+       /**
+        * Set a default for string value.
+        *
+        * @param key           key including sections, printf style format
+        * @param def           value to set if unconfigured
+        * @param ...           argument list for key
+        * @return                      TRUE if a new default value for key has been set
+        */
+       bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
+
+       /**
+        * Create an enumerator over subsection names of a section.
+        *
+        * @param section       section including parents, printf style format
+        * @param ...           argument list for key
+        * @return                      enumerator over subsection names
+        */
+       enumerator_t* (*create_section_enumerator)(settings_t *this,
+                                                                                          char *section, ...);
+
+       /**
+        * Create an enumerator over key/value pairs in a section.
+        *
+        * @param section       section name to list key/value pairs of, printf style
+        * @param ...           argument list for section
+        * @return                      enumerator over (char *key, char *value)
+        */
+       enumerator_t* (*create_key_value_enumerator)(settings_t *this,
+                                                                                                char *section, ...);
+
+       /**
+        * Add a fallback for the given section.
+        *
+        * Example: When the fallback 'section-two' is configured for
+        * 'section-one.two' any failed lookup for a section or key in
+        * 'section-one.two' will result in a lookup for the same section/key
+        * in 'section-two'.
+        *
+        * @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.
+        *
+        * @param section       section for which a fallback is configured, printf style
+        * @param fallback      fallback section, printf style
+        * @param ...           argument list for section and fallback
+        */
+       void (*add_fallback)(settings_t *this, const char *section,
+                                                const char *fallback, ...);
+
+       /**
+        * Load settings from the files matching the given pattern.
+        *
+        * If merge is TRUE, existing sections are extended, existing values
+        * replaced, by those found in the loaded files. If it is FALSE, existing
+        * sections are purged before reading the new config.
+        *
+        * @note If any of the files matching the pattern fails to load, no settings
+        * are added at all. So, it's all or nothing.
+        *
+        * @param pattern       file pattern
+        * @param merge         TRUE to merge config with existing values
+        * @return                      TRUE, if settings were loaded successfully
+        */
+       bool (*load_files)(settings_t *this, char *pattern, bool merge);
+
+       /**
+        * Load settings from the files matching the given pattern.
+        *
+        * If merge is TRUE, existing sections are extended, existing values
+        * replaced, by those found in the loaded files. If it is FALSE, existing
+        * sections are purged before reading the new config.
+        *
+        * All settings are loaded relative to the given section. The section is
+        * created, if it does not yet exist.
+        *
+        * @note If any of the files matching the pattern fails to load, no settings
+        * are added at all. So, it's all or nothing.
+        *
+        * @param pattern       file pattern
+        * @param merge         TRUE to merge config with existing values
+        * @param section       section name of parent section, printf style
+        * @param ...           argument list for section
+        * @return                      TRUE, if settings were loaded successfully
+        */
+       bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
+                                                          char *section, ...);
+
+       /**
+        * Destroy a settings instance.
+        */
+       void (*destroy)(settings_t *this);
+};
+
+/**
+ * Load settings from a file.
+ *
+ * @param file                 file to read settings from, NULL for default
+ * @return                             settings object
+ */
+settings_t *settings_create(char *file);
+
+#endif /** SETTINGS_H_ @}*/
index 0964651..8a08e36 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <unistd.h>
 
-#include <utils/settings.h>
+#include <settings/settings.h>
 #include <utils/chunk.h>
 #include <utils/utils.h>
 #include <collections/linked_list.h>
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
deleted file mode 100644 (file)
index cf34fd1..0000000
+++ /dev/null
@@ -1,1520 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif /* HAVE_GLOB_H */
-
-#include "settings.h"
-
-#include "collections/array.h"
-#include "collections/hashtable.h"
-#include "collections/linked_list.h"
-#include "threading/rwlock.h"
-#include "utils/debug.h"
-
-#define MAX_INCLUSION_LEVEL            10
-
-typedef struct private_settings_t private_settings_t;
-typedef struct section_t section_t;
-typedef struct kv_t kv_t;
-
-/**
- * private data of settings
- */
-struct private_settings_t {
-
-       /**
-        * public functions
-        */
-       settings_t public;
-
-       /**
-        * top level section
-        */
-       section_t *top;
-
-       /**
-        * contents of loaded files and in-memory settings (char*)
-        */
-       linked_list_t *contents;
-
-       /**
-        * lock to safely access the settings
-        */
-       rwlock_t *lock;
-};
-
-/**
- * section containing subsections and key value pairs
- */
-struct section_t {
-
-       /**
-        * name of the section
-        */
-       char *name;
-
-       /**
-        * fallback sections, as section_t
-        */
-       array_t *fallbacks;
-
-       /**
-        * subsections, as section_t
-        */
-       array_t *sections;
-
-       /**
-        * key value pairs, as kv_t
-        */
-       array_t *kv;
-};
-
-/**
- * Key value pair
- */
-struct kv_t {
-
-       /**
-        * key string, relative
-        */
-       char *key;
-
-       /**
-        * value as string
-        */
-       char *value;
-};
-
-/**
- * create a key/value pair
- */
-static kv_t *kv_create(char *key, char *value)
-{
-       kv_t *this;
-       INIT(this,
-               .key = strdup(key),
-               .value = value,
-       );
-       return this;
-}
-
-/**
- * destroy a key/value pair
- */
-static void kv_destroy(kv_t *this)
-{
-       free(this->key);
-       free(this);
-}
-
-/**
- * create a section with the given name
- */
-static section_t *section_create(char *name)
-{
-       section_t *this;
-       INIT(this,
-               .name = strdupnull(name),
-       );
-       return this;
-}
-
-/**
- * destroy a section
- */
-static void section_destroy(section_t *this)
-{
-       array_destroy_function(this->sections, (void*)section_destroy, NULL);
-       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
-       array_destroy(this->fallbacks);
-       free(this->name);
-       free(this);
-}
-
-/**
- * Purge contents of a section, returns if section can be safely removed.
- */
-static bool section_purge(section_t *this)
-{
-       section_t *current;
-       int i;
-
-       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
-       this->kv = NULL;
-       /* we ensure sections used as fallback, or configured with fallbacks (or
-        * having any such subsections) are not removed */
-       for (i = array_count(this->sections) - 1; i >= 0; i--)
-       {
-               array_get(this->sections, i, &current);
-               if (section_purge(current))
-               {
-                       array_remove(this->sections, i, NULL);
-                       section_destroy(current);
-               }
-       }
-       return !this->fallbacks && !array_count(this->sections);
-}
-
-/**
- * callback to find a section by name
- */
-static int section_find(const void *a, const void *b)
-{
-       const char *key = a;
-       const section_t *item = b;
-       return strcmp(key, item->name);
-}
-
-/**
- * callback to sort sections by name
- */
-static int section_sort(const void *a, const void *b, void *user)
-{
-       const section_t *sa = a, *sb = b;
-       return strcmp(sa->name, sb->name);
-}
-
-/**
- * callback to find a kv pair by key
- */
-static int kv_find(const void *a, const void *b)
-{
-       const char *key = a;
-       const kv_t *item = b;
-       return strcmp(key, item->key);
-}
-
-/**
- * callback to sort kv pairs by key
- */
-static int kv_sort(const void *a, const void *b, void *user)
-{
-       const kv_t *kva = a, *kvb = b;
-       return strcmp(kva->key, kvb->key);
-}
-
-/**
- * Print a format key, but consume already processed arguments
- */
-static bool print_key(char *buf, int len, char *start, char *key, va_list args)
-{
-       va_list copy;
-       char *pos = start;
-       bool res;
-
-       va_copy(copy, args);
-       while (TRUE)
-       {
-               pos = memchr(pos, '%', key - pos);
-               if (!pos)
-               {
-                       break;
-               }
-               pos++;
-               switch (*pos)
-               {
-                       case 'd':
-                               va_arg(copy, int);
-                               break;
-                       case 's':
-                               va_arg(copy, char*);
-                               break;
-                       case 'N':
-                               va_arg(copy, enum_name_t*);
-                               va_arg(copy, int);
-                               break;
-                       case '%':
-                               break;
-                       default:
-                               DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
-                               break;
-               }
-               pos++;
-       }
-       res = vsnprintf(buf, len, key, copy) < len;
-       va_end(copy);
-       return res;
-}
-
-/**
- * Find a section by a given key, using buffered key, reusable buffer.
- * If "ensure" is TRUE, the sections are created if they don't exist.
- */
-static section_t *find_section_buffered(section_t *section,
-                                       char *start, char *key, va_list args, char *buf, int len,
-                                       bool ensure)
-{
-       char *pos;
-       section_t *found = NULL;
-
-       if (section == NULL)
-       {
-               return NULL;
-       }
-       pos = strchr(key, '.');
-       if (pos)
-       {
-               *pos = '\0';
-               pos++;
-       }
-       if (!print_key(buf, len, start, key, args))
-       {
-               return NULL;
-       }
-       if (!strlen(buf))
-       {
-               found = section;
-       }
-       else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
-       {
-               if (ensure)
-               {
-                       found = section_create(buf);
-                       array_insert_create(&section->sections, ARRAY_TAIL, found);
-                       array_sort(section->sections, section_sort, NULL);
-               }
-       }
-       if (found && pos)
-       {
-               return find_section_buffered(found, start, pos, args, buf, len, ensure);
-       }
-       return found;
-}
-
-/**
- * Find all sections via a given key considering fallbacks, using buffered key,
- * reusable buffer.
- */
-static void find_sections_buffered(section_t *section, char *start, char *key,
-                                               va_list args, char *buf, int len, array_t **sections)
-{
-       section_t *found = NULL, *fallback;
-       char *pos;
-       int i;
-
-       if (!section)
-       {
-               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);
-               }
-       }
-}
-
-/**
- * Ensure that the section with the given key exists (thread-safe).
- */
-static section_t *ensure_section(private_settings_t *this, section_t *section,
-                                                                const char *key, va_list args)
-{
-       char buf[128], keybuf[512];
-       section_t *found;
-
-       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
-       {
-               return NULL;
-       }
-       /* we might have to change the tree */
-       this->lock->write_lock(this->lock);
-       found = find_section_buffered(section, keybuf, keybuf, args, buf,
-                                                                 sizeof(buf), TRUE);
-       this->lock->unlock(this->lock);
-       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
- */
-static bool fallback_exists(section_t *section, section_t *fallback)
-{
-       if (section == fallback)
-       {
-               return TRUE;
-       }
-       else if (section->fallbacks)
-       {
-               section_t *existing;
-               int i;
-
-               for (i = 0; i < array_count(section->fallbacks); i++)
-               {
-                       array_get(section->fallbacks, i, &existing);
-                       if (existing == fallback)
-                       {
-                               return TRUE;
-                       }
-               }
-       }
-       return FALSE;
-}
-
-/**
- * Ensure that the section with the given key exists and add the given fallback
- * section (thread-safe).
- */
-static void add_fallback_to_section(private_settings_t *this,
-                                                       section_t *section, const char *key, va_list args,
-                                                       section_t *fallback)
-{
-       char buf[128], keybuf[512];
-       section_t *found;
-
-       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
-       {
-               return;
-       }
-       this->lock->write_lock(this->lock);
-       found = find_section_buffered(section, keybuf, keybuf, args, buf,
-                                                                 sizeof(buf), TRUE);
-       if (!fallback_exists(found, fallback))
-       {
-               /* to ensure sections referred to as fallback are not purged, we create
-                * the array there too */
-               if (!fallback->fallbacks)
-               {
-                       fallback->fallbacks = array_create(0, 0);
-               }
-               array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback);
-       }
-       this->lock->unlock(this->lock);
-}
-
-/**
- * Find the key/value pair for a key, using buffered key, reusable buffer
- * If "ensure" is TRUE, the sections (and key/value pair) are created if they
- * don't exist.
- * Fallbacks are only considered if "ensure" is FALSE.
- */
-static kv_t *find_value_buffered(section_t *section, char *start, char *key,
-                                                                va_list args, char *buf, int len, bool ensure)
-{
-       int i;
-       char *pos;
-       kv_t *kv = NULL;
-       section_t *found = NULL;
-
-       if (section == NULL)
-       {
-               return NULL;
-       }
-
-       pos = strchr(key, '.');
-       if (pos)
-       {
-               *pos = '\0';
-               if (!print_key(buf, len, start, key, args))
-               {
-                       return NULL;
-               }
-               /* restore so we can retry for fallbacks */
-               *pos = '.';
-               if (!strlen(buf))
-               {
-                       found = section;
-               }
-               else if (array_bsearch(section->sections, buf, section_find,
-                                                          &found) == -1)
-               {
-                       if (ensure)
-                       {
-                               found = section_create(buf);
-                               array_insert_create(&section->sections, ARRAY_TAIL, found);
-                               array_sort(section->sections, section_sort, NULL);
-                       }
-               }
-               if (found)
-               {
-                       kv = find_value_buffered(found, start, pos+1, args, buf, len,
-                                                                        ensure);
-               }
-               if (!kv && !ensure && section->fallbacks)
-               {
-                       for (i = 0; !kv && i < array_count(section->fallbacks); i++)
-                       {
-                               array_get(section->fallbacks, i, &found);
-                               kv = find_value_buffered(found, start, key, args, buf, len,
-                                                                                ensure);
-                       }
-               }
-       }
-       else
-       {
-               if (!print_key(buf, len, start, key, args))
-               {
-                       return NULL;
-               }
-               if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
-               {
-                       if (ensure)
-                       {
-                               kv = kv_create(buf, NULL);
-                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
-                               array_sort(section->kv, kv_sort, NULL);
-                       }
-                       else if (section->fallbacks)
-                       {
-                               for (i = 0; !kv && i < array_count(section->fallbacks); i++)
-                               {
-                                       array_get(section->fallbacks, i, &found);
-                                       kv = find_value_buffered(found, start, key, args, buf, len,
-                                                                                        ensure);
-                               }
-                       }
-               }
-       }
-       return kv;
-}
-
-/**
- * Find the string value for a key (thread-safe).
- */
-static char *find_value(private_settings_t *this, section_t *section,
-                                               char *key, va_list args)
-{
-       char buf[128], keybuf[512], *value = NULL;
-       kv_t *kv;
-
-       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
-       {
-               return NULL;
-       }
-       this->lock->read_lock(this->lock);
-       kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
-                                                        FALSE);
-       if (kv)
-       {
-               value = kv->value;
-       }
-       this->lock->unlock(this->lock);
-       return value;
-}
-
-/**
- * Set a value to a copy of the given string (thread-safe).
- */
-static void set_value(private_settings_t *this, section_t *section,
-                                         char *key, va_list args, char *value)
-{
-       char buf[128], keybuf[512];
-       kv_t *kv;
-
-       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
-       {
-               return;
-       }
-       this->lock->write_lock(this->lock);
-       kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
-                                                        TRUE);
-       if (kv)
-       {
-               if (!value)
-               {
-                       kv->value = NULL;
-               }
-               else if (kv->value && (strlen(value) <= strlen(kv->value)))
-               {       /* overwrite in-place, if possible */
-                       strcpy(kv->value, value);
-               }
-               else
-               {       /* otherwise clone the string and store it in the cache */
-                       kv->value = strdup(value);
-                       this->contents->insert_last(this->contents, kv->value);
-               }
-       }
-       this->lock->unlock(this->lock);
-}
-
-METHOD(settings_t, get_str, char*,
-       private_settings_t *this, char *key, char *def, ...)
-{
-       char *value;
-       va_list args;
-
-       va_start(args, def);
-       value = find_value(this, this->top, key, args);
-       va_end(args);
-       if (value)
-       {
-               return value;
-       }
-       return def;
-}
-
-/**
- * Described in header
- */
-inline bool settings_value_as_bool(char *value, bool def)
-{
-       if (value)
-       {
-               if (strcaseeq(value, "1") ||
-                       strcaseeq(value, "yes") ||
-                       strcaseeq(value, "true") ||
-                       strcaseeq(value, "enabled"))
-               {
-                       return TRUE;
-               }
-               else if (strcaseeq(value, "0") ||
-                                strcaseeq(value, "no") ||
-                                strcaseeq(value, "false") ||
-                                strcaseeq(value, "disabled"))
-               {
-                       return FALSE;
-               }
-       }
-       return def;
-}
-
-METHOD(settings_t, get_bool, bool,
-       private_settings_t *this, char *key, bool def, ...)
-{
-       char *value;
-       va_list args;
-
-       va_start(args, def);
-       value = find_value(this, this->top, key, args);
-       va_end(args);
-       return settings_value_as_bool(value, def);
-}
-
-/**
- * Described in header
- */
-inline int settings_value_as_int(char *value, int def)
-{
-       int intval;
-       if (value)
-       {
-               errno = 0;
-               intval = strtol(value, NULL, 10);
-               if (errno == 0)
-               {
-                       return intval;
-               }
-       }
-       return def;
-}
-
-METHOD(settings_t, get_int, int,
-       private_settings_t *this, char *key, int def, ...)
-{
-       char *value;
-       va_list args;
-
-       va_start(args, def);
-       value = find_value(this, this->top, key, args);
-       va_end(args);
-       return settings_value_as_int(value, def);
-}
-
-/**
- * Described in header
- */
-inline double settings_value_as_double(char *value, double def)
-{
-       double dval;
-       if (value)
-       {
-               errno = 0;
-               dval = strtod(value, NULL);
-               if (errno == 0)
-               {
-                       return dval;
-               }
-       }
-       return def;
-}
-
-METHOD(settings_t, get_double, double,
-       private_settings_t *this, char *key, double def, ...)
-{
-       char *value;
-       va_list args;
-
-       va_start(args, def);
-       value = find_value(this, this->top, key, args);
-       va_end(args);
-       return settings_value_as_double(value, def);
-}
-
-/**
- * Described in header
- */
-inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
-{
-       char *endptr;
-       u_int32_t timeval;
-       if (value)
-       {
-               errno = 0;
-               timeval = strtoul(value, &endptr, 10);
-               if (errno == 0)
-               {
-                       switch (*endptr)
-                       {
-                               case 'd':               /* time in days */
-                                       timeval *= 24 * 3600;
-                                       break;
-                               case 'h':               /* time in hours */
-                                       timeval *= 3600;
-                                       break;
-                               case 'm':               /* time in minutes */
-                                       timeval *= 60;
-                                       break;
-                               case 's':               /* time in seconds */
-                               default:
-                                       break;
-                       }
-                       return timeval;
-               }
-       }
-       return def;
-}
-
-METHOD(settings_t, get_time, u_int32_t,
-       private_settings_t *this, char *key, u_int32_t def, ...)
-{
-       char *value;
-       va_list args;
-
-       va_start(args, def);
-       value = find_value(this, this->top, key, args);
-       va_end(args);
-       return settings_value_as_time(value, def);
-}
-
-METHOD(settings_t, set_str, void,
-       private_settings_t *this, char *key, char *value, ...)
-{
-       va_list args;
-       va_start(args, value);
-       set_value(this, this->top, key, args, value);
-       va_end(args);
-}
-
-METHOD(settings_t, set_bool, void,
-       private_settings_t *this, char *key, bool value, ...)
-{
-       va_list args;
-       va_start(args, value);
-       set_value(this, this->top, key, args, value ? "1" : "0");
-       va_end(args);
-}
-
-METHOD(settings_t, set_int, void,
-       private_settings_t *this, char *key, int value, ...)
-{
-       char val[16];
-       va_list args;
-       va_start(args, value);
-       if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
-       {
-               set_value(this, this->top, key, args, val);
-       }
-       va_end(args);
-}
-
-METHOD(settings_t, set_double, void,
-       private_settings_t *this, char *key, double value, ...)
-{
-       char val[64];
-       va_list args;
-       va_start(args, value);
-       if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
-       {
-               set_value(this, this->top, key, args, val);
-       }
-       va_end(args);
-}
-
-METHOD(settings_t, set_time, void,
-       private_settings_t *this, char *key, u_int32_t value, ...)
-{
-       char val[16];
-       va_list args;
-       va_start(args, value);
-       if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
-       {
-               set_value(this, this->top, key, args, val);
-       }
-       va_end(args);
-}
-
-METHOD(settings_t, set_default_str, bool,
-       private_settings_t *this, char *key, char *value, ...)
-{
-       char *old;
-       va_list args;
-
-       va_start(args, value);
-       old = find_value(this, this->top, key, args);
-       va_end(args);
-
-       if (!old)
-       {
-               va_start(args, value);
-               set_value(this, this->top, key, args, value);
-               va_end(args);
-               return TRUE;
-       }
-       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(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, ...)
-{
-       enumerator_data_t *data;
-       array_t *sections;
-       va_list args;
-
-       this->lock->read_lock(this->lock);
-       va_start(args, key);
-       sections = find_sections(this, this->top, key, args);
-       va_end(args);
-
-       if (!sections)
-       {
-               this->lock->unlock(this->lock);
-               return enumerator_create_empty();
-       }
-       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(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, ...)
-{
-       enumerator_data_t *data;
-       array_t *sections;
-       va_list args;
-
-       this->lock->read_lock(this->lock);
-       va_start(args, key);
-       sections = find_sections(this, this->top, key, args);
-       va_end(args);
-
-       if (!sections)
-       {
-               this->lock->unlock(this->lock);
-               return enumerator_create_empty();
-       }
-       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,
-       private_settings_t *this, const char *key, const char *fallback, ...)
-{
-       section_t *section;
-       va_list args;
-
-       /* find/create the fallback */
-       va_start(args, fallback);
-       section = ensure_section(this, this->top, fallback, args);
-       va_end(args);
-
-       va_start(args, fallback);
-       add_fallback_to_section(this, this->top, key, args, section);
-       va_end(args);
-}
-
-/**
- * parse text, truncate "skip" chars, delimited by term respecting brackets.
- *
- * Chars in "skip" are truncated at the beginning and the end of the resulting
- * token. "term" contains a list of characters to read up to (first match),
- * while "br" contains bracket counterparts found in "term" to skip.
- */
-static char parse(char **text, char *skip, char *term, char *br, char **token)
-{
-       char *best = NULL;
-       char best_term = '\0';
-
-       /* skip leading chars */
-       while (strchr(skip, **text))
-       {
-               (*text)++;
-               if (!**text)
-               {
-                       return 0;
-               }
-       }
-       /* mark begin of subtext */
-       *token = *text;
-       while (*term)
-       {
-               char *pos = *text;
-               int level = 1;
-
-               /* find terminator */
-               while (*pos)
-               {
-                       if (*pos == *term)
-                       {
-                               level--;
-                       }
-                       else if (br && *pos == *br)
-                       {
-                               level++;
-                       }
-                       if (level == 0)
-                       {
-                               if (best == NULL || best > pos)
-                               {
-                                       best = pos;
-                                       best_term = *term;
-                               }
-                               break;
-                       }
-                       pos++;
-               }
-               /* try next terminator */
-               term++;
-               if (br)
-               {
-                       br++;
-               }
-       }
-       if (best)
-       {
-               /* update input */
-               *text = best;
-               /* null trailing bytes */
-               do
-               {
-                       *best = '\0';
-                       best--;
-               }
-               while (best >= *token && strchr(skip, *best));
-               /* return found terminator */
-               return best_term;
-       }
-       return 0;
-}
-
-/**
- * Check if "text" starts with "pattern".
- * Characters in "skip" are skipped first. If found, TRUE is returned and "text"
- * is modified to point to the character right after "pattern".
- */
-static bool starts_with(char **text, char *skip, char *pattern)
-{
-       char *pos = *text;
-       int len = strlen(pattern);
-       while (strchr(skip, *pos))
-       {
-               pos++;
-               if (!*pos)
-               {
-                       return FALSE;
-               }
-       }
-       if (strlen(pos) < len || !strneq(pos, pattern, len))
-       {
-               return FALSE;
-       }
-       *text = pos + len;
-       return TRUE;
-}
-
-/**
- * Check if what follows in "text" is an include statement.
- * If this function returns TRUE, "text" will point to the character right after
- * the include pattern, which is returned in "pattern".
- */
-static bool parse_include(char **text, char **pattern)
-{
-       char *pos = *text;
-       if (!starts_with(&pos, "\n\t ", "include"))
-       {
-               return FALSE;
-       }
-       if (starts_with(&pos, "\t ", "="))
-       {       /* ignore "include = value" */
-               return FALSE;
-       }
-       *text = pos;
-       return parse(text, "\t ", "\n", NULL, pattern) != 0;
-}
-
-/**
- * Forward declaration.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
-                                               char *pattern, section_t *section);
-
-/**
- * Parse a section
- */
-static bool parse_section(linked_list_t *contents, char *file, int level,
-                                                 char **text, section_t *section)
-{
-       bool finished = FALSE;
-       char *key, *value, *inner;
-
-       while (!finished)
-       {
-               if (parse_include(text, &value))
-               {
-                       if (!parse_files(contents, file, level, value, section))
-                       {
-                               DBG1(DBG_LIB, "failed to include '%s'", value);
-                               return FALSE;
-                       }
-                       continue;
-               }
-               switch (parse(text, "\t\n ", "{=#", NULL, &key))
-               {
-                       case '{':
-                               if (parse(text, "\t ", "}", "{", &inner))
-                               {
-                                       section_t *sub;
-                                       if (!strlen(key))
-                                       {
-                                               DBG1(DBG_LIB, "skipping section without name in '%s'",
-                                                        section->name);
-                                               continue;
-                                       }
-                                       if (array_bsearch(section->sections, key, section_find,
-                                                                         &sub) == -1)
-                                       {
-                                               sub = section_create(key);
-                                               if (parse_section(contents, file, level, &inner, sub))
-                                               {
-                                                       array_insert_create(&section->sections, ARRAY_TAIL,
-                                                                                               sub);
-                                                       array_sort(section->sections, section_sort, NULL);
-                                                       continue;
-                                               }
-                                               section_destroy(sub);
-                                       }
-                                       else
-                                       {       /* extend the existing section */
-                                               if (parse_section(contents, file, level, &inner, sub))
-                                               {
-                                                       continue;
-                                               }
-                                       }
-                                       DBG1(DBG_LIB, "parsing subsection '%s' failed", key);
-                                       break;
-                               }
-                               DBG1(DBG_LIB, "matching '}' not found near %s", *text);
-                               break;
-                       case '=':
-                               if (parse(text, "\t ", "\n", NULL, &value))
-                               {
-                                       kv_t *kv;
-                                       if (!strlen(key))
-                                       {
-                                               DBG1(DBG_LIB, "skipping value without key in '%s'",
-                                                        section->name);
-                                               continue;
-                                       }
-                                       if (array_bsearch(section->kv, key, kv_find, &kv) == -1)
-                                       {
-                                               kv = kv_create(key, value);
-                                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
-                                               array_sort(section->kv, kv_sort, NULL);
-                                       }
-                                       else
-                                       {       /* replace with the most recently read value */
-                                               kv->value = value;
-                                       }
-                                       continue;
-                               }
-                               DBG1(DBG_LIB, "parsing value failed near %s", *text);
-                               break;
-                       case '#':
-                               parse(text, "", "\n", NULL, &value);
-                               continue;
-                       default:
-                               finished = TRUE;
-                               continue;
-               }
-               return FALSE;
-       }
-       return TRUE;
-}
-
-/**
- * Parse a file and add the settings to the given section.
- */
-static bool parse_file(linked_list_t *contents, char *file, int level,
-                                          section_t *section)
-{
-       bool success;
-       char *text, *pos;
-       struct stat st;
-       FILE *fd;
-       int len;
-
-       DBG2(DBG_LIB, "loading config file '%s'", file);
-       if (stat(file, &st) == -1)
-       {
-               if (errno == ENOENT)
-               {
-#ifdef STRONGSWAN_CONF
-                       if (streq(file, STRONGSWAN_CONF))
-                       {
-                               DBG2(DBG_LIB, "'%s' does not exist, ignored", file);
-                       }
-                       else
-#endif
-                       {
-                               DBG1(DBG_LIB, "'%s' does not exist, ignored", file);
-                       }
-                       return TRUE;
-               }
-               DBG1(DBG_LIB, "failed to stat '%s': %s", file, strerror(errno));
-               return FALSE;
-       }
-       else if (!S_ISREG(st.st_mode))
-       {
-               DBG1(DBG_LIB, "'%s' is not a regular file", file);
-               return FALSE;
-       }
-       fd = fopen(file, "r");
-       if (fd == NULL)
-       {
-               DBG1(DBG_LIB, "'%s' is not readable", file);
-               return FALSE;
-       }
-       fseek(fd, 0, SEEK_END);
-       len = ftell(fd);
-       rewind(fd);
-       text = malloc(len + 2);
-       text[len] = text[len + 1] = '\0';
-       if (fread(text, 1, len, fd) != len)
-       {
-               free(text);
-               fclose(fd);
-               return FALSE;
-       }
-       fclose(fd);
-
-       pos = text;
-       success = parse_section(contents, file, level, &pos, section);
-       if (!success)
-       {
-               free(text);
-       }
-       else
-       {
-               contents->insert_last(contents, text);
-       }
-       return success;
-}
-
-/**
- * Load the files matching "pattern", which is resolved with glob(3), if
- * available.
- * If the pattern is relative, the directory of "file" is used as base.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
-                                               char *pattern, section_t *section)
-{
-       bool success = TRUE;
-       char pat[PATH_MAX];
-
-       if (level > MAX_INCLUSION_LEVEL)
-       {
-               DBG1(DBG_LIB, "maximum level of %d includes reached, ignored",
-                        MAX_INCLUSION_LEVEL);
-               return TRUE;
-       }
-
-       if (!strlen(pattern))
-       {
-               DBG1(DBG_LIB, "empty include pattern, ignored");
-               return TRUE;
-       }
-
-       if (!file || pattern[0] == '/')
-       {       /* absolute path */
-               if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
-               {
-                       DBG1(DBG_LIB, "include pattern too long, ignored");
-                       return TRUE;
-               }
-       }
-       else
-       {       /* base relative paths to the directory of the current file */
-               char *dir = path_dirname(file);
-               if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat))
-               {
-                       DBG1(DBG_LIB, "include pattern too long, ignored");
-                       free(dir);
-                       return TRUE;
-               }
-               free(dir);
-       }
-#ifdef HAVE_GLOB_H
-       {
-               int status;
-               glob_t buf;
-
-               status = glob(pat, GLOB_ERR, NULL, &buf);
-               if (status == GLOB_NOMATCH)
-               {
-                       DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
-               }
-               else if (status != 0)
-               {
-                       DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
-                       success = FALSE;
-               }
-               else
-               {
-                       char **expanded;
-                       for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
-                       {
-                               success &= parse_file(contents, *expanded, level + 1, section);
-                               if (!success)
-                               {
-                                       break;
-                               }
-                       }
-               }
-               globfree(&buf);
-       }
-#else /* HAVE_GLOB_H */
-       /* if glob(3) is not available, try to load pattern directly */
-       success = parse_file(contents, pat, level + 1, section);
-#endif /* HAVE_GLOB_H */
-       return success;
-}
-
-/**
- * Recursivly extends "base" with "extension".
- */
-static void section_extend(section_t *base, section_t *extension)
-{
-       enumerator_t *enumerator;
-       section_t *sec;
-       kv_t *kv;
-
-       enumerator = array_create_enumerator(extension->sections);
-       while (enumerator->enumerate(enumerator, (void**)&sec))
-       {
-               section_t *found;
-               if (array_bsearch(base->sections, sec->name, section_find,
-                       &found) != -1)
-               {
-                       section_extend(found, sec);
-               }
-               else
-               {
-                       array_remove_at(extension->sections, enumerator);
-                       array_insert_create(&base->sections, ARRAY_TAIL, sec);
-                       array_sort(base->sections, section_sort, NULL);
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       enumerator = array_create_enumerator(extension->kv);
-       while (enumerator->enumerate(enumerator, (void**)&kv))
-       {
-               kv_t *found;
-               if (array_bsearch(base->kv, kv->key, kv_find, &found) != -1)
-               {
-                       found->value = kv->value;
-               }
-               else
-               {
-                       array_remove_at(extension->kv, enumerator);
-                       array_insert_create(&base->kv, ARRAY_TAIL, kv);
-                       array_sort(base->kv, kv_sort, NULL);
-               }
-       }
-       enumerator->destroy(enumerator);
-}
-
-/**
- * Load settings from files matching the given file pattern.
- * All sections and values are added relative to "parent".
- * All files (even included ones) have to be loaded successfully.
- */
-static bool load_files_internal(private_settings_t *this, section_t *parent,
-                                                               char *pattern, bool merge)
-{
-       char *text;
-       linked_list_t *contents;
-       section_t *section;
-
-       if (pattern == NULL)
-       {
-#ifdef STRONGSWAN_CONF
-               pattern = STRONGSWAN_CONF;
-#else
-               return FALSE;
-#endif
-       }
-
-       contents = linked_list_create();
-       section = section_create(NULL);
-
-       if (!parse_files(contents, NULL, 0, pattern, section))
-       {
-               contents->destroy_function(contents, (void*)free);
-               section_destroy(section);
-               return FALSE;
-       }
-
-       this->lock->write_lock(this->lock);
-       if (!merge)
-       {
-               section_purge(parent);
-       }
-       /* extend parent section */
-       section_extend(parent, section);
-       /* move contents of loaded files to main store */
-       while (contents->remove_first(contents, (void**)&text) == SUCCESS)
-       {
-               this->contents->insert_last(this->contents, text);
-       }
-       this->lock->unlock(this->lock);
-
-       section_destroy(section);
-       contents->destroy(contents);
-       return TRUE;
-}
-
-METHOD(settings_t, load_files, bool,
-       private_settings_t *this, char *pattern, bool merge)
-{
-       return load_files_internal(this, this->top, pattern, merge);
-}
-
-METHOD(settings_t, load_files_section, bool,
-       private_settings_t *this, char *pattern, bool merge, char *key, ...)
-{
-       section_t *section;
-       va_list args;
-
-       va_start(args, key);
-       section = ensure_section(this, this->top, key, args);
-       va_end(args);
-
-       if (!section)
-       {
-               return FALSE;
-       }
-       return load_files_internal(this, section, pattern, merge);
-}
-
-METHOD(settings_t, destroy, void,
-       private_settings_t *this)
-{
-       section_destroy(this->top);
-       this->contents->destroy_function(this->contents, (void*)free);
-       this->lock->destroy(this->lock);
-       free(this);
-}
-
-/*
- * see header file
- */
-settings_t *settings_create(char *file)
-{
-       private_settings_t *this;
-
-       INIT(this,
-               .public = {
-                       .get_str = _get_str,
-                       .get_int = _get_int,
-                       .get_double = _get_double,
-                       .get_time = _get_time,
-                       .get_bool = _get_bool,
-                       .set_str = _set_str,
-                       .set_int = _set_int,
-                       .set_double = _set_double,
-                       .set_time = _set_time,
-                       .set_bool = _set_bool,
-                       .set_default_str = _set_default_str,
-                       .create_section_enumerator = _create_section_enumerator,
-                       .create_key_value_enumerator = _create_key_value_enumerator,
-                       .add_fallback = _add_fallback,
-                       .load_files = _load_files,
-                       .load_files_section = _load_files_section,
-                       .destroy = _destroy,
-               },
-               .top = section_create(NULL),
-               .contents = linked_list_create(),
-               .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
-       );
-
-       load_files(this, file, FALSE);
-
-       return &this->public;
-}
diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h
deleted file mode 100644 (file)
index 46403c4..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2010 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup settings settings
- * @{ @ingroup utils
- */
-
-#ifndef SETTINGS_H_
-#define SETTINGS_H_
-
-typedef struct settings_t settings_t;
-
-#include "utils.h"
-#include "collections/enumerator.h"
-
-/**
- * Convert a string value returned by a key/value enumerator to a boolean.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_bool()
- * @param value                        the string value
- * @param def                  the default value, if value is NULL or invalid
- */
-bool settings_value_as_bool(char *value, bool def);
-
-/**
- * Convert a string value returned by a key/value enumerator to an integer.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_int()
- * @param value                        the string value
- * @param def                  the default value, if value is NULL or invalid
- */
-int settings_value_as_int(char *value, int def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a double.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_double()
- * @param value                        the string value
- * @param def                  the default value, if value is NULL or invalid
- */
-double settings_value_as_double(char *value, double def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a time value.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_time()
- * @param value                        the string value
- * @param def                  the default value, if value is NULL or invalid
- */
-u_int32_t settings_value_as_time(char *value, u_int32_t def);
-
-/**
- * Generic configuration options read from a config file.
- *
- * The syntax is quite simple:
- * @code
- * settings := (section|keyvalue)*
- * section  := name { settings }
- * keyvalue := key = value\n
- * @endcode
- * E.g.:
- * @code
-       a = b
-       section-one {
-               somevalue = asdf
-               subsection {
-                       othervalue = xxx
-               }
-               yetanother = zz
-       }
-       section-two {
-       }
-       @endcode
- *
- * The values are accessed using the get() functions using dotted keys, e.g.
- *   section-one.subsection.othervalue
- *
- * Currently only a limited set of printf format specifiers are supported
- * (namely %s, %d and %N, see implementation for details).
- *
- * \section includes Including other files
- * Other files can be included, using the include statement e.g.
- * @code
- *   include /somepath/subconfig.conf
- * @endcode
- * Shell patterns like *.conf are possible.
- *
- * If the path is relative, the directory of the file containing the include
- * statement is used as base.
- *
- * Sections loaded from included files extend previously loaded sections,
- * already existing values are replaced.
- *
- * All settings included from files are added relative to the section the
- * include statement is in.
- *
- * The following files result in the same final config as above:
- *
- * @code
-       a = b
-       section-one {
-               somevalue = before include
-               include include.conf
-       }
-       include two.conf
-       @endcode
- * include.conf
- * @code
-       somevalue = asdf
-       subsection {
-               othervalue = yyy
-       }
-       yetanother = zz
-       @endcode
- * two.conf
- * @code
-       section-one {
-               subsection {
-                       othervalue = xxx
-               }
-       }
-       section-two {
-       }
-       @endcode
- */
-struct settings_t {
-
-       /**
-        * Get a settings value as a string.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value returned if key not found
-        * @param ...           argument list for key
-        * @return                      value pointing to internal string
-        */
-       char* (*get_str)(settings_t *this, char *key, char *def, ...);
-
-       /**
-        * Get a boolean yes|no, true|false value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value returned if key not found
-        * @param ...           argument list for key
-        * @return                      value of the key
-        */
-       bool (*get_bool)(settings_t *this, char *key, bool def, ...);
-
-       /**
-        * Get an integer value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value returned if key not found
-        * @param ...           argument list for key
-        * @return                      value of the key
-        */
-       int (*get_int)(settings_t *this, char *key, int def, ...);
-
-       /**
-        * Get an double value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value returned if key not found
-        * @param ...           argument list for key
-        * @return                      value of the key
-        */
-       double (*get_double)(settings_t *this, char *key, double def, ...);
-
-       /**
-        * Get a time value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value returned if key not found
-        * @param ...           argument list for key
-        * @return                      value of the key (in seconds)
-        */
-       u_int32_t (*get_time)(settings_t *this, char *key, u_int32_t def, ...);
-
-       /**
-        * Set a string value.
-        *
-        * @param key           key including sections, printf style format
-        * @param value         value to set (gets cloned)
-        * @param ...           argument list for key
-        */
-       void (*set_str)(settings_t *this, char *key, char *value, ...);
-
-       /**
-        * Set a boolean value.
-        *
-        * @param key           key including sections, printf style format
-        * @param value         value to set
-        * @param ...           argument list for key
-        */
-       void (*set_bool)(settings_t *this, char *key, bool value, ...);
-
-       /**
-        * Set an integer value.
-        *
-        * @param key           key including sections, printf style format
-        * @param value         value to set
-        * @param ...           argument list for key
-        */
-       void (*set_int)(settings_t *this, char *key, int value, ...);
-
-       /**
-        * Set an double value.
-        *
-        * @param key           key including sections, printf style format
-        * @param value         value to set
-        * @param ...           argument list for key
-        */
-       void (*set_double)(settings_t *this, char *key, double value, ...);
-
-       /**
-        * Set a time value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value to set
-        * @param ...           argument list for key
-        */
-       void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
-
-       /**
-        * Set a default for string value.
-        *
-        * @param key           key including sections, printf style format
-        * @param def           value to set if unconfigured
-        * @param ...           argument list for key
-        * @return                      TRUE if a new default value for key has been set
-        */
-       bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
-
-       /**
-        * Create an enumerator over subsection names of a section.
-        *
-        * @param section       section including parents, printf style format
-        * @param ...           argument list for key
-        * @return                      enumerator over subsection names
-        */
-       enumerator_t* (*create_section_enumerator)(settings_t *this,
-                                                                                          char *section, ...);
-
-       /**
-        * Create an enumerator over key/value pairs in a section.
-        *
-        * @param section       section name to list key/value pairs of, printf style
-        * @param ...           argument list for section
-        * @return                      enumerator over (char *key, char *value)
-        */
-       enumerator_t* (*create_key_value_enumerator)(settings_t *this,
-                                                                                                char *section, ...);
-
-       /**
-        * Add a fallback for the given section.
-        *
-        * Example: When the fallback 'section-two' is configured for
-        * 'section-one.two' any failed lookup for a section or key in
-        * 'section-one.two' will result in a lookup for the same section/key
-        * in 'section-two'.
-        *
-        * @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.
-        *
-        * @param section       section for which a fallback is configured, printf style
-        * @param fallback      fallback section, printf style
-        * @param ...           argument list for section and fallback
-        */
-       void (*add_fallback)(settings_t *this, const char *section,
-                                                const char *fallback, ...);
-
-       /**
-        * Load settings from the files matching the given pattern.
-        *
-        * If merge is TRUE, existing sections are extended, existing values
-        * replaced, by those found in the loaded files. If it is FALSE, existing
-        * sections are purged before reading the new config.
-        *
-        * @note If any of the files matching the pattern fails to load, no settings
-        * are added at all. So, it's all or nothing.
-        *
-        * @param pattern       file pattern
-        * @param merge         TRUE to merge config with existing values
-        * @return                      TRUE, if settings were loaded successfully
-        */
-       bool (*load_files)(settings_t *this, char *pattern, bool merge);
-
-       /**
-        * Load settings from the files matching the given pattern.
-        *
-        * If merge is TRUE, existing sections are extended, existing values
-        * replaced, by those found in the loaded files. If it is FALSE, existing
-        * sections are purged before reading the new config.
-        *
-        * All settings are loaded relative to the given section. The section is
-        * created, if it does not yet exist.
-        *
-        * @note If any of the files matching the pattern fails to load, no settings
-        * are added at all. So, it's all or nothing.
-        *
-        * @param pattern       file pattern
-        * @param merge         TRUE to merge config with existing values
-        * @param section       section name of parent section, printf style
-        * @param ...           argument list for section
-        * @return                      TRUE, if settings were loaded successfully
-        */
-       bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
-                                                          char *section, ...);
-
-       /**
-        * Destroy a settings instance.
-        */
-       void (*destroy)(settings_t *this);
-};
-
-/**
- * Load settings from a file.
- *
- * @param file                 file to read settings from, NULL for default
- * @return                             settings object
- */
-settings_t *settings_create(char *file);
-
-#endif /** SETTINGS_H_ @}*/