]> granicus.if.org Git - apache/blobdiff - modules/filters/mod_include.c
switch to APR 1.0 API (which is still in flux)
[apache] / modules / filters / mod_include.c
index de3996a3ff8288a11f4ffeba3c93452340dfd8c8..cf1535c7a10c4fc00b94c04765af157067eecbbf 100644 (file)
 #endif /* !APR_CHARSET_EBCDIC */
 
 
-/*
- * +-------------------------------------------------------+
- * |                                                       |
- * |                  Debugging Macros
- * |                                                       |
- * +-------------------------------------------------------+
- */
-
-#ifdef DEBUG_INCLUDE
-
-#define MAX_DEBUG_SIZE MAX_STRING_LEN
-
-#define LOG_COND_STATUS(ctx, f, bb, text)                                   \
-do {                                                                        \
-    char *cond_txt = apr_pstrcat((ctx)->dpool, "**** ", (text),             \
-        " conditional_status=\"", ((ctx)->flags & SSI_FLAG_COND_TRUE)?"1":"0", \
-        "\"\n", NULL);                                                      \
-    APR_BRIGADE_INSERT_TAIL((bb), apr_bucket_heap_create(cond_txt,          \
-                            strlen(cond_txt), NULL, (f)->c->bucket_alloc)); \
-} while(0)
-
-#define DUMP_PARSE_EXPR_DEBUG(buf, f, bb)                                   \
-do {                                                                        \
-    APR_BRIGADE_INSERT_TAIL((bb), apr_bucket_heap_create((buf),             \
-                            strlen((buf)), NULL, (f)->c->bucket_alloc));    \
-} while(0)
-
-#else
-
-#define MAX_DEBUG_SIZE 10
-#define LOG_COND_STATUS(ctx, f, bb, text)
-#define DUMP_PARSE_EXPR_DEBUG(buf, f, bb)
-
-#endif
-
-
 /*
  * +-------------------------------------------------------+
  * |                                                       |
@@ -151,29 +115,60 @@ typedef struct result_item {
     const char *string;
 } result_item_t;
 
+/* conditional expression parser stuff */
+typedef enum {
+    TOKEN_STRING,
+    TOKEN_RE,
+    TOKEN_AND,
+    TOKEN_OR,
+    TOKEN_NOT,
+    TOKEN_EQ,
+    TOKEN_NE,
+    TOKEN_RBRACE,
+    TOKEN_LBRACE,
+    TOKEN_GROUP,
+    TOKEN_GE,
+    TOKEN_LE,
+    TOKEN_GT,
+    TOKEN_LT
+} token_type_t;
+
+typedef struct {
+    token_type_t  type;
+    const char   *value;
+#ifdef DEBUG_INCLUDE
+    const char   *s;
+#endif
+} token_t;
+
+typedef struct parse_node {
+    struct parse_node *parent;
+    struct parse_node *left;
+    struct parse_node *right;
+    token_t token;
+    int value;
+    int done;
+#ifdef DEBUG_INCLUDE
+    int dump_done;
+#endif
+} parse_node_t;
+
 typedef enum {
     XBITHACK_OFF,
     XBITHACK_ON,
     XBITHACK_FULL
 } xbithack_t;
 
-typedef struct {
-    unsigned int T[256];
-    unsigned int x;
-    apr_size_t pattern_len;
-} bndm_t;
-
 typedef struct {
     const char *default_error_msg;
     const char *default_time_fmt;
+    const char *undefined_echo;
     xbithack_t  xbithack;
 } include_dir_config;
 
 typedef struct {
     const char *default_start_tag;
     const char *default_end_tag;
-    const char *undefined_echo;
-    apr_size_t  undefined_echo_len;
 } include_server_config;
 
 /* main parser states */
@@ -215,6 +210,12 @@ typedef struct {
     regmatch_t  match[MAX_NMATCH];
 } backref_t;
 
