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