Merge branch 'development'
[NeonServV5.git] / src / ConfigParser.c
1 /* ConfigParser.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "ConfigParser.h"
19 #include "tools.h"
20
21 #define ENTRYTYPE_BLOCK   1
22 #define ENTRYTYPE_STRING  2
23 #define ENTRYTYPE_INTEGER 3
24
25 struct ConfigEntry {
26     char *name;
27     int type;
28     void *value;
29     struct ConfigEntry *next;
30 };
31
32 static struct ConfigEntry *root_entry = NULL;
33
34 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element);
35 static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry);
36
37 int loadConfig(const char *filename) {
38     FILE *f;
39     f = fopen(filename, "rb");
40     if(!f) return 0;
41     
42     // obtain file size:
43     long lSize;
44     fseek (f, 0, SEEK_END);
45     lSize = ftell(f);
46     rewind(f);
47     
48     // allocate memory to contain the whole file:
49     char *buffer = (char*) malloc (sizeof(char)*lSize + 1);
50     if (buffer == NULL) {
51         fclose(f);
52         return 0;
53     }
54     
55     // copy the file into the buffer:
56     size_t result = fread (buffer, 1, lSize, f);
57     if (result != lSize) {
58         fclose(f);
59         return 0;
60     }
61     buffer[lSize] = '\0';
62     
63     fclose(f);
64     
65     // now parse the config file...
66     if(root_entry) {
67         free_loaded_config();
68     }
69     root_entry = malloc(sizeof(*root_entry));
70     if (!root_entry) return 0;
71     parse_config_recursive(root_entry, buffer, 1);
72     
73     // free the buffer...
74     free(buffer);
75     return 1;
76 }
77
78 #define PARSER_FLAG_ESCAPED    0x01
79 #define PARSER_FLAG_BLOCK      0x02
80 #define PARSER_FLAG_STRING     0x04
81 #define PARSER_FLAG_EXPECT_END 0x08
82 #define PARSER_FLAG_NEXTCHAR   0x10
83 #define PARSER_FLAG_INTEGER    0x20
84 #define PARSER_FLAG_EOBN       0x40 /* End of Block name */
85 #define PARSER_FLAG_COMMAND    0x80
86 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element) {
87     int flags = 0;
88     int type = (root_element ? ENTRYTYPE_BLOCK : 0);
89     char cbuf[1024];
90     int cbufpos = 0;
91     struct ConfigEntry *sub_entrys = NULL;
92     while(*buffer) {
93         if(flags & PARSER_FLAG_NEXTCHAR) {
94             buffer++;
95             if(*buffer == '\0')
96                 break;
97         }
98         flags |= PARSER_FLAG_NEXTCHAR;
99         if(flags & PARSER_FLAG_EOBN) {
100             flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR);
101             struct ConfigEntry *new_entry = malloc(sizeof(*new_entry));
102             if (!new_entry) return buffer;
103             new_entry->name = strdup(cbuf);
104             buffer = parse_config_recursive(new_entry, buffer, 0);
105             if(sub_entrys)
106                 new_entry->next = sub_entrys;
107             else
108                 new_entry->next = NULL;
109             sub_entrys = new_entry;
110             centry->value = sub_entrys;
111         }
112         if(flags & PARSER_FLAG_ESCAPED) {
113             cbuf[cbufpos++] = *buffer;
114             flags &= ~PARSER_FLAG_ESCAPED;
115             continue;
116         }
117         if(flags & PARSER_FLAG_EXPECT_END) {
118             if(*buffer == ';') {
119                 centry->type = type;
120                 return (buffer+1);
121             }
122             continue;
123         }
124         if(flags & PARSER_FLAG_STRING) {
125             if(*buffer == '"') {
126                 cbuf[cbufpos] = '\0';
127                 flags &= ~PARSER_FLAG_STRING;
128                 if(type == ENTRYTYPE_STRING) {
129                     flags |= PARSER_FLAG_EXPECT_END;
130                     centry->value = strdup(cbuf);
131                 } else if(type == ENTRYTYPE_BLOCK) {
132                     flags |= PARSER_FLAG_EOBN;
133                 }
134             } else if(*buffer == '\\')
135                 flags |= PARSER_FLAG_ESCAPED;
136             else
137                 cbuf[cbufpos++] = *buffer;
138             continue;
139         }
140         if(flags & PARSER_FLAG_INTEGER) {
141             if(!isdigit(*buffer)) {
142                 cbuf[cbufpos] = '\0';
143                 flags &= ~PARSER_FLAG_INTEGER;
144                 if(type == ENTRYTYPE_INTEGER) {
145                     flags |= PARSER_FLAG_EXPECT_END;
146                     int *intbuf = malloc(sizeof(int));
147                     *intbuf = atoi(cbuf);
148                     centry->value = intbuf;
149                 }
150                 if(*buffer == ';') {
151                     centry->type = type;
152                     return (buffer+1);
153                 }
154             } else
155                 cbuf[cbufpos++] = *buffer;
156             continue;
157         }
158         if(flags & PARSER_FLAG_COMMAND) {
159             int found_command = 0;
160             char *tmp_buffer;
161             switch(*buffer) {
162                 case '/':
163                     tmp_buffer = buffer;
164                     buffer = strchr(buffer, '\r');
165                     if(!buffer)
166                         buffer = strchr(tmp_buffer, '\n');
167                     if(!buffer)
168                         buffer = tmp_buffer + strlen(tmp_buffer)-1;
169                     found_command = 1;
170                     break;
171                 case '*':
172                     //simple search for the next */
173                     buffer = strstr(buffer, "*/")+1;
174                     found_command = 1;
175             }
176             flags &= ~PARSER_FLAG_COMMAND;
177             if(found_command)
178                 continue;
179         }
180         switch(*buffer) {
181             case '\\':
182                 flags |= PARSER_FLAG_ESCAPED;
183                 break;
184             case '/':
185                 if(!(flags & PARSER_FLAG_STRING)) {
186                     flags |= PARSER_FLAG_COMMAND;
187                 }
188                 break;
189             case '{':
190                 flags |= PARSER_FLAG_BLOCK;
191                 type = ENTRYTYPE_BLOCK;
192                 break;
193             case '}':
194                 if(flags & PARSER_FLAG_BLOCK)
195                     flags &= ~PARSER_FLAG_BLOCK;
196                 flags |= PARSER_FLAG_EXPECT_END;
197                 break;
198             case '"':
199                 flags |= PARSER_FLAG_STRING;
200                 if(!type)
201                     type = ENTRYTYPE_STRING;
202                 cbufpos = 0;
203                 break;
204             case ';':
205                 centry->type = type;
206                 return (buffer+1);
207             case '0':
208             case '1':
209             case '2':
210             case '3':
211             case '4':
212             case '5':
213             case '6':
214             case '7':
215             case '8':
216             case '9':
217                 if(!type)
218                     type = ENTRYTYPE_INTEGER;
219                 flags |= PARSER_FLAG_INTEGER;
220                 cbufpos = 0;
221                 cbuf[cbufpos++] = *buffer;
222                 break;
223             default:
224                 break;
225         }
226     }
227     centry->type = type;
228     return buffer; //end of the buffer
229 }
230
231 int get_int_field(char *field_path) {
232     struct ConfigEntry *centry = root_entry;
233     char *a, *b = field_path;
234     struct ConfigEntry *subentry;
235     while((a = strstr(b, ".")) && centry) {
236         if(centry->type == ENTRYTYPE_BLOCK) {
237             int found = 0;
238             for(subentry = centry->value; subentry; subentry = subentry->next) {
239                 if(!stricmplen(subentry->name, b, a-b)) {
240                     centry = subentry;
241                     found = 1;
242                     break;
243                 }
244             }
245             if(!found)
246                 return 0;
247         } else
248             return 0;
249         b = a+1;
250     }
251     if(centry->type == ENTRYTYPE_BLOCK) {
252         int found = 0;
253         for(subentry = centry->value; subentry; subentry = subentry->next) {
254             if(!stricmp(subentry->name, b)) {
255                 centry = subentry;
256                 found = 1;
257                 break;
258             }
259         }
260         if(!found)
261             return 0;
262     } else
263         return 0;
264     if(centry->type == ENTRYTYPE_INTEGER)
265         return ((int *)centry->value)[0];
266     else
267         return 0;
268 }
269
270 char *get_string_field(char *field_path) {
271     struct ConfigEntry *centry = root_entry;
272     char *a, *b = field_path;
273     struct ConfigEntry *subentry;
274     while((a = strstr(b, ".")) && centry) {
275         if(centry->type == ENTRYTYPE_BLOCK) {
276             int found = 0;
277             for(subentry = centry->value; subentry; subentry = subentry->next) {
278                 if(!stricmplen(subentry->name, b, a-b)) {
279                     centry = subentry;
280                     found = 1;
281                     break;
282                 }
283             }
284             if(!found)
285                 return NULL;
286         } else
287             return NULL;
288         b = a+1;
289     }
290     if(centry->type == ENTRYTYPE_BLOCK) {
291         int found = 0;
292         for(subentry = centry->value; subentry; subentry = subentry->next) {
293             if(!stricmp(subentry->name, b)) {
294                 centry = subentry;
295                 found = 1;
296                 break;
297             }
298         }
299         if(!found)
300             return NULL;
301     } else
302         return NULL;
303     if(centry->type == ENTRYTYPE_STRING)
304         return centry->value;
305     else
306         return NULL;
307 }
308
309 char **get_all_fieldnames(char *block_path) {
310     struct ConfigEntry *centry = root_entry;
311     char *a, *b = block_path;
312     struct ConfigEntry *subentry;
313     while((a = strstr(b, ".")) && centry) {
314         if(centry->type == ENTRYTYPE_BLOCK) {
315             int found = 0;
316             for(subentry = centry->value; subentry; subentry = subentry->next) {
317                 if(!stricmplen(subentry->name, b, a-b)) {
318                     centry = subentry;
319                     found = 1;
320                     break;
321                 }
322             }
323             if(!found)
324                 return NULL;
325         } else
326             return NULL;
327         b = a+1;
328     }
329     if(centry->type == ENTRYTYPE_BLOCK) {
330         int found = 0;
331         for(subentry = centry->value; subentry; subentry = subentry->next) {
332             if(!stricmp(subentry->name, b)) {
333                 centry = subentry;
334                 found = 1;
335                 break;
336             }
337         }
338         if(!found)
339             return NULL;
340     } else
341         return NULL;
342     if(centry->type != ENTRYTYPE_BLOCK) return NULL;
343     int count = 0;
344     for(subentry = centry->value; subentry; subentry = subentry->next) {
345         count++;
346     }
347     char **fieldnames = calloc(count+1, sizeof(char *));
348     count = 0;
349     for(subentry = centry->value; subentry; subentry = subentry->next) {
350         fieldnames[count++] = subentry->name;
351     }
352     return fieldnames;
353 }
354
355 void free_loaded_config() {
356     if(root_entry) {
357         free_entry_rekursiv(root_entry, 1);
358         root_entry = NULL;
359     }
360 }
361
362 static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry) {
363     if(centry->type == ENTRYTYPE_BLOCK) {
364         struct ConfigEntry *subentry, *nextentry;
365         for(subentry = centry->value; subentry; subentry = nextentry) {
366             nextentry = subentry->next;
367             free_entry_rekursiv(subentry, 0);
368         }
369     } else if(centry->type == ENTRYTYPE_STRING) {
370         free(centry->value);
371     } else if(centry->type == ENTRYTYPE_INTEGER) {
372         free(centry->value);
373     }
374     if(!is_root_entry)
375         free(centry->name);
376     free(centry);
377 }