Another year is about to end... So we have to update these damn copyright information :P
[NeonServV5.git] / src / ConfigParser.c
1 /* ConfigParser.c - NeonServ v5.3
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
20 #define ENTRYTYPE_BLOCK   1
21 #define ENTRYTYPE_STRING  2
22 #define ENTRYTYPE_INTEGER 3
23
24 struct ConfigEntry {
25     char *name;
26     int type;
27     void *value;
28     struct ConfigEntry *next;
29 };
30
31 static struct ConfigEntry *root_entry = NULL;
32
33 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element);
34 static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry);
35
36 int loadConfig(const char *filename) {
37     FILE *f;
38     f = fopen(filename, "rb");
39     if(!f) return 0;
40     
41     // obtain file size:
42     long lSize;
43     fseek (f, 0, SEEK_END);
44     lSize = ftell(f);
45     rewind(f);
46     
47     // allocate memory to contain the whole file:
48     char *buffer = (char*) malloc (sizeof(char)*lSize + 1);
49     if (buffer == NULL) {
50         fclose(f);
51         return 0;
52     }
53     
54     // copy the file into the buffer:
55     size_t result = fread (buffer, 1, lSize, f);
56     if (result != lSize) {
57         fclose(f);
58         return 0;
59     }
60     buffer[lSize] = '\0';
61     
62     fclose(f);
63     
64     // now parse the config file...
65     if(root_entry) {
66         free_loaded_config();
67     }
68     root_entry = malloc(sizeof(*root_entry));
69     if (!root_entry) return 0;
70     parse_config_recursive(root_entry, buffer, 1);
71     
72     // free the buffer...
73     free(buffer);
74     return 1;
75 }
76
77 #define PARSER_FLAG_ESCAPED    0x01
78 #define PARSER_FLAG_BLOCK      0x02
79 #define PARSER_FLAG_STRING     0x04
80 #define PARSER_FLAG_EXPECT_END 0x08
81 #define PARSER_FLAG_NEXTCHAR   0x10
82 #define PARSER_FLAG_INTEGER    0x20
83 #define PARSER_FLAG_EOBN       0x40 /* End of Block name */
84 #define PARSER_FLAG_COMMAND    0x80
85 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element) {
86     int flags = 0;
87     int type = (root_element ? ENTRYTYPE_BLOCK : 0);
88     char cbuf[1024];
89     int cbufpos = 0;
90     struct ConfigEntry *sub_entrys = NULL;
91     while(*buffer) {
92         if(flags & PARSER_FLAG_NEXTCHAR) {
93             buffer++;
94             if(*buffer == '\0')
95                 break;
96         }
97         flags |= PARSER_FLAG_NEXTCHAR;
98         if(flags & PARSER_FLAG_EOBN) {
99             flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR);
100             struct ConfigEntry *new_entry = malloc(sizeof(*new_entry));
101             if (!new_entry) return buffer;
102             new_entry->name = strdup(cbuf);
103             buffer = parse_config_recursive(new_entry, buffer, 0);
104             if(sub_entrys)
105                 new_entry->next = sub_entrys;
106             else
107                 new_entry->next = NULL;
108             sub_entrys = new_entry;
109             centry->value = sub_entrys;
110         }
111         if(flags & PARSER_FLAG_ESCAPED) {
112             cbuf[cbufpos++] = *buffer;
113             flags &= ~PARSER_FLAG_ESCAPED;
114             continue;
115         }
116         if(flags & PARSER_FLAG_EXPECT_END) {
117             if(*buffer == ';') {
118                 centry->type = type;
119                 return (buffer+1);
120             }
121             continue;
122         }
123         if(flags & PARSER_FLAG_STRING) {
124             if(*buffer == '"') {
125                 cbuf[cbufpos] = '\0';
126                 flags &= ~PARSER_FLAG_STRING;
127                 if(type == ENTRYTYPE_STRING) {
128                     flags |= PARSER_FLAG_EXPECT_END;
129                     centry->value = strdup(cbuf);
130                 } else if(type == ENTRYTYPE_BLOCK) {
131                     flags |= PARSER_FLAG_EOBN;
132                 }
133             } else if(*buffer == '\\')
134                 flags |= PARSER_FLAG_ESCAPED;
135             else
136                 cbuf[cbufpos++] = *buffer;
137             continue;
138         }
139         if(flags & PARSER_FLAG_INTEGER) {
140             if(!isdigit(*buffer)) {
141                 cbuf[cbufpos] = '\0';
142                 flags &= ~PARSER_FLAG_INTEGER;
143                 if(type == ENTRYTYPE_INTEGER) {
144                     flags |= PARSER_FLAG_EXPECT_END;
145                     int *intbuf = malloc(sizeof(int));
146                     *intbuf = atoi(cbuf);
147                     centry->value = intbuf;
148                 }
149                 if(*buffer == ';') {
150                     centry->type = type;
151                     return (buffer+1);
152                 }
153             } else
154                 cbuf[cbufpos++] = *buffer;
155             continue;
156         }
157         if(flags & PARSER_FLAG_COMMAND) {
158             int found_command = 0;
159             switch(*buffer) {
160                 case '/':
161                     buffer = strstr(buffer, "\n");
162                     found_command = 1;
163                     break;
164                 case '*':
165                     //simple search for the next */
166                     buffer = strstr(buffer, "*/")+1;
167                     found_command = 1;
168             }
169             flags &= ~PARSER_FLAG_COMMAND;
170             if(found_command)
171                 continue;
172         }
173         switch(*buffer) {
174             case '\\':
175                 flags |= PARSER_FLAG_ESCAPED;
176                 break;
177             case '/':
178                 if(!(flags & PARSER_FLAG_STRING)) {
179                     flags |= PARSER_FLAG_COMMAND;
180                 }
181                 break;
182             case '{':
183                 flags |= PARSER_FLAG_BLOCK;
184                 type = ENTRYTYPE_BLOCK;
185                 break;
186             case '}':
187                 if(flags & PARSER_FLAG_BLOCK)
188                     flags &= ~PARSER_FLAG_BLOCK;
189                 flags |= PARSER_FLAG_EXPECT_END;
190                 break;
191             case '"':
192                 flags |= PARSER_FLAG_STRING;
193                 if(!type)
194                     type = ENTRYTYPE_STRING;
195                 cbufpos = 0;
196                 break;
197             case ';':
198                 centry->type = type;
199                 return (buffer+1);
200             case '0':
201             case '1':
202             case '2':
203             case '3':
204             case '4':
205             case '5':
206             case '6':
207             case '7':
208             case '8':
209             case '9':
210                 if(!type)
211                     type = ENTRYTYPE_INTEGER;
212                 flags |= PARSER_FLAG_INTEGER;
213                 cbufpos = 0;
214                 cbuf[cbufpos++] = *buffer;
215                 break;
216             default:
217                 break;
218         }
219     }
220     centry->type = type;
221     return buffer; //end of the buffer
222 }
223
224 int get_int_field(char *field_path) {
225     struct ConfigEntry *centry = root_entry;
226     char *a, *b = field_path;
227     struct ConfigEntry *subentry;
228     while((a = strstr(b, ".")) && centry) {
229         if(centry->type == ENTRYTYPE_BLOCK) {
230             int found = 0;
231             for(subentry = centry->value; subentry; subentry = subentry->next) {
232                 if(!stricmplen(subentry->name, b, a-b)) {
233                     centry = subentry;
234                     found = 1;
235                     break;
236                 }
237             }
238             if(!found)
239                 return 0;
240         } else
241             return 0;
242         b = a+1;
243     }
244     if(centry->type == ENTRYTYPE_BLOCK) {
245         int found = 0;
246         for(subentry = centry->value; subentry; subentry = subentry->next) {
247             if(!stricmp(subentry->name, b)) {
248                 centry = subentry;
249                 found = 1;
250                 break;
251             }
252         }
253         if(!found)
254             return 0;
255     } else
256         return 0;
257     if(centry->type == ENTRYTYPE_INTEGER)
258         return ((int *)centry->value)[0];
259     else
260         return 0;
261 }
262
263 char *get_string_field(char *field_path) {
264     struct ConfigEntry *centry = root_entry;
265     char *a, *b = field_path;
266     struct ConfigEntry *subentry;
267     while((a = strstr(b, ".")) && centry) {
268         if(centry->type == ENTRYTYPE_BLOCK) {
269             int found = 0;
270             for(subentry = centry->value; subentry; subentry = subentry->next) {
271                 if(!stricmplen(subentry->name, b, a-b)) {
272                     centry = subentry;
273                     found = 1;
274                     break;
275                 }
276             }
277             if(!found)
278                 return NULL;
279         } else
280             return NULL;
281         b = a+1;
282     }
283     if(centry->type == ENTRYTYPE_BLOCK) {
284         int found = 0;
285         for(subentry = centry->value; subentry; subentry = subentry->next) {
286             if(!stricmp(subentry->name, b)) {
287                 centry = subentry;
288                 found = 1;
289                 break;
290             }
291         }
292         if(!found)
293             return NULL;
294     } else
295         return NULL;
296     if(centry->type == ENTRYTYPE_STRING)
297         return centry->value;
298     else
299         return NULL;
300 }
301
302 void free_loaded_config() {
303     if(root_entry) {
304         free_entry_rekursiv(root_entry, 1);
305         root_entry = NULL;
306     }
307 }
308
309 static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry) {
310     if(centry->type == ENTRYTYPE_BLOCK) {
311         struct ConfigEntry *subentry, *nextentry;
312         for(subentry = centry->value; subentry; subentry = nextentry) {
313             nextentry = subentry->next;
314             free_entry_rekursiv(subentry, 0);
315         }
316     } else if(centry->type == ENTRYTYPE_STRING) {
317         free(centry->value);
318     } else if(centry->type == ENTRYTYPE_INTEGER) {
319         free(centry->value);
320     }
321     if(!is_root_entry)
322         free(centry->name);
323     free(centry);
324 }