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 #include <string.h>
22 #include <regex.h>
23
24 typedef struct
25 {
26 char *section, *key, *value;
27 } ConfigEntry;
28
29 static char *
copy_match(const char * line,regmatch_t * match)30 copy_match(const char *line, regmatch_t *match)
31 {
32 char *r;
33 r = (char *)malloc(match->rm_eo + 1);
34 strncpy(r, line + match->rm_so, match->rm_eo - match->rm_so);
35 r[match->rm_eo - match->rm_so] = 0;
36 return r;
37 }
38
39 static ConfigEntry *
entry_new(const char * section,char * key,char * value)40 entry_new(const char *section, char *key, char *value)
41 {
42 ConfigEntry *e = (ConfigEntry *)malloc(sizeof(ConfigEntry));
43 e->section = strdup(section);
44 e->key = key;
45 e->value = value;
46 return e;
47 }
48
49 static dlist *
entry_set(dlist * config,const char * section,char * key,char * value)50 entry_set(dlist *config, const char *section, char *key, char *value)
51 {
52 dlist *iter = dlist_first(config);
53 ConfigEntry *entry;
54 for(; iter; iter = iter->next)
55 {
56 entry = (ConfigEntry *)iter->data;
57 if(! strcasecmp(entry->section, section) && ! strcasecmp(entry->key, key)) {
58 free(key);
59 free(entry->value);
60 entry->value = value;
61 return config;
62 }
63 }
64 entry = entry_new(section, key, value);
65 return dlist_add(config, entry);
66 }
67
68 static dlist *
config_parse(const char * config)69 config_parse(const char *config) {
70 regex_t re_section, re_empty, re_entry;
71 regmatch_t matches[5];
72 char line[8192], *section = 0;
73 int ix = 0, l_ix = 0;
74 dlist *new_config = 0;
75
76 regcomp(&re_section, "^[[:space:]]*\\[[[:space:]]*([[:alnum:]]*)[[:space:]]*\\][[:space:]]*$", REG_EXTENDED);
77 regcomp(&re_empty, "^[[:space:]]*#|^[[:space:]]*$", REG_EXTENDED);
78 regcomp(&re_entry, "^[[:space:]]*([[:alnum:]]+)[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", REG_EXTENDED);
79
80 while(1)
81 {
82 // strchr() considers '\0' at the end of the string as well
83 if (strchr("\n\r", config[ix])) {
84 line[l_ix] = 0;
85 if(regexec(&re_empty, line, 5, matches, 0) == 0) {
86 /* do nothing */
87 } else if(regexec(&re_section, line, 5, matches, 0) == 0) {
88 if(section)
89 free(section);
90 section = copy_match(line, &matches[1]);
91 } else if(section && regexec(&re_entry, line, 5, matches, 0) == 0) {
92 char *key = copy_match(line, &matches[1]),
93 *value = copy_match(line, &matches[2]);
94 new_config = entry_set(new_config, section, key, value);
95 } else {
96 fprintf(stderr, "WARNING: Ignoring invalid line: %s\n", line);
97 }
98 l_ix = 0;
99 } else {
100 line[l_ix] = config[ix];
101 l_ix++;
102 }
103 if (!config[ix])
104 break;
105 ++ix;
106 }
107
108 if(section)
109 free(section);
110
111 regfree(&re_section);
112 regfree(&re_empty);
113 regfree(&re_entry);
114
115 return new_config;
116 }
117
118 dlist *
config_load(const char * path)119 config_load(const char *path)
120 {
121 FILE *fin = fopen(path, "r");
122 long flen;
123 char *data;
124 dlist *config;
125
126 if(! fin)
127 {
128 fprintf(stderr, "WARNING: Couldn't load config file '%s'.\n", path);
129 return 0;
130 }
131
132 fseek(fin, 0, SEEK_END);
133 flen = ftell(fin);
134
135 if(! flen)
136 {
137 fprintf(stderr, "WARNING: '%s' is empty.\n", path);
138 fclose(fin);
139 return 0;
140 }
141
142 fseek(fin, 0, SEEK_SET);
143
144 data = allocchk(malloc(flen + 1));
145 data[flen] = '\0';
146 if(fread(data, 1, flen, fin) != flen)
147 {
148 fprintf(stderr, "WARNING: Couldn't read from config file '%s'.\n", path);
149 free(data);
150 fclose(fin);
151 return 0;
152 }
153 data[flen] = '\0';
154
155 fclose(fin);
156
157 config = config_parse(data);
158
159 free(data);
160
161 return config;
162 }
163
164 static void
entry_free(ConfigEntry * entry)165 entry_free(ConfigEntry *entry)
166 {
167 free(entry->section);
168 free(entry->key);
169 free(entry->value);
170 free(entry);
171 }
172
173 void
config_free(dlist * config)174 config_free(dlist *config)
175 {
176 dlist_free_with_func(config, (dlist_free_func)entry_free);
177 }
178
179 static int
entry_find_func(dlist * l,ConfigEntry * key)180 entry_find_func(dlist *l, ConfigEntry *key)
181 {
182 ConfigEntry *entry = (ConfigEntry*)l->data;
183 return ! (strcasecmp(entry->section, key->section) || strcasecmp(entry->key, key->key));
184 }
185
186 const char *
config_get(dlist * config,const char * section,const char * key,const char * def)187 config_get(dlist *config, const char *section, const char *key, const char *def)
188 {
189 ConfigEntry needle;
190 dlist *iter;
191
192 needle.section = (char *)section;
193 needle.key = (char *)key;
194
195 iter = dlist_find(dlist_first(config), (dlist_match_func)entry_find_func, &needle);
196
197 if(! iter)
198 return def;
199
200 return ((ConfigEntry*)iter->data)->value;
201 }
202