]> granicus.if.org Git - apache/blob - server/util_expr.c
Thou shalt not let tabs creep in.
[apache] / server / util_expr.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "apr.h"
18 #include "apr_strings.h"
19 #include "apr_lib.h"
20
21 #define APR_WANT_STRFUNC
22 #define APR_WANT_MEMFUNC
23 #include "apr_want.h"
24
25 #include "httpd.h"
26 #include "http_log.h"
27
28 #include "ap_expr.h"
29 #if 1
30 /*
31  * +-------------------------------------------------------+
32  * |                                                       |
33  * |                  Debugging Utilities
34  * |                                                       |
35  * +-------------------------------------------------------+
36  */
37
38 #ifdef DEBUG_INCLUDE
39
40 #define TYPE_TOKEN(token, ttype) do { \
41     (token)->type = ttype;            \
42     (token)->s = #ttype;              \
43 } while(0)
44
45 #define CREATE_NODE(pool,name) do {                          \
46     (name) = apr_palloc(pool, sizeof(*(name)));      \
47     (name)->parent = (name)->left = (name)->right = NULL; \
48     (name)->done = 0;                                     \
49     (name)->dump_done = 0;                                \
50 } while(0)
51
52 static void debug_printf(request_rec *r, const char *fmt, ...)
53 {
54     va_list ap;
55     char *debug__str;
56
57     va_start(ap, fmt);
58     debug__str = apr_pvsprintf(r->pool, fmt, ap);
59     va_end(ap);
60 /*
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));
64                             */
65 }
66
67 #define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
68     parse_node_t *d__c = node->child;                                        \
69     if (d__c) {                                                              \
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 "  \
75                                  "NULL.\n");                                 \
76                 }                                                            \
77                 else {                                                       \
78                     debug_printf(ctx, "Parent of " #child " child node "     \
79                                  "points to another node (of type %s)!\n",   \
80                                  d__c->parent->token.s);                     \
81                 }                                                            \
82                 return;                                                      \
83             }                                                                \
84             node = d__c;                                                     \
85             continue;                                                        \
86         }                                                                    \
87     }                                                                        \
88     else {                                                                   \
89         debug_printf(ctx, "%s(missing)\n", is);                              \
90     }                                                                        \
91 }
92
93 static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
94 {
95     parse_node_t *current;
96     char *is;
97
98     if (!root) {
99         debug_printf(ctx, "     -- Parse Tree empty --\n\n");
100         return;
101     }
102
103     debug_printf(ctx, "     ----- Parse Tree -----\n");
104     current = root;
105     is = "     ";
106
107     while (current) {
108         switch (current->token.type) {
109         case TOKEN_STRING:
110         case TOKEN_RE:
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;
115             continue;
116
117         case TOKEN_NOT:
118         case TOKEN_GROUP:
119         case TOKEN_RBRACE:
120         case TOKEN_LBRACE:
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;
125             }
126
127             DUMP__CHILD(ctx, is, current, right)
128
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;
133             }
134             continue;
135
136         default:
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;
141             }
142
143             DUMP__CHILD(ctx, is, current, left)
144             DUMP__CHILD(ctx, is, current, right)
145
146             if ((!current->left || current->left->dump_done) &&
147                 (!current->right || current->right->dump_done)) {
148
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;
153             }
154             continue;
155         }
156     }
157
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
161      */
162     root->dump_done = 0;
163     if (root->left) root->left->dump_done = 0;
164     if (root->right) root->right->dump_done = 0;
165
166     debug_printf(ctx, "     --- End Parse Tree ---\n\n");
167
168     return;
169 }
170
171 #define DEBUG_INIT(ctx, filter, brigade) do { \
172     (ctx)->intern->debug.f = filter;          \
173     (ctx)->intern->debug.bb = brigade;        \
174 } while(0)
175
176 #define DEBUG_PRINTF(arg) debug_printf arg
177
178 #define DEBUG_DUMP_TOKEN(ctx, token) do {                                     \
179     token_t *d__t = (token);                                                  \
180                                                                               \
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)); \
183     }                                                                         \
184     else {                                                                    \
185         DEBUG_PRINTF((ctx, "     Found: %s\n", d__t->s));                     \
186     }                                                                         \
187 } while(0)
188
189 #define DEBUG_DUMP_EVAL(r, node) do {                                       \
190     char c = '"';                                                             \
191     switch ((node)->token.type) {                                             \
192     case TOKEN_STRING:                                                        \
193         debug_printf((r), "     Evaluate: %s (%s) -> %c\n", (node)->token.s,\
194                      (node)->token.value, ((node)->value) ? '1':'0');         \
195         break;                                                                \
196     case TOKEN_AND:                                                           \
197     case TOKEN_OR:                                                            \
198         debug_printf((r), "     Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
199                      (node)->token.s,                                         \
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');                              \
205         break;                                                                \
206     case TOKEN_EQ:                                                            \
207     case TOKEN_NE:                                                            \
208     case TOKEN_GT:                                                            \
209     case TOKEN_GE:                                                            \
210     case TOKEN_LT:                                                            \
211     case TOKEN_LE:                                                            \
212         if ((node)->right->token.type == TOKEN_RE) c = '/';                   \
213         debug_printf((r), "     Compare:  %s (\"%s\" with %c%s%c) -> %c\n", \
214                      (node)->token.s,                                         \
215                      (node)->left->token.value,                               \
216                      c, (node)->right->token.value, c,                        \
217                      (node)->value ? '1' : '0');                              \
218         break;                                                                \
219     default:                                                                  \
220         debug_printf((r), "     Evaluate: %s -> %c\n", (node)->token.s,     \
221                      (node)->value ? '1' : '0');                              \
222         break;                                                                \
223     }                                                                         \
224 } while(0)
225
226 #define DEBUG_DUMP_UNMATCHED(r, unmatched) do {                        \
227     if (unmatched) {                                                     \
228         DEBUG_PRINTF(((r), "     Unmatched %c\n", (char)(unmatched))); \
229     }                                                                    \
230 } while(0)
231
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'))
235
236 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
237
238 #else /* DEBUG_INCLUDE */
239
240 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
241
242 #define CREATE_NODE(pool,name) do {                       \
243     (name) = apr_palloc(pool, sizeof(*(name)));   \
244     (name)->parent = (name)->left = (name)->right = NULL; \
245     (name)->done = 0;                                     \
246 } while(0)
247
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)
255
256 #endif /* !DEBUG_INCLUDE */
257
258 #endif /* 0 */
259
260
261 /*
262  * +-------------------------------------------------------+
263  * |                                                       |
264  * |              Conditional Expression Parser
265  * |                                                       |
266  * +-------------------------------------------------------+
267  */
268 static APR_INLINE int re_check(request_rec *r, const char *string,
269                                const char *rexp, backref_t **reptr)
270 {
271     ap_regex_t *compiled;
272     backref_t *re = *reptr;
273     int rc;
274
275     compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
276     if (!compiled) {
277         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to "
278                       "compile pattern \"%s\"", rexp);
279         return -1;
280     }
281
282     if (!re) {
283         re = *reptr = apr_palloc(r->pool, sizeof(*re));
284     }
285
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);
290
291     ap_pregfree(r->pool, compiled);
292     return rc;
293 }
294
295 static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
296                       token_t *previous)
297 {
298     const char *p;
299     apr_size_t shift;
300     int unmatched;
301
302     token->value = NULL;
303
304     if (!*parse) {
305         return 0;
306     }
307
308     /* Skip leading white space */
309     while (apr_isspace(**parse)) {
310         ++*parse;
311     }
312
313     if (!**parse) {
314         *parse = NULL;
315         return 0;
316     }
317
318     TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
319     p = *parse;
320     unmatched = 0;
321
322     switch (*(*parse)++) {
323     case '(':
324         TYPE_TOKEN(token, TOKEN_LBRACE);
325         return 0;
326     case ')':
327         TYPE_TOKEN(token, TOKEN_RBRACE);
328         return 0;
329     case '=':
330         if (**parse == '=') ++*parse;
331         TYPE_TOKEN(token, TOKEN_EQ);
332         return 0;
333     case '!':
334         if (**parse == '=') {
335             TYPE_TOKEN(token, TOKEN_NE);
336             ++*parse;
337             return 0;
338         }
339         TYPE_TOKEN(token, TOKEN_NOT);
340         return 0;
341     case '\'':
342         unmatched = '\'';
343         break;
344     case '/':
345         /* if last token was ACCESS, this token is STRING */
346         if (previous != NULL && TOKEN_ACCESS == previous->type) {
347             break;
348         }
349         TYPE_TOKEN(token, TOKEN_RE);
350         unmatched = '/';
351         break;
352     case '|':
353         if (**parse == '|') {
354             TYPE_TOKEN(token, TOKEN_OR);
355             ++*parse;
356             return 0;
357         }
358         break;
359     case '&':
360         if (**parse == '&') {
361             TYPE_TOKEN(token, TOKEN_AND);
362             ++*parse;
363             return 0;
364         }
365         break;
366     case '>':
367         if (**parse == '=') {
368             TYPE_TOKEN(token, TOKEN_GE);
369             ++*parse;
370             return 0;
371         }
372         TYPE_TOKEN(token, TOKEN_GT);
373         return 0;
374     case '<':
375         if (**parse == '=') {
376             TYPE_TOKEN(token, TOKEN_LE);
377             ++*parse;
378             return 0;
379         }
380         TYPE_TOKEN(token, TOKEN_LT);
381         return 0;
382     case '-':
383         if (apr_isalnum(**parse) && apr_isspace((*parse)[1])) {
384             TYPE_TOKEN(token, TOKEN_ACCESS);
385             token->value = *parse;
386             ++*parse;
387             return 0;
388         }
389         break;
390     }
391
392     /* It's a string or regex token
393      * Now search for the next token, which finishes this string
394      */
395     shift = 0;
396     p = *parse = token->value = unmatched ? *parse : p;
397
398     for (; **parse; p = ++*parse) {
399         if (**parse == '\\') {
400             if (!*(++*parse)) {
401                 p = *parse;
402                 break;
403             }
404
405             ++shift;
406         }
407         else {
408             if (unmatched) {
409                 if (**parse == unmatched) {
410                     unmatched = 0;
411                     ++*parse;
412                     break;
413                 }
414             }
415             else if (apr_isspace(**parse)) {
416                 break;
417             }
418             else {
419                 int found = 0;
420
421                 switch (**parse) {
422                 case '(':
423                 case ')':
424                 case '=':
425                 case '!':
426                 case '<':
427                 case '>':
428                     ++found;
429                     break;
430
431                 case '|':
432                 case '&':
433                     if ((*parse)[1] == **parse) {
434                         ++found;
435                     }
436                     break;
437                 }
438
439                 if (found) {
440                     break;
441                 }
442             }
443         }
444     }
445
446     if (unmatched) {
447         token->value = apr_pstrdup(pool, "");
448     }
449     else {
450         apr_size_t len = p - token->value - shift;
451         char *c = apr_palloc(pool, len + 1);
452
453         p = token->value;
454         token->value = c;
455
456         while (shift--) {
457             const char *e = ap_strchr_c(p, '\\');
458
459             memcpy(c, p, e-p);
460             c   += e-p;
461             *c++ = *++e;
462             len -= e-p;
463             p    = e+1;
464         }
465
466         if (len) {
467             memcpy(c, p, len);
468         }
469         c[len] = '\0';
470     }
471
472     return unmatched;
473 }
474
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,
477                                         int *was_error)
478 {
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;
483     unsigned regex = 0;
484
485     *was_error = 0;
486
487     if (!parse) {
488         return 0;
489     }
490
491     /* Create Parse Tree */
492     while (1) {
493         /* uncomment this to see how the tree a built:
494          *
495          * DEBUG_DUMP_TREE(ctx, root);
496          */
497         CREATE_NODE(pool, new);
498
499         was_unmatched = get_ptoken(pool, &parse, &new->token,
500                      (current != NULL ? &current->token : NULL));
501         if (!parse) {
502             break;
503         }
504
505         DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
506         DEBUG_DUMP_TOKEN(ctx, &new->token);
507
508         if (!current) {
509             switch (new->token.type) {
510             case TOKEN_STRING:
511             case TOKEN_NOT:
512             case TOKEN_ACCESS:
513             case TOKEN_LBRACE:
514                 root = current = new;
515                 continue;
516
517             default:
518                 *was_error = 1;
519                 return 0;
520             }
521         }
522
523         switch (new->token.type) {
524         case TOKEN_STRING:
525             switch (current->token.type) {
526             case TOKEN_STRING:
527                 current->token.value =
528                     apr_pstrcat(pool, current->token.value,
529                                 *current->token.value ? " " : "",
530                                 new->token.value, NULL);
531                 continue;
532
533             case TOKEN_RE:
534             case TOKEN_RBRACE:
535             case TOKEN_GROUP:
536                 break;
537
538             default:
539                 new->parent = current;
540                 current = current->right = new;
541                 continue;
542             }
543             break;
544
545         case TOKEN_RE:
546             switch (current->token.type) {
547             case TOKEN_EQ:
548             case TOKEN_NE:
549                 new->parent = current;
550                 current = current->right = new;
551                 ++regex;
552                 continue;
553
554             default:
555                 break;
556             }
557             break;
558
559         case TOKEN_AND:
560         case TOKEN_OR:
561             switch (current->token.type) {
562             case TOKEN_STRING:
563             case TOKEN_RE:
564             case TOKEN_GROUP:
565                 current = current->parent;
566
567                 while (current) {
568                     switch (current->token.type) {
569                     case TOKEN_AND:
570                     case TOKEN_OR:
571                     case TOKEN_LBRACE:
572                         break;
573
574                     default:
575                         current = current->parent;
576                         continue;
577                     }
578                     break;
579                 }
580
581                 if (!current) {
582                     new->left = root;
583                     root->parent = new;
584                     current = root = new;
585                     continue;
586                 }
587
588                 new->left = current->right;
589                 new->left->parent = new;
590                 new->parent = current;
591                 current = current->right = new;
592                 continue;
593
594             default:
595                 break;
596             }
597             break;
598
599         case TOKEN_EQ:
600         case TOKEN_NE:
601         case TOKEN_GE:
602         case TOKEN_GT:
603         case TOKEN_LE:
604         case TOKEN_LT:
605             if (current->token.type == TOKEN_STRING) {
606                 current = current->parent;
607
608                 if (!current) {
609                     new->left = root;
610                     root->parent = new;
611                     current = root = new;
612                     continue;
613                 }
614
615                 switch (current->token.type) {
616                 case TOKEN_LBRACE:
617                 case TOKEN_AND:
618                 case TOKEN_OR:
619                     new->left = current->right;
620                     new->left->parent = new;
621                     new->parent = current;
622                     current = current->right = new;
623                     continue;
624
625                 default:
626                     break;
627                 }
628             }
629             break;
630
631         case TOKEN_RBRACE:
632             while (current && current->token.type != TOKEN_LBRACE) {
633                 current = current->parent;
634             }
635
636             if (current) {
637                 TYPE_TOKEN(&current->token, TOKEN_GROUP);
638                 continue;
639             }
640
641             error = "Unmatched ')' in \"%s\" in file %s";
642             break;
643
644         case TOKEN_NOT:
645         case TOKEN_ACCESS:
646         case TOKEN_LBRACE:
647             switch (current->token.type) {
648             case TOKEN_STRING:
649             case TOKEN_RE:
650             case TOKEN_RBRACE:
651             case TOKEN_GROUP:
652                 break;
653
654             default:
655                 current->right = new;
656                 new->parent = current;
657                 current = new;
658                 continue;
659             }
660             break;
661
662         default:
663             break;
664         }
665
666         *was_error = 1;
667         return 0;
668     }
669
670     DEBUG_DUMP_TREE(ctx, root);
671     return root;
672 }
673
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)
678 {
679     parse_node_t *current = root;
680     const char *error = NULL;
681     unsigned int regex = 0;
682
683     /* Evaluate Parse Tree */
684     while (current) {
685         switch (current->token.type) {
686         case TOKEN_STRING:
687             current->token.value = PARSE_STRING(r, current->token.value);
688             current->value = !!*current->token.value;
689             break;
690
691         case TOKEN_AND:
692         case TOKEN_OR:
693             if (!current->left || !current->right) {
694                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
695                               "Invalid expression in file %s", r->filename);
696                 *was_error = 1;
697                 return 0;
698             }
699
700             if (!current->left->done) {
701                 switch (current->left->token.type) {
702                 case TOKEN_STRING:
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;
708                     break;
709
710                 default:
711                     current = current->left;
712                     continue;
713                 }
714             }
715
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;
721             }
722             else {
723                 if (!current->right->done) {
724                     switch (current->right->token.type) {
725                     case TOKEN_STRING:
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;
731                         break;
732
733                     default:
734                         current = current->right;
735                         continue;
736                     }
737                 }
738
739                 if (current->token.type == TOKEN_AND) {
740                     current->value = current->left->value &&
741                                      current->right->value;
742                 }
743                 else {
744                     current->value = current->left->value ||
745                                      current->right->value;
746                 }
747             }
748             break;
749
750         case TOKEN_EQ:
751         case TOKEN_NE:
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);
758                 *was_error = 1;
759                 return 0;
760             }
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);
765
766             if (current->right->token.type == TOKEN_RE) {
767                 current->value = re_check(r, current->left->token.value,
768                                           current->right->token.value, reptr);
769                 --regex;
770             }
771             else {
772                 current->value = !strcmp(current->left->token.value,
773                                          current->right->token.value);
774             }
775
776             if (current->token.type == TOKEN_NE) {
777                 current->value = !current->value;
778             }
779             break;
780
781         case TOKEN_GE:
782         case TOKEN_GT:
783         case TOKEN_LE:
784         case TOKEN_LT:
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);
790                 *was_error = 1;
791                 return 0;
792             }
793
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);
798
799             current->value = strcmp(current->left->token.value,
800                                     current->right->token.value);
801
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 */
808             }
809             break;
810
811         case TOKEN_NOT:
812         case TOKEN_GROUP:
813             if (current->right) {
814                 if (!current->right->done) {
815                     current = current->right;
816                     continue;
817                 }
818                 current->value = current->right->value;
819             }
820             else {
821                 current->value = 1;
822             }
823
824             if (current->token.type == TOKEN_NOT) {
825                 current->value = !current->value;
826             }
827             break;
828         case TOKEN_ACCESS:
829             if (eval_func) {
830                 *was_error = eval_func(r, current, string_func);
831                 if (*was_error) {
832                     return 0;
833                 }
834             }
835             break;
836
837         case TOKEN_RE:
838             if (!error) {
839                 error = "No operator before regex in file %s";
840             }
841         case TOKEN_LBRACE:
842             if (!error) {
843                 error = "Unmatched '(' in file %s";
844             }
845         default:
846             if (!error) {
847                 error = "internal parser error in file %s";
848             }
849
850             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, r->filename);
851             *was_error = 1;
852             return 0;
853         }
854
855         DEBUG_DUMP_EVAL(r, current);
856         current->done = 1;
857         current = current->parent;
858     }
859
860     return (root ? root->value : 0);
861 }
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)
866 {
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);   
871         return 0;
872     }
873     return ap_expr_eval(r, root, was_error, reptr, string_func, eval_func);
874 }