]> granicus.if.org Git - apache/commitdiff
Flesh out ap_expr with:
authorNick Kew <niq@apache.org>
Mon, 31 Mar 2008 12:16:58 +0000 (12:16 +0000)
committerNick Kew <niq@apache.org>
Mon, 31 Mar 2008 12:16:58 +0000 (12:16 +0000)
 * Re-usable parse trees
 * Canonical string parser function (candidate)

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@642971 13f79535-47bb-0310-9956-ffa450edef68

include/ap_expr.h
include/ap_mmn.h
server/main.c
server/util_expr.c

index a87d322a9f078f65b93712b8dd7db22b1678d77c..2dcaf3935ee1713fbe6d6dbd3a6ca52f4b5dce14 100644 (file)
@@ -23,6 +23,7 @@
 #define AP_EXPR_H
 
 #include "httpd.h"
+#include "ap_regex.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -65,7 +66,7 @@ typedef struct parse_node {
 #ifdef DEBUG_INCLUDE
     int dump_done;
 #endif
-} parse_node_t;
+} ap_parse_node_t;
 
 typedef struct {
     const char *source;
@@ -74,8 +75,8 @@ typedef struct {
     ap_regmatch_t match[AP_MAX_REG_MATCH];
 } backref_t;
 
-typedef char *(*string_func_t)(request_rec*, const char*);
-typedef int (*opt_func_t)(request_rec*, parse_node_t*, string_func_t);
+typedef const char *(*string_func_t)(request_rec*, const char*);
+typedef int (*opt_func_t)(request_rec*, ap_parse_node_t*, string_func_t);
 
 /**
  * Parse an expression into a parse tree
@@ -84,8 +85,8 @@ typedef int (*opt_func_t)(request_rec*, parse_node_t*, string_func_t);
  * @param was_error On return, set to zero if parse successful, nonzero on error
  * @return The parse tree
  */
-AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t *pool, const char *expr,
-                                        int *was_error);
+AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t *pool, const char *expr,
+                                           int *was_error);
 /**
  * Evaluate a parse tree
  * @param r The current request
@@ -93,10 +94,11 @@ AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t *pool, const char *expr,
  * @param was_error On return, set to zero if parse successful, nonzero on error
  * @param reptr Regular expression memory for backreferencing if a regexp was parsed
  * @param string_func String parser function - perform variable substitutions
+ *                    Use ap_expr_string where applicable
  * @param eval_func Option evaluation function (e.g. -A filename)
  * @return the value the expression parsed to
  */
-AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
+AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
                              int *was_error, backref_t **reptr,
                              string_func_t string_func, opt_func_t eval_func);
 /**
@@ -106,6 +108,7 @@ AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
  * @param was_error On return, set to zero if parse successful, nonzero on error
  * @param reptr Regular expression memory for backreferencing if a regexp was parsed
  * @param string_func String parser function - perform variable substitutions
+ *                    Use ap_expr_string where applicable
  * @param eval_func Option evaluation function (e.g. -A filename)
  * @return the value the expression parsed to
  */
@@ -114,6 +117,43 @@ AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
                                    string_func_t string_func,
                                    opt_func_t eval_func);
 
+/**
+ * Internal initialisation of ap_expr (for httpd)
+ * @param pool Pool
+ * @return APR_SUCCESS or error
+ */
+AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool);
+
+/**
+ * Default string evaluation function for passing to ap_expr_eval and
+ * ap_expr_evalstring.  Use this (and update as necessary) to offer
+ * a consistent expression syntax across different modules.
+ * Supports the following:
+ *     $req{foo}     - request header "foo"
+ *     $resp{foo}    - response header "foo"
+ *     $env{foo}     - environment variable "foo"
+ *     $handler      - r->handler
+ *     $content-type - r->content_type
+ * Other strings are returned unmodified.
+ * @param r The current request
+ * @param str The string to evaluate
+ * @return The evaluated string
+ */
+AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str);
+
+/**
+ * Clone a parse tree.  This is required if you create a parse tree
+ * using ap_expr_parse, and wish to re-use it many times in ap_expr_eval.
+ * It is not required if you need to use it just once.
+ * @param pool Pool
+ * @param node The parse tree to clone
+ * @param parent Parent node (for internal use when recursing - pass in NULL)
+ * @return The cloned tree
+ */
+AP_DECLARE(ap_parse_node_t*) ap_expr_clone_tree(apr_pool_t *pool,
+                                                ap_parse_node_t *node,
+                                                ap_parse_node_t *parent);
+
 #ifdef __cplusplus
 }
 #endif
