Index: modules/mappers/mod_rewrite.c
===================================================================
--- modules/mappers/mod_rewrite.c	(revision 1586508)
+++ modules/mappers/mod_rewrite.c	(working copy)
@@ -182,6 +182,7 @@
 #define MAPTYPE_RND                 1<<4
 #define MAPTYPE_DBD                 1<<5
 #define MAPTYPE_DBD_CACHE           1<<6
+#define MAPTYPE_REGEXP              1<<7
 
 #define ENGINE_DISABLED             1<<0
 #define ENGINE_ENABLED              1<<1
@@ -247,20 +248,20 @@
  */
 
 typedef struct {
-    const char *datafile;          /* filename for map data files         */
-    const char *dbmtype;           /* dbm type for dbm map data files     */
-    const char *checkfile;         /* filename to check for map existence */
-    const char *cachename;         /* for cached maps (txt/rnd/dbm)       */
-    int   type;                    /* the type of the map                 */
-    apr_file_t *fpin;              /* in  file pointer for program maps   */
-    apr_file_t *fpout;             /* out file pointer for program maps   */
-    apr_file_t *fperr;             /* err file pointer for program maps   */
-    char *(*func)(request_rec *,   /* function pointer for internal maps  */
+    const char *datafile;          /* filename for map data files          */
+    const char *dbmtype;           /* dbm type for dbm map data files      */
+    const char *checkfile;         /* filename to check for map existence  */
+    const char *cachename;         /* for cached maps (txt/regexp/rnd/dbm) */
+    int   type;                    /* the type of the map                  */
+    apr_file_t *fpin;              /* in  file pointer for program maps    */
+    apr_file_t *fpout;             /* out file pointer for program maps    */
+    apr_file_t *fperr;             /* err file pointer for program maps    */
+    char *(*func)(request_rec *,   /* function pointer for internal maps   */
                   char *);
-    char **argv;                   /* argv of the external rewrite map    */
-    const char *dbdq;              /* SQL SELECT statement for rewritemap */
+    char **argv;                   /* argv of the external rewrite map     */
+    const char *dbdq;              /* SQL SELECT statement for rewritemap  */
     const char *checkfile2;        /* filename to check for map existence
-                                      NULL if only one file               */
+                                      NULL if only one file                */
 } rewritemap_entry;
 
 /* special pattern types for RewriteCond */
@@ -357,8 +358,15 @@
     apr_time_t mtime;
     apr_pool_t *pool;
     apr_hash_t *entries;
+    apr_array_header_t *regexps;
 } cachedmap;
 
+typedef struct regexp_entry {
+    ap_regex_t *regexp;
+    const char *pattern;
+    const char *replacement;
+} regexp_entry;
+
 /* the regex structure for the
  * substitution of backreferences
  */
@@ -959,6 +967,39 @@
  * +-------------------------------------------------------+
  */
 
