]> granicus.if.org Git - apache/blobdiff - server/util.c
Spelling fix in comment.
[apache] / server / util.c
index 8b12f85216651411d2ceba11ebadb5f2f3011e17..729c754baa29acf3534b7183d6003a7332e61ebb 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-2001 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:
@@ -84,8 +42,6 @@
 #include <netdb.h>              /* for gethostbyname() */
 #endif
 
-#define CORE_PRIVATE
-
 #include "ap_config.h"
 #include "apr_base64.h"
 #include "httpd.h"
  * char in here and get it to work, because if char is signed then it
  * will first be sign extended.
  */
-#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
+#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
+
+/* Win32/NetWare/OS2 need to check for both forward and back slashes
+ * in ap_getparents() and ap_escape_url.
+ */
+#ifdef CASE_BLIND_FILESYSTEM
+#define IS_SLASH(s) ((s == '/') || (s == '\\'))
+#else
+#define IS_SLASH(s) (s == '/')
+#endif
+
 
 /*
  * Examine a field value (such as a media-/content-type) string and return
@@ -127,60 +93,61 @@ 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);
-    } 
+        return apr_pstrdup(p, intype);
+    }
     else {
-       while ((semi > intype) && apr_isspace(semi[-1])) {
-           semi--;
-       }
-       return apr_pstrndup(p, intype, semi - intype);
+        while ((semi > intype) && apr_isspace(semi[-1])) {
+            semi--;
+        }
+        return apr_pstrndup(p, intype, semi - intype);
     }
 }
 
-AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt, int gmt)
+AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
+                              int gmt)
 {
     apr_size_t retcode;
     char ts[MAX_STRING_LEN];
     char tf[MAX_STRING_LEN];
-    apr_exploded_time_t xt;
+    apr_time_exp_t xt;
 
     if (gmt) {
-       const char *f;
-       char *strp;
-
-        apr_explode_gmt(&xt, t);
-       /* Convert %Z to "GMT" and %z to "+0000";
-        * on hosts that do not have a time zone string in struct tm,
-        * strftime must assume its argument is local time.
-        */
-       for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
-           ; f++, strp++) {
-           if (*f != '%') continue;
-           switch (f[1]) {
-           case '%':
-               *++strp = *++f;
-               break;
-           case 'Z':
-               *strp++ = 'G';
-               *strp++ = 'M';
-               *strp = 'T';
-               f++;
-               break;
-           case 'z': /* common extension */
-               *strp++ = '+';
-               *strp++ = '0';
-               *strp++ = '0';
-               *strp++ = '0';
-               *strp = '0';
-               f++;
-               break;
-           }
-       }
-       *strp = '\0';
-       fmt = tf;
+        const char *f;
+        char *strp;
+
+        apr_time_exp_gmt(&xt, t);
+        /* Convert %Z to "GMT" and %z to "+0000";
+         * on hosts that do not have a time zone string in struct tm,
+         * strftime must assume its argument is local time.
+         */
+        for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
+            ; f++, strp++) {
+            if (*f != '%') continue;
+            switch (f[1]) {
+            case '%':
+                *++strp = *++f;
+                break;
+            case 'Z':
+                *strp++ = 'G';
+                *strp++ = 'M';
+                *strp = 'T';
+                f++;
+                break;
+            case 'z': /* common extension */
+                *strp++ = '+';
+                *strp++ = '0';
+                *strp++ = '0';
+                *strp++ = '0';
+                *strp = '0';
+                f++;
+                break;
+            }
+        }
+        *strp = '\0';
+        fmt = tf;
     }
     else {
-        apr_explode_localtime(&xt, t);
+        apr_time_exp_lt(&xt, t);
     }
 
     /* check return code? */
@@ -202,60 +169,61 @@ AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt, int
  * 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] != '*'))
