1 %token_prefix TK_
2 %extra_argument {config_t *ctx}
3 %name configparser
4
5 %include {
6 #define NDEBUG
7 #include "first.h"
8 #include "base.h"
9 #include "configfile.h"
10 #include "buffer.h"
11 #include "array.h"
12 #include "http_header.h" /* http_header_hkey_get() */
13 #include "request.h" /* http_request_host_normalize() */
14
15 #include <ctype.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 /*(missing declarations in generated configparser.c)*/
22 static void configparserInit(void *yypRawParser);
23 static void configparserFinalize(void *p);
24 /*(missing declarations in generated configparser.c; defined but not used)*/
25 static inline int configparserFallback(int iToken)
26 __attribute_unused__;
27 #ifndef NDEBUG
28 static inline void configparserTrace(FILE *TraceFILE, char *zTracePrompt)
29 __attribute_unused__;
30 #endif
31
32 __attribute_pure__
configparser_get_data_config(const array * a,const char * k,const size_t klen)33 static data_config * configparser_get_data_config(const array *a, const char *k, const size_t klen) {
34 return (data_config *)array_get_data_unset(a, k, klen);
35 }
36
37 __attribute_noinline__
configparser_push_data_config_list(data_config_list * v,data_config * dc)38 static void configparser_push_data_config_list(data_config_list *v, data_config *dc) {
39 if (v->size == v->used) {
40 ck_realloc_u32((void **)&v->data, v->size, 4, sizeof(*v->data));
41 v->size += 4;
42 }
43 v->data[v->used++] = dc;
44 }
45
configparser_push(config_t * ctx,data_config * dc,int isnew)46 static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
47 if (isnew) {
48 dc->context_ndx = ctx->all_configs->used;
49 force_assert(dc->context_ndx > ctx->current->context_ndx);
50 array_insert_unique(ctx->all_configs, (data_unset *)dc);
51 dc->parent = ctx->current;
52 configparser_push_data_config_list(&dc->parent->children, dc);
53 }
54 if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) {
55 fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n");
56 exit(-1);
57 }
58 configparser_push_data_config_list(&ctx->configs_stack, ctx->current);
59 ctx->current = dc;
60 }
61
configparser_pop(config_t * ctx)62 static data_config *configparser_pop(config_t *ctx) {
63 data_config *old = ctx->current;
64 force_assert(ctx->configs_stack.used);
65 ctx->current = ctx->configs_stack.data[--ctx->configs_stack.used];
66 force_assert(old && ctx->current);
67 return old;
68 }
69
70 /* return a copied variable */
configparser_get_variable(config_t * ctx,const buffer * key)71 static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
72 const data_unset *du;
73 data_config *dc;
74
75 #if 0
76 fprintf(stderr, "get var %s\n", key->ptr);
77 #endif
78 for (dc = ctx->current; dc; dc = dc->parent) {
79 #if 0
80 fprintf(stderr, "get var on block: %s\n", dc->key.ptr);
81 array_print(dc->value, 0);
82 #endif
83 if (NULL != (du = array_get_element_klen(dc->value, BUF_PTR_LEN(key)))) {
84 data_unset *du_copy = du->fn->copy(du);
85 buffer_clear(&du_copy->key);
86 return du_copy;
87 }
88 }
89 return NULL;
90 }
91
92 /* op1 is to be eat/return by this function if success, op1->key is not cared
93 op2 is left untouch, unreferenced
94 */
configparser_merge_data(data_unset * op1,const data_unset * op2)95 static data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
96 /* type mismatch */
97 if (op1->type != op2->type) {
98 if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
99 data_string *ds = (data_string *)op1;
100 buffer_append_int(&ds->value, ((data_integer*)op2)->value);
101 return op1;
102 } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
103 data_string *ds = array_data_string_init();
104 buffer_append_int(&ds->value, ((data_integer*)op1)->value);
105 buffer_append_string_buffer(&ds->value, &((data_string*)op2)->value);
106 op1->fn->free(op1);
107 return (data_unset *)ds;
108 } else {
109 fprintf(stderr, "data type mismatch, cannot merge\n");
110 op1->fn->free(op1);
111 return NULL;
112 }
113 }
114
115 switch (op1->type) {
116 case TYPE_STRING:
117 buffer_append_string_buffer(&((data_string *)op1)->value, &((data_string *)op2)->value);
118 break;
119 case TYPE_INTEGER:
120 ((data_integer *)op1)->value += ((data_integer *)op2)->value;
121 break;
122 case TYPE_ARRAY: {
123 array *dst = &((data_array *)op1)->value;
124 array *src = &((data_array *)op2)->value;
125 const data_unset *du, *ddu;
126 size_t i;
127
128 for (i = 0; i < src->used; i ++) {
129 du = (data_unset *)src->data[i];
130 if (du) {
131 if (buffer_is_unset(&du->key)
132 || !(ddu = array_get_element_klen(dst, BUF_PTR_LEN(&du->key)))){
133 array_insert_unique(dst, du->fn->copy(du));
134 } else {
135 fprintf(stderr, "Duplicate array-key '%s'\n", du->key.ptr);
136 if (ddu->type == du->type) {
137 /*(ignore if new key/value pair matches existing key/value)*/
138 if (du->type == TYPE_STRING
139 && buffer_is_equal(&((data_string *)du)->value,
140 &((data_string *)ddu)->value))
141 continue;
142 if (du->type == TYPE_INTEGER
143 && ((data_integer*)du)->value == ((data_integer*)ddu)->value)
144 continue;
145 }
146 op1->fn->free(op1);
147 return NULL;
148 }
149 }
150 }
151 break;
152 }
153 default:
154 force_assert(0);
155 break;
156 }
157 return op1;
158 }
159
160 __attribute_pure__
161 static comp_key_t
configparser_comp_key_id(const buffer * const obj_tag,const buffer * const comp_tag)162 configparser_comp_key_id(const buffer * const obj_tag, const buffer * const comp_tag)
163 {
164 /* $REQUEST_HEADER["..."] */
165 /* $SERVER["socket"] */
166 /* $HTTP["..."] */
167 if (buffer_eq_slen(obj_tag, CONST_STR_LEN("REQUEST_HEADER")))
168 return COMP_HTTP_REQUEST_HEADER;
169 else if (buffer_eq_slen(obj_tag, CONST_STR_LEN("SERVER")))
170 return (buffer_eq_slen(comp_tag, CONST_STR_LEN("socket")))
171 ? COMP_SERVER_SOCKET
172 : COMP_UNSET;
173 else if (buffer_eq_slen(obj_tag, CONST_STR_LEN("HTTP"))) {
174 static const struct {
175 comp_key_t comp;
176 uint32_t len;
177 const char *comp_tag;
178 } comps[] = {
179 { COMP_HTTP_URL, CONST_LEN_STR("url" ) },
180 { COMP_HTTP_HOST, CONST_LEN_STR("host" ) },
181 { COMP_HTTP_REQUEST_HEADER, CONST_LEN_STR("referer" ) },
182 { COMP_HTTP_USER_AGENT, CONST_LEN_STR("useragent" ) },
183 { COMP_HTTP_REQUEST_HEADER, CONST_LEN_STR("user-agent" ) },
184 { COMP_HTTP_LANGUAGE, CONST_LEN_STR("language" ) },
185 { COMP_HTTP_REQUEST_HEADER, CONST_LEN_STR("cookie" ) },
186 { COMP_HTTP_REMOTE_IP, CONST_LEN_STR("remoteip" ) },
187 { COMP_HTTP_REMOTE_IP, CONST_LEN_STR("remote-ip" ) },
188 { COMP_HTTP_QUERY_STRING, CONST_LEN_STR("querystring" ) },
189 { COMP_HTTP_QUERY_STRING, CONST_LEN_STR("query-string" ) },
190 { COMP_HTTP_REQUEST_METHOD, CONST_LEN_STR("request-method") },
191 { COMP_HTTP_SCHEME, CONST_LEN_STR("scheme" ) }
192 };
193
194 for (uint32_t i = 0; i < sizeof(comps)/sizeof(comps[0]); ++i) {
195 if (buffer_eq_slen(comp_tag, comps[i].comp_tag, comps[i].len))
196 return comps[i].comp;
197 }
198 }
199 return COMP_UNSET;
200 }
201
202 static config_cond_t
configparser_simplify_regex(buffer * const b)203 configparser_simplify_regex(buffer * const b)
204 {
205 /* translate simple regex anchored with ^ and/or $ to simpler match types
206 * (note: skips if regex contains any '\\', even if some could be removed,
207 * though we special-case "\.ext"; skips if other '.' found in str)
208 * (currently assumes CONFIG_COND_MATCH input, not CONFIG_COND_NOMATCH) */
209 uint32_t len = buffer_clen(b);
210 config_cond_t cond = CONFIG_COND_MATCH;
211 int off = 0;
212 if (len && b->ptr[len-1] == '$') {
213 cond = CONFIG_COND_SUFFIX;
214 if (b->ptr[0] == '\\' && b->ptr[1] == '.')
215 off = 2;
216 else if (b->ptr[0] == '^') {
217 off = 1;
218 cond = CONFIG_COND_EQ;
219 }
220 --len;
221 }
222 else if (b->ptr[0] == '^') {
223 off = 1;
224 cond = CONFIG_COND_PREFIX;
225 }
226 else
227 return CONFIG_COND_MATCH;
228
229 static const char regex_chars[] = "\\^$.|?*+()[]{}";
230 if (strcspn(b->ptr+off, regex_chars) != len - off)
231 return CONFIG_COND_MATCH;
232 if (off) { /*(remove only first char if (off == 2) to keep '.' in "\.")*/
233 memmove(b->ptr, b->ptr+1, len-1);
234 --len;
235 }
236 buffer_truncate(b, len);
237 return cond;
238 }
239
240 static void
configparser_parse_condition(config_t * const ctx,const buffer * const obj_tag,const buffer * const comp_tag,config_cond_t cond,buffer * const rvalue)241 configparser_parse_condition(config_t * const ctx, const buffer * const obj_tag, const buffer * const comp_tag, config_cond_t cond, buffer * const rvalue)
242 {
243 const comp_key_t comp = configparser_comp_key_id(obj_tag, comp_tag);
244 if (cond == CONFIG_COND_MATCH && comp != COMP_SERVER_SOCKET)
245 cond = configparser_simplify_regex(rvalue);
246
247 const char *op = NULL;
248 switch(cond) {
249 case CONFIG_COND_NE: op = "!="; break;
250 case CONFIG_COND_EQ: op = "=="; break;
251 case CONFIG_COND_NOMATCH: op = "!~"; break;
252 case CONFIG_COND_MATCH: op = "=~"; break;
253 case CONFIG_COND_PREFIX: op = "=^"; break;
254 case CONFIG_COND_SUFFIX: op = "=$"; break;
255 default:
256 force_assert(0);
257 return; /* unreachable */
258 }
259
260 const uint32_t comp_offset = buffer_clen(&ctx->current->key)+3;
261 buffer * const tb = ctx->srv->tmp_buf;
262 buffer_clear(tb);
263 struct const_iovec iov[] = {
264 { BUF_PTR_LEN(&ctx->current->key) }
265 ,{ CONST_STR_LEN(" / ") } /* comp_offset */
266 ,{ CONST_STR_LEN("$") }
267 ,{ BUF_PTR_LEN(obj_tag) } /*(HTTP, REQUEST_HEADER, SERVER)*/
268 ,{ CONST_STR_LEN("[\"") }
269 ,{ BUF_PTR_LEN(comp_tag) }
270 ,{ CONST_STR_LEN("\"] ") }
271 ,{ op, 2 }
272 ,{ CONST_STR_LEN(" \"") }
273 ,{ BUF_PTR_LEN(rvalue) }
274 ,{ CONST_STR_LEN("\"") }
275 };
276 buffer_append_iovec(tb, iov, sizeof(iov)/sizeof(*iov));
277
278 data_config *dc;
279 if (NULL != (dc = configparser_get_data_config(ctx->all_configs,
280 BUF_PTR_LEN(tb)))) {
281 configparser_push(ctx, dc, 0);
282 }
283 else {
284 dc = data_config_init();
285 dc->cond = cond;
286 dc->comp = comp;
287
288 buffer_copy_buffer(&dc->key, tb);
289 buffer_copy_buffer(&dc->comp_tag, comp_tag);
290 dc->comp_key = dc->key.ptr + comp_offset;
291
292 if (COMP_UNSET == dc->comp) {
293 fprintf(stderr, "error comp_key %s", dc->comp_key);
294 ctx->ok = 0;
295 }
296 else if (COMP_HTTP_LANGUAGE == dc->comp) {
297 dc->comp = COMP_HTTP_REQUEST_HEADER;
298 buffer_copy_string_len(&dc->comp_tag, CONST_STR_LEN("Accept-Language"));
299 }
300 else if (COMP_HTTP_USER_AGENT == dc->comp) {
301 dc->comp = COMP_HTTP_REQUEST_HEADER;
302 buffer_copy_string_len(&dc->comp_tag, CONST_STR_LEN("User-Agent"));
303 }
304 else if (COMP_HTTP_REMOTE_IP == dc->comp
305 && (dc->cond == CONFIG_COND_EQ ||
306 dc->cond == CONFIG_COND_NE ||
307 dc->cond == CONFIG_COND_PREFIX ||
308 dc->cond == CONFIG_COND_SUFFIX)) {
309 if (!config_remoteip_normalize(rvalue, tb)) {
310 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
311 ctx->ok = 0;
312 }
313 }
314 else if (COMP_SERVER_SOCKET == dc->comp) {
315 /*(redundant with parsing in network.c; not actually required here)*/
316 if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/
317 && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')
318 && !(rvalue->ptr[0] == '/' || rvalue->ptr[0] == '\\')) { /*(UDS)*/
319 if (http_request_host_normalize(rvalue, 0)) {
320 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
321 ctx->ok = 0;
322 }
323 }
324 }
325 else if (COMP_HTTP_HOST == dc->comp) {
326 if (dc->cond == CONFIG_COND_EQ ||
327 dc->cond == CONFIG_COND_NE ||
328 dc->cond == CONFIG_COND_PREFIX ||
329 dc->cond == CONFIG_COND_SUFFIX) {
330 if (http_request_host_normalize(rvalue, 0)) {
331 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
332 ctx->ok = 0;
333 }
334 }
335 }
336
337 if (COMP_HTTP_REQUEST_HEADER == dc->comp) {
338 dc->ext = http_header_hkey_get(BUF_PTR_LEN(&dc->comp_tag));
339 }
340
341 buffer_move(&dc->string, rvalue);
342
343 if (ctx->ok)
344 configparser_push(ctx, dc, 1);
345 else
346 dc->fn->free((data_unset*) dc);
347 }
348 }
349
350 static void
configparser_parse_else_condition(config_t * const ctx)351 configparser_parse_else_condition(config_t * const ctx)
352 {
353 data_config * const dc = data_config_init();
354 dc->cond = CONFIG_COND_ELSE;
355 buffer_append_str2(&dc->key, BUF_PTR_LEN(&ctx->current->key),
356 CONST_STR_LEN(" / "
357 "else_tmp_token"));
358 configparser_push(ctx, dc, 1);
359 }
360
361 }
362
363 %parse_failure {
364 ctx->ok = 0;
365 }
366
367 input ::= metalines.
368 metalines ::= metalines metaline.
369 metalines ::= .
370 metaline ::= varline.
371 metaline ::= global.
condlines(A)372 metaline ::= condlines(A) EOL. { A = NULL; }
373 metaline ::= include.
374 metaline ::= include_shell.
375 metaline ::= EOL.
376
377 %type value {data_unset *}
378 %type expression {data_unset *}
379 %type aelement {data_unset *}
380 %type condline {data_config *}
381 %type cond_else {data_config *}
382 %type condlines {data_config *}
383 %type aelements {array *}
384 %type array {array *}
385 %type key {buffer *}
386 %type stringop {buffer *}
387
388 %type cond {config_cond_t }
389
390 %destructor value { if ($$) $$->fn->free($$); }
391 %destructor expression { if ($$) $$->fn->free($$); }
392 %destructor aelement { if ($$) $$->fn->free($$); }
393 %destructor aelements { array_free($$); }
394 %destructor array { array_free($$); }
395 %destructor key { buffer_free($$); }
396 %destructor stringop { buffer_free($$); }
397
398 %token_type {buffer *}
399 %token_destructor { buffer_free($$); UNUSED(ctx); }
400
key(A)401 varline ::= key(A) ASSIGN expression(B). {
402 if (ctx->ok) {
403 buffer_copy_buffer(&B->key, A);
404 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
405 fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
406 ctx->current->context_ndx,
407 ctx->current->key.ptr, A->ptr);
408 ctx->ok = 0;
409 } else if (NULL == array_get_element_klen(ctx->current->value, BUF_PTR_LEN(&B->key))) {
410 array_insert_unique(ctx->current->value, B);
411 B = NULL;
412 } else {
413 fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
414 ctx->current->context_ndx,
415 ctx->current->key.ptr, B->key.ptr);
416 ctx->ok = 0;
417 }
418 }
419 buffer_free(A);
420 A = NULL;
421 if (B) B->fn->free(B);
422 B = NULL;
423 }
424
key(A)425 varline ::= key(A) FORCE_ASSIGN expression(B). {
426 if (ctx->ok) {
427 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
428 fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
429 ctx->current->context_ndx,
430 ctx->current->key.ptr, A->ptr);
431 ctx->ok = 0;
432 } else {
433 buffer_copy_buffer(&B->key, A);
434 array_replace(ctx->current->value, B);
435 B = NULL;
436 }
437 }
438 buffer_free(A);
439 A = NULL;
440 if (B) B->fn->free(B);
441 B = NULL;
442 }
443
key(A)444 varline ::= key(A) APPEND expression(B). {
445 if (ctx->ok) {
446 array *vars = ctx->current->value;
447 data_unset *du;
448
449 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
450 fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
451 ctx->current->context_ndx,
452 ctx->current->key.ptr, A->ptr);
453 ctx->ok = 0;
454 } else if (NULL != (du = array_extract_element_klen(vars, BUF_PTR_LEN(A))) || NULL != (du = configparser_get_variable(ctx, A))) {
455 du = configparser_merge_data(du, B);
456 if (NULL == du) {
457 ctx->ok = 0;
458 }
459 else {
460 buffer_copy_buffer(&du->key, A);
461 array_insert_unique(ctx->current->value, du);
462 }
463 } else {
464 buffer_copy_buffer(&B->key, A);
465 array_insert_unique(ctx->current->value, B);
466 B = NULL;
467 }
468 }
469 buffer_free(A);
470 A = NULL;
471 if (B) B->fn->free(B);
472 B = NULL;
473 }
474
key(A)475 key(A) ::= LKEY(B). {
476 if (strchr(B->ptr, '.') == NULL) {
477 buffer_copy_string((A = buffer_init()), "var.");
478 buffer_append_string_buffer(A, B);
479 } else {
480 A = B;
481 B = NULL;
482 }
483 buffer_free(B);
484 B = NULL;
485 }
486
expression(A)487 expression(A) ::= expression(B) PLUS value(C). {
488 A = NULL;
489 if (ctx->ok) {
490 A = configparser_merge_data(B, C);
491 B = NULL;
492 if (NULL == A) {
493 ctx->ok = 0;
494 }
495 }
496 if (B) B->fn->free(B);
497 B = NULL;
498 if (C) C->fn->free(C);
499 C = NULL;
500 }
501
expression(A)502 expression(A) ::= value(B). {
503 A = B;
504 B = NULL;
505 }
506
value(A)507 value(A) ::= key(B). {
508 A = NULL;
509 if (ctx->ok) {
510 if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
511 char *env;
512
513 if (NULL != (env = getenv(B->ptr + 4))) {
514 data_string *ds;
515 ds = array_data_string_init();
516 buffer_append_string(&ds->value, env);
517 A = (data_unset *)ds;
518 }
519 else {
520 fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
521 ctx->ok = 0;
522 }
523 } else if (NULL == (A = configparser_get_variable(ctx, B))) {
524 fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
525 ctx->ok = 0;
526 }
527 }
528 buffer_free(B);
529 B = NULL;
530 }
531
value(A)532 value(A) ::= STRING(B). {
533 A = (data_unset *)array_data_string_init();
534 /* assumes array_data_string_init() result does not need swap, buffer_free()*/
535 memcpy(&((data_string *)A)->value, B, sizeof(*B));
536 free(B);
537 B = NULL;
538 }
539
value(A)540 value(A) ::= INTEGER(B). {
541 char *endptr;
542 A = (data_unset *)array_data_integer_init();
543 errno = 0;
544 ((data_integer *)(A))->value = strtol(B->ptr, &endptr, 10);
545 /* skip trailing whitespace */
546 if (endptr != B->ptr) while (isspace(*(unsigned char *)endptr)) endptr++;
547 if (0 != errno || *endptr != '\0') {
548 fprintf(stderr, "error parsing number: '%s'\n", B->ptr);
549 ctx->ok = 0;
550 }
551 buffer_free(B);
552 B = NULL;
553 }
value(A)554 value(A) ::= array(B). {
555 A = (data_unset *)array_data_array_init();
556 /* assumes array_data_array_init() result does not need swap, array_free() */
557 memcpy(&((data_array *)(A))->value, B, sizeof(*B));
558 free(B);
559 B = NULL;
560 }
array(A)561 array(A) ::= LPARAN RPARAN. {
562 A = array_init(8);
563 }
array(A)564 array(A) ::= LPARAN aelements(B) RPARAN. {
565 A = B;
566 B = NULL;
567 }
568
aelements(A)569 aelements(A) ::= aelements(C) COMMA aelement(B). {
570 A = NULL;
571 if (ctx->ok) {
572 if (buffer_is_unset(&B->key) ||
573 NULL == array_get_element_klen(C, BUF_PTR_LEN(&B->key))) {
574 array_insert_unique(C, B);
575 B = NULL;
576 } else {
577 fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
578 B->key.ptr);
579 ctx->ok = 0;
580 }
581
582 A = C;
583 C = NULL;
584 }
585 array_free(C);
586 C = NULL;
587 if (B) B->fn->free(B);
588 B = NULL;
589 }
590
aelements(A)591 aelements(A) ::= aelements(C) COMMA. {
592 A = C;
593 C = NULL;
594 }
595
aelements(A)596 aelements(A) ::= aelement(B). {
597 A = NULL;
598 if (ctx->ok) {
599 A = array_init(4);
600 array_insert_unique(A, B);
601 B = NULL;
602 }
603 if (B) B->fn->free(B);
604 B = NULL;
605 }
606
aelement(A)607 aelement(A) ::= expression(B). {
608 A = B;
609 B = NULL;
610 }
aelement(A)611 aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
612 A = NULL;
613 if (ctx->ok) {
614 buffer_copy_buffer(&C->key, B);
615
616 A = C;
617 C = NULL;
618 }
619 if (C) C->fn->free(C);
620 C = NULL;
621 buffer_free(B);
622 B = NULL;
623 }
624
625 eols ::= EOL.
626 eols ::= .
627
628 globalstart ::= GLOBAL. {
629 data_config *dc;
630 dc = configparser_get_data_config(ctx->srv->config_context, CONST_STR_LEN("global"));
631 force_assert(dc);
632 configparser_push(ctx, dc, 0);
633 }
634
635 global ::= globalstart LCURLY metalines RCURLY. {
636 configparser_pop(ctx);
637 }
638
condlines(A)639 condlines(A) ::= condlines(B) eols ELSE condline(C). {
640 A = NULL;
641 if (ctx->ok) {
642 if (B->context_ndx >= C->context_ndx) {
643 fprintf(stderr, "unreachable else condition\n");
644 ctx->ok = 0;
645 }
646 if (B->cond == CONFIG_COND_ELSE) {
647 fprintf(stderr, "unreachable condition following else catch-all\n");
648 ctx->ok = 0;
649 }
650 C->prev = B;
651 B->next = C;
652 A = C;
653 }
654 B = NULL;
655 C = NULL;
656 }
657
condlines(A)658 condlines(A) ::= condlines(B) eols ELSE cond_else(C). {
659 A = NULL;
660 if (ctx->ok) {
661 if (B->context_ndx >= C->context_ndx) {
662 fprintf(stderr, "unreachable else condition\n");
663 ctx->ok = 0;
664 }
665 if (B->cond == CONFIG_COND_ELSE) {
666 fprintf(stderr, "unreachable condition following else catch-all\n");
667 ctx->ok = 0;
668 }
669 }
670 if (ctx->ok) {
671 size_t pos;
672 data_config *dc;
673 dc = (data_config *)array_extract_element_klen(ctx->all_configs, BUF_PTR_LEN(&C->key));
674 force_assert(C == dc);
675 buffer_copy_buffer(&C->key, &B->key);
676 C->comp_key = C->key.ptr + (B->comp_key - B->key.ptr);
677 C->comp = B->comp;
678 /*buffer_copy_buffer(&C->string, &B->string);*/
679 /* -2 for "==" and minus 3 for spaces and quotes around string (in key) */
680 pos = buffer_clen(&C->key) - buffer_clen(&B->string) - 5;
681 switch(B->cond) {
682 case CONFIG_COND_NE:
683 C->key.ptr[pos] = '='; /* opposite cond */
684 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=="));*/
685 break;
686 case CONFIG_COND_EQ:
687 C->key.ptr[pos] = '!'; /* opposite cond */
688 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!="));*/
689 break;
690 case CONFIG_COND_NOMATCH:
691 C->key.ptr[pos] = '='; /* opposite cond */
692 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=~"));*/
693 break;
694 case CONFIG_COND_MATCH:
695 C->key.ptr[pos] = '!'; /* opposite cond */
696 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!~"));*/
697 break;
698 case CONFIG_COND_PREFIX:
699 C->key.ptr[pos] = '!'; /* opposite cond */
700 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!^"));*/
701 break;
702 case CONFIG_COND_SUFFIX:
703 C->key.ptr[pos] = '!'; /* opposite cond */
704 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!$"));*/
705 break;
706 default: /* should not happen; CONFIG_COND_ELSE checked further above */
707 force_assert(0);
708 }
709
710 if (NULL == (dc = configparser_get_data_config(ctx->all_configs, BUF_PTR_LEN(&C->key)))) {
711 /* re-insert into ctx->all_configs with new C->key */
712 array_insert_unique(ctx->all_configs, (data_unset *)C);
713 C->prev = B;
714 B->next = C;
715 } else {
716 fprintf(stderr, "unreachable else condition\n");
717 ctx->ok = 0;
718 C->fn->free((data_unset *)C);
719 C = dc;
720 }
721
722 A = C;
723 }
724 B = NULL;
725 C = NULL;
726 }
727
condlines(A)728 condlines(A) ::= condline(B). {
729 A = B;
730 B = NULL;
731 }
732
condline(A)733 condline(A) ::= context LCURLY metalines RCURLY. {
734 A = NULL;
735 if (ctx->ok) {
736 A = configparser_pop(ctx);
737 }
738 }
739
cond_else(A)740 cond_else(A) ::= context_else LCURLY metalines RCURLY. {
741 A = NULL;
742 if (ctx->ok) {
743 A = configparser_pop(ctx);
744 }
745 }
746
SRVVARNAME(B)747 context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
748
749 if (ctx->ok && D->type != TYPE_STRING) {
750 fprintf(stderr, "rvalue must be string");
751 ctx->ok = 0;
752 }
753
754 if (ctx->ok) {
755 configparser_parse_condition(ctx, B, C, E, &((data_string *)D)->value);
756 }
757
758 buffer_free(B);
759 B = NULL;
760 buffer_free(C);
761 C = NULL;
762 if (D) D->fn->free(D);
763 D = NULL;
764 }
765
766 context_else ::= . {
767 if (ctx->ok) {
768 configparser_parse_else_condition(ctx);
769 }
770 }
771
cond(A)772 cond(A) ::= EQ. {
773 A = CONFIG_COND_EQ;
774 }
cond(A)775 cond(A) ::= MATCH. {
776 A = CONFIG_COND_MATCH;
777 }
cond(A)778 cond(A) ::= NE. {
779 A = CONFIG_COND_NE;
780 }
cond(A)781 cond(A) ::= NOMATCH. {
782 A = CONFIG_COND_NOMATCH;
783 }
cond(A)784 cond(A) ::= PREFIX. {
785 A = CONFIG_COND_PREFIX;
786 }
cond(A)787 cond(A) ::= SUFFIX. {
788 A = CONFIG_COND_SUFFIX;
789 }
790
stringop(A)791 stringop(A) ::= expression(B). {
792 A = NULL;
793 if (ctx->ok) {
794 if (B->type == TYPE_STRING) {
795 buffer_copy_buffer((A = buffer_init()), &((data_string*)B)->value);
796 } else if (B->type == TYPE_INTEGER) {
797 A = buffer_init();
798 buffer_append_int(A, ((data_integer *)B)->value);
799 } else {
800 fprintf(stderr, "operand must be string");
801 ctx->ok = 0;
802 }
803 }
804 if (B) B->fn->free(B);
805 B = NULL;
806 }
807
stringop(A)808 include ::= INCLUDE stringop(A). {
809 if (ctx->ok) {
810 if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
811 ctx->ok = 0;
812 }
813 }
814 buffer_free(A);
815 A = NULL;
816 }
817
stringop(A)818 include_shell ::= INCLUDE_SHELL stringop(A). {
819 if (ctx->ok) {
820 if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
821 ctx->ok = 0;
822 }
823 }
824 buffer_free(A);
825 A = NULL;
826 }
827