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