-           return -1;
-       if (exp[y] == '*') {
-           while (exp[++y] == '*');
-           if (!exp[y])
-               return 0;
-           while (str[x]) {
-               int ret;
-               if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1)
-                   return ret;
-           }
-           return -1;
-       }
-       else if ((exp[y] != '?') && (str[x] != exp[y]))
-           return 1;
+    for (x = 0, y = 0; expected[y]; ++y, ++x) {
+        if ((!str[x]) && (expected[y] != '*'))
+            return -1;
+        if (expected[y] == '*') {
+            while (expected[++y] == '*');
+            if (!expected[y])
+                return 0;
+            while (str[x]) {
+                int ret;
+                if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
+                    return ret;
+            }
+            return -1;
+        }
+        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] != '*'))
-           return -1;
-       if (exp[y] == '*') {
-           while (exp[++y] == '*');
-           if (!exp[y])
-               return 0;
-           while (str[x]) {
-               int ret;
-               if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1)
-                   return ret;
-           }
-           return -1;
-       }
-       else if ((exp[y] != '?') && (apr_tolower(str[x]) != apr_tolower(exp[y])))
-           return 1;
+    for (x = 0, y = 0; expected[y]; ++y, ++x) {
+        if (!str[x] && expected[y] != '*')
+            return -1;
+        if (expected[y] == '*') {
+            while (expected[++y] == '*');
+            if (!expected[y])
+                return 0;
+            while (str[x]) {
+                int ret;
+                if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
+                    return ret;
+            }
+            return -1;
+        }
+        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;
@@ -271,42 +239,43 @@ AP_DECLARE(int) ap_is_matchexp(const char *str)
     register int x;
 
     for (x = 0; str[x]; x++)
-       if ((str[x] == '*') || (str[x] == '?'))
-           return 1;
+        if ((str[x] == '*') || (str[x] == '?'))
+            return 1;
     return 0;
 }
 
 /*
- * 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));
+    ap_regex_t *preg = apr_palloc(p, sizeof *preg);
 
-    if (regcomp(preg, pattern, cflags)) {
-       return NULL;
+    if (ap_regcomp(preg, pattern, cflags)) {
+        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);
 }
 
@@ -318,16 +287,18 @@ AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
 {
     char *p1, *p2;
     if (*s2 == '\0') {
-       /* an empty s2 */
+        /* an empty s2 */
         return((char *)s1);
     }
     while(1) {
-       for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
-       if (*s1 == '\0') return(NULL);
-       /* found first character of s2, see if the rest matches */
+        for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
+        if (*s1 == '\0') {
+            return(NULL);
+        }
+        /* found first character of s2, see if the rest matches */
         p1 = (char *)s1;
         p2 = (char *)s2;
-        while (apr_tolower(*++p1) == apr_tolower(*++p2)) {
+        for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) {
             if (*p1 == '\0') {
                 /* both strings ended together */
                 return((char *)s1);
@@ -337,7 +308,7 @@ AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
             /* second string ended, a match */
             break;
         }
-       /* didn't find a match here, try starting at next character in s1 */
+        /* didn't find a match here, try starting at next character in s1 */
         s1++;
     }
     return((char *)s1);
@@ -371,28 +342,10 @@ 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.
  *
  * input should be the string with the $-expressions, source should be the
  * string that was matched against.
@@ -403,8 +356,9 @@ AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, s
  * 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[])
+AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
+                              const char *source, size_t nmatch,
+                              ap_regmatch_t pmatch[])
 {
     const char *src = input;
     char *dest, *dst;
@@ -413,30 +367,30 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, const char *sour
     int len;
 
     if (!source)
-       return NULL;
+        return NULL;
     if (!nmatch)
-       return apr_pstrdup(p, src);
+        return apr_pstrdup(p, src);
 
     /* First pass, find the size */
 
     len = 0;
 
     while ((c = *src++) != '\0') {
-       if (c == '&')
-           no = 0;
-       else if (c == '$' && apr_isdigit(*src))
-           no = *src++ - '0';
-       else
-           no = 10;
+        if (c == '&')
+            no = 0;
+        else if (c == '$' && apr_isdigit(*src))
+            no = *src++ - '0';
+        else
+            no = 10;
 
-       if (no > 9) {           /* Ordinary character. */
-           if (c == '\\' && (*src == '$' || *src == '&'))
-               c = *src++;
-           len++;
-       }
-       else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
-           len += pmatch[no].rm_eo - pmatch[no].rm_so;
-       }
+        if (no > 9) {                /* Ordinary character. */
+            if (c == '\\' && (*src == '$' || *src == '&'))
+                c = *src++;
+            len++;
+        }
+        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            len += pmatch[no].rm_eo - pmatch[no].rm_so;
+        }
 
     }
 
@@ -447,23 +401,23 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, const char *sour
     src = input;
 
     while ((c = *src++) != '\0') {
-       if (c == '&')
-           no = 0;
-       else if (c == '$' && apr_isdigit(*src))
-           no = *src++ - '0';
-       else
-           no = 10;
-
-       if (no > 9) {           /* Ordinary character. */
-           if (c == '\\' && (*src == '$' || *src == '&'))
-               c = *src++;
-           *dst++ = c;
-       }
-       else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
-           len = pmatch[no].rm_eo - pmatch[no].rm_so;
-           memcpy(dst, source + pmatch[no].rm_so, len);
-           dst += len;
-       }
+        if (c == '&')
+            no = 0;
+        else if (c == '$' && apr_isdigit(*src))
+            no = *src++ - '0';
+        else
+            no = 10;
+
+        if (no > 9) {                /* Ordinary character. */
+            if (c == '\\' && (*src == '$' || *src == '&'))
+                c = *src++;
+            *dst++ = c;
+        }
+        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            len = pmatch[no].rm_eo - pmatch[no].rm_so;
+            memcpy(dst, source + pmatch[no].rm_so, len);
+            dst += len;
+        }
 
     }
     *dst = '\0';
