26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
27 */ |
27 */ |
28 |
28 |
29 |
29 |
30 #include "serverconfig.h" |
30 #include "serverconfig.h" |
|
31 #include "conf.h" |
31 |
32 |
32 #include <stdio.h> |
33 #include <stdio.h> |
33 #include <stdlib.h> |
34 #include <stdlib.h> |
34 #include <string.h> |
35 #include <string.h> |
35 #include <ctype.h> |
36 #include <ctype.h> |
36 |
37 |
37 #include <ucx/buffer.h> |
38 #include <cx/buffer.h> |
38 #include <ucx/utils.h> |
39 #include <cx/utils.h> |
39 |
40 |
40 ServerConfig* serverconfig_load(const char *file) { |
41 ServerConfig* serverconfig_load(const char *file) { |
41 FILE *in = fopen(file, "r"); |
42 FILE *in = fopen(file, "r"); |
42 if(in == NULL) { |
43 if(in == NULL) { |
43 return NULL; |
44 return NULL; |
44 } |
45 } |
45 |
46 |
46 UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
47 CxBuffer buf; |
47 if(!buf) { |
48 cxBufferInit(&buf, NULL, 16384, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); |
48 fclose(in); |
49 |
49 return NULL; |
50 //ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); |
50 } |
51 char readbuf[2048]; |
51 |
52 size_t r; |
52 ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); |
53 while((r = fread(readbuf, 1, 2048, in)) > 0) { |
|
54 cxBufferWrite(readbuf, 1, r, &buf); |
|
55 } |
53 fclose(in); |
56 fclose(in); |
54 |
57 |
55 ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size)); |
58 ServerConfig *scfg = serverconfig_parse(cx_strn(buf.space, buf.size)); |
56 |
59 |
57 ucx_buffer_free(buf); |
60 cxBufferDestroy(&buf); |
58 return scfg; |
61 return scfg; |
59 } |
62 } |
60 |
63 |
61 |
64 |
62 static CFGToken get_next_token(scstr_t content, int *pos) { |
65 static CFGToken get_next_token(cxstring content, int *pos) { |
63 CFGToken token = { {NULL, 0}, CFG_NO_TOKEN }; |
66 CFGToken token = { {NULL, 0}, CFG_NO_TOKEN }; |
64 CFGTokenType type = CFG_TOKEN; |
67 CFGTokenType type = CFG_TOKEN; |
65 |
68 |
66 int start = *pos; |
69 int start = *pos; |
67 |
70 |
154 } |
157 } |
155 } |
158 } |
156 } |
159 } |
157 */ |
160 */ |
158 |
161 |
159 static void config_arg_set_value(UcxAllocator *a, ConfigArg *arg, CFGToken token) { |
162 static void config_arg_set_value(CxAllocator *a, ConfigParam *arg, CFGToken token) { |
160 scstr_t nv = scstrchr(token.content, '='); |
163 cxstring nv = cx_strchr(token.content, '='); |
161 if(!nv.ptr) { |
164 if(!nv.ptr) { |
162 arg->value = sstrdup_a(a, token.content); |
165 arg->value = cx_strdup_a(a, token.content); |
163 } else { |
166 } else { |
164 intptr_t eq = (intptr_t)(nv.ptr - token.content.ptr); |
167 intptr_t eq = (intptr_t)(nv.ptr - token.content.ptr); |
165 scstr_t name = token.content; |
168 cxstring name = token.content; |
166 name.length = (size_t)eq; |
169 name.length = (size_t)eq; |
167 |
170 |
168 scstr_t value = nv; |
171 cxstring value = nv; |
169 value.ptr++; |
172 value.ptr++; |
170 value.length--; |
173 value.length--; |
171 if(value.length > 1 && value.ptr[0] == '"' && value.ptr[value.length-1] == '"') { |
174 if(value.length > 1 && value.ptr[0] == '"' && value.ptr[value.length-1] == '"') { |
172 value.ptr++; |
175 value.ptr++; |
173 value.length -= 2; // remove quote |
176 value.length -= 2; // remove quote |
174 } |
177 } |
175 |
178 |
176 arg->name = sstrdup_a(a, name); |
179 arg->name = cx_strdup_a(a, name); |
177 arg->value = sstrdup_a(a, value); |
180 arg->value = cx_strdup_a(a, value); |
178 } |
181 } |
179 } |
182 } |
180 |
183 |
181 ServerConfig* serverconfig_parse(scstr_t content) { |
184 static int nodestack_prepend(CxAllocator *a, ConfigNodeStack **stack, ConfigNode *node) { |
182 UcxMempool *mp = ucx_mempool_new(512); |
185 ConfigNodeStack *elm = cxMalloc(a, sizeof(ConfigNodeStack)); |
|
186 if(!elm) return 1; |
|
187 elm->node = node; |
|
188 elm->next = NULL; |
|
189 cx_linked_list_prepend((void**)stack, NULL, -1, offsetof(ConfigNodeStack, next), elm); |
|
190 return 0; |
|
191 } |
|
192 |
|
193 ServerConfig* serverconfig_parse(cxstring content) { |
|
194 CxMempool *mp = cxBasicMempoolCreate(512); |
183 if(!mp) return NULL; |
195 if(!mp) return NULL; |
184 UcxAllocator *a = mp->allocator; |
196 CxAllocator *a = (CxAllocator*)mp->allocator; |
185 |
197 |
186 ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig)); |
198 ServerConfig *config = cxMalloc(a, sizeof(ServerConfig)); |
187 if(!config) { |
199 if(!config) { |
188 ucx_mempool_destroy(mp); |
200 cxMempoolDestroy(mp); |
189 return NULL; |
201 return NULL; |
190 } |
202 } |
191 config->mp = mp; |
203 config->mp = mp; |
192 |
204 |
193 // PARSE: |
205 // PARSE: |
197 // '{' converts directive to object and following directives will |
209 // '{' converts directive to object and following directives will |
198 // be placed into the object |
210 // be placed into the object |
199 int pos = 0; // needed for tokenizer |
211 int pos = 0; // needed for tokenizer |
200 CFGToken token; |
212 CFGToken token; |
201 |
213 |
202 ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); |
214 ConfigNode *root_obj = cxCalloc(a, 1, sizeof(ConfigNode)); |
203 root_obj->type = CONFIG_NODE_OBJECT; |
215 root_obj->type = CONFIG_NODE_OBJECT; |
204 |
216 |
205 UcxList *node_stack = ucx_list_prepend(NULL, root_obj); |
217 ConfigNodeStack *node_stack = cxMalloc(a, sizeof(ConfigNodeStack)); |
206 |
218 node_stack->node = root_obj; |
207 ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); |
219 node_stack->next = NULL; |
|
220 |
|
221 ConfigNode *current = cxCalloc(a, 1, sizeof(ConfigNode)); |
208 current->type = CONFIG_NODE_SPACE; |
222 current->type = CONFIG_NODE_SPACE; |
209 ConfigNode *obj = NULL; |
223 ConfigNode *obj = NULL; |
210 int obj_closed = 0; |
224 int obj_closed = 0; |
211 |
225 |
212 int text_start = 0; |
226 int text_start = 0; |
213 int err = 0; |
227 int err = 0; |
214 while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) { |
228 while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) { |
215 //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr); |
229 //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr); |
216 |
230 |
217 switch(token.type) { |
231 switch(token.type) { |
218 case CFG_NO_TOKEN: break; |
232 case CFG_NO_TOKEN: break; |
219 case CFG_TOKEN_COMMENT: { |
233 case CFG_TOKEN_COMMENT: { |
220 if(current->type == CONFIG_NODE_SPACE) { |
234 if(current->type == CONFIG_NODE_SPACE) { |
221 current->type = CONFIG_NODE_COMMENT; |
235 current->type = CONFIG_NODE_COMMENT; |
222 } |
236 } |
223 break; |
237 break; |
224 } |
238 } |
225 case CFG_TOKEN_SPACE: break; |
239 case CFG_TOKEN_SPACE: break; |
226 case CFG_TOKEN_NEWLINE: { |
240 case CFG_TOKEN_NEWLINE: { |
227 scstr_t line = scstrsubsl(content, text_start, pos - text_start); |
241 cxstring line = cx_strsubsl(content, text_start, pos - text_start); |
228 text_start = pos; |
242 text_start = pos; |
229 |
243 |
230 sstr_t line_cp = sstrdup_a(a, line); |
244 cxmutstr line_cp = cx_strdup_a(a, line); |
231 |
245 |
232 ConfigNode *parent = node_stack->data; |
246 ConfigNode *parent = node_stack->node; |
233 if(current->type == CONFIG_NODE_CLOSE_OBJECT) { |
247 if(current->type == CONFIG_NODE_CLOSE_OBJECT) { |
|
248 // this is a newline after a object is closed with '}' |
|
249 // the line containing "}\n" should be added to the object |
234 parent->text_end = line_cp; |
250 parent->text_end = line_cp; |
235 node_stack = ucx_list_remove_a(a, node_stack, node_stack); |
251 // done with this object, remove it from the stack |
|
252 ConfigNodeStack *remove_item = node_stack; |
|
253 node_stack = node_stack->next; |
|
254 cxFree(a, remove_item); |
236 } else if(current->type == CONFIG_NODE_OPEN_OBJECT) { |
255 } else if(current->type == CONFIG_NODE_OPEN_OBJECT) { |
237 sstr_t new_textbegin = sstrcat_a(a, 2, obj->text_begin, line_cp); |
256 // newline after a object is opened with '{' |
238 alfree(a, obj->text_begin.ptr); |
257 // append '{' to the object text |
239 alfree(a, line_cp.ptr); |
258 cxmutstr new_textbegin = cx_strcat_a(a, 2, obj->text_begin, line_cp); |
|
259 cxFree(a, obj->text_begin.ptr); |
|
260 cxFree(a, line_cp.ptr); |
240 obj->text_begin = new_textbegin; |
261 obj->text_begin = new_textbegin; |
241 } else { |
262 } else { |
|
263 // normal line containing a directive, space or comment |
|
264 // add it to parent node |
242 current->text_begin = line_cp; |
265 current->text_begin = line_cp; |
243 ConfigNode *parent = node_stack->data; |
266 CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current); |
244 parent->children = ucx_list_append_a(a, parent->children, current); |
|
245 } |
267 } |
246 |
268 |
|
269 // obj points to the previous node that started as a directive |
|
270 // the type is set to CONFIG_NODE_OBECT if it was followed by |
|
271 // a '{' character |
247 if(obj && obj->type == CONFIG_NODE_OBJECT) { |
272 if(obj && obj->type == CONFIG_NODE_OBJECT) { |
248 node_stack = ucx_list_prepend_a(a, node_stack, obj); |
273 // new object started, add it to the stack |
|
274 nodestack_prepend(a, &node_stack, obj); |
249 obj = NULL; |
275 obj = NULL; |
250 } |
276 } |
251 |
277 |
252 current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); |
278 current = cxCalloc(a, 1, sizeof(ConfigNode)); |
253 current->type = CONFIG_NODE_SPACE; |
279 current->type = CONFIG_NODE_SPACE; |
254 |
280 |
255 obj_closed = 0; |
281 obj_closed = 0; |
256 break; |
282 break; |
257 } |
283 } |
258 case CFG_TOKEN: { |
284 case CFG_TOKEN: { |
259 if(!sstrcmp(token.content, S("{"))) { |
285 // normal text token |
|
286 // either a directive/obj name, parameter or { } |
|
287 |
|
288 if(!cx_strcmp(token.content, cx_str("{"))) { |
|
289 // obj is pointing to the previous node that started |
|
290 // a directive |
260 if(!obj) { |
291 if(!obj) { |
261 err = 1; |
292 err = 1; |
262 break; |
293 break; |
263 } |
294 } |
264 obj->type = CONFIG_NODE_OBJECT; |
295 obj->type = CONFIG_NODE_OBJECT; |
265 if(current != obj) { |
296 if(current != obj) { |
266 current->type = CONFIG_NODE_OPEN_OBJECT; |
297 current->type = CONFIG_NODE_OPEN_OBJECT; |
267 } |
298 } |
268 } else if(!sstrcmp(token.content, S("}"))) { |
299 } else if(!cx_strcmp(token.content, cx_str("}"))) { |
269 obj_closed = 1; // force newline before next directive |
300 obj_closed = 1; // force newline before next directive |
270 obj = NULL; |
301 obj = NULL; |
271 current->type = CONFIG_NODE_CLOSE_OBJECT; |
302 current->type = CONFIG_NODE_CLOSE_OBJECT; |
272 } else { |
303 } else { |
273 if(obj_closed) { |
304 if(obj_closed) { |
274 err = 1; |
305 err = 1; |
275 break; |
306 break; |
276 } |
307 } |
277 |
308 |
278 if(!current->name.ptr) { |
309 if(!current->name.ptr) { |
279 current->name = sstrdup_a(a, token.content); |
310 // currently this could be a directive or object |
|
311 current->name = cx_strdup_a(a, token.content); |
280 current->type = CONFIG_NODE_DIRECTIVE; |
312 current->type = CONFIG_NODE_DIRECTIVE; |
281 obj = current; |
313 obj = current; // potential object |
282 } else { |
314 } else { |
283 ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg)); |
315 // name already set, therefore this token must |
|
316 // be a parameter |
|
317 ConfigParam *arg = cxCalloc(a, 1, sizeof(ConfigParam)); |
284 config_arg_set_value(a, arg, token); |
318 config_arg_set_value(a, arg, token); |
285 current->args = ucx_list_append_a(a, current->args, arg); |
319 CFG_PARAM_ADD(¤t->args, NULL, arg); |
286 } |
320 } |
287 } |
321 } |
288 break; |
322 break; |
289 } |
323 } |
290 } |
324 } |
294 } |
328 } |
295 } |
329 } |
296 |
330 |
297 if(pos < content.length || err) { |
331 if(pos < content.length || err) { |
298 // content not fully parsed because of an error |
332 // content not fully parsed because of an error |
299 ucx_mempool_destroy(mp); |
333 cxMempoolDestroy(mp); |
300 return NULL; |
334 return NULL; |
301 } |
335 } |
302 |
336 |
303 //test_print_config(&root_obj); |
337 //test_print_config(&root_obj); |
304 config->root = root_obj; |
338 config->root = root_obj; |
305 config->tab = sstrdup_a(a, SC("\t")); |
339 config->tab = cx_strdup_a(a, cx_str("\t")); |
306 |
340 |
307 return config; |
341 return config; |
308 } |
342 } |
309 |
343 |
310 void serverconfig_free(ServerConfig *cfg) { |
344 void serverconfig_free(ServerConfig *cfg) { |
311 ucx_mempool_destroy(cfg->mp); |
345 cxMempoolDestroy(cfg->mp); |
312 } |
346 } |
313 |
347 |
314 ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name) { |
348 ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, cxstring name) { |
315 UCX_FOREACH(elm, parent->children) { |
349 for(ConfigNode *node=parent->children_begin;node;node=node->next) { |
316 ConfigNode *node = elm->data; |
350 if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) { |
317 if(node->type == type && !sstrcasecmp(node->name, name)) { |
|
318 return node; |
351 return node; |
319 } |
352 } |
320 } |
353 } |
321 return NULL; |
354 return NULL; |
322 } |
355 } |
323 |
356 |
324 UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) { |
357 CxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, cxstring name) { |
325 UcxList *nodes = NULL; |
358 CxList *nodes = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); |
326 |
359 |
327 UCX_FOREACH(elm, parent->children) { |
360 for(ConfigNode *node=parent->children_begin;node;node=node->next) { |
328 ConfigNode *node = elm->data; |
361 if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) { |
329 if(node->type == type && !sstrcasecmp(node->name, name)) { |
362 cxListAdd(nodes, node); |
330 nodes = ucx_list_append(nodes, node); |
|
331 } |
363 } |
332 } |
364 } |
333 |
365 |
334 return nodes; |
366 return nodes; |
335 } |
367 } |
336 |
368 |
337 scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name) { |
369 cxstring serverconfig_directive_value(ConfigNode *obj, cxstring name) { |
338 ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name); |
370 ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name); |
339 if(node && ucx_list_size(node->args) == 1) { |
371 if(node && CFG_NUM_PARAMS(node->args) == 1) { |
340 ConfigArg *arg = node->args->data; |
372 ConfigParam *arg = node->args; |
341 return SCSTR(arg->value); |
373 return (cxstring){ arg->value.ptr, arg->value.length }; |
342 } |
374 } |
343 return scstrn(NULL, 0); |
375 return (cxstring){ NULL, 0 }; |
344 } |
376 } |
345 |
377 |
346 sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name) { |
378 cxmutstr serverconfig_arg_name_value(CxAllocator *a, cxstring str, cxstring *name) { |
347 int valstart = 0; |
379 int valstart = 0; |
348 for(int i=0;i<str.length;i++) { |
380 for(int i=0;i<str.length;i++) { |
349 if(str.ptr[i] == '=') { |
381 if(str.ptr[i] == '=') { |
350 if(name) { |
382 if(name) { |
351 name->ptr = str.ptr; |
383 name->ptr = str.ptr; |