]> granicus.if.org Git - apache/blobdiff - server/util.c
Wildcard name-based vhosts printed twice in apachectl -S
[apache] / server / util.c
index 4c4f68e054ba7d8e69a4f2e221b611412221bff4..e0ba5c28a39a5c33cdfd9f4763ac47b4cf77aabd 100644 (file)
@@ -1,67 +1,25 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
- * reserved.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" must
- *    not be used to endorse or promote products derived from this
- *    software without prior written permission. For written
- *    permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without prior written
- *    permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- * Portions of this software are based upon public domain software
- * originally written at the National Center for Supercomputing Applications,
- * University of Illinois, Urbana-Champaign.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 /*
  * util.c: string utility things
- * 
+ *
  * 3/21/93 Rob McCool
  * 1995-96 Many changes by the Apache Software Foundation
- * 
+ *
  */
 
 /* Debugging aid:
 #if APR_HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#if APR_HAVE_PROCESS_H
+#include <process.h>            /* for getpid() on Win32 */
+#endif
 #if APR_HAVE_NETDB_H
 #include <netdb.h>              /* for gethostbyname() */
 #endif
 
-#define CORE_PRIVATE
-
 #include "ap_config.h"
 #include "apr_base64.h"
 #include "httpd.h"
@@ -93,7 +52,9 @@
 #include "http_log.h"
 #include "http_protocol.h"
 #include "http_config.h"
+#include "http_core.h"
 #include "util_ebcdic.h"
+#include "util_varbuf.h"
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #ifdef HAVE_GRP_H
 #include <grp.h>
 #endif
+#ifdef HAVE_SYS_LOADAVG_H
+#include <sys/loadavg.h>
+#endif
+
+#include "ap_mpm.h"
 
 /* A bunch of functions in util.c scan strings looking for certain characters.
  * To make that more efficient we encode a lookup table.  The test_char_table
  */
 #ifdef CASE_BLIND_FILESYSTEM
 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
+#define SLASHES "/\\"
 #else
 #define IS_SLASH(s) (s == '/')
+#define SLASHES "/"
 #endif
 
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
 
 /*
  * Examine a field value (such as a media-/content-type) string and return
@@ -138,7 +110,7 @@ AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
     semi = ap_strchr_c(intype, ';');
     if (semi == NULL) {
         return apr_pstrdup(p, intype);
-    } 
+    }
     else {
         while ((semi > intype) && apr_isspace(semi[-1])) {
             semi--;
@@ -213,61 +185,61 @@ AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
  * Based loosely on sections of wildmat.c by Rich Salz
  * Hmmm... shouldn't this really go component by component?
  */
-AP_DECLARE(int) ap_strcmp_match(const char *str, const char *exp)
+AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
 {
     int x, y;
 
-    for (x = 0, y = 0; exp[y]; ++y, ++x) {
-        if ((!str[x]) && (exp[y] != '*'))
+    for (x = 0, y = 0; expected[y]; ++y, ++x) {
+        if ((!str[x]) && (expected[y] != '*'))
             return -1;
-        if (exp[y] == '*') {
-            while (exp[++y] == '*');
-            if (!exp[y])
+        if (expected[y] == '*') {
+            while (expected[++y] == '*');
+            if (!expected[y])
                 return 0;
             while (str[x]) {
                 int ret;
-                if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1)
+                if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
                     return ret;
             }
             return -1;
         }
-        else if ((exp[y] != '?') && (str[x] != exp[y]))
+        else if ((expected[y] != '?') && (str[x] != expected[y]))
             return 1;
     }
     return (str[x] != '\0');
 }
 
-AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *exp)
+AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
 {
     int x, y;
 
-    for (x = 0, y = 0; exp[y]; ++y, ++x) {
-        if (!str[x] && exp[y] != '*')
+    for (x = 0, y = 0; expected[y]; ++y, ++x) {
+        if (!str[x] && expected[y] != '*')
             return -1;
-        if (exp[y] == '*') {
-            while (exp[++y] == '*');
-            if (!exp[y])
+        if (expected[y] == '*') {
+            while (expected[++y] == '*');
+            if (!expected[y])
                 return 0;
             while (str[x]) {
                 int ret;
-                if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1)
+                if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
                     return ret;
             }
             return -1;
         }
-        else if (exp[y] != '?'
-                 && apr_tolower(str[x]) != apr_tolower(exp[y]))
+        else if (expected[y] != '?'
+                 && apr_tolower(str[x]) != apr_tolower(expected[y]))
             return 1;
     }
     return (str[x] != '\0');
 }
 
 /* We actually compare the canonical root to this root, (but we don't
- * waste time checking the case), since every use of this function in 
- * httpd-2.0 tests if the path is 'proper', meaning we've already passed
+ * waste time checking the case), since every use of this function in
+ * httpd-2.1 tests if the path is 'proper', meaning we've already passed
  * it through apr_filepath_merge, or we haven't.
  */
-AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir) 
+AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
 {
     const char *newpath;
     const char *ourdir = dir;
@@ -289,36 +261,39 @@ AP_DECLARE(int) ap_is_matchexp(const char *str)
 }
 
 /*
- * Here's a pool-based interface to POSIX regex's regcomp().
- * Note that we return regex_t instead of being passed one.
- * The reason is that if you use an already-used regex_t structure,
+ * Here's a pool-based interface to the POSIX-esque ap_regcomp().
+ * Note that we return ap_regex_t instead of being passed one.
+ * The reason is that if you use an already-used ap_regex_t structure,
  * the memory that you've already allocated gets forgotten, and
  * regfree() doesn't clear it. So we don't allow it.
  */
 
 static apr_status_t regex_cleanup(void *preg)
 {
-    regfree((regex_t *) preg);
+    ap_regfree((ap_regex_t *) preg);
     return APR_SUCCESS;
 }
 
-AP_DECLARE(regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
-                                   int cflags)
+AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
+                                     int cflags)
 {
-    regex_t *preg = apr_palloc(p, sizeof(regex_t));
-
-    if (regcomp(preg, pattern, cflags)) {
+    ap_regex_t *preg = apr_palloc(p, sizeof *preg);
+    int err = ap_regcomp(preg, pattern, cflags);
+    if (err) {
+        if (err == AP_REG_ESPACE)
+            ap_abort_on_oom();
         return NULL;
     }
 
-    apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, regex_cleanup);
+    apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
+                              apr_pool_cleanup_null);
 
     return preg;
 }
 
-AP_DECLARE(void) ap_pregfree(apr_pool_t *p, regex_t * reg)
+AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
 {
-    regfree(reg);
+    ap_regfree(reg);
     apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
 }
 
@@ -385,93 +360,97 @@ AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
     return bigstring;
 }
 
-/* 
- * Apache stub function for the regex libraries regexec() to make sure the
- * whole regex(3) API is available through the Apache (exported) namespace.
- * This is especially important for the DSO situations of modules.
- * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
- */
-AP_DECLARE(int) ap_regexec(regex_t *preg, const char *string,
-                           size_t nmatch, regmatch_t pmatch[], int eflags)
-{
-    return regexec(preg, string, nmatch, pmatch, eflags);
-}
-
-AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf,
-                               size_t errbuf_size)
-{
-    return regerror(errcode, preg, errbuf, errbuf_size);
-}
-
-
 /* This function substitutes for $0-$9, filling in regular expression
  * submatches. Pass it the same nmatch and pmatch arguments that you
  * passed ap_regexec(). pmatch should not be greater than the maximum number
- * of subexpressions - i.e. one more than the re_nsub member of regex_t.
+ * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
+ *
+ * nmatch must be <=AP_MAX_REG_MATCH (10).
  *
  * input should be the string with the $-expressions, source should be the
  * string that was matched against.
  *
- * It returns the substituted string, or NULL on error.
+ * It returns the substituted string, or NULL if a vbuf is used.
+ * On errors, returns the orig string.
  *
  * Parts of this code are based on Henry Spencer's regsub(), from his
  * AT&T V8 regexp package.
  */
 
-AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
-                              const char *source, size_t nmatch,
-                              regmatch_t pmatch[])
+static apr_status_t regsub_core(apr_pool_t *p, char **result,
+                                struct ap_varbuf *vb, const char *input,
+                                const char *source, apr_size_t nmatch,
+                                ap_regmatch_t pmatch[], apr_size_t maxlen)
 {
     const char *src = input;
-    char *dest, *dst;
+    char *dst;
     char c;
-    size_t no;
-    int len;
-
-    if (!source)
-        return NULL;
-    if (!nmatch)
-        return apr_pstrdup(p, src);
+    apr_size_t no;
+    apr_size_t len = 0;
+
+    AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result));
+    if (!source || nmatch>AP_MAX_REG_MATCH)
+        return APR_EINVAL;
+    if (!nmatch) {
+        len = strlen(src);
+        if (maxlen > 0 && len >= maxlen)
+            return APR_ENOMEM;
+        if (!vb) {
+            *result = apr_pstrmemdup(p, src, len);
+            return APR_SUCCESS;
+        }
+        else {
+            ap_varbuf_strmemcat(vb, src, len);
+            return APR_SUCCESS;
+        }
+    }
 
     /* First pass, find the size */
-
-    len = 0;
-
     while ((c = *src++) != '\0') {
-        if (c == '&')
-            no = 0;
-        else if (c == '$' && apr_isdigit(*src))
+        if (c == '$' && apr_isdigit(*src))
             no = *src++ - '0';
         else
-            no = 10;
+            no = AP_MAX_REG_MATCH;
 
-        if (no > 9) {                /* Ordinary character. */
-            if (c == '\\' && (*src == '$' || *src == '&'))
-                c = *src++;
+        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
+            if (c == '\\' && *src)
+                src++;
             len++;
         }
         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so)
+                return APR_ENOMEM;
             len += pmatch[no].rm_eo - pmatch[no].rm_so;
         }
 
     }
 
-    dest = dst = apr_pcalloc(p, len + 1);
+    if (len >= maxlen && maxlen > 0)
+        return APR_ENOMEM;
+
+    if (!vb) {
+        *result = dst = apr_palloc(p, len + 1);
+    }
+    else {
+        if (vb->strlen == AP_VARBUF_UNKNOWN)
+            vb->strlen = strlen(vb->buf);
+        ap_varbuf_grow(vb, vb->strlen + len);
+        dst = vb->buf + vb->strlen;
+        vb->strlen += len;
+    }
 
     /* Now actually fill in the string */
 
     src = input;
 
     while ((c = *src++) != '\0') {
-        if (c == '&')
-            no = 0;
-        else if (c == '$' && apr_isdigit(*src))
+        if (c == '$' && apr_isdigit(*src))
             no = *src++ - '0';
         else
-            no = 10;
+            no = AP_MAX_REG_MATCH;
 
-        if (no > 9) {                /* Ordinary character. */
-            if (c == '\\' && (*src == '$' || *src == '&'))
+        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
+            if (c == '\\' && *src)
                 c = *src++;
             *dst++ = c;
         }
@@ -484,7 +463,34 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
     }
     *dst = '\0';
 
-    return dest;
+    return APR_SUCCESS;
+}
+
+#ifndef AP_PREGSUB_MAXLEN
+#define AP_PREGSUB_MAXLEN   (HUGE_STRING_LEN * 8)
+#endif
+AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
+                              const char *source, apr_size_t nmatch,
+                              ap_regmatch_t pmatch[])
+{
+    char *result;
+    apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch,
+                                  pmatch, AP_PREGSUB_MAXLEN);
+    if (rc != APR_SUCCESS)
+        result = NULL;
+    return result;
+}
+
+AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
+                                       const char *input, const char *source,
+                                       apr_size_t nmatch, ap_regmatch_t pmatch[],
+                                       apr_size_t maxlen)
+{
+    apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch,
+                                  pmatch, maxlen);
+    if (rc != APR_SUCCESS)
+        *result = NULL;
+    return rc;
 }
 
 /*
@@ -499,7 +505,7 @@ AP_DECLARE(void) ap_getparents(char *name)
     /* a) remove ./ path segments */
     for (next = name; *next && (*next != '.'); next++) {
     }
-    
+
     l = w = first_dot = next - name;
     while (name[l] != '\0') {
         if (name[l] == '.' && IS_SLASH(name[l + 1])
@@ -589,8 +595,8 @@ AP_DECLARE(void) ap_no2slash(char *name)
  * assumes n > 0
  * the return value is the ever useful pointer to the trailing \0 of d
  *
- * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments, 
- * so that if n == 0, "/" is returned in d with n == 1 
+ * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
+ * so that if n == 0, "/" is returned in d with n == 1
  * and s == "e:/test.html", "e:/" is returned in d
  * *** See also directory_walk in modules/http/http_request.c
 
@@ -640,9 +646,8 @@ AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
         return apr_pstrdup(p, "");
     }
     l = (last_slash - s) + 1;
-    d = apr_palloc(p, l + 1);
-    memcpy(d, s, l);
-    d[l] = 0;
+    d = apr_pstrmemdup(p, s, l);
+
     return (d);
 }
 
@@ -673,9 +678,7 @@ AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
     }
 
     len = pos - *line;
-    res = (char *)apr_palloc(atrans, len + 1);
-    memcpy(res, *line, len);
-    res[len] = 0;
+    res = apr_pstrmemdup(atrans, *line, len);
 
     if (stop) {
         while (*pos == stop) {
@@ -703,9 +706,7 @@ AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
     }
 
     len = pos - *line;
-    res = (char *)apr_palloc(atrans, len + 1);
-    memcpy(res, *line, len);
-    res[len] = 0;
+    res = apr_pstrmemdup(atrans, *line, len);
 
     while (apr_isspace(*pos)) {
         ++pos;
@@ -729,8 +730,9 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
     char *res;
 
     if (!pos) {
-        res = apr_pstrdup(atrans, *line);
-        *line += strlen(*line);
+        apr_size_t len = strlen(*line);
+        res = apr_pstrmemdup(atrans, *line, len);
+        *line += len;
         return res;
     }
 
@@ -750,7 +752,7 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
 static char *substring_conf(apr_pool_t *p, const char *start, int len,
                             char quote)
 {
-    char *result = apr_palloc(p, len + 2);
+    char *result = apr_palloc(p, len + 1);
     char *resp = result;
     int i;
 
@@ -764,7 +766,7 @@ static char *substring_conf(apr_pool_t *p, const char *start, int len,
 
     *resp++ = '\0';
 #if RESOLVE_ENV_PER_TOKEN
-    return ap_resolve_env(p,result);
+    return (char *)ap_resolve_env(p,result);
 #else
     return result;
 #endif
@@ -781,7 +783,7 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     char *res;
     char quote;
 
-    while (*str && apr_isspace(*str))
+    while (apr_isspace(*str))
         ++str;
 
     if (!*str) {
@@ -792,10 +794,13 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     if ((quote = *str) == '"' || quote == '\'') {
         strend = str + 1;
         while (*strend && *strend != quote) {
-            if (*strend == '\\' && strend[1] && strend[1] == quote)
+            if (*strend == '\\' && strend[1] &&
+                (strend[1] == quote || strend[1] == '\\')) {
                 strend += 2;
-            else
+            }
+            else {
                 ++strend;
+            }
         }
         res = substring_conf(p, str + 1, strend - str - 1, quote);
 
@@ -810,83 +815,35 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
         res = substring_conf(p, str, strend - str, 0);
     }
 
-    while (*strend && apr_isspace(*strend))
+    while (apr_isspace(*strend))
         ++strend;
     *line = strend;
     return res;
 }
 
-/* Check a string for any ${ENV} environment variable
- * construct and replace each them by the value of
- * that environment variable, if it exists. If the
- * environment value does not exist, leave the ${ENV}
- * construct alone; it means something else.
- */
-AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
-{
-       char tmp[ MAX_STRING_LEN ];
-       const char *s, *e;
-       tmp[0] = '\0';
-
-       if (!(s=ap_strchr_c(word,'$')))
-               return word;
-
-       do {
-               /* XXX - relies on strncat() to add '\0'
-                */
-               strncat(tmp,word,s - word);
-               if ((s[1] == '{') && (e=ap_strchr_c(s,'}'))) {
-                       const char *e2 = e;
-                       word = e + 1;
-                       e = getenv(s+2);
-                       if (e) {
-                           strcat(tmp,e);
-                       } else {
-                           strncat(tmp, s, e2-s);
-                           strcat(tmp,"}");
-                       }
-               } else {
-                       /* ignore invalid strings */
-                       word = s+1;
-                       strcat(tmp,"$");
-               };
-       } while ((s=ap_strchr_c(word,'$')));
-       strcat(tmp,word);
-
-       return apr_pstrdup(p,tmp);
-}
 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
 {
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00551)
         "Done with config file %s", cfp->name);
 #endif
     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
 }
 
+/* we can't use apr_file_* directly because of linking issues on Windows */
 static apr_status_t cfg_close(void *param)
 {
-    apr_file_t *cfp = (apr_file_t *) param;
-    return (apr_file_close(cfp));
+    return apr_file_close(param);
 }
 
-static int cfg_getch(void *param)
+static apr_status_t cfg_getch(char *ch, void *param)
 {
-    char ch;
-    apr_file_t *cfp = (apr_file_t *) param;
-    if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
-        return ch;
-    return (int)EOF;
+    return apr_file_getc(ch, param);
 }
 
-static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
 {
-    apr_file_t *cfp = (apr_file_t *) param;
-    apr_status_t rv;
-    rv = apr_file_gets(buf, bufsiz, cfp);
-    if (rv == APR_SUCCESS || (rv == APR_EOF && strcmp(buf, "")))
-        return buf;
-    return NULL;
+    return apr_file_gets(buf, bufsiz, param);
 }
 
 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
@@ -902,7 +859,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 #endif
 
     if (name == NULL) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
                "Internal error: pcfg_openfile() called with NULL filename");
         return APR_EBADF;
     }
