libxml2 r3763 - in trunk: . include/libxml result/errors test/errors



Author: veillard
Date: Mon Aug  4 15:29:44 2008
New Revision: 3763
URL: http://svn.gnome.org/viewvc/libxml2?rev=3763&view=rev

Log:
* uri.c include/libxml/uri.h: rewrite the URI parser to update to
  rfc3986 (from 2396)
* test/errors/webdav.xml result/errors/webdav.xml*: removed the
  error test, 'DAV:' is a correct URI under 3986
* Makefile.am: small cleanup in make check
Daniel


Removed:
   trunk/result/errors/webdav.xml
   trunk/result/errors/webdav.xml.err
   trunk/result/errors/webdav.xml.str
   trunk/test/errors/webdav.xml
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/include/libxml/uri.h
   trunk/testapi.c
   trunk/uri.c

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Mon Aug  4 15:29:44 2008
@@ -167,7 +167,7 @@
 #testOOM_LDADD= $(LDADDS)
 
 runtests:
-	runtest$(EXEEXT) && testapi$(EXEEXT) && runxmlconf$(EXEEXT)
+	./runtest$(EXEEXT) && ./testapi$(EXEEXT) && ./runxmlconf$(EXEEXT)
 
 check: all runtests
 

Modified: trunk/include/libxml/uri.h
==============================================================================
--- trunk/include/libxml/uri.h	(original)
+++ trunk/include/libxml/uri.h	Mon Aug  4 15:29:44 2008
@@ -49,43 +49,43 @@
  * xmlChar *	xmlNodeGetBase	(xmlDocPtr doc,
  *                               xmlNodePtr cur);
  */
-XMLPUBFUN xmlURIPtr XMLCALL	
+XMLPUBFUN xmlURIPtr XMLCALL
 		xmlCreateURI		(void);
-XMLPUBFUN xmlChar * XMLCALL	
+XMLPUBFUN xmlChar * XMLCALL
 		xmlBuildURI		(const xmlChar *URI,
-	                         	 const xmlChar *base);
-XMLPUBFUN xmlChar * XMLCALL	
+					 const xmlChar *base);
+XMLPUBFUN xmlChar * XMLCALL
 		xmlBuildRelativeURI	(const xmlChar *URI,
-	                         	 const xmlChar *base);
-XMLPUBFUN xmlURIPtr XMLCALL	
+					 const xmlChar *base);
+XMLPUBFUN xmlURIPtr XMLCALL
 		xmlParseURI		(const char *str);
-XMLPUBFUN xmlURIPtr XMLCALL	
+XMLPUBFUN xmlURIPtr XMLCALL
 		xmlParseURIRaw		(const char *str,
 					 int raw);
-XMLPUBFUN int XMLCALL		
+XMLPUBFUN int XMLCALL
 		xmlParseURIReference	(xmlURIPtr uri,
 					 const char *str);
-XMLPUBFUN xmlChar * XMLCALL	
+XMLPUBFUN xmlChar * XMLCALL
 		xmlSaveUri		(xmlURIPtr uri);
-XMLPUBFUN void XMLCALL		
+XMLPUBFUN void XMLCALL
 		xmlPrintURI		(FILE *stream,
 					 xmlURIPtr uri);
-XMLPUBFUN xmlChar * XMLCALL       
+XMLPUBFUN xmlChar * XMLCALL
 		xmlURIEscapeStr         (const xmlChar *str,
- 					 const xmlChar *list);
-XMLPUBFUN char * XMLCALL		
+					 const xmlChar *list);
+XMLPUBFUN char * XMLCALL
 		xmlURIUnescapeString	(const char *str,
 					 int len,
 					 char *target);
-XMLPUBFUN int XMLCALL		
+XMLPUBFUN int XMLCALL
 		xmlNormalizeURIPath	(char *path);
-XMLPUBFUN xmlChar * XMLCALL	
+XMLPUBFUN xmlChar * XMLCALL
 		xmlURIEscape		(const xmlChar *str);
-XMLPUBFUN void XMLCALL		
+XMLPUBFUN void XMLCALL
 		xmlFreeURI		(xmlURIPtr uri);
-XMLPUBFUN xmlChar* XMLCALL	
+XMLPUBFUN xmlChar* XMLCALL
 		xmlCanonicPath		(const xmlChar *path);