+typedef struct {
+    unsigned int T[256];
+    unsigned int x;
+    apr_size_t pattern_len;
+} bndm_t;
+
 struct ssi_internal_ctx {
     parse_state_t state;
     int           seen_eos;
@@ -237,9 +238,247 @@ struct ssi_internal_ctx {
     arg_item_t   *argv;          /* all arguments */
 
     backref_t    *re;            /* NULL if there wasn't a regex yet */
+
+    const char   *undefined_echo;
+    apr_size_t    undefined_echo_len;
+
+#ifdef DEBUG_INCLUDE
+    struct {
+        ap_filter_t *f;
+        apr_bucket_brigade *bb;
+    } debug;
+#endif
 };
 
 
+/*
+ * +-------------------------------------------------------+
+ * |                                                       |
+ * |                  Debugging Utilities
+ * |                                                       |
+ * +-------------------------------------------------------+
+ */
+
+#ifdef DEBUG_INCLUDE
+
+#define TYPE_TOKEN(token, ttype) do { \
+    (token)->type = ttype;            \
+    (token)->s = #ttype;              \
+} while(0)
+
+#define CREATE_NODE(ctx, name) do {                       \
+    (name) = apr_palloc((ctx)->dpool, sizeof(*(name)));   \
+    (name)->parent = (name)->left = (name)->right = NULL; \
+    (name)->done = 0;                                     \
+    (name)->dump_done = 0;                                \
+} while(0)
+
+static void debug_printf(include_ctx_t *ctx, const char *fmt, ...)
+{
+    va_list ap;
+    char *debug__str;
+
+    va_start(ap, fmt);
+    debug__str = apr_pvsprintf(ctx->pool, fmt, ap);
+    va_end(ap);
+
+    APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
+                            debug__str, strlen(debug__str), ctx->pool,
+                            ctx->intern->debug.f->c->bucket_alloc));
+}
+
+#define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
+    parse_node_t *d__c = node->child;                                        \
+    if (d__c) {                                                              \
+        if (!d__c->dump_done) {                                              \
+            if (d__c->parent != node) {                                      \
+                debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
+                if (!d__c->parent) {                                         \
+                    debug_printf(ctx, "Parent of " #child " child node is "  \
+                                 "NULL.\n");                                 \
+                }                                                            \
+                else {                                                       \
+                    debug_printf(ctx, "Parent of " #child " child node "     \
+                                 "points to another node (of type %s)!\n",   \
+                                 d__c->parent->token.s);                     \
+                }                                                            \
+                return;                                                      \
+            }                                                                \
+            node = d__c;                                                     \
+            continue;                                                        \
+        }                                                                    \
+    }                                                                        \
+    else {                                                                   \
+        debug_printf(ctx, "%s(missing)\n", is);                              \
+    }                                                                        \
+}
+
+static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
+{
+    parse_node_t *current;
+    char *is;
+
+    if (!root) {
+        debug_printf(ctx, "     -- Parse Tree empty --\n\n");
+        return;
+    }
+
+    debug_printf(ctx, "     ----- Parse Tree -----\n");
+    current = root;
+    is = "     ";
+
+    while (current) {
+        switch (current->token.type) {
+        case TOKEN_STRING:
+        case TOKEN_RE:
+            debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
+                         current->token.value);
+            current->dump_done = 1;
+            current = current->parent;
+            continue;
+
+        case TOKEN_NOT:
+        case TOKEN_GROUP:
+        case TOKEN_RBRACE:
+        case TOKEN_LBRACE:
+            if (!current->dump_done) {
+                debug_printf(ctx, "%s%s\n", is, current->token.s);
+                is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
+                current->dump_done = 1;
+            }
+
+            DUMP__CHILD(ctx, is, current, right)
+
+            if (!current->right || current->right->dump_done) {
+                is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
+                if (current->right) current->right->dump_done = 0;
+                current = current->parent;
+            }
+            continue;
+
+        default:
+            if (!current->dump_done) {
+                debug_printf(ctx, "%s%s\n", is, current->token.s);
+                is = apr_pstrcat(ctx->dpool, is, "    ", NULL);
+                current->dump_done = 1;
+            }
+
+            DUMP__CHILD(ctx, is, current, left)
+            DUMP__CHILD(ctx, is, current, right)
+
+            if ((!current->left || current->left->dump_done) &&
+                (!current->right || current->right->dump_done)) {
+
+                is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
+                if (current->left) current->left->dump_done = 0;
+                if (current->right) current->right->dump_done = 0;
+                current = current->parent;
+            }
+            continue;
+        }
+    }
+
+    /* it is possible to call this function within the parser loop, to see
+     * how the tree is built. That way, we must cleanup after us to dump
+     * always the whole tree
+     */
+    root->dump_done = 0;
+    if (root->left) root->left->dump_done = 0;
+    if (root->right) root->right->dump_done = 0;
+
+    debug_printf(ctx, "     --- End Parse Tree ---\n\n");
+
+    return;
+}
+
+#define DEBUG_INIT(ctx, filter, brigade) do { \
+    (ctx)->intern->debug.f = filter;          \
+    (ctx)->intern->debug.bb = brigade;        \
+} while(0)
+
+#define DEBUG_PRINTF(arg) debug_printf arg
+
+#define DEBUG_DUMP_TOKEN(ctx, token) do {                                     \
+    token_t *d__t = (token);                                                  \
+                                                                              \
+    if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) {               \
+        DEBUG_PRINTF(((ctx), "     Found: %s (%s)\n", d__t->s, d__t->value)); \
+    }                                                                         \
+    else {                                                                    \
+        DEBUG_PRINTF((ctx, "     Found: %s\n", d__t->s));                     \
+    }                                                                         \
+} while(0)
+
+#define DEBUG_DUMP_EVAL(ctx, node) do {                                       \
+    char c = '"';                                                             \
+    switch ((node)->token.type) {                                             \
+    case TOKEN_STRING:                                                        \
+        debug_printf((ctx), "     Evaluate: %s (%s) -> %c\n", (node)->token.s,\
+                     (node)->token.value, ((node)->value) ? '1':'0');         \
+        break;                                                                \
+    case TOKEN_AND:                                                           \
+    case TOKEN_OR:                                                            \
+        debug_printf((ctx), "     Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
+                     (node)->token.s,                                         \
+                     (((node)->left->done) ? ((node)->left->value ?"1":"0")   \
+                                          : "short circuited"),               \
+                     (((node)->right->done) ? ((node)->right->value?"1":"0")  \
+                                          : "short circuited"),               \
+                     (node)->value ? '1' : '0');                              \
+        break;                                                                \
+    case TOKEN_EQ:                                                            \
+    case TOKEN_NE:                                                            \
+    case TOKEN_GT:                                                            \
+    case TOKEN_GE:                                                            \
+    case TOKEN_LT:                                                            \
+    case TOKEN_LE:                                                            \
+        if ((node)->right->token.type == TOKEN_RE) c = '/';                   \
+        debug_printf((ctx), "     Compare:  %s (\"%s\" with %c%s%c) -> %c\n", \
+                     (node)->token.s,                                         \
+                     (node)->left->token.value,                               \
+                     c, (node)->right->token.value, c,                        \
+                     (node)->value ? '1' : '0');                              \
+        break;                                                                \
+    default:                                                                  \
+        debug_printf((ctx), "     Evaluate: %s -> %c\n", (node)->token.s,     \
+                     (node)->value ? '1' : '0');                              \
+        break;                                                                \
+    }                                                                         \
+} while(0)
+
+#define DEBUG_DUMP_UNMATCHED(ctx, unmatched) do {                        \
+    if (unmatched) {                                                     \
+        DEBUG_PRINTF(((ctx), "     Unmatched %c\n", (char)(unmatched))); \
+    }                                                                    \
+} while(0)
+
+#define DEBUG_DUMP_COND(ctx, text)                                 \
+    DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text),   \
+                  ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
+
+#define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
+
+#else /* DEBUG_INCLUDE */
+
+#define TYPE_TOKEN(token, ttype) (token)->type = ttype
+
+#define CREATE_NODE(ctx, name) do {                       \
+    (name) = apr_palloc((ctx)->dpool, sizeof(*(name)));   \
+    (name)->parent = (name)->left = (name)->right = NULL; \
+    (name)->done = 0;                                     \
+} while(0)
+
+#define DEBUG_INIT(ctx, f, bb)
+#define DEBUG_PRINTF(arg)
+#define DEBUG_DUMP_TOKEN(ctx, token)
+#define DEBUG_DUMP_EVAL(ctx, node)
+#define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
+#define DEBUG_DUMP_COND(ctx, text)
+#define DEBUG_DUMP_TREE(ctx, root)
+
+#endif /* !DEBUG_INCLUDE */
+
+
 /*
  * +-------------------------------------------------------+
  * |                                                       |
@@ -306,20 +545,23 @@ static void decodehtml(char *s)
     const char *ents;
     static const char * const entlist[MAXENTLEN + 1] =
     {
-        NULL,                   /* 0 */
-        NULL,                   /* 1 */
-        "lt\074gt\076",         /* 2 */
-        "amp\046ETH\320eth\360",        /* 3 */
-        "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\
-iuml\357ouml\366uuml\374yuml\377",      /* 4 */
-        "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\
-THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\
-ucirc\373thorn\376",            /* 5 */
-        "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\
-Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\
-Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\
-egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\
-otilde\365oslash\370ugrave\371uacute\372yacute\375"     /* 6 */
+        NULL,                     /* 0 */
+        NULL,                     /* 1 */
+        "lt\074gt\076",           /* 2 */
+        "amp\046ETH\320eth\360",  /* 3 */
+        "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml"
+        "\353iuml\357ouml\366uuml\374yuml\377",                         /* 4 */
+
+        "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc"
+        "\333THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352"
+        "icirc\356ocirc\364ucirc\373thorn\376",                         /* 5 */
+
+        "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311"
+        "Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde"
+        "\325Oslash\330Ugrave\331Uacute\332Yacute\335agrave\340"
+        "aacute\341atilde\343ccedil\347egrave\350eacute\351igrave"
+        "\354iacute\355ntilde\361ograve\362oacute\363otilde\365"
+        "oslash\370ugrave\371uacute\372yacute\375"                      /* 6 */
     };
 
     /* Do a fast scan through the string until we find anything
@@ -436,7 +678,7 @@ static const char *add_include_vars_lazy(request_rec *r, const char *var)
         val = ap_ht_time(r->pool, r->finfo.mtime, conf->default_time_fmt, 0);
     }
     else if (!strcasecmp(var, "USER_NAME")) {
-        if (apr_get_username(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
+        if (apr_uid_name_get(&val, r->finfo.user, r->pool) != APR_SUCCESS) {
             val = "<unknown>";
         }
     }
@@ -456,7 +698,7 @@ static const char *get_include_var(const char *var, include_ctx_t *ctx)
     request_rec *r = ctx->intern->r;
 
     if (apr_isdigit(*var) && !var[1]) {
-        int idx = *var - '0';
+        apr_size_t idx = *var - '0';
         backref_t *re = ctx->intern->re;
 
         /* Handle $0 .. $9 from the last regex evaluated.
@@ -722,7 +964,8 @@ static char *ap_ssi_parse_string(include_ctx_t *ctx, const char *in, char *out,
  * +-------------------------------------------------------+
  */
 
-static APR_INLINE int re_check(include_ctx_t *ctx, char *string, char *rexp)
+static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
+                               const char *rexp)
 {
     regex_t *compiled;
     backref_t *re = ctx->intern->re;
@@ -739,8 +982,8 @@ static APR_INLINE int re_check(include_ctx_t *ctx, char *string, char *rexp)
         re = ctx->intern->re = apr_palloc(ctx->pool, sizeof(*re));
     }
 
-    re->source = string;
-    re->rexp = rexp;
+    re->source = apr_pstrdup(ctx->pool, string);
+    re->rexp = apr_pstrdup(ctx->pool, rexp);
     re->nsub = compiled->re_nsub;
     rc = !ap_regexec(compiled, string, MAX_NMATCH, re->match, 0);
 
