Index: server/util_expr_eval.c
===================================================================
--- server/util_expr_eval.c	(revision 1640910)
+++ server/util_expr_eval.c	(working copy)
@@ -24,6 +24,7 @@
 #include "http_protocol.h"
 #include "http_request.h"
 #include "ap_provider.h"
+#include "util_varbuf.h"
 #include "util_expr_private.h"
 #include "util_md5.h"
 
@@ -32,6 +33,8 @@
 #include "apr_base64.h"
 #include "apr_sha1.h"
 #include "apr_version.h"
+#include "apr_strings.h"
+#include "apr_strmatch.h"
 #if APR_VERSION_AT_LEAST(1,5,0)
 #include "apr_escape.h"
 #endif
@@ -183,13 +186,29 @@
                                             const ap_expr_t *info,
                                             const ap_expr_t *arg)
 {
-    ap_expr_string_func_t *func = (ap_expr_string_func_t *)info->node_arg1;
     const void *data = info->node_arg2;
 
     AP_DEBUG_ASSERT(info->node_op == op_StringFuncInfo);
-    AP_DEBUG_ASSERT(func != NULL);
+    AP_DEBUG_ASSERT(info->node_arg1 != NULL);
     AP_DEBUG_ASSERT(data != NULL);
-    return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
+    if (arg->node_op == op_ListElement) {
+        /* Evaluate the list elements and store them in apr_array_header. */
+        ap_expr_string_list_func_t *func = (ap_expr_string_list_func_t *)info->node_arg1;
+        apr_array_header_t *args = apr_array_make(ctx->p, 1, sizeof(char *));
+        do {
+            const ap_expr_t *val = arg->node_arg1;
+            const char **new = apr_array_push(args);
+            *new = ap_expr_eval_word(ctx, val);
+
+            arg = arg->node_arg2;
+        } while (arg != NULL);
+
+        return (*func)(ctx, data, args);
+    }
+    else {
+        ap_expr_string_func_t *func = (ap_expr_string_func_t *)info->node_arg1;
+        return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
+    }
 }
 
 static int intstrcmp(const char *s1, const char *s2)
@@ -443,7 +462,27 @@
     parms.func  = &info->node_arg1;
     parms.data  = &info->node_arg2;
     parms.err   = &ctx->error2;
-    parms.arg   = (arg && arg->node_op == op_String) ? arg->node_arg1 : NULL;
+    parms.arg   = NULL;
+    if (arg) {
+        switch(arg->node_op) {
+            case op_String:
+                parms.arg = arg->node_arg1;
+                break;
+            case op_ListElement:
+                do {
+                    const ap_expr_t *val = arg->node_arg1;
+                    if (val->node_op == op_String) {
+                        parms.arg = val->node_arg1;
+                    }
+                    arg = arg->node_arg2;
+                } while (arg != NULL);
+                break;
+            default:
+                break;
+        }
+    }
+                        ap_log_error(APLOG_MARK, APLOG_ERR, 0, 0,
+                                    "sss %s", parms.arg);
     if (ctx->lookup_fn(&parms) != OK)
         return NULL;
     return info;
@@ -1071,6 +1110,59 @@
 }
 #endif
 
+static int replace_func_parse_arg(ap_expr_lookup_parms *parms)
+{
+    const char *original = parms->arg;
+    const apr_strmatch_pattern *pattern;
+
+    if (!parms->arg) {
+        *parms->err = apr_psprintf(parms->ptemp, "replace() function needs "
+                                   "exactly 3 arguments");
+        return !OK;
+    }
+    pattern = apr_strmatch_precompile(parms->pool, original, 0);
+    *parms->data = pattern;
+    return OK;
+}
+
+static const char *replace_func(ap_expr_eval_ctx_t *ctx, const void *data,
+                               const apr_array_header_t *args)
+{
+    char *buff, *original, *replacement;
+    struct ap_varbuf vb;
+    apr_size_t repl_len;
+    const char *repl;
+    apr_size_t bytes;
+    apr_size_t len;
+    const apr_strmatch_pattern *pattern = data;
+    if (args->nelts != 3) {
+        *ctx->err = apr_psprintf(ctx->p, "replace() function needs "
+                                 "exactly 3 arguments");
+        return "";
+    }
+
+    buff = APR_ARRAY_IDX(args, 2, char *);
+    original = APR_ARRAY_IDX(args, 1, char *);
+    replacement = APR_ARRAY_IDX(args, 0, char *);
+    repl_len = strlen(replacement);
+    bytes = strlen(buff);
+
+    ap_varbuf_init(ctx->p, &vb, 0);
+    vb.strlen = 0;
+    
+    while ((repl = apr_strmatch(pattern, buff, bytes))) {
+        len = (apr_size_t) (repl - buff);
+        ap_varbuf_strmemcat(&vb, buff, len);
+        ap_varbuf_strmemcat(&vb, replacement, repl_len);
+
+        len += repl_len;
+        bytes -= len;
+        buff += len;
+    }
+
+    return ap_varbuf_pdup(ctx->p, &vb, NULL, 0, buff, bytes, &len);
+}
+
 #define MAX_FILE_SIZE 10*1024*1024
 static const char *file_func(ap_expr_eval_ctx_t *ctx, const void *data,
                              char *arg)
@@ -1657,6 +1749,7 @@
 #if APR_VERSION_AT_LEAST(1,6,0)
     { ldap_func,            "ldap",           NULL, 0 },
 #endif
+    { replace_func,         "replace",        replace_func_parse_arg, 0 },
     { NULL, NULL, NULL}
 };
 
Index: include/ap_expr.h
===================================================================
--- include/ap_expr.h	(revision 1640910)
+++ include/ap_expr.h	(working copy)
@@ -232,6 +232,16 @@
                                             const void *data,
                                             const char *arg);
 
+/** String valued function, takes a list argument and returns a string
+ * @param ctx The evaluation context
+ * @param data An opaque context provided by the lookup hook function
+ * @param args The list of string arguments
+ * @return The functions result string, may be NULL for 'empty string'
+ */
+typedef const char *(ap_expr_string_list_func_t)(ap_expr_eval_ctx_t *ctx,
+                                            const void *data,
+                                            const apr_array_header_t *args);
+
 /** List valued function, takes a string argument and returns a list of strings
  * Can currently only be called following the builtin '-in' operator.
  * @param ctx The evaluation context
@@ -276,7 +286,9 @@
     const char **err;
 
     /** arg for pre-parsing (only if a simple string).
-     *  For binary ops, this is the right argument. */
+     *  For binary ops, this is the right argument.
+     *  For functions with more arguments, this is the first string
+     *  argument. */
     const char *arg;
 } ap_expr_lookup_parms;
 
Index: server/util_expr_parse.y
===================================================================
--- server/util_expr_parse.y	(revision 1640910)
+++ server/util_expr_parse.y	(working copy)
@@ -205,6 +205,7 @@
             ;
 
 strfunccall : T_ID '(' word ')' { $$ = ap_expr_str_func_make($1, $3, ctx); }
+            | T_ID '(' words ')' { $$ = ap_expr_str_func_make($1, $3, ctx); }
             ;
 
 %%
