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