use buffer_string_set_length() to truncate strings
[lighttpd.git] / src / configparser.y
blob349fbfe4fc317a10c388a0408ba56f0132de71fc
1 %token_prefix TK_
2 %extra_argument {config_t *ctx}
3 %name configparser
5 %include {
6 #include "first.h"
7 #include "configfile.h"
8 #include "buffer.h"
9 #include "array.h"
10 #include "request.h" /* http_request_host_normalize() */
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
18 static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
19 if (isnew) {
20 dc->context_ndx = ctx->all_configs->used;
21 force_assert(dc->context_ndx > ctx->current->context_ndx);
22 array_insert_unique(ctx->all_configs, (data_unset *)dc);
23 dc->parent = ctx->current;
24 vector_config_weak_push(&dc->parent->children, dc);
26 if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) {
27 fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n");
28 exit(-1);
30 vector_config_weak_push(&ctx->configs_stack, ctx->current);
31 ctx->current = dc;
34 static data_config *configparser_pop(config_t *ctx) {
35 data_config *old = ctx->current;
36 ctx->current = vector_config_weak_pop(&ctx->configs_stack);
37 return old;
40 /* return a copied variable */
41 static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
42 data_unset *du;
43 data_config *dc;
45 #if 0
46 fprintf(stderr, "get var %s\n", key->ptr);
47 #endif
48 for (dc = ctx->current; dc; dc = dc->parent) {
49 #if 0
50 fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
51 array_print(dc->value, 0);
52 #endif
53 if (NULL != (du = array_get_element(dc->value, key->ptr))) {
54 return du->copy(du);
57 return NULL;
60 /* op1 is to be eat/return by this function if success, op1->key is not cared
61 op2 is left untouch, unreferenced
63 data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
64 /* type mismatch */
65 if (op1->type != op2->type) {
66 if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
67 data_string *ds = (data_string *)op1;
68 buffer_append_int(ds->value, ((data_integer*)op2)->value);
69 return op1;
70 } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
71 data_string *ds = data_string_init();
72 buffer_append_int(ds->value, ((data_integer*)op1)->value);
73 buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
74 op1->free(op1);
75 return (data_unset *)ds;
76 } else {
77 fprintf(stderr, "data type mismatch, cannot merge\n");
78 op1->free(op1);
79 return NULL;
83 switch (op1->type) {
84 case TYPE_STRING:
85 buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
86 break;
87 case TYPE_INTEGER:
88 ((data_integer *)op1)->value += ((data_integer *)op2)->value;
89 break;
90 case TYPE_ARRAY: {
91 array *dst = ((data_array *)op1)->value;
92 array *src = ((data_array *)op2)->value;
93 data_unset *du;
94 size_t i;
96 for (i = 0; i < src->used; i ++) {
97 du = (data_unset *)src->data[i];
98 if (du) {
99 if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element(dst, du->key->ptr)) {
100 array_insert_unique(dst, du->copy(du));
101 } else {
102 fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr);
103 op1->free(op1);
104 return NULL;
108 break;
109 default:
110 force_assert(0);
111 break;
114 return op1;
119 %parse_failure {
120 ctx->ok = 0;
123 input ::= metalines.
124 metalines ::= metalines metaline.
125 metalines ::= .
126 metaline ::= varline.
127 metaline ::= global.
128 metaline ::= condlines(A) EOL. { A = NULL; }
129 metaline ::= include.
130 metaline ::= include_shell.
131 metaline ::= EOL.
133 %type value {data_unset *}
134 %type expression {data_unset *}
135 %type aelement {data_unset *}
136 %type condline {data_config *}
137 %type condlines {data_config *}
138 %type aelements {array *}
139 %type array {array *}
140 %type key {buffer *}
141 %type stringop {buffer *}
143 %type cond {config_cond_t }
145 %destructor value { if ($$) $$->free($$); }
146 %destructor expression { if ($$) $$->free($$); }
147 %destructor aelement { if ($$) $$->free($$); }
148 %destructor aelements { array_free($$); }
149 %destructor array { array_free($$); }
150 %destructor key { buffer_free($$); }
151 %destructor stringop { buffer_free($$); }
153 %token_type {buffer *}
154 %token_destructor { buffer_free($$); }
156 varline ::= key(A) ASSIGN expression(B). {
157 if (ctx->ok) {
158 buffer_copy_buffer(B->key, A);
159 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
160 fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
161 ctx->current->context_ndx,
162 ctx->current->key->ptr, A->ptr);
163 ctx->ok = 0;
164 } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
165 array_insert_unique(ctx->current->value, B);
166 B = NULL;
167 } else {
168 fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
169 ctx->current->context_ndx,
170 ctx->current->key->ptr, B->key->ptr);
171 ctx->ok = 0;
172 B->free(B);
173 B = NULL;
176 buffer_free(A);
177 A = NULL;
180 varline ::= key(A) APPEND expression(B). {
181 if (ctx->ok) {
182 array *vars = ctx->current->value;
183 data_unset *du;
185 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
186 fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
187 ctx->current->context_ndx,
188 ctx->current->key->ptr, A->ptr);
189 ctx->ok = 0;
190 } else if (NULL != (du = array_extract_element(vars, A->ptr)) || NULL != (du = configparser_get_variable(ctx, A))) {
191 du = configparser_merge_data(du, B);
192 if (NULL == du) {
193 ctx->ok = 0;
195 else {
196 buffer_copy_buffer(du->key, A);
197 array_insert_unique(ctx->current->value, du);
199 B->free(B);
200 } else {
201 buffer_copy_buffer(B->key, A);
202 array_insert_unique(ctx->current->value, B);
204 buffer_free(A);
205 A = NULL;
206 B = NULL;
210 key(A) ::= LKEY(B). {
211 if (strchr(B->ptr, '.') == NULL) {
212 A = buffer_init_string("var.");
213 buffer_append_string_buffer(A, B);
214 buffer_free(B);
215 B = NULL;
216 } else {
217 A = B;
218 B = NULL;
222 expression(A) ::= expression(B) PLUS value(C). {
223 A = NULL;
224 if (ctx->ok) {
225 A = configparser_merge_data(B, C);
226 if (NULL == A) {
227 ctx->ok = 0;
229 B = NULL;
230 C->free(C);
231 C = NULL;
235 expression(A) ::= value(B). {
236 A = B;
237 B = NULL;
240 value(A) ::= key(B). {
241 A = NULL;
242 if (ctx->ok) {
243 if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
244 char *env;
246 if (NULL != (env = getenv(B->ptr + 4))) {
247 data_string *ds;
248 ds = data_string_init();
249 buffer_append_string(ds->value, env);
250 A = (data_unset *)ds;
252 else {
253 fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
254 ctx->ok = 0;
256 } else if (NULL == (A = configparser_get_variable(ctx, B))) {
257 fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
258 ctx->ok = 0;
260 buffer_free(B);
261 B = NULL;
265 value(A) ::= STRING(B). {
266 A = (data_unset *)data_string_init();
267 buffer_copy_buffer(((data_string *)(A))->value, B);
268 buffer_free(B);
269 B = NULL;
272 value(A) ::= INTEGER(B). {
273 char *endptr;
274 A = (data_unset *)data_integer_init();
275 errno = 0;
276 ((data_integer *)(A))->value = strtol(B->ptr, &endptr, 10);
277 /* skip trailing whitespace */
278 if (endptr != B->ptr) while (isspace(*endptr)) endptr++;
279 if (0 != errno || *endptr != '\0') {
280 fprintf(stderr, "error parsing number: '%s'\n", B->ptr);
281 ctx->ok = 0;
283 buffer_free(B);
284 B = NULL;
286 value(A) ::= array(B). {
287 A = (data_unset *)data_array_init();
288 array_free(((data_array *)(A))->value);
289 ((data_array *)(A))->value = B;
290 B = NULL;
292 array(A) ::= LPARAN RPARAN. {
293 A = array_init();
295 array(A) ::= LPARAN aelements(B) RPARAN. {
296 A = B;
297 B = NULL;
300 aelements(A) ::= aelements(C) COMMA aelement(B). {
301 A = NULL;
302 if (ctx->ok) {
303 if (buffer_is_empty(B->key) ||
304 NULL == array_get_element(C, B->key->ptr)) {
305 array_insert_unique(C, B);
306 B = NULL;
307 } else {
308 fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
309 B->key->ptr);
310 ctx->ok = 0;
311 B->free(B);
312 B = NULL;
315 A = C;
316 C = NULL;
320 aelements(A) ::= aelements(C) COMMA. {
321 A = C;
322 C = NULL;
325 aelements(A) ::= aelement(B). {
326 A = NULL;
327 if (ctx->ok) {
328 A = array_init();
329 array_insert_unique(A, B);
330 B = NULL;
334 aelement(A) ::= expression(B). {
335 A = B;
336 B = NULL;
338 aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
339 A = NULL;
340 if (ctx->ok) {
341 buffer_copy_buffer(C->key, B);
342 buffer_free(B);
343 B = NULL;
345 A = C;
346 C = NULL;
350 eols ::= EOL.
351 eols ::= .
353 globalstart ::= GLOBAL. {
354 data_config *dc;
355 dc = (data_config *)array_get_element(ctx->srv->config_context, "global");
356 force_assert(dc);
357 configparser_push(ctx, dc, 0);
360 global ::= globalstart LCURLY metalines RCURLY. {
361 force_assert(ctx->current);
362 configparser_pop(ctx);
363 force_assert(ctx->current);
366 condlines(A) ::= condlines(B) eols ELSE condline(C). {
367 A = NULL;
368 if (ctx->ok) {
369 if (B->context_ndx >= C->context_ndx) {
370 fprintf(stderr, "unreachable else condition\n");
371 ctx->ok = 0;
373 C->prev = B;
374 B->next = C;
375 A = C;
376 B = NULL;
377 C = NULL;
381 condlines(A) ::= condline(B). {
382 A = B;
383 B = NULL;
386 condline(A) ::= context LCURLY metalines RCURLY. {
387 A = NULL;
388 if (ctx->ok) {
389 data_config *cur;
391 cur = ctx->current;
392 configparser_pop(ctx);
394 force_assert(cur && ctx->current);
396 A = cur;
400 context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
401 data_config *dc;
402 buffer *b, *rvalue, *op;
404 if (ctx->ok && D->type != TYPE_STRING) {
405 fprintf(stderr, "rvalue must be string");
406 ctx->ok = 0;
409 if (ctx->ok) {
410 switch(E) {
411 case CONFIG_COND_NE:
412 op = buffer_init_string("!=");
413 break;
414 case CONFIG_COND_EQ:
415 op = buffer_init_string("==");
416 break;
417 case CONFIG_COND_NOMATCH:
418 op = buffer_init_string("!~");
419 break;
420 case CONFIG_COND_MATCH:
421 op = buffer_init_string("=~");
422 break;
423 default:
424 force_assert(0);
425 return; /* unreachable */
428 b = buffer_init();
429 buffer_copy_buffer(b, ctx->current->key);
430 buffer_append_string(b, "/");
431 buffer_append_string_buffer(b, B);
432 buffer_append_string_buffer(b, C);
433 buffer_append_string_buffer(b, op);
434 rvalue = ((data_string*)D)->value;
435 buffer_append_string_buffer(b, rvalue);
437 if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
438 configparser_push(ctx, dc, 0);
439 } else {
440 static const struct {
441 comp_key_t comp;
442 char *comp_key;
443 size_t len;
444 } comps[] = {
445 { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) },
446 { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) },
447 { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) },
448 { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) },
449 { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) },
450 { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) },
451 { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) },
452 { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) },
453 { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) },
454 { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) },
455 { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") },
456 { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") },
457 { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") },
458 { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) },
459 { COMP_UNSET, NULL, 0 },
461 size_t i;
463 dc = data_config_init();
465 buffer_copy_buffer(dc->key, b);
466 buffer_copy_buffer(dc->op, op);
467 buffer_copy_buffer(dc->comp_key, B);
468 buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\""));
469 buffer_append_string_buffer(dc->comp_key, C);
470 buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]"));
471 dc->cond = E;
473 for (i = 0; comps[i].comp_key; i ++) {
474 if (buffer_is_equal_string(
475 dc->comp_key, comps[i].comp_key, comps[i].len)) {
476 dc->comp = comps[i].comp;
477 break;
480 if (COMP_UNSET == dc->comp) {
481 fprintf(stderr, "error comp_key %s", dc->comp_key->ptr);
482 ctx->ok = 0;
484 else if (COMP_HTTP_REMOTE_IP == dc->comp
485 && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) {
486 char * const slash = strchr(rvalue->ptr, '/');
487 if (NULL != slash && slash != rvalue->ptr){/*(skip AF_UNIX /path/file)*/
488 char *nptr;
489 const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10);
490 if (*nptr || 0 == nm_bits
491 || nm_bits > (rvalue->ptr[0] == '[' ? 128 : 32)) {
492 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
493 fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr);
494 ctx->ok = 0;
496 else {
497 int rc;
498 buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/
499 rc = http_request_host_normalize(rvalue);
500 buffer_append_string_len(rvalue, CONST_STR_LEN("/"));
501 buffer_append_int(rvalue, (int)nm_bits);
502 if (0 != rc) {
503 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
504 ctx->ok = 0;
508 else {
509 if (http_request_host_normalize(rvalue)) {
510 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
511 ctx->ok = 0;
515 else if (COMP_SERVER_SOCKET == dc->comp) {
516 /*(redundant with parsing in network.c; not actually required here)*/
517 if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/
518 && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) {
519 if (http_request_host_normalize(rvalue)) {
520 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
521 ctx->ok = 0;
525 else if (COMP_HTTP_HOST == dc->comp) {
526 if (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) {
527 if (http_request_host_normalize(rvalue)) {
528 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
529 ctx->ok = 0;
534 if (ctx->ok) switch(E) {
535 case CONFIG_COND_NE:
536 case CONFIG_COND_EQ:
537 dc->string = buffer_init_buffer(rvalue);
538 break;
539 case CONFIG_COND_NOMATCH:
540 case CONFIG_COND_MATCH: {
541 #ifdef HAVE_PCRE_H
542 const char *errptr;
543 int erroff, captures;
545 if (NULL == (dc->regex =
546 pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
547 dc->string = buffer_init_string(errptr);
548 dc->cond = CONFIG_COND_UNSET;
550 fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
551 rvalue->ptr, errptr, erroff);
553 ctx->ok = 0;
554 } else if (NULL == (dc->regex_study =
555 pcre_study(dc->regex, 0, &errptr)) &&
556 errptr != NULL) {
557 fprintf(stderr, "studying regex failed: %s -> %s\n",
558 rvalue->ptr, errptr);
559 ctx->ok = 0;
560 } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) {
561 fprintf(stderr, "getting capture count for regex failed: %s\n",
562 rvalue->ptr);
563 ctx->ok = 0;
564 } else if (captures > 9) {
565 fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n",
566 rvalue->ptr);
567 ctx->ok = 0;
568 } else {
569 dc->string = buffer_init_buffer(rvalue);
571 #else
572 fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
573 "(perhaps just a missing pcre-devel package ?) \n",
574 B->ptr, C->ptr);
575 ctx->ok = 0;
576 #endif
577 break;
580 default:
581 fprintf(stderr, "unknown condition for $%s[%s]\n",
582 B->ptr, C->ptr);
583 ctx->ok = 0;
584 break;
587 if (ctx->ok) {
588 configparser_push(ctx, dc, 1);
589 } else {
590 dc->free((data_unset*) dc);
594 buffer_free(b);
595 buffer_free(op);
596 buffer_free(B);
597 B = NULL;
598 buffer_free(C);
599 C = NULL;
600 D->free(D);
601 D = NULL;
604 cond(A) ::= EQ. {
605 A = CONFIG_COND_EQ;
607 cond(A) ::= MATCH. {
608 A = CONFIG_COND_MATCH;
610 cond(A) ::= NE. {
611 A = CONFIG_COND_NE;
613 cond(A) ::= NOMATCH. {
614 A = CONFIG_COND_NOMATCH;
617 stringop(A) ::= expression(B). {
618 A = NULL;
619 if (ctx->ok) {
620 if (B->type == TYPE_STRING) {
621 A = buffer_init_buffer(((data_string*)B)->value);
622 } else if (B->type == TYPE_INTEGER) {
623 A = buffer_init();
624 buffer_copy_int(A, ((data_integer *)B)->value);
625 } else {
626 fprintf(stderr, "operand must be string");
627 ctx->ok = 0;
630 B->free(B);
631 B = NULL;
634 include ::= INCLUDE stringop(A). {
635 if (ctx->ok) {
636 if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
637 ctx->ok = 0;
639 buffer_free(A);
640 A = NULL;
644 include_shell ::= INCLUDE_SHELL stringop(A). {
645 if (ctx->ok) {
646 if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
647 ctx->ok = 0;
649 buffer_free(A);
650 A = NULL;