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