@@ -910,9 +867,9 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
     status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
                            APR_OS_DEFAULT, p);
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
                 "Opening config file %s (%s)",
-                name, (status != APR_SUCCESS) ? 
+                name, (status != APR_SUCCESS) ?
                 apr_strerror(status, buf, sizeof(buf)) : "successful");
 #endif
     if (status != APR_SUCCESS)
@@ -924,11 +881,11 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 
     if (finfo.filetype != APR_REG &&
 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
-        strcasecmp(apr_filename_of_pathname(name), "nul") != 0) {
+        strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
 #else
         strcmp(name, "/dev/null") != 0) {
 #endif /* WIN32 || OS2 */
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
                      "Access to file %s denied by server: not a regular file",
                      name);
         apr_file_close(file);
@@ -943,14 +900,14 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
      * would signify utf-8 text files.
      *
      * Since MS configuration files are all protecting utf-8 encoded
-     * Unicode path, file and resource names, we already have the correct 
+     * Unicode path, file and resource names, we already have the correct
      * WinNT encoding.  But at least eat the stupid three bytes up front.
      */
     {
         unsigned char buf[4];
         apr_size_t len = 3;
         status = apr_file_read(file, buf, &len);
-        if ((status != APR_SUCCESS) || (len < 3) 
+        if ((status != APR_SUCCESS) || (len < 3)
               || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
             apr_off_t zero = 0;
             apr_file_seek(file, APR_SET, &zero);
@@ -961,9 +918,9 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
     new_cfg = apr_palloc(p, sizeof(*new_cfg));
     new_cfg->param = file;
     new_cfg->name = apr_pstrdup(p, name);
-    new_cfg->getch = (int (*)(void *)) cfg_getch;
-    new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
-    new_cfg->close = (int (*)(void *)) cfg_close;
+    new_cfg->getch = cfg_getch;
+    new_cfg->getstr = cfg_getstr;
+    new_cfg->close = cfg_close;
     new_cfg->line_number = 0;
     *ret_cfg = new_cfg;
     return APR_SUCCESS;
@@ -971,170 +928,212 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 
 
 /* Allocate a ap_configfile_t handle with user defined functions and params */
-AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
-                       const char *descr,
-                       void *param,
-                       int(*getch)(void *param),
-                       void *(*getstr) (void *buf, size_t bufsiz, void *param),
-                       int(*close_func)(void *param))
+AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
+            apr_pool_t *p, const char *descr, void *param,
+            apr_status_t (*getc_func) (char *ch, void *param),
+            apr_status_t (*gets_func) (void *buf, apr_size_t bufsize, void *param),
+            apr_status_t (*close_func) (void *param))
 {
     ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
-#ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
-                 "Opening config handler %s", descr);
-#endif
     new_cfg->param = param;
     new_cfg->name = descr;
-    new_cfg->getch = getch;
-    new_cfg->getstr = getstr;
+    new_cfg->getch = getc_func;
+    new_cfg->getstr = gets_func;
     new_cfg->close = close_func;
     new_cfg->line_number = 0;
     return new_cfg;
 }
-   
+
 /* Read one character from a configfile_t */
-AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
+AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
 {
-    register int ch = cfp->getch(cfp->param);
-    if (ch == LF) 
+    apr_status_t rc = cfp->getch(ch, cfp->param);
+    if (rc == APR_SUCCESS && *ch == LF)
         ++cfp->line_number;
-    return ch;
+    return rc;
 }
-  
+
+AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
+                                          apr_status_t rc)
+{
+    char buf[MAX_STRING_LEN];
+    if (rc == APR_SUCCESS)
+        return NULL;
+    return apr_psprintf(p, "Error reading %s at line %d: %s",
+                        cfp->name, cfp->line_number,
+                        rc == APR_ENOSPC ? "Line too long"
+                                         : apr_strerror(rc, buf, sizeof(buf)));
+}
+
 /* Read one line from open ap_configfile_t, strip LF, increase line number */
 /* If custom handler does not define a getstr() function, read char by char */
-AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
+static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize,
+                                        ap_configfile_t *cfp)
 {
+    apr_status_t rc;
     /* If a "get string" function is defined, use it */
     if (cfp->getstr != NULL) {
-        char *src, *dst;
         char *cp;
         char *cbuf = buf;
-        size_t cbufsize = bufsize;
+        apr_size_t cbufsize = bufsize;
 
         while (1) {
             ++cfp->line_number;
-            if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
-                return 1;
+            rc = cfp->getstr(cbuf, cbufsize, cfp->param);
+            if (rc == APR_EOF) {
+                if (cbuf != buf) {
+                    *cbuf = '\0';
+                    break;
+                }
+                else {
+                    return APR_EOF;
+                }
+            }
+            if (rc != APR_SUCCESS) {
+                return rc;
+            }
 
             /*
              *  check for line continuation,
              *  i.e. match [^\\]\\[\r]\n only
              */
             cp = cbuf;
-            while (cp < cbuf+cbufsize && *cp != '\0')
-                cp++;
+            cp += strlen(cp);
             if (cp > cbuf && cp[-1] == LF) {
                 cp--;
                 if (cp > cbuf && cp[-1] == CR)
                     cp--;
                 if (cp > cbuf && cp[-1] == '\\') {
                     cp--;
-                    if (!(cp > cbuf && cp[-1] == '\\')) {
-                        /*
-                         * line continuation requested -
-                         * then remove backslash and continue
-                         */
-                        cbufsize -= (cp-cbuf);
-                        cbuf = cp;
-                        continue;
-                    }
-                    else {
-                        /* 
-                         * no real continuation because escaped -
-                         * then just remove escape character
-                         */
-                        for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
-                            cp[0] = cp[1];
-                    }   
+                    /*
+                     * line continuation requested -
+                     * then remove backslash and continue
+                     */
+                    cbufsize -= (cp-cbuf);
+                    cbuf = cp;
+                    continue;
                 }
             }
+            else if (cp - buf >= bufsize - 1) {
+                return APR_ENOSPC;
+            }
             break;
         }
-
-        /*
-         * Leading and trailing white space is eliminated completely
-         */
-        src = buf;
-        while (apr_isspace(*src))
-            ++src;
-        /* blast trailing whitespace */
-        dst = &src[strlen(src)];
-        while (--dst >= src && apr_isspace(*dst))
-            *dst = '\0';
-        /* Zap leading whitespace by shifting */
-        if (src != buf)
-            for (dst = buf; (*dst++ = *src++) != '\0'; )
-                ;
-
-#ifdef DEBUG_CFG_LINES
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
-#endif
-        return 0;
     } else {
         /* No "get string" function defined; read character by character */
-        register int c;
-        register size_t i = 0;
+        apr_size_t i = 0;
 
-        buf[0] = '\0';
-        /* skip leading whitespace */
-        do {
-            c = cfp->getch(cfp->param);
-        } while (c == '\t' || c == ' ');
-
-        if (c == EOF)
-            return 1;
-        
-        if(bufsize < 2) {
+        if (bufsize < 2) {
             /* too small, assume caller is crazy */
-            return 1;
+            return APR_EINVAL;
         }
+        buf[0] = '\0';
 
         while (1) {
-            if ((c == '\t') || (c == ' ')) {
-                buf[i++] = ' ';
-                while ((c == '\t') || (c == ' '))
-                    c = cfp->getch(cfp->param);
-            }
-            if (c == CR) {
-                /* silently ignore CR (_assume_ that a LF follows) */
-                c = cfp->getch(cfp->param);
+            char c;
+            rc = cfp->getch(&c, cfp->param);
+            if (rc == APR_EOF) {
+                if (i > 0)
+                    break;
+                else
+                    return APR_EOF;
             }
+            if (rc != APR_SUCCESS)
+                return rc;
             if (c == LF) {
-                /* increase line number and return on LF */
                 ++cfp->line_number;
-            }
-            if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
-                /* 
-                 *  check for line continuation
-                 */
+                /* check for line continuation */
                 if (i > 0 && buf[i-1] == '\\') {
                     i--;
-                    if (!(i > 0 && buf[i-1] == '\\')) {
-                        /* line is continued */
-                        c = cfp->getch(cfp->param);
-                        continue;
-                    }
-                    /* else nothing needs be done because
-                     * then the backslash is escaped and
-                     * we just strip to a single one
-                     */
+                    continue;
+                }
+                else {
+                    break;
                 }
-                /* blast trailing whitespace */
-                while (i > 0 && apr_isspace(buf[i - 1]))
-                    --i;
-                buf[i] = '\0';
-#ifdef DEBUG_CFG_LINES
-                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
-                             "Read config: %s", buf);
-#endif
-                return 0;
+            }
+            else if (i >= bufsize - 2) {
+                return APR_ENOSPC;
             }
             buf[i] = c;
             ++i;
-            c = cfp->getch(cfp->param);
         }
+        buf[i] = '\0';
     }
+    return APR_SUCCESS;
+}
+
+static int cfg_trim_line(char *buf)
+{
+    char *start, *end;
+    /*
+     * Leading and trailing white space is eliminated completely
+     */
+    start = buf;
+    while (apr_isspace(*start))
+        ++start;
+    /* blast trailing whitespace */
+    end = &start[strlen(start)];
+    while (--end >= start && apr_isspace(*end))
+        *end = '\0';
+    /* Zap leading whitespace by shifting */
+    if (start != buf)
+        memmove(buf, start, end - start + 2);
+#ifdef DEBUG_CFG_LINES
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
+#endif
+    return end - start + 1;
+}
+
+/* Read one line from open ap_configfile_t, strip LF, increase line number */
+/* If custom handler does not define a getstr() function, read char by char */
+AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize,
+                                        ap_configfile_t *cfp)
+{
+    apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp);
+    if (rc == APR_SUCCESS)
+        cfg_trim_line(buf);
+    return rc;
+}
+
+AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
+                                               ap_configfile_t *cfp,
+                                               apr_size_t max_len)
+{
+    apr_status_t rc;
+    apr_size_t new_len;
+    vb->strlen = 0;
+    *vb->buf = '\0';
+
+    if (vb->strlen == AP_VARBUF_UNKNOWN)
+        vb->strlen = strlen(vb->buf);
+    if (vb->avail - vb->strlen < 3) {
+        new_len = vb->avail * 2;
+        if (new_len > max_len)
+            new_len = max_len;
+        else if (new_len < 3)
+            new_len = 3;
+        ap_varbuf_grow(vb, new_len);
+    }
+
+    for (;;) {
+        rc = ap_cfg_getline_core(vb->buf + vb->strlen, vb->avail - vb->strlen, cfp);
+        if (rc == APR_ENOSPC || rc == APR_SUCCESS)
+            vb->strlen += strlen(vb->buf + vb->strlen);
+        if (rc != APR_ENOSPC)
+            break;
+        if (vb->avail >= max_len)
+            return APR_ENOSPC;
+        new_len = vb->avail * 2;
+        if (new_len > max_len)
+            new_len = max_len;
+        ap_varbuf_grow(vb, new_len);
+        --cfp->line_number;
+    }
+    if (vb->strlen > max_len)
+        return APR_ENOSPC;
+    if (rc == APR_SUCCESS)
+        vb->strlen = cfg_trim_line(vb->buf);
+    return rc;
 }
 
 /* Size an HTTP header field list item, as separated by a comma.
@@ -1287,27 +1286,58 @@ AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
     return token;
 }
 
+typedef enum ap_etag_e {
+    AP_ETAG_NONE,
+    AP_ETAG_WEAK,
+    AP_ETAG_STRONG
+} ap_etag_e;
+
 /* Find an item in canonical form (lowercase, no extra spaces) within
  * an HTTP field value list.  Returns 1 if found, 0 if not found.
  * This would be much more efficient if we stored header fields as
  * an array of list items as they are received instead of a plain string.
  */
-AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
-                                  const char *tok)
+static int find_list_item(apr_pool_t *p, const char *line,
+                                  const char *tok, ap_etag_e type)
 {
     const unsigned char *pos;
     const unsigned char *ptr = (const unsigned char *)line;
     int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
 
-    if (!line || !tok)
+    if (!line || !tok) {
+        return 0;
+    }
+    if (type == AP_ETAG_STRONG && *tok != '\"') {
         return 0;
+    }
+    if (type == AP_ETAG_WEAK) {
+        if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') {
+            tok += 2;
+        }
+        else if (*tok != '\"') {
+            return 0;
+        }
+    }
 
     do {  /* loop for each item in line's list */
 
         /* Find first non-comma, non-whitespace byte */
-
-        while (*ptr == ',' || apr_isspace(*ptr))
+        while (*ptr == ',' || apr_isspace(*ptr)) {
             ++ptr;
+        }
+
+        /* Account for strong or weak Etags, depending on our search */
+        if (type == AP_ETAG_STRONG && *ptr != '\"') {
+            break;
+        }
+        if (type == AP_ETAG_WEAK) {
+            if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
+                ptr += 2;
+            }
+            else if (*ptr != '\"') {
+                break;
+            }
+        }
 
         if (*ptr)
             good = 1;  /* until proven otherwise for this item */
@@ -1375,7 +1405,8 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
                                if (in_com || in_qstr)
                                    good = good && (*pos++ == *ptr);
                                else
-                                   good = good && (*pos++ == apr_tolower(*ptr));
+                                   good = good
+                                       && (apr_tolower(*pos++) == apr_tolower(*ptr));
                                addspace = 0;
                                break;
                 }
@@ -1389,6 +1420,34 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
     return good;
 }
 
+/* Find an item in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ * This would be much more efficient if we stored header fields as
+ * an array of list items as they are received instead of a plain string.
+ */
+AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
+                                  const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_NONE);
+}
+
+/* Find a strong Etag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line,
+                                    const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_STRONG);
+}
+
+/* Find a weak ETag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line,
+                                  const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_WEAK);
+}
 
 /* Retrieve a token, spacing over it and returning a pointer to
  * the first non-white byte afterwards.  Note that these tokens
@@ -1406,7 +1465,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
 
     /* Find first non-white byte */
 
-    while (*ptr && apr_isspace(*ptr))
+    while (apr_isspace(*ptr))
         ++ptr;
 
     tok_start = ptr;
@@ -1428,7 +1487,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
 
     /* Advance accept_line pointer to the next non-white byte */
 
-    while (*ptr && apr_isspace(*ptr))
+    while (apr_isspace(*ptr))
         ++ptr;
 
     *accept_line = ptr;