-XMLPUBFUN xmlChar* XMLCALL	
+XMLPUBFUN xmlChar* XMLCALL
 		xmlPathToURI		(const xmlChar *path);
 
 #ifdef __cplusplus

Modified: trunk/testapi.c
==============================================================================
--- trunk/testapi.c	(original)
+++ trunk/testapi.c	Mon Aug  4 15:29:44 2008
@@ -212,7 +212,7 @@
  name resolution delays, so we use these
 */
 #define	REMOTE1GOOD	"http://localhost/";
-#define	REMOTE1BAD	"http://missing. example.org/"
+#define	REMOTE1BAD	"http:http://http";
 #define	REMOTE2GOOD	"ftp://localhost/foo";
 
 #define gen_nb_void_ptr 2

Modified: trunk/uri.c
==============================================================================
--- trunk/uri.c	(original)
+++ trunk/uri.c	Mon Aug  4 15:29:44 2008
@@ -1,7 +1,7 @@
 /**
  * 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.
  *
@@ -18,14 +18,10 @@
 #include <libxml/globals.h>
 #include <libxml/xmlerror.h>
 
-/************************************************************************
- *									*
- *		Macros to differentiate various character type		*
- *			directly extracted from RFC 2396		*
- *									*
- ************************************************************************/
+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))
@@ -61,31 +57,30 @@
 #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
@@ -94,1851 +89,1827 @@
 #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) == '=')))
 
 /*
- * 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
+ *
+ * query = *uric
+ *
+ * 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 == '?') ||
+           ((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;
-    xmlChar *temp;
-    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;
-		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-		    xmlGenericError(xmlGenericErrorContext,
-			    "xmlSaveUri: out of memory\n");
-		    xmlFree(ret);
-		    return(NULL);
-		}
-		ret = temp;
-	    }
-	    ret[len++] = *p++;
-	}
-	if (len >= max) {
-	    max *= 2;
-	    temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-	    if (temp == NULL) {
-		xmlGenericError(xmlGenericErrorContext,
-			"xmlSaveUri: out of memory\n");
-		xmlFree(ret);
-		return(NULL);
-	    }
-	    ret = temp;
-	}
-	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;
-		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-		    xmlGenericError(xmlGenericErrorContext,
-			    "xmlSaveUri: out of memory\n");
-		    xmlFree(ret);
-		    return(NULL);
-		}
-		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) {
-	    if (len + 3 >= max) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-		    xmlGenericError(xmlGenericErrorContext,
-			    "xmlSaveUri: out of memory\n");
-                  xmlFree(ret);  
-		    return(NULL);
-		}
-		ret = temp;
-	    }
-	    ret[len++] = '/';
-	    ret[len++] = '/';
-	    if (uri->user != NULL) {
-		p = uri->user;
-		while (*p != 0) {
-		    if (len + 3 >= max) {
-			max *= 2;
-			temp = (xmlChar *) xmlRealloc(ret,
-				(max + 1) * sizeof(xmlChar));
-			if (temp == NULL) {
-			    xmlGenericError(xmlGenericErrorContext,
-				    "xmlSaveUri: out of memory\n");
-			    xmlFree(ret);
-			    return(NULL);
-			}
-			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) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-			xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-		}
-		ret[len++] = '@';
-	    }
-	    p = uri->server;
-	    while (*p != 0) {
-		if (len >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-			xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-		}
-		ret[len++] = *p++;
-	    }
-	    if (uri->port > 0) {
-		if (len + 10 >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-		}
-		len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
-	    }
-	} else if (uri->authority != NULL) {
-	    if (len + 3 >= max) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret,
-			(max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-	    }
-	    ret[len++] = '/';
-	    ret[len++] = '/';
-	    p = uri->authority;
-	    while (*p != 0) {
-		if (len + 3 >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    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) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret,
-			(max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-	    }
-	    ret[len++] = '/';
-	    ret[len++] = '/';
-	}
-	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(uri->scheme, BAD_CAST "file"))) {
-		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++;
-		ret[len++] = *p++;
-		ret[len++] = *p++;
-	    }
-	    while (*p != 0) {
-		if (len + 3 >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    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) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret,
-			(max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-	    }
-	    ret[len++] = '?';
-	    p = uri->query_raw;
-	    while (*p != 0) {
-		if (len + 1 >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-		}
-		ret[len++] = *p++;
-	    }
-	} else if (uri->query != NULL) {
-	    if (len + 3 >= max) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret,
-			(max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
-	    }
-	    ret[len++] = '?';
-	    p = uri->query;
-	    while (*p != 0) {
-		if (len + 3 >= max) {
-		    max *= 2;
-		    temp = (xmlChar *) xmlRealloc(ret,
-			    (max + 1) * sizeof(xmlChar));
-		    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    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');
-		}
-	    }
-	}
+    /*
+     * 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;
+    }
+    /*
+     * then this should be a hostname which can be empty
+     */
+    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);
+}
+
+/**
+ * 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 == ':') {
+        ret = xmlParse3986Port(uri, &cur);
+	if (ret != 0) return(ret);
+    }
+    *str = cur;
+    return(0);
+}
+
+/**
+ * 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
+ *
+ * segment       = *pchar
+ * segment-nz    = 1*pchar
+ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ *               ; non-zero-length segment without any colon ":"
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986Segment(const char **str, char forbid, int empty)
+{
+    const char *cur;
+
+    cur = *str;
+    if (!ISA_PCHAR(cur)) {
+        if (empty)
+	    return(0);
+	return(1);
+    }
+    while (ISA_PCHAR(cur) && (*cur != forbid))
+        NEXT(cur);
+    *str = cur;
+    return (0);
+}
+
+/**
+ * xmlParse3986PathAbEmpty:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
+ *
+ * Parse an path absolute or empty and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * path-abempty  = *( "/" segment )
+ *
+ * Returns 0 or the error code
+ */
+static int
+xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
+
+    cur = *str;
+
+    while (*cur == '/') {
+        cur++;
+	ret = xmlParse3986Segment(&cur, 0, 1);
+	if (ret != 0) return(ret);
     }