@@ -748,747 +991,512 @@ static APR_INLINE int re_check(include_ctx_t *ctx, char *string, char *rexp)
     return rc;
 }
 
-enum token_type {
-    token_string, token_re,
-    token_and, token_or, token_not, token_eq, token_ne,
-    token_rbrace, token_lbrace, token_group,
-    token_ge, token_le, token_gt, token_lt
-};
-struct token {
-    enum token_type type;
-    char* value;
-};
-
-static const char *get_ptoken(request_rec *r, const char *string, 
-                              struct token *token, int *unmatched)
+static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
 {
-    char ch;
-    int next = 0;
-    char qs = 0;
-    int tkn_fnd = 0;
+    const char *p;
+    apr_size_t shift;
+    int unmatched;
 
     token->value = NULL;
 
-    /* Skip leading white space */
-    if (string == (char *) NULL) {
-        return (char *) NULL;
+    if (!*parse) {
+        return 0;
     }
-    while ((ch = *string++)) {
-        if (!apr_isspace(ch)) {
-            break;
-        }
+
+    /* Skip leading white space */
+    while (apr_isspace(**parse)) {
+        ++*parse;
     }
-    if (ch == '\0') {
-        return (char *) NULL;
+
+    if (!**parse) {
+        *parse = NULL;
+        return 0;
     }
 
-    token->type = token_string; /* the default type */
-    switch (ch) {
+    TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
+    p = *parse;
+    unmatched = 0;
+
+    switch (*(*parse)++) {
     case '(':
-        token->type = token_lbrace;
-        return (string);
+        TYPE_TOKEN(token, TOKEN_LBRACE);
+        return 0;
     case ')':
-        token->type = token_rbrace;
-        return (string);
+        TYPE_TOKEN(token, TOKEN_RBRACE);
+        return 0;
     case '=':
-        token->type = token_eq;
-        return (string);
+        if (**parse == '=') ++*parse;
+        TYPE_TOKEN(token, TOKEN_EQ);
+        return 0;
     case '!':
-        if (*string == '=') {
-            token->type = token_ne;
-            return (string + 1);
-        }
-        else {
-            token->type = token_not;
-            return (string);
+        if (**parse == '=') {
+            TYPE_TOKEN(token, TOKEN_NE);
+            ++*parse;
+            return 0;
         }
+        TYPE_TOKEN(token, TOKEN_NOT);
+        return 0;
     case '\'':
-        /* already token->type == token_string */
-        qs = '\'';
+        unmatched = '\'';
         break;
     case '/':
-        token->type = token_re;
-        qs = '/';
+        TYPE_TOKEN(token, TOKEN_RE);
+        unmatched = '/';
         break;
     case '|':
-        if (*string == '|') {
-            token->type = token_or;
-            return (string + 1);
+        if (**parse == '|') {
+            TYPE_TOKEN(token, TOKEN_OR);
+            ++*parse;
+            return 0;
         }
         break;
     case '&':
-        if (*string == '&') {
-            token->type = token_and;
-            return (string + 1);
+        if (**parse == '&') {
+            TYPE_TOKEN(token, TOKEN_AND);
+            ++*parse;
+            return 0;
         }
         break;
     case '>':
-        if (*string == '=') {
-            token->type = token_ge;
-            return (string + 1);
-        }
-        else {
-            token->type = token_gt;
-            return (string);
+        if (**parse == '=') {
+            TYPE_TOKEN(token, TOKEN_GE);
+            ++*parse;
+            return 0;
         }
+        TYPE_TOKEN(token, TOKEN_GT);
+        return 0;
     case '<':
-        if (*string == '=') {
-            token->type = token_le;
-            return (string + 1);
-        }
-        else {
-            token->type = token_lt;
-            return (string);
+        if (**parse == '=') {
+            TYPE_TOKEN(token, TOKEN_LE);
+            ++*parse;
+            return 0;
         }
-    default:
-        /* already token->type == token_string */
-        break;
-    }
-    /* We should only be here if we are in a string */
-    token->value = apr_palloc(r->pool, strlen(string) + 2); /* 2 for ch plus
-                                                               trailing null */
-    if (!qs) {
-        token->value[next++] = ch;
+        TYPE_TOKEN(token, TOKEN_LT);
+        return 0;
     }
 
-    /* 
-     * I used the ++string throughout this section so that string
-     * ends up pointing to the next token and I can just return it
+    /* It's a string or regex token
+     * Now search for the next token, which finishes this string
      */
-    for (ch = *string; ((ch != '\0') && (!tkn_fnd)); ch = *++string) {
-        if (ch == '\\') {
-            if ((ch = *++string) == '\0') {
-                tkn_fnd = 1;
-            }
-            else {
-                token->value[next++] = ch;
+    shift = 0;
+    p = *parse = token->value = unmatched ? *parse : p;
+
+    for (; **parse; p = ++*parse) {
+        if (**parse == '\\') {
+            if (!*(++*parse)) {
+                p = *parse;
+                break;
             }
+
+            ++shift;
         }
         else {
-            if (!qs) {
-                if (apr_isspace(ch)) {
-                    tkn_fnd = 1;
-                }
-                else {
-                    switch (ch) {
-                    case '(':
-                    case ')':
-                    case '=':
-                    case '!':
-                    case '<':
-                    case '>':
-                        tkn_fnd = 1;
-                        break;
-                    case '|':
-                        if (*(string + 1) == '|') {
-                            tkn_fnd = 1;
-                        }
-                        break;
-                    case '&':
-                        if (*(string + 1) == '&') {
-                            tkn_fnd = 1;
-                        }
-                        break;
-                    }
-                    if (!tkn_fnd) {
-                        token->value[next++] = ch;
-                    }
+            if (unmatched) {
+                if (**parse == unmatched) {
+                    unmatched = 0;
+                    ++*parse;
+                    break;
                 }
+            } else if (apr_isspace(**parse)) {
+                break;
             }
             else {
-                if (ch == qs) {
-                    qs = 0;
-                    tkn_fnd = 1;
-                    string++;
+                int found = 0;
+
+                switch (**parse) {
+                case '(':
+                case ')':
+                case '=':
+                case '!':
+                case '<':
+                case '>':
+                    ++found;
+                    break;
+
+                case '|':
+                case '&':
+                    if ((*parse)[1] == **parse) {
+                        ++found;
+                    }
+                    break;
                 }
-                else {
-                    token->value[next++] = ch;
+
+                if (found) {
+                    break;
                 }
             }
         }
-        if (tkn_fnd) {
-            break;
-        }
     }
 
-    /* If qs is still set, we have an unmatched quote */
-    if (qs) {
-        *unmatched = 1;
-        next = 0;
+    if (unmatched) {
+        token->value = apr_pstrdup(pool, "");
     }
-    token->value[next] = '\0';
+    else {
+        apr_size_t len = p - token->value - shift;
+        char *c = apr_palloc(pool, len + 1);
 
-    return (string);
-}
+        p = token->value;
+        token->value = c;
 
+        while (shift--) {
+            const char *e = ap_strchr_c(p, '\\');
 
-/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
- * characters long...
- */
-static int parse_expr(request_rec *r, include_ctx_t *ctx, const char *expr,
-                      int *was_error, int *was_unmatched, char *debug)
+            memcpy(c, p, e-p);
+            c   += e-p;
+            *c++ = *++e;
+            len -= e-p;
+            p    = e+1;
+        }
+
+        if (len) {
+            memcpy(c, p, len);
+        }
+        c[len] = '\0';
+    }
+
+    return unmatched;
+}
+
+static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
 {
-    struct parse_node {
-        struct parse_node *left, *right, *parent;
-        struct token token;
-        int value, done;
-    } *root, *current, *new;
-    const char *parse;
-    char* buffer;
-    int retval = 0;
-    apr_size_t debug_pos = 0;
-
-    debug[debug_pos] = '\0';
-    *was_error       = 0;
-    *was_unmatched   = 0;
-    if ((parse = expr) == (char *) NULL) {
-        return (0);
-    }
-    root = current = (struct parse_node *) NULL;
+    parse_node_t *new, *root = NULL, *current = NULL;
+    request_rec *r = ctx->intern->r;
+    const char *error = "Invalid expression \"%s\" in file %s";
+    const char *parse = expr;
+    int was_unmatched = 0;
+    unsigned regex = 0;
+
+    *was_error = 0;
+
+    if (!parse) {
+        return 0;
+    }
 
     /* Create Parse Tree */
     while (1) {
-        new = (struct parse_node *) apr_palloc(r->pool,
-                                           sizeof(struct parse_node));
-        new->parent = new->left = new->right = (struct parse_node *) NULL;
-        new->done = 0;
-        if ((parse = get_ptoken(r, parse, &new->token, was_unmatched)) == 
-            (char *) NULL) {
+        /* uncomment this to see how the tree a built:
+         *
+         * DEBUG_DUMP_TREE(ctx, root);
+         */
+        CREATE_NODE(ctx, new);
+
+        was_unmatched = get_ptoken(ctx->dpool, &parse, &new->token);
+        if (!parse) {
             break;
         }
-        switch (new->token.type) {
 
-        case token_string:
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], 
-                                  "     Token: string (%s)\n", 
-                                  new->token.value);
-#endif
-            if (current == (struct parse_node *) NULL) {
+        DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
+        DEBUG_DUMP_TOKEN(ctx, &new->token);
+
+        if (!current) {
+            switch (new->token.type) {
+            case TOKEN_STRING:
+            case TOKEN_NOT:
+            case TOKEN_LBRACE:
                 root = current = new;
-                break;
+                continue;
+
+            default:
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,
+                              r->filename);
+                *was_error = 1;
+                return 0;
             }
+        }
+
+        switch (new->token.type) {
+        case TOKEN_STRING:
             switch (current->token.type) {
-            case token_string:
-                current->token.value = apr_pstrcat(r->pool,
-                                                   current->token.value,
-                                                   current->token.value[0] ? " " : "",
-                                                   new->token.value,
-                                                   NULL);
-                                                   
+            case TOKEN_STRING:
+                current->token.value =
+                    apr_pstrcat(ctx->dpool, current->token.value,
+                                *current->token.value ? " " : "",
+                                new->token.value, NULL);
+                continue;
+
+            case TOKEN_RE:
+            case TOKEN_RBRACE:
+            case TOKEN_GROUP:
                 break;
-            case token_eq:
-            case token_ne:
-            case token_and:
-            case token_or:
-            case token_lbrace:
-            case token_not:
-            case token_ge:
-            case token_gt:
-            case token_le:
-            case token_lt:
+
+            default:
                 new->parent = current;
                 current = current->right = new;
-                break;
-            default:
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s",
-                            expr, r->filename);
-                *was_error = 1;
-                return retval;
+                continue;
             }
             break;
 
-        case token_re:
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], 
-                                  "     Token: regex (%s)\n", 
-                                  new->token.value);
-#endif
-            if (current == (struct parse_node *) NULL) {
-                root = current = new;
-                break;
-            }
+        case TOKEN_RE:
             switch (current->token.type) {
-            case token_eq:
-            case token_ne:
-            case token_and:
-            case token_or:
-            case token_lbrace:
-            case token_not:
+            case TOKEN_EQ:
+            case TOKEN_NE:
                 new->parent = current;
                 current = current->right = new;
-                break;
+                ++regex;
+                continue;
+
             default:
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s",
-                            expr, r->filename);
-                *was_error = 1;
-                return retval;
+                break;
             }
             break;
 
-        case token_and:
-        case token_or:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Token: and/or\n",
-                    sizeof ("     Token: and/or\n"));
-            debug_pos += sizeof ("     Token: and/or\n");
-#endif
-            if (current == (struct parse_node *) NULL) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s",
-                            expr, r->filename);
-                *was_error = 1;
-                return retval;
-            }
-            /* Percolate upwards */
-            while (current != (struct parse_node *) NULL) {
-                switch (current->token.type) {
-                case token_string:
-                case token_re:
-                case token_group:
-                case token_not:
-                case token_eq:
-                case token_ne:
-                case token_and:
-                case token_or:
-                case token_ge:
-                case token_gt:
-                case token_le:
-                case token_lt:
-                    current = current->parent;
-                    continue;
-                case token_lbrace:
+        case TOKEN_AND:
+        case TOKEN_OR:
+            switch (current->token.type) {
+            case TOKEN_STRING:
+            case TOKEN_RE:
+            case TOKEN_GROUP:
+                current = current->parent;
+
+                while (current) {
+                    switch (current->token.type) {
+                    case TOKEN_AND:
+                    case TOKEN_OR:
+                    case TOKEN_LBRACE:
+                        break;
+
+                    default:
+                        current = current->parent;
+                        continue;
+                    }
                     break;
-                default:
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                                "Invalid expression \"%s\" in file %s",
-                                expr, r->filename);
-                    *was_error = 1;
-                    return retval;
                 }
-                break;
-            }
-            if (current == (struct parse_node *) NULL) {
-                new->left = root;
-                new->left->parent = new;
-                new->parent = (struct parse_node *) NULL;
-                root = new;
-            }
-            else {
-                new->left = current->right;
-                current->right = new;
-                new->parent = current;
-            }
-            current = new;
-            break;
 
-        case token_not:
-#ifdef DEBUG_INCLUDE
-            memcpy(&debug[debug_pos], "     Token: not\n",
-                    sizeof("     Token: not\n"));
-            debug_pos += sizeof("     Token: not\n");
-#endif
-            if (current == (struct parse_node *) NULL) {
-                root = current = new;
-                break;
-            }
-            /* Percolate upwards */
-            if (current != (struct parse_node *) NULL) {
-                switch (current->token.type) {
-                case token_not:
-                case token_eq:
-                case token_ne:
-                case token_and:
-                case token_or:
-                case token_lbrace:
-                case token_ge:
-                case token_gt:
-                case token_le:
-                case token_lt:
-                    break;
-                default:
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                                  "Invalid expression \"%s\" in file %s",
-                                  expr, r->filename);
-                    *was_error = 1;
-                    return retval;
+                if (!current) {
+                    new->left = root;
+                    root->parent = new;
+                    current = root = new;
+                    continue;
                 }
-            }
-            if (current == (struct parse_node *) NULL) {
-                new->left = root;
-                new->left->parent = new;
-                new->parent = (struct parse_node *) NULL;
-                root = new;
-            }
-            else {
+            
                 new->left = current->right;
-                current->right = new;
+                new->left->parent = new;
                 new->parent = current;
+                current = current->right = new;
+                continue;
+
+            default:
+                break;
             }
-            current = new;
             break;
 
-        case token_eq:
-        case token_ne:
-        case token_ge:
-        case token_gt:
-        case token_le:
-        case token_lt:
-#ifdef DEBUG_INCLUDE
-            memcpy(&debug[debug_pos], "     Token: eq/ne/ge/gt/le/lt\n",
-                    sizeof("     Token: eq/ne/ge/gt/le/lt\n"));
-            debug_pos += sizeof("     Token: eq/ne/ge/gt/le/lt\n");
-#endif
-            if (current == (struct parse_node *) NULL) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                              "Invalid expression \"%s\" in file %s",
-                              expr, r->filename);
-                *was_error = 1;
-                return retval;
-            }
-            /* Percolate upwards */
-            while (current != (struct parse_node *) NULL) {
+        case TOKEN_EQ:
+        case TOKEN_NE:
+        case TOKEN_GE:
+        case TOKEN_GT:
+        case TOKEN_LE:
+        case TOKEN_LT:
+            if (current->token.type == TOKEN_STRING) {
+                current = current->parent;
+
+                if (!current) {
+                    new->left = root;
+                    root->parent = new;
+                    current = root = new;
+                    continue;
+                }
+
                 switch (current->token.type) {
-                case token_string:
-                case token_re:
-                case token_group:
-                    current = current->parent;
+                case TOKEN_LBRACE:
+                case TOKEN_AND:
+                case TOKEN_OR:
+                    new->left = current->right;
+                    new->left->parent = new;
+                    new->parent = current;
+                    current = current->right = new;
                     continue;
-                case token_lbrace:
-                case token_and:
-                case token_or:
-                    break;
-                case token_not:
-                case token_eq:
-                case token_ne:
-                case token_ge:
-                case token_gt:
-                case token_le:
-                case token_lt:
+
                 default:
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                                "Invalid expression \"%s\" in file %s",
-                                expr, r->filename);
-                    *was_error = 1;
-                    return retval;
+                    break;
                 }
-                break;
-            }
-            if (current == (struct parse_node *) NULL) {
-                new->left = root;
-                new->left->parent = new;
-                new->parent = (struct parse_node *) NULL;
-                root = new;
             }
-            else {
-                new->left = current->right;
-                current->right = new;
-                new->parent = current;
-            }
-            current = new;
             break;
 
-        case token_rbrace:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Token: rbrace\n",
-                    sizeof ("     Token: rbrace\n"));
-            debug_pos += sizeof ("     Token: rbrace\n");
-#endif
-            while (current != (struct parse_node *) NULL) {
-                if (current->token.type == token_lbrace) {
-                    current->token.type = token_group;
-                    break;
-                }
+        case TOKEN_RBRACE:
+            while (current && current->token.type != TOKEN_LBRACE) {
                 current = current->parent;
             }
-            if (current == (struct parse_node *) NULL) {
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Unmatched ')' in \"%s\" in file %s",
-                            expr, r->filename);
-                *was_error = 1;
-                return retval;
+
+            if (current) {
+                TYPE_TOKEN(&current->token, TOKEN_GROUP);
+                continue;
             }
+
+            error = "Unmatched ')' in \"%s\" in file %s";
             break;
 
-        case token_lbrace:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Token: lbrace\n",
-                    sizeof ("     Token: lbrace\n"));
-            debug_pos += sizeof ("     Token: lbrace\n");
-#endif
-            if (current == (struct parse_node *) NULL) {
-                root = current = new;
+        case TOKEN_NOT:
+        case TOKEN_LBRACE:
+            switch (current->token.type) {
+            case TOKEN_STRING:
+            case TOKEN_RE:
+            case TOKEN_RBRACE:
+            case TOKEN_GROUP:
                 break;
-            }
-            /* Percolate upwards */
-            if (current != (struct parse_node *) NULL) {
-                switch (current->token.type) {
-                case token_not:
-                case token_eq:
-                case token_ne:
-                case token_and:
-                case token_or:
-                case token_lbrace:
-                case token_ge:
-                case token_gt:
-                case token_le:
-                case token_lt:
-                    break;
-                case token_string:
-                case token_re:
-                case token_group:
-                default:
-                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                                "Invalid expression \"%s\" in file %s",
-                                expr, r->filename);
-                    *was_error = 1;
-                    return retval;
-                }
-            }
-            if (current == (struct parse_node *) NULL) {
-                new->left = root;
-                new->left->parent = new;
-                new->parent = (struct parse_node *) NULL;
-                root = new;
-            }
-            else {
-                new->left = current->right;
+
+            default:
                 current->right = new;
                 new->parent = current;
+                current = new;
+                continue;
             }
-            current = new;
             break;
+
         default:
             break;
         }
+
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr, r->filename);
+        *was_error = 1;
+        return 0;
     }
 
+    DEBUG_DUMP_TREE(ctx, root);
+
     /* Evaluate Parse Tree */
     current = root;
-    while (current != (struct parse_node *) NULL) {
+    error = NULL;
+    while (current) {
         switch (current->token.type) {
-        case token_string:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Evaluate string\n",
-                    sizeof ("     Evaluate string\n"));
-            debug_pos += sizeof ("     Evaluate string\n");
-#endif
-            buffer = ap_ssi_parse_string(ctx, current->token.value, NULL, 
-                                         MAX_STRING_LEN, 0);
-            current->token.value = buffer;
-            current->value = (current->token.value[0] != '\0');
-            current->done = 1;
-            current = current->parent;
+        case TOKEN_STRING:
+            current->token.value =
+                ap_ssi_parse_string(ctx, current->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+            current->value = !!*current->token.value;
             break;
 
-        case token_re:
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                          "No operator before regex of expr \"%s\" in file %s",
-                          expr, r->filename);
-            *was_error = 1;
-            return retval;
-
-        case token_and:
-        case token_or:
-#ifdef DEBUG_INCLUDE
-            memcpy(&debug[debug_pos], "     Evaluate and/or\n",
-                    sizeof("     Evaluate and/or\n"));
-            debug_pos += sizeof("     Evaluate and/or\n");
-#endif
-            if (current->left  == (struct parse_node *) NULL ||
-                current->right == (struct parse_node *) NULL) {
+        case TOKEN_AND:
+        case TOKEN_OR:
+            if (!current->left || !current->right) {
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                               "Invalid expression \"%s\" in file %s",
                               expr, r->filename);
                 *was_error = 1;
-                return retval;
+                return 0;
             }
+
             if (!current->left->done) {
                 switch (current->left->token.type) {
-                case token_string:
-                    buffer = ap_ssi_parse_string(ctx, current->left->token.value,
-                                                 NULL, MAX_STRING_LEN, 0);
-                    current->left->token.value = buffer;
-                    current->left->value = 
-                                       (current->left->token.value[0] != '\0');
+                case TOKEN_STRING:
+                    current->left->token.value =
+                        ap_ssi_parse_string(ctx, current->left->token.value,
+                                            NULL, 0, SSI_EXPAND_DROP_NAME);
+                    current->left->value = !!*current->left->token.value;
+                    DEBUG_DUMP_EVAL(ctx, current->left);
                     current->left->done = 1;
                     break;
+
                 default:
                     current = current->left;
                     continue;
                 }
             }
-            if (!current->right->done) {
-                switch (current->right->token.type) {
-                case token_string:
-                    buffer = ap_ssi_parse_string(ctx, current->right->token.value,
-                                                 NULL, MAX_STRING_LEN, 0);
-                    current->right->token.value = buffer;
-                    current->right->value = 
-                                      (current->right->token.value[0] != '\0');
-                    current->right->done = 1;
-                    break;
-                default:
-                    current = current->right;
-                    continue;
-                }
-            }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Left: %c\n",
-                                  current->left->value ? '1' : '0');
-            debug_pos += sprintf (&debug[debug_pos], "     Right: %c\n",
-                                  current->right->value ? '1' : '0');
-#endif
-            if (current->token.type == token_and) {
-                current->value = current->left->value && current->right->value;
+
+            /* short circuit evaluation */
+            if (!current->right->done && !regex &&
+                ((current->token.type == TOKEN_AND && !current->left->value) ||
+                (current->token.type == TOKEN_OR && current->left->value))) {
+                current->value = current->left->value;
             }
             else {
-                current->value = current->left->value || current->right->value;
+                if (!current->right->done) {
+                    switch (current->right->token.type) {
+                    case TOKEN_STRING:
+                        current->right->token.value =
+                            ap_ssi_parse_string(ctx,current->right->token.value,
+                                                NULL, 0, SSI_EXPAND_DROP_NAME);
+                        current->right->value = !!*current->right->token.value;
+                        DEBUG_DUMP_EVAL(ctx, current->right);
+                        current->right->done = 1;
+                        break;
+
+                    default:
+                        current = current->right;
+                        continue;
+                    }
+                }
+
+                if (current->token.type == TOKEN_AND) {
+                    current->value = current->left->value &&
+                                     current->right->value;
+                }
+                else {
+                    current->value = current->left->value ||
+                                     current->right->value;
+                }
             }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Returning %c\n",
-                                  current->value ? '1' : '0');
-#endif
-            current->done = 1;
-            current = current->parent;
             break;
 
-        case token_eq:
-        case token_ne:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Evaluate eq/ne\n",
-                    sizeof ("     Evaluate eq/ne\n"));
-            debug_pos += sizeof ("     Evaluate eq/ne\n");
-#endif
-            if ((current->left == (struct parse_node *) NULL) ||
-                (current->right == (struct parse_node *) NULL) ||
-                (current->left->token.type != token_string) ||
-                ((current->right->token.type != token_string) &&
-                 (current->right->token.type != token_re))) {
+        case TOKEN_EQ:
+        case TOKEN_NE:
+            if (!current->left || !current->right ||
+                current->left->token.type != TOKEN_STRING ||
+                (current->right->token.type != TOKEN_STRING &&
+                 current->right->token.type != TOKEN_RE)) {
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                             "Invalid expression \"%s\" in file %s",
                             expr, r->filename);
                 *was_error = 1;
-                return retval;
-            }
-            buffer = ap_ssi_parse_string(ctx, current->left->token.value,
-                                         NULL, MAX_STRING_LEN, 0);
-            current->left->token.value = buffer;
-            buffer = ap_ssi_parse_string(ctx, current->right->token.value,
-                                         NULL, MAX_STRING_LEN, 0);
-            current->right->token.value = buffer;
-            if (current->right->token.type == token_re) {
-#ifdef DEBUG_INCLUDE
-                debug_pos += sprintf (&debug[debug_pos],
-                                      "     Re Compare (%s) with /%s/\n",
-                                      current->left->token.value,
-                                      current->right->token.value);
-#endif
-                current->value =
-                    re_check(ctx, current->left->token.value,
-                             current->right->token.value);
+                return 0;
+            }
+            current->left->token.value =
+                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+            current->right->token.value =
+                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+
+            if (current->right->token.type == TOKEN_RE) {
+                current->value = re_check(ctx, current->left->token.value,
+                                          current->right->token.value);
+                --regex;
             }
             else {
-#ifdef DEBUG_INCLUDE
-                debug_pos += sprintf (&debug[debug_pos],
-                                      "     Compare (%s) with (%s)\n",
-                                      current->left->token.value,
-                                      current->right->token.value);
-#endif
-                current->value =
-                    (strcmp(current->left->token.value,
-                            current->right->token.value) == 0);
+                current->value = !strcmp(current->left->token.value,
+                                         current->right->token.value);
             }
-            if (current->token.type == token_ne) {
+
+            if (current->token.type == TOKEN_NE) {
                 current->value = !current->value;
             }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Returning %c\n",
-                                  current->value ? '1' : '0');
-#endif
-            current->done = 1;
-            current = current->parent;
             break;
-        case token_ge:
-        case token_gt:
-        case token_le:
-        case token_lt:
-#ifdef DEBUG_INCLUDE
-            memcpy (&debug[debug_pos], "     Evaluate ge/gt/le/lt\n",
-                    sizeof ("     Evaluate ge/gt/le/lt\n"));
-            debug_pos += sizeof ("     Evaluate ge/gt/le/lt\n");
-#endif
-            if ((current->left == (struct parse_node *) NULL) ||
-                (current->right == (struct parse_node *) NULL) ||
-                (current->left->token.type != token_string) ||
-                (current->right->token.type != token_string)) {
+
+        case TOKEN_GE:
+        case TOKEN_GT:
+        case TOKEN_LE:
+        case TOKEN_LT:
+            if (!current->left || !current->right ||
+                current->left->token.type != TOKEN_STRING ||
+                current->right->token.type != TOKEN_STRING) {
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                            "Invalid expression \"%s\" in file %s",
-                            expr, r->filename);
+                              "Invalid expression \"%s\" in file %s",
+                              expr, r->filename);
                 *was_error = 1;
-                return retval;
-            }
-            buffer = ap_ssi_parse_string(ctx, current->left->token.value, NULL,
-                                         MAX_STRING_LEN, 0);
-            current->left->token.value = buffer;
-            buffer = ap_ssi_parse_string(ctx, current->right->token.value, NULL,
-                                         MAX_STRING_LEN, 0);
-            current->right->token.value = buffer;
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos],
-                                  "     Compare (%s) with (%s)\n",
-                                  current->left->token.value,
-                                  current->right->token.value);
-#endif
-            current->value =
-                strcmp(current->left->token.value,
-                       current->right->token.value);
-            if (current->token.type == token_ge) {
-                current->value = current->value >= 0;
+                return 0;
             }
-            else if (current->token.type == token_gt) {
-                current->value = current->value > 0;
-            }
-            else if (current->token.type == token_le) {
-                current->value = current->value <= 0;
-            }
-            else if (current->token.type == token_lt) {
-                current->value = current->value < 0;
-            }
-            else {
-                current->value = 0;     /* Don't return -1 if unknown token */
-            }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Returning %c\n",
-                                  current->value ? '1' : '0');
-#endif
-            current->done = 1;
-            current = current->parent;
-            break;
 
-        case token_not:
-            if (current->right != (struct parse_node *) NULL) {
-                if (!current->right->done) {
-                    current = current->right;
-                    continue;
-                }
-                current->value = !current->right->value;
-            }
-            else {
-                current->value = 0;
+            current->left->token.value =
+                ap_ssi_parse_string(ctx, current->left->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+            current->right->token.value =
+                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+
+            current->value = strcmp(current->left->token.value,
+                                    current->right->token.value);
+
+            switch (current->token.type) {
+            case TOKEN_GE: current->value = current->value >= 0; break;
+            case TOKEN_GT: current->value = current->value >  0; break;
+            case TOKEN_LE: current->value = current->value <= 0; break;
+            case TOKEN_LT: current->value = current->value <  0; break;
+            default: current->value = 0; break; /* should not happen */
             }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Evaluate !: %c\n",
-                                  current->value ? '1' : '0');
-#endif
-            current->done = 1;
-            current = current->parent;
             break;
 
-        case token_group:
-            if (current->right != (struct parse_node *) NULL) {
+        case TOKEN_NOT:
+        case TOKEN_GROUP:
+            if (current->right) {
                 if (!current->right->done) {
                     current = current->right;
                     continue;
@@ -1498,38 +1506,36 @@ static int parse_expr(request_rec *r, include_ctx_t *ctx, const char *expr,
             else {
                 current->value = 1;
             }
-#ifdef DEBUG_INCLUDE
-            debug_pos += sprintf (&debug[debug_pos], "     Evaluate (): %c\n",
-                                  current->value ? '1' : '0');
-#endif
-            current->done = 1;
-            current = current->parent;
-            break;
 
-        case token_lbrace:
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                        "Unmatched '(' in \"%s\" in file %s",
-                        expr, r->filename);
-            *was_error = 1;
-            return retval;
-
-        case token_rbrace:
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                        "Unmatched ')' in \"%s\" in file %s",
-                        expr, r->filename);
-            *was_error = 1;
-            return retval;
+            if (current->token.type == TOKEN_NOT) {
+                current->value = !current->value;
+            }
+            break;
 
+        case TOKEN_RE:
+            if (!error) {
+                error = "No operator before regex in expr \"%s\" in file %s";
+            }
+        case TOKEN_LBRACE:
+            if (!error) {
+                error = "Unmatched '(' in \"%s\" in file %s";
+            }
         default:
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                          "bad token type");
+            if (!error) {
+                error = "internal parser error in \"%s\" in file %s";
+            }
+
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, expr,r->filename);
             *was_error = 1;
-            return retval;
+            return 0;
         }
+
+        DEBUG_DUMP_EVAL(ctx, current);
+        current->done = 1;
+        current = current->parent;
     }
 
-    retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
-    return (retval);
+    return (root ? root->value : 0);
 }
 
 
@@ -1569,51 +1575,6 @@ static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
     return;
 }
 
