/* Skippy - Seduces Kids Into Perversion * * Copyright (C) 2004 Hyriand * * 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; }