diff options
Diffstat (limited to 'src/libstrongswan/settings.c')
-rw-r--r-- | src/libstrongswan/settings.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/libstrongswan/settings.c b/src/libstrongswan/settings.c new file mode 100644 index 000000000..7c87dccd8 --- /dev/null +++ b/src/libstrongswan/settings.c @@ -0,0 +1,411 @@ +/* + * 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. + * + * $Id$ + */ + +#define _GNU_SOURCE +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "settings.h" + +#include <debug.h> +#include <utils/linked_list.h> + + +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; + + /** + * allocated file text + */ + char *text; +}; + +/** + * 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; +}; + +static char *find(section_t *section, char *key) +{ + char *name, *pos, *value = NULL; + enumerator_t *enumerator; + kv_t *kv; + section_t *current, *found = NULL; + + if (section == NULL) + { + return NULL; + } + + name = strdupa(key); + + pos = strchr(name, '.'); + if (pos) + { + *pos = '\0'; + pos++; + enumerator = section->sections->create_enumerator(section->sections); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->name, name)) + { + found = current; + break; + } + } + enumerator->destroy(enumerator); + if (found) + { + return find(found, pos); + } + } + else + { + enumerator = section->kv->create_enumerator(section->kv); + while (enumerator->enumerate(enumerator, &kv)) + { + if (streq(kv->key, name)) + { + value = kv->value; + break; + } + } + enumerator->destroy(enumerator); + } + return value; +} + +/** + * Implementation of settings_t.get. + */ +static char* get_str(private_settings_t *this, char *key, char *def) +{ + char *value; + + value = find(this->top, key); + if (value) + { + return value; + } + return def; +} + +/** + * Implementation of settings_t.get_bool. + */ +static bool get_bool(private_settings_t *this, char *key, bool def) +{ + char *value; + + value = find(this->top, key); + if (value) + { + if (strcasecmp(value, "true") == 0 || + strcasecmp(value, "enabled") == 0 || + strcasecmp(value, "yes") == 0 || + strcasecmp(value, "1") == 0) + { + return TRUE; + } + else if (strcasecmp(value, "false") == 0 || + strcasecmp(value, "disabled") == 0 || + strcasecmp(value, "no") == 0 || + strcasecmp(value, "0") == 0) + { + return FALSE; + } + } + return def; +} + +/** + * Implementation of settings_t.get_int. + */ +static int get_int(private_settings_t *this, char *key, int def) +{ + char *value; + int intval; + + value = find(this->top, key); + if (value) + { + errno = 0; + intval = strtol(value, NULL, 10); + if (errno == 0) + { + return intval; + } + } + return def; +} + +/** + * destry a section +*/ +static void section_destroy(section_t *this) +{ + this->kv->destroy_function(this->kv, free); + this->sections->destroy_function(this->sections, (void*)section_destroy); + + free(this); +} + +/** + * 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; +} + +/** + * Parse a section + */ +static section_t* parse_section(char **text, char *name) +{ + section_t *sub, *section; + bool finished = FALSE; + char *key, *value, *inner; + + static int lev = 0; + lev++; + + section = malloc_thing(section_t); + section->name = name; + section->sections = linked_list_create(); + section->kv = linked_list_create(); + + while (!finished) + { + switch (parse(text, "\t\n ", "{=#", NULL, &key)) + { + case '{': + if (parse(text, "\t ", "}", "{", &inner)) + { + sub = parse_section(&inner, key); + if (sub) + { + section->sections->insert_last(section->sections, sub); + continue; + } + } + DBG1("matching '}' not found near %s", *text); + break; + case '=': + if (parse(text, "\t ", "\n", NULL, &value)) + { + kv_t *kv = malloc_thing(kv_t); + kv->key = key; + kv->value = value; + section->kv->insert_last(section->kv, kv); + continue; + } + DBG1("parsing value failed near %s", *text); + break; + case '#': + parse(text, "", "\n", NULL, &value); + continue; + default: + finished = TRUE; + continue; + } + section_destroy(section); + return NULL; + } + return section; +} + +/** + * Implementation of settings_t.destroy + */ +static void destroy(private_settings_t *this) +{ + if (this->top) + { + section_destroy(this->top); + } + free(this->text); + free(this); +} + +/* + * see header file + */ +settings_t *settings_create(char *file) +{ + private_settings_t *this = malloc_thing(private_settings_t); + + this->public.get_str = (char*(*)(settings_t*, char *key, char* def))get_str; + this->public.get_int = (int(*)(settings_t*, char *key, bool def))get_int; + this->public.get_bool = (bool(*)(settings_t*, char *key, bool def))get_bool; + this->public.destroy = (void(*)(settings_t*))destroy; + + this->top = NULL; + this->text = NULL; + + if (file) + { + FILE *fd; + int len; + char *pos; + + fd = fopen(file, "r"); + if (fd == NULL) + { + DBG1("'%s' does not exist or is not readable", file); + return &this->public; + } + fseek(fd, 0, SEEK_END); + len = ftell(fd); + rewind(fd); + this->text = malloc(len + 1); + this->text[len] = '\0'; + if (fread(this->text, 1, len, fd) != len) + { + free(this->text); + this->text = NULL; + return &this->public; + } + fclose(fd); + + pos = this->text; + this->top = parse_section(&pos, NULL); + if (this->top == NULL) + { + free(this->text); + this->text = NULL; + return &this->public; + } + } + return &this->public; +} + |