Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / libxml / uri.c
index 1e29223..ff47abb 100644 (file)
@@ -1,7 +1,7 @@
 /**
- * uri.c: set of generic URI related routines 
+ * uri.c: set of generic URI related routines
  *
- * Reference: RFCs 2396, 2732 and 2373
+ * Reference: RFCs 3986, 2732 and 2373
  *
  * See Copyright for the status of this software.
  *
 #include <libxml/globals.h>
 #include <libxml/xmlerror.h>
 
-/************************************************************************
- *                                                                     *
- *             Macros to differentiate various character type          *
- *                     directly extracted from RFC 2396                *
- *                                                                     *
- ************************************************************************/
+/**
+ * MAX_URI_LENGTH:
+ *
+ * The definition of the URI regexp in the above RFC has no size limit
+ * In practice they are usually relativey short except for the
+ * data URI scheme as defined in RFC 2397. Even for data URI the usual
+ * maximum size before hitting random practical limits is around 64 KB
+ * and 4KB is usually a maximum admitted limit for proper operations.
+ * The value below is more a security limit than anything else and
+ * really should never be hit by 'normal' operations
+ * Set to 1 MByte in 2012, this is only enforced on output
+ */
+#define MAX_URI_LENGTH 1024 * 1024
+
+static void
+xmlURIErrMemory(const char *extra)
+{
+    if (extra)
+        __xmlRaiseError(NULL, NULL, NULL,
+                        NULL, NULL, XML_FROM_URI,
+                        XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
+                        extra, NULL, NULL, 0, 0,
+                        "Memory allocation failed : %s\n", extra);
+    else
+        __xmlRaiseError(NULL, NULL, NULL,
+                        NULL, NULL, XML_FROM_URI,
+                        XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
+                        NULL, NULL, NULL, 0, 0,
+                        "Memory allocation failed\n");
+}
+
+static void xmlCleanURI(xmlURIPtr uri);
 
 /*
+ * Old rule from 2396 used in legacy handling code
  * alpha    = lowalpha | upalpha
  */
 #define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x))
 #define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x))
 
 /*
- * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
- *               "a" | "b" | "c" | "d" | "e" | "f"
- */
-
-#define IS_HEX(x) ((IS_DIGIT(x)) || (((x) >= 'a') && ((x) <= 'f')) || \
-           (((x) >= 'A') && ((x) <= 'F')))
-
-/*
  * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
  */
 
-#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') ||    \
-    ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') ||   \
+#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') ||     \
+    ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') ||    \
     ((x) == '(') || ((x) == ')'))
 
+/*
+ * unwise = "{" | "}" | "|" | "\" | "^" | "`"
+ */
 
+#define IS_UNWISE(p)                                                    \
+      (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) ||         \
+       ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) ||        \
+       ((*(p) == ']')) || ((*(p) == '`')))
 /*
  * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
- *           "[" | "]"
+ *            "[" | "]"
  */
 
-#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') ||        \
-        ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') ||        \
-       ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
-       ((x) == ']'))
+#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \
+        ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \
+        ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
+        ((x) == ']'))
 
 /*
  * unreserved = alphanum | mark
 #define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x))
 
 /*
- * escaped = "%" hex hex
+ * Skip to next pointer char, handle escaped sequences
  */
 
-#define IS_ESCAPED(p) ((*(p) == '%') && (IS_HEX((p)[1])) &&            \
-           (IS_HEX((p)[2])))
+#define NEXT(p) ((*p == '%')? p += 3 : p++)
 
 /*
- * uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
- *                        "&" | "=" | "+" | "$" | ","
+ * Productions from the spec.
+ *
+ *    authority     = server | reg_name
+ *    reg_name      = 1*( unreserved | escaped | "$" | "," |
+ *                        ";" | ":" | "@" | "&" | "=" | "+" )
+ *
+ * path          = [ abs_path | opaque_part ]
  */
-#define IS_URIC_NO_SLASH(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||\
-               ((*(p) == ';')) || ((*(p) == '?')) || ((*(p) == ':')) ||\
-               ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) ||\
-               ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ',')))
+
+#define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n))
+
+/************************************************************************
+ *                                                                     *
+ *                         RFC 3986 parser                             *
+ *                                                                     *
+ ************************************************************************/
+
+#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9'))
+#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) ||              \
+                      ((*(p) >= 'A') && (*(p) <= 'Z')))
+#define ISA_HEXDIG(p)                                                  \
+       (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) ||            \
+        ((*(p) >= 'A') && (*(p) <= 'F')))
 
 /*
- * pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+" | "$" | ","
+ *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
+ *                     / "*" / "+" / "," / ";" / "="
  */
-#define IS_PCHAR(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||       \
-               ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||\
-               ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||\
-               ((*(p) == ',')))
+#define ISA_SUB_DELIM(p)                                               \
+      (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) ||                \
+       ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) ||                \
+       ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) ||                \
+       ((*(p) == '=')) || ((*(p) == '\'')))
 
 /*
- * rel_segment   = 1*( unreserved | escaped |
- *                 ";" | "@" | "&" | "=" | "+" | "$" | "," )
+ *    gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
  */
-
-#define IS_SEGMENT(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||     \
-          ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||     \
-         ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||      \
-         ((*(p) == ',')))
+#define ISA_GEN_DELIM(p)                                               \
+      (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) ||         \
+       ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) ||         \
+       ((*(p) == '@')))
 
 /*
- * scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ *    reserved      = gen-delims / sub-delims
  */
-
-#define IS_SCHEME(x) ((IS_ALPHA(x)) || (IS_DIGIT(x)) ||                        \
-                     ((x) == '+') || ((x) == '-') || ((x) == '.'))
+#define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p)))
 
 /*
- * reg_name = 1*( unreserved | escaped | "$" | "," |
- *                ";" | ":" | "@" | "&" | "=" | "+" )
+ *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
  */
-
-#define IS_REG_NAME(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||    \
-       ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) ||                \
-       ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||                \
-       ((*(p) == '=')) || ((*(p) == '+')))
+#define ISA_UNRESERVED(p)                                              \
+      ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) ||          \
+       ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~')))
 
 /*
- * userinfo = *( unreserved | escaped | ";" | ":" | "&" | "=" |
- *                      "+" | "$" | "," )
+ *    pct-encoded   = "%" HEXDIG HEXDIG
  */
-#define IS_USERINFO(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||    \
-       ((*(p) == ';')) || ((*(p) == ':')) || ((*(p) == '&')) ||                \
-       ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||                \
-       ((*(p) == ',')))
+#define ISA_PCT_ENCODED(p)                                             \
+     ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2)))
 
 /*
- * uric = reserved | unreserved | escaped
+ *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
  */
+#define ISA_PCHAR(p)                                                   \
+     (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) ||   \
+      ((*(p) == ':')) || ((*(p) == '@')))
 
-#define IS_URIC(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||                \
-                   (IS_RESERVED(*(p))))
+/**
+ * xmlParse3986Scheme:
+ * @uri:  pointer to an URI structure
+ * @str:  pointer to the string to analyze
+ *
+ * Parse an URI scheme
+ *
+ * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986Scheme(xmlURIPtr uri, const char **str) {
+    const char *cur;
 
-/*                                                                              
-* unwise = "{" | "}" | "|" | "\" | "^" | "`"
-*/                                                                             
+    if (str == NULL)
+       return(-1);
 
-#define IS_UNWISE(p)                                                    \
-      (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) ||         \
-       ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) ||        \
-       ((*(p) == ']')) || ((*(p) == '`')))  
+    cur = *str;
+    if (!ISA_ALPHA(cur))
+       return(2);
+    cur++;
+    while (ISA_ALPHA(cur) || ISA_DIGIT(cur) ||
+           (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++;
+    if (uri != NULL) {
+       if (uri->scheme != NULL) xmlFree(uri->scheme);
+       uri->scheme = STRNDUP(*str, cur - *str);
+    }
+    *str = cur;
+    return(0);
+}
 
-/*
- * Skip to next pointer char, handle escaped sequences
+/**
+ * xmlParse3986Fragment:
+ * @uri:  pointer to an URI structure
+ * @str:  pointer to the string to analyze
+ *
+ * Parse the query part of an URI
+ *
+ * fragment      = *( pchar / "/" / "?" )
+ * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']'
+ *       in the fragment identifier but this is used very broadly for
+ *       xpointer scheme selection, so we are allowing it here to not break
+ *       for example all the DocBook processing chains.
+ *
+ * Returns 0 or the error code
  */
+static int
+xmlParse3986Fragment(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
 
-#define NEXT(p) ((*p == '%')? p += 3 : p++)
+    if (str == NULL)
+        return (-1);
 
-/*
- * Productions from the spec.
+    cur = *str;
+
+    while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
+           (*cur == '[') || (*cur == ']') ||
+           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
+        NEXT(cur);
+    if (uri != NULL) {
+        if (uri->fragment != NULL)
+            xmlFree(uri->fragment);
+       if (uri->cleanup & 2)
+           uri->fragment = STRNDUP(*str, cur - *str);
+       else
+           uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL);
+    }
+    *str = cur;
+    return (0);
+}
+
+/**
+ * xmlParse3986Query:
+ * @uri:  pointer to an URI structure
+ * @str:  pointer to the string to analyze
  *
- *    authority     = server | reg_name
- *    reg_name      = 1*( unreserved | escaped | "$" | "," |
- *                        ";" | ":" | "@" | "&" | "=" | "+" )
+ * Parse the query part of an URI
  *
- * path          = [ abs_path | opaque_part ]
+ * query = *uric
+ *
+ * Returns 0 or the error code
  */
+static int
+xmlParse3986Query(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
 
-#define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n))
+    if (str == NULL)
+        return (-1);
 
-/************************************************************************
- *                                                                     *
- *                     Generic URI structure functions                 *
- *                                                                     *
- ************************************************************************/
+    cur = *str;
+
+    while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
+           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
+        NEXT(cur);
+    if (uri != NULL) {
+        if (uri->query != NULL)
+            xmlFree(uri->query);
+       if (uri->cleanup & 2)
+           uri->query = STRNDUP(*str, cur - *str);
+       else
+           uri->query = xmlURIUnescapeString(*str, cur - *str, NULL);
+
+       /* Save the raw bytes of the query as well.
+        * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114
+        */
+       if (uri->query_raw != NULL)
+           xmlFree (uri->query_raw);
+       uri->query_raw = STRNDUP (*str, cur - *str);
+    }
+    *str = cur;
+    return (0);
+}
 
 /**
- * xmlCreateURI:
+ * xmlParse3986Port:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Simply creates an empty xmlURI
+ * Parse a port  part and fills in the appropriate fields
+ * of the @uri structure
  *
- * Returns the new structure or NULL in case of error
+ * port          = *DIGIT
+ *
+ * Returns 0 or the error code
  */
-xmlURIPtr
-xmlCreateURI(void) {
-    xmlURIPtr ret;
+static int
+xmlParse3986Port(xmlURIPtr uri, const char **str)
+{
+    const char *cur = *str;
 
-    ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI));
-    if (ret == NULL) {
-       xmlGenericError(xmlGenericErrorContext,
-               "xmlCreateURI: out of memory\n");
-       return(NULL);
+    if (ISA_DIGIT(cur)) {
+       if (uri != NULL)
+           uri->port = 0;
+       while (ISA_DIGIT(cur)) {
+           if (uri != NULL)
+               uri->port = uri->port * 10 + (*cur - '0');
+           cur++;
+       }
+       *str = cur;
+       return(0);
     }
-    memset(ret, 0, sizeof(xmlURI));
-    return(ret);
+    return(1);
 }
 
 /**
- * xmlSaveUri:
- * @uri:  pointer to an xmlURI
+ * xmlParse3986Userinfo:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Save the URI as an escaped string
+ * Parse an user informations part and fills in the appropriate fields
+ * of the @uri structure
  *
- * Returns a new string (to be deallocated by caller)
+ * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
+ *
+ * Returns 0 or the error code
  */
-xmlChar *
-xmlSaveUri(xmlURIPtr uri) {
-    xmlChar *ret = NULL;
-    const char *p;
-    int len;
-    int max;
+static int
+xmlParse3986Userinfo(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
 
-    if (uri == NULL) return(NULL);
+    cur = *str;
+    while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) ||
+           ISA_SUB_DELIM(cur) || (*cur == ':'))
+       NEXT(cur);
+    if (*cur == '@') {
+       if (uri != NULL) {
+           if (uri->user != NULL) xmlFree(uri->user);
+           if (uri->cleanup & 2)
+               uri->user = STRNDUP(*str, cur - *str);
+           else
+               uri->user = xmlURIUnescapeString(*str, cur - *str, NULL);
+       }
+       *str = cur;
+       return(0);
+    }
+    return(1);
+}
 
+/**
+ * xmlParse3986DecOctet:
+ * @str:  the string to analyze
+ *
+ *    dec-octet     = DIGIT                 ; 0-9
+ *                  / %x31-39 DIGIT         ; 10-99
+ *                  / "1" 2DIGIT            ; 100-199
+ *                  / "2" %x30-34 DIGIT     ; 200-249
+ *                  / "25" %x30-35          ; 250-255
+ *
+ * Skip a dec-octet.
+ *
+ * Returns 0 if found and skipped, 1 otherwise
+ */
+static int
+xmlParse3986DecOctet(const char **str) {
+    const char *cur = *str;
 
-    max = 80;
-    ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar));
-    if (ret == NULL) {
-       xmlGenericError(xmlGenericErrorContext,
-               "xmlSaveUri: out of memory\n");
-       return(NULL);
-    }
-    len = 0;
+    if (!(ISA_DIGIT(cur)))
+        return(1);
+    if (!ISA_DIGIT(cur+1))
+       cur++;
+    else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur+2)))
+       cur += 2;
+    else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2)))
+       cur += 3;
+    else if ((*cur == '2') && (*(cur + 1) >= '0') &&
+            (*(cur + 1) <= '4') && (ISA_DIGIT(cur + 2)))
+       cur += 3;
+    else if ((*cur == '2') && (*(cur + 1) == '5') &&
+            (*(cur + 2) >= '0') && (*(cur + 1) <= '5'))
+       cur += 3;
+    else
+        return(1);
+    *str = cur;
+    return(0);
+}
+/**
+ * xmlParse3986Host:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an host part and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * host          = IP-literal / IPv4address / reg-name
+ * IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"
+ * IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
+ * reg-name      = *( unreserved / pct-encoded / sub-delims )
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986Host(xmlURIPtr uri, const char **str)
+{
+    const char *cur = *str;
+    const char *host;
 
-    if (uri->scheme != NULL) {
-       p = uri->scheme;
-       while (*p != 0) {
-           if (len >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           ret[len++] = *p++;
-       }
-       if (len >= max) {
-           max *= 2;
-           ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-           if (ret == NULL) {
-               xmlGenericError(xmlGenericErrorContext,
-                       "xmlSaveUri: out of memory\n");
-               return(NULL);
-           }
-       }
-       ret[len++] = ':';
+    host = cur;
+    /*
+     * IPv6 and future adressing scheme are enclosed between brackets
+     */
+    if (*cur == '[') {
+        cur++;
+       while ((*cur != ']') && (*cur != 0))
+           cur++;
+       if (*cur != ']')
+           return(1);
+       cur++;
+       goto found;
     }
