src/server/config/serverconfig.c

branch
webdav
changeset 259
0b8692959d37
parent 258
134279e804b6
child 269
3dfbd0b91950
equal deleted inserted replaced
252:5653a9626cc0 259:0b8692959d37
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2020 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30 #include "serverconfig.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include <ucx/buffer.h>
38 #include <ucx/utils.h>
39
40 ServerConfig* serverconfig_load(const char *file) {
41 FILE *in = fopen(file, "r");
42 if(in == NULL) {
43 return NULL;
44 }
45
46 UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
47 if(!buf) {
48 fclose(in);
49 return NULL;
50 }
51
52 ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
53 fclose(in);
54
55 ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size));
56
57 ucx_buffer_free(buf);
58 return scfg;
59 }
60
61
62 static CFGToken get_next_token(scstr_t content, int *pos) {
63 CFGToken token = { {NULL, 0}, CFG_NO_TOKEN };
64 CFGTokenType type = CFG_TOKEN;
65
66 int start = *pos;
67
68 int token_begin = -1;
69 int token_end = content.length-1;
70
71 int quote = 0;
72 int comment = 0;
73
74 int i;
75 char prev = 0;
76 for(i=start;i<content.length;i++) {
77 char c = content.ptr[i];
78 if(c == '\n') {
79 if(quote) {
80 *pos = i;
81 return token; // error
82 } else if(start == i) {
83 // single newline char token
84 type = CFG_TOKEN_NEWLINE;
85 token_begin = i;
86 token_end = i+1;
87 break;
88 }
89
90 token_end = i;
91 if(token_begin < 0) {
92 // only space/comment token
93 token_begin = start;
94 type = comment ? CFG_TOKEN_COMMENT : CFG_TOKEN_SPACE;
95 }
96 // make sure next run will return current newline char as token
97 i--;
98 break;
99 } else if(quote) {
100 if(c == '"' && prev != '\\') {
101 quote = 0;
102 }
103 } else if(comment) {
104 // ignore
105 if(c == '\n') {
106 comment = 0;
107 }
108 } else if(c == '#') {
109 comment = 1;
110 } else if(isspace(c)) {
111 if(token_begin >= 0) {
112 token_end = i;
113 break;
114 }
115 } else if(c == '"') {
116 quote = 1;
117 if(token_begin < 0) {
118 token_begin = i;
119 }
120 } else if(token_begin < 0) {
121 token_begin = i;
122 }
123 prev = c;
124 }
125
126 *pos = i + 1;
127
128 if(token_begin < 0) {
129 return token; // error
130 }
131
132 token.type = type;
133 token.content = scstrsubsl(content, token_begin, token_end - token_begin);
134 return token;
135 }
136
137 /*
138 static void test_print_config(ConfigNode *parent) {
139 UCX_FOREACH(elm, parent->children) {
140 ConfigNode *node = elm->data;
141
142 if(node->type == CONFIG_NODE_SPACE) {
143 printf("sp: %s", node->text_begin.ptr);
144 } else if(node->type == CONFIG_NODE_COMMENT) {
145 printf("cm: %s", node->text_begin.ptr);
146 } else if(node->type == CONFIG_NODE_OBJECT) {
147 printf("o{: %s : %s", node->name.ptr, node->text_begin.ptr);
148 test_print_config(node);
149 printf("o}: %s", node->text_end.ptr);
150 } else if(node->type == CONFIG_NODE_DIRECTIVE) {
151 printf("di: %s", node->text_begin.ptr);
152 } else {
153 printf("fk: %s", node->text_begin.ptr);
154 }
155 }
156 }
157 */
158
159 ServerConfig* serverconfig_parse(scstr_t content) {
160 UcxMempool *mp = ucx_mempool_new(512);
161 if(!mp) return NULL;
162 UcxAllocator *a = mp->allocator;
163
164 ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig));
165 if(!config) {
166 ucx_mempool_destroy(mp);
167 return NULL;
168 }
169 config->mp = mp;
170
171 // PARSE:
172 // first non space/comment token is directive/object name
173 // following tokens are arguments
174 // newline starts new directive
175 // '{' converts directive to object and following directives will
176 // be placed into the object
177 int pos = 0; // needed for tokenizer
178 CFGToken token;
179
180 ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
181 root_obj->type = CONFIG_NODE_OBJECT;
182
183 UcxList *node_stack = ucx_list_prepend(NULL, root_obj);
184
185 ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
186 current->type = CONFIG_NODE_SPACE;
187 ConfigNode *obj = NULL;
188 int obj_closed = 0;
189
190 int text_start = 0;
191 int err = 0;
192 while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) {
193 //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr);
194
195 switch(token.type) {
196 CFG_NO_TOKEN: break;
197 case CFG_TOKEN_COMMENT: {
198 if(current->type == CONFIG_NODE_SPACE) {
199 current->type = CONFIG_NODE_COMMENT;
200 }
201 break;
202 }
203 case CFG_TOKEN_SPACE: break;
204 case CFG_TOKEN_NEWLINE: {
205 scstr_t line = scstrsubsl(content, text_start, pos - text_start);
206 text_start = pos;
207
208 sstr_t line_cp = sstrdup_a(a, line);
209
210 ConfigNode *parent = node_stack->data;
211 if(current->type == CONFIG_NODE_CLOSE_OBJECT) {
212 parent->text_end = line_cp;
213 node_stack = ucx_list_remove_a(a, node_stack, node_stack);
214 } else if(current->type == CONFIG_NODE_OPEN_OBJECT) {
215 sstr_t new_textbegin = sstrcat_a(a, 2, obj->text_begin, line_cp);
216 alfree(a, obj->text_begin.ptr);
217 alfree(a, line_cp.ptr);
218 obj->text_begin = new_textbegin;
219 } else {
220 current->text_begin = line_cp;
221 ConfigNode *parent = node_stack->data;
222 parent->children = ucx_list_append_a(a, parent->children, current);
223 }
224
225 if(obj && obj->type == CONFIG_NODE_OBJECT) {
226 node_stack = ucx_list_prepend_a(a, node_stack, obj);
227 obj = NULL;
228 }
229
230 current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
231 current->type = CONFIG_NODE_SPACE;
232
233 obj_closed = 0;
234 break;
235 }
236 case CFG_TOKEN: {
237 if(!sstrcmp(token.content, S("{"))) {
238 if(!obj) {
239 err = 1;
240 break;
241 }
242 obj->type = CONFIG_NODE_OBJECT;
243 if(current != obj) {
244 current->type = CONFIG_NODE_OPEN_OBJECT;
245 }
246 } else if(!sstrcmp(token.content, S("}"))) {
247 obj_closed = 1; // force newline before next directive
248 obj = NULL;
249 current->type = CONFIG_NODE_CLOSE_OBJECT;
250 } else {
251 if(obj_closed) {
252 err = 1;
253 break;
254 }
255
256 if(!current->name.ptr) {
257 current->name = sstrdup_a(a, token.content);
258 current->type = CONFIG_NODE_DIRECTIVE;
259 obj = current;
260 } else {
261 ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg));
262 // TODO: add support for key/value
263 arg->value = sstrdup_a(a, token.content);
264 current->args = ucx_list_append_a(a, current->args, arg);
265 }
266 }
267 break;
268 }
269 }
270
271 if(err) {
272 break;
273 }
274 }
275
276 if(pos < content.length || err) {
277 // content not fully parsed because of an error
278 ucx_mempool_destroy(mp);
279 return NULL;
280 }
281
282 //test_print_config(&root_obj);
283 config->root = root_obj;
284 config->tab = sstrdup_a(a, SC("\t"));
285
286 return config;
287 }
288
289 void serverconfig_free(ServerConfig *cfg) {
290 ucx_mempool_destroy(cfg->mp);
291 }
292
293 ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name) {
294 UCX_FOREACH(elm, parent->children) {
295 ConfigNode *node = elm->data;
296 if(node->type == type && !sstrcmp(node->name, name)) {
297 return node;
298 }
299 }
300 return NULL;
301 }
302
303 UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) {
304 UcxList *nodes = NULL;
305
306 UCX_FOREACH(elm, parent->children) {
307 ConfigNode *node = elm->data;
308 if(node->type == type && !sstrcmp(node->name, name)) {
309 nodes = ucx_list_append(nodes, node);
310 }
311 }
312
313 return nodes;
314 }
315
316 scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name) {
317 ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name);
318 if(node && ucx_list_size(node->args) == 1) {
319 ConfigArg *arg = node->args->data;
320 return SCSTR(arg->value);
321 }
322 return scstrn(NULL, 0);
323 }

mercurial