Moved settings_t to utils folder
authorTobias Brunner <tobias@strongswan.org>
Tue, 16 Oct 2012 14:08:43 +0000 (16:08 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 24 Oct 2012 14:00:51 +0000 (16:00 +0200)
src/libstrongswan/Android.mk
src/libstrongswan/Makefile.am
src/libstrongswan/crypto/crypto_factory.h
src/libstrongswan/library.h
src/libstrongswan/settings.c [deleted file]
src/libstrongswan/settings.h [deleted file]
src/libstrongswan/utils/settings.c [new file with mode: 0644]
src/libstrongswan/utils/settings.h [new file with mode: 0644]

index f024f2c..47470c3 100644 (file)
@@ -3,7 +3,7 @@ include $(CLEAR_VARS)
 
 # copy-n-paste from Makefile.am
 LOCAL_SRC_FILES := \
-library.c settings.c \
+library.c \
 asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
 collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
 collections/linked_list.c \
@@ -32,7 +32,7 @@ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
 threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
 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.c
+utils/printf_hook.c utils/settings.c
 
 # adding the plugin source files
 
index 205cb64..2aed071 100644 (file)
@@ -1,7 +1,7 @@
 ipseclib_LTLIBRARIES = libstrongswan.la
 
 libstrongswan_la_SOURCES = \
-library.c settings.c \
+library.c \
 asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
 collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
 collections/linked_list.c \
@@ -30,12 +30,12 @@ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
 threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
 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.c
+utils/printf_hook.c utils/settings.c
 
 if USE_DEV_HEADERS
 strongswan_includedir = ${dev_headers}
 nobase_strongswan_include_HEADERS = \
-library.h settings.h \
+library.h \
 asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \
 collections/blocking_queue.h collections/enumerator.h collections/hashtable.h \
 collections/linked_list.h \
@@ -71,7 +71,7 @@ threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h
 threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.h \
 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.h utils/integrity_checker.h
+utils/leak_detective.h utils/printf_hook.h utils/settings.h utils/integrity_checker.h
 endif
 
 library.lo :   $(top_builddir)/config.status
index 611ca0b..5d23c89 100644 (file)
@@ -24,6 +24,7 @@
 typedef struct crypto_factory_t crypto_factory_t;
 
 #include <library.h>
+#include <collections/enumerator.h>
 #include <crypto/crypters/crypter.h>
 #include <crypto/aead.h>
 #include <crypto/signers/signer.h>
index 699d5cb..e547c7e 100644 (file)
@@ -83,7 +83,6 @@
 /* make sure we include printf_hook.h first */
 #include "utils/printf_hook.h"
 #include "utils.h"
-#include "settings.h"
 #include "networking/host_resolver.h"
 #include "processing/processor.h"
 #include "processing/scheduler.h"
@@ -97,6 +96,7 @@
 #include "utils/chunk.h"
 #include "utils/integrity_checker.h"
 #include "utils/leak_detective.h"
+#include "utils/settings.h"
 #include "plugins/plugin_loader.h"
 
 typedef struct library_t library_t;
diff --git a/src/libstrongswan/settings.c b/src/libstrongswan/settings.c
deleted file mode 100644 (file)
index 712ea6e..0000000
+++ /dev/null
@@ -1,1227 +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.
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <limits.h>
-#include <libgen.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/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;
-
-       /**
-        * subsections, as section_t
-        */
-       linked_list_t *sections;
-
-       /**
-        * key value pairs, as kv_t
-        */
-       linked_list_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),
-               .sections = linked_list_create(),
-               .kv = linked_list_create(),
-       );
-       return this;
-}
-
-/**
- * destroy a section
- */
-static void section_destroy(section_t *this)
-{
-       this->kv->destroy_function(this->kv, (void*)kv_destroy);
-       this->sections->destroy_function(this->sections, (void*)section_destroy);
-       free(this->name);
-       free(this);
-}
-
-/**
- * Purge contents of a section
- */
-static void section_purge(section_t *this)
-{
-       this->kv->destroy_function(this->kv, (void*)kv_destroy);
-       this->kv = linked_list_create();
-       this->sections->destroy_function(this->sections, (void*)section_destroy);
-       this->sections = linked_list_create();
-}
-
-/**
- * callback to find a section by name
- */
-static bool section_find(section_t *this, char *name)
-{
-       return streq(this->name, name);
-}
-
-/**
- * callback to find a kv pair by key
- */
-static bool kv_find(kv_t *this, char *key)
-{
-       return streq(this->key, 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;
-       bool res;
-       char *pos;
-
-       va_copy(copy, args);
-       while (start < key)
-       {
-               pos = strchr(start, '%');
-               if (!pos)
-               {
-                       start += strlen(start) + 1;
-                       continue;
-               }
-               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;
-               }
-               start = pos;
-               if (*start)
-               {
-                       start++;
-               }
-       }
-       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 (section->sections->find_first(section->sections,
-                                                                         (linked_list_match_t)section_find,
-                                                                         (void**)&found, buf) != SUCCESS)
-       {
-               if (ensure)
-               {
-                       found = section_create(buf);
-                       section->sections->insert_last(section->sections, found);
-               }
-       }
-       if (found && pos)
-       {
-               return find_section_buffered(found, start, pos, args, buf, len, ensure);
-       }
-       return found;
-}
-
-/**
- * Find a section by a given key (thread-safe).
- */
-static section_t *find_section(private_settings_t *this, section_t *section,
-                                                          char *key, va_list args)
-{
-       char buf[128], keybuf[512];
-       section_t *found;
-
-       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
-       {
-               return NULL;
-       }
-       this->lock->read_lock(this->lock);
-       found = find_section_buffered(section, keybuf, keybuf, args, buf,
-                                                                 sizeof(buf), FALSE);
-       this->lock->unlock(this->lock);
-       return found;
-}
-
-/**
- * Ensure that the section with the given key exists (thread-safe).
- */
-static section_t *ensure_section(private_settings_t *this, section_t *section,
-                                                                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 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.
- */
-static kv_t *find_value_buffered(section_t *section, char *start, char *key,
-                                                                va_list args, char *buf, int len, bool ensure)
-{
-       char *pos;
-       kv_t *kv = NULL;
-       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 (section->sections->find_first(section->sections,
-                                                                                 (linked_list_match_t)section_find,
-                                                                                 (void**)&found, buf) != SUCCESS)
-               {
-                       if (!ensure)
-                       {
-                               return NULL;
-                       }
-                       found = section_create(buf);
-                       section->sections->insert_last(section->sections, found);
-               }
-               return find_value_buffered(found, start, pos, args, buf, len,
-                                                                  ensure);
-       }
-       else
-       {
-               if (!print_key(buf, len, start, key, args))
-               {
-                       return NULL;
-               }
-               if (section->kv->find_first(section->kv, (linked_list_match_t)kv_find,
-                                                                       (void**)&kv, buf) != SUCCESS)
-               {
-                       if (ensure)
-                       {
-                               kv = kv_create(buf, NULL);
-                               section->kv->insert_last(section->kv, kv);
-                       }
-               }
-       }
-       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);
-}
-
-/**
- * Enumerate section names, not sections
- */
-static bool section_filter(void *null, section_t **in, char **out)
-{
-       *out = (*in)->name;
-       return TRUE;
-}
-
-METHOD(settings_t, create_section_enumerator, enumerator_t*,
-          private_settings_t *this, char *key, ...)
-{
-       section_t *section;
-       va_list args;
-
-       va_start(args, key);
-       section = find_section(this, this->top, key, args);
-       va_end(args);
-
-       if (!section)
-       {
-               return enumerator_create_empty();
-       }
-       this->lock->read_lock(this->lock);
-       return enumerator_create_filter(
-                               section->sections->create_enumerator(section->sections),
-                               (void*)section_filter, this->lock, (void*)this->lock->unlock);
-}
-
-/**
- * Enumerate key and values, not kv_t entries
- */
-static bool kv_filter(void *null, kv_t **in, char **key,
-                                         void *none, char **value)
-{
-       *key = (*in)->key;
-       *value = (*in)->value;
-       return TRUE;
-}
-
-METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
-          private_settings_t *this, char *key, ...)
-{
-       section_t *section;
-       va_list args;
-
-       va_start(args, key);
-       section = find_section(this, this->top, key, args);
-       va_end(args);
-
-       if (!section)
-       {
-               return enumerator_create_empty();
-       }
-       this->lock->read_lock(this->lock);
-       return enumerator_create_filter(
-                                       section->kv->create_enumerator(section->kv),
-                                       (void*)kv_filter, this->lock, (void*)this->lock->unlock);
-}
-
-/**
- * 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 (section->sections->find_first(section->sections,
-                                                                                       (linked_list_match_t)section_find,
-                                                                                       (void**)&sub, key) != SUCCESS)
-                                       {
-                                               sub = section_create(key);
-                                               if (parse_section(contents, file, level, &inner, sub))
-                                               {
-                                                       section->sections->insert_last(section->sections,
-                                                                                                                  sub);
-                                                       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 (section->kv->find_first(section->kv,
-                                                               (linked_list_match_t)kv_find,
-                                                               (void**)&kv, key) != SUCCESS)
-                                       {
-                                               kv = kv_create(key, value);
-                                               section->kv->insert_last(section->kv, kv);
-                                       }
-                                       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)
-               {
-                       DBG2(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 + 1);
-       text[len] = '\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))
-       {
-               DBG2(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 = strdup(file);
-               dir = dirname(dir);
-               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)
-               {
-                       DBG2(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 = extension->sections->create_enumerator(extension->sections);
-       while (enumerator->enumerate(enumerator, (void**)&sec))
-       {
-               section_t *found;
-               if (base->sections->find_first(base->sections,
-                                       (linked_list_match_t)section_find, (void**)&found,
-                                       sec->name) == SUCCESS)
-               {
-                       section_extend(found, sec);
-               }
-               else
-               {
-                       extension->sections->remove_at(extension->sections, enumerator);
-                       base->sections->insert_last(base->sections, sec);
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       enumerator = extension->kv->create_enumerator(extension->kv);
-       while (enumerator->enumerate(enumerator, (void**)&kv))
-       {
-               kv_t *found;
-               if (base->kv->find_first(base->kv, (linked_list_match_t)kv_find,
-                                       (void**)&found, kv->key) == SUCCESS)
-               {
-                       found->value = kv->value;
-               }
-               else
-               {
-                       extension->kv->remove_at(extension->kv, enumerator);
-                       base->kv->insert_last(base->kv, kv);
-               }
-       }
-       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,
-                       .create_section_enumerator = _create_section_enumerator,
-                       .create_key_value_enumerator = _create_key_value_enumerator,
-                       .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.h b/src/libstrongswan/settings.h
deleted file mode 100644 (file)
index 4246b31..0000000
+++ /dev/null
@@ -1,313 +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 libstrongswan
- */
-
-#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, ...);
-
-       /**
-        * 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, ...);
-
-       /**
-        * 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_ @}*/
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
new file mode 100644 (file)
index 0000000..712ea6e
--- /dev/null
@@ -0,0 +1,1227 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <libgen.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/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;
+
+       /**
+        * subsections, as section_t
+        */
+       linked_list_t *sections;
+
+       /**
+        * key value pairs, as kv_t
+        */
+       linked_list_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),
+               .sections = linked_list_create(),
+               .kv = linked_list_create(),
+       );
+       return this;
+}
+
+/**
+ * destroy a section
+ */
+static void section_destroy(section_t *this)
+{
+       this->kv->destroy_function(this->kv, (void*)kv_destroy);
+       this->sections->destroy_function(this->sections, (void*)section_destroy);
+       free(this->name);
+       free(this);
+}
+
+/**
+ * Purge contents of a section
+ */
+static void section_purge(section_t *this)
+{
+       this->kv->destroy_function(this->kv, (void*)kv_destroy);
+       this->kv = linked_list_create();
+       this->sections->destroy_function(this->sections, (void*)section_destroy);
+       this->sections = linked_list_create();
+}
+
+/**
+ * callback to find a section by name
+ */
+static bool section_find(section_t *this, char *name)
+{
+       return streq(this->name, name);
+}
+
+/**
+ * callback to find a kv pair by key
+ */
+static bool kv_find(kv_t *this, char *key)
+{
+       return streq(this->key, 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;
+       bool res;
+       char *pos;
+
+       va_copy(copy, args);
+       while (start < key)
+       {
+               pos = strchr(start, '%');
+               if (!pos)
+               {
+                       start += strlen(start) + 1;
+                       continue;
+               }
+               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;
+               }
+               start = pos;
+               if (*start)
+               {
+                       start++;
+               }
+       }
+       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 (section->sections->find_first(section->sections,
+                                                                         (linked_list_match_t)section_find,
+                                                                         (void**)&found, buf) != SUCCESS)
+       {
+               if (ensure)
+               {
+                       found = section_create(buf);
+                       section->sections->insert_last(section->sections, found);
+               }
+       }
+       if (found && pos)
+       {
+               return find_section_buffered(found, start, pos, args, buf, len, ensure);
+       }
+       return found;
+}
+
+/**
+ * Find a section by a given key (thread-safe).
+ */
+static section_t *find_section(private_settings_t *this, section_t *section,
+                                                          char *key, va_list args)
+{
+       char buf[128], keybuf[512];
+       section_t *found;
+
+       if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+       {
+               return NULL;
+       }
+       this->lock->read_lock(this->lock);
+       found = find_section_buffered(section, keybuf, keybuf, args, buf,
+                                                                 sizeof(buf), FALSE);
+       this->lock->unlock(this->lock);
+       return found;
+}
+
+/**
+ * Ensure that the section with the given key exists (thread-safe).
+ */
+static section_t *ensure_section(private_settings_t *this, section_t *section,
+                                                                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 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.
+ */
+static kv_t *find_value_buffered(section_t *section, char *start, char *key,
+                                                                va_list args, char *buf, int len, bool ensure)
+{
+       char *pos;
+       kv_t *kv = NULL;
+       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 (section->sections->find_first(section->sections,
+                                                                                 (linked_list_match_t)section_find,
+                                                                                 (void**)&found, buf) != SUCCESS)
+               {
+                       if (!ensure)
+                       {
+                               return NULL;
+                       }
+                       found = section_create(buf);
+                       section->sections->insert_last(section->sections, found);
+               }
+               return find_value_buffered(found, start, pos, args, buf, len,
+                                                                  ensure);
+       }
+       else
+       {
+               if (!print_key(buf, len, start, key, args))
+               {
+                       return NULL;
+               }
+               if (section->kv->find_first(section->kv, (linked_list_match_t)kv_find,
+                                                                       (void**)&kv, buf) != SUCCESS)
+               {
+                       if (ensure)
+                       {
+                               kv = kv_create(buf, NULL);
+                               section->kv->insert_last(section->kv, kv);
+                       }
+               }
+       }
+       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);
+}
+
+/**
+ * Enumerate section names, not sections
+ */
+static bool section_filter(void *null, section_t **in, char **out)
+{
+       *out = (*in)->name;
+       return TRUE;
+}
+
+METHOD(settings_t, create_section_enumerator, enumerator_t*,
+          private_settings_t *this, char *key, ...)
+{
+       section_t *section;
+       va_list args;
+
+       va_start(args, key);
+       section = find_section(this, this->top, key, args);
+       va_end(args);
+
+       if (!section)
+       {
+               return enumerator_create_empty();
+       }
+       this->lock->read_lock(this->lock);
+       return enumerator_create_filter(
+                               section->sections->create_enumerator(section->sections),
+                               (void*)section_filter, this->lock, (void*)this->lock->unlock);
+}
+
+/**
+ * Enumerate key and values, not kv_t entries
+ */
+static bool kv_filter(void *null, kv_t **in, char **key,
+                                         void *none, char **value)
+{
+       *key = (*in)->key;
+       *value = (*in)->value;
+       return TRUE;
+}
+
+METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
+          private_settings_t *this, char *key, ...)
+{
+       section_t *section;
+       va_list args;
+
+       va_start(args, key);
+       section = find_section(this, this->top, key, args);
+       va_end(args);
+
+       if (!section)
+       {
+               return enumerator_create_empty();
+       }
+       this->lock->read_lock(this->lock);
+       return enumerator_create_filter(
+                                       section->kv->create_enumerator(section->kv),
+                                       (void*)kv_filter, this->lock, (void*)this->lock->unlock);
+}
+
+/**
+ * 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 (section->sections->find_first(section->sections,
+                                                                                       (linked_list_match_t)section_find,
+                                                                                       (void**)&sub, key) != SUCCESS)
+                                       {
+                                               sub = section_create(key);
+                                               if (parse_section(contents, file, level, &inner, sub))
+                                               {
+                                                       section->sections->insert_last(section->sections,
+                                                                                                                  sub);
+                                                       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 (section->kv->find_first(section->kv,
+                                                               (linked_list_match_t)kv_find,
+                                                               (void**)&kv, key) != SUCCESS)
+                                       {
+                                               kv = kv_create(key, value);
+                                               section->kv->insert_last(section->kv, kv);
+                                       }
+                                       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)
+               {
+                       DBG2(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 + 1);
+       text[len] = '\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))
+       {
+               DBG2(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 = strdup(file);
+               dir = dirname(dir);
+               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)
+               {
+                       DBG2(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 = extension->sections->create_enumerator(extension->sections);
+       while (enumerator->enumerate(enumerator, (void**)&sec))
+       {
+               section_t *found;
+               if (base->sections->find_first(base->sections,
+                                       (linked_list_match_t)section_find, (void**)&found,
+                                       sec->name) == SUCCESS)
+               {
+                       section_extend(found, sec);
+               }
+               else
+               {
+                       extension->sections->remove_at(extension->sections, enumerator);
+                       base->sections->insert_last(base->sections, sec);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = extension->kv->create_enumerator(extension->kv);
+       while (enumerator->enumerate(enumerator, (void**)&kv))
+       {
+               kv_t *found;
+               if (base->kv->find_first(base->kv, (linked_list_match_t)kv_find,
+                                       (void**)&found, kv->key) == SUCCESS)
+               {
+                       found->value = kv->value;
+               }
+               else
+               {
+                       extension->kv->remove_at(extension->kv, enumerator);
+                       base->kv->insert_last(base->kv, kv);
+               }
+       }
+       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,
+                       .create_section_enumerator = _create_section_enumerator,
+                       .create_key_value_enumerator = _create_key_value_enumerator,
+                       .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
new file mode 100644 (file)
index 0000000..ae4e719
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * 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, ...);
+
+       /**
+        * 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, ...);
+
+       /**
+        * 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_ @}*/