1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "apr_strings.h"
21 #define APR_WANT_STRFUNC
22 #define APR_WANT_MEMFUNC
31 * +-------------------------------------------------------+
33 * | Debugging Utilities
35 * +-------------------------------------------------------+
40 #define TYPE_TOKEN(token, ttype) do { \
41 (token)->type = ttype; \
42 (token)->s = #ttype; \
45 #define CREATE_NODE(pool,name) do { \
46 (name) = apr_palloc(pool, sizeof(*(name))); \
47 (name)->parent = (name)->left = (name)->right = NULL; \
49 (name)->dump_done = 0; \
52 static void debug_printf(request_rec *r, const char *fmt, ...)
58 debug__str = apr_pvsprintf(r->pool, fmt, ap);
61 APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
62 debug__str, strlen(debug__str), ctx->pool,
63 ctx->intern->debug.f->c->bucket_alloc));
67 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
68 parse_node_t *d__c = node->child; \
70 if (!d__c->dump_done) { \
71 if (d__c->parent != node) { \
72 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
73 if (!d__c->parent) { \
74 debug_printf(ctx, "Parent of " #child " child node is " \
78 debug_printf(ctx, "Parent of " #child " child node " \
79 "points to another node (of type %s)!\n", \
80 d__c->parent->token.s); \
89 debug_printf(ctx, "%s(missing)\n", is); \
93 static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
95 parse_node_t *current;
99 debug_printf(ctx, " -- Parse Tree empty --\n\n");
103 debug_printf(ctx, " ----- Parse Tree -----\n");
108 switch (current->token.type) {
111 debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
112 current->token.value);
113 current->dump_done = 1;
114 current = current->parent;
121 if (!current->dump_done) {
122 debug_printf(ctx, "%s%s\n", is, current->token.s);
123 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
124 current->dump_done = 1;
127 DUMP__CHILD(ctx, is, current, right)
129 if (!current->right || current->right->dump_done) {
130 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
131 if (current->right) current->right->dump_done = 0;
132 current = current->parent;
137 if (!current->dump_done) {
138 debug_printf(ctx, "%s%s\n", is, current->token.s);
139 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
140 current->dump_done = 1;
143 DUMP__CHILD(ctx, is, current, left)
144 DUMP__CHILD(ctx, is, current, right)
146 if ((!current->left || current->left->dump_done) &&
147 (!current->right || current->right->dump_done)) {
149 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
150 if (current->left) current->left->dump_done = 0;
151 if (current->right) current->right->dump_done = 0;
152 current = current->parent;
158 /* it is possible to call this function within the parser loop, to see
159 * how the tree is built. That way, we must cleanup after us to dump
160 * always the whole tree
163 if (root->left) root->left->dump_done = 0;
164 if (root->right) root->right->dump_done = 0;
166 debug_printf(ctx, " --- End Parse Tree ---\n\n");
171 #define DEBUG_INIT(ctx, filter, brigade) do { \
172 (ctx)->intern->debug.f = filter; \
173 (ctx)->intern->debug.bb = brigade; \
176 #define DEBUG_PRINTF(arg) debug_printf arg
178 #define DEBUG_DUMP_TOKEN(ctx, token) do { \
179 token_t *d__t = (token); \
181 if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
182 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
185 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
189 #define DEBUG_DUMP_EVAL(r, node) do { \
191 switch ((node)->token.type) { \
193 debug_printf((r), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
194 (node)->token.value, ((node)->value) ? '1':'0'); \
198 debug_printf((r), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
200 (((node)->left->done) ? ((node)->left->value ?"1":"0") \
201 : "short circuited"), \
202 (((node)->right->done) ? ((node)->right->value?"1":"0") \
203 : "short circuited"), \
204 (node)->value ? '1' : '0'); \
212 if ((node)->right->token.type == TOKEN_RE) c = '/'; \
213 debug_printf((r), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
215 (node)->left->token.value, \
216 c, (node)->right->token.value, c, \
217 (node)->value ? '1' : '0'); \
220 debug_printf((r), " Evaluate: %s -> %c\n", (node)->token.s, \
221 (node)->value ? '1' : '0'); \
226 #define DEBUG_DUMP_UNMATCHED(r, unmatched) do { \
228 DEBUG_PRINTF(((r), " Unmatched %c\n", (char)(unmatched))); \
232 #define DEBUG_DUMP_COND(ctx, text) \
233 DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
234 ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
236 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
238 #else /* DEBUG_INCLUDE */
240 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
242 #define CREATE_NODE(pool,name) do { \
243 (name) = apr_palloc(pool, sizeof(*(name))); \
244 (name)->parent = (name)->left = (name)->right = NULL; \
248 #define DEBUG_INIT(ctx, f, bb)
249 #define DEBUG_PRINTF(arg)
250 #define DEBUG_DUMP_TOKEN(ctx, token)
251 #define DEBUG_DUMP_EVAL(ctx, node)
252 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
253 #define DEBUG_DUMP_COND(ctx, text)
254 #define DEBUG_DUMP_TREE(ctx, root)
256 #endif /* !DEBUG_INCLUDE */
262 * +-------------------------------------------------------+
264 * | Conditional Expression Parser
266 * +-------------------------------------------------------+
268 static APR_INLINE int re_check(request_rec *r, const char *string,
269 const char *rexp, backref_t **reptr)
271 ap_regex_t *compiled;
272 backref_t *re = *reptr;
275 compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
277 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to "
278 "compile pattern \"%s\"", rexp);
283 re = *reptr = apr_palloc(r->pool, sizeof(*re));
286 re->source = apr_pstrdup(r->pool, string);
287 re->rexp = apr_pstrdup(r->pool, rexp);
288 re->nsub = compiled->re_nsub;
289 rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
291 ap_pregfree(r->pool, compiled);
295 static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
308 /* Skip leading white space */
309 while (apr_isspace(**parse)) {
318 TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
322 switch (*(*parse)++) {
324 TYPE_TOKEN(token, TOKEN_LBRACE);
327 TYPE_TOKEN(token, TOKEN_RBRACE);
330 if (**parse == '=') ++*parse;
331 TYPE_TOKEN(token, TOKEN_EQ);
334 if (**parse == '=') {
335 TYPE_TOKEN(token, TOKEN_NE);
339 TYPE_TOKEN(token, TOKEN_NOT);
345 /* if last token was ACCESS, this token is STRING */
346 if (previous != NULL && TOKEN_ACCESS == previous->type) {
349 TYPE_TOKEN(token, TOKEN_RE);
353 if (**parse == '|') {
354 TYPE_TOKEN(token, TOKEN_OR);
360 if (**parse == '&') {
361 TYPE_TOKEN(token, TOKEN_AND);
367 if (**parse == '=') {
368 TYPE_TOKEN(token, TOKEN_GE);
372 TYPE_TOKEN(token, TOKEN_GT);
375 if (**parse == '=') {
376 TYPE_TOKEN(token, TOKEN_LE);
380 TYPE_TOKEN(token, TOKEN_LT);
383 if (apr_isalnum(**parse) && apr_isspace((*parse)[1])) {
384 TYPE_TOKEN(token, TOKEN_ACCESS);
385 token->value = *parse;
392 /* It's a string or regex token
393 * Now search for the next token, which finishes this string
396 p = *parse = token->value = unmatched ? *parse : p;
398 for (; **parse; p = ++*parse) {
399 if (**parse == '\\') {
409 if (**parse == unmatched) {
415 else if (apr_isspace(**parse)) {
433 if ((*parse)[1] == **parse) {
447 token->value = apr_pstrdup(pool, "");
450 apr_size_t len = p - token->value - shift;
451 char *c = apr_palloc(pool, len + 1);
457 const char *e = ap_strchr_c(p, '\\');
475 /* This is what we export. We can split it in two. */
476 AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
479 parse_node_t *new, *root = NULL, *current = NULL;
480 const char *error = "Invalid expression \"%s\" in file %s";
481 const char *parse = expr;
482 int was_unmatched = 0;
491 /* Create Parse Tree */
493 /* uncomment this to see how the tree a built:
495 * DEBUG_DUMP_TREE(ctx, root);
497 CREATE_NODE(pool, new);
499 was_unmatched = get_ptoken(pool, &parse, &new->token,
500 (current != NULL ? ¤t->token : NULL));
505 DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
506 DEBUG_DUMP_TOKEN(ctx, &new->token);
509 switch (new->token.type) {
514 root = current = new;
523 switch (new->token.type) {
525 switch (current->token.type) {
527 current->token.value =
528 apr_pstrcat(pool, current->token.value,
529 *current->token.value ? " " : "",
530 new->token.value, NULL);
539 new->parent = current;
540 current = current->right = new;
546 switch (current->token.type) {
549 new->parent = current;
550 current = current->right = new;
561 switch (current->token.type) {
565 current = current->parent;
568 switch (current->token.type) {
575 current = current->parent;
584 current = root = new;
588 new->left = current->right;
589 new->left->parent = new;
590 new->parent = current;
591 current = current->right = new;
605 if (current->token.type == TOKEN_STRING) {
606 current = current->parent;
611 current = root = new;
615 switch (current->token.type) {
619 new->left = current->right;
620 new->left->parent = new;
621 new->parent = current;
622 current = current->right = new;
632 while (current && current->token.type != TOKEN_LBRACE) {
633 current = current->parent;
637 TYPE_TOKEN(¤t->token, TOKEN_GROUP);
641 error = "Unmatched ')' in \"%s\" in file %s";
647 switch (current->token.type) {
655 current->right = new;
656 new->parent = current;
670 DEBUG_DUMP_TREE(ctx, root);
674 #define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
675 AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
676 int *was_error, backref_t **reptr,
677 string_func_t string_func, opt_func_t eval_func)
679 parse_node_t *current = root;
680 const char *error = NULL;
681 unsigned int regex = 0;
683 /* Evaluate Parse Tree */
685 switch (current->token.type) {
687 current->token.value = PARSE_STRING(r, current->token.value);
688 current->value = !!*current->token.value;
693 if (!current->left || !current->right) {
694 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
695 "Invalid expression in file %s", r->filename);
700 if (!current->left->done) {
701 switch (current->left->token.type) {
703 current->left->token.value =
704 PARSE_STRING(r, current->left->token.value);
705 current->left->value = !!*current->left->token.value;
706 DEBUG_DUMP_EVAL(ctx, current->left);
707 current->left->done = 1;
711 current = current->left;
716 /* short circuit evaluation */
717 if (!current->right->done && !regex &&
718 ((current->token.type == TOKEN_AND && !current->left->value) ||
719 (current->token.type == TOKEN_OR && current->left->value))) {
720 current->value = current->left->value;
723 if (!current->right->done) {
724 switch (current->right->token.type) {
726 current->right->token.value =
727 PARSE_STRING(r,current->right->token.value);
728 current->right->value = !!*current->right->token.value;
729 DEBUG_DUMP_EVAL(r, current->right);
730 current->right->done = 1;
734 current = current->right;
739 if (current->token.type == TOKEN_AND) {
740 current->value = current->left->value &&
741 current->right->value;
744 current->value = current->left->value ||
745 current->right->value;
752 if (!current->left || !current->right ||
753 current->left->token.type != TOKEN_STRING ||
754 (current->right->token.type != TOKEN_STRING &&
755 current->right->token.type != TOKEN_RE)) {
756 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
757 "Invalid expression in file %s", r->filename);
761 current->left->token.value =
762 PARSE_STRING(r, current->left->token.value);
763 current->right->token.value =
764 PARSE_STRING(r, current->right->token.value);
766 if (current->right->token.type == TOKEN_RE) {
767 current->value = re_check(r, current->left->token.value,
768 current->right->token.value, reptr);
772 current->value = !strcmp(current->left->token.value,
773 current->right->token.value);
776 if (current->token.type == TOKEN_NE) {
777 current->value = !current->value;
785 if (!current->left || !current->right ||
786 current->left->token.type != TOKEN_STRING ||
787 current->right->token.type != TOKEN_STRING) {
788 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
789 "Invalid expression in file %s", r->filename);
794 current->left->token.value =
795 PARSE_STRING(r, current->left->token.value);
796 current->right->token.value =
797 PARSE_STRING(r, current->right->token.value);
799 current->value = strcmp(current->left->token.value,
800 current->right->token.value);
802 switch (current->token.type) {
803 case TOKEN_GE: current->value = current->value >= 0; break;
804 case TOKEN_GT: current->value = current->value > 0; break;
805 case TOKEN_LE: current->value = current->value <= 0; break;
806 case TOKEN_LT: current->value = current->value < 0; break;
807 default: current->value = 0; break; /* should not happen */
813 if (current->right) {
814 if (!current->right->done) {
815 current = current->right;
818 current->value = current->right->value;
824 if (current->token.type == TOKEN_NOT) {
825 current->value = !current->value;
830 *was_error = eval_func(r, current, string_func);
839 error = "No operator before regex in file %s";
843 error = "Unmatched '(' in file %s";
847 error = "internal parser error in file %s";
850 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, r->filename);
855 DEBUG_DUMP_EVAL(r, current);
857 current = current->parent;
860 return (root ? root->value : 0);
862 AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
863 int *was_error, backref_t **reptr,
864 string_func_t string_func,
865 opt_func_t eval_func)
867 parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
868 if (*was_error || !root) {
869 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
870 "Error parsing expression in %s", r->filename);
873 return ap_expr_eval(r, root, was_error, reptr, string_func, eval_func);