1 |
/* |
2 |
* confparse.C: Parsing of the configuration file. |
3 |
* |
4 |
* Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team |
5 |
* Rights to this code are as documented in COPYING. |
6 |
* |
7 |
* |
8 |
* Portions of this file were derived from sources bearing the following license: |
9 |
* Rights to this code are documented in doc/pod/license.pod. |
10 |
* Copyright © 2005-2007 Atheme Project (http://www.atheme.org) |
11 |
*/ |
12 |
|
13 |
static char const rcsid[] = "$Id: confparse.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $"; |
14 |
|
15 |
#include "atheme.h" |
16 |
#include "confparse.h" |
17 |
|
18 |
static void |
19 |
config_entry_free (config_entry_t *ceptr) |
20 |
{ |
21 |
config_entry_t *nptr; |
22 |
|
23 |
for (; ceptr; ceptr = nptr) |
24 |
{ |
25 |
nptr = ceptr->ce_next; |
26 |
if (ceptr->ce_entries) |
27 |
config_entry_free (ceptr->ce_entries); |
28 |
if (ceptr->ce_varname) |
29 |
sfree (ceptr->ce_varname); |
30 |
if (ceptr->ce_vardata) |
31 |
sfree (ceptr->ce_vardata); |
32 |
delete ceptr; |
33 |
} |
34 |
} |
35 |
|
36 |
void |
37 |
config_free (config_file_t *cfptr) |
38 |
{ |
39 |
config_file_t *nptr; |
40 |
|
41 |
for (; cfptr; cfptr = nptr) |
42 |
{ |
43 |
nptr = cfptr->cf_next; |
44 |
if (cfptr->cf_entries) |
45 |
config_entry_free (cfptr->cf_entries); |
46 |
if (cfptr->cf_filename) |
47 |
sfree (cfptr->cf_filename); |
48 |
delete cfptr; |
49 |
} |
50 |
} |
51 |
|
52 |
|
53 |
static void |
54 |
config_error (char const * const format, ...) |
55 |
{ |
56 |
va_list ap; |
57 |
char buffer[1024]; |
58 |
char *ptr; |
59 |
|
60 |
va_start (ap, format); |
61 |
vsnprintf (buffer, 1024, format, ap); |
62 |
va_end (ap); |
63 |
if ((ptr = strchr (buffer, '\n')) != NULL) |
64 |
*ptr = '\0'; |
65 |
slog (LG_ERROR, "config_parse(): %s", buffer); |
66 |
} |
67 |
|
68 |
static config_file_t * |
69 |
config_parse (char *filename, char *confdata) |
70 |
{ |
71 |
char *ptr; |
72 |
char *start; |
73 |
int linenumber = 1; |
74 |
config_entry_t *curce; |
75 |
config_entry_t **lastce; |
76 |
config_entry_t *cursection; |
77 |
|
78 |
config_file_t *curcf; |
79 |
config_file_t *lastcf; |
80 |
|
81 |
lastcf = curcf = new config_file_t; |
82 |
curcf->cf_filename = sstrdup (filename); |
83 |
lastce = &(curcf->cf_entries); |
84 |
curce = NULL; |
85 |
cursection = NULL; |
86 |
|
87 |
for (ptr = confdata; *ptr; ptr++) |
88 |
{ |
89 |
switch (*ptr) |
90 |
{ |
91 |
case '#': |
92 |
while (*++ptr && (*ptr != '\n')) |
93 |
; |
94 |
if (!*ptr) |
95 |
{ |
96 |
/* make for(;;) exit from the loop */ |
97 |
ptr--; |
98 |
continue; |
99 |
} |
100 |
linenumber++; |
101 |
break; |
102 |
case ';': |
103 |
if (!curce) |
104 |
{ |
105 |
config_error ("%s:%i Ignoring extra semicolon\n", filename, linenumber); |
106 |
break; |
107 |
} |
108 |
if (!strcmp (curce->ce_varname, "include")) |
109 |
{ |
110 |
config_file_t *cfptr; |
111 |
|
112 |
if (!curce->ce_vardata) |
113 |
{ |
114 |
config_error ("%s:%i Ignoring \"include\": No filename given\n", filename, linenumber); |
115 |
config_entry_free (curce); |
116 |
curce = NULL; |
117 |
continue; |
118 |
} |
119 |
if (strlen (curce->ce_vardata) > 255) |
120 |
curce->ce_vardata[255] = '\0'; |
121 |
cfptr = config_load (curce->ce_vardata); |
122 |
if (cfptr) |
123 |
{ |
124 |
lastcf->cf_next = cfptr; |
125 |
lastcf = cfptr; |
126 |
} |
127 |
config_entry_free (curce); |
128 |
curce = NULL; |
129 |
continue; |
130 |
} |
131 |
*lastce = curce; |
132 |
lastce = &(curce->ce_next); |
133 |
curce->ce_fileposend = (ptr - confdata); |
134 |
curce = NULL; |
135 |
break; |
136 |
case '{': |
137 |
if (!curce) |
138 |
{ |
139 |
config_error ("%s:%i: No name for section start\n", filename, linenumber); |
140 |
continue; |
141 |
} |
142 |
else if (curce->ce_entries) |
143 |
{ |
144 |
config_error ("%s:%i: Ignoring extra section start\n", filename, linenumber); |
145 |
continue; |
146 |
} |
147 |
curce->ce_sectlinenum = linenumber; |
148 |
lastce = &(curce->ce_entries); |
149 |
cursection = curce; |
150 |
curce = NULL; |
151 |
break; |
152 |
case '}': |
153 |
if (curce) |
154 |
{ |
155 |
config_error ("%s:%i: Missing semicolon before close brace\n", filename, linenumber); |
156 |
config_entry_free (curce); |
157 |
config_free (curcf); |
158 |
return NULL; |
159 |
} |
160 |
else if (!cursection) |
161 |
{ |
162 |
config_error ("%s:%i: Ignoring extra close brace\n", filename, linenumber); |
163 |
continue; |
164 |
} |
165 |
curce = cursection; |
166 |
cursection->ce_fileposend = (ptr - confdata); |
167 |
cursection = cursection->ce_prevlevel; |
168 |
if (!cursection) |
169 |
lastce = &(curcf->cf_entries); |
170 |
else |
171 |
lastce = &(cursection->ce_entries); |
172 |
for (; *lastce; lastce = &((*lastce)->ce_next)) |
173 |
continue; |
174 |
break; |
175 |
case '/': |
176 |
if (*(ptr + 1) == '/') |
177 |
{ |
178 |
ptr += 2; |
179 |
while (*ptr && (*ptr != '\n')) |
180 |
ptr++; |
181 |
if (!*ptr) |
182 |
break; |
183 |
ptr--; /* grab the \n on next loop thru */ |
184 |
continue; |
185 |
} |
186 |
else if (*(ptr + 1) == '*') |
187 |
{ |
188 |
int commentstart = linenumber; |
189 |
|
190 |
for (ptr += 2; *ptr; ptr++) |
191 |
{ |
192 |
if ((*ptr == '*') && (*(ptr + 1) == '/')) |
193 |
{ |
194 |
ptr++; |
195 |
break; |
196 |
} |
197 |
else if (*ptr == '\n') |
198 |
linenumber++; |
199 |
} |
200 |
if (!*ptr) |
201 |
{ |
202 |
config_error ("%s:%i Comment on this line does not end\n", filename, commentstart); |
203 |
config_entry_free (curce); |
204 |
config_free (curcf); |
205 |
return NULL; |
206 |
} |
207 |
} |
208 |
break; |
209 |
case '\"': |
210 |
start = ++ptr; |
211 |
for (; *ptr; ptr++) |
212 |
{ |
213 |
if ((*ptr == '\\') && (*(ptr + 1) == '\"')) |
214 |
{ |
215 |
char *tptr = ptr; |
216 |
while ((*tptr = *(tptr + 1))) |
217 |
tptr++; |
218 |
} |
219 |
else if ((*ptr == '\"') || (*ptr == '\n')) |
220 |
break; |
221 |
} |
222 |
if (!*ptr || (*ptr == '\n')) |
223 |
{ |
224 |
config_error ("%s:%i: Unterminated quote found\n", filename, linenumber); |
225 |
config_entry_free (curce); |
226 |
config_free (curcf); |
227 |
return NULL; |
228 |
} |
229 |
if (curce) |
230 |
{ |
231 |
if (curce->ce_vardata) |
232 |
config_error ("%s:%i: Ignoring extra data\n", filename, linenumber); |
233 |
else |
234 |
{ |
235 |
char *eptr; |
236 |
|
237 |
curce->ce_vardata = salloc<char> (ptr - start + 1); |
238 |
strlcpy (curce->ce_vardata, start, ptr - start + 1); |
239 |
curce->ce_vardatanum = strtol (curce->ce_vardata, &eptr, 0) & 0xffffffff; /* we only want 32bits and long is 64bit on 64bit compiles */ |
240 |
if (eptr != (curce->ce_vardata + (ptr - start))) |
241 |
curce->ce_vardatanum = 0; |
242 |
} |
243 |
} |
244 |
else |
245 |
{ |
246 |
curce = new config_entry_t; |
247 |
curce->ce_varname = salloc<char> (ptr - start + 1); |
248 |
strlcpy (curce->ce_varname, start, ptr - start + 1); |
249 |
curce->ce_varlinenum = linenumber; |
250 |
curce->ce_fileptr = curcf; |
251 |
curce->ce_prevlevel = cursection; |
252 |
curce->ce_fileposstart = (start - confdata); |
253 |
} |
254 |
break; |
255 |
case '\n': |
256 |
linenumber++; |
257 |
break; |
258 |
case '\t': |
259 |
case ' ': |
260 |
case '=': |
261 |
case '\r': |
262 |
break; |
263 |
default: |
264 |
if ((*ptr == '*') && (*(ptr + 1) == '/')) |
265 |
{ |
266 |
config_error ("%s:%i Ignoring extra end comment\n", filename, linenumber); |
267 |
ptr++; |
268 |
break; |
269 |
} |
270 |
start = ptr; |
271 |
for (; *ptr; ptr++) |
272 |
{ |
273 |
if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n') || (*ptr == ';')) |
274 |
break; |
275 |
} |
276 |
if (!*ptr) |
277 |
{ |
278 |
if (curce) |
279 |
config_error ("%s: Unexpected EOF for variable starting at %i\n", filename, curce->ce_varlinenum); |
280 |
else if (cursection) |
281 |
config_error ("%s: Unexpected EOF for section starting at %i\n", filename, curce->ce_sectlinenum); |
282 |
else |
283 |
config_error ("%s: Unexpected EOF.\n", filename); |
284 |
config_entry_free (curce); |
285 |
config_free (curcf); |
286 |
return NULL; |
287 |
} |
288 |
if (curce) |
289 |
{ |
290 |
if (curce->ce_vardata) |
291 |
{ |
292 |
config_error ("%s:%i: Ignoring extra data\n", filename, linenumber); |
293 |
} |
294 |
else |
295 |
{ |
296 |
char *eptr; |
297 |
|
298 |
curce->ce_vardata = salloc<char> (ptr - start + 1); |
299 |
strlcpy (curce->ce_vardata, start, ptr - start + 1); |
300 |
curce->ce_vardatanum = strtol (curce->ce_vardata, &eptr, 0) & 0xffffffff; /* we only want 32bits and long is 64bit on 64bit compiles */ |
301 |
if (eptr != (curce->ce_vardata + (ptr - start))) |
302 |
{ |
303 |
curce->ce_vardatanum = 0; |
304 |
} |
305 |
} |
306 |
} |
307 |
else |
308 |
{ |
309 |
curce = new config_entry_t; |
310 |
curce->ce_varname = salloc<char> (ptr - start + 1); |
311 |
strlcpy (curce->ce_varname, start, ptr - start + 1); |
312 |
curce->ce_varlinenum = linenumber; |
313 |
curce->ce_fileptr = curcf; |
314 |
curce->ce_prevlevel = cursection; |
315 |
curce->ce_fileposstart = (start - confdata); |
316 |
} |
317 |
if ((*ptr == ';') || (*ptr == '\n')) |
318 |
ptr--; |
319 |
break; |
320 |
} /* switch */ |
321 |
} /* for */ |
322 |
if (curce) |
323 |
{ |
324 |
config_error ("%s: Unexpected EOF for variable starting on line %i\n", filename, curce->ce_varlinenum); |
325 |
config_entry_free (curce); |
326 |
config_free (curcf); |
327 |
return NULL; |
328 |
} |
329 |
else if (cursection) |
330 |
{ |
331 |
config_error ("%s: Unexpected EOF for section starting on line %i\n", filename, cursection->ce_sectlinenum); |
332 |
config_free (curcf); |
333 |
return NULL; |
334 |
} |
335 |
return curcf; |
336 |
} |
337 |
|
338 |
config_file_t * |
339 |
config_load (char *filename) |
340 |
{ |
341 |
struct stat sb; |
342 |
FILE *fd; |
343 |
int ret; |
344 |
char *buf = NULL; |
345 |
config_file_t *cfptr; |
346 |
|
347 |
fd = fopen (filename, "rb"); |
348 |
if (!fd) |
349 |
{ |
350 |
config_error ("Couldn't open \"%s\": %s\n", filename, strerror (errno)); |
351 |
return NULL; |
352 |
} |
353 |
if (stat (filename, &sb) == -1) |
354 |
{ |
355 |
config_error ("Couldn't fstat \"%s\": %s\n", filename, strerror (errno)); |
356 |
fclose (fd); |
357 |
return NULL; |
358 |
} |
359 |
if (!sb.st_size) |
360 |
{ |
361 |
fclose (fd); |
362 |
return NULL; |
363 |
} |
364 |
buf = salloc<char> (sb.st_size + 1); |
365 |
if (buf == NULL) |
366 |
{ |
367 |
config_error ("Out of memory trying to load \"%s\"\n", filename); |
368 |
fclose (fd); |
369 |
return NULL; |
370 |
} |
371 |
ret = fread (buf, 1, sb.st_size, fd); |
372 |
if (ret != sb.st_size) |
373 |
{ |
374 |
config_error ("Error reading \"%s\": %s\n", filename, ret == -1 ? strerror (errno) : strerror (EFAULT)); |
375 |
sfree (buf); |
376 |
fclose (fd); |
377 |
return NULL; |
378 |
} |
379 |
buf[ret] = '\0'; |
380 |
fclose (fd); |
381 |
cfptr = config_parse (filename, buf); |
382 |
sfree (buf); |
383 |
return cfptr; |
384 |
} |
385 |
|
386 |
config_entry_t * |
387 |
config_find (config_entry_t *ceptr, char *name) |
388 |
{ |
389 |
for (; ceptr; ceptr = ceptr->ce_next) |
390 |
if (!strcmp (ceptr->ce_varname, name)) |
391 |
break; |
392 |
return ceptr; |
393 |
} |
394 |
|
395 |
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs |
396 |
* vim:ts=8 |
397 |
* vim:sw=8 |
398 |
* vim:noexpandtab |
399 |
*/ |