@@ -476,62 +430,68 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, const char *sour
  */
 AP_DECLARE(void) ap_getparents(char *name)
 {
-    int l, w;
+    char *next;
+    int l, w, first_dot;
 
     /* Four paseses, as per RFC 1808 */
     /* a) remove ./ path segments */
+    for (next = name; *next && (*next != '.'); next++) {
+    }
 
-    for (l = 0, w = 0; name[l] != '\0';) {
-       if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/'))
-           l += 2;
-       else
-           name[w++] = name[l++];
+    l = w = first_dot = next - name;
+    while (name[l] != '\0') {
+        if (name[l] == '.' && IS_SLASH(name[l + 1])
+            && (l == 0 || IS_SLASH(name[l - 1])))
+            l += 2;
+        else
+            name[w++] = name[l++];
     }
 
     /* b) remove trailing . path, segment */
     if (w == 1 && name[0] == '.')
-       w--;
-    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
-       w--;
+        w--;
+    else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
+        w--;
     name[w] = '\0';
 
     /* c) remove all xx/../ segments. (including leading ../ and /../) */
-    l = 0;
+    l = first_dot;
 
     while (name[l] != '\0') {
-       if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
-           (l == 0 || name[l - 1] == '/')) {
-           register int m = l + 3, n;
-
-           l = l - 2;
-           if (l >= 0) {
-               while (l >= 0 && name[l] != '/')
-                   l--;
-               l++;
-           }
-           else
-               l = 0;
-           n = l;
-           while ((name[n] = name[m]))
-               (++n, ++m);
-       }
-       else
-           ++l;
+        if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
+            && (l == 0 || IS_SLASH(name[l - 1]))) {
+            register int m = l + 3, n;
+
+            l = l - 2;
+            if (l >= 0) {
+                while (l >= 0 && !IS_SLASH(name[l]))
+                    l--;
+                l++;
+            }
+            else
+                l = 0;
+            n = l;
+            while ((name[n] = name[m]))
+                (++n, ++m);
+        }
+        else
+            ++l;
     }
 
     /* d) remove trailing xx/.. segment. */
     if (l == 2 && name[0] == '.' && name[1] == '.')
-       name[0] = '\0';
-    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') {
-       l = l - 4;
-       if (l >= 0) {
-           while (l >= 0 && name[l] != '/')
-               l--;
-           l++;
-       }
-       else
-           l = 0;
-       name[l] = '\0';
+        name[0] = '\0';
+    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
+             && IS_SLASH(name[l - 3])) {
+        l = l - 4;
+        if (l >= 0) {
+            while (l >= 0 && !IS_SLASH(name[l]))
+                l--;
+            l++;
+        }
+        else
+            l = 0;
+        name[l] = '\0';
     }
 }
 
@@ -548,14 +508,14 @@ AP_DECLARE(void) ap_no2slash(char *name)
 #endif
 
     while (*s) {
-       if ((*d++ = *s) == '/') {
-           do {
-               ++s;
-           } while (*s == '/');
-       }
-       else {
-           ++s;
-       }
+        if ((*d++ = *s) == '/') {
+            do {
+                ++s;
+            } while (*s == '/');
+        }
+        else {
+            ++s;
+        }
     }
     *d = '\0';
 }
@@ -567,8 +527,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
 
@@ -594,11 +554,11 @@ AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
     }
 
     for (;;) {
-       if (*s == '\0' || (*s == '/' && (--n) == 0)) {
-           *d = '/';
-           break;
-       }
-       *d++ = *s++;
+        if (*s == '\0' || (*s == '/' && (--n) == 0)) {
+            *d = '/';
+            break;
+        }
+        *d++ = *s++;
     }
     *++d = 0;
     return (d);
@@ -615,12 +575,11 @@ AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
     int l;
 
     if (last_slash == NULL) {
-       return apr_pstrdup(p, "");
+        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);
 }
 
@@ -630,8 +589,8 @@ AP_DECLARE(int) ap_count_dirs(const char *path)
     register int x, n;
 
     for (x = 0, n = 0; path[x]; x++)
-       if (path[x] == '/')
-           n++;
+        if (path[x] == '/')
+            n++;
     return n;
 }
 
