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