-    if (uri->opaque != NULL) {
-       p = uri->opaque;
-       while (*p != 0) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p)))
-               ret[len++] = *p++;
-           else {
-               int val = *(unsigned char *)p++;
-               int hi = val / 0x10, lo = val % 0x10;
-               ret[len++] = '%';
-               ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-               ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-           }
-       }
-    } else {
-       if (uri->server != NULL) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           ret[len++] = '/';
-           ret[len++] = '/';
-           if (uri->user != NULL) {
-               p = uri->user;
-               while (*p != 0) {
-                   if (len + 3 >= max) {
-                       max *= 2;
-                       ret = (xmlChar *) xmlRealloc(ret,
-                               (max + 1) * sizeof(xmlChar));
-                       if (ret == NULL) {
-                           xmlGenericError(xmlGenericErrorContext,
-                                   "xmlSaveUri: out of memory\n");
-                           return(NULL);
-                       }
-                   }
-                   if ((IS_UNRESERVED(*(p))) ||
-                       ((*(p) == ';')) || ((*(p) == ':')) ||
-                       ((*(p) == '&')) || ((*(p) == '=')) ||
-                       ((*(p) == '+')) || ((*(p) == '$')) ||
-                       ((*(p) == ',')))
-                       ret[len++] = *p++;
-                   else {
-                       int val = *(unsigned char *)p++;
-                       int hi = val / 0x10, lo = val % 0x10;
-                       ret[len++] = '%';
-                       ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-                       ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-                   }
-               }
-               if (len + 3 >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               ret[len++] = '@';
-           }
-           p = uri->server;
-           while (*p != 0) {
-               if (len >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               ret[len++] = *p++;
-           }
-           if (uri->port > 0) {
-               if (len + 10 >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
-           }
-       } else if (uri->authority != NULL) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret,
-                       (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           ret[len++] = '/';
-           ret[len++] = '/';
-           p = uri->authority;
-           while (*p != 0) {
-               if (len + 3 >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               if ((IS_UNRESERVED(*(p))) ||
-                    ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) ||
-                    ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||
-                    ((*(p) == '=')) || ((*(p) == '+')))
-                   ret[len++] = *p++;
-               else {
-                   int val = *(unsigned char *)p++;
-                   int hi = val / 0x10, lo = val % 0x10;
-                   ret[len++] = '%';
-                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-               }
-           }
-       } else if (uri->scheme != NULL) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret,
-                       (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           ret[len++] = '/';
-           ret[len++] = '/';
-       }
-       if (uri->path != NULL) {
-           p = uri->path;
-           while (*p != 0) {
-               if (len + 3 >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) ||
-                    ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||
-                   ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||
-                   ((*(p) == ',')))
-                   ret[len++] = *p++;
-               else {
-                   int val = *(unsigned char *)p++;
-                   int hi = val / 0x10, lo = val % 0x10;
-                   ret[len++] = '%';
-                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-               }
-           }
-       }
-       if (uri->query != NULL) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret,
-                       (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           ret[len++] = '?';
-           p = uri->query;
-           while (*p != 0) {
-               if (len + 3 >= max) {
-                   max *= 2;
-                   ret = (xmlChar *) xmlRealloc(ret,
-                           (max + 1) * sizeof(xmlChar));
-                   if (ret == NULL) {
-                       xmlGenericError(xmlGenericErrorContext,
-                               "xmlSaveUri: out of memory\n");
-                       return(NULL);
-                   }
-               }
-               if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) 
-                   ret[len++] = *p++;
-               else {
-                   int val = *(unsigned char *)p++;
-                   int hi = val / 0x10, lo = val % 0x10;
-                   ret[len++] = '%';
-                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-               }
-           }
-       }
-    }
-    if (uri->fragment != NULL) {
-       if (len + 3 >= max) {
-           max *= 2;
-           ret = (xmlChar *) xmlRealloc(ret,
-                   (max + 1) * sizeof(xmlChar));
-           if (ret == NULL) {
-               xmlGenericError(xmlGenericErrorContext,
-                       "xmlSaveUri: out of memory\n");
-               return(NULL);
-           }
-       }
-       ret[len++] = '#';
-       p = uri->fragment;
-       while (*p != 0) {
-           if (len + 3 >= max) {
-               max *= 2;
-               ret = (xmlChar *) xmlRealloc(ret,
-                       (max + 1) * sizeof(xmlChar));
-               if (ret == NULL) {
-                   xmlGenericError(xmlGenericErrorContext,
-                           "xmlSaveUri: out of memory\n");
-                   return(NULL);
-               }
-           }
-           if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) 
-               ret[len++] = *p++;
-           else {
-               int val = *(unsigned char *)p++;
-               int hi = val / 0x10, lo = val % 0x10;
-               ret[len++] = '%';
-               ret[len++] = hi + (hi > 9? 'A'-10 : '0');
-               ret[len++] = lo + (lo > 9? 'A'-10 : '0');
-           }
-       }
-    }
-    if (len >= max) {
-       max *= 2;
-       ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-       if (ret == NULL) {
-           xmlGenericError(xmlGenericErrorContext,
-                   "xmlSaveUri: out of memory\n");
-           return(NULL);
-       }
-    }
-    ret[len++] = 0;
-    return(ret);
-}
-
-/**
- * xmlPrintURI:
- * @stream:  a FILE* for the output
- * @uri:  pointer to an xmlURI
- *
- * Prints the URI in the stream @stream.
- */
-void
-xmlPrintURI(FILE *stream, xmlURIPtr uri) {
-    xmlChar *out;
-
-    out = xmlSaveUri(uri);
-    if (out != NULL) {
-       fprintf(stream, "%s", (char *) out);
-       xmlFree(out);
-    }
-}
-
-/**
- * xmlCleanURI:
- * @uri:  pointer to an xmlURI
- *
- * Make sure the xmlURI struct is free of content
- */
-static void
-xmlCleanURI(xmlURIPtr uri) {
-    if (uri == NULL) return;
-
-    if (uri->scheme != NULL) xmlFree(uri->scheme);
-    uri->scheme = NULL;
-    if (uri->server != NULL) xmlFree(uri->server);
-    uri->server = NULL;
-    if (uri->user != NULL) xmlFree(uri->user);
-    uri->user = NULL;
-    if (uri->path != NULL) xmlFree(uri->path);
-    uri->path = NULL;
-    if (uri->fragment != NULL) xmlFree(uri->fragment);
-    uri->fragment = NULL;
-    if (uri->opaque != NULL) xmlFree(uri->opaque);
-    uri->opaque = NULL;
-    if (uri->authority != NULL) xmlFree(uri->authority);
-    uri->authority = NULL;
-    if (uri->query != NULL) xmlFree(uri->query);
-    uri->query = NULL;
-}
-
-/**
- * xmlFreeURI:
- * @uri:  pointer to an xmlURI
- *
- * Free up the xmlURI struct
- */
-void
-xmlFreeURI(xmlURIPtr uri) {
-    if (uri == NULL) return;
-
-    if (uri->scheme != NULL) xmlFree(uri->scheme);
-    if (uri->server != NULL) xmlFree(uri->server);
-    if (uri->user != NULL) xmlFree(uri->user);
-    if (uri->path != NULL) xmlFree(uri->path);
-    if (uri->fragment != NULL) xmlFree(uri->fragment);
-    if (uri->opaque != NULL) xmlFree(uri->opaque);
-    if (uri->authority != NULL) xmlFree(uri->authority);
-    if (uri->query != NULL) xmlFree(uri->query);
-    xmlFree(uri);
-}
-
-/************************************************************************
- *                                                                     *
- *                     Helper functions                                *
- *                                                                     *
- ************************************************************************/
-
-/**
- * xmlNormalizeURIPath:
- * @path:  pointer to the path string
- *
- * Applies the 5 normalization steps to a path string--that is, RFC 2396
- * Section 5.2, steps 6.c through 6.g.
- *
- * Normalization occurs directly on the string, no new allocation is done
- *
- * Returns 0 or an error code
- */
-int
-xmlNormalizeURIPath(char *path) {
-    char *cur, *out;
-
-    if (path == NULL)
-       return(-1);
-
-    /* Skip all initial "/" chars.  We want to get to the beginning of the
-     * first non-empty segment.
-     */
-    cur = path;
-    while (cur[0] == '/')
-      ++cur;
-    if (cur[0] == '\0')
-      return(0);
-
-    /* Keep everything we've seen so far.  */
-    out = cur;
-
-    /*
-     * Analyze each segment in sequence for cases (c) and (d).
-     */
-    while (cur[0] != '\0') {
-       /*
-        * c) All occurrences of "./", where "." is a complete path segment,
-        *    are removed from the buffer string.
-        */
-       if ((cur[0] == '.') && (cur[1] == '/')) {
-           cur += 2;
-           /* '//' normalization should be done at this point too */
-           while (cur[0] == '/')
-               cur++;
-           continue;
-       }
-
-       /*
-        * d) If the buffer string ends with "." as a complete path segment,
-        *    that "." is removed.
-        */
-       if ((cur[0] == '.') && (cur[1] == '\0'))
-           break;
-
-       /* Otherwise keep the segment.  */
-       while (cur[0] != '/') {
-            if (cur[0] == '\0')
-              goto done_cd;
-           (out++)[0] = (cur++)[0];
-       }
-       /* nomalize // */
-       while ((cur[0] == '/') && (cur[1] == '/'))
-           cur++;
-
-        (out++)[0] = (cur++)[0];
-    }
- done_cd:
-    out[0] = '\0';
-
-    /* Reset to the beginning of the first segment for the next sequence.  */
-    cur = path;
-    while (cur[0] == '/')
-      ++cur;
-    if (cur[0] == '\0')
-       return(0);
-
-    /*
-     * Analyze each segment in sequence for cases (e) and (f).
-     *
-     * e) All occurrences of "<segment>/../", where <segment> is a
-     *    complete path segment not equal to "..", are removed from the
-     *    buffer string.  Removal of these path segments is performed
-     *    iteratively, removing the leftmost matching pattern on each
-     *    iteration, until no matching pattern remains.
-     *
-     * f) If the buffer string ends with "<segment>/..", where <segment>
-     *    is a complete path segment not equal to "..", that
-     *    "<segment>/.." is removed.
-     *
-     * To satisfy the "iterative" clause in (e), we need to collapse the
-     * string every time we find something that needs to be removed.  Thus,
-     * we don't need to keep two pointers into the string: we only need a
-     * "current position" pointer.
-     */
-    while (1) {
-        char *segp, *tmp;
-
-        /* At the beginning of each iteration of this loop, "cur" points to
-         * the first character of the segment we want to examine.
-         */
-
-        /* Find the end of the current segment.  */
-        segp = cur;
-        while ((segp[0] != '/') && (segp[0] != '\0'))
-          ++segp;
-
-        /* If this is the last segment, we're done (we need at least two
-         * segments to meet the criteria for the (e) and (f) cases).
-         */
-        if (segp[0] == '\0')
-          break;
-
-        /* If the first segment is "..", or if the next segment _isn't_ "..",
-         * keep this segment and try the next one.
-         */
-        ++segp;
-        if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3))
-            || ((segp[0] != '.') || (segp[1] != '.')
-                || ((segp[2] != '/') && (segp[2] != '\0')))) {
-          cur = segp;
-          continue;
-        }
-
-        /* If we get here, remove this segment and the next one and back up
-         * to the previous segment (if there is one), to implement the
-         * "iteratively" clause.  It's pretty much impossible to back up
-         * while maintaining two pointers into the buffer, so just compact
-         * the whole buffer now.
-         */
-
-        /* If this is the end of the buffer, we're done.  */
-        if (segp[2] == '\0') {
-          cur[0] = '\0';
-          break;
-        }
-        /* Valgrind complained, strcpy(cur, segp + 3); */
-       /* string will overlap, do not use strcpy */
-       tmp = cur;
-       segp += 3;
-       while ((*tmp++ = *segp++) != 0);
-
-        /* If there are no previous segments, then keep going from here.  */
-        segp = cur;
-        while ((segp > path) && ((--segp)[0] == '/'))
-          ;
-        if (segp == path)
-          continue;
-
-        /* "segp" is pointing to the end of a previous segment; find it's
-         * start.  We need to back up to the previous segment and start
-         * over with that to handle things like "foo/bar/../..".  If we
-         * don't do this, then on the first pass we'll remove the "bar/..",
-         * but be pointing at the second ".." so we won't realize we can also
-         * remove the "foo/..".
-         */
-        cur = segp;
-        while ((cur > path) && (cur[-1] != '/'))
-          --cur;
+    /*
+     * try to parse an IPv4
+     */
+    if (ISA_DIGIT(cur)) {
+        if (xmlParse3986DecOctet(&cur) != 0)
+           goto not_ipv4;
+       if (*cur != '.')
+           goto not_ipv4;
+       cur++;
+        if (xmlParse3986DecOctet(&cur) != 0)
+           goto not_ipv4;
+       if (*cur != '.')
+           goto not_ipv4;
+        if (xmlParse3986DecOctet(&cur) != 0)
+           goto not_ipv4;
+       if (*cur != '.')
+           goto not_ipv4;
+        if (xmlParse3986DecOctet(&cur) != 0)
+           goto not_ipv4;
+       goto found;
+not_ipv4:
+        cur = *str;
     }