-/* ensure that path is relative, and does not contain ".." elements
- * ensentially ensure that it does not match the regex:
- * (^/|(^|/)\.\.(/|$))
- * XXX: Simply replace with apr_filepath_merge                    
- */
-static int is_only_below(const char *path)
-{
-#ifdef HAVE_DRIVE_LETTERS
-    if (path[1] == ':') 
-        return 0;
-#endif
-#ifdef NETWARE
-    if (ap_strchr_c(path, ':'))
-        return 0;
-#endif
-    if (path[0] == '/') {
-        return 0;
-    }
-    while (*path) {
-        int dots = 0;
-        while (path[dots] == '.')
-            ++dots;
-#if defined(WIN32) 
-        /* If the name is canonical this is redundant
-         * but in security, redundancy is worthwhile.
-         * Does OS2 belong here (accepts ... for ..)?
-         */
-        if (dots > 1 && (!path[dots] || path[dots] == '/'))
-            return 0;
-#else
-        if (dots == 2 && (!path[dots] || path[dots] == '/'))
-            return 0;
-#endif
-        path += dots;
-        /* Advance to either the null byte at the end of the
-         * string or the character right after the next slash,
-         * whichever comes first
-         */
-        while (*path && (*path++ != '/')) {
-            continue;
-        }
-    }
-    return 1;
-}
-
 static int find_file(request_rec *r, const char *directive, const char *tag,
                      char *tag_val, apr_finfo_t *finfo)
 {
@@ -1624,19 +1585,22 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
     apr_status_t rv = APR_SUCCESS;
 
     if (!strcmp(tag, "file")) {
-        /* XXX: Port to apr_filepath_merge
-         * be safe; only files in this directory or below allowed 
-         */
-        if (!is_only_below(tag_val)) {
+        char *newpath;
+
+        /* be safe; only files in this directory or below allowed */
+        rv = apr_filepath_merge(&newpath, NULL, tag_val,
+                                APR_FILEPATH_NOTABOVEROOT |
+                                APR_FILEPATH_SECUREROOTTEST |
+                                APR_FILEPATH_NOTABSOLUTE, r->pool);
+
+        if (!APR_STATUS_IS_SUCCESS(rv)) {
             error_fmt = "unable to access file \"%s\" "
                         "in parsed file %s";
         }
         else {
-            ap_getparents(tag_val);    /* get rid of any nasties */
-
             /* note: it is okay to pass NULL for the "next filter" since
                we never attempt to "run" this sub request. */
-            rr = ap_sub_req_lookup_file(tag_val, r, NULL);
+            rr = ap_sub_req_lookup_file(newpath, r, NULL);
 
             if (rr->status == HTTP_OK && rr->finfo.filetype != 0) {
                 to_send = rr->filename;
@@ -1675,18 +1639,16 @@ static int find_file(request_rec *r, const char *directive, const char *tag,
             return 0;
         }
         else {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                        "unable to get information about \"%s\" "
-                        "in parsed file %s",
-                        tag_val, r->filename);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to get "
+                          "information about \"%s\" in parsed file %s",
+                          tag_val, r->filename);
             ap_destroy_sub_req(rr);
             return -1;
         }
     }
     else {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                    "unknown parameter \"%s\" to tag %s in %s",
-                    tag, directive, r->filename);
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter \"%s\" "
+                      "to tag %s in %s", tag, directive, r->filename);
         return -1;
     }
 }