@@ -642,21 +601,22 @@ AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
 
 AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
 {
-    const char *pos = ap_strchr_c(*line, stop);
+    const char *pos = *line;
+    int len;
     char *res;
 
-    if (!pos) {
-       res = apr_pstrdup(atrans, *line);
-       *line += strlen(*line);
-       return res;
+    while ((*pos != stop) && *pos) {
+        ++pos;
     }
 
-    res = apr_pstrndup(atrans, *line, pos - *line);
+    len = pos - *line;
+    res = apr_pstrmemdup(atrans, *line, len);
 
-    while (*pos == stop) {
-       ++pos;
+    if (stop) {
+        while (*pos == stop) {
+            ++pos;
+        }
     }
-
     *line = pos;
 
     return res;
@@ -669,47 +629,42 @@ AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
 
 AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
 {
-    int pos = -1, x;
+    const char *pos = *line;
+    int len;
     char *res;
 
-    for (x = 0; (*line)[x]; x++) {
-       if (apr_isspace((*line)[x])) {
-           pos = x;
-           break;
-       }
-    }
-
-    if (pos == -1) {
-       res = apr_pstrdup(atrans, *line);
-       *line += strlen(*line);
-       return res;
+    while (!apr_isspace(*pos) && *pos) {
+        ++pos;
     }
 
-    res = apr_palloc(atrans, pos + 1);
-    apr_cpystrn(res, *line, pos + 1);
+    len = pos - *line;
+    res = apr_pstrmemdup(atrans, *line, len);
 
-    while (apr_isspace((*line)[pos]))
-       ++pos;
+    while (apr_isspace(*pos)) {
+        ++pos;
+    }
 
-    *line += pos;
+    *line = pos;
 
     return res;
 }
 
-AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line, char stop)
+AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
+                                       char stop)
 {
     return ap_getword_nulls(atrans, (const char **) line, stop);
 }
 
-AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line, char stop)
+AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
+                                    char stop)
 {
     const char *pos = ap_strchr_c(*line, stop);
     char *res;
 
     if (!pos) {
-       res = apr_pstrdup(atrans, *line);
-       *line += strlen(*line);
-       return res;
+        res = apr_pstrdup(atrans, *line);
+        *line += strlen(*line);
+        return res;
     }
 
     res = apr_pstrndup(atrans, *line, pos - *line);
@@ -725,23 +680,24 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line, char
  * all honored
  */
 
-static char *substring_conf(apr_pool_t *p, const char *start, int len, char quote)
+static char *substring_conf(apr_pool_t *p, const char *start, int len,
+                            char quote)
 {
     char *result = apr_palloc(p, len + 2);
     char *resp = result;
     int i;
 
     for (i = 0; i < len; ++i) {
-       if (start[i] == '\\' && (start[i + 1] == '\\'
-                                || (quote && start[i + 1] == quote)))
-           *resp++ = start[++i];
-       else
-           *resp++ = start[i];
+        if (start[i] == '\\' && (start[i + 1] == '\\'
+                                 || (quote && start[i + 1] == quote)))
+            *resp++ = start[++i];
+        else
+            *resp++ = start[i];
     }
 
     *resp++ = '\0';
 #if RESOLVE_ENV_PER_TOKEN
-    return ap_resolve_env(p,result);
+    return (char *)ap_resolve_env(p,result);
 #else
     return result;
 #endif
@@ -759,36 +715,39 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     char quote;
 
     while (*str && apr_isspace(*str))
-       ++str;
+        ++str;
 
     if (!*str) {
-       *line = str;
-       return "";
+        *line = str;
+        return "";
     }
 
     if ((quote = *str) == '"' || quote == '\'') {
-       strend = str + 1;
-       while (*strend && *strend != quote) {
-           if (*strend == '\\' && strend[1] && strend[1] == quote)
-               strend += 2;
-           else
-               ++strend;
-       }
-       res = substring_conf(p, str + 1, strend - str - 1, quote);
-
-       if (*strend == quote)
-           ++strend;
+        strend = str + 1;
+        while (*strend && *strend != quote) {
+            if (*strend == '\\' && strend[1] &&
+                (strend[1] == quote || strend[1] == '\\')) {
+                strend += 2;
+            }
+            else {
+                ++strend;
+            }
+        }
+        res = substring_conf(p, str + 1, strend - str - 1, quote);
+
+        if (*strend == quote)
+            ++strend;
     }
     else {
-       strend = str;
-       while (*strend && !apr_isspace(*strend))
-           ++strend;
+        strend = str;
+        while (*strend && !apr_isspace(*strend))
+            ++strend;
 
-       res = substring_conf(p, str, strend - str, 0);
+        res = substring_conf(p, str, strend - str, 0);
     }
 
     while (*strend && apr_isspace(*strend))
-       ++strend;
+        ++strend;
     *line = strend;
     return res;
 }
@@ -801,41 +760,91 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
  */
 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);
+# define SMALL_EXPANSION 5
+    struct sll {
+        struct sll *next;
+        const char *string;
+        apr_size_t len;
+    } *result, *current, sresult[SMALL_EXPANSION];
+    char *res_buf, *cp;
+    const char *s, *e, *ep;
+    unsigned spc;
+    apr_size_t outlen;
+
+    s = ap_strchr_c(word, '$');
+    if (!s) {
+        return word;
+    }
+
+    /* well, actually something to do */
+    ep = word + strlen(word);
+    spc = 0;
+    result = current = &(sresult[spc++]);
+    current->next = NULL;
+    current->string = word;
+    current->len = s - word;
+    outlen = current->len;
+
+    do {
+        /* prepare next entry */
+        if (current->len) {
+            current->next = (spc < SMALL_EXPANSION)
+                            ? &(sresult[spc++])
+                            : (struct sll *)apr_palloc(p,
+                                                       sizeof(*current->next));
+            current = current->next;
+            current->next = NULL;
+            current->len = 0;
+        }
+
+        if (*s == '$') {
+            if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
+                word = getenv(apr_pstrndup(p, s+2, e-s-2));
+                if (word) {
+                    current->string = word;
+                    current->len = strlen(word);
+                    outlen += current->len;
+                }
+                else {
+                    current->string = s;
+                    current->len = e - s + 1;
+                    outlen += current->len;
+                }
+                s = e + 1;
+            }
+            else {
+                current->string = s++;
+                current->len = 1;
+                ++outlen;
+            }
+        }
+        else {
+            word = s;
+            s = ap_strchr_c(s, '$');
+            current->string = word;
+            current->len = s ? s - word : ep - word;
+            outlen += current->len;
+        }
+    } while (s && *s);
+
+    /* assemble result */
+    res_buf = cp = apr_palloc(p, outlen + 1);
+    do {
+        if (result->len) {
+            memcpy(cp, result->string, result->len);
+            cp += result->len;
+        }
+        result = result->next;
+    } while (result);
+    res_buf[outlen] = '\0';
+
+    return res_buf;
 }
