tsst
[skippy-xd] / config.c
1 /* Skippy - Seduces Kids Into Perversion
2  *
3  * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include "skippy.h"
21
22 typedef struct
23 {
24         char *section, *key, *value;
25 } ConfigEntry;
26
27 static char *
28 copy_match(const char *line, regmatch_t *match)
29 {
30         char *r;
31         r = (char *)malloc(match->rm_eo + 1);
32         strncpy(r, line + match->rm_so, match->rm_eo - match->rm_so);
33         r[match->rm_eo - match->rm_so] = 0;
34         return r;
35 }
36
37 static ConfigEntry *
38 entry_new(const char *section, char *key, char *value)
39 {
40         ConfigEntry *e = (ConfigEntry *)malloc(sizeof(ConfigEntry));
41         e->section = strdup(section);
42         e->key = key;
43         e->value = value;
44         return e;
45 }
46
47 static dlist *
48 entry_set(dlist *config, const char *section, char *key, char *value)
49 {
50         dlist *iter = dlist_first(config);
51         ConfigEntry *entry;
52         for(; iter; iter = iter->next)
53         {
54                 entry = (ConfigEntry *)iter->data;
55                 if(! strcasecmp(entry->section, section) && ! strcasecmp(entry->key, key)) {
56                         free(key);
57                         free(entry->value);
58                         entry->value = value;
59                         return config;
60                 }
61         }
62         entry = entry_new(section, key, value);
63         return dlist_add(config, entry);
64 }
65
66 static dlist *
67 config_parse(char *config)
68 {
69         regex_t re_section, re_empty, re_entry;
70         regmatch_t matches[5];
71         char line[8192], *section = 0;
72         int ix = 0, l_ix = 0;
73         dlist *new_config = 0;
74         
75         regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*?)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED);
76         regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED);
77         regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*?)[[:space:]]*$", REG_EXTENDED);
78         
79         while(1)
80         {
81                 if((config[ix] == '\0' || config[ix] == '\n'))
82                 {
83                         line[l_ix] = 0;
84                         if(regexec(&re_empty, line, 5, matches, 0) == 0) {
85                                 /* do nothing */
86                         } else if(regexec(&re_section, line, 5, matches, 0) == 0) {
87                                 if(section)
88                                         free(section);
89                                 section = copy_match(line, &matches[1]);
90                         } else if(section && regexec(&re_entry, line, 5, matches, 0) == 0) {
91                                 char *key = copy_match(line, &matches[1]),
92                                      *value = copy_match(line, &matches[2]);
93                                 new_config = entry_set(new_config, section, key, value);
94                         } else  {
95                                 fprintf(stderr, "WARNING: Ignoring invalid line: %s\n", line);
96                         }
97                         l_ix = 0;
98                 } else {
99                         line[l_ix] = config[ix];
100                         l_ix++;
101                 }
102                 if(config[ix] == 0)
103                         break;
104                 ++ix;
105         }
106         
107         if(section)
108                 free(section);
109         
110         regfree(&re_section);
111         regfree(&re_empty);
112         regfree(&re_entry);
113         
114         return new_config;
115 }
116
117 dlist *
118 config_load(const char *path)
119 {
120         FILE *fin = fopen(path, "r");
121         long flen;
122         char *data;
123         dlist *config;
124         
125         if(! fin)
126         {
127                 fprintf(stderr, "WARNING: Couldn't load config file '%s'.\n", path);
128                 return 0;
129         }
130         
131         fseek(fin, 0, SEEK_END);
132         flen = ftell(fin);
133         
134         if(! flen)
135         {
136                 fprintf(stderr, "WARNING: '%s' is empty.\n", path);
137                 fclose(fin);
138                 return 0;
139         }
140         
141         fseek(fin, 0, SEEK_SET);
142         
143         data = (char *)malloc(flen + 1);
144         if(fread(data, 1, flen, fin) != flen)
145         {
146                 fprintf(stderr, "WARNING: Couldn't read from config file '%s'.\n", path);
147                 free(data);
148                 fclose(fin);
149                 return 0;
150         }
151         
152         fclose(fin);
153         
154         config = config_parse(data);
155         
156         free(data);
157         
158         return config;
159 }
160
161 static void
162 entry_free(ConfigEntry *entry)
163 {
164         free(entry->section);
165         free(entry->key);
166         free(entry->value);
167         free(entry);
168 }
169
170 void
171 config_free(dlist *config)
172 {
173         dlist_free_with_func(config, (dlist_free_func)entry_free);
174 }
175
176 static int
177 entry_find_func(dlist *l, ConfigEntry *key)
178 {
179         ConfigEntry *entry = (ConfigEntry*)l->data;
180         return ! (strcasecmp(entry->section, key->section) || strcasecmp(entry->key, key->key));
181 }
182
183 const char *
184 config_get(dlist *config, const char *section, const char *key, const char *def)
185 {
186         ConfigEntry needle;
187         dlist *iter;
188         
189         needle.section = (char *)section;
190         needle.key = (char *)key;
191         
192         iter = dlist_find(dlist_first(config), (dlist_match_func)entry_find_func, &needle);
193         
194         if(! iter)
195                 return def;
196         
197         return ((ConfigEntry*)iter->data)->value;
198 }