-    if (uri->fragment != NULL) {
-	if (len + 3 >= max) {
-	    max *= 2;
-	    temp = (xmlChar *) xmlRealloc(ret,
-		    (max + 1) * sizeof(xmlChar));
-	    if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-        	    }
-        	    ret = temp;
-	}
-	ret[len++] = '#';
-	p = uri->fragment;
-	while (*p != 0) {
-	    if (len + 3 >= max) {
-		max *= 2;
-		temp = (xmlChar *) xmlRealloc(ret,
-			(max + 1) * sizeof(xmlChar));
-		if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    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 != 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);
+    }
+    *str = cur;
+    return (0);
+}
+
+/**
+ * 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;
+
+    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 (len >= max) {
-	max *= 2;
-	temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
-	if (temp == NULL) {
-			xmlGenericError(xmlGenericErrorContext,
-				"xmlSaveUri: out of memory\n");
-                     xmlFree(ret);
-			return(NULL);
-		    }
-		    ret = temp;
+    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);
     }
-    ret[len++] = 0;
-    return(ret);
+    *str = cur;
+    return (0);
 }
 
 /**
- * xmlPrintURI:
- * @stream:  a FILE* for the output
- * @uri:  pointer to an xmlURI
+ * xmlParse3986PathRootless:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Prints the URI in the stream @stream.
+ * 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
  */
-void
-xmlPrintURI(FILE *stream, xmlURIPtr uri) {
-    xmlChar *out;
+static int
+xmlParse3986PathRootless(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
-    out = xmlSaveUri(uri);
-    if (out != NULL) {
-	fprintf(stream, "%s", (char *) out);
-	xmlFree(out);
+    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);
+    }
+    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);
     }
+    *str = cur;
+    return (0);
 }
 
 /**
- * xmlCleanURI:
- * @uri:  pointer to an xmlURI
+ * xmlParse3986PathNoScheme:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Make sure the xmlURI struct is free of content
+ * Parse an path which is not a scheme and fills in the appropriate fields
+ * of the @uri structure
+ *
+ * path-noscheme = segment-nz-nc *( "/" segment )
+ *
+ * Returns 0 or the error code
  */