@@ -1735,17 +1697,23 @@ static apr_status_t handle_include(include_ctx_t *ctx, ap_filter_t *f,
             break;
         }
 
-        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, MAX_STRING_LEN,
+        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                             SSI_EXPAND_DROP_NAME);
         if (tag[0] == 'f') {
-            /* XXX: Port to apr_filepath_merge
-             * be safe; only files in this directory or below allowed 
-             */
-            if (!is_only_below(parsed_string)) {
+            char *newpath;
+            apr_status_t rv;
+
+            /* be safe; only files in this directory or below allowed */
+            rv = apr_filepath_merge(&newpath, NULL, tag_val,
+                                    APR_FILEPATH_NOTABOVEROOT |
+                                    APR_FILEPATH_SECUREROOTTEST |
+                                    APR_FILEPATH_NOTABSOLUTE, ctx->dpool);
+
+            if (!APR_STATUS_IS_SUCCESS(rv)) {
                 error_fmt = "unable to include file \"%s\" in parsed file %s";
             }
             else {
-                rr = ap_sub_req_lookup_uri(parsed_string, r, f->next);
+                rr = ap_sub_req_lookup_file(newpath, r, f->next);
             }
         }
         else {
@@ -1881,8 +1849,7 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
             apr_size_t e_len;
 
             val = get_include_var(ap_ssi_parse_string(ctx, tag_val, NULL,
-                                                      MAX_STRING_LEN,
-                                                      SSI_EXPAND_DROP_NAME),
+                                                      0, SSI_EXPAND_DROP_NAME),
                                   ctx);
 
             if (val) {
@@ -1901,16 +1868,12 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
                 e_len = strlen(echo_text);
             }
             else {
-                include_server_config *sconf;
-
-                sconf = ap_get_module_config(r->server->module_config,
-                                             &include_module);
-                echo_text = sconf->undefined_echo;
-                e_len = sconf->undefined_echo_len;
+                echo_text = ctx->intern->undefined_echo;
+                e_len = ctx->intern->undefined_echo_len;
             }
 
             APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(
-                                    apr_pstrmemdup(ctx->pool, echo_text, e_len),
+                                    apr_pmemdup(ctx->pool, echo_text, e_len),
                                     e_len, ctx->pool, f->c->bucket_alloc));
         }
         else if (!strcmp(tag, "encoding")) {
@@ -1943,7 +1906,8 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f,
 }
 
 /*
- * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."] -->
+ * <!--#config [timefmt="..."] [sizefmt="..."] [errmsg="..."]
+ *             [echomsg="..."] -->
  */
 static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
                                   apr_bucket_brigade *bb)