@@ -1503,7 +1562,7 @@ AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
     for (; *s; ++s) {
 
 #if defined(OS2) || defined(WIN32)
-        /* 
+        /*
          * Newlines to Win32/OS2 CreateProcess() are ill advised.
          * Convert them to spaces since they are effectively white
          * space to most applications
@@ -1548,16 +1607,15 @@ static char x2c(const char *what)
 }
 
 /*
- * Unescapes a URL.
+ * Unescapes a URL, leaving reserved characters intact.
  * Returns 0 on success, non-zero on error
  * Failure is due to
  *   bad % escape       returns HTTP_BAD_REQUEST
  *
- *   decoding %00 -> \0  (the null character)
- *   decoding %2f -> /   (a special character)
- *                      returns HTTP_NOT_FOUND
+ *   decoding %00 or a forbidden character returns HTTP_NOT_FOUND
  */
-AP_DECLARE(int) ap_unescape_url(char *url)
+
+static int unescape_url(char *url, const char *forbid, const char *reserved)
 {
     register int badesc, badpath;
     char *x, *y;
@@ -1571,28 +1629,94 @@ AP_DECLARE(int) ap_unescape_url(char *url)
         return OK;
     }
     for (x = y; *y; ++x, ++y) {
-        if (*y != '%')
+        if (*y != '%') {
             *x = *y;
+        }
         else {
             if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
                 badesc = 1;
                 *x = '%';
             }
             else {
-                *x = x2c(y + 1);
-                y += 2;
-                if (IS_SLASH(*x) || *x == '\0')
+                char decoded;
+                decoded = x2c(y + 1);
+                if ((decoded == '\0')
+                    || (forbid && ap_strchr_c(forbid, decoded))) {
                     badpath = 1;
+                    *x = decoded;
+                    y += 2;
+                }
+                else if (reserved && ap_strchr_c(reserved, decoded)) {
+                    *x++ = *y++;
+                    *x++ = *y++;
+                    *x = *y;
+                }
+                else {
+                    *x = decoded;
+                    y += 2;
+                }
             }
         }
     }
     *x = '\0';
-    if (badesc)
+    if (badesc) {
         return HTTP_BAD_REQUEST;
-    else if (badpath)
+    }
+    else if (badpath) {
         return HTTP_NOT_FOUND;
-    else
+    }
+    else {
         return OK;
+    }
+}
+AP_DECLARE(int) ap_unescape_url(char *url)
+{
+    /* Traditional */
+    return unescape_url(url, SLASHES, NULL);
+}
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
+{
+    /* AllowEncodedSlashes (corrected) */
+    if (decode_slashes) {
+        /* no chars reserved */
+        return unescape_url(url, NULL, NULL);
+    } else {
+        /* reserve (do not decode) encoded slashes */
+        return unescape_url(url, NULL, SLASHES);
+    }
+}
+#ifdef NEW_APIS
+/* IFDEF these out until they've been thought through.
+ * Just a germ of an API extension for now
+ */
+AP_DECLARE(int) ap_unescape_url_proxy(char *url)
+{
+    /* leave RFC1738 reserved characters intact, * so proxied URLs
+     * don't get mangled.  Where does that leave encoded '&' ?
+     */
+    return unescape_url(url, NULL, "/;?");
+}
+AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
+{
+    return unescape_url(url, NULL, reserved);
+}
+#endif
+
+AP_DECLARE(int) ap_unescape_urlencoded(char *query)
+{
+    char *slider;
+
+    /* replace plus with a space */
+    if (query) {
+        for (slider = query; *slider; slider++) {
+            if (*slider == '+') {
+                *slider = ' ';
+            }
+        }
+    }
+
+    /* unescape everything else */
+    return unescape_url(query, NULL, NULL);
 }
 
 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
@@ -1606,6 +1730,11 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
     }
 }
 
+AP_DECLARE(int) ap_unescape_all(char *url)
+{
+    return unescape_url(url, NULL, NULL);
+}
+
 /* c2x takes an unsigned, and expects the caller has guaranteed that
  * 0 <= what < 256... which usually means that you have to cast to
  * unsigned char first, because (unsigned)(char)(x) first goes through
@@ -1618,12 +1747,13 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
  */
 static const char c2x_table[] = "0123456789abcdef";
 
-static APR_INLINE unsigned char *c2x(unsigned what, unsigned char *where)
+static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
+                                     unsigned char *where)
 {
 #if APR_CHARSET_EBCDIC
     what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
 #endif /*APR_CHARSET_EBCDIC*/
-    *where++ = '%';
+    *where++ = prefix;
     *where++ = c2x_table[what >> 4];
     *where++ = c2x_table[what & 0xf];
     return where;
@@ -1644,16 +1774,15 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char *where)
  * something with a '/' in it (and thus does not prefix "./").
  */
 
-AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
 {
-    char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
     const unsigned char *s = (const unsigned char *)segment;
     unsigned char *d = (unsigned char *)copy;
     unsigned c;
 
     while ((c = *s)) {
         if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
-            d = c2x(c, d);
+            d = c2x(c, '%', d);
         }
         else {
             *d++ = c;
@@ -1664,6 +1793,11 @@ AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
     return copy;
 }
 
+AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+{
+    return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
+}
+
 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
 {
     char *copy = apr_palloc(p, 3 * strlen(path) + 3);
@@ -1682,7 +1816,29 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
     }
     while ((c = *s)) {
         if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
-            d = c2x(c, d);
+            d = c2x(c, '%', d);
+        }
+        else {
+            *d++ = c;
+        }
+        ++s;
+    }
+    *d = '\0';
+    return copy;
+}
+
+AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer)
+{
+    const unsigned char *s = (const unsigned char *)buffer;
+    unsigned char *d = (unsigned char *)copy;
+    unsigned c;
+
+    while ((c = *s)) {
+        if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
+            d = c2x(c, '%', d);
+        }
+        else if (c == ' ') {
+            *d++ = '+';
         }
         else {
             *d++ = c;
@@ -1693,9 +1849,14 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
     return copy;
 }
 
+AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
+{
+    return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer);
+}
+
 /* ap_escape_uri is now a macro for os_escape_path */
 
-AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
+AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
 {
     int i, j;
     char *x;
@@ -1706,6 +1867,10 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             j += 3;
         else if (s[i] == '&')
             j += 4;
+        else if (s[i] == '"')
+            j += 5;
+        else if (toasc && !apr_isascii(s[i]))
+            j += 5;
 
     if (j == 0)
         return apr_pstrmemdup(p, s, i);
@@ -1724,12 +1889,163 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             memcpy(&x[j], "&amp;", 5);
             j += 4;
         }
+        else if (s[i] == '"') {
+            memcpy(&x[j], "&quot;", 6);
+            j += 5;
+        }
+        else if (toasc && !apr_isascii(s[i])) {
+            char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
+            memcpy(&x[j], esc, 6);
+            j += 5;
+        }
         else
             x[j] = s[i];
 
     x[j] = '\0';
     return x;
 }
+AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
+{
+    char *ret;
+    unsigned char *d;
+    const unsigned char *s;
+    apr_size_t length, escapes = 0;
+
+    if (!str) {
+        return NULL;
+    }
+
+    /* Compute how many characters need to be escaped */
+    s = (const unsigned char *)str;
+    for (; *s; ++s) {
+        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
+            escapes++;
+        }
+    }
+    
+    /* Compute the length of the input string, including NULL */
+    length = s - (const unsigned char *)str + 1;
+    
+    /* Fast path: nothing to escape */
+    if (escapes == 0) {
+        return apr_pmemdup(p, str, length);
+    }
+    
+    /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
+    ret = apr_palloc(p, length + 3 * escapes);
+    d = (unsigned char *)ret;
+    s = (const unsigned char *)str;
+    for (; *s; ++s) {
+        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
+            *d++ = '\\';
+            switch(*s) {
+            case '\b':
+                *d++ = 'b';
+                break;
+            case '\n':
+                *d++ = 'n';
+                break;
+            case '\r':
+                *d++ = 'r';
+                break;
+            case '\t':
+                *d++ = 't';
+                break;
+            case '\v':
+                *d++ = 'v';
+                break;
+            case '\\':
+            case '"':
+                *d++ = *s;
+                break;
+            default:
+                c2x(*s, 'x', d);
+                d += 3;
+            }
+        }
+        else {
+            *d++ = *s;
+        }
+    }
+    *d = '\0';
+
+    return ret;
+}
+
+AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
+                                               apr_size_t buflen)
+{
+    unsigned char *d, *ep;
+    const unsigned char *s;
+
+    if (!source || !buflen) { /* be safe */
+        return 0;
+    }
+
+    d = (unsigned char *)dest;
+    s = (const unsigned char *)source;
+    ep = d + buflen - 1;
+
+    for (; d < ep && *s; ++s) {
+
+        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
+            *d++ = '\\';
+            if (d >= ep) {
+                --d;
+                break;
+            }
+
+            switch(*s) {
+            case '\b':
+                *d++ = 'b';
+                break;
+            case '\n':
+                *d++ = 'n';
+                break;
+            case '\r':
+                *d++ = 'r';
+                break;
+            case '\t':
+                *d++ = 't';
+                break;
+            case '\v':
+                *d++ = 'v';
+                break;
+            case '\\':
+                *d++ = *s;
+                break;
+            case '"': /* no need for this in error log */
+                d[-1] = *s;
+                break;
+            default:
+                if (d >= ep - 2) {
+                    ep = --d; /* break the for loop as well */
+                    break;
+                }
+                c2x(*s, 'x', d);
+                d += 3;
+            }
+        }
+        else {
+            *d++ = *s;
+        }
+    }
+    *d = '\0';
+
+    return (d - (unsigned char *)dest);
+}
+
+AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
+{
+    const unsigned char *in = src;
+    apr_size_t i;
+
+    for (i = 0; i < srclen; i++) {
+        *dest++ = c2x_table[in[i] >> 4];
+        *dest++ = c2x_table[in[i] & 0xf];
+    }
+    *dest = '\0';
+}
 
 AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
 {
@@ -1745,7 +2061,7 @@ AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
 {
     apr_finfo_t finfo;
 
-    if (apr_lstat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
+    if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
         return 0;                /* in error condition, just return no */
 
     return (finfo.filetype == APR_DIR);
@@ -1823,24 +2139,17 @@ AP_DECLARE(void) ap_str_tolower(char *str)
     }
 }
 
-static char *find_fqdn(apr_pool_t *a, struct hostent *p)
+AP_DECLARE(void) ap_str_toupper(char *str)
 {
-    int x;
-
-    if (!strchr(p->h_name, '.')) {
-        if (p->h_aliases) {
-            for (x = 0; p->h_aliases[x]; ++x) {
-                if (strchr(p->h_aliases[x], '.') &&
-                    (!strncasecmp(p->h_aliases[x], p->h_name,
-                                  strlen(p->h_name))))
-                    return apr_pstrdup(a, p->h_aliases[x]);
-            }
-        }
-        return NULL;
+    while (*str) {
+        *str = apr_toupper(*str);
+        ++str;
     }
-    return apr_pstrdup(a, (void *) p->h_name);
 }
 
+/*
+ * We must return a FQDN
+ */
 char *ap_get_local_host(apr_pool_t *a)
 {
 #ifndef MAXHOSTNAMELEN
@@ -1848,45 +2157,42 @@ char *ap_get_local_host(apr_pool_t *a)
 #endif
     char str[MAXHOSTNAMELEN + 1];
     char *server_hostname = NULL;
-    struct hostent *p;
+    apr_sockaddr_t *sockaddr;
+    char *hostname;
 
-#ifdef BEOS_R5
-    if (gethostname(str, sizeof(str) - 1) == 0)
-#else
-    if (gethostname(str, sizeof(str) - 1) != 0)
-#endif
-    {
-        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
-                     "%s: gethostname() failed to determine ServerName",
+    if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556)
+                     "%s: apr_gethostname() failed to determine ServerName",
                      ap_server_argv0);
-    }
-    else 
-    {
+    } else {
         str[sizeof(str) - 1] = '\0';
-        /* TODO: Screaming for APR-ization */
-        if ((!(p = gethostbyname(str))) 
-            || (!(server_hostname = find_fqdn(a, p)))) {
-            /* Recovery - return the default servername by IP: */
-            if (p && p->h_addr_list[0]) {
-                apr_snprintf(str, sizeof(str), "%pA", p->h_addr_list[0]);
+        if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
+            if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
+                (ap_strchr_c(hostname, '.')) ) {
+                server_hostname = apr_pstrdup(a, hostname);
+                return server_hostname;
+            } else if (ap_strchr_c(str, '.')) {
                 server_hostname = apr_pstrdup(a, str);
-                /* We will drop through to report the IP-named server */
+            } else {
+                apr_sockaddr_ip_get(&hostname, sockaddr);
+                server_hostname = apr_pstrdup(a, hostname);
             }
-        }
-        else {
-            /* Since we found a fdqn, return it with no logged message. */
-            return server_hostname;
+        } else {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557)
+                         "%s: apr_sockaddr_info_get() failed for %s",
+                         ap_server_argv0, str);
         }
     }
 
-    if (!server_hostname) 
+    if (!server_hostname)
         server_hostname = apr_pstrdup(a, "127.0.0.1");
 
-    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
-                 "%s: Could not determine the server's fully qualified "
-                 "domain name, using %s for ServerName",
+    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
+                 "%s: Could not reliably determine the server's fully qualified "
+                 "domain name, using %s. Set the 'ServerName' directive globally "
+                 "to suppress this message",
                  ap_server_argv0, server_hostname);
-             
+
     return server_hostname;
 }
 
@@ -1904,8 +2210,8 @@ AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
     return decoded;
 }
 
-AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string) 
-{ 
+AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
+{
     char *encoded;
     int l = strlen(string);
 
@@ -1931,10 +2237,9 @@ AP_DECLARE(void) ap_content_type_tolower(char *str)
     if (semi) {
         *semi = '\0';
     }
-    while (*str) {
-        *str = apr_tolower(*str);
-        ++str;
-    }
+
+    ap_str_tolower(str);
+
     if (semi) {
         *semi = ';';
     }
@@ -1990,3 +2295,713 @@ AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
     *outchr = '\0';
     return outstring;
 }
+
+/*
+ * Given a string, append the PID deliminated by delim.
+ * Usually used to create a pid-appended filepath name
+ * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
+ * a macro, to avoid unistd.h dependency
+ */
+AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
+                                    const char *delim)
+{
+    return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
+                        delim, getpid());
+
+}
+
+/**
+ * Parse a given timeout parameter string into an apr_interval_time_t value.
+ * The unit of the time interval is given as postfix string to the numeric
+ * string. Currently the following units are understood:
+ *
+ * ms    : milliseconds
+ * s     : seconds
+ * mi[n] : minutes
+ * h     : hours
+ *
+ * If no unit is contained in the given timeout parameter the default_time_unit
+ * will be used instead.
+ * @param timeout_parameter The string containing the timeout parameter.
+ * @param timeout The timeout value to be returned.
+ * @param default_time_unit The default time unit to use if none is specified
+ * in timeout_parameter.
+ * @return Status value indicating whether the parsing was successful or not.
+ */
+AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
+                                               const char *timeout_parameter,
+                                               apr_interval_time_t *timeout,
+                                               const char *default_time_unit)
+{
+    char *endp;
+    const char *time_str;
+    apr_int64_t tout;
+
+    tout = apr_strtoi64(timeout_parameter, &endp, 10);
+    if (errno) {
+        return errno;
+    }
+    if (!endp || !*endp) {
+        time_str = default_time_unit;
+    }
+    else {
+        time_str = endp;
+    }
+
+    switch (*time_str) {
+        /* Time is in seconds */
+    case 's':
+        *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
+        break;
+    case 'h':
+        /* Time is in hours */
+        *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
+        break;
+    case 'm':
+        switch (*(++time_str)) {
+        /* Time is in milliseconds */
+        case 's':
+            *timeout = (apr_interval_time_t) tout * 1000;
+            break;
+        /* Time is in minutes */
+        case 'i':
+            *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
+            break;
+        default:
+            return APR_EGENERAL;
+        }
+        break;
+    default:
+        return APR_EGENERAL;
+    }
+    return APR_SUCCESS;
+}
+
+/**
+ * Determine if a request has a request body or not.
+ *
+ * @param r the request_rec of the request
+ * @return truth value
+ */
+AP_DECLARE(int) ap_request_has_body(request_rec *r)
+{
+    apr_off_t cl;
+    char *estr;
+    const char *cls;
+    int has_body;
+
+    has_body = (!r->header_only
+                && (r->kept_body
+                    || apr_table_get(r->headers_in, "Transfer-Encoding")
+                    || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
+                        && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
+                        && (!*estr)
+                        && (cl > 0) )
+                    )
+                );
+    return has_body;
+}
+
+AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
+{
+    void **ptr = (void **)data_;
+    *ptr = NULL;
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
+
+    for ( ; *src; src++, dest++)
+    {
+        if (!apr_isprint(*src))
+            *dest = 'x';
+        else if (!apr_isalnum(*src))
+            *dest = '_';
+        else
+            *dest = (char)*src;
+    }
+    *dest = '\0';
+    return APR_SUCCESS;
+
+}
+
+AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
+                                        const char **dest)
+{
+    char *new = apr_palloc(p, strlen(src)+1);
+    if (!new)
+        return APR_ENOMEM;
+    *dest = new;
+    return ap_str2_alnum(src, new);
+}
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ *
+ * Name/value pairs are returned in an array, with the names as
+ * strings with a maximum length of HUGE_STRING_LEN, and the
+ * values as bucket brigades. This allows values to be arbitrarily
+ * large.
+ *
+ * All url-encoding is removed from both the names and the values
+ * on the fly. The names are interpreted as strings, while the
+ * values are interpreted as blocks of binary data, that may
+ * contain the 0 character.
+ *
+ * In order to ensure that resource limits are not exceeded, a
+ * maximum size must be provided. If the sum of the lengths of
+ * the names and the values exceed this size, this function
+ * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
+ *
+ * An optional number of parameters can be provided, if the number
+ * of parameters provided exceeds this amount, this function will
+ * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
+ * no limit is imposed, and the number of parameters is in turn
+ * constrained by the size parameter above.
+ *
+ * This function honours any kept_body configuration, and the
+ * original raw request body will be saved to the kept_body brigade
+ * if so configured, just as ap_discard_request_body does.
+ *
+ * NOTE: File upload is not yet supported, but can be without change
+ * to the function call.
+ */
+
+/* form parsing stuff */
+typedef enum {
+    FORM_NORMAL,
+    FORM_AMP,
+    FORM_NAME,
+    FORM_VALUE,
+    FORM_PERCENTA,
+    FORM_PERCENTB,
+    FORM_ABORT
+} ap_form_type_t;
+
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
+                                   apr_array_header_t **ptr,
+                                   apr_size_t num, apr_size_t usize)
+{
+    apr_bucket_brigade *bb = NULL;
+    int seen_eos = 0;
+    char buffer[HUGE_STRING_LEN + 1];
+    const char *ct;
+    apr_size_t offset = 0;
+    apr_ssize_t size;
+    ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
+    ap_form_pair_t *pair = NULL;
+    apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
+
+    char hi = 0;
+    char low = 0;
+
+    *ptr = pairs;
+
+    /* sanity check - we only support forms for now */
+    ct = apr_table_get(r->headers_in, "Content-Type");
+    if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
+        return ap_discard_request_body(r);
+    }
+
+    if (usize > APR_SIZE_MAX >> 1)
+        size = APR_SIZE_MAX >> 1;
+    else
+        size = usize;
+
+    if (!f) {
+        f = r->input_filters;
+    }
+
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    do {
+        apr_bucket *bucket = NULL, *last = NULL;
+
+        int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
+                                APR_BLOCK_READ, HUGE_STRING_LEN);
+        if (rv != APR_SUCCESS) {
+            apr_brigade_destroy(bb);
+            return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
+        }
+
+        for (bucket = APR_BRIGADE_FIRST(bb);
+             bucket != APR_BRIGADE_SENTINEL(bb);
+             last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
+            const char *data;
+            apr_size_t len, slide;
+
+            if (last) {
+                apr_bucket_delete(last);
+            }
+            if (APR_BUCKET_IS_EOS(bucket)) {
+                seen_eos = 1;
+                break;
+            }
+            if (bucket->length == 0) {
+                continue;
+            }
+
+            rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+            if (rv != APR_SUCCESS) {
+                apr_brigade_destroy(bb);
+                return HTTP_BAD_REQUEST;
+            }
+
+            slide = len;
+            while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
+                char c = *data++;
+                if ('+' == c) {
+                    c = ' ';
+                }
+                else if ('&' == c) {
+                    state = FORM_AMP;
+                }
+                if ('%' == c) {
+                    percent = FORM_PERCENTA;
+                    continue;
+                }
+                if (FORM_PERCENTA == percent) {
+                    if (c >= 'a') {
+                        hi = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        hi = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        hi = c - '0';
+                    }
+                    hi = hi << 4;
+                    percent = FORM_PERCENTB;
+                    continue;
+                }
+                if (FORM_PERCENTB == percent) {
+                    if (c >= 'a') {
+                        low = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        low = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        low = c - '0';
+                    }
+                    c = low | hi;
+                    percent = FORM_NORMAL;
+                }
+                switch (state) {
+                    case FORM_AMP:
+                        if (pair) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                        }
+                        state = FORM_NAME;
+                        pair = NULL;
+                        offset = 0;
+                        num--;
+                        break;
+                    case FORM_NAME:
+                        if (offset < HUGE_STRING_LEN) {
+                            if ('=' == c) {
+                                buffer[offset] = 0;
+                                offset = 0;
+                                pair = (ap_form_pair_t *) apr_array_push(pairs);
+                                pair->name = apr_pstrdup(r->pool, buffer);
+                                pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+                                state = FORM_VALUE;
+                            }
+                            else {
+                                buffer[offset++] = c;
+                                size--;
+                            }
+                        }
+                        else {
+                            state = FORM_ABORT;
+                        }
+                        break;
+                    case FORM_VALUE:
+                        if (offset >= HUGE_STRING_LEN) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                            offset = 0;
+                        }
+                        buffer[offset++] = c;
+                        size--;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+        }
+
+        apr_brigade_cleanup(bb);
+    } while (!seen_eos);
+
+    if (FORM_ABORT == state || size < 0 || num == 0) {
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+    else if (FORM_VALUE == state && pair && offset > 0) {
+        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(pair->value, b);
+    }
+
+    return OK;
+
+}
+
+#define VARBUF_SMALL_SIZE 2048
+#define VARBUF_MAX_SIZE   (APR_SIZE_MAX - 1 -                                \
+                           APR_ALIGN_DEFAULT(sizeof(struct ap_varbuf_info)))
+
+struct ap_varbuf_info {
+    struct apr_memnode_t *node;
+    apr_allocator_t *allocator;
+};
+
+static apr_status_t varbuf_cleanup(void *info_)
+{
+    struct ap_varbuf_info *info = info_;
+    info->node->next = NULL;
+    apr_allocator_free(info->allocator, info->node);
+    return APR_SUCCESS;
+}
+
+const char nul = '\0';
+static char * const varbuf_empty = (char *)&nul;
+
+AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
+                                apr_size_t init_size)
+{
+    vb->buf = varbuf_empty;
+    vb->avail = 0;
+    vb->strlen = AP_VARBUF_UNKNOWN;
+    vb->pool = p;
+    vb->info = NULL;
+
+    ap_varbuf_grow(vb, init_size);
+}
+
+AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
+{
+    apr_memnode_t *new_node = NULL;
+    apr_allocator_t *allocator;
+    struct ap_varbuf_info *new_info;
+    char *new;
+
+    AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN || vb->avail >= vb->strlen);
+
+    if (new_len <= vb->avail)
+        return;
+
+    if (new_len < 2 * vb->avail && vb->avail < VARBUF_MAX_SIZE/2) {
+        /* at least double the size, to avoid repeated reallocations */
+        new_len = 2 * vb->avail;
+    }
+    else if (new_len > VARBUF_MAX_SIZE) {
+        apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
+        ap_assert(abort_fn != NULL);
+        abort_fn(APR_ENOMEM);
+        return;
+    }
+
+    new_len++;  /* add space for trailing \0 */
+    if (new_len <= VARBUF_SMALL_SIZE) {
+        new_len = APR_ALIGN_DEFAULT(new_len);
+        new = apr_palloc(vb->pool, new_len);
+        if (vb->avail && vb->strlen != 0) {
+            AP_DEBUG_ASSERT(vb->buf != NULL);
+            AP_DEBUG_ASSERT(vb->buf != varbuf_empty);
+            if (new == vb->buf + vb->avail + 1) {
+                /* We are lucky: the new memory lies directly after our old
+                 * buffer, we can now use both.
+                 */
+                vb->avail += new_len;
+                return;
+            }
+            else {
+                /* copy up to vb->strlen + 1 bytes */
+                memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
+                                     vb->avail + 1 : vb->strlen + 1);
+            }
+        }
+        else {
+            *new = '\0';
+        }
+        vb->avail = new_len - 1;
+        vb->buf = new;
+        return;
+    }
+
+    /* The required block is rather larger. Use allocator directly so that
+     * the memory can be freed independently from the pool. */
+    allocator = apr_pool_allocator_get(vb->pool);
+    if (new_len <= VARBUF_MAX_SIZE)
+        new_node = apr_allocator_alloc(allocator,
+                                       new_len + APR_ALIGN_DEFAULT(sizeof(*new_info)));
+    if (!new_node) {
+        apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
+        ap_assert(abort_fn != NULL);
+        abort_fn(APR_ENOMEM);
+        return;
+    }
+    new_info = (struct ap_varbuf_info *)new_node->first_avail;
+    new_node->first_avail += APR_ALIGN_DEFAULT(sizeof(*new_info));
+    new_info->node = new_node;
+    new_info->allocator = allocator;
+    new = new_node->first_avail;
+    AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len);
+    new_len = new_node->endp - new_node->first_avail;
+
+    if (vb->avail && vb->strlen != 0)
+        memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
+                             vb->avail + 1 : vb->strlen + 1);
+    else
+        *new = '\0';
+    if (vb->info)
+        apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
+    apr_pool_cleanup_register(vb->pool, new_info, varbuf_cleanup,
+                              apr_pool_cleanup_null);
+    vb->info = new_info;
+    vb->buf = new;
+    vb->avail = new_len - 1;
+}
+
+AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str,
+                                     int len)
+{
+    if (len == 0)
+        return;
+    if (!vb->avail) {
+        ap_varbuf_grow(vb, len);
+        memcpy(vb->buf, str, len);
+        vb->buf[len] = '\0';
+        vb->strlen = len;
+        return;
+    }
+    if (vb->strlen == AP_VARBUF_UNKNOWN)
+        vb->strlen = strlen(vb->buf);
+    ap_varbuf_grow(vb, vb->strlen + len);
+    memcpy(vb->buf + vb->strlen, str, len);
+    vb->strlen += len;
+    vb->buf[vb->strlen] = '\0';
+}
+
+AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb)
+{
+    if (vb->info) {
+        apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
+        vb->info = NULL;
+    }
+    vb->buf = NULL;
+}
+
+AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf,
+                                  const char *prepend, apr_size_t prepend_len,
+                                  const char *append, apr_size_t append_len,
+                                  apr_size_t *new_len)
+{
+    apr_size_t i = 0;
+    struct iovec vec[3];
+
+    if (prepend) {
+        vec[i].iov_base = (void *)prepend;
+        vec[i].iov_len = prepend_len;
+        i++;
+    }
+    if (buf->avail && buf->strlen) {
+        if (buf->strlen == AP_VARBUF_UNKNOWN)
+            buf->strlen = strlen(buf->buf);
+        vec[i].iov_base = (void *)buf->buf;
+        vec[i].iov_len = buf->strlen;
+        i++;
+    }
+    if (append) {
+        vec[i].iov_base = (void *)append;
+        vec[i].iov_len = append_len;
+        i++;
+    }
+    if (i)
+        return apr_pstrcatv(p, vec, i, new_len);
+
+    if (new_len)
+        *new_len = 0;
+    return "";
+}
+
+AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
+                                          const char *input,
+                                          const char *source,
+                                          apr_size_t nmatch,
+                                          ap_regmatch_t pmatch[],
+                                          apr_size_t maxlen)
+{
+    return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen);
+}
+
+static const char * const oom_message = "[crit] Memory allocation failed, "
+                                        "aborting process." APR_EOL_STR;
+
+AP_DECLARE(void) ap_abort_on_oom()
+{
+    int written, count = strlen(oom_message);
+    const char *buf = oom_message;
+    do {
+        written = write(STDERR_FILENO, buf, count);
+        if (written == count)
+            break;
+        if (written > 0) {
+            buf += written;
+            count -= written;
+        }
+    } while (written >= 0 || errno == EINTR);
+    abort();
+}
+
+AP_DECLARE(void *) ap_malloc(size_t size)
+{
+    void *p = malloc(size);
+    if (p == NULL && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size)
+{
+    void *p = calloc(nelem, size);
+    if (p == NULL && nelem != 0 && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
+{
+    void *p = realloc(ptr, size);
+    if (p == NULL && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void) ap_get_sload(ap_sload_t *ld)
+{
+    int i, j, server_limit, thread_limit;
+    int ready = 0;
+    int busy = 0;
+    int total;
+    ap_generation_t mpm_generation;
+
+    /* preload errored fields, we overwrite */
+    ld->idle = -1;
+    ld->busy = -1;
+    ld->bytes_served = 0;
+    ld->access_count = 0;
+
+    ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
+
+    for (i = 0; i < server_limit; i++) {
+        process_score *ps;
+        ps = ap_get_scoreboard_process(i);
+
+        for (j = 0; j < thread_limit; j++) {
+            int res;
+            worker_score *ws = NULL;
+            ws = &ap_scoreboard_image->servers[i][j];
+            res = ws->status;
+
+            if (!ps->quiescing && ps->pid) {
+                if (res == SERVER_READY && ps->generation == mpm_generation) {
+                    ready++;
+                }
+                else if (res != SERVER_DEAD &&
+                         res != SERVER_STARTING && res != SERVER_IDLE_KILL &&
+                         ps->generation == mpm_generation) {
+                    busy++;
+                }   
+            }
+
+            if (ap_extended_status && !ps->quiescing && ps->pid) {
+                if (ws->access_count != 0 
+                    || (res != SERVER_READY && res != SERVER_DEAD)) {
+                    ld->access_count += ws->access_count;
+                    ld->bytes_served += ws->bytes_served;
+                }
+            }
+        }
+    }
+    total = busy + ready;
+    if (total) {
+        ld->idle = ready * 100 / total;
+        ld->busy = busy * 100 / total;
+    }
+}
+
+AP_DECLARE(void) ap_get_loadavg(ap_loadavg_t *ld)
+{
+    /* preload errored fields, we overwrite */
+    ld->loadavg = -1.0;
+    ld->loadavg5 = -1.0;
+    ld->loadavg15 = -1.0;
+
+#if HAVE_GETLOADAVG
+    {
+        double la[3];
+        int num;
+
+        num = getloadavg(la, 3);
+        if (num > 0) {
+            ld->loadavg = (float)la[0];
+        }
+        if (num > 1) {
+            ld->loadavg5 = (float)la[1];
+        }
+        if (num > 2) {
+            ld->loadavg15 = (float)la[2];
+        }
+    }
+#endif
+}
+
+AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p,
+                                    const char *cmd,
+                                    const char * const * argv)
+{
+    char buf[MAX_STRING_LEN];
+    apr_procattr_t *procattr;
+    apr_proc_t *proc;
+    apr_file_t *fp;
+    apr_size_t nbytes = 1;
+    char c;
+    int k;
+
+    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
+                            APR_FULL_BLOCK) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_dir_set(procattr,
+                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
+        return NULL;
+    proc = apr_pcalloc(p, sizeof(apr_proc_t));
+    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
+        return NULL;
+    fp = proc->out;
+
+    if (fp == NULL)
+        return NULL;
+    /* XXX: we are reading 1 byte at a time here */
+    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
+                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
+        if (c == '\n' || c == '\r')
+            break;
+        buf[k++] = c;
+    }
+    buf[k] = '\0'; 
+    apr_file_close(fp);
+
+    return apr_pstrndup(p, buf, k);
+}