-static void
-xmlCleanURI(xmlURIPtr uri) {
-    if (uri == NULL) return;
+static int
+xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str)
+{
+    const char *cur;
+    int ret;
 
-    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;
+    cur = *str;
+
+    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) {
+	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);
+    }
+    *str = cur;
+    return (0);
 }
 
 /**
- * xmlFreeURI:
- * @uri:  pointer to an xmlURI
+ * 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;
+
+    cur = *str;
+
+    if ((*cur == '/') && (*(cur + 1) == '/')) {
+        cur += 2;
+	ret = xmlParse3986Authority(uri, &cur);
+	if (ret != 0) return(ret);
+	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);
+}
+
+/**
+ * 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 ((*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 (*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);
+}
+
+
+/**
+ * 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 ]
  *
- * Free up the xmlURI struct
+ * Returns 0 or the error code
  */
-void
-xmlFreeURI(xmlURIPtr uri) {
-    if (uri == NULL) return;
+static int
+xmlParse3986URI(xmlURIPtr uri, const char *str) {
+    int ret;
 
-    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);
+    ret = xmlParse3986Scheme(uri, &str);
+    if (ret != 0) return(ret);
+    if (*str != ':') {
+	return(1);
+    }
+    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);
 }
 
-/************************************************************************
- *									*
- *			Helper functions				*
- *									*
- ************************************************************************/
-
 /**
- * xmlNormalizeURIPath:
- * @path:  pointer to the path string
+ * xmlParse3986URIReference:
+ * @uri:  pointer to an URI structure
+ * @str:  the string to analyze
  *
- * Applies the 5 normalization steps to a path string--that is, RFC 2396
- * Section 5.2, steps 6.c through 6.g.
+ * Parse an URI reference string and fills in the appropriate fields
+ * of the @uri structure
  *
- * Normalization occurs directly on the string, no new allocation is done
+ * URI-reference = URI / relative-ref
  *
- * Returns 0 or an error code
+ * Returns 0 or the error code
  */