-    out[0] = '\0';
-
     /*
-     * g) If the resulting buffer string still begins with one or more
-     *    complete path segments of "..", then the reference is
-     *    considered to be in error. Implementations may handle this
-     *    error by retaining these components in the resolved path (i.e.,
-     *    treating them as part of the final URI), by removing them from
-     *    the resolved path (i.e., discarding relative levels above the
-     *    root), or by avoiding traversal of the reference.
-     *
-     * We discard them from the final path.
+     * then this should be a hostname which can be empty
      */
-    if (path[0] == '/') {
-      cur = path;
-      while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.')
-             && ((cur[3] == '/') || (cur[3] == '\0')))
-       cur += 3;
-
-      if (cur != path) {
-       out = path;
-       while (cur[0] != '\0')
-          (out++)[0] = (cur++)[0];
-       out[0] = 0;
-      }
+    while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur))
+        NEXT(cur);
+found:
+    if (uri != NULL) {
+       if (uri->authority != NULL) xmlFree(uri->authority);
+       uri->authority = NULL;
+       if (uri->server != NULL) xmlFree(uri->server);
+       if (cur != host) {
+           if (uri->cleanup & 2)
+               uri->server = STRNDUP(host, cur - host);
+           else
+               uri->server = xmlURIUnescapeString(host, cur - host, NULL);
+       } else
+           uri->server = NULL;
     }
-
+    *str = cur;
     return(0);
 }
 
-static int is_hex(char c) {
-    if (((c >= '0') && (c <= '9')) ||
-        ((c >= 'a') && (c <= 'f')) ||
-        ((c >= 'A') && (c <= 'F')))
-       return(1);
+/**
+ * xmlParse3986Authority:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an authority part and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * authority     = [ userinfo "@" ] host [ ":" port ]
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986Authority(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
+
+    cur = *str;
+    /*
+     * try to parse an userinfo and check for the trailing @
+     */
+    ret = xmlParse3986Userinfo(uri, &cur);
+    if ((ret != 0) || (*cur != '@'))
+        cur = *str;
+    else
+        cur++;
+    ret = xmlParse3986Host(uri, &cur);
+    if (ret != 0) return(ret);
+    if (*cur == ':') {
+        cur++;
+        ret = xmlParse3986Port(uri, &cur);
+       if (ret != 0) return(ret);
+    }
+    *str = cur;
     return(0);
 }
 
 /**
- * xmlURIUnescapeString:
- * @str:  the string to unescape
- * @len:   the length in bytes to unescape (or <= 0 to indicate full string)
- * @target:  optional destination buffer
+ * xmlParse3986Segment:
+ * @str:  the string to analyze
+ * @forbid: an optional forbidden character
+ * @empty: allow an empty segment
+ *
+ * Parse a segment and fills in the appropriate fields
+ * of the @uri structure
  *
- * Unescaping routine, does not do validity checks !
- * Output is direct unsigned char translation of %XX values (no encoding)
+ * segment       = *pchar
+ * segment-nz    = 1*pchar
+ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ *               ; non-zero-length segment without any colon ":"
  *
- * Returns an copy of the string, but unescaped
+ * Returns 0 or the error code
  */
-char *
-xmlURIUnescapeString(const char *str, int len, char *target) {
-    char *ret, *out;
-    const char *in;
-
-    if (str == NULL)
-       return(NULL);
-    if (len <= 0) len = strlen(str);
-    if (len < 0) return(NULL);
+static int
+xmlParse3986Segment(const char **str, char forbid, int empty)
+{
+    const char *cur;
 
-    if (target == NULL) {
-       ret = (char *) xmlMallocAtomic(len + 1);
-       if (ret == NULL) {
-           xmlGenericError(xmlGenericErrorContext,
-                   "xmlURIUnescapeString: out of memory\n");
-           return(NULL);
-       }
-    } else
-       ret = target;
-    in = str;
-    out = ret;
-    while(len > 0) {
-       if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) {
-           in++;
-           if ((*in >= '0') && (*in <= '9')) 
-               *out = (*in - '0');
-           else if ((*in >= 'a') && (*in <= 'f'))
-               *out = (*in - 'a') + 10;
-           else if ((*in >= 'A') && (*in <= 'F'))
-               *out = (*in - 'A') + 10;
-           in++;
-           if ((*in >= '0') && (*in <= '9')) 
-               *out = *out * 16 + (*in - '0');
-           else if ((*in >= 'a') && (*in <= 'f'))
-               *out = *out * 16 + (*in - 'a') + 10;
-           else if ((*in >= 'A') && (*in <= 'F'))
-               *out = *out * 16 + (*in - 'A') + 10;
-           in++;
-           len -= 3;
-           out++;
-       } else {
-           *out++ = *in++;
-           len--;
-       }
+    cur = *str;
+    if (!ISA_PCHAR(cur)) {
+        if (empty)
+           return(0);
+       return(1);
     }
-    *out = 0;
-    return(ret);
+    while (ISA_PCHAR(cur) && (*cur != forbid))
+        NEXT(cur);
+    *str = cur;
+    return (0);
 }
 
 /**
- * xmlURIEscapeStr:
- * @str:  string to escape
- * @list: exception list string of chars not to escape
+ * xmlParse3986PathAbEmpty:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * This routine escapes a string to hex, ignoring reserved characters (a-z)
- * and the characters in the exception list.
+ * Parse an path absolute or empty and fills in the appropriate fields
+ * of the @uri structure
  *
- * Returns a new escaped string or NULL in case of error.
+ * path-abempty  = *( "/" segment )
+ *
+ * Returns 0 or the error code
  */
-xmlChar *
-xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) {
-    xmlChar *ret, ch;
-    const xmlChar *in;
-
-    unsigned int len, out;
+static int
+xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
-    if (str == NULL)
-       return(NULL);
-    if (str[0] == 0)
-       return(xmlStrdup(str));
-    len = xmlStrlen(str);
-    if (!(len > 0)) return(NULL);
+    cur = *str;
 
-    len += 20;
-    ret = (xmlChar *) xmlMallocAtomic(len);
-    if (ret == NULL) {
-       xmlGenericError(xmlGenericErrorContext,
-               "xmlURIEscapeStr: out of memory\n");
-       return(NULL);
+    while (*cur == '/') {
+        cur++;
+       ret = xmlParse3986Segment(&cur, 0, 1);
+       if (ret != 0) return(ret);
     }
-    in = (const xmlChar *) str;
-    out = 0;
-    while(*in != 0) {
-       if (len - out <= 3) {
-           len += 20;
-           ret = (xmlChar *) xmlRealloc(ret, len);
-           if (ret == NULL) {
-               xmlGenericError(xmlGenericErrorContext,
-                       "xmlURIEscapeStr: out of memory\n");
-               return(NULL);
-           }
-       }
+    if (uri != NULL) {
+       if (uri->path != NULL) xmlFree(uri->path);
+        if (*str != cur) {
+            if (uri->cleanup & 2)
+                uri->path = STRNDUP(*str, cur - *str);
+            else
+                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
+        } else {
+            uri->path = NULL;
+        }
+    }
+    *str = cur;
+    return (0);
+}
 
-       ch = *in;
+/**
+ * xmlParse3986PathAbsolute:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an path absolute and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
-       if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!xmlStrchr(list, ch))) {
-           unsigned char val;
-           ret[out++] = '%';
-           val = ch >> 4;
-           if (val <= 9)
-               ret[out++] = '0' + val;
-           else
-               ret[out++] = 'A' + val - 0xA;
-           val = ch & 0xF;
-           if (val <= 9)
-               ret[out++] = '0' + val;
-           else
-               ret[out++] = 'A' + val - 0xA;
-           in++;
-       } else {
-           ret[out++] = *in++;
+    cur = *str;
+
+    if (*cur != '/')
+        return(1);
+    cur++;
+    ret = xmlParse3986Segment(&cur, 0, 0);
+    if (ret == 0) {
+       while (*cur == '/') {
+           cur++;
+           ret = xmlParse3986Segment(&cur, 0, 1);
+           if (ret != 0) return(ret);
        }
+    }
+    if (uri != NULL) {
+       if (uri->path != NULL) xmlFree(uri->path);
+        if (cur != *str) {
+            if (uri->cleanup & 2)
+                uri->path = STRNDUP(*str, cur - *str);
+            else
+                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
+        } else {
+            uri->path = NULL;
+        }
+    }
+    *str = cur;
+    return (0);
+}
+
+/**
+ * xmlParse3986PathRootless:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an path without root and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * path-rootless = segment-nz *( "/" segment )
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986PathRootless(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
+    cur = *str;
+
+    ret = xmlParse3986Segment(&cur, 0, 0);
+    if (ret != 0) return(ret);
+    while (*cur == '/') {
+        cur++;
+       ret = xmlParse3986Segment(&cur, 0, 1);
+       if (ret != 0) return(ret);
     }
-    ret[out] = 0;
-    return(ret);
+    if (uri != NULL) {
+       if (uri->path != NULL) xmlFree(uri->path);
+        if (cur != *str) {
+            if (uri->cleanup & 2)
+                uri->path = STRNDUP(*str, cur - *str);
+            else
+                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
+        } else {
+            uri->path = NULL;
+        }
+    }
+    *str = cur;
+    return (0);
 }
 
 /**
- * xmlURIEscape:
- * @str:  the string of the URI to escape
+ * xmlParse3986PathNoScheme:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Escaping routine, does not do validity checks !
- * It will try to escape the chars needing this, but this is heuristic
- * based it's impossible to be sure.
+ * Parse an path which is not a scheme and fills in the appropriate fields
+ * of the @uri structure
  *
- * Returns an copy of the string, but escaped
+ * path-noscheme = segment-nz-nc *( "/" segment )
  *
- * 25 May 2001
- * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly
- * according to RFC2396.
- *   - Carl Douglas
+ * Returns 0 or the error code
  */
-xmlChar *
-xmlURIEscape(const xmlChar * str)
+static int
+xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str)
 {
-    xmlChar *ret, *segment = NULL;
-    xmlURIPtr uri;
-    int ret2;
-
-#define NULLCHK(p) if(!p) { \
-                   xmlGenericError(xmlGenericErrorContext, \
-                        "xmlURIEscape: out of memory\n"); \
-                   return NULL; }
+    const char *cur;
+    int ret;
 
-    if (str == NULL)
-        return (NULL);
+    cur = *str;
 
-    uri = xmlCreateURI();
+    ret = xmlParse3986Segment(&cur, ':', 0);
+    if (ret != 0) return(ret);
+    while (*cur == '/') {
+        cur++;
+       ret = xmlParse3986Segment(&cur, 0, 1);
+       if (ret != 0) return(ret);
+    }
     if (uri != NULL) {
-       /*
-        * Allow escaping errors in the unescaped form
-        */
-        uri->cleanup = 1;
-        ret2 = xmlParseURIReference(uri, (const char *)str);
-        if (ret2) {
-            xmlFreeURI(uri);
-            return (NULL);
+       if (uri->path != NULL) xmlFree(uri->path);
+        if (cur != *str) {
+            if (uri->cleanup & 2)
+                uri->path = STRNDUP(*str, cur - *str);
+            else
+                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
+        } else {
+            uri->path = NULL;
         }
     }
+    *str = cur;
+    return (0);
+}
 
-    if (!uri)
-        return NULL;
+/**
+ * xmlParse3986HierPart:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an hierarchical part and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * hier-part     = "//" authority path-abempty
+ *                / path-absolute
+ *                / path-rootless
+ *                / path-empty
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986HierPart(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
-    ret = NULL;
+    cur = *str;
 
-    if (uri->scheme) {
-        segment = xmlURIEscapeStr(BAD_CAST uri->scheme, BAD_CAST "+-.");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, segment);
-        ret = xmlStrcat(ret, BAD_CAST ":");
-        xmlFree(segment);
+    if ((*cur == '/') && (*(cur + 1) == '/')) {
+        cur += 2;
+       ret = xmlParse3986Authority(uri, &cur);
+       if (ret != 0) return(ret);
+       if (uri->server == NULL)
+           uri->port = -1;
+       ret = xmlParse3986PathAbEmpty(uri, &cur);
+       if (ret != 0) return(ret);
+       *str = cur;
+       return(0);
+    } else if (*cur == '/') {
+        ret = xmlParse3986PathAbsolute(uri, &cur);
+       if (ret != 0) return(ret);
+    } else if (ISA_PCHAR(cur)) {
+        ret = xmlParse3986PathRootless(uri, &cur);
+       if (ret != 0) return(ret);
+    } else {
+       /* path-empty is effectively empty */
+       if (uri != NULL) {
+           if (uri->path != NULL) xmlFree(uri->path);
+           uri->path = NULL;
+       }
     }