@@ -1971,7 +1935,6 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
     while (1) {
         char *tag     = NULL;
         char *tag_val = NULL;
-        char *parsed_string;
 
         ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, SSI_VALUE_RAW);
         if (!tag || !tag_val) {
@@ -1982,6 +1945,11 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
             ctx->error_str = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                                  SSI_EXPAND_DROP_NAME);
         }
+        else if (!strcmp(tag, "echomsg")) {
+            ctx->intern->undefined_echo =
+                ap_ssi_parse_string(ctx, tag_val, NULL, 0,SSI_EXPAND_DROP_NAME);
+            ctx->intern->undefined_echo_len=strlen(ctx->intern->undefined_echo);
+        }
         else if (!strcmp(tag, "timefmt")) {
             apr_time_t date = r->request_time;
 
@@ -1997,8 +1965,9 @@ static apr_status_t handle_config(include_ctx_t *ctx, ap_filter_t *f,
                            ctx->time_str, 0));
         }
         else if (!strcmp(tag, "sizefmt")) {
-            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL,
-                                                MAX_STRING_LEN,
+            char *parsed_string;
+
+            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                                 SSI_EXPAND_DROP_NAME);
             if (!strcmp(parsed_string, "bytes")) {
                 ctx->flags |= SSI_FLAG_SIZE_IN_BYTES;
@@ -2061,7 +2030,7 @@ static apr_status_t handle_fsize(include_ctx_t *ctx, ap_filter_t *f,
             break;
         }
 
-        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, MAX_STRING_LEN,
+        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                             SSI_EXPAND_DROP_NAME);
 
         if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