-int
-xmlNormalizeURIPath(char *path) {
-    char *cur, *out;
+static int
+xmlParse3986URIReference(xmlURIPtr uri, const char *str) {
+    int ret;
 
-    if (path == NULL)
+    if (str == 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;
+    xmlCleanURI(uri);
 
     /*
-     * Analyze each segment in sequence for cases (c) and (d).
+     * Try first to parse absolute refs, then fallback to relative if
+     * it fails.
      */
-    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;
+    ret = xmlParse3986URI(uri, str);
+    if (ret != 0) {
+	xmlCleanURI(uri);
+        ret = xmlParse3986RelativeRef(uri, str);
+	if (ret != 0) {
+	    xmlCleanURI(uri);
+	    return(ret);
 	}
+    }
+    return(0);
+}
 
-	/*
-	 * d) If the buffer string ends with "." as a complete path segment,
-	 *    that "." is removed.
-	 */
-	if ((cur[0] == '.') && (cur[1] == '\0'))
-	    break;
+/**
+ * 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;
 
-	/* Otherwise keep the segment.  */
-	while (cur[0] != '/') {
-            if (cur[0] == '\0')
-              goto done_cd;
-	    (out++)[0] = (cur++)[0];
+    if (str == NULL)
+	return(NULL);
+    uri = xmlCreateURI();
+    if (uri != NULL) {
+	ret = xmlParse3986URIReference(uri, str);
+        if (ret) {
+	    xmlFreeURI(uri);
+	    return(NULL);
 	}
-	/* 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;
+    return(uri);
+}
 
-        /* "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';
+/**
+ * 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));
+}
 
-    /*
-     * 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.
-     */
-    if (path[0] == '/') {
-      cur = path;
-      while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.')
-             && ((cur[3] == '/') || (cur[3] == '\0')))
-	cur += 3;
+/**
+ * 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;
 
-      if (cur != path) {
-	out = path;
-	while (cur[0] != '\0')
-          (out++)[0] = (cur++)[0];
-	out[0] = 0;
-      }
+    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(0);
+    return(uri);
 }
 
-static int is_hex(char c) {
-    if (((c >= '0') && (c <= '9')) ||
-        ((c >= 'a') && (c <= 'f')) ||
-        ((c >= 'A') && (c <= 'F')))
-	return(1);
-    return(0);
+/************************************************************************
+ *									*
+ *			Generic URI structure functions			*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlCreateURI:
+ *
+ * Simply creates an empty xmlURI
+ *
+ * Returns the new structure or NULL in case of error
+ */
+xmlURIPtr
+xmlCreateURI(void) {
+    xmlURIPtr ret;
+
+    ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI));
+    if (ret == NULL) {
+	xmlGenericError(xmlGenericErrorContext,
+		"xmlCreateURI: out of memory\n");
+	return(NULL);
+    }
+    memset(ret, 0, sizeof(xmlURI));
+    return(ret);
 }
 
 /**
- * xmlURIUnescapeString:
- * @str:  the string to unescape
- * @len:   the length in bytes to unescape (or <= 0 to indicate full string)
- * @target:  optional destination buffer
+ * xmlSaveUri:
+ * @uri:  pointer to an xmlURI
  *
- * 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.
+ * Save the URI as an escaped string
  *
- * Returns a copy of the string, but unescaped, will return NULL only in case
- * of error
+ * Returns a new string (to be deallocated by caller)
  */
-char *
-xmlURIUnescapeString(const char *str, int len, char *target) {
-    char *ret, *out;
-    const char *in;
+xmlChar *
+xmlSaveUri(xmlURIPtr uri) {
+    xmlChar *ret = NULL;
+    xmlChar *temp;
+    const char *p;
+    int len;
+    int max;
 
-    if (str == NULL)
+    if (uri == NULL) return(NULL);
+
+
+    max = 80;
+    ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar));
+    if (ret == NULL) {
+	xmlGenericError(xmlGenericErrorContext,
+		"xmlSaveUri: out of memory\n");
 	return(NULL);
-    if (len <= 0) len = strlen(str);
-    if (len < 0) return(NULL);
+    }
+    len = 0;
 
-    if (target == NULL) {
-	ret = (char *) xmlMallocAtomic(len + 1);
-	if (ret == NULL) {
-	    xmlGenericError(xmlGenericErrorContext,
-		    "xmlURIUnescapeString: out of memory\n");
-	    return(NULL);
+    if (uri->scheme != NULL) {
+	p = uri->scheme;
+	while (*p != 0) {
+	    if (len >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+		    xmlGenericError(xmlGenericErrorContext,
+			    "xmlSaveUri: out of memory\n");
+		    xmlFree(ret);
+		    return(NULL);
+		}
+		ret = temp;
+	    }
+	    ret[len++] = *p++;
 	}
-    } 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 (len >= max) {
+	    max *= 2;
+	    temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
+	    if (temp == NULL) {
+		xmlGenericError(xmlGenericErrorContext,
+			"xmlSaveUri: out of memory\n");
+		xmlFree(ret);
+		return(NULL);
+	    }
+	    ret = temp;
+	}
+	ret[len++] = ':';
+    }
+    if (uri->opaque != NULL) {
+	p = uri->opaque;
+	while (*p != 0) {
+	    if (len + 3 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+		    xmlGenericError(xmlGenericErrorContext,
+			    "xmlSaveUri: out of memory\n");
+		    xmlFree(ret);
+		    return(NULL);
+		}
+		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) {
+	    if (len + 3 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+		    xmlGenericError(xmlGenericErrorContext,
+			    "xmlSaveUri: out of memory\n");
+                  xmlFree(ret);  
+		    return(NULL);
+		}
+		ret = temp;
+	    }
+	    ret[len++] = '/';
+	    ret[len++] = '/';
+	    if (uri->user != NULL) {
+		p = uri->user;
+		while (*p != 0) {
+		    if (len + 3 >= max) {
+			max *= 2;
+			temp = (xmlChar *) xmlRealloc(ret,
+				(max + 1) * sizeof(xmlChar));
+			if (temp == NULL) {
+			    xmlGenericError(xmlGenericErrorContext,
+				    "xmlSaveUri: out of memory\n");
+			    xmlFree(ret);
+			    return(NULL);
+			}
+			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) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+			xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+		}
+		ret[len++] = '@';
+	    }
+	    p = uri->server;
+	    while (*p != 0) {
+		if (len >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+			xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+		}
+		ret[len++] = *p++;
+	    }
+	    if (uri->port > 0) {
+		if (len + 10 >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+		}
+		len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
+	    }
+	} else if (uri->authority != NULL) {
+	    if (len + 3 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret,
+			(max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+	    }
+	    ret[len++] = '/';
+	    ret[len++] = '/';
+	    p = uri->authority;
+	    while (*p != 0) {
+		if (len + 3 >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    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) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret,
+			(max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+	    }
+	    ret[len++] = '/';
+	    ret[len++] = '/';
 	}
-    }
-    *out = 0;
-    return(ret);
-}
-
-/**
- * xmlURIEscapeStr:
- * @str:  string to escape
- * @list: exception list string of chars not to escape
- *
- * This routine escapes a string to hex, ignoring reserved characters (a-z)
- * and the characters in the exception list.
- *
- * Returns a new escaped string or NULL in case of error.
- */
-xmlChar *
-xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) {
-    xmlChar *ret, ch;
-    xmlChar *temp;
-    const xmlChar *in;
-
-    unsigned int len, out;
-
-    if (str == NULL)
-	return(NULL);
-    if (str[0] == 0)
-	return(xmlStrdup(str));
-    len = xmlStrlen(str);
-    if (!(len > 0)) return(NULL);
-
-    len += 20;
-    ret = (xmlChar *) xmlMallocAtomic(len);
-    if (ret == NULL) {
-	xmlGenericError(xmlGenericErrorContext,
-		"xmlURIEscapeStr: out of memory\n");
-	return(NULL);
-    }
-    in = (const xmlChar *) str;
-    out = 0;
-    while(*in != 0) {
-	if (len - out <= 3) {
-	    len += 20;
-	    temp = (xmlChar *) xmlRealloc(ret, len);
-	    if (temp == NULL) {
-		xmlGenericError(xmlGenericErrorContext,
-			"xmlURIEscapeStr: out of memory\n");
-		xmlFree(ret);
-		return(NULL);
+	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) {
+		    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++;
+		ret[len++] = *p++;
+		ret[len++] = *p++;
+	    }
+	    while (*p != 0) {
+		if (len + 3 >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    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');
+		}
 	    }
-	    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 (uri->query_raw != NULL) {
+	    if (len + 1 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret,
+			(max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+	    }
+	    ret[len++] = '?';
+	    p = uri->query_raw;
+	    while (*p != 0) {
+		if (len + 1 >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+		}
+		ret[len++] = *p++;
+	    }
+	} else if (uri->query != NULL) {
+	    if (len + 3 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret,
+			(max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+	    }
+	    ret[len++] = '?';
+	    p = uri->query;
+	    while (*p != 0) {
+		if (len + 3 >= max) {
+		    max *= 2;
+		    temp = (xmlChar *) xmlRealloc(ret,
+			    (max + 1) * sizeof(xmlChar));
+		    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    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');
+		}
+	    }
 	}
-
-    }
-    ret[out] = 0;
-    return(ret);
-}
-
-/**
- * xmlURIEscape:
- * @str:  the string of the URI to escape
- *
- * 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 an copy of the string, but escaped
- *
- * 25 May 2001
- * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly
- * according to RFC2396.
- *   - Carl Douglas
- */
-xmlChar *
-xmlURIEscape(const xmlChar * str)
-{
-    xmlChar *ret, *segment = NULL;
-    xmlURIPtr uri;
-    int ret2;
-
-#define NULLCHK(p) if(!p) { \
-                   xmlGenericError(xmlGenericErrorContext, \
-                        "xmlURIEscape: out of memory\n"); \
-                        xmlFreeURI(uri); \
-                        return NULL; } \
-
-    if (str == NULL)
-        return (NULL);
-
-    uri = xmlCreateURI();
-    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)
-        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);
-    }
-
-    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);
-}
-
-/************************************************************************
- *									*
- *			Escaped URI parsing				*
- *									*
- ************************************************************************/
-
-/**
- * xmlParseURIFragment:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI fragment string and fills in the appropriate fields
- * of the @uri structure.
- * 
- * fragment = *uric
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIFragment(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
-    
-    if (str == NULL)
-        return (-1);
-
-    cur = *str;
-
-    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);
+    if (uri->fragment != NULL) {
+	if (len + 3 >= max) {
+	    max *= 2;
+	    temp = (xmlChar *) xmlRealloc(ret,
+		    (max + 1) * sizeof(xmlChar));
+	    if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
+	}
+	ret[len++] = '#';
+	p = uri->fragment;
+	while (*p != 0) {
+	    if (len + 3 >= max) {
+		max *= 2;
+		temp = (xmlChar *) xmlRealloc(ret,
+			(max + 1) * sizeof(xmlChar));
+		if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    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');
+	    }
+	}
     }
-    *str = cur;
-    return (0);
-}
-
-/**
- * xmlParseURIQuery:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse the query part of an URI
- * 
- * query = *uric
- *
- * Returns 0 or the error code
- */
-static int
-xmlParseURIQuery(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
-
-    if (str == NULL)
-        return (-1);
-
-    cur = *str;
-
-    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);
-
-	/* 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);
+    if (len >= max) {
+	max *= 2;
+	temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
+	if (temp == NULL) {
+			xmlGenericError(xmlGenericErrorContext,
+				"xmlSaveUri: out of memory\n");
+                     xmlFree(ret);
+			return(NULL);
+		    }
+		    ret = temp;
     }
-    *str = cur;
-    return (0);
+    ret[len++] = 0;
+    return(ret);
 }
 
 /**
- * xmlParseURIScheme:
- * @uri:  pointer to an URI structure
- * @str:  pointer to the string to analyze
- *
- * Parse an URI scheme
- * 
- * scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ * xmlPrintURI:
+ * @stream:  a FILE* for the output
+ * @uri:  pointer to an xmlURI
  *
- * Returns 0 or the error code
+ * Prints the URI in the stream @stream.
  */