+    *str = cur;
+    return (0);
+}
 
-    if (uri->authority) {
-        segment =
-            xmlURIEscapeStr(BAD_CAST uri->authority, BAD_CAST "/?;:@");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, BAD_CAST "//");
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
-    }
+/**
+ * xmlParse3986RelativeRef:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an URI string and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
+ * relative-part = "//" authority path-abempty
+ *               / path-absolute
+ *               / path-noscheme
+ *               / path-empty
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) {
+    int ret;
 
-    if (uri->user) {
-        segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,");
-        NULLCHK(segment)
-               ret = xmlStrcat(ret,BAD_CAST "//");     
-        ret = xmlStrcat(ret, segment);
-        ret = xmlStrcat(ret, BAD_CAST "@");
-        xmlFree(segment);
+    if ((*str == '/') && (*(str + 1) == '/')) {
+        str += 2;
+       ret = xmlParse3986Authority(uri, &str);
+       if (ret != 0) return(ret);
+       ret = xmlParse3986PathAbEmpty(uri, &str);
+       if (ret != 0) return(ret);
+    } else if (*str == '/') {
+       ret = xmlParse3986PathAbsolute(uri, &str);
+       if (ret != 0) return(ret);
+    } else if (ISA_PCHAR(str)) {
+        ret = xmlParse3986PathNoScheme(uri, &str);
+       if (ret != 0) return(ret);
+    } else {
+       /* path-empty is effectively empty */
+       if (uri != NULL) {
+           if (uri->path != NULL) xmlFree(uri->path);
+           uri->path = NULL;
+       }
     }
 
-    if (uri->server) {
-        segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@");
-        NULLCHK(segment)
-               if (uri->user == NULL)
-                       ret = xmlStrcat(ret, BAD_CAST "//");
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+    if (*str == '?') {
+       str++;
+       ret = xmlParse3986Query(uri, &str);
+       if (ret != 0) return(ret);
+    }
+    if (*str == '#') {
+       str++;
+       ret = xmlParse3986Fragment(uri, &str);
+       if (ret != 0) return(ret);
+    }
+    if (*str != 0) {
+       xmlCleanURI(uri);
+       return(1);
     }
+    return(0);
+}
 
-    if (uri->port) {
-        xmlChar port[10];
 
-        snprintf((char *) port, 10, "%d", uri->port);
-        ret = xmlStrcat(ret, BAD_CAST ":");
-        ret = xmlStrcat(ret, port);
-    }
+/**
+ * xmlParse3986URI:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an URI string and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986URI(xmlURIPtr uri, const char *str) {
+    int ret;
 
-    if (uri->path) {
-        segment =
-            xmlURIEscapeStr(BAD_CAST uri->path, BAD_CAST ":@&=+$,/?;");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+    ret = xmlParse3986Scheme(uri, &str);
+    if (ret != 0) return(ret);
+    if (*str != ':') {
+       return(1);
     }
-
-    if (uri->query) {
-        segment =
-            xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, BAD_CAST "?");
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+    str++;
+    ret = xmlParse3986HierPart(uri, &str);
+    if (ret != 0) return(ret);
+    if (*str == '?') {
+       str++;
+       ret = xmlParse3986Query(uri, &str);
+       if (ret != 0) return(ret);
+    }
+    if (*str == '#') {
+       str++;
+       ret = xmlParse3986Fragment(uri, &str);
+       if (ret != 0) return(ret);
+    }
+    if (*str != 0) {
+       xmlCleanURI(uri);
+       return(1);
     }
+    return(0);
+}
 
-    if (uri->opaque) {
-        segment = xmlURIEscapeStr(BAD_CAST uri->opaque, BAD_CAST "");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+/**
+ * xmlParse3986URIReference:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an URI reference string and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * URI-reference = URI / relative-ref
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986URIReference(xmlURIPtr uri, const char *str) {
+    int ret;
+
+    if (str == NULL)
+       return(-1);
+    xmlCleanURI(uri);
+
+    /*
+     * Try first to parse absolute refs, then fallback to relative if
+     * it fails.
+     */
+    ret = xmlParse3986URI(uri, str);
+    if (ret != 0) {
+       xmlCleanURI(uri);
+        ret = xmlParse3986RelativeRef(uri, str);
+       if (ret != 0) {
+           xmlCleanURI(uri);
+           return(ret);
+       }
     }
+    return(0);
+}
 
-    if (uri->fragment) {
-        segment = xmlURIEscapeStr(BAD_CAST uri->fragment, BAD_CAST "#");
-        NULLCHK(segment)
-        ret = xmlStrcat(ret, BAD_CAST "#");
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+/**
+ * xmlParseURI:
+ * @str:  the URI string to analyze
+ *
+ * Parse an URI based on RFC 3986
+ *
+ * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ *
+ * Returns a newly built xmlURIPtr or NULL in case of error
+ */
+xmlURIPtr
+xmlParseURI(const char *str) {
+    xmlURIPtr uri;
+    int ret;
+
+    if (str == NULL)
+       return(NULL);
+    uri = xmlCreateURI();
+    if (uri != NULL) {
+       ret = xmlParse3986URIReference(uri, str);
+        if (ret) {
+           xmlFreeURI(uri);
+           return(NULL);
+       }
     }
+    return(uri);
+}
+
+/**
+ * xmlParseURIReference:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an URI reference string based on RFC 3986 and fills in the
+ * appropriate fields of the @uri structure
+ *
+ * URI-reference = URI / relative-ref
+ *
+ * Returns 0 or the error code
+ */
+int
+xmlParseURIReference(xmlURIPtr uri, const char *str) {
+    return(xmlParse3986URIReference(uri, str));
+}
 
-    xmlFreeURI(uri);
-#undef NULLCHK
+/**
+ * xmlParseURIRaw:
+ * @str:  the URI string to analyze
+ * @raw:  if 1 unescaping of URI pieces are disabled
+ *
+ * Parse an URI but allows to keep intact the original fragments.
+ *
+ * URI-reference = URI / relative-ref
+ *
+ * Returns a newly built xmlURIPtr or NULL in case of error
+ */
+xmlURIPtr
+xmlParseURIRaw(const char *str, int raw) {
+    xmlURIPtr uri;
+    int ret;
 
-    return (ret);
+    if (str == NULL)
+       return(NULL);
+    uri = xmlCreateURI();
+    if (uri != NULL) {
+        if (raw) {
+           uri->cleanup |= 2;
+       }
+       ret = xmlParseURIReference(uri, str);
+        if (ret) {
+           xmlFreeURI(uri);
+           return(NULL);
+       }
+    }
+    return(uri);
 }
 
 /************************************************************************
  *                                                                     *
- *                     Escaped URI parsing                             *
+ *                     Generic URI structure functions                 *
  *                                                                     *
  ************************************************************************/
 
 /**
- * xmlParseURIFragment:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
+ * xmlCreateURI:
  *
- * Parse an URI fragment string and fills in the appropriate fields
- * of the @uri structure.
- * 
- * fragment = *uric
+ * Simply creates an empty xmlURI
  *
- * Returns 0 or the error code
+ * Returns the new structure or NULL in case of error
  */
-static int
-xmlParseURIFragment(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
-    
-    if (str == NULL)
-        return (-1);
+xmlURIPtr
+xmlCreateURI(void) {
+    xmlURIPtr ret;
 
-    cur = *str;
+    ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI));
+    if (ret == NULL) {
+        xmlURIErrMemory("creating URI structure\n");
+       return(NULL);
+    }
+    memset(ret, 0, sizeof(xmlURI));
+    return(ret);
+}
 
-    while (IS_URIC(cur) || IS_UNWISE(cur))
-        NEXT(cur);
-    if (uri != NULL) {
-        if (uri->fragment != NULL)
-            xmlFree(uri->fragment);
-       if (uri->cleanup & 2)
-           uri->fragment = STRNDUP(*str, cur - *str);
-       else
-           uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL);
+/**
+ * xmlSaveUriRealloc:
+ *
+ * Function to handle properly a reallocation when saving an URI
+ * Also imposes some limit on the length of an URI string output
+ */
+static xmlChar *
+xmlSaveUriRealloc(xmlChar *ret, int *max) {
+    xmlChar *temp;
+    int tmp;
+
+    if (*max > MAX_URI_LENGTH) {
+        xmlURIErrMemory("reaching arbitrary MAX_URI_LENGTH limit\n");
+        return(NULL);
     }
-    *str = cur;
-    return (0);
+    tmp = *max * 2;
+    temp = (xmlChar *) xmlRealloc(ret, (tmp + 1));
+    if (temp == NULL) {
+        xmlURIErrMemory("saving URI\n");
+        return(NULL);
+    }
+    *max = tmp;
+    return(temp);
 }
 
 /**
- * xmlParseURIQuery:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
+ * xmlSaveUri:
+ * @uri:  pointer to an xmlURI
  *
- * Parse the query part of an URI
- * 
- * query = *uric
+ * Save the URI as an escaped string
  *
- * Returns 0 or the error code
+ * Returns a new string (to be deallocated by caller)
  */
