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