-static int
-xmlParseURIScheme(xmlURIPtr uri, const char **str) {
-    const char *cur;
+void
+xmlPrintURI(FILE *stream, xmlURIPtr uri) {
+    xmlChar *out;
 
-    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);
+    out = xmlSaveUri(uri);
+    if (out != NULL) {
+	fprintf(stream, "%s", (char *) out);
+	xmlFree(out);
     }
-    *str = cur;
-    return(0);
 }
 
 /**
- * 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
+ * xmlCleanURI:
+ * @uri:  pointer to an xmlURI
  *
- * Returns 0 or the error code
+ * Make sure the xmlURI struct is free of content
  */
-static int
-xmlParseURIOpaquePart(xmlURIPtr uri, const char **str)
-{
-    const char *cur;
+static void
+xmlCleanURI(xmlURIPtr uri) {
+    if (uri == NULL) return;
 
-    if (str == NULL)
-        return (-1);
+    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;
+}
 
-    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);
+/**
+ * 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);
+    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
  *
- * 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
+ * Applies the 5 normalization steps to a path string--that is, RFC 2396
+ * Section 5.2, steps 6.c through 6.g.
  *
- * Returns 0 or the error code
+ * Normalization occurs directly on the string, no new allocation is done
+ *
+ * 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->user = 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);
+
+    /*
+     * 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;
 
-/**
- * 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;
+        /* At the beginning of each iteration of this loop, "cur" points to
+         * the first character of the segment we want to examine.
+         */
 
-    if (str == NULL)
-        return (-1);
+        /* Find the end of the current segment.  */
+        segp = cur;
+        while ((segp[0] != '/') && (segp[0] != '\0'))
+          ++segp;
 
-    cur = *str;
+        /* 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) {
+	    xmlGenericError(xmlGenericErrorContext,
+		    "xmlURIUnescapeString: out of memory\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;
+
+    unsigned 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) {
+	xmlGenericError(xmlGenericErrorContext,
+		"xmlURIEscapeStr: out of memory\n");
+	return(NULL);
     }
+    in = (const xmlChar *) str;
+    out = 0;
+    while(*in != 0) {
+	if (len - out <= 3) {
+	    len += 20;
+	    temp = (xmlChar *) xmlRealloc(ret, len);
+	    if (temp == NULL) {
+		xmlGenericError(xmlGenericErrorContext,
+			"xmlURIEscapeStr: out of memory\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) { \
+                   xmlGenericError(xmlGenericErrorContext, \
+                        "xmlURIEscape: out of memory\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);
 }
 
 /************************************************************************



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]