-static int
-xmlParseURIQuery(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
+xmlChar *
+xmlSaveUri(xmlURIPtr uri) {
+    xmlChar *ret = NULL;
+    xmlChar *temp;
+    const char *p;
+    int len;
+    int max;
+
+    if (uri == NULL) return(NULL);
+
+
+    max = 80;
+    ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar));
+    if (ret == NULL) {
+        xmlURIErrMemory("saving URI\n");
+       return(NULL);
+    }
+    len = 0;
+
+    if (uri->scheme != NULL) {
+       p = uri->scheme;
+       while (*p != 0) {
+           if (len >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+               ret = temp;
+           }
+           ret[len++] = *p++;
+       }
+       if (len >= max) {
+            temp = xmlSaveUriRealloc(ret, &max);
+            if (temp == NULL) goto mem_error;
+            ret = temp;
+       }
+       ret[len++] = ':';
+    }
+    if (uri->opaque != NULL) {
+       p = uri->opaque;
+       while (*p != 0) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p)))
+               ret[len++] = *p++;
+           else {
+               int val = *(unsigned char *)p++;
+               int hi = val / 0x10, lo = val % 0x10;
+               ret[len++] = '%';
+               ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+               ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+           }
+       }
+    } else {
+       if ((uri->server != NULL) || (uri->port == -1)) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           ret[len++] = '/';
+           ret[len++] = '/';
+           if (uri->user != NULL) {
+               p = uri->user;
+               while (*p != 0) {
+                   if (len + 3 >= max) {
+                        temp = xmlSaveUriRealloc(ret, &max);
+                        if (temp == NULL) goto mem_error;
+                        ret = temp;
+                   }
+                   if ((IS_UNRESERVED(*(p))) ||
+                       ((*(p) == ';')) || ((*(p) == ':')) ||
+                       ((*(p) == '&')) || ((*(p) == '=')) ||
+                       ((*(p) == '+')) || ((*(p) == '$')) ||
+                       ((*(p) == ',')))
+                       ret[len++] = *p++;
+                   else {
+                       int val = *(unsigned char *)p++;
+                       int hi = val / 0x10, lo = val % 0x10;
+                       ret[len++] = '%';
+                       ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+                       ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+                   }
+               }
+               if (len + 3 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               ret[len++] = '@';
+           }
+           if (uri->server != NULL) {
+               p = uri->server;
+               while (*p != 0) {
+                   if (len >= max) {
+                       temp = xmlSaveUriRealloc(ret, &max);
+                       if (temp == NULL) goto mem_error;
+                       ret = temp;
+                   }
+                   ret[len++] = *p++;
+               }
+               if (uri->port > 0) {
+                   if (len + 10 >= max) {
+                       temp = xmlSaveUriRealloc(ret, &max);
+                       if (temp == NULL) goto mem_error;
+                       ret = temp;
+                   }
+                   len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
+               }
+           }
+       } else if (uri->authority != NULL) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           ret[len++] = '/';
+           ret[len++] = '/';
+           p = uri->authority;
+           while (*p != 0) {
+               if (len + 3 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               if ((IS_UNRESERVED(*(p))) ||
+                    ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) ||
+                    ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||
+                    ((*(p) == '=')) || ((*(p) == '+')))
+                   ret[len++] = *p++;
+               else {
+                   int val = *(unsigned char *)p++;
+                   int hi = val / 0x10, lo = val % 0x10;
+                   ret[len++] = '%';
+                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+               }
+           }
+       } else if (uri->scheme != NULL) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+       }
+       if (uri->path != NULL) {
+           p = uri->path;
+           /*
+            * the colon in file:///d: should not be escaped or
+            * Windows accesses fail later.
+            */
+           if ((uri->scheme != NULL) &&
+               (p[0] == '/') &&
+               (((p[1] >= 'a') && (p[1] <= 'z')) ||
+                ((p[1] >= 'A') && (p[1] <= 'Z'))) &&
+               (p[2] == ':') &&
+               (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
+               if (len + 3 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               ret[len++] = *p++;
+               ret[len++] = *p++;
+               ret[len++] = *p++;
+           }
+           while (*p != 0) {
+               if (len + 3 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) ||
+                    ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||
+                   ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||
+                   ((*(p) == ',')))
+                   ret[len++] = *p++;
+               else {
+                   int val = *(unsigned char *)p++;
+                   int hi = val / 0x10, lo = val % 0x10;
+                   ret[len++] = '%';
+                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+               }
+           }
+       }
+       if (uri->query_raw != NULL) {
+           if (len + 1 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           ret[len++] = '?';
+           p = uri->query_raw;
+           while (*p != 0) {
+               if (len + 1 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               ret[len++] = *p++;
+           }
+       } else if (uri->query != NULL) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           ret[len++] = '?';
+           p = uri->query;
+           while (*p != 0) {
+               if (len + 3 >= max) {
+                    temp = xmlSaveUriRealloc(ret, &max);
+                    if (temp == NULL) goto mem_error;
+                    ret = temp;
+               }
+               if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
+                   ret[len++] = *p++;
+               else {
+                   int val = *(unsigned char *)p++;
+                   int hi = val / 0x10, lo = val % 0x10;
+                   ret[len++] = '%';
+                   ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+                   ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+               }
+           }
+       }
+    }
+    if (uri->fragment != NULL) {
+       if (len + 3 >= max) {
+            temp = xmlSaveUriRealloc(ret, &max);
+            if (temp == NULL) goto mem_error;
+            ret = temp;
+       }
+       ret[len++] = '#';
+       p = uri->fragment;
+       while (*p != 0) {
+           if (len + 3 >= max) {
+                temp = xmlSaveUriRealloc(ret, &max);
+                if (temp == NULL) goto mem_error;
+                ret = temp;
+           }
+           if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
+               ret[len++] = *p++;
+           else {
+               int val = *(unsigned char *)p++;
+               int hi = val / 0x10, lo = val % 0x10;
+               ret[len++] = '%';
+               ret[len++] = hi + (hi > 9? 'A'-10 : '0');
+               ret[len++] = lo + (lo > 9? 'A'-10 : '0');
+           }
+       }
+    }
+    if (len >= max) {
+        temp = xmlSaveUriRealloc(ret, &max);
+        if (temp == NULL) goto mem_error;
+        ret = temp;
+    }
+    ret[len] = 0;
+    return(ret);
 
-    if (str == NULL)
-        return (-1);
+mem_error:
+    xmlFree(ret);
+    return(NULL);
+}
 