@@ -2148,7 +2117,7 @@ static apr_status_t handle_flastmod(include_ctx_t *ctx, ap_filter_t *f,
             break;
         }
 
-        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, MAX_STRING_LEN,
+        parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                             SSI_EXPAND_DROP_NAME);
 
         if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
@@ -2178,9 +2147,8 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
 {
     char *tag = NULL;
     char *expr = NULL;
-    char debug_buf[MAX_DEBUG_SIZE];
     request_rec *r = f->r;
-    int expr_ret, was_error, was_unmatched;
+    int expr_ret, was_error;
 
     if (ctx->argc != 1) {
         ap_log_rerror(APLOG_MARK,
@@ -2217,28 +2185,16 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
         return APR_SUCCESS;
     }
-#ifdef DEBUG_INCLUDE
-    do {
-        apr_size_t d_len = 0;
-        d_len = sprintf(debug_buf, "**** if expr=\"%s\"\n", expr);
-        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_heap_create(debug_buf, d_len,
-                                NULL, f->c->bucket_alloc));
 
-    } while (0);
-#endif
+    DEBUG_PRINTF((ctx, "****    if expr=\"%s\"\n", expr));
 
-    expr_ret = parse_expr(r, ctx, expr, &was_error, &was_unmatched, debug_buf);
+    expr_ret = parse_expr(ctx, expr, &was_error);
 
     if (was_error) {
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
         return APR_SUCCESS;
     }
 
-    if (was_unmatched) {
-        DUMP_PARSE_EXPR_DEBUG("\nUnmatched '\n", f, bb);
-    }
-    DUMP_PARSE_EXPR_DEBUG(debug_buf, f, bb);
-
     if (expr_ret) {
         ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
     }
@@ -2246,7 +2202,8 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f,
         ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
     }
 
