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