+
 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
 {
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
         "Done with config file %s", cfp->name);
 #endif
     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
@@ -861,13 +870,15 @@ static void *cfg_getstr(void *buf, 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, "")))
+    if (rv == APR_SUCCESS) {
         return buf;
+    }
     return NULL;
 }
 
 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
-AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, apr_pool_t *p, const char *name)
+AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
+                                          apr_pool_t *p, const char *name)
 {
     ap_configfile_t *new_cfg;
     apr_file_t *file = NULL;
@@ -878,16 +889,17 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, apr_pool_t
 #endif
 
     if (name == NULL) {
-        ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, NULL,
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                "Internal error: pcfg_openfile() called with NULL filename");
         return APR_EBADF;
     }
 
-    status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, APR_OS_DEFAULT, p);
+    status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
+                           APR_OS_DEFAULT, p);
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, NULL,
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                 "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)
@@ -899,17 +911,40 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, apr_pool_t
 
     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 | APLOG_NOERRNO, 0, NULL,
-                    "Access to file %s denied by server: not a regular file",
-                    name);
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                     "Access to file %s denied by server: not a regular file",
+                     name);
         apr_file_close(file);
         return APR_EBADF;
     }
 
+#ifdef WIN32
+    /* Some twisted character [no pun intended] at MS decided that a
+     * zero width joiner as the lead wide character would be ideal for
+     * describing Unicode text files.  This was further convoluted to
+     * another MSism that the same character mapped into utf-8, EF BB BF
+     * 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
+     * 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)
+              || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
+            apr_off_t zero = 0;
+            apr_file_seek(file, APR_SET, &zero);
+        }
+    }
+#endif
+
     new_cfg = apr_palloc(p, sizeof(*new_cfg));
     new_cfg->param = file;
     new_cfg->name = apr_pstrdup(p, name);
@@ -923,15 +958,17 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, apr_pool_t
 
 
 /* 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,
+                       int(*getch)(void *param),
+                       void *(*getstr) (void *buf, size_t bufsiz, void *param),
+                       int(*close_func)(void *param))
 {
     ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, NULL, "Opening config handler %s", descr);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "Opening config handler %s", descr);
 #endif
     new_cfg->param = param;
     new_cfg->name = descr;
@@ -941,148 +978,149 @@ AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p, const char *des
     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)
 {
     register int ch = cfp->getch(cfp->param);
-    if (ch == LF) 
+    if (ch == LF)
         ++cfp->line_number;
     return ch;
 }
-  
+
 /* 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)
 {
     /* 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;
-
-       while (1) {
-           ++cfp->line_number;
-           if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
-               return 1;
-
-           /*
-            *  check for line continuation,
-            *  i.e. match [^\\]\\[\r]\n only
-            */
-           cp = cbuf;
-           while (cp < cbuf+cbufsize && *cp != '\0')
-               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];
-                   }   
-               }
-           }
-           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';
+        char *src, *dst;
+        char *cp;
+        char *cbuf = buf;
+        size_t cbufsize = bufsize;
+
+        while (1) {
+            ++cfp->line_number;
+            if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
+                return 1;
+
+            /*
+             *  check for line continuation,
+             *  i.e. match [^\\]\\[\r]\n only
+             */
+            cp = cbuf;
+            while (cp < cbuf+cbufsize && *cp != '\0')
+                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];
+                    }
+                }
+            }
+            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'; )
-               ;
+            for (dst = buf; (*dst++ = *src++) != '\0'; )
+                ;
 
 #ifdef DEBUG_CFG_LINES
-       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "Read config: %s", buf);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
 #endif
-       return 0;
+        return 0;
     } else {
-       /* No "get string" function defined; read character by character */
-       register int c;
-       register 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) {
-           /* too small, assume caller is crazy */
-           return 1;
-       }
-
-       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);
-           }
-           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
-                */
-               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
-                    */
-               }
-               /* blast trailing whitespace */
-               while (i > 0 && apr_isspace(buf[i - 1]))
-                   --i;
-               buf[i] = '\0';
+        /* No "get string" function defined; read character by character */
+        register int c;
+        register 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) {
+            /* too small, assume caller is crazy */
+            return 1;
+        }
+
+        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);
+            }
+            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
+                 */
+                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
+                     */
+                }
+                /* 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|APLOG_NOERRNO, 0, NULL, "Read config: %s", buf);
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                             "Read config: %s", buf);
 #endif
-               return 0;
-           }
-           buf[i] = c;
-           ++i;
-           c = cfp->getch(cfp->param);
-       }
+                return 0;
+            }
+            buf[i] = c;
+            ++i;
+            c = cfp->getch(cfp->param);
+        }
     }
 }
 
@@ -1140,7 +1178,7 @@ AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
     /* Advance field pointer to the next non-comma, non-white byte */
 
     while (*ptr == ',' || apr_isspace(*ptr))
