xref: /lighttpd1.4/src/configparser.y (revision 782fa347)
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