+static cachedmap* find_cachemap(const char *name, apr_time_t t)
+{
+    cachedmap* map;
+
+    /* this assumes cachep is already locked, if appropriate */
+
+    map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
+
+    if (!map) {
+        apr_pool_t *p;
+
+        if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
+            return NULL;
+        }
+
+        map = apr_palloc(cachep->pool, sizeof(cachedmap));
+        map->pool = p;
+        map->entries = apr_hash_make(map->pool);
+        map->regexps = NULL;
+        map->mtime = t;
+
+        apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
+    }
+    else if (map->mtime != t) {
+        apr_pool_clear(map->pool);
+        map->entries = apr_hash_make(map->pool);
+        map->regexps = NULL;
+        map->mtime = t;
+    }
+
+    return map;
+}
+
 static void set_cache_value(const char *name, apr_time_t t, char *key,
                             char *val)
 {
@@ -968,41 +1009,20 @@
 #if APR_HAS_THREADS
         apr_thread_mutex_lock(cachep->lock);
 #endif
-        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
+        map = find_cachemap(name, t);
 
-        if (!map) {
-            apr_pool_t *p;
-
-            if (apr_pool_create(&p, cachep->pool) != APR_SUCCESS) {
-#if APR_HAS_THREADS
-                apr_thread_mutex_unlock(cachep->lock);
-#endif
-                return;
-            }
-
-            map = apr_palloc(cachep->pool, sizeof(cachedmap));
-            map->pool = p;
-            map->entries = apr_hash_make(map->pool);
-            map->mtime = t;
-
-            apr_hash_set(cachep->maps, name, APR_HASH_KEY_STRING, map);
+        if (map) {
+            /* Now we should have a valid map->entries hash, where we
+             * can store our value.
+             *
+             * We need to copy the key and the value into OUR pool,
+             * so that we don't leave it during the r->pool cleanup.
+             */
+            apr_hash_set(map->entries,
+                         apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING,
+                         apr_pstrdup(map->pool, val));
         }
-        else if (map->mtime != t) {
-            apr_pool_clear(map->pool);
-            map->entries = apr_hash_make(map->pool);
-            map->mtime = t;
-        }
 
-        /* Now we should have a valid map->entries hash, where we
-         * can store our value.
-         *
-         * We need to copy the key and the value into OUR pool,
-         * so that we don't leave it during the r->pool cleanup.
-         */
-        apr_hash_set(map->entries,
-                     apr_pstrdup(map->pool, key), APR_HASH_KEY_STRING,
-                     apr_pstrdup(map->pool, val));
-
 #if APR_HAS_THREADS
         apr_thread_mutex_unlock(cachep->lock);
 #endif
@@ -1021,24 +1041,16 @@
 #if APR_HAS_THREADS
         apr_thread_mutex_lock(cachep->lock);
 #endif
-        map = apr_hash_get(cachep->maps, name, APR_HASH_KEY_STRING);
+        map = find_cachemap(name, t);
 
         if (map) {
-            /* if this map is outdated, forget it. */
-            if (map->mtime != t) {
-                apr_pool_clear(map->pool);
-                map->entries = apr_hash_make(map->pool);
-                map->mtime = t;
+            val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING);
+            if (val) {
+                /* copy the cached value into the supplied pool,
+                 * where it belongs (r->pool usually)
+                 */
+                val = apr_pstrdup(p, val);
             }
-            else {
-                val = apr_hash_get(map->entries, key, APR_HASH_KEY_STRING);
-                if (val) {
-                    /* copy the cached value into the supplied pool,
-                     * where it belongs (r->pool usually)
-                     */
-                    val = apr_pstrdup(p, val);
-                }
-            }
         }
 
 #if APR_HAS_THREADS
@@ -1296,6 +1308,130 @@
     return value;
 }
 
+static void build_regexp_cache(request_rec *r, const char *file, cachedmap *map)
+{
+    apr_file_t *fp = NULL;
+    char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
+
+    if (apr_file_open(&fp, file, APR_READ|APR_BUFFERED, APR_OS_DEFAULT,
+                      r->pool) != APR_SUCCESS) {
+        return;
+    }
+
+    while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
+        char *p, *c, *linelast;
+        ap_regex_t *regexp;
+        regexp_entry* re;
+
+        /* ignore comments and lines starting with whitespaces */
+        if (*line == '#' || apr_isspace(*line)) {
+            continue;
+        }
+
+        p = line;
+        linelast = line + strlen(line);
+        while (p < linelast && !apr_isspace(*p)) {
+            ++p;
+        }
+
+        /* no value */
+        if (p == linelast) {
+          continue;
+        }
+
+        *p++ = '\0';
+
+        regexp = ap_pregcomp(map->pool, line, AP_REG_EXTENDED | AP_REG_ICASE);
+
+        if (!regexp) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
+                          "RewriteMap regexp:%s: cannot compile regular expression '%s'",
+                          file, line);
+            continue;
+        }
+
+        /* jump to the value */
+        while (*p && apr_isspace(*p)) {
+            ++p;
+        }
+
+        /* no value? ignore */
+        if (!*p) {
+            continue;
+        }
+
+        /* extract the value and return. */
+        c = p;
+        while (*p && !apr_isspace(*p)) {
+            ++p;
+        }
+
+        re = apr_array_push(map->regexps);
+
+        re->regexp = regexp;
+        re->pattern = apr_pstrdup(map->pool, line);
+        re->replacement = apr_pstrmemdup(map->pool, c, p - c);
+    }
+
+    apr_file_close(fp);
+}
+
+static char *lookup_map_regexp(request_rec *r, rewritemap_entry* map, apr_time_t t, char *key, char** pattern)
+{
+    char *value = NULL;
+
+    if (cachep) {
+      cachedmap *cmap;
+
+#if APR_HAS_THREADS
+        apr_thread_mutex_lock(cachep->lock);
+#endif
+
+        cmap = find_cachemap(map->cachename, t);
+
+        if (cmap) {
+            int i;
+            regexp_entry* regexps;
+
+            if (!cmap->regexps) {
+                cmap->regexps = apr_array_make(cmap->pool, 10, sizeof(regexp_entry));
+
+                build_regexp_cache(r, map->datafile, cmap);
+            }
+
+            regexps = (regexp_entry*)cmap->regexps->elts;
+
+            for (i = 0; i < cmap->regexps->nelts; i++) {
+                int rc;
+                ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
+                regexp_entry* re = &regexps[i];
+
+                rc = ap_regexec(re->regexp, key, AP_MAX_REG_MATCH, regmatch, 0);
+
+                if (rc == AP_REG_NOMATCH) {
+                    continue;
+                }
+                else if (rc) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                                  "RewriteMap regexp:%s: error matching regular expression '%s'",
+                                  map->datafile, re->pattern);
+                    continue;
+                }
+
+                value = ap_pregsub(r->pool, re->replacement, key, AP_MAX_REG_MATCH, regmatch);
+                *pattern = apr_pstrdup(r->pool, re->pattern);
+                break;
+            }
+        }
+
+#if APR_HAS_THREADS
+        apr_thread_mutex_unlock(cachep->lock);
+#endif
+    }
+
+    return value;
+}
+
 static char *lookup_map_dbmfile(request_rec *r, const char *file,
                                 const char *dbmtype, char *key)
 {
@@ -1605,6 +1741,46 @@
         return *value ? value : NULL;
 
     /*
+     * Regexp file map
+     */
+    case MAPTYPE_REGEXP:
+        rv = apr_stat(&st, s->checkfile, APR_FINFO_MIN, r->pool);
+        if (rv != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                          "mod_rewrite: can't access text RewriteMap file %s",
+                          s->checkfile);
+            rewritelog((r, 1, NULL,
+                        "can't open RewriteMap file, see error log"));
+            return NULL;
+        }
+
+        value = get_cache_value(s->cachename, st.mtime, key, r->pool);
+        if (!value) {
+            char *pattern = NULL;
+
+            rewritelog((r, 6, NULL,
+                        "cache lookup FAILED, forcing new map lookup"));
+
+            value = lookup_map_regexp(r, s, st.mtime, key, &pattern);
+            if (!value) {
+                rewritelog((r, 5, NULL, "map lookup FAILED: map=%s[regexp] key=%s",
+                            name, key));
+                set_cache_value(s->cachename, st.mtime, key, "");
+                return NULL;
+            }
+
+            rewritelog((r, 5, NULL,"map lookup OK: map=%s[regexp] key=%s[pattern=%s] -> val=%s",
+                        name, key, pattern, value));
+            set_cache_value(s->cachename, st.mtime, key, value);
+        }
+        else {
+            rewritelog((r,5,NULL,"cache lookup OK: map=%s[regexp] key=%s -> val=%s",
+                        name, key, value));
+        }
+
+        return *value ? value : NULL;
+
+    /*
      * DBM file map
      */
     case MAPTYPE_DBM:
@@ -3001,6 +3177,19 @@
         newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
                                          (void *)cmd->server, a1);
     }
+    else if (strncasecmp(a2, "regexp:", 7) == 0) {
+        if ((fname = ap_server_root_relative(cmd->pool, a2+7)) == NULL) {
+            return apr_pstrcat(cmd->pool, "RewriteMap: bad path to regexp map: ",
+                               a2+7, NULL);
+        }
+
+        newmap->type      = MAPTYPE_REGEXP;
+        newmap->datafile  = fname;
+        newmap->checkfile = fname;
+        newmap->checkfile2= NULL;
+        newmap->cachename = apr_psprintf(cmd->pool, "%pp:%s",
+                                         (void *)cmd->server, a1);
+    }
     else if (strncasecmp(a2, "rnd:", 4) == 0) {
         if ((fname = ap_server_root_relative(cmd->pool, a2+4)) == NULL) {
             return apr_pstrcat(cmd->pool, "RewriteMap: bad path to rnd map: ",