-       ++ptr;
+        ++ptr;
 
     *field = (const char *)ptr;
     return (const char *)token;
@@ -1241,7 +1279,8 @@ AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
  * 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)
+AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
+                                  const char *tok)
 {
     const unsigned char *pos;
     const unsigned char *ptr = (const unsigned char *)line;
@@ -1344,7 +1383,8 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line, const char *t
  * by whitespace at the caller's option.
  */
 
-AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, int accept_white)
+AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
+                                int accept_white)
 {
     const char *ptr = *accept_line;
     const char *tok_start;
@@ -1354,7 +1394,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, int acc
     /* Find first non-white byte */
 
     while (*ptr && apr_isspace(*ptr))
-       ++ptr;
+        ++ptr;
 
     tok_start = ptr;
 
@@ -1363,11 +1403,11 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, int acc
      */
 
     while (*ptr && (accept_white || !apr_isspace(*ptr))
-          && *ptr != ';' && *ptr != ',') {
-       if (*ptr++ == '"')
-           while (*ptr)
-               if (*ptr++ == '"')
-                   break;
+           && *ptr != ';' && *ptr != ',') {
+        if (*ptr++ == '"')
+            while (*ptr)
+                if (*ptr++ == '"')
+                    break;
     }
 
     tok_len = ptr - tok_start;
@@ -1376,7 +1416,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, int acc
     /* Advance accept_line pointer to the next non-white byte */
 
     while (*ptr && apr_isspace(*ptr))
-       ++ptr;
+        ++ptr;
 
     *accept_line = ptr;
     return token;
@@ -1390,48 +1430,50 @@ AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
     const unsigned char *s;
 
     if (!line)
-       return 0;
+        return 0;
 
     s = (const unsigned char *)line;
     for (;;) {
-       /* find start of token, skip all stop characters, note NUL
-        * isn't a token stop, so we don't need to test for it
-        */
-       while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
-           ++s;
-       }
-       if (!*s) {
-           return 0;
-       }
-       start_token = s;
-       /* find end of the token */
-       while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
-           ++s;
-       }
-       if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) {
-           return 1;
-       }
-       if (!*s) {
-           return 0;
-       }
-    }
-}
-
-
-AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, const char *tok)
+        /* find start of token, skip all stop characters, note NUL
+         * isn't a token stop, so we don't need to test for it
+         */
+        while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+            ++s;
+        }
+        if (!*s) {
+            return 0;
+        }
+        start_token = s;
+        /* find end of the token */
+        while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+            ++s;
+        }
+        if (!strncasecmp((const char *)start_token, (const char *)tok,
+                         s - start_token)) {
+            return 1;
+        }
+        if (!*s) {
+            return 0;
+        }
+    }
+}
+
+
+AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
+                                   const char *tok)
 {
     int llen, tlen, lidx;
 
     if (!line)
-       return 0;
+        return 0;
 
     llen = strlen(line);
     tlen = strlen(tok);
     lidx = llen - tlen;
 
-    if ((lidx < 0) ||
-       ((lidx > 0) && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
-       return 0;
+    if (lidx < 0 ||
+        (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
+        return 0;
 
     return (strncasecmp(&line[lidx], tok, tlen) == 0);
 }
@@ -1442,24 +1484,27 @@ AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
     unsigned char *d;
     const unsigned char *s;
 
-    cmd = apr_palloc(p, 2 * strlen(str) + 1);  /* Be safe */
+    cmd = apr_palloc(p, 2 * strlen(str) + 1);        /* Be safe */
     d = (unsigned char *)cmd;
     s = (const unsigned char *)str;
     for (; *s; ++s) {
 
 #if defined(OS2) || defined(WIN32)
-       /* Don't allow '&' in parameters under OS/2. */
-       /* This can be used to send commands to the shell. */
-       if (*s == '&') {
-           *d++ = ' ';
-           continue;
-       }
+        /*
+         * Newlines to Win32/OS2 CreateProcess() are ill advised.
+         * Convert them to spaces since they are effectively white
+         * space to most applications
+         */
+        if (*s == '\r' || *s == '\n') {
+             *d++ = ' ';
+             continue;
+         }
 #endif
 
-       if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
-           *d++ = '\\';
-       }
-       *d++ = *s;
+        if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
+            *d++ = '\\';
+        }
+        *d++ = *s;
     }
     *d = '\0';
 
@@ -1471,9 +1516,11 @@ static char x2c(const char *what)
     register char digit;
 
 #if !APR_CHARSET_EBCDIC
-    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
+             : (what[0] - '0'));
     digit *= 16;
-    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
+              : (what[1] - '0'));
 #else /*APR_CHARSET_EBCDIC*/
     char xstr[5];
     xstr[0]='0';
@@ -1481,22 +1528,22 @@ static char x2c(const char *what)
     xstr[2]=what[0];
     xstr[3]=what[1];
     xstr[4]='\0';