index 02ee76152f09be04bc8648d4251e777edfc64996..52f324063f9a8de37b12c8bbf53624a27817cb7a 100644 (file)
  * 20071108.8 (2.3.0-dev)  Add optional function ap_logio_add_bytes_in() to mog_logio
  * 20071108.9 (2.3.0-dev)  Add chroot support to unixd_config
  * 20071108.10(2.3.0-dev)  Introduce new ap_expr API
+ * 20071108.11(2.3.0-dev)  Revise/Expand new ap_expr API
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20071108
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 10                    /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 11                    /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index 92d4b8143ec85add1d43997bfce6f03b347270f7..e758211375e4eff3491ea43210eabc20c49beadd 100644 (file)
@@ -40,6 +40,7 @@
 #include "util_ebcdic.h"
 #include "ap_mpm.h"
 #include "mpm_common.h"
+#include "ap_expr.h"
 
 #if APR_HAVE_UNISTD_H
 #include <unistd.h>
@@ -492,6 +493,9 @@ int main(int argc, const char * const argv[])
         destroy_and_exit_process(process, 1);
     }
 #endif
+    if (ap_expr_init(pglobal) != APR_SUCCESS) {
+        destroy_and_exit_process(process, 1);
+    }
 
     apr_pool_create(&pcommands, pglobal);
     apr_pool_tag(pcommands, "pcommands");
index 15f20c0e14088d6b0e286a5603bab8145311da59..dedde033ebe281ee68f7564e9a92bb2718a5a5ff 100644 (file)
@@ -26,6 +26,7 @@
 #include "http_log.h"
 
 #include "ap_expr.h"
+#include <assert.h>
 #if 1
 /*
  * +-------------------------------------------------------+
 } while(0)
 
 #define CREATE_NODE(pool,name) do {                          \
-    (name) = apr_palloc(pool, sizeof(*(name)));      \
-    (name)->parent = (name)->left = (name)->right = NULL; \
-    (name)->done = 0;                                     \
-    (name)->dump_done = 0;                                \
+    (name) = apr_pcalloc(pool, sizeof(*(name)));
 } while(0)
 
 static void debug_printf(request_rec *r, const char *fmt, ...)
@@ -65,7 +63,7 @@ static void debug_printf(request_rec *r, const char *fmt, ...)
 }
 
 #define DUMP__CHILD(ctx, is, node, child) if (1) {                           \
-    parse_node_t *d__c = node->child;                                        \
+    ap_parse_node_t *d__c = node->child;                                     \
     if (d__c) {                                                              \
         if (!d__c->dump_done) {                                              \
             if (d__c->parent != node) {                                      \
@@ -90,9 +88,9 @@ static void debug_printf(request_rec *r, const char *fmt, ...)
     }                                                                        \
 }
 
-static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
+static void debug_dump_tree(include_ctx_t *ctx, ap_parse_node_t *root)
 {
-    parse_node_t *current;
+    ap_parse_node_t *current;
     char *is;
 
     if (!root) {
@@ -240,9 +238,7 @@ static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
 
 #define CREATE_NODE(pool,name) do {                       \
-    (name) = apr_palloc(pool, sizeof(*(name)));   \
-    (name)->parent = (name)->left = (name)->right = NULL; \
-    (name)->done = 0;                                     \
+    (name) = apr_pcalloc(pool, sizeof(*(name)));       \
 } while(0)
 
 #define DEBUG_INIT(ctx, f, bb)
@@ -269,7 +265,7 @@ static APR_INLINE int re_check(request_rec *r, const char *string,
                                const char *rexp, backref_t **reptr)
 {
     ap_regex_t *compiled;
-    backref_t *re = *reptr;
+    backref_t *re = reptr ? *reptr : NULL;
     int rc;
 
     compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
@@ -280,7 +276,10 @@ static APR_INLINE int re_check(request_rec *r, const char *string,
     }
 
     if (!re) {
-        re = *reptr = apr_palloc(r->pool, sizeof(*re));
+        re = apr_palloc(r->pool, sizeof(*re));
+        if (reptr) {
+            *reptr = re;
+        }
     }
 
     re->source = apr_pstrdup(r->pool, string);
@@ -473,10 +472,10 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
 }
 
 /* This is what we export.  We can split it in two. */
-AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
-                                        int *was_error)
+AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
+                                           int *was_error)
 {
-    parse_node_t *new, *root = NULL, *current = NULL;
+    ap_parse_node_t *new, *root = NULL, *current = NULL;
     const char *error = "Invalid expression \"%s\" in file %s";
     const char *parse = expr;
     int was_unmatched = 0;
@@ -671,21 +670,40 @@ AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
     return root;
 }
 
+AP_DECLARE(ap_parse_node_t*) ap_expr_clone_tree(apr_pool_t *pool,
+                                                ap_parse_node_t *pnode,
+                                               ap_parse_node_t *parent)
+{
+    ap_parse_node_t *ret;
+    ret = apr_pmemdup(pool, pnode, sizeof(ap_parse_node_t));
+    if (pnode->left) {
+        ret->left = ap_expr_clone_tree(pool, pnode->left, ret);
+    }
+    if (pnode->right) {
+        ret->right = ap_expr_clone_tree(pool, pnode->right, ret);
+    }
+    ret->parent = parent;
+    return ret;
+}
+
 #define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
-AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
+AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
                              int *was_error, backref_t **reptr,
                              string_func_t string_func, opt_func_t eval_func)
 {
-    parse_node_t *current = root;
+    ap_parse_node_t *current = root;
     const char *error = NULL;
     unsigned int regex = 0;
+    const char *val;
+    const char *lval;
+    const char *rval;
 
     /* Evaluate Parse Tree */
     while (current) {
         switch (current->token.type) {
         case TOKEN_STRING:
-            current->token.value = PARSE_STRING(r, current->token.value);
-            current->value = !!*current->token.value;
+            val = PARSE_STRING(r, current->token.value);
+            current->value = !!*val;
             break;
 
         case TOKEN_AND:
@@ -700,9 +718,8 @@ AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
             if (!current->left->done) {
                 switch (current->left->token.type) {
                 case TOKEN_STRING:
-                    current->left->token.value =
-                        PARSE_STRING(r, current->left->token.value);
-                    current->left->value = !!*current->left->token.value;
+                    lval = PARSE_STRING(r, current->left->token.value);
+                    current->left->value = !!*lval;
                     DEBUG_DUMP_EVAL(ctx, current->left);
                     current->left->done = 1;
                     break;
@@ -723,9 +740,8 @@ AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
                 if (!current->right->done) {
                     switch (current->right->token.type) {
                     case TOKEN_STRING:
-                        current->right->token.value =
-                            PARSE_STRING(r,current->right->token.value);
-                        current->right->value = !!*current->right->token.value;
+                        rval = PARSE_STRING(r,current->right->token.value);
+                        current->right->value = !!*rval;
                         DEBUG_DUMP_EVAL(r, current->right);
                         current->right->done = 1;
                         break;
@@ -758,19 +774,15 @@ AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
                 *was_error = 1;
                 return 0;
             }
-            current->left->token.value =
-                PARSE_STRING(r, current->left->token.value);
-            current->right->token.value =
-                PARSE_STRING(r, current->right->token.value);
+            lval = PARSE_STRING(r, current->left->token.value);
+            rval = PARSE_STRING(r, current->right->token.value);
 
             if (current->right->token.type == TOKEN_RE) {
-                current->value = re_check(r, current->left->token.value,
-                                          current->right->token.value, reptr);
+                current->value = re_check(r, lval, rval, reptr);
                 --regex;
             }
             else {
-                current->value = !strcmp(current->left->token.value,
-                                         current->right->token.value);
+                current->value = !strcmp(lval, rval);
             }
 
             if (current->token.type == TOKEN_NE) {
@@ -791,13 +803,10 @@ AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
                 return 0;
             }
 
-            current->left->token.value =
-                PARSE_STRING(r, current->left->token.value);
-            current->right->token.value =
-                PARSE_STRING(r, current->right->token.value);
+            lval = PARSE_STRING(r, current->left->token.value);
+            rval = PARSE_STRING(r, current->right->token.value);
 
-            current->value = strcmp(current->left->token.value,
-                                    current->right->token.value);
+            current->value = strcmp(lval, rval);
 
             switch (current->token.type) {
             case TOKEN_GE: current->value = current->value >= 0; break;
@@ -864,7 +873,7 @@ AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
                                    string_func_t string_func,
                                    opt_func_t eval_func)
 {
-    parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
+    ap_parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
     if (*was_error || !root) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                       "Error parsing expression in %s", r->filename);   
@@ -872,3 +881,65 @@ AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
     }
     return ap_expr_eval(r, root, was_error, reptr, string_func, eval_func);
 }
+
+
+static ap_regex_t *isvar = NULL;
+AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str)
+{
+    /* a default string evaluator: support headers and env */
+    ap_regmatch_t match[3];
+    assert(isvar != NULL);
+    if (ap_regexec(isvar, str, 3, match, 0) == 0) {
+        apr_table_t *table = NULL;
+        int len = match[1].rm_eo-match[1].rm_so;
+        const char *name = str+match[1].rm_so;
+        if (!strncasecmp("req", name, len)) {
+            table = r->headers_in;
+        }
+        else if (!strncasecmp("resp", name, len)) {
+            table = r->headers_out;
+        }
+        else if (!strncasecmp("env", name, len)) {
+            table = r->subprocess_env;
+        }
+        if (table != NULL) {
+            char *key = apr_pstrndup(r->pool, str+match[2].rm_so,
+                                     match[2].rm_eo-match[2].rm_so);
+            return apr_table_get(table, key);
+        }
+    }
+    else if (str[0] == '$') {
+        if (!strcasecmp(str, "$handler")) {
+            return r->handler;
+        }
+        else if (!strcasecmp(str, "$content-type")) {
+            return r->content_type;
+        }
+    }
+    /* TODO: provide a hook so modules can interpret other patterns */
+    /* OhBugger, where's the regexp for backreferences ? */
+    return str;  /* default - literal string as-is */
+}
+static apr_status_t ap_expr_term(void *expr)
+{
+    if (isvar) {
+        ap_regfree(isvar);
+        isvar = NULL;
+    }
+    return APR_SUCCESS;
+}
+AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool)
+{
+    static ap_regex_t var;
+    if (!isvar) {
+        isvar = &var;
+        if (ap_regcomp(isvar, "\\$([A-Za-z0-9]+)\\{([^\\}]+)\\}", 0)) {
+            isvar = NULL;
+        }
+        else {
+            apr_pool_cleanup_register(pool, isvar, ap_expr_term,
+                                      apr_pool_cleanup_null);
+        }
+    }
+    return isvar ? APR_SUCCESS : APR_EGENERAL;
+}