-    LOG_COND_STATUS(ctx, f, bb, "   if");
+    DEBUG_DUMP_COND(ctx, "   if");
+
     ctx->if_nesting_level = 0;
 
     return APR_SUCCESS;
@@ -2261,8 +2218,7 @@ static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
     char *tag = NULL;
     char *expr = NULL;
     request_rec *r = f->r;
-    char debug_buf[MAX_DEBUG_SIZE];
-    int expr_ret, was_error, was_unmatched;
+    int expr_ret, was_error;
 
     if (ctx->argc != 1) {
         ap_log_rerror(APLOG_MARK,
@@ -2297,34 +2253,22 @@ static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
         return APR_SUCCESS;
     }
-#ifdef DEBUG_INCLUDE
-    do {
-        apr_size_t d_len = 0;
-        d_len = sprintf(debug_buf, "**** elif expr=\"%s\"\n", expr);
-        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_heap_create(debug_buf, d_len,
-                                NULL, f->c->bucket_alloc));
-    } while (0);
-#endif
 
-    LOG_COND_STATUS(ctx, f, bb, " elif");
+    DEBUG_PRINTF((ctx, "****  elif expr=\"%s\"\n", expr));
+    DEBUG_DUMP_COND(ctx, " elif");
 
     if (ctx->flags & SSI_FLAG_COND_TRUE) {
         ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
         return APR_SUCCESS;
     }
 
-    expr_ret = parse_expr(r, ctx, expr, &was_error, &was_unmatched, debug_buf);
+    expr_ret = parse_expr(ctx, expr, &was_error);
 
     if (was_error) {
         SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
         return APR_SUCCESS;
     }
 
-    if (was_unmatched) {
-        DUMP_PARSE_EXPR_DEBUG("\nUnmatched '\n", f, bb);
-    }
-    DUMP_PARSE_EXPR_DEBUG(debug_buf, f, bb);
-
     if (expr_ret) {
         ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
     }
@@ -2332,7 +2276,7 @@ static apr_status_t handle_elif(include_ctx_t *ctx, ap_filter_t *f,
         ctx->flags &= SSI_FLAG_CLEAR_PRINT_COND;
     }
 
-    LOG_COND_STATUS(ctx, f, bb, " elif");
+    DEBUG_DUMP_COND(ctx, " elif");
 
     return APR_SUCCESS;
 }
@@ -2364,7 +2308,7 @@ static apr_status_t handle_else(include_ctx_t *ctx, ap_filter_t *f,
         return APR_SUCCESS;
     }
 
-    LOG_COND_STATUS(ctx, f, bb, " else");
+    DEBUG_DUMP_COND(ctx, " else");
             
     if (ctx->flags & SSI_FLAG_COND_TRUE) {
         ctx->flags &= SSI_FLAG_CLEAR_PRINTING;
@@ -2401,7 +2345,8 @@ static apr_status_t handle_endif(include_ctx_t *ctx, ap_filter_t *f,
         return APR_SUCCESS;
     }
 
-    LOG_COND_STATUS(ctx, f, bb, "endif");
+    DEBUG_DUMP_COND(ctx, "endif");
+
     ctx->flags |= (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE);
 
     return APR_SUCCESS;
@@ -2454,7 +2399,7 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
         }
 
         if (!strcmp(tag, "var")) {
-            var = ap_ssi_parse_string(ctx, tag_val, NULL, MAX_STRING_LEN,
+            var = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                       SSI_EXPAND_DROP_NAME);
         }
         else if (!strcmp(tag, "value")) {
@@ -2468,8 +2413,7 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f,
                 break;
             }
 
-            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL,
-                                                MAX_STRING_LEN,
+            parsed_string = ap_ssi_parse_string(ctx, tag_val, NULL, 0,
                                                 SSI_EXPAND_DROP_NAME);
             apr_table_setn(r->subprocess_env, apr_pstrdup(p, var),
                            apr_pstrdup(p, parsed_string));
@@ -3531,6 +3475,7 @@ static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
                                            intern->directive_len);
 
                 if (handle_func) {
+                    DEBUG_INIT(ctx, f, pass_bb);
                     rv = handle_func(ctx, f, pass_bb);
                     if (!APR_STATUS_IS_SUCCESS(rv)) {
                         apr_brigade_destroy(pass_bb);
@@ -3680,6 +3625,8 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
                                              strlen(intern->start_seq));
         intern->end_seq = sconf->default_end_tag;
         intern->end_seq_len = strlen(intern->end_seq);
+        intern->undefined_echo = conf->undefined_echo;
+        intern->undefined_echo_len = strlen(conf->undefined_echo);
     }
 
     if ((parent = ap_get_module_config(r->request_config, &include_module))) {
@@ -3799,6 +3746,7 @@ static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
 
     result->default_error_msg = DEFAULT_ERROR_MSG;
     result->default_time_fmt  = DEFAULT_TIME_FORMAT;
+    result->undefined_echo    = DEFAULT_UNDEFINED_ECHO;
     result->xbithack          = DEFAULT_XBITHACK;
 
     return result;
@@ -3811,8 +3759,6 @@ static void *create_includes_server_config(apr_pool_t *p, server_rec *server)
     result = apr_palloc(p, sizeof(include_server_config));
     result->default_end_tag    = DEFAULT_END_SEQUENCE;
     result->default_start_tag  = DEFAULT_START_SEQUENCE;
-    result->undefined_echo     = DEFAULT_UNDEFINED_ECHO;
-    result->undefined_echo_len = sizeof(DEFAULT_UNDEFINED_ECHO) - 1;
 
     return result; 
 }
@@ -3880,11 +3826,8 @@ static const char *set_default_end_tag(cmd_parms *cmd, void *mconfig,
 static const char *set_undefined_echo(cmd_parms *cmd, void *mconfig,
                                       const char *msg)
 {
-    include_server_config *conf;
-
-    conf = ap_get_module_config(cmd->server->module_config, &include_module);
+    include_dir_config *conf = mconfig;
     conf->undefined_echo = msg;
-    conf->undefined_echo_len = strlen(msg);
 
     return NULL;
 }
@@ -3952,7 +3895,7 @@ static const command_rec includes_cmds[] =
                   "SSI Start String Tag"),
     AP_INIT_TAKE1("SSIEndTag", set_default_end_tag, NULL, RSRC_CONF,
                   "SSI End String Tag"),
-    AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, RSRC_CONF,
+    AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
                   "String to be displayed if an echoed variable is undefined"),
     {NULL}
 };