-    digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFF & strtol(xstr, NULL, 16));
+    digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
+                                0xFF & strtol(xstr, NULL, 16));
 #endif /*APR_CHARSET_EBCDIC*/
     return (digit);
 }
 
 /*
- * 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
- *   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;
@@ -1510,40 +1557,93 @@ AP_DECLARE(int) ap_unescape_url(char *url)
         return OK;
     }
     for (x = y; *y; ++x, ++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 (*x == '/' || *x == '\0')
-                   badpath = 1;
-           }
-       }
+        if (*y != '%') {
+            *x = *y;
+        }
+        else {
+            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
+                badesc = 1;
+                *x = '%';
+            }
+            else {
+                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)
-       return HTTP_BAD_REQUEST;
-    else if (badpath)
-       return HTTP_NOT_FOUND;
-    else
-       return OK;
+    if (badesc) {
+        return HTTP_BAD_REQUEST;
+    }
+    else if (badpath) {
+        return HTTP_NOT_FOUND;
+    }
+    else {
+        return OK;
+    }
+}
+AP_DECLARE(int) ap_unescape_url(char *url)
+{
+    /* Traditional */
+#ifdef CASE_BLIND_FILESYSTEM
+    return unescape_url(url, "/\\", NULL);
+#else
+    return unescape_url(url, "/", NULL);
+#endif
+}
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
+{
+    /* AllowEncodedSlashes (corrected) */
+    return unescape_url(url, NULL, "/");
 }
+#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(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
-                                   apr_port_t port, const request_rec *r)
+                                       apr_port_t port, const request_rec *r)
 {
-    if (ap_is_default_port(port, r))
-       return apr_pstrdup(p, hostname);
+    if (ap_is_default_port(port, r)) {
+        return apr_pstrdup(p, hostname);
+    }
     else {
-       return apr_psprintf(p, "%s:%u", hostname, port);
+        return apr_psprintf(p, "%s:%u", hostname, port);
     }
 }
 
+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
@@ -1556,12 +1656,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;
@@ -1582,26 +1683,30 @@ 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);
-       }
-       else {
-           *d++ = c;
-       }
-       ++s;
+        if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
+            d = c2x(c, '%', d);
+        }
+        else {
+            *d++ = c;
+        }
+        ++s;
     }
     *d = '\0';
     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);
@@ -1610,22 +1715,22 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
     unsigned c;
 
     if (!partial) {
-       const char *colon = ap_strchr_c(path, ':');
-       const char *slash = ap_strchr_c(path, '/');
+        const char *colon = ap_strchr_c(path, ':');
+        const char *slash = ap_strchr_c(path, '/');
 
-       if (colon && (!slash || colon < slash)) {
-           *d++ = '.';
-           *d++ = '/';
-       }
+        if (colon && (!slash || colon < slash)) {
+            *d++ = '.';
+            *d++ = '/';
+        }
     }
     while ((c = *s)) {
-       if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
-           d = c2x(c, d);
-       }
-       else {
-           *d++ = c;
-       }
-       ++s;
+        if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
+            d = c2x(c, '%', d);
+        }
+        else {
+            *d++ = c;
+        }
+        ++s;
     }
     *d = '\0';
     return copy;
@@ -1633,48 +1738,174 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
 
 /* 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;
 
     /* first, count the number of extra characters */
     for (i = 0, j = 0; s[i] != '\0'; i++)
-       if (s[i] == '<' || s[i] == '>')
-           j += 3;
-       else if (s[i] == '&')
-           j += 4;
+        if (s[i] == '<' || s[i] == '>')
+            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_pstrndup(p, s, i);
+        return apr_pstrmemdup(p, s, i);
 
     x = apr_palloc(p, i + j + 1);
     for (i = 0, j = 0; s[i] != '\0'; i++, j++)
-       if (s[i] == '<') {
-           memcpy(&x[j], "&lt;", 4);
-           j += 3;
-       }
-       else if (s[i] == '>') {
-           memcpy(&x[j], "&gt;", 4);
-           j += 3;
-       }
-       else if (s[i] == '&') {
-           memcpy(&x[j], "&amp;", 5);
-           j += 4;
-       }
-       else
-           x[j] = s[i];
+        if (s[i] == '<') {
+            memcpy(&x[j], "&lt;", 4);
+            j += 3;
+        }
+        else if (s[i] == '>') {
+            memcpy(&x[j], "&gt;", 4);
+            j += 3;
+        }
+        else if (s[i] == '&') {
+            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;
+
+    if (!str) {
+        return NULL;
+    }
+
+    ret = apr_palloc(p, 4 * strlen(str) + 1); /* Be safe */
+    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(int) ap_is_directory(apr_pool_t *p, const char *path)
 {
     apr_finfo_t finfo;
 
     if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
-       return 0;               /* in error condition, just return no */
+        return 0;                /* in error condition, just return no */
 
     return (finfo.filetype == APR_DIR);
 }
@@ -1683,21 +1914,24 @@ 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)
-       return 0;               /* in error condition, just return no */
+    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);
 }
 
 AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