-    cur = *str;
+/**
+ * xmlPrintURI:
+ * @stream:  a FILE* for the output
+ * @uri:  pointer to an xmlURI
+ *
+ * Prints the URI in the stream @stream.
+ */
+void
+xmlPrintURI(FILE *stream, xmlURIPtr uri) {
+    xmlChar *out;
 
-    while ((IS_URIC(cur)) ||
-           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
-        NEXT(cur);
-    if (uri != NULL) {
-        if (uri->query != NULL)
-            xmlFree(uri->query);
-       if (uri->cleanup & 2)
-           uri->query = STRNDUP(*str, cur - *str);
-       else
-           uri->query = xmlURIUnescapeString(*str, cur - *str, NULL);
+    out = xmlSaveUri(uri);
+    if (out != NULL) {
+       fprintf(stream, "%s", (char *) out);
+       xmlFree(out);
     }
-    *str = cur;
-    return (0);
 }
 
 /**
- * xmlParseURIScheme:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI scheme
- * 
- * scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ * xmlCleanURI:
+ * @uri:  pointer to an xmlURI
  *
- * Returns 0 or the error code
+ * Make sure the xmlURI struct is free of content
  */
-static int
-xmlParseURIScheme(xmlURIPtr uri, const char **str) {
-    const char *cur;
+static void
+xmlCleanURI(xmlURIPtr uri) {
+    if (uri == NULL) return;
 
-    if (str == NULL)
-       return(-1);
-    
-    cur = *str;
-    if (!IS_ALPHA(*cur))
-       return(2);
-    cur++;
-    while (IS_SCHEME(*cur)) cur++;
-    if (uri != NULL) {
-       if (uri->scheme != NULL) xmlFree(uri->scheme);
-       uri->scheme = STRNDUP(*str, cur - *str);
-    }
-    *str = cur;
-    return(0);
+    if (uri->scheme != NULL) xmlFree(uri->scheme);
+    uri->scheme = NULL;
+    if (uri->server != NULL) xmlFree(uri->server);
+    uri->server = NULL;
+    if (uri->user != NULL) xmlFree(uri->user);
+    uri->user = NULL;
+    if (uri->path != NULL) xmlFree(uri->path);
+    uri->path = NULL;
+    if (uri->fragment != NULL) xmlFree(uri->fragment);
+    uri->fragment = NULL;
+    if (uri->opaque != NULL) xmlFree(uri->opaque);
+    uri->opaque = NULL;
+    if (uri->authority != NULL) xmlFree(uri->authority);
+    uri->authority = NULL;
+    if (uri->query != NULL) xmlFree(uri->query);
+    uri->query = NULL;
+    if (uri->query_raw != NULL) xmlFree(uri->query_raw);
+    uri->query_raw = NULL;
 }
 
 /**
- * xmlParseURIOpaquePart:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI opaque part
- * 
- * opaque_part = uric_no_slash *uric
+ * xmlFreeURI:
+ * @uri:  pointer to an xmlURI
  *
- * Returns 0 or the error code
+ * Free up the xmlURI struct
  */
-static int
-xmlParseURIOpaquePart(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
-
-    if (str == NULL)
-        return (-1);
+void
+xmlFreeURI(xmlURIPtr uri) {
+    if (uri == NULL) return;
 
-    cur = *str;
-    if (!((IS_URIC_NO_SLASH(cur)) ||
-          ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))) {
-        return (3);
-    }
-    NEXT(cur);
-    while ((IS_URIC(cur)) ||
-           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
-        NEXT(cur);
-    if (uri != NULL) {
-        if (uri->opaque != NULL)
-            xmlFree(uri->opaque);
-       if (uri->cleanup & 2)
-           uri->opaque = STRNDUP(*str, cur - *str);
-       else
-           uri->opaque = xmlURIUnescapeString(*str, cur - *str, NULL);
-    }
-    *str = cur;
-    return (0);
+    if (uri->scheme != NULL) xmlFree(uri->scheme);
+    if (uri->server != NULL) xmlFree(uri->server);
+    if (uri->user != NULL) xmlFree(uri->user);
+    if (uri->path != NULL) xmlFree(uri->path);
+    if (uri->fragment != NULL) xmlFree(uri->fragment);
+    if (uri->opaque != NULL) xmlFree(uri->opaque);
+    if (uri->authority != NULL) xmlFree(uri->authority);
+    if (uri->query != NULL) xmlFree(uri->query);
+    if (uri->query_raw != NULL) xmlFree(uri->query_raw);
+    xmlFree(uri);
 }
 
+/************************************************************************
+ *                                                                     *
+ *                     Helper functions                                *
+ *                                                                     *
+ ************************************************************************/
+
 /**
- * xmlParseURIServer:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
+ * xmlNormalizeURIPath:
+ * @path:  pointer to the path string
+ *
+ * Applies the 5 normalization steps to a path string--that is, RFC 2396
+ * Section 5.2, steps 6.c through 6.g.
  *
- * Parse a server subpart of an URI, it's a finer grain analysis
- * of the authority part.
- * 
- * server        = [ [ userinfo "@" ] hostport ]
- * userinfo      = *( unreserved | escaped |
- *                       ";" | ":" | "&" | "=" | "+" | "$" | "," )
- * hostport      = host [ ":" port ]
- * host          = hostname | IPv4address | IPv6reference
- * hostname      = *( domainlabel "." ) toplabel [ "." ]
- * domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
- * toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
- * IPv6reference = "[" IPv6address "]"
- * IPv6address   = hexpart [ ":" IPv4address ]
- * IPv4address   = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
- * hexpart       = hexseq | hexseq "::" [ hexseq ]| "::" [ hexseq ]
- * hexseq        = hex4 *( ":" hex4)
- * hex4          = 1*4hexdig
- * port          = *digit
+ * Normalization occurs directly on the string, no new allocation is done
  *
- * Returns 0 or the error code
+ * Returns 0 or an error code
  */
-static int
-xmlParseURIServer(xmlURIPtr uri, const char **str) {
-    const char *cur;
-    const char *host, *tmp;
-    const int IPV4max = 4;
-    const int IPV6max = 8;
-    int oct;
+int
+xmlNormalizeURIPath(char *path) {
+    char *cur, *out;
 
-    if (str == NULL)
+    if (path == NULL)
        return(-1);
-    
-    cur = *str;
 
-    /*
-     * is there a userinfo ?
-     */
-    while (IS_USERINFO(cur)) NEXT(cur);
-    if (*cur == '@') {
-       if (uri != NULL) {
-           if (uri->user != NULL) xmlFree(uri->user);
-           if (uri->cleanup & 2)
-               uri->path = STRNDUP(*str, cur - *str);
-           else
-               uri->user = xmlURIUnescapeString(*str, cur - *str, NULL);
-       }
-       cur++;
-    } else {
-       if (uri != NULL) {
-           if (uri->user != NULL) xmlFree(uri->user);
-           uri->user = NULL;
-       }
-        cur = *str;
-    }
-    /*
-     * This can be empty in the case where there is no server
+    /* Skip all initial "/" chars.  We want to get to the beginning of the
+     * first non-empty segment.
      */
-    host = cur;
-    if (*cur == '/') {
-       if (uri != NULL) {
-           if (uri->authority != NULL) xmlFree(uri->authority);
-           uri->authority = NULL;
-           if (uri->server != NULL) xmlFree(uri->server);
-           uri->server = NULL;
-           uri->port = 0;
-       }
-       return(0);
-    }
+    cur = path;
+    while (cur[0] == '/')
+      ++cur;
+    if (cur[0] == '\0')
+      return(0);
+
+    /* Keep everything we've seen so far.  */
+    out = cur;
+
     /*
-     * host part of hostport can denote an IPV4 address, an IPV6 address
-     * or an unresolved name. Check the IP first, its easier to detect
-     * errors if wrong one.
-     * An IPV6 address must start with a '[' and end with a ']'.
+     * Analyze each segment in sequence for cases (c) and (d).
      */
-    if (*cur == '[') {
-       int compress=0;
-       cur++;
-       for (oct = 0; oct < IPV6max; ++oct) {
-           if (*cur == ':') {
-               if (compress)
-                   return(3);  /* multiple compression attempted */
-               if (!oct) {     /* initial char is compression */
-                   if (*++cur != ':')
-                       return(3);
-               }
-               compress = 1;   /* set compression-encountered flag */
-               cur++;          /* skip over the second ':' */
-               continue;
-           }
-           while(IS_HEX(*cur)) cur++;
-           if (oct == (IPV6max-1))
-               continue;
-           if (*cur != ':')
-               break;
-           cur++;
-       }
-       if ((!compress) && (oct != IPV6max))
-           return(3);
-       if (*cur != ']')
-           return(3);
-       if (uri != NULL) {
-           if (uri->server != NULL) xmlFree(uri->server);
-           uri->server = (char *)xmlStrndup((xmlChar *)host+1,
-                       (cur-host)-1);
-       }
-       cur++;
-    } else {
+    while (cur[0] != '\0') {
        /*
-        * Not IPV6, maybe IPV4
+        * c) All occurrences of "./", where "." is a complete path segment,
+        *    are removed from the buffer string.
         */
-       for (oct = 0; oct < IPV4max; ++oct) {
-            if (*cur == '.') 
-                return(3); /* e.g. http://.xml/ or http://18.29..30/ */
-            while(IS_DIGIT(*cur)) cur++;
-            if (oct == (IPV4max-1))
-                continue;
-            if (*cur != '.')
-               break;
-            cur++;
-       }
-    }
-    if ((host[0] != '[') && (oct < IPV4max || (*cur == '.' && cur++) ||
-                            IS_ALPHA(*cur))) {
-        /* maybe host_name */
-        if (!IS_ALPHANUM(*cur))
-            return(4); /* e.g. http://xml.$oft */
-        do {
-            do ++cur; while (IS_ALPHANUM(*cur));
-            if (*cur == '-') {
-               --cur;
-                if (*cur == '.')
-                    return(5); /* e.g. http://xml.-soft */
-               ++cur;
-               continue;
-            }
-           if (*cur == '.') {
-               --cur;
-                if (*cur == '-')
-                    return(6); /* e.g. http://xml-.soft */
-                if (*cur == '.')
-                    return(7); /* e.g. http://xml..soft */
-               ++cur;
-               continue;
-            }
-           break;
-        } while (1);
-        tmp = cur;
-        if (tmp[-1] == '.')
-            --tmp; /* e.g. http://xml.$Oft/ */
-        do --tmp; while (tmp >= host && IS_ALPHANUM(*tmp));
-        if ((++tmp == host || tmp[-1] == '.') && !IS_ALPHA(*tmp))
-            return(8); /* e.g. http://xmlsOft.0rg/ */
-    }
-    if (uri != NULL) {
-       if (uri->authority != NULL) xmlFree(uri->authority);
-       uri->authority = NULL;
-       if (host[0] != '[') {   /* it's not an IPV6 addr */
-           if (uri->server != NULL) xmlFree(uri->server);
-           if (uri->cleanup & 2)
-               uri->server = STRNDUP(host, cur - host);
-           else
-               uri->server = xmlURIUnescapeString(host, cur - host, NULL);
-       }
-    }
-    /*
-     * finish by checking for a port presence.
-     */
-    if (*cur == ':') {
-        cur++;
-       if (IS_DIGIT(*cur)) {
-           if (uri != NULL)
-               uri->port = 0;
-           while (IS_DIGIT(*cur)) {
-               if (uri != NULL)
-                   uri->port = uri->port * 10 + (*cur - '0');
+       if ((cur[0] == '.') && (cur[1] == '/')) {
+           cur += 2;
+           /* '//' normalization should be done at this point too */
+           while (cur[0] == '/')
                cur++;
-           }
+           continue;
        }
-    }
-    *str = cur;
-    return(0);
-}      
 
-/**
- * xmlParseURIRelSegment:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI relative segment
- * 
- * rel_segment = 1*( unreserved | escaped | ";" | "@" | "&" | "=" |
- *                          "+" | "$" | "," )
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIRelSegment(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
+       /*
+        * d) If the buffer string ends with "." as a complete path segment,
+        *    that "." is removed.
+        */
+       if ((cur[0] == '.') && (cur[1] == '\0'))
+           break;
 
-    if (str == NULL)
-        return (-1);
+       /* Otherwise keep the segment.  */
+       while (cur[0] != '/') {
+            if (cur[0] == '\0')
+              goto done_cd;
+           (out++)[0] = (cur++)[0];
+       }
+       /* nomalize // */
+       while ((cur[0] == '/') && (cur[1] == '/'))
+           cur++;
 
-    cur = *str;
-    if (!((IS_SEGMENT(cur)) ||
-          ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))) {
-        return (3);
-    }
-    NEXT(cur);
-    while ((IS_SEGMENT(cur)) ||
-           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
-        NEXT(cur);
-    if (uri != NULL) {
-        if (uri->path != NULL)
-            xmlFree(uri->path);
-       if (uri->cleanup & 2)
-           uri->path = STRNDUP(*str, cur - *str);
-       else
-           uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
+        (out++)[0] = (cur++)[0];
     }
-    *str = cur;
-    return (0);
-}
+ done_cd:
+    out[0] = '\0';
+
+    /* Reset to the beginning of the first segment for the next sequence.  */
+    cur = path;
+    while (cur[0] == '/')
+      ++cur;
+    if (cur[0] == '\0')
+       return(0);
 
-/**
- * xmlParseURIPathSegments:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- * @slash:  should we add a leading slash
- *
- * Parse an URI set of path segments
- * 
- * path_segments = segment *( "/" segment )
- * segment       = *pchar *( ";" param )
- * param         = *pchar
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIPathSegments(xmlURIPtr uri, const char **str, int slash)
-{
-    const char *cur;
+    /*
+     * Analyze each segment in sequence for cases (e) and (f).
+     *
+     * e) All occurrences of "<segment>/../", where <segment> is a
+     *    complete path segment not equal to "..", are removed from the
+     *    buffer string.  Removal of these path segments is performed
+     *    iteratively, removing the leftmost matching pattern on each
+     *    iteration, until no matching pattern remains.
+     *
+     * f) If the buffer string ends with "<segment>/..", where <segment>
+     *    is a complete path segment not equal to "..", that
+     *    "<segment>/.." is removed.
+     *
+     * To satisfy the "iterative" clause in (e), we need to collapse the
+     * string every time we find something that needs to be removed.  Thus,
+     * we don't need to keep two pointers into the string: we only need a
+     * "current position" pointer.
+     */
+    while (1) {
+        char *segp, *tmp;
 
-    if (str == NULL)
-        return (-1);
+        /* At the beginning of each iteration of this loop, "cur" points to
+         * the first character of the segment we want to examine.
+         */
 
-    cur = *str;
+        /* Find the end of the current segment.  */
+        segp = cur;
+        while ((segp[0] != '/') && (segp[0] != '\0'))
+          ++segp;
+
+        /* If this is the last segment, we're done (we need at least two
+         * segments to meet the criteria for the (e) and (f) cases).
+         */
+        if (segp[0] == '\0')
+          break;
 
-    do {
-        while ((IS_PCHAR(cur)) ||
-              ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
-            NEXT(cur);
-        while (*cur == ';') {
-            cur++;
-            while ((IS_PCHAR(cur)) ||
-                  ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
-                NEXT(cur);
+        /* If the first segment is "..", or if the next segment _isn't_ "..",
+         * keep this segment and try the next one.
+         */
+        ++segp;
+        if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3))
+            || ((segp[0] != '.') || (segp[1] != '.')
+                || ((segp[2] != '/') && (segp[2] != '\0')))) {
+          cur = segp;
+          continue;
         }
-        if (*cur != '/')
-            break;
-        cur++;
-    } while (1);
-    if (uri != NULL) {
-        int len, len2 = 0;
-        char *path;
 
-        /*
-         * Concat the set of path segments to the current path
+        /* If we get here, remove this segment and the next one and back up
+         * to the previous segment (if there is one), to implement the
+         * "iteratively" clause.  It's pretty much impossible to back up
+         * while maintaining two pointers into the buffer, so just compact
+         * the whole buffer now.
          */
-        len = cur - *str;
-        if (slash)
-            len++;
 
-        if (uri->path != NULL) {
-            len2 = strlen(uri->path);
-            len += len2;
-        }
-        path = (char *) xmlMallocAtomic(len + 1);
-        if (path == NULL) {
-           xmlGenericError(xmlGenericErrorContext,
-                           "xmlParseURIPathSegments: out of memory\n");
-            *str = cur;
-            return (-1);
-        }
-        if (uri->path != NULL)
-            memcpy(path, uri->path, len2);
-        if (slash) {
-            path[len2] = '/';
-            len2++;
+        /* If this is the end of the buffer, we're done.  */
+        if (segp[2] == '\0') {
+          cur[0] = '\0';
+          break;
         }
-        path[len2] = 0;
-        if (cur - *str > 0) {
-           if (uri->cleanup & 2) {
-               memcpy(&path[len2], *str, cur - *str);
-               path[len2 + (cur - *str)] = 0;
-           } else
-               xmlURIUnescapeString(*str, cur - *str, &path[len2]);
-       }
-        if (uri->path != NULL)
-            xmlFree(uri->path);
-        uri->path = path;
-    }
-    *str = cur;
-    return (0);
-}
+        /* Valgrind complained, strcpy(cur, segp + 3); */
+        /* string will overlap, do not use strcpy */
+        tmp = cur;
+        segp += 3;
+        while ((*tmp++ = *segp++) != 0)
+          ;
 
-/**
- * xmlParseURIAuthority:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse the authority part of an URI.
- * 
- * authority = server | reg_name
- * server    = [ [ userinfo "@" ] hostport ]
- * reg_name  = 1*( unreserved | escaped | "$" | "," | ";" | ":" |
- *                        "@" | "&" | "=" | "+" )
- *
- * Note : this is completely ambiguous since reg_name is allowed to
- *        use the full set of chars in use by server:
- *
- *        3.2.1. Registry-based Naming Authority
- *
- *        The structure of a registry-based naming authority is specific
- *        to the URI scheme, but constrained to the allowed characters
- *        for an authority component.
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIAuthority(xmlURIPtr uri, const char **str) {
-    const char *cur;
-    int ret;
+        /* If there are no previous segments, then keep going from here.  */
+        segp = cur;
+        while ((segp > path) && ((--segp)[0] == '/'))
+          ;
+        if (segp == path)
+          continue;
 
-    if (str == NULL)
-       return(-1);
-    
-    cur = *str;
+        /* "segp" is pointing to the end of a previous segment; find it's
+         * start.  We need to back up to the previous segment and start
+         * over with that to handle things like "foo/bar/../..".  If we
+         * don't do this, then on the first pass we'll remove the "bar/..",
+         * but be pointing at the second ".." so we won't realize we can also
+         * remove the "foo/..".
+         */
+        cur = segp;
+        while ((cur > path) && (cur[-1] != '/'))
+          --cur;
+    }
+    out[0] = '\0';
 
     /*
-     * try first to parse it as a server string.
+     * g) If the resulting buffer string still begins with one or more
+     *    complete path segments of "..", then the reference is
+     *    considered to be in error. Implementations may handle this
+     *    error by retaining these components in the resolved path (i.e.,
+     *    treating them as part of the final URI), by removing them from
+     *    the resolved path (i.e., discarding relative levels above the
+     *    root), or by avoiding traversal of the reference.
+     *
+     * We discard them from the final path.
      */
-    ret = xmlParseURIServer(uri, str);
-    if ((ret == 0) && (*str != NULL) &&
-       ((**str == 0) || (**str == '/') || (**str == '?')))
-        return(0);
-    *str = cur;
+    if (path[0] == '/') {
+      cur = path;
+      while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.')
+             && ((cur[3] == '/') || (cur[3] == '\0')))
+       cur += 3;
 
-    /*
-     * failed, fallback to reg_name
-     */
-    if (!IS_REG_NAME(cur)) {
-       return(5);
-    }
-    NEXT(cur);
-    while (IS_REG_NAME(cur)) NEXT(cur);
-    if (uri != NULL) {
-       if (uri->server != NULL) xmlFree(uri->server);
-       uri->server = NULL;
-       if (uri->user != NULL) xmlFree(uri->user);
-       uri->user = NULL;
-       if (uri->authority != NULL) xmlFree(uri->authority);
-       if (uri->cleanup & 2)
-           uri->authority = STRNDUP(*str, cur - *str);
-       else
-           uri->authority = xmlURIUnescapeString(*str, cur - *str, NULL);
+      if (cur != path) {
+       out = path;
+       while (cur[0] != '\0')
+          (out++)[0] = (cur++)[0];
+       out[0] = 0;
+      }
     }
-    *str = cur;
-    return(0);
-}
-
-/**
- * xmlParseURIHierPart:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI hierarchical part
- * 
- * hier_part = ( net_path | abs_path ) [ "?" query ]
- * abs_path = "/"  path_segments
- * net_path = "//" authority [ abs_path ]
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIHierPart(xmlURIPtr uri, const char **str) {
-    int ret;
-    const char *cur;
-
-    if (str == NULL)
-       return(-1);
-    
-    cur = *str;
 
-    if ((cur[0] == '/') && (cur[1] == '/')) {
-       cur += 2;
-       ret = xmlParseURIAuthority(uri, &cur);
-       if (ret != 0)
-           return(ret);
-       if (cur[0] == '/') {
-           cur++;
-           ret = xmlParseURIPathSegments(uri, &cur, 1);
-       }
-    } else if (cur[0] == '/') {
-       cur++;
-       ret = xmlParseURIPathSegments(uri, &cur, 1);
-    } else {
-       return(4);
-    }
-    if (ret != 0)
-       return(ret);
-    if (*cur == '?') {
-       cur++;
-       ret = xmlParseURIQuery(uri, &cur);
-       if (ret != 0)
-           return(ret);
-    }
-    *str = cur;
     return(0);
 }
 
-/**
- * xmlParseAbsoluteURI:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI reference string and fills in the appropriate fields
- * of the @uri structure
- * 
- * absoluteURI   = scheme ":" ( hier_part | opaque_part )
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseAbsoluteURI(xmlURIPtr uri, const char **str) {
-    int ret;
-    const char *cur;
-
-    if (str == NULL)
-       return(-1);
-    
-    cur = *str;
-
-    ret = xmlParseURIScheme(uri, str);
-    if (ret != 0) return(ret);
-    if (**str != ':') {
-       *str = cur;
+static int is_hex(char c) {
+    if (((c >= '0') && (c <= '9')) ||
+        ((c >= 'a') && (c <= 'f')) ||
+        ((c >= 'A') && (c <= 'F')))
        return(1);
-    }
-    (*str)++;
-    if (**str == '/')
-       return(xmlParseURIHierPart(uri, str));
-    return(xmlParseURIOpaquePart(uri, str));
+    return(0);
 }
 
 /**
- * xmlParseRelativeURI:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
+ * xmlURIUnescapeString:
+ * @str:  the string to unescape
+ * @len:   the length in bytes to unescape (or <= 0 to indicate full string)
+ * @target:  optional destination buffer
  *
- * Parse an relative URI string and fills in the appropriate fields
- * of the @uri structure
- * 
- * relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
- * abs_path = "/"  path_segments
- * net_path = "//" authority [ abs_path ]
- * rel_path = rel_segment [ abs_path ]
+ * Unescaping routine, but does not check that the string is an URI. The
+ * output is a direct unsigned char translation of %XX values (no encoding)
+ * Note that the length of the result can only be smaller or same size as
+ * the input string.
  *
- * Returns 0 or the error code
+ * Returns a copy of the string, but unescaped, will return NULL only in case
+ * of error
  */
-static int
-xmlParseRelativeURI(xmlURIPtr uri, const char **str) {
-    int ret = 0;
-    const char *cur;
+char *
+xmlURIUnescapeString(const char *str, int len, char *target) {
+    char *ret, *out;
+    const char *in;
 
     if (str == NULL)
-       return(-1);
-    
-    cur = *str;
-    if ((cur[0] == '/') && (cur[1] == '/')) {
-       cur += 2;
-       ret = xmlParseURIAuthority(uri, &cur);
-       if (ret != 0)
-           return(ret);
-       if (cur[0] == '/') {
-           cur++;
-           ret = xmlParseURIPathSegments(uri, &cur, 1);
+       return(NULL);
+    if (len <= 0) len = strlen(str);
+    if (len < 0) return(NULL);
+
+    if (target == NULL) {
+       ret = (char *) xmlMallocAtomic(len + 1);
+       if (ret == NULL) {
+            xmlURIErrMemory("unescaping URI value\n");
+           return(NULL);
        }
-    } else if (cur[0] == '/') {
-       cur++;
-       ret = xmlParseURIPathSegments(uri, &cur, 1);
-    } else if (cur[0] != '#' && cur[0] != '?') {
-       ret = xmlParseURIRelSegment(uri, &cur);
-       if (ret != 0)
-           return(ret);
-       if (cur[0] == '/') {
-           cur++;
-           ret = xmlParseURIPathSegments(uri, &cur, 1);
+    } else
+       ret = target;
+    in = str;
+    out = ret;
+    while(len > 0) {
+       if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) {
+           in++;
+           if ((*in >= '0') && (*in <= '9'))
+               *out = (*in - '0');
+           else if ((*in >= 'a') && (*in <= 'f'))
+               *out = (*in - 'a') + 10;
+           else if ((*in >= 'A') && (*in <= 'F'))
+               *out = (*in - 'A') + 10;
+           in++;
+           if ((*in >= '0') && (*in <= '9'))
+               *out = *out * 16 + (*in - '0');
+           else if ((*in >= 'a') && (*in <= 'f'))
+               *out = *out * 16 + (*in - 'a') + 10;
+           else if ((*in >= 'A') && (*in <= 'F'))
+               *out = *out * 16 + (*in - 'A') + 10;
+           in++;
+           len -= 3;
+           out++;
+       } else {
+           *out++ = *in++;
+           len--;
        }
     }
-    if (ret != 0)
-       return(ret);
-    if (*cur == '?') {
-       cur++;
-       ret = xmlParseURIQuery(uri, &cur);
-       if (ret != 0)
-           return(ret);
-    }
-    *str = cur;
+    *out = 0;
     return(ret);
 }
 
 /**
- * xmlParseURIReference:
- * @uri:  pointer to an URI structure
- * @str:  the string to analyze
+ * xmlURIEscapeStr:
+ * @str:  string to escape
+ * @list: exception list string of chars not to escape
  *
- * Parse an URI reference string and fills in the appropriate fields
- * of the @uri structure
- * 
- * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ * This routine escapes a string to hex, ignoring reserved characters (a-z)
+ * and the characters in the exception list.
  *
- * Returns 0 or the error code
+ * Returns a new escaped string or NULL in case of error.
  */
-int
-xmlParseURIReference(xmlURIPtr uri, const char *str) {
-    int ret;
-    const char *tmp = str;
+xmlChar *
+xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) {
+    xmlChar *ret, ch;
+    xmlChar *temp;
+    const xmlChar *in;
+    int len, out;
 
     if (str == NULL)
-       return(-1);
-    xmlCleanURI(uri);
+       return(NULL);
+    if (str[0] == 0)
+       return(xmlStrdup(str));
+    len = xmlStrlen(str);
+    if (!(len > 0)) return(NULL);
 
-    /*
-     * Try first to parse absolute refs, then fallback to relative if
-     * it fails.
-     */
-    ret = xmlParseAbsoluteURI(uri, &str);
-    if (ret != 0) {
-       xmlCleanURI(uri);
-       str = tmp;
-        ret = xmlParseRelativeURI(uri, &str);
-    }
-    if (ret != 0) {
-       xmlCleanURI(uri);
-       return(ret);
+    len += 20;
+    ret = (xmlChar *) xmlMallocAtomic(len);
+    if (ret == NULL) {
+        xmlURIErrMemory("escaping URI value\n");
+       return(NULL);
     }
+    in = (const xmlChar *) str;
+    out = 0;
+    while(*in != 0) {
+       if (len - out <= 3) {
+            temp = xmlSaveUriRealloc(ret, &len);
+           if (temp == NULL) {
+                xmlURIErrMemory("escaping URI value\n");
+               xmlFree(ret);
+               return(NULL);
+           }
+           ret = temp;
+       }
+
+       ch = *in;
+
+       if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!xmlStrchr(list, ch))) {
+           unsigned char val;
+           ret[out++] = '%';
+           val = ch >> 4;
+           if (val <= 9)
+               ret[out++] = '0' + val;
+           else
+               ret[out++] = 'A' + val - 0xA;
+           val = ch & 0xF;
+           if (val <= 9)
+               ret[out++] = '0' + val;
+           else
+               ret[out++] = 'A' + val - 0xA;
+           in++;
+       } else {
+           ret[out++] = *in++;
+       }
 
-    if (*str == '#') {
-       str++;
-       ret = xmlParseURIFragment(uri, &str);
-       if (ret != 0) return(ret);
-    }
-    if (*str != 0) {
-       xmlCleanURI(uri);
-       return(1);
     }
-    return(0);
+    ret[out] = 0;
+    return(ret);
 }
 
 /**
- * xmlParseURI:
- * @str:  the URI string to analyze
+ * xmlURIEscape:
+ * @str:  the string of the URI to escape
  *
- * Parse an URI 
- * 
- * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ * Escaping routine, does not do validity checks !
+ * It will try to escape the chars needing this, but this is heuristic
+ * based it's impossible to be sure.
  *
- * Returns a newly built xmlURIPtr or NULL in case of error
+ * Returns an copy of the string, but escaped
+ *
+ * 25 May 2001
+ * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly
+ * according to RFC2396.
+ *   - Carl Douglas
  */
-xmlURIPtr
-xmlParseURI(const char *str) {
+xmlChar *
+xmlURIEscape(const xmlChar * str)
+{
+    xmlChar *ret, *segment = NULL;
     xmlURIPtr uri;
-    int ret;
+    int ret2;
+
+#define NULLCHK(p) if(!p) { \
+         xmlURIErrMemory("escaping URI value\n"); \
+         xmlFreeURI(uri); \
+         return NULL; } \
 
     if (str == NULL)
-       return(NULL);
+        return (NULL);
+
     uri = xmlCreateURI();
     if (uri != NULL) {
-       ret = xmlParseURIReference(uri, str);
-        if (ret) {
-           xmlFreeURI(uri);
-           return(NULL);
-       }
+       /*
+        * Allow escaping errors in the unescaped form
+        */
+        uri->cleanup = 1;
+        ret2 = xmlParseURIReference(uri, (const char *)str);
+        if (ret2) {
+            xmlFreeURI(uri);
+            return (NULL);
+        }
     }
-    return(uri);
-}
 
-/**
- * xmlParseURIRaw:
- * @str:  the URI string to analyze
- * @raw:  if 1 unescaping of URI pieces are disabled
- *
- * Parse an URI but allows to keep intact the original fragments.
- * 
- * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
- *
- * Returns a newly built xmlURIPtr or NULL in case of error
- */
-xmlURIPtr
-xmlParseURIRaw(const char *str, int raw) {
-    xmlURIPtr uri;
-    int ret;
+    if (!uri)
+        return NULL;
 
-    if (str == NULL)
-       return(NULL);
-    uri = xmlCreateURI();
-    if (uri != NULL) {
-        if (raw) {
-           uri->cleanup |= 2;
-       }
-       ret = xmlParseURIReference(uri, str);
-        if (ret) {
-           xmlFreeURI(uri);
-           return(NULL);
-       }
+    ret = NULL;
+
+    if (uri->scheme) {
+        segment = xmlURIEscapeStr(BAD_CAST uri->scheme, BAD_CAST "+-.");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, segment);
+        ret = xmlStrcat(ret, BAD_CAST ":");
+        xmlFree(segment);
     }
-    return(uri);
+
+    if (uri->authority) {
+        segment =
+            xmlURIEscapeStr(BAD_CAST uri->authority, BAD_CAST "/?;:@");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, BAD_CAST "//");
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    if (uri->user) {
+        segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,");
+        NULLCHK(segment)
+               ret = xmlStrcat(ret,BAD_CAST "//");
+        ret = xmlStrcat(ret, segment);
+        ret = xmlStrcat(ret, BAD_CAST "@");
+        xmlFree(segment);
+    }
+
+    if (uri->server) {
+        segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@");
+        NULLCHK(segment)
+               if (uri->user == NULL)
+               ret = xmlStrcat(ret, BAD_CAST "//");
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    if (uri->port) {
+        xmlChar port[10];
+
+        snprintf((char *) port, 10, "%d", uri->port);
+        ret = xmlStrcat(ret, BAD_CAST ":");
+        ret = xmlStrcat(ret, port);
+    }
+
+    if (uri->path) {
+        segment =
+            xmlURIEscapeStr(BAD_CAST uri->path, BAD_CAST ":@&=+$,/?;");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    if (uri->query_raw) {
+        ret = xmlStrcat(ret, BAD_CAST "?");
+        ret = xmlStrcat(ret, BAD_CAST uri->query_raw);
+    }
+    else if (uri->query) {
+        segment =
+            xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, BAD_CAST "?");
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    if (uri->opaque) {
+        segment = xmlURIEscapeStr(BAD_CAST uri->opaque, BAD_CAST "");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    if (uri->fragment) {
+        segment = xmlURIEscapeStr(BAD_CAST uri->fragment, BAD_CAST "#");
+        NULLCHK(segment)
+        ret = xmlStrcat(ret, BAD_CAST "#");
+        ret = xmlStrcat(ret, segment);
+        xmlFree(segment);
+    }
+
+    xmlFreeURI(uri);
+#undef NULLCHK
+
+    return (ret);
 }
 
 /************************************************************************
@@ -1844,7 +1869,7 @@ xmlParseURIRaw(const char *str, int raw) {
  *
  * Computes he final URI of the reference done by checking that
  * the given URI is valid, and building the final URI using the
- * base URI. This is processed according to section 5.2 of the 
+ * base URI. This is processed according to section 5.2 of the
  * RFC 2396
  *
  * 5.2. Resolving Relative References to Absolute Form
@@ -1868,7 +1893,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
      *    as a reference to "." rather than as a synonym for the current
      *    URI.  Should we do that here?
      */
-    if (URI == NULL) 
+    if (URI == NULL)
        ret = -1;
     else {
        if (*URI) {
@@ -1939,12 +1964,16 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
            res->server = xmlMemStrdup(bas->server);
            if (bas->user != NULL)
                res->user = xmlMemStrdup(bas->user);
-           res->port = bas->port;              
+           res->port = bas->port;
        }
        if (bas->path != NULL)
            res->path = xmlMemStrdup(bas->path);
-       if (ref->query != NULL)
+       if (ref->query_raw != NULL)
+           res->query_raw = xmlMemStrdup (ref->query_raw);
+       else if (ref->query != NULL)
            res->query = xmlMemStrdup(ref->query);
+       else if (bas->query_raw != NULL)
+           res->query_raw = xmlMemStrdup(bas->query_raw);
        else if (bas->query != NULL)
            res->query = xmlMemStrdup(bas->query);
        if (ref->fragment != NULL)
@@ -1964,8 +1993,10 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
     }
     if (bas->scheme != NULL)
        res->scheme = xmlMemStrdup(bas->scheme);
-    if (ref->query != NULL)
+
+    if (ref->query_raw != NULL)
+       res->query_raw = xmlMemStrdup(ref->query_raw);
+    else if (ref->query != NULL)
        res->query = xmlMemStrdup(ref->query);
     if (ref->fragment != NULL)
        res->fragment = xmlMemStrdup(ref->fragment);
@@ -1984,7 +2015,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
            res->server = xmlMemStrdup(ref->server);
            if (ref->user != NULL)
                res->user = xmlMemStrdup(ref->user);
-            res->port = ref->port;             
+            res->port = ref->port;
        }
        if (ref->path != NULL)
            res->path = xmlMemStrdup(ref->path);
@@ -1996,7 +2027,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
        res->server = xmlMemStrdup(bas->server);
        if (bas->user != NULL)
            res->user = xmlMemStrdup(bas->user);
-       res->port = bas->port;          
+       res->port = bas->port;
     }
 
     /*
@@ -2024,8 +2055,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
        len += strlen(bas->path);
     res->path = (char *) xmlMallocAtomic(len);
     if (res->path == NULL) {
-       xmlGenericError(xmlGenericErrorContext,
-               "xmlBuildURI: out of memory\n");
+        xmlURIErrMemory("resolving URI against base\n");
        goto done;
     }
     res->path[0] = 0;
@@ -2209,7 +2239,8 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
            uptr = (xmlChar *) ref->path;
            if (*uptr == '/')
                uptr++;
-           val = xmlStrdup(uptr);
+           /* exception characters from xmlSaveUri */
+           val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,");
        }
        goto done;
     }
@@ -2270,10 +2301,11 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
        }
        len = xmlStrlen (uptr) + 1;
     }
-    
+
     if (nbslash == 0) {
        if (uptr != NULL)
-           val = xmlStrdup (uptr);
+           /* exception characters from xmlSaveUri */
+           val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,");
        goto done;
     }
 
@@ -2284,8 +2316,7 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
      */
     val = (xmlChar *) xmlMalloc (len + 3 * nbslash);
     if (val == NULL) {
-       xmlGenericError(xmlGenericErrorContext,
-               "xmlBuildRelativeURI: out of memory\n");
+        xmlURIErrMemory("building relative URI\n");
        goto done;
     }
     vptr = val;
@@ -2313,6 +2344,12 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
        vptr[len - 1] = 0;
     }
 
+    /* escape the freshly-built path */
+    vptr = val;
+       /* exception characters from xmlSaveUri */
+    val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,");
+    xmlFree(vptr);
+
 done:
     /*
      * Free the working variables
@@ -2331,14 +2368,14 @@ done:
  * xmlCanonicPath:
  * @path:  the resource locator in a filesystem notation
  *
- * Constructs a canonic path from the specified path. 
+ * Constructs a canonic path from the specified path.
  *
- * Returns a new canonic path, or a duplicate of the path parameter if the 
+ * Returns a new canonic path, or a duplicate of the path parameter if the
  * construction fails. The caller is responsible for freeing the memory occupied
- * by the returned string. If there is insufficient memory available, or the 
+ * by the returned string. If there is insufficient memory available, or the
  * argument is NULL, the function returns NULL.
  */
-#define IS_WINDOWS_PATH(p)                                     \
+#define IS_WINDOWS_PATH(p)                                     \
        ((p != NULL) &&                                         \
         (((p[0] >= 'a') && (p[0] <= 'z')) ||                   \
          ((p[0] >= 'A') && (p[0] <= 'Z'))) &&                  \
@@ -2346,7 +2383,11 @@ done:
 xmlChar *
 xmlCanonicPath(const xmlChar *path)
 {
-#if defined(_WIN32) && !defined(__CYGWIN__)    
+/*
+ * For Windows implementations, additional work needs to be done to
+ * replace backslashes in pathnames with "forward slashes"
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
     int len = 0;
     int i = 0;
     xmlChar *p = NULL;
@@ -2357,11 +2398,30 @@ xmlCanonicPath(const xmlChar *path)
 
     if (path == NULL)
        return(NULL);
+
+#if defined(_WIN32)
+    /*
+     * We must not change the backslashes to slashes if the the path
+     * starts with \\?\
+     * Those paths can be up to 32k characters long.
+     * Was added specifically for OpenOffice, those paths can't be converted
+     * to URIs anyway.
+     */
+    if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') &&
+        (path[3] == '\\') )
+       return xmlStrdup((const xmlChar *) path);
+#endif
+
+       /* sanitize filename starting with // so it can be used as URI */
+    if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/'))
+        path++;
+
     if ((uri = xmlParseURI((const char *) path)) != NULL) {
        xmlFreeURI(uri);
        return xmlStrdup(path);
     }
 
+    /* Check if this is an "absolute uri" */
     absuri = xmlStrstr(path, BAD_CAST "://");
     if (absuri != NULL) {
         int l, j;
@@ -2370,47 +2430,55 @@ xmlCanonicPath(const xmlChar *path)
 
         /*
         * this looks like an URI where some parts have not been
-        * escaped leading to a parsing problem check that the first
+        * escaped leading to a parsing problem.  Check that the first
         * part matches a protocol.
         */
        l = absuri - path;
+       /* Bypass if first part (part before the '://') is > 20 chars */
        if ((l <= 0) || (l > 20))
            goto path_processing;
+       /* Bypass if any non-alpha characters are present in first part */
        for (j = 0;j < l;j++) {
            c = path[j];
            if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))))
                goto path_processing;
        }
 
+       /* Escape all except the characters specified in the supplied path */
         escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;=");
        if (escURI != NULL) {
+           /* Try parsing the escaped path */
            uri = xmlParseURI((const char *) escURI);
+           /* If successful, return the escaped string */
            if (uri != NULL) {
                xmlFreeURI(uri);
                return escURI;
            }
-           xmlFreeURI(uri);
        }
     }
 
 path_processing:
-#if defined(_WIN32) && !defined(__CYGWIN__)    
+/* For Windows implementations, replace backslashes with 'forward slashes' */
+#if defined(_WIN32) && !defined(__CYGWIN__)
     /*
-     * This really need to be cleaned up by someone with a Windows box
+     * Create a URI structure
      */
     uri = xmlCreateURI();
-    if (uri == NULL) {
+    if (uri == NULL) {         /* Guard against 'out of memory' */
         return(NULL);
     }
 
     len = xmlStrlen(path);
     if ((len > 2) && IS_WINDOWS_PATH(path)) {
+        /* make the scheme 'file' */
        uri->scheme = xmlStrdup(BAD_CAST "file");
+       /* allocate space for leading '/' + path + string terminator */
        uri->path = xmlMallocAtomic(len + 2);
        if (uri->path == NULL) {
-           xmlFreeURI(uri);
+           xmlFreeURI(uri);    /* Guard agains 'out of memory' */
            return(NULL);
        }
+       /* Put in leading '/' plus path */
        uri->path[0] = '/';
        p = uri->path + 1;
        strncpy(p, path, len + 1);
@@ -2422,18 +2490,15 @@ path_processing:
        }
        p = uri->path;
     }
+    /* Now change all occurences of '\' to '/' */
     while (*p != '\0') {
        if (*p == '\\')
            *p = '/';
        p++;
     }
-    if (uri->path == NULL) {
-        xmlFreeURI(uri);
-        return(NULL);
-    }
 
     if (uri->scheme == NULL) {
-       ret = xmlStrdup((const xmlChar *) path);
+       ret = xmlStrdup((const xmlChar *) uri->path);
     } else {
        ret = xmlSaveUri(uri);
     }
@@ -2451,7 +2516,7 @@ path_processing:
  *
  * Constructs an URI expressing the existing path
  *
- * Returns a new URI, or a duplicate of the path parameter if the 
+ * Returns a new URI, or a duplicate of the path parameter if the
  * construction fails. The caller is responsible for freeing the memory
  * occupied by the returned string. If there is insufficient memory available,
  * or the argument is NULL, the function returns NULL.
@@ -2473,6 +2538,24 @@ xmlPathToURI(const xmlChar *path)
     cal = xmlCanonicPath(path);
     if (cal == NULL)
         return(NULL);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+    /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?)
+       If 'cal' is a valid URI allready then we are done here, as continuing would make
+       it invalid. */
+    if ((uri = xmlParseURI((const char *) cal)) != NULL) {
+       xmlFreeURI(uri);
+       return cal;
+    }
+    /* 'cal' can contain a relative path with backslashes. If that is processed
+       by xmlSaveURI, they will be escaped and the external entity loader machinery
+       will fail. So convert them to slashes. Misuse 'ret' for walking. */
+    ret = cal;
+    while (*ret != '\0') {
+       if (*ret == '\\')
+           *ret = '/';
+       ret++;
+    }
+#endif
     memset(&temp, 0, sizeof(temp));
     temp.path = (char *) cal;
     ret = xmlSaveUri(&temp);