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