/* Skippy - Seduces Kids Into Perversion
*
* Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "skippy.h"
typedef struct
{
char *section, *key, *value;
} ConfigEntry;
static char *
copy_match(const char *line, regmatch_t *match)
{
char *r;
r = (char *)malloc(match->rm_eo + 1);
strncpy(r, line + match->rm_so, match->rm_eo - match->rm_so);
r[match->rm_eo - match->rm_so] = 0;
return r;
}
static ConfigEntry *
entry_new(const char *section, char *key, char *value)
{
ConfigEntry *e = (ConfigEntry *)malloc(sizeof(ConfigEntry));
e->section = strdup(section);
e->key = key;
e->value = value;
return e;
}
static dlist *
entry_set(dlist *config, const char *section, char *key, char *value)
{
dlist *iter = dlist_first(config);
ConfigEntry *entry;
for(; iter; iter = iter->next)
{
entry = (ConfigEntry *)iter->data;
if(! strcasecmp(entry->section, section) && ! strcasecmp(entry->key, key)) {
free(key);
free(entry->value);
entry->value = value;
return config;
}
}
entry = entry_new(section, key, value);
return dlist_add(config, entry);
}
static dlist *
config_parse(char *config)
{
regex_t re_section, re_empty, re_entry;
regmatch_t matches[5];
char line[8192], *section = 0;
int ix = 0, l_ix = 0;
dlist *new_config = 0;
regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED);
regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED);
regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", REG_EXTENDED);
while(1)
{
if((config[ix] == '\0' || config[ix] == '\n'))
{
line[l_ix] = 0;
if(regexec(&re_empty, line, 5, matches, 0) == 0) {
/* do nothing */
} else if(regexec(&re_section, line, 5, matches, 0) == 0) {
if(section)
free(section);
section = copy_match(line, &matches[1]);
} else if(section && regexec(&re_entry, line, 5, matches, 0) == 0) {
char *key = copy_match(line, &matches[1]),
*value = copy_match(line, &matches[2]);
new_config = entry_set(new_config, section, key, value);
} else {
fprintf(stderr, "WARNING: Ignoring invalid line: %s\n", line);
}
l_ix = 0;
} else {
line[l_ix] = config[ix];
l_ix++;
}
if(config[ix] == 0)
break;
++ix;
}
if(section)
free(section);
regfree(&re_section);
regfree(&re_empty);
regfree(&re_entry);
return new_config;
}
dlist *
config_load(const char *path)
{
FILE *fin = fopen(path, "r");
long flen;
char *data;
dlist *config;
if(! fin)
{
fprintf(stderr, "WARNING: Couldn't load config file '%s'.\n", path);
return 0;
}
fseek(fin, 0, SEEK_END);
flen = ftell(fin);
if(! flen)
{
fprintf(stderr, "WARNING: '%s' is empty.\n", path);
fclose(fin);
return 0;
}
fseek(fin, 0, SEEK_SET);
data = (char *)malloc(flen + 1);
if(fread(data, 1, flen, fin) != flen)
{
fprintf(stderr, "WARNING: Couldn't read from config file '%s'.\n", path);
free(data);
fclose(fin);
return 0;
}
data[flen] = '\0';
fclose(fin);
config = config_parse(data);
free(data);
return config;
}
static void
entry_free(ConfigEntry *entry)
{
free(entry->section);
free(entry->key);
free(entry->value);
free(entry);
}
void
config_free(dlist *config)
{
dlist_free_with_func(config, (dlist_free_func)entry_free);
}
static int
entry_find_func(dlist *l, ConfigEntry *key)
{
ConfigEntry *entry = (ConfigEntry*)l->data;
return ! (strcasecmp(entry->section, key->section) || strcasecmp(entry->key, key->key));
}
const char *
config_get(dlist *config, const char *section, const char *key, const char *def)
{
ConfigEntry needle;
dlist *iter;
needle.section = (char *)section;
needle.key = (char *)key;
iter = dlist_find(dlist_first(config), (dlist_match_func)entry_find_func, &needle);
if(! iter)
return def;
return ((ConfigEntry*)iter->data)->value;
}
syntax highlighted by Code2HTML, v. 0.9.1