-                                 const char *src2)
+                                  const char *src2)
 {
     apr_size_t len1, len2;
     char *path;
 
     len1 = strlen(src1);
     len2 = strlen(src2);
-    path = (char *)apr_palloc(a, len1 + len2 + 2); /* +2 for '/' plus null */
+     /* allocate +3 for '/' delimiter, trailing NULL and overallocate
+      * one extra byte to allow the caller to add a trailing '/'
+      */
+    path = (char *)apr_palloc(a, len1 + len2 + 3);
     if (len1 == 0) {
         *path = '/';
         memcpy(path + 1, src2, len2 + 1);
@@ -1722,14 +1956,14 @@ AP_DECLARE(int) ap_is_url(const char *u)
     register int x;
 
     for (x = 0; u[x] != ':'; x++) {
-       if ((!u[x]) ||
-           ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) &&
-            (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
-           return 0;
-       }
+        if ((!u[x]) ||
+            ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) &&
+             (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
+            return 0;
+        }
     }
 
-    return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
+    return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
 }
 
 AP_DECLARE(int) ap_ind(const char *s, char c)
@@ -1753,26 +1987,14 @@ AP_DECLARE(int) ap_rind(const char *s, char c)
 AP_DECLARE(void) ap_str_tolower(char *str)
 {
     while (*str) {
-       *str = apr_tolower(*str);
-       ++str;
+        *str = apr_tolower(*str);
+        ++str;
     }
 }
 
-static char *find_fqdn(apr_pool_t *a, struct hostent *p)
-{
-    int x;
-
-    if (!strchr(p->h_name, '.')) {
-       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;
-    }
-    return apr_pstrdup(a, (void *) p->h_name);
-}
-
+/*
+ * We must return a FQDN
+ */
 char *ap_get_local_host(apr_pool_t *a)
 {
 #ifndef MAXHOSTNAMELEN
@@ -1780,44 +2002,41 @@ 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
-    {
+    if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
         ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
-                     "%s: gethostname() failed to determine ServerName",
+                     "%s: apr_gethostname() failed to determine ServerName",
                      ap_server_argv0);
-    }
-    else 
-    {
+    } else {
         str[sizeof(str) - 1] = '\0';
-        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]);
-               server_hostname = apr_pstrdup(a, str);
-                /* We will drop through to report the IP-named server */
+        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);
+            } 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,
+                         "%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_NOERRNO|APLOG_STARTUP, 0, a,
-                 "%s: Could not determine the server's fully qualified "
+    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
+                 "%s: Could not reliably determine the server's fully qualified "
                  "domain name, using %s for ServerName",
                  ap_server_argv0, server_hostname);
-             
+
     return server_hostname;
 }
 
@@ -1835,8 +2054,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);
 
@@ -1860,21 +2079,20 @@ AP_DECLARE(void) ap_content_type_tolower(char *str)
 
     semi = strchr(str, ';');
     if (semi) {
-       *semi = '\0';
-    }
-    while (*str) {
-       *str = apr_tolower(*str);
-       ++str;
+        *semi = '\0';
     }
+
+    ap_str_tolower(str);
+
     if (semi) {
-       *semi = ';';
+        *semi = ';';
     }
 }
 
 /*
  * Given a string, replace any bare " with \" .
  */
-AP_DECLARE(char *) ap_escape_quotes (apr_pool_t *p, const char *instring)
+AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
 {
     int newlen = 0;
     const char *inchr = instring;
@@ -1885,19 +2103,19 @@ AP_DECLARE(char *) ap_escape_quotes (apr_pool_t *p, const char *instring)
      * string up by an extra byte each time we find an unescaped ".
      */
     while (*inchr != '\0') {
-       newlen++;
+        newlen++;
         if (*inchr == '"') {
-           newlen++;
-       }
-       /*
-        * If we find a slosh, and it's not the last byte in the string,
-        * it's escaping something - advance past both bytes.
-        */
-       if ((*inchr == '\\') && (inchr[1] != '\0')) {
-           inchr++;
-           newlen++;
-       }
-       inchr++;
+            newlen++;
+        }
+        /*
+         * If we find a slosh, and it's not the last byte in the string,
+         * it's escaping something - advance past both bytes.
+         */
+        if ((*inchr == '\\') && (inchr[1] != '\0')) {
+            inchr++;
+            newlen++;
+        }
+        inchr++;
     }
     outstring = apr_palloc(p, newlen + 1);
     inchr = instring;
@@ -1907,17 +2125,99 @@ AP_DECLARE(char *) ap_escape_quotes (apr_pool_t *p, const char *instring)
      * in front of every " that doesn't already have one.
      */
     while (*inchr != '\0') {
-       if ((*inchr == '\\') && (inchr[1] != '\0')) {
-           *outchr++ = *inchr++;
-           *outchr++ = *inchr++;
-       }
-       if (*inchr == '"') {
-           *outchr++ = '\\';
-       }
-       if (*inchr != '\0') {
-           *outchr++ = *inchr++;
-       }
+        if ((*inchr == '\\') && (inchr[1] != '\0')) {
+            *outchr++ = *inchr++;
+            *outchr++ = *inchr++;
+        }
+        if (*inchr == '"') {
+            *outchr++ = '\\';
+        }
+        if (*inchr != '\0') {
+            *outchr++ = *inchr++;
+        }
     }
     *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;
+}
+