Home | History | Annotate | Download | only in libxml2
      1 /**
      2  * uri.c: set of generic URI related routines
      3  *
      4  * Reference: RFCs 3986, 2732 and 2373
      5  *
      6  * See Copyright for the status of this software.
      7  *
      8  * daniel (at) veillard.com
      9  */
     10 
     11 #define IN_LIBXML
     12 #include "libxml.h"
     13 
     14 #include <string.h>
     15 
     16 #include <libxml/xmlmemory.h>
     17 #include <libxml/uri.h>
     18 #include <libxml/globals.h>
     19 #include <libxml/xmlerror.h>
     20 
     21 static void xmlCleanURI(xmlURIPtr uri);
     22 
     23 /*
     24  * Old rule from 2396 used in legacy handling code
     25  * alpha    = lowalpha | upalpha
     26  */
     27 #define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x))
     28 
     29 
     30 /*
     31  * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" |
     32  *            "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" |
     33  *            "u" | "v" | "w" | "x" | "y" | "z"
     34  */
     35 
     36 #define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z'))
     37 
     38 /*
     39  * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" |
     40  *           "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" |
     41  *           "U" | "V" | "W" | "X" | "Y" | "Z"
     42  */
     43 #define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z'))
     44 
     45 #ifdef IS_DIGIT
     46 #undef IS_DIGIT
     47 #endif
     48 /*
     49  * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
     50  */
     51 #define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9'))
     52 
     53 /*
     54  * alphanum = alpha | digit
     55  */
     56 
     57 #define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x))
     58 
     59 /*
     60  * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
     61  */
     62 
     63 #define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') ||     \
     64     ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') ||    \
     65     ((x) == '(') || ((x) == ')'))
     66 
     67 /*
     68  * unwise = "{" | "}" | "|" | "\" | "^" | "`"
     69  */
     70 
     71 #define IS_UNWISE(p)                                                    \
     72       (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) ||         \
     73        ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) ||        \
     74        ((*(p) == ']')) || ((*(p) == '`')))
     75 /*
     76  * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
     77  *            "[" | "]"
     78  */
     79 
     80 #define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \
     81         ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \
     82         ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
     83         ((x) == ']'))
     84 
     85 /*
     86  * unreserved = alphanum | mark
     87  */
     88 
     89 #define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x))
     90 
     91 /*
     92  * Skip to next pointer char, handle escaped sequences
     93  */
     94 
     95 #define NEXT(p) ((*p == '%')? p += 3 : p++)
     96 
     97 /*
     98  * Productions from the spec.
     99  *
    100  *    authority     = server | reg_name
    101  *    reg_name      = 1*( unreserved | escaped | "$" | "," |
    102  *                        ";" | ":" | "@" | "&" | "=" | "+" )
    103  *
    104  * path          = [ abs_path | opaque_part ]
    105  */
    106 
    107 #define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n))
    108 
    109 /************************************************************************
    110  *									*
    111  *                         RFC 3986 parser				*
    112  *									*
    113  ************************************************************************/
    114 
    115 #define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9'))
    116 #define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) ||		\
    117                       ((*(p) >= 'A') && (*(p) <= 'Z')))
    118 #define ISA_HEXDIG(p)							\
    119        (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) ||		\
    120         ((*(p) >= 'A') && (*(p) <= 'F')))
    121 
    122 /*
    123  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
    124  *                     / "*" / "+" / "," / ";" / "="
    125  */
    126 #define ISA_SUB_DELIM(p)						\
    127       (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) ||		\
    128        ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) ||		\
    129        ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) ||		\
    130        ((*(p) == '=')) || ((*(p) == '\'')))
    131 
    132 /*
    133  *    gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
    134  */
    135 #define ISA_GEN_DELIM(p)						\
    136       (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) ||         \
    137        ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) ||         \
    138        ((*(p) == '@')))
    139 
    140 /*
    141  *    reserved      = gen-delims / sub-delims
    142  */
    143 #define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p)))
    144 
    145 /*
    146  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
    147  */
    148 #define ISA_UNRESERVED(p)						\
    149       ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) ||		\
    150        ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~')))
    151 
    152 /*
    153  *    pct-encoded   = "%" HEXDIG HEXDIG
    154  */
    155 #define ISA_PCT_ENCODED(p)						\
    156      ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2)))
    157 
    158 /*
    159  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
    160  */
    161 #define ISA_PCHAR(p)							\
    162      (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) ||	\
    163       ((*(p) == ':')) || ((*(p) == '@')))
    164 
    165 /**
    166  * xmlParse3986Scheme:
    167  * @uri:  pointer to an URI structure
    168  * @str:  pointer to the string to analyze
    169  *
    170  * Parse an URI scheme
    171  *
    172  * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
    173  *
    174  * Returns 0 or the error code
    175  */
    176 static int
    177 xmlParse3986Scheme(xmlURIPtr uri, const char **str) {
    178     const char *cur;
    179 
    180     if (str == NULL)
    181 	return(-1);
    182 
    183     cur = *str;
    184     if (!ISA_ALPHA(cur))
    185 	return(2);
    186     cur++;
    187     while (ISA_ALPHA(cur) || ISA_DIGIT(cur) ||
    188            (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++;
    189     if (uri != NULL) {
    190 	if (uri->scheme != NULL) xmlFree(uri->scheme);
    191 	uri->scheme = STRNDUP(*str, cur - *str);
    192     }
    193     *str = cur;
    194     return(0);
    195 }
    196 
    197 /**
    198  * xmlParse3986Fragment:
    199  * @uri:  pointer to an URI structure
    200  * @str:  pointer to the string to analyze
    201  *
    202  * Parse the query part of an URI
    203  *
    204  * fragment      = *( pchar / "/" / "?" )
    205  * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']'
    206  *       in the fragment identifier but this is used very broadly for
    207  *       xpointer scheme selection, so we are allowing it here to not break
    208  *       for example all the DocBook processing chains.
    209  *
    210  * Returns 0 or the error code
    211  */
    212 static int
    213 xmlParse3986Fragment(xmlURIPtr uri, const char **str)
    214 {
    215     const char *cur;
    216 
    217     if (str == NULL)
    218         return (-1);
    219 
    220     cur = *str;
    221 
    222     while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
    223            (*cur == '[') || (*cur == ']') ||
    224            ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
    225         NEXT(cur);
    226     if (uri != NULL) {
    227         if (uri->fragment != NULL)
    228             xmlFree(uri->fragment);
    229 	if (uri->cleanup & 2)
    230 	    uri->fragment = STRNDUP(*str, cur - *str);
    231 	else
    232 	    uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL);
    233     }
    234     *str = cur;
    235     return (0);
    236 }
    237 
    238 /**
    239  * xmlParse3986Query:
    240  * @uri:  pointer to an URI structure
    241  * @str:  pointer to the string to analyze
    242  *
    243  * Parse the query part of an URI
    244  *
    245  * query = *uric
    246  *
    247  * Returns 0 or the error code
    248  */
    249 static int
    250 xmlParse3986Query(xmlURIPtr uri, const char **str)
    251 {
    252     const char *cur;
    253 
    254     if (str == NULL)
    255         return (-1);
    256 
    257     cur = *str;
    258 
    259     while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
    260            ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
    261         NEXT(cur);
    262     if (uri != NULL) {
    263         if (uri->query != NULL)
    264             xmlFree(uri->query);
    265 	if (uri->cleanup & 2)
    266 	    uri->query = STRNDUP(*str, cur - *str);
    267 	else
    268 	    uri->query = xmlURIUnescapeString(*str, cur - *str, NULL);
    269 
    270 	/* Save the raw bytes of the query as well.
    271 	 * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114
    272 	 */
    273 	if (uri->query_raw != NULL)
    274 	    xmlFree (uri->query_raw);
    275 	uri->query_raw = STRNDUP (*str, cur - *str);
    276     }
    277     *str = cur;
    278     return (0);
    279 }
    280 
    281 /**
    282  * xmlParse3986Port:
    283  * @uri:  pointer to an URI structure
    284  * @str:  the string to analyze
    285  *
    286  * Parse a port  part and fills in the appropriate fields
    287  * of the @uri structure
    288  *
    289  * port          = *DIGIT
    290  *
    291  * Returns 0 or the error code
    292  */
    293 static int
    294 xmlParse3986Port(xmlURIPtr uri, const char **str)
    295 {
    296     const char *cur = *str;
    297 
    298     if (ISA_DIGIT(cur)) {
    299 	if (uri != NULL)
    300 	    uri->port = 0;
    301 	while (ISA_DIGIT(cur)) {
    302 	    if (uri != NULL)
    303 		uri->port = uri->port * 10 + (*cur - '0');
    304 	    cur++;
    305 	}
    306 	*str = cur;
    307 	return(0);
    308     }
    309     return(1);
    310 }
    311 
    312 /**
    313  * xmlParse3986Userinfo:
    314  * @uri:  pointer to an URI structure
    315  * @str:  the string to analyze
    316  *
    317  * Parse an user informations part and fills in the appropriate fields
    318  * of the @uri structure
    319  *
    320  * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
    321  *
    322  * Returns 0 or the error code
    323  */
    324 static int
    325 xmlParse3986Userinfo(xmlURIPtr uri, const char **str)
    326 {
    327     const char *cur;
    328 
    329     cur = *str;
    330     while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) ||
    331            ISA_SUB_DELIM(cur) || (*cur == ':'))
    332 	NEXT(cur);
    333     if (*cur == '@') {
    334 	if (uri != NULL) {
    335 	    if (uri->user != NULL) xmlFree(uri->user);
    336 	    if (uri->cleanup & 2)
    337 		uri->user = STRNDUP(*str, cur - *str);
    338 	    else
    339 		uri->user = xmlURIUnescapeString(*str, cur - *str, NULL);
    340 	}
    341 	*str = cur;
    342 	return(0);
    343     }
    344     return(1);
    345 }
    346 
    347 /**
    348  * xmlParse3986DecOctet:
    349  * @str:  the string to analyze
    350  *
    351  *    dec-octet     = DIGIT                 ; 0-9
    352  *                  / %x31-39 DIGIT         ; 10-99
    353  *                  / "1" 2DIGIT            ; 100-199
    354  *                  / "2" %x30-34 DIGIT     ; 200-249
    355  *                  / "25" %x30-35          ; 250-255
    356  *
    357  * Skip a dec-octet.
    358  *
    359  * Returns 0 if found and skipped, 1 otherwise
    360  */
    361 static int
    362 xmlParse3986DecOctet(const char **str) {
    363     const char *cur = *str;
    364 
    365     if (!(ISA_DIGIT(cur)))
    366         return(1);
    367     if (!ISA_DIGIT(cur+1))
    368 	cur++;
    369     else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur+2)))
    370 	cur += 2;
    371     else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2)))
    372 	cur += 3;
    373     else if ((*cur == '2') && (*(cur + 1) >= '0') &&
    374 	     (*(cur + 1) <= '4') && (ISA_DIGIT(cur + 2)))
    375 	cur += 3;
    376     else if ((*cur == '2') && (*(cur + 1) == '5') &&
    377 	     (*(cur + 2) >= '0') && (*(cur + 1) <= '5'))
    378 	cur += 3;
    379     else
    380         return(1);
    381     *str = cur;
    382     return(0);
    383 }
    384 /**
    385  * xmlParse3986Host:
    386  * @uri:  pointer to an URI structure
    387  * @str:  the string to analyze
    388  *
    389  * Parse an host part and fills in the appropriate fields
    390  * of the @uri structure
    391  *
    392  * host          = IP-literal / IPv4address / reg-name
    393  * IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"
    394  * IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
    395  * reg-name      = *( unreserved / pct-encoded / sub-delims )
    396  *
    397  * Returns 0 or the error code
    398  */
    399 static int
    400 xmlParse3986Host(xmlURIPtr uri, const char **str)
    401 {
    402     const char *cur = *str;
    403     const char *host;
    404 
    405     host = cur;
    406     /*
    407      * IPv6 and future adressing scheme are enclosed between brackets
    408      */
    409     if (*cur == '[') {
    410         cur++;
    411 	while ((*cur != ']') && (*cur != 0))
    412 	    cur++;
    413 	if (*cur != ']')
    414 	    return(1);
    415 	cur++;
    416 	goto found;
    417     }
    418     /*
    419      * try to parse an IPv4
    420      */
    421     if (ISA_DIGIT(cur)) {
    422         if (xmlParse3986DecOctet(&cur) != 0)
    423 	    goto not_ipv4;
    424 	if (*cur != '.')
    425 	    goto not_ipv4;
    426 	cur++;
    427         if (xmlParse3986DecOctet(&cur) != 0)
    428 	    goto not_ipv4;
    429 	if (*cur != '.')
    430 	    goto not_ipv4;
    431         if (xmlParse3986DecOctet(&cur) != 0)
    432 	    goto not_ipv4;
    433 	if (*cur != '.')
    434 	    goto not_ipv4;
    435         if (xmlParse3986DecOctet(&cur) != 0)
    436 	    goto not_ipv4;
    437 	goto found;
    438 not_ipv4:
    439         cur = *str;
    440     }
    441     /*
    442      * then this should be a hostname which can be empty
    443      */
    444     while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur))
    445         NEXT(cur);
    446 found:
    447     if (uri != NULL) {
    448 	if (uri->authority != NULL) xmlFree(uri->authority);
    449 	uri->authority = NULL;
    450 	if (uri->server != NULL) xmlFree(uri->server);
    451 	if (cur != host) {
    452 	    if (uri->cleanup & 2)
    453 		uri->server = STRNDUP(host, cur - host);
    454 	    else
    455 		uri->server = xmlURIUnescapeString(host, cur - host, NULL);
    456 	} else
    457 	    uri->server = NULL;
    458     }
    459     *str = cur;
    460     return(0);
    461 }
    462 
    463 /**
    464  * xmlParse3986Authority:
    465  * @uri:  pointer to an URI structure
    466  * @str:  the string to analyze
    467  *
    468  * Parse an authority part and fills in the appropriate fields
    469  * of the @uri structure
    470  *
    471  * authority     = [ userinfo "@" ] host [ ":" port ]
    472  *
    473  * Returns 0 or the error code
    474  */
    475 static int
    476 xmlParse3986Authority(xmlURIPtr uri, const char **str)
    477 {
    478     const char *cur;
    479     int ret;
    480 
    481     cur = *str;
    482     /*
    483      * try to parse an userinfo and check for the trailing @
    484      */
    485     ret = xmlParse3986Userinfo(uri, &cur);
    486     if ((ret != 0) || (*cur != '@'))
    487         cur = *str;
    488     else
    489         cur++;
    490     ret = xmlParse3986Host(uri, &cur);
    491     if (ret != 0) return(ret);
    492     if (*cur == ':') {
    493         cur++;
    494         ret = xmlParse3986Port(uri, &cur);
    495 	if (ret != 0) return(ret);
    496     }
    497     *str = cur;
    498     return(0);
    499 }
    500 
    501 /**
    502  * xmlParse3986Segment:
    503  * @str:  the string to analyze
    504  * @forbid: an optional forbidden character
    505  * @empty: allow an empty segment
    506  *
    507  * Parse a segment and fills in the appropriate fields
    508  * of the @uri structure
    509  *
    510  * segment       = *pchar
    511  * segment-nz    = 1*pchar
    512  * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
    513  *               ; non-zero-length segment without any colon ":"
    514  *
    515  * Returns 0 or the error code
    516  */
    517 static int
    518 xmlParse3986Segment(const char **str, char forbid, int empty)
    519 {
    520     const char *cur;
    521 
    522     cur = *str;
    523     if (!ISA_PCHAR(cur)) {
    524         if (empty)
    525 	    return(0);
    526 	return(1);
    527     }
    528     while (ISA_PCHAR(cur) && (*cur != forbid))
    529         NEXT(cur);
    530     *str = cur;
    531     return (0);
    532 }
    533 
    534 /**
    535  * xmlParse3986PathAbEmpty:
    536  * @uri:  pointer to an URI structure
    537  * @str:  the string to analyze
    538  *
    539  * Parse an path absolute or empty and fills in the appropriate fields
    540  * of the @uri structure
    541  *
    542  * path-abempty  = *( "/" segment )
    543  *
    544  * Returns 0 or the error code
    545  */
    546 static int
    547 xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str)
    548 {
    549     const char *cur;
    550     int ret;
    551 
    552     cur = *str;
    553 
    554     while (*cur == '/') {
    555         cur++;
    556 	ret = xmlParse3986Segment(&cur, 0, 1);
    557 	if (ret != 0) return(ret);
    558     }
    559     if (uri != NULL) {
    560 	if (uri->path != NULL) xmlFree(uri->path);
    561         if (*str != cur) {
    562             if (uri->cleanup & 2)
    563                 uri->path = STRNDUP(*str, cur - *str);
    564             else
    565                 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
    566         } else {
    567             uri->path = NULL;
    568         }
    569     }
    570     *str = cur;
    571     return (0);
    572 }
    573 
    574 /**
    575  * xmlParse3986PathAbsolute:
    576  * @uri:  pointer to an URI structure
    577  * @str:  the string to analyze
    578  *
    579  * Parse an path absolute and fills in the appropriate fields
    580  * of the @uri structure
    581  *
    582  * path-absolute = "/" [ segment-nz *( "/" segment ) ]
    583  *
    584  * Returns 0 or the error code
    585  */
    586 static int
    587 xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str)
    588 {
    589     const char *cur;
    590     int ret;
    591 
    592     cur = *str;
    593 
    594     if (*cur != '/')
    595         return(1);
    596     cur++;
    597     ret = xmlParse3986Segment(&cur, 0, 0);
    598     if (ret == 0) {
    599 	while (*cur == '/') {
    600 	    cur++;
    601 	    ret = xmlParse3986Segment(&cur, 0, 1);
    602 	    if (ret != 0) return(ret);
    603 	}
    604     }
    605     if (uri != NULL) {
    606 	if (uri->path != NULL) xmlFree(uri->path);
    607         if (cur != *str) {
    608             if (uri->cleanup & 2)
    609                 uri->path = STRNDUP(*str, cur - *str);
    610             else
    611                 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
    612         } else {
    613             uri->path = NULL;
    614         }
    615     }
    616     *str = cur;
    617     return (0);
    618 }
    619 
    620 /**
    621  * xmlParse3986PathRootless:
    622  * @uri:  pointer to an URI structure
    623  * @str:  the string to analyze
    624  *
    625  * Parse an path without root and fills in the appropriate fields
    626  * of the @uri structure
    627  *
    628  * path-rootless = segment-nz *( "/" segment )
    629  *
    630  * Returns 0 or the error code
    631  */
    632 static int
    633 xmlParse3986PathRootless(xmlURIPtr uri, const char **str)
    634 {
    635     const char *cur;
    636     int ret;
    637 
    638     cur = *str;
    639 
    640     ret = xmlParse3986Segment(&cur, 0, 0);
    641     if (ret != 0) return(ret);
    642     while (*cur == '/') {
    643         cur++;
    644 	ret = xmlParse3986Segment(&cur, 0, 1);
    645 	if (ret != 0) return(ret);
    646     }
    647     if (uri != NULL) {
    648 	if (uri->path != NULL) xmlFree(uri->path);
    649         if (cur != *str) {
    650             if (uri->cleanup & 2)
    651                 uri->path = STRNDUP(*str, cur - *str);
    652             else
    653                 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
    654         } else {
    655             uri->path = NULL;
    656         }
    657     }
    658     *str = cur;
    659     return (0);
    660 }
    661 
    662 /**
    663  * xmlParse3986PathNoScheme:
    664  * @uri:  pointer to an URI structure
    665  * @str:  the string to analyze
    666  *
    667  * Parse an path which is not a scheme and fills in the appropriate fields
    668  * of the @uri structure
    669  *
    670  * path-noscheme = segment-nz-nc *( "/" segment )
    671  *
    672  * Returns 0 or the error code
    673  */
    674 static int
    675 xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str)
    676 {
    677     const char *cur;
    678     int ret;
    679 
    680     cur = *str;
    681 
    682     ret = xmlParse3986Segment(&cur, ':', 0);
    683     if (ret != 0) return(ret);
    684     while (*cur == '/') {
    685         cur++;
    686 	ret = xmlParse3986Segment(&cur, 0, 1);
    687 	if (ret != 0) return(ret);
    688     }
    689     if (uri != NULL) {
    690 	if (uri->path != NULL) xmlFree(uri->path);
    691         if (cur != *str) {
    692             if (uri->cleanup & 2)
    693                 uri->path = STRNDUP(*str, cur - *str);
    694             else
    695                 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
    696         } else {
    697             uri->path = NULL;
    698         }
    699     }
    700     *str = cur;
    701     return (0);
    702 }
    703 
    704 /**
    705  * xmlParse3986HierPart:
    706  * @uri:  pointer to an URI structure
    707  * @str:  the string to analyze
    708  *
    709  * Parse an hierarchical part and fills in the appropriate fields
    710  * of the @uri structure
    711  *
    712  * hier-part     = "//" authority path-abempty
    713  *                / path-absolute
    714  *                / path-rootless
    715  *                / path-empty
    716  *
    717  * Returns 0 or the error code
    718  */
    719 static int
    720 xmlParse3986HierPart(xmlURIPtr uri, const char **str)
    721 {
    722     const char *cur;
    723     int ret;
    724 
    725     cur = *str;
    726 
    727     if ((*cur == '/') && (*(cur + 1) == '/')) {
    728         cur += 2;
    729 	ret = xmlParse3986Authority(uri, &cur);
    730 	if (ret != 0) return(ret);
    731 	ret = xmlParse3986PathAbEmpty(uri, &cur);
    732 	if (ret != 0) return(ret);
    733 	*str = cur;
    734 	return(0);
    735     } else if (*cur == '/') {
    736         ret = xmlParse3986PathAbsolute(uri, &cur);
    737 	if (ret != 0) return(ret);
    738     } else if (ISA_PCHAR(cur)) {
    739         ret = xmlParse3986PathRootless(uri, &cur);
    740 	if (ret != 0) return(ret);
    741     } else {
    742 	/* path-empty is effectively empty */
    743 	if (uri != NULL) {
    744 	    if (uri->path != NULL) xmlFree(uri->path);
    745 	    uri->path = NULL;
    746 	}
    747     }
    748     *str = cur;
    749     return (0);
    750 }
    751 
    752 /**
    753  * xmlParse3986RelativeRef:
    754  * @uri:  pointer to an URI structure
    755  * @str:  the string to analyze
    756  *
    757  * Parse an URI string and fills in the appropriate fields
    758  * of the @uri structure
    759  *
    760  * relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
    761  * relative-part = "//" authority path-abempty
    762  *               / path-absolute
    763  *               / path-noscheme
    764  *               / path-empty
    765  *
    766  * Returns 0 or the error code
    767  */
    768 static int
    769 xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) {
    770     int ret;
    771 
    772     if ((*str == '/') && (*(str + 1) == '/')) {
    773         str += 2;
    774 	ret = xmlParse3986Authority(uri, &str);
    775 	if (ret != 0) return(ret);
    776 	ret = xmlParse3986PathAbEmpty(uri, &str);
    777 	if (ret != 0) return(ret);
    778     } else if (*str == '/') {
    779 	ret = xmlParse3986PathAbsolute(uri, &str);
    780 	if (ret != 0) return(ret);
    781     } else if (ISA_PCHAR(str)) {
    782         ret = xmlParse3986PathNoScheme(uri, &str);
    783 	if (ret != 0) return(ret);
    784     } else {
    785 	/* path-empty is effectively empty */
    786 	if (uri != NULL) {
    787 	    if (uri->path != NULL) xmlFree(uri->path);
    788 	    uri->path = NULL;
    789 	}
    790     }
    791 
    792     if (*str == '?') {
    793 	str++;
    794 	ret = xmlParse3986Query(uri, &str);
    795 	if (ret != 0) return(ret);
    796     }
    797     if (*str == '#') {
    798 	str++;
    799 	ret = xmlParse3986Fragment(uri, &str);
    800 	if (ret != 0) return(ret);
    801     }
    802     if (*str != 0) {
    803 	xmlCleanURI(uri);
    804 	return(1);
    805     }
    806     return(0);
    807 }
    808 
    809 
    810 /**
    811  * xmlParse3986URI:
    812  * @uri:  pointer to an URI structure
    813  * @str:  the string to analyze
    814  *
    815  * Parse an URI string and fills in the appropriate fields
    816  * of the @uri structure
    817  *
    818  * scheme ":" hier-part [ "?" query ] [ "#" fragment ]
    819  *
    820  * Returns 0 or the error code
    821  */
    822 static int
    823 xmlParse3986URI(xmlURIPtr uri, const char *str) {
    824     int ret;
    825 
    826     ret = xmlParse3986Scheme(uri, &str);
    827     if (ret != 0) return(ret);
    828     if (*str != ':') {
    829 	return(1);
    830     }
    831     str++;
    832     ret = xmlParse3986HierPart(uri, &str);
    833     if (ret != 0) return(ret);
    834     if (*str == '?') {
    835 	str++;
    836 	ret = xmlParse3986Query(uri, &str);
    837 	if (ret != 0) return(ret);
    838     }
    839     if (*str == '#') {
    840 	str++;
    841 	ret = xmlParse3986Fragment(uri, &str);
    842 	if (ret != 0) return(ret);
    843     }
    844     if (*str != 0) {
    845 	xmlCleanURI(uri);
    846 	return(1);
    847     }
    848     return(0);
    849 }
    850 
    851 /**
    852  * xmlParse3986URIReference:
    853  * @uri:  pointer to an URI structure
    854  * @str:  the string to analyze
    855  *
    856  * Parse an URI reference string and fills in the appropriate fields
    857  * of the @uri structure
    858  *
    859  * URI-reference = URI / relative-ref
    860  *
    861  * Returns 0 or the error code
    862  */
    863 static int
    864 xmlParse3986URIReference(xmlURIPtr uri, const char *str) {
    865     int ret;
    866 
    867     if (str == NULL)
    868 	return(-1);
    869     xmlCleanURI(uri);
    870 
    871     /*
    872      * Try first to parse absolute refs, then fallback to relative if
    873      * it fails.
    874      */
    875     ret = xmlParse3986URI(uri, str);
    876     if (ret != 0) {
    877 	xmlCleanURI(uri);
    878         ret = xmlParse3986RelativeRef(uri, str);
    879 	if (ret != 0) {
    880 	    xmlCleanURI(uri);
    881 	    return(ret);
    882 	}
    883     }
    884     return(0);
    885 }
    886 
    887 /**
    888  * xmlParseURI:
    889  * @str:  the URI string to analyze
    890  *
    891  * Parse an URI based on RFC 3986
    892  *
    893  * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
    894  *
    895  * Returns a newly built xmlURIPtr or NULL in case of error
    896  */
    897 xmlURIPtr
    898 xmlParseURI(const char *str) {
    899     xmlURIPtr uri;
    900     int ret;
    901 
    902     if (str == NULL)
    903 	return(NULL);
    904     uri = xmlCreateURI();
    905     if (uri != NULL) {
    906 	ret = xmlParse3986URIReference(uri, str);
    907         if (ret) {
    908 	    xmlFreeURI(uri);
    909 	    return(NULL);
    910 	}
    911     }
    912     return(uri);
    913 }
    914 
    915 /**
    916  * xmlParseURIReference:
    917  * @uri:  pointer to an URI structure
    918  * @str:  the string to analyze
    919  *
    920  * Parse an URI reference string based on RFC 3986 and fills in the
    921  * appropriate fields of the @uri structure
    922  *
    923  * URI-reference = URI / relative-ref
    924  *
    925  * Returns 0 or the error code
    926  */
    927 int
    928 xmlParseURIReference(xmlURIPtr uri, const char *str) {
    929     return(xmlParse3986URIReference(uri, str));
    930 }
    931 
    932 /**
    933  * xmlParseURIRaw:
    934  * @str:  the URI string to analyze
    935  * @raw:  if 1 unescaping of URI pieces are disabled
    936  *
    937  * Parse an URI but allows to keep intact the original fragments.
    938  *
    939  * URI-reference = URI / relative-ref
    940  *
    941  * Returns a newly built xmlURIPtr or NULL in case of error
    942  */
    943 xmlURIPtr
    944 xmlParseURIRaw(const char *str, int raw) {
    945     xmlURIPtr uri;
    946     int ret;
    947 
    948     if (str == NULL)
    949 	return(NULL);
    950     uri = xmlCreateURI();
    951     if (uri != NULL) {
    952         if (raw) {
    953 	    uri->cleanup |= 2;
    954 	}
    955 	ret = xmlParseURIReference(uri, str);
    956         if (ret) {
    957 	    xmlFreeURI(uri);
    958 	    return(NULL);
    959 	}
    960     }
    961     return(uri);
    962 }
    963 
    964 /************************************************************************
    965  *									*
    966  *			Generic URI structure functions			*
    967  *									*
    968  ************************************************************************/
    969 
    970 /**
    971  * xmlCreateURI:
    972  *
    973  * Simply creates an empty xmlURI
    974  *
    975  * Returns the new structure or NULL in case of error
    976  */
    977 xmlURIPtr
    978 xmlCreateURI(void) {
    979     xmlURIPtr ret;
    980 
    981     ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI));
    982     if (ret == NULL) {
    983 	xmlGenericError(xmlGenericErrorContext,
    984 		"xmlCreateURI: out of memory\n");
    985 	return(NULL);
    986     }
    987     memset(ret, 0, sizeof(xmlURI));
    988     return(ret);
    989 }
    990 
    991 /**
    992  * xmlSaveUri:
    993  * @uri:  pointer to an xmlURI
    994  *
    995  * Save the URI as an escaped string
    996  *
    997  * Returns a new string (to be deallocated by caller)
    998  */
    999 xmlChar *
   1000 xmlSaveUri(xmlURIPtr uri) {
   1001     xmlChar *ret = NULL;
   1002     xmlChar *temp;
   1003     const char *p;
   1004     int len;
   1005     int max;
   1006 
   1007     if (uri == NULL) return(NULL);
   1008 
   1009 
   1010     max = 80;
   1011     ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar));
   1012     if (ret == NULL) {
   1013 	xmlGenericError(xmlGenericErrorContext,
   1014 		"xmlSaveUri: out of memory\n");
   1015 	return(NULL);
   1016     }
   1017     len = 0;
   1018 
   1019     if (uri->scheme != NULL) {
   1020 	p = uri->scheme;
   1021 	while (*p != 0) {
   1022 	    if (len >= max) {
   1023 		max *= 2;
   1024 		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
   1025 		if (temp == NULL) {
   1026 		    xmlGenericError(xmlGenericErrorContext,
   1027 			    "xmlSaveUri: out of memory\n");
   1028 		    xmlFree(ret);
   1029 		    return(NULL);
   1030 		}
   1031 		ret = temp;
   1032 	    }
   1033 	    ret[len++] = *p++;
   1034 	}
   1035 	if (len >= max) {
   1036 	    max *= 2;
   1037 	    temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
   1038 	    if (temp == NULL) {
   1039 		xmlGenericError(xmlGenericErrorContext,
   1040 			"xmlSaveUri: out of memory\n");
   1041 		xmlFree(ret);
   1042 		return(NULL);
   1043 	    }
   1044 	    ret = temp;
   1045 	}
   1046 	ret[len++] = ':';
   1047     }
   1048     if (uri->opaque != NULL) {
   1049 	p = uri->opaque;
   1050 	while (*p != 0) {
   1051 	    if (len + 3 >= max) {
   1052 		max *= 2;
   1053 		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
   1054 		if (temp == NULL) {
   1055 		    xmlGenericError(xmlGenericErrorContext,
   1056 			    "xmlSaveUri: out of memory\n");
   1057 		    xmlFree(ret);
   1058 		    return(NULL);
   1059 		}
   1060 		ret = temp;
   1061 	    }
   1062 	    if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p)))
   1063 		ret[len++] = *p++;
   1064 	    else {
   1065 		int val = *(unsigned char *)p++;
   1066 		int hi = val / 0x10, lo = val % 0x10;
   1067 		ret[len++] = '%';
   1068 		ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1069 		ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1070 	    }
   1071 	}
   1072     } else {
   1073 	if (uri->server != NULL) {
   1074 	    if (len + 3 >= max) {
   1075 		max *= 2;
   1076 		temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
   1077 		if (temp == NULL) {
   1078 		    xmlGenericError(xmlGenericErrorContext,
   1079 			    "xmlSaveUri: out of memory\n");
   1080                   xmlFree(ret);
   1081 		    return(NULL);
   1082 		}
   1083 		ret = temp;
   1084 	    }
   1085 	    ret[len++] = '/';
   1086 	    ret[len++] = '/';
   1087 	    if (uri->user != NULL) {
   1088 		p = uri->user;
   1089 		while (*p != 0) {
   1090 		    if (len + 3 >= max) {
   1091 			max *= 2;
   1092 			temp = (xmlChar *) xmlRealloc(ret,
   1093 				(max + 1) * sizeof(xmlChar));
   1094 			if (temp == NULL) {
   1095 			    xmlGenericError(xmlGenericErrorContext,
   1096 				    "xmlSaveUri: out of memory\n");
   1097 			    xmlFree(ret);
   1098 			    return(NULL);
   1099 			}
   1100 			ret = temp;
   1101 		    }
   1102 		    if ((IS_UNRESERVED(*(p))) ||
   1103 			((*(p) == ';')) || ((*(p) == ':')) ||
   1104 			((*(p) == '&')) || ((*(p) == '=')) ||
   1105 			((*(p) == '+')) || ((*(p) == '$')) ||
   1106 			((*(p) == ',')))
   1107 			ret[len++] = *p++;
   1108 		    else {
   1109 			int val = *(unsigned char *)p++;
   1110 			int hi = val / 0x10, lo = val % 0x10;
   1111 			ret[len++] = '%';
   1112 			ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1113 			ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1114 		    }
   1115 		}
   1116 		if (len + 3 >= max) {
   1117 		    max *= 2;
   1118 		    temp = (xmlChar *) xmlRealloc(ret,
   1119 			    (max + 1) * sizeof(xmlChar));
   1120 		    if (temp == NULL) {
   1121 			xmlGenericError(xmlGenericErrorContext,
   1122 				"xmlSaveUri: out of memory\n");
   1123 			xmlFree(ret);
   1124 			return(NULL);
   1125 		    }
   1126 		    ret = temp;
   1127 		}
   1128 		ret[len++] = '@';
   1129 	    }
   1130 	    p = uri->server;
   1131 	    while (*p != 0) {
   1132 		if (len >= max) {
   1133 		    max *= 2;
   1134 		    temp = (xmlChar *) xmlRealloc(ret,
   1135 			    (max + 1) * sizeof(xmlChar));
   1136 		    if (temp == NULL) {
   1137 			xmlGenericError(xmlGenericErrorContext,
   1138 				"xmlSaveUri: out of memory\n");
   1139 			xmlFree(ret);
   1140 			return(NULL);
   1141 		    }
   1142 		    ret = temp;
   1143 		}
   1144 		ret[len++] = *p++;
   1145 	    }
   1146 	    if (uri->port > 0) {
   1147 		if (len + 10 >= max) {
   1148 		    max *= 2;
   1149 		    temp = (xmlChar *) xmlRealloc(ret,
   1150 			    (max + 1) * sizeof(xmlChar));
   1151 		    if (temp == NULL) {
   1152 			xmlGenericError(xmlGenericErrorContext,
   1153 				"xmlSaveUri: out of memory\n");
   1154                      xmlFree(ret);
   1155 			return(NULL);
   1156 		    }
   1157 		    ret = temp;
   1158 		}
   1159 		len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
   1160 	    }
   1161 	} else if (uri->authority != NULL) {
   1162 	    if (len + 3 >= max) {
   1163 		max *= 2;
   1164 		temp = (xmlChar *) xmlRealloc(ret,
   1165 			(max + 1) * sizeof(xmlChar));
   1166 		if (temp == NULL) {
   1167 			xmlGenericError(xmlGenericErrorContext,
   1168 				"xmlSaveUri: out of memory\n");
   1169                      xmlFree(ret);
   1170 			return(NULL);
   1171 		    }
   1172 		    ret = temp;
   1173 	    }
   1174 	    ret[len++] = '/';
   1175 	    ret[len++] = '/';
   1176 	    p = uri->authority;
   1177 	    while (*p != 0) {
   1178 		if (len + 3 >= max) {
   1179 		    max *= 2;
   1180 		    temp = (xmlChar *) xmlRealloc(ret,
   1181 			    (max + 1) * sizeof(xmlChar));
   1182 		    if (temp == NULL) {
   1183 			xmlGenericError(xmlGenericErrorContext,
   1184 				"xmlSaveUri: out of memory\n");
   1185                      xmlFree(ret);
   1186 			return(NULL);
   1187 		    }
   1188 		    ret = temp;
   1189 		}
   1190 		if ((IS_UNRESERVED(*(p))) ||
   1191                     ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) ||
   1192                     ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||
   1193                     ((*(p) == '=')) || ((*(p) == '+')))
   1194 		    ret[len++] = *p++;
   1195 		else {
   1196 		    int val = *(unsigned char *)p++;
   1197 		    int hi = val / 0x10, lo = val % 0x10;
   1198 		    ret[len++] = '%';
   1199 		    ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1200 		    ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1201 		}
   1202 	    }
   1203 	} else if (uri->scheme != NULL) {
   1204 	    if (len + 3 >= max) {
   1205 		max *= 2;
   1206 		temp = (xmlChar *) xmlRealloc(ret,
   1207 			(max + 1) * sizeof(xmlChar));
   1208 		if (temp == NULL) {
   1209 			xmlGenericError(xmlGenericErrorContext,
   1210 				"xmlSaveUri: out of memory\n");
   1211                      xmlFree(ret);
   1212 			return(NULL);
   1213 		    }
   1214 		    ret = temp;
   1215 	    }
   1216 	    ret[len++] = '/';
   1217 	    ret[len++] = '/';
   1218 	}
   1219 	if (uri->path != NULL) {
   1220 	    p = uri->path;
   1221 	    /*
   1222 	     * the colon in file:///d: should not be escaped or
   1223 	     * Windows accesses fail later.
   1224 	     */
   1225 	    if ((uri->scheme != NULL) &&
   1226 		(p[0] == '/') &&
   1227 		(((p[1] >= 'a') && (p[1] <= 'z')) ||
   1228 		 ((p[1] >= 'A') && (p[1] <= 'Z'))) &&
   1229 		(p[2] == ':') &&
   1230 	        (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
   1231 		if (len + 3 >= max) {
   1232 		    max *= 2;
   1233 		    ret = (xmlChar *) xmlRealloc(ret,
   1234 			    (max + 1) * sizeof(xmlChar));
   1235 		    if (ret == NULL) {
   1236 			xmlGenericError(xmlGenericErrorContext,
   1237 				"xmlSaveUri: out of memory\n");
   1238 			return(NULL);
   1239 		    }
   1240 		}
   1241 		ret[len++] = *p++;
   1242 		ret[len++] = *p++;
   1243 		ret[len++] = *p++;
   1244 	    }
   1245 	    while (*p != 0) {
   1246 		if (len + 3 >= max) {
   1247 		    max *= 2;
   1248 		    temp = (xmlChar *) xmlRealloc(ret,
   1249 			    (max + 1) * sizeof(xmlChar));
   1250 		    if (temp == NULL) {
   1251 			xmlGenericError(xmlGenericErrorContext,
   1252 				"xmlSaveUri: out of memory\n");
   1253                      xmlFree(ret);
   1254 			return(NULL);
   1255 		    }
   1256 		    ret = temp;
   1257 		}
   1258 		if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) ||
   1259                     ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||
   1260 	            ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||
   1261 	            ((*(p) == ',')))
   1262 		    ret[len++] = *p++;
   1263 		else {
   1264 		    int val = *(unsigned char *)p++;
   1265 		    int hi = val / 0x10, lo = val % 0x10;
   1266 		    ret[len++] = '%';
   1267 		    ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1268 		    ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1269 		}
   1270 	    }
   1271 	}
   1272 	if (uri->query_raw != NULL) {
   1273 	    if (len + 1 >= max) {
   1274 		max *= 2;
   1275 		temp = (xmlChar *) xmlRealloc(ret,
   1276 			(max + 1) * sizeof(xmlChar));
   1277 		if (temp == NULL) {
   1278 			xmlGenericError(xmlGenericErrorContext,
   1279 				"xmlSaveUri: out of memory\n");
   1280                      xmlFree(ret);
   1281 			return(NULL);
   1282 		    }
   1283 		    ret = temp;
   1284 	    }
   1285 	    ret[len++] = '?';
   1286 	    p = uri->query_raw;
   1287 	    while (*p != 0) {
   1288 		if (len + 1 >= max) {
   1289 		    max *= 2;
   1290 		    temp = (xmlChar *) xmlRealloc(ret,
   1291 			    (max + 1) * sizeof(xmlChar));
   1292 		    if (temp == NULL) {
   1293 			xmlGenericError(xmlGenericErrorContext,
   1294 				"xmlSaveUri: out of memory\n");
   1295                      xmlFree(ret);
   1296 			return(NULL);
   1297 		    }
   1298 		    ret = temp;
   1299 		}
   1300 		ret[len++] = *p++;
   1301 	    }
   1302 	} else if (uri->query != NULL) {
   1303 	    if (len + 3 >= max) {
   1304 		max *= 2;
   1305 		temp = (xmlChar *) xmlRealloc(ret,
   1306 			(max + 1) * sizeof(xmlChar));
   1307 		if (temp == NULL) {
   1308 			xmlGenericError(xmlGenericErrorContext,
   1309 				"xmlSaveUri: out of memory\n");
   1310                      xmlFree(ret);
   1311 			return(NULL);
   1312 		    }
   1313 		    ret = temp;
   1314 	    }
   1315 	    ret[len++] = '?';
   1316 	    p = uri->query;
   1317 	    while (*p != 0) {
   1318 		if (len + 3 >= max) {
   1319 		    max *= 2;
   1320 		    temp = (xmlChar *) xmlRealloc(ret,
   1321 			    (max + 1) * sizeof(xmlChar));
   1322 		    if (temp == NULL) {
   1323 			xmlGenericError(xmlGenericErrorContext,
   1324 				"xmlSaveUri: out of memory\n");
   1325                      xmlFree(ret);
   1326 			return(NULL);
   1327 		    }
   1328 		    ret = temp;
   1329 		}
   1330 		if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
   1331 		    ret[len++] = *p++;
   1332 		else {
   1333 		    int val = *(unsigned char *)p++;
   1334 		    int hi = val / 0x10, lo = val % 0x10;
   1335 		    ret[len++] = '%';
   1336 		    ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1337 		    ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1338 		}
   1339 	    }
   1340 	}
   1341     }
   1342     if (uri->fragment != NULL) {
   1343 	if (len + 3 >= max) {
   1344 	    max *= 2;
   1345 	    temp = (xmlChar *) xmlRealloc(ret,
   1346 		    (max + 1) * sizeof(xmlChar));
   1347 	    if (temp == NULL) {
   1348 			xmlGenericError(xmlGenericErrorContext,
   1349 				"xmlSaveUri: out of memory\n");
   1350                      xmlFree(ret);
   1351 			return(NULL);
   1352 		    }
   1353 		    ret = temp;
   1354 	}
   1355 	ret[len++] = '#';
   1356 	p = uri->fragment;
   1357 	while (*p != 0) {
   1358 	    if (len + 3 >= max) {
   1359 		max *= 2;
   1360 		temp = (xmlChar *) xmlRealloc(ret,
   1361 			(max + 1) * sizeof(xmlChar));
   1362 		if (temp == NULL) {
   1363 			xmlGenericError(xmlGenericErrorContext,
   1364 				"xmlSaveUri: out of memory\n");
   1365                      xmlFree(ret);
   1366 			return(NULL);
   1367 		    }
   1368 		    ret = temp;
   1369 	    }
   1370 	    if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
   1371 		ret[len++] = *p++;
   1372 	    else {
   1373 		int val = *(unsigned char *)p++;
   1374 		int hi = val / 0x10, lo = val % 0x10;
   1375 		ret[len++] = '%';
   1376 		ret[len++] = hi + (hi > 9? 'A'-10 : '0');
   1377 		ret[len++] = lo + (lo > 9? 'A'-10 : '0');
   1378 	    }
   1379 	}
   1380     }
   1381     if (len >= max) {
   1382 	max *= 2;
   1383 	temp = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar));
   1384 	if (temp == NULL) {
   1385 			xmlGenericError(xmlGenericErrorContext,
   1386 				"xmlSaveUri: out of memory\n");
   1387                      xmlFree(ret);
   1388 			return(NULL);
   1389 		    }
   1390 		    ret = temp;
   1391     }
   1392     ret[len] = 0;
   1393     return(ret);
   1394 }
   1395 
   1396 /**
   1397  * xmlPrintURI:
   1398  * @stream:  a FILE* for the output
   1399  * @uri:  pointer to an xmlURI
   1400  *
   1401  * Prints the URI in the stream @stream.
   1402  */
   1403 void
   1404 xmlPrintURI(FILE *stream, xmlURIPtr uri) {
   1405     xmlChar *out;
   1406 
   1407     out = xmlSaveUri(uri);
   1408     if (out != NULL) {
   1409 	fprintf(stream, "%s", (char *) out);
   1410 	xmlFree(out);
   1411     }
   1412 }
   1413 
   1414 /**
   1415  * xmlCleanURI:
   1416  * @uri:  pointer to an xmlURI
   1417  *
   1418  * Make sure the xmlURI struct is free of content
   1419  */
   1420 static void
   1421 xmlCleanURI(xmlURIPtr uri) {
   1422     if (uri == NULL) return;
   1423 
   1424     if (uri->scheme != NULL) xmlFree(uri->scheme);
   1425     uri->scheme = NULL;
   1426     if (uri->server != NULL) xmlFree(uri->server);
   1427     uri->server = NULL;
   1428     if (uri->user != NULL) xmlFree(uri->user);
   1429     uri->user = NULL;
   1430     if (uri->path != NULL) xmlFree(uri->path);
   1431     uri->path = NULL;
   1432     if (uri->fragment != NULL) xmlFree(uri->fragment);
   1433     uri->fragment = NULL;
   1434     if (uri->opaque != NULL) xmlFree(uri->opaque);
   1435     uri->opaque = NULL;
   1436     if (uri->authority != NULL) xmlFree(uri->authority);
   1437     uri->authority = NULL;
   1438     if (uri->query != NULL) xmlFree(uri->query);
   1439     uri->query = NULL;
   1440     if (uri->query_raw != NULL) xmlFree(uri->query_raw);
   1441     uri->query_raw = NULL;
   1442 }
   1443 
   1444 /**
   1445  * xmlFreeURI:
   1446  * @uri:  pointer to an xmlURI
   1447  *
   1448  * Free up the xmlURI struct
   1449  */
   1450 void
   1451 xmlFreeURI(xmlURIPtr uri) {
   1452     if (uri == NULL) return;
   1453 
   1454     if (uri->scheme != NULL) xmlFree(uri->scheme);
   1455     if (uri->server != NULL) xmlFree(uri->server);
   1456     if (uri->user != NULL) xmlFree(uri->user);
   1457     if (uri->path != NULL) xmlFree(uri->path);
   1458     if (uri->fragment != NULL) xmlFree(uri->fragment);
   1459     if (uri->opaque != NULL) xmlFree(uri->opaque);
   1460     if (uri->authority != NULL) xmlFree(uri->authority);
   1461     if (uri->query != NULL) xmlFree(uri->query);
   1462     if (uri->query_raw != NULL) xmlFree(uri->query_raw);
   1463     xmlFree(uri);
   1464 }
   1465 
   1466 /************************************************************************
   1467  *									*
   1468  *			Helper functions				*
   1469  *									*
   1470  ************************************************************************/
   1471 
   1472 /**
   1473  * xmlNormalizeURIPath:
   1474  * @path:  pointer to the path string
   1475  *
   1476  * Applies the 5 normalization steps to a path string--that is, RFC 2396
   1477  * Section 5.2, steps 6.c through 6.g.
   1478  *
   1479  * Normalization occurs directly on the string, no new allocation is done
   1480  *
   1481  * Returns 0 or an error code
   1482  */
   1483 int
   1484 xmlNormalizeURIPath(char *path) {
   1485     char *cur, *out;
   1486 
   1487     if (path == NULL)
   1488 	return(-1);
   1489 
   1490     /* Skip all initial "/" chars.  We want to get to the beginning of the
   1491      * first non-empty segment.
   1492      */
   1493     cur = path;
   1494     while (cur[0] == '/')
   1495       ++cur;
   1496     if (cur[0] == '\0')
   1497       return(0);
   1498 
   1499     /* Keep everything we've seen so far.  */
   1500     out = cur;
   1501 
   1502     /*
   1503      * Analyze each segment in sequence for cases (c) and (d).
   1504      */
   1505     while (cur[0] != '\0') {
   1506 	/*
   1507 	 * c) All occurrences of "./", where "." is a complete path segment,
   1508 	 *    are removed from the buffer string.
   1509 	 */
   1510 	if ((cur[0] == '.') && (cur[1] == '/')) {
   1511 	    cur += 2;
   1512 	    /* '//' normalization should be done at this point too */
   1513 	    while (cur[0] == '/')
   1514 		cur++;
   1515 	    continue;
   1516 	}
   1517 
   1518 	/*
   1519 	 * d) If the buffer string ends with "." as a complete path segment,
   1520 	 *    that "." is removed.
   1521 	 */
   1522 	if ((cur[0] == '.') && (cur[1] == '\0'))
   1523 	    break;
   1524 
   1525 	/* Otherwise keep the segment.  */
   1526 	while (cur[0] != '/') {
   1527             if (cur[0] == '\0')
   1528               goto done_cd;
   1529 	    (out++)[0] = (cur++)[0];
   1530 	}
   1531 	/* nomalize // */
   1532 	while ((cur[0] == '/') && (cur[1] == '/'))
   1533 	    cur++;
   1534 
   1535         (out++)[0] = (cur++)[0];
   1536     }
   1537  done_cd:
   1538     out[0] = '\0';
   1539 
   1540     /* Reset to the beginning of the first segment for the next sequence.  */
   1541     cur = path;
   1542     while (cur[0] == '/')
   1543       ++cur;
   1544     if (cur[0] == '\0')
   1545 	return(0);
   1546 
   1547     /*
   1548      * Analyze each segment in sequence for cases (e) and (f).
   1549      *
   1550      * e) All occurrences of "<segment>/../", where <segment> is a
   1551      *    complete path segment not equal to "..", are removed from the
   1552      *    buffer string.  Removal of these path segments is performed
   1553      *    iteratively, removing the leftmost matching pattern on each
   1554      *    iteration, until no matching pattern remains.
   1555      *
   1556      * f) If the buffer string ends with "<segment>/..", where <segment>
   1557      *    is a complete path segment not equal to "..", that
   1558      *    "<segment>/.." is removed.
   1559      *
   1560      * To satisfy the "iterative" clause in (e), we need to collapse the
   1561      * string every time we find something that needs to be removed.  Thus,
   1562      * we don't need to keep two pointers into the string: we only need a
   1563      * "current position" pointer.
   1564      */
   1565     while (1) {
   1566         char *segp, *tmp;
   1567 
   1568         /* At the beginning of each iteration of this loop, "cur" points to
   1569          * the first character of the segment we want to examine.
   1570          */
   1571 
   1572         /* Find the end of the current segment.  */
   1573         segp = cur;
   1574         while ((segp[0] != '/') && (segp[0] != '\0'))
   1575           ++segp;
   1576 
   1577         /* If this is the last segment, we're done (we need at least two
   1578          * segments to meet the criteria for the (e) and (f) cases).
   1579          */
   1580         if (segp[0] == '\0')
   1581           break;
   1582 
   1583         /* If the first segment is "..", or if the next segment _isn't_ "..",
   1584          * keep this segment and try the next one.
   1585          */
   1586         ++segp;
   1587         if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3))
   1588             || ((segp[0] != '.') || (segp[1] != '.')
   1589                 || ((segp[2] != '/') && (segp[2] != '\0')))) {
   1590           cur = segp;
   1591           continue;
   1592         }
   1593 
   1594         /* If we get here, remove this segment and the next one and back up
   1595          * to the previous segment (if there is one), to implement the
   1596          * "iteratively" clause.  It's pretty much impossible to back up
   1597          * while maintaining two pointers into the buffer, so just compact
   1598          * the whole buffer now.
   1599          */
   1600 
   1601         /* If this is the end of the buffer, we're done.  */
   1602         if (segp[2] == '\0') {
   1603           cur[0] = '\0';
   1604           break;
   1605         }
   1606         /* Valgrind complained, strcpy(cur, segp + 3); */
   1607 	/* string will overlap, do not use strcpy */
   1608 	tmp = cur;
   1609 	segp += 3;
   1610 	while ((*tmp++ = *segp++) != 0);
   1611 
   1612         /* If there are no previous segments, then keep going from here.  */
   1613         segp = cur;
   1614         while ((segp > path) && ((--segp)[0] == '/'))
   1615           ;
   1616         if (segp == path)
   1617           continue;
   1618 
   1619         /* "segp" is pointing to the end of a previous segment; find it's
   1620          * start.  We need to back up to the previous segment and start
   1621          * over with that to handle things like "foo/bar/../..".  If we
   1622          * don't do this, then on the first pass we'll remove the "bar/..",
   1623          * but be pointing at the second ".." so we won't realize we can also
   1624          * remove the "foo/..".
   1625          */
   1626         cur = segp;
   1627         while ((cur > path) && (cur[-1] != '/'))
   1628           --cur;
   1629     }
   1630     out[0] = '\0';
   1631 
   1632     /*
   1633      * g) If the resulting buffer string still begins with one or more
   1634      *    complete path segments of "..", then the reference is
   1635      *    considered to be in error. Implementations may handle this
   1636      *    error by retaining these components in the resolved path (i.e.,
   1637      *    treating them as part of the final URI), by removing them from
   1638      *    the resolved path (i.e., discarding relative levels above the
   1639      *    root), or by avoiding traversal of the reference.
   1640      *
   1641      * We discard them from the final path.
   1642      */
   1643     if (path[0] == '/') {
   1644       cur = path;
   1645       while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.')
   1646              && ((cur[3] == '/') || (cur[3] == '\0')))
   1647 	cur += 3;
   1648 
   1649       if (cur != path) {
   1650 	out = path;
   1651 	while (cur[0] != '\0')
   1652           (out++)[0] = (cur++)[0];
   1653 	out[0] = 0;
   1654       }
   1655     }
   1656 
   1657     return(0);
   1658 }
   1659 
   1660 static int is_hex(char c) {
   1661     if (((c >= '0') && (c <= '9')) ||
   1662         ((c >= 'a') && (c <= 'f')) ||
   1663         ((c >= 'A') && (c <= 'F')))
   1664 	return(1);
   1665     return(0);
   1666 }
   1667 
   1668 /**
   1669  * xmlURIUnescapeString:
   1670  * @str:  the string to unescape
   1671  * @len:   the length in bytes to unescape (or <= 0 to indicate full string)
   1672  * @target:  optional destination buffer
   1673  *
   1674  * Unescaping routine, but does not check that the string is an URI. The
   1675  * output is a direct unsigned char translation of %XX values (no encoding)
   1676  * Note that the length of the result can only be smaller or same size as
   1677  * the input string.
   1678  *
   1679  * Returns a copy of the string, but unescaped, will return NULL only in case
   1680  * of error
   1681  */
   1682 char *
   1683 xmlURIUnescapeString(const char *str, int len, char *target) {
   1684     char *ret, *out;
   1685     const char *in;
   1686 
   1687     if (str == NULL)
   1688 	return(NULL);
   1689     if (len <= 0) len = strlen(str);
   1690     if (len < 0) return(NULL);
   1691 
   1692     if (target == NULL) {
   1693 	ret = (char *) xmlMallocAtomic(len + 1);
   1694 	if (ret == NULL) {
   1695 	    xmlGenericError(xmlGenericErrorContext,
   1696 		    "xmlURIUnescapeString: out of memory\n");
   1697 	    return(NULL);
   1698 	}
   1699     } else
   1700 	ret = target;
   1701     in = str;
   1702     out = ret;
   1703     while(len > 0) {
   1704 	if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) {
   1705 	    in++;
   1706 	    if ((*in >= '0') && (*in <= '9'))
   1707 	        *out = (*in - '0');
   1708 	    else if ((*in >= 'a') && (*in <= 'f'))
   1709 	        *out = (*in - 'a') + 10;
   1710 	    else if ((*in >= 'A') && (*in <= 'F'))
   1711 	        *out = (*in - 'A') + 10;
   1712 	    in++;
   1713 	    if ((*in >= '0') && (*in <= '9'))
   1714 	        *out = *out * 16 + (*in - '0');
   1715 	    else if ((*in >= 'a') && (*in <= 'f'))
   1716 	        *out = *out * 16 + (*in - 'a') + 10;
   1717 	    else if ((*in >= 'A') && (*in <= 'F'))
   1718 	        *out = *out * 16 + (*in - 'A') + 10;
   1719 	    in++;
   1720 	    len -= 3;
   1721 	    out++;
   1722 	} else {
   1723 	    *out++ = *in++;
   1724 	    len--;
   1725 	}
   1726     }
   1727     *out = 0;
   1728     return(ret);
   1729 }
   1730 
   1731 /**
   1732  * xmlURIEscapeStr:
   1733  * @str:  string to escape
   1734  * @list: exception list string of chars not to escape
   1735  *
   1736  * This routine escapes a string to hex, ignoring reserved characters (a-z)
   1737  * and the characters in the exception list.
   1738  *
   1739  * Returns a new escaped string or NULL in case of error.
   1740  */
   1741 xmlChar *
   1742 xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) {
   1743     xmlChar *ret, ch;
   1744     xmlChar *temp;
   1745     const xmlChar *in;
   1746 
   1747     unsigned int len, out;
   1748 
   1749     if (str == NULL)
   1750 	return(NULL);
   1751     if (str[0] == 0)
   1752 	return(xmlStrdup(str));
   1753     len = xmlStrlen(str);
   1754     if (!(len > 0)) return(NULL);
   1755 
   1756     len += 20;
   1757     ret = (xmlChar *) xmlMallocAtomic(len);
   1758     if (ret == NULL) {
   1759 	xmlGenericError(xmlGenericErrorContext,
   1760 		"xmlURIEscapeStr: out of memory\n");
   1761 	return(NULL);
   1762     }
   1763     in = (const xmlChar *) str;
   1764     out = 0;
   1765     while(*in != 0) {
   1766 	if (len - out <= 3) {
   1767 	    len += 20;
   1768 	    temp = (xmlChar *) xmlRealloc(ret, len);
   1769 	    if (temp == NULL) {
   1770 		xmlGenericError(xmlGenericErrorContext,
   1771 			"xmlURIEscapeStr: out of memory\n");
   1772 		xmlFree(ret);
   1773 		return(NULL);
   1774 	    }
   1775 	    ret = temp;
   1776 	}
   1777 
   1778 	ch = *in;
   1779 
   1780 	if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!xmlStrchr(list, ch))) {
   1781 	    unsigned char val;
   1782 	    ret[out++] = '%';
   1783 	    val = ch >> 4;
   1784 	    if (val <= 9)
   1785 		ret[out++] = '0' + val;
   1786 	    else
   1787 		ret[out++] = 'A' + val - 0xA;
   1788 	    val = ch & 0xF;
   1789 	    if (val <= 9)
   1790 		ret[out++] = '0' + val;
   1791 	    else
   1792 		ret[out++] = 'A' + val - 0xA;
   1793 	    in++;
   1794 	} else {
   1795 	    ret[out++] = *in++;
   1796 	}
   1797 
   1798     }
   1799     ret[out] = 0;
   1800     return(ret);
   1801 }
   1802 
   1803 /**
   1804  * xmlURIEscape:
   1805  * @str:  the string of the URI to escape
   1806  *
   1807  * Escaping routine, does not do validity checks !
   1808  * It will try to escape the chars needing this, but this is heuristic
   1809  * based it's impossible to be sure.
   1810  *
   1811  * Returns an copy of the string, but escaped
   1812  *
   1813  * 25 May 2001
   1814  * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly
   1815  * according to RFC2396.
   1816  *   - Carl Douglas
   1817  */
   1818 xmlChar *
   1819 xmlURIEscape(const xmlChar * str)
   1820 {
   1821     xmlChar *ret, *segment = NULL;
   1822     xmlURIPtr uri;
   1823     int ret2;
   1824 
   1825 #define NULLCHK(p) if(!p) { \
   1826                    xmlGenericError(xmlGenericErrorContext, \
   1827                         "xmlURIEscape: out of memory\n"); \
   1828                         xmlFreeURI(uri); \
   1829                         return NULL; } \
   1830 
   1831     if (str == NULL)
   1832         return (NULL);
   1833 
   1834     uri = xmlCreateURI();
   1835     if (uri != NULL) {
   1836 	/*
   1837 	 * Allow escaping errors in the unescaped form
   1838 	 */
   1839         uri->cleanup = 1;
   1840         ret2 = xmlParseURIReference(uri, (const char *)str);
   1841         if (ret2) {
   1842             xmlFreeURI(uri);
   1843             return (NULL);
   1844         }
   1845     }
   1846 
   1847     if (!uri)
   1848         return NULL;
   1849 
   1850     ret = NULL;
   1851 
   1852     if (uri->scheme) {
   1853         segment = xmlURIEscapeStr(BAD_CAST uri->scheme, BAD_CAST "+-.");
   1854         NULLCHK(segment)
   1855         ret = xmlStrcat(ret, segment);
   1856         ret = xmlStrcat(ret, BAD_CAST ":");
   1857         xmlFree(segment);
   1858     }
   1859 
   1860     if (uri->authority) {
   1861         segment =
   1862             xmlURIEscapeStr(BAD_CAST uri->authority, BAD_CAST "/?;:@");
   1863         NULLCHK(segment)
   1864         ret = xmlStrcat(ret, BAD_CAST "//");
   1865         ret = xmlStrcat(ret, segment);
   1866         xmlFree(segment);
   1867     }
   1868 
   1869     if (uri->user) {
   1870         segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,");
   1871         NULLCHK(segment)
   1872 		ret = xmlStrcat(ret,BAD_CAST "//");
   1873         ret = xmlStrcat(ret, segment);
   1874         ret = xmlStrcat(ret, BAD_CAST "@");
   1875         xmlFree(segment);
   1876     }
   1877 
   1878     if (uri->server) {
   1879         segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@");
   1880         NULLCHK(segment)
   1881 		if (uri->user == NULL)
   1882 		ret = xmlStrcat(ret, BAD_CAST "//");
   1883         ret = xmlStrcat(ret, segment);
   1884         xmlFree(segment);
   1885     }
   1886 
   1887     if (uri->port) {
   1888         xmlChar port[10];
   1889 
   1890         snprintf((char *) port, 10, "%d", uri->port);
   1891         ret = xmlStrcat(ret, BAD_CAST ":");
   1892         ret = xmlStrcat(ret, port);
   1893     }
   1894 
   1895     if (uri->path) {
   1896         segment =
   1897             xmlURIEscapeStr(BAD_CAST uri->path, BAD_CAST ":@&=+$,/?;");
   1898         NULLCHK(segment)
   1899         ret = xmlStrcat(ret, segment);
   1900         xmlFree(segment);
   1901     }
   1902 
   1903     if (uri->query_raw) {
   1904         ret = xmlStrcat(ret, BAD_CAST "?");
   1905         ret = xmlStrcat(ret, BAD_CAST uri->query_raw);
   1906     }
   1907     else if (uri->query) {
   1908         segment =
   1909             xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$");
   1910         NULLCHK(segment)
   1911         ret = xmlStrcat(ret, BAD_CAST "?");
   1912         ret = xmlStrcat(ret, segment);
   1913         xmlFree(segment);
   1914     }
   1915 
   1916     if (uri->opaque) {
   1917         segment = xmlURIEscapeStr(BAD_CAST uri->opaque, BAD_CAST "");
   1918         NULLCHK(segment)
   1919         ret = xmlStrcat(ret, segment);
   1920         xmlFree(segment);
   1921     }
   1922 
   1923     if (uri->fragment) {
   1924         segment = xmlURIEscapeStr(BAD_CAST uri->fragment, BAD_CAST "#");
   1925         NULLCHK(segment)
   1926         ret = xmlStrcat(ret, BAD_CAST "#");
   1927         ret = xmlStrcat(ret, segment);
   1928         xmlFree(segment);
   1929     }
   1930 
   1931     xmlFreeURI(uri);
   1932 #undef NULLCHK
   1933 
   1934     return (ret);
   1935 }
   1936 
   1937 /************************************************************************
   1938  *									*
   1939  *			Public functions				*
   1940  *									*
   1941  ************************************************************************/
   1942 
   1943 /**
   1944  * xmlBuildURI:
   1945  * @URI:  the URI instance found in the document
   1946  * @base:  the base value
   1947  *
   1948  * Computes he final URI of the reference done by checking that
   1949  * the given URI is valid, and building the final URI using the
   1950  * base URI. This is processed according to section 5.2 of the
   1951  * RFC 2396
   1952  *
   1953  * 5.2. Resolving Relative References to Absolute Form
   1954  *
   1955  * Returns a new URI string (to be freed by the caller) or NULL in case
   1956  *         of error.
   1957  */
   1958 xmlChar *
   1959 xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
   1960     xmlChar *val = NULL;
   1961     int ret, len, indx, cur, out;
   1962     xmlURIPtr ref = NULL;
   1963     xmlURIPtr bas = NULL;
   1964     xmlURIPtr res = NULL;
   1965 
   1966     /*
   1967      * 1) The URI reference is parsed into the potential four components and
   1968      *    fragment identifier, as described in Section 4.3.
   1969      *
   1970      *    NOTE that a completely empty URI is treated by modern browsers
   1971      *    as a reference to "." rather than as a synonym for the current
   1972      *    URI.  Should we do that here?
   1973      */
   1974     if (URI == NULL)
   1975 	ret = -1;
   1976     else {
   1977 	if (*URI) {
   1978 	    ref = xmlCreateURI();
   1979 	    if (ref == NULL)
   1980 		goto done;
   1981 	    ret = xmlParseURIReference(ref, (const char *) URI);
   1982 	}
   1983 	else
   1984 	    ret = 0;
   1985     }
   1986     if (ret != 0)
   1987 	goto done;
   1988     if ((ref != NULL) && (ref->scheme != NULL)) {
   1989 	/*
   1990 	 * The URI is absolute don't modify.
   1991 	 */
   1992 	val = xmlStrdup(URI);
   1993 	goto done;
   1994     }
   1995     if (base == NULL)
   1996 	ret = -1;
   1997     else {
   1998 	bas = xmlCreateURI();
   1999 	if (bas == NULL)
   2000 	    goto done;
   2001 	ret = xmlParseURIReference(bas, (const char *) base);
   2002     }
   2003     if (ret != 0) {
   2004 	if (ref)
   2005 	    val = xmlSaveUri(ref);
   2006 	goto done;
   2007     }
   2008     if (ref == NULL) {
   2009 	/*
   2010 	 * the base fragment must be ignored
   2011 	 */
   2012 	if (bas->fragment != NULL) {
   2013 	    xmlFree(bas->fragment);
   2014 	    bas->fragment = NULL;
   2015 	}
   2016 	val = xmlSaveUri(bas);
   2017 	goto done;
   2018     }
   2019 
   2020     /*
   2021      * 2) If the path component is empty and the scheme, authority, and
   2022      *    query components are undefined, then it is a reference to the
   2023      *    current document and we are done.  Otherwise, the reference URI's
   2024      *    query and fragment components are defined as found (or not found)
   2025      *    within the URI reference and not inherited from the base URI.
   2026      *
   2027      *    NOTE that in modern browsers, the parsing differs from the above
   2028      *    in the following aspect:  the query component is allowed to be
   2029      *    defined while still treating this as a reference to the current
   2030      *    document.
   2031      */
   2032     res = xmlCreateURI();
   2033     if (res == NULL)
   2034 	goto done;
   2035     if ((ref->scheme == NULL) && (ref->path == NULL) &&
   2036 	((ref->authority == NULL) && (ref->server == NULL))) {
   2037 	if (bas->scheme != NULL)
   2038 	    res->scheme = xmlMemStrdup(bas->scheme);
   2039 	if (bas->authority != NULL)
   2040 	    res->authority = xmlMemStrdup(bas->authority);
   2041 	else if (bas->server != NULL) {
   2042 	    res->server = xmlMemStrdup(bas->server);
   2043 	    if (bas->user != NULL)
   2044 		res->user = xmlMemStrdup(bas->user);
   2045 	    res->port = bas->port;
   2046 	}
   2047 	if (bas->path != NULL)
   2048 	    res->path = xmlMemStrdup(bas->path);
   2049 	if (ref->query_raw != NULL)
   2050 	    res->query_raw = xmlMemStrdup (ref->query_raw);
   2051 	else if (ref->query != NULL)
   2052 	    res->query = xmlMemStrdup(ref->query);
   2053 	else if (bas->query_raw != NULL)
   2054 	    res->query_raw = xmlMemStrdup(bas->query_raw);
   2055 	else if (bas->query != NULL)
   2056 	    res->query = xmlMemStrdup(bas->query);
   2057 	if (ref->fragment != NULL)
   2058 	    res->fragment = xmlMemStrdup(ref->fragment);
   2059 	goto step_7;
   2060     }
   2061 
   2062     /*
   2063      * 3) If the scheme component is defined, indicating that the reference
   2064      *    starts with a scheme name, then the reference is interpreted as an
   2065      *    absolute URI and we are done.  Otherwise, the reference URI's
   2066      *    scheme is inherited from the base URI's scheme component.
   2067      */
   2068     if (ref->scheme != NULL) {
   2069 	val = xmlSaveUri(ref);
   2070 	goto done;
   2071     }
   2072     if (bas->scheme != NULL)
   2073 	res->scheme = xmlMemStrdup(bas->scheme);
   2074 
   2075     if (ref->query_raw != NULL)
   2076 	res->query_raw = xmlMemStrdup(ref->query_raw);
   2077     else if (ref->query != NULL)
   2078 	res->query = xmlMemStrdup(ref->query);
   2079     if (ref->fragment != NULL)
   2080 	res->fragment = xmlMemStrdup(ref->fragment);
   2081 
   2082     /*
   2083      * 4) If the authority component is defined, then the reference is a
   2084      *    network-path and we skip to step 7.  Otherwise, the reference
   2085      *    URI's authority is inherited from the base URI's authority
   2086      *    component, which will also be undefined if the URI scheme does not
   2087      *    use an authority component.
   2088      */
   2089     if ((ref->authority != NULL) || (ref->server != NULL)) {
   2090 	if (ref->authority != NULL)
   2091 	    res->authority = xmlMemStrdup(ref->authority);
   2092 	else {
   2093 	    res->server = xmlMemStrdup(ref->server);
   2094 	    if (ref->user != NULL)
   2095 		res->user = xmlMemStrdup(ref->user);
   2096             res->port = ref->port;
   2097 	}
   2098 	if (ref->path != NULL)
   2099 	    res->path = xmlMemStrdup(ref->path);
   2100 	goto step_7;
   2101     }
   2102     if (bas->authority != NULL)
   2103 	res->authority = xmlMemStrdup(bas->authority);
   2104     else if (bas->server != NULL) {
   2105 	res->server = xmlMemStrdup(bas->server);
   2106 	if (bas->user != NULL)
   2107 	    res->user = xmlMemStrdup(bas->user);
   2108 	res->port = bas->port;
   2109     }
   2110 
   2111     /*
   2112      * 5) If the path component begins with a slash character ("/"), then
   2113      *    the reference is an absolute-path and we skip to step 7.
   2114      */
   2115     if ((ref->path != NULL) && (ref->path[0] == '/')) {
   2116 	res->path = xmlMemStrdup(ref->path);
   2117 	goto step_7;
   2118     }
   2119 
   2120 
   2121     /*
   2122      * 6) If this step is reached, then we are resolving a relative-path
   2123      *    reference.  The relative path needs to be merged with the base
   2124      *    URI's path.  Although there are many ways to do this, we will
   2125      *    describe a simple method using a separate string buffer.
   2126      *
   2127      * Allocate a buffer large enough for the result string.
   2128      */
   2129     len = 2; /* extra / and 0 */
   2130     if (ref->path != NULL)
   2131 	len += strlen(ref->path);
   2132     if (bas->path != NULL)
   2133 	len += strlen(bas->path);
   2134     res->path = (char *) xmlMallocAtomic(len);
   2135     if (res->path == NULL) {
   2136 	xmlGenericError(xmlGenericErrorContext,
   2137 		"xmlBuildURI: out of memory\n");
   2138 	goto done;
   2139     }
   2140     res->path[0] = 0;
   2141 
   2142     /*
   2143      * a) All but the last segment of the base URI's path component is
   2144      *    copied to the buffer.  In other words, any characters after the
   2145      *    last (right-most) slash character, if any, are excluded.
   2146      */
   2147     cur = 0;
   2148     out = 0;
   2149     if (bas->path != NULL) {
   2150 	while (bas->path[cur] != 0) {
   2151 	    while ((bas->path[cur] != 0) && (bas->path[cur] != '/'))
   2152 		cur++;
   2153 	    if (bas->path[cur] == 0)
   2154 		break;
   2155 
   2156 	    cur++;
   2157 	    while (out < cur) {
   2158 		res->path[out] = bas->path[out];
   2159 		out++;
   2160 	    }
   2161 	}
   2162     }
   2163     res->path[out] = 0;
   2164 
   2165     /*
   2166      * b) The reference's path component is appended to the buffer
   2167      *    string.
   2168      */
   2169     if (ref->path != NULL && ref->path[0] != 0) {
   2170 	indx = 0;
   2171 	/*
   2172 	 * Ensure the path includes a '/'
   2173 	 */
   2174 	if ((out == 0) && (bas->server != NULL))
   2175 	    res->path[out++] = '/';
   2176 	while (ref->path[indx] != 0) {
   2177 	    res->path[out++] = ref->path[indx++];
   2178 	}
   2179     }
   2180     res->path[out] = 0;
   2181 
   2182     /*
   2183      * Steps c) to h) are really path normalization steps
   2184      */
   2185     xmlNormalizeURIPath(res->path);
   2186 
   2187 step_7:
   2188 
   2189     /*
   2190      * 7) The resulting URI components, including any inherited from the
   2191      *    base URI, are recombined to give the absolute form of the URI
   2192      *    reference.
   2193      */
   2194     val = xmlSaveUri(res);
   2195 
   2196 done:
   2197     if (ref != NULL)
   2198 	xmlFreeURI(ref);
   2199     if (bas != NULL)
   2200 	xmlFreeURI(bas);
   2201     if (res != NULL)
   2202 	xmlFreeURI(res);
   2203     return(val);
   2204 }
   2205 
   2206 /**
   2207  * xmlBuildRelativeURI:
   2208  * @URI:  the URI reference under consideration
   2209  * @base:  the base value
   2210  *
   2211  * Expresses the URI of the reference in terms relative to the
   2212  * base.  Some examples of this operation include:
   2213  *     base = "http://site1.com/docs/book1.html"
   2214  *        URI input                        URI returned
   2215  *     docs/pic1.gif                    pic1.gif
   2216  *     docs/img/pic1.gif                img/pic1.gif
   2217  *     img/pic1.gif                     ../img/pic1.gif
   2218  *     http://site1.com/docs/pic1.gif   pic1.gif
   2219  *     http://site2.com/docs/pic1.gif   http://site2.com/docs/pic1.gif
   2220  *
   2221  *     base = "docs/book1.html"
   2222  *        URI input                        URI returned
   2223  *     docs/pic1.gif                    pic1.gif
   2224  *     docs/img/pic1.gif                img/pic1.gif
   2225  *     img/pic1.gif                     ../img/pic1.gif
   2226  *     http://site1.com/docs/pic1.gif   http://site1.com/docs/pic1.gif
   2227  *
   2228  *
   2229  * Note: if the URI reference is really wierd or complicated, it may be
   2230  *       worthwhile to first convert it into a "nice" one by calling
   2231  *       xmlBuildURI (using 'base') before calling this routine,
   2232  *       since this routine (for reasonable efficiency) assumes URI has
   2233  *       already been through some validation.
   2234  *
   2235  * Returns a new URI string (to be freed by the caller) or NULL in case
   2236  * error.
   2237  */
   2238 xmlChar *
   2239 xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
   2240 {
   2241     xmlChar *val = NULL;
   2242     int ret;
   2243     int ix;
   2244     int pos = 0;
   2245     int nbslash = 0;
   2246     int len;
   2247     xmlURIPtr ref = NULL;
   2248     xmlURIPtr bas = NULL;
   2249     xmlChar *bptr, *uptr, *vptr;
   2250     int remove_path = 0;
   2251 
   2252     if ((URI == NULL) || (*URI == 0))
   2253 	return NULL;
   2254 
   2255     /*
   2256      * First parse URI into a standard form
   2257      */
   2258     ref = xmlCreateURI ();
   2259     if (ref == NULL)
   2260 	return NULL;
   2261     /* If URI not already in "relative" form */
   2262     if (URI[0] != '.') {
   2263 	ret = xmlParseURIReference (ref, (const char *) URI);
   2264 	if (ret != 0)
   2265 	    goto done;		/* Error in URI, return NULL */
   2266     } else
   2267 	ref->path = (char *)xmlStrdup(URI);
   2268 
   2269     /*
   2270      * Next parse base into the same standard form
   2271      */
   2272     if ((base == NULL) || (*base == 0)) {
   2273 	val = xmlStrdup (URI);
   2274 	goto done;
   2275     }
   2276     bas = xmlCreateURI ();
   2277     if (bas == NULL)
   2278 	goto done;
   2279     if (base[0] != '.') {
   2280 	ret = xmlParseURIReference (bas, (const char *) base);
   2281 	if (ret != 0)
   2282 	    goto done;		/* Error in base, return NULL */
   2283     } else
   2284 	bas->path = (char *)xmlStrdup(base);
   2285 
   2286     /*
   2287      * If the scheme / server on the URI differs from the base,
   2288      * just return the URI
   2289      */
   2290     if ((ref->scheme != NULL) &&
   2291 	((bas->scheme == NULL) ||
   2292 	 (xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) ||
   2293 	 (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)))) {
   2294 	val = xmlStrdup (URI);
   2295 	goto done;
   2296     }
   2297     if (xmlStrEqual((xmlChar *)bas->path, (xmlChar *)ref->path)) {
   2298 	val = xmlStrdup(BAD_CAST "");
   2299 	goto done;
   2300     }
   2301     if (bas->path == NULL) {
   2302 	val = xmlStrdup((xmlChar *)ref->path);
   2303 	goto done;
   2304     }
   2305     if (ref->path == NULL) {
   2306         ref->path = (char *) "/";
   2307 	remove_path = 1;
   2308     }
   2309 
   2310     /*
   2311      * At this point (at last!) we can compare the two paths
   2312      *
   2313      * First we take care of the special case where either of the
   2314      * two path components may be missing (bug 316224)
   2315      */
   2316     if (bas->path == NULL) {
   2317 	if (ref->path != NULL) {
   2318 	    uptr = (xmlChar *) ref->path;
   2319 	    if (*uptr == '/')
   2320 		uptr++;
   2321 	    /* exception characters from xmlSaveUri */
   2322 	    val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,");
   2323 	}
   2324 	goto done;
   2325     }
   2326     bptr = (xmlChar *)bas->path;
   2327     if (ref->path == NULL) {
   2328 	for (ix = 0; bptr[ix] != 0; ix++) {
   2329 	    if (bptr[ix] == '/')
   2330 		nbslash++;
   2331 	}
   2332 	uptr = NULL;
   2333 	len = 1;	/* this is for a string terminator only */
   2334     } else {
   2335     /*
   2336      * Next we compare the two strings and find where they first differ
   2337      */
   2338 	if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
   2339             pos += 2;
   2340 	if ((*bptr == '.') && (bptr[1] == '/'))
   2341             bptr += 2;
   2342 	else if ((*bptr == '/') && (ref->path[pos] != '/'))
   2343 	    bptr++;
   2344 	while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0))
   2345 	    pos++;
   2346 
   2347 	if (bptr[pos] == ref->path[pos]) {
   2348 	    val = xmlStrdup(BAD_CAST "");
   2349 	    goto done;		/* (I can't imagine why anyone would do this) */
   2350 	}
   2351 
   2352 	/*
   2353 	 * In URI, "back up" to the last '/' encountered.  This will be the
   2354 	 * beginning of the "unique" suffix of URI
   2355 	 */
   2356 	ix = pos;
   2357 	if ((ref->path[ix] == '/') && (ix > 0))
   2358 	    ix--;
   2359 	else if ((ref->path[ix] == 0) && (ix > 1) && (ref->path[ix - 1] == '/'))
   2360 	    ix -= 2;
   2361 	for (; ix > 0; ix--) {
   2362 	    if (ref->path[ix] == '/')
   2363 		break;
   2364 	}
   2365 	if (ix == 0) {
   2366 	    uptr = (xmlChar *)ref->path;
   2367 	} else {
   2368 	    ix++;
   2369 	    uptr = (xmlChar *)&ref->path[ix];
   2370 	}
   2371 
   2372 	/*
   2373 	 * In base, count the number of '/' from the differing point
   2374 	 */
   2375 	if (bptr[pos] != ref->path[pos]) {/* check for trivial URI == base */
   2376 	    for (; bptr[ix] != 0; ix++) {
   2377 		if (bptr[ix] == '/')
   2378 		    nbslash++;
   2379 	    }
   2380 	}
   2381 	len = xmlStrlen (uptr) + 1;
   2382     }
   2383 
   2384     if (nbslash == 0) {
   2385 	if (uptr != NULL)
   2386 	    /* exception characters from xmlSaveUri */
   2387 	    val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,");
   2388 	goto done;
   2389     }
   2390 
   2391     /*
   2392      * Allocate just enough space for the returned string -
   2393      * length of the remainder of the URI, plus enough space
   2394      * for the "../" groups, plus one for the terminator
   2395      */
   2396     val = (xmlChar *) xmlMalloc (len + 3 * nbslash);
   2397     if (val == NULL) {
   2398 	xmlGenericError(xmlGenericErrorContext,
   2399 		"xmlBuildRelativeURI: out of memory\n");
   2400 	goto done;
   2401     }
   2402     vptr = val;
   2403     /*
   2404      * Put in as many "../" as needed
   2405      */
   2406     for (; nbslash>0; nbslash--) {
   2407 	*vptr++ = '.';
   2408 	*vptr++ = '.';
   2409 	*vptr++ = '/';
   2410     }
   2411     /*
   2412      * Finish up with the end of the URI
   2413      */
   2414     if (uptr != NULL) {
   2415         if ((vptr > val) && (len > 0) &&
   2416 	    (uptr[0] == '/') && (vptr[-1] == '/')) {
   2417 	    memcpy (vptr, uptr + 1, len - 1);
   2418 	    vptr[len - 2] = 0;
   2419 	} else {
   2420 	    memcpy (vptr, uptr, len);
   2421 	    vptr[len - 1] = 0;
   2422 	}
   2423     } else {
   2424 	vptr[len - 1] = 0;
   2425     }
   2426 
   2427     /* escape the freshly-built path */
   2428     vptr = val;
   2429 	/* exception characters from xmlSaveUri */
   2430     val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,");
   2431     xmlFree(vptr);
   2432 
   2433 done:
   2434     /*
   2435      * Free the working variables
   2436      */
   2437     if (remove_path != 0)
   2438         ref->path = NULL;
   2439     if (ref != NULL)
   2440 	xmlFreeURI (ref);
   2441     if (bas != NULL)
   2442 	xmlFreeURI (bas);
   2443 
   2444     return val;
   2445 }
   2446 
   2447 /**
   2448  * xmlCanonicPath:
   2449  * @path:  the resource locator in a filesystem notation
   2450  *
   2451  * Constructs a canonic path from the specified path.
   2452  *
   2453  * Returns a new canonic path, or a duplicate of the path parameter if the
   2454  * construction fails. The caller is responsible for freeing the memory occupied
   2455  * by the returned string. If there is insufficient memory available, or the
   2456  * argument is NULL, the function returns NULL.
   2457  */
   2458 #define IS_WINDOWS_PATH(p) 					\
   2459 	((p != NULL) &&						\
   2460 	 (((p[0] >= 'a') && (p[0] <= 'z')) ||			\
   2461 	  ((p[0] >= 'A') && (p[0] <= 'Z'))) &&			\
   2462 	 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
   2463 xmlChar *
   2464 xmlCanonicPath(const xmlChar *path)
   2465 {
   2466 /*
   2467  * For Windows implementations, additional work needs to be done to
   2468  * replace backslashes in pathnames with "forward slashes"
   2469  */
   2470 #if defined(_WIN32) && !defined(__CYGWIN__)
   2471     int len = 0;
   2472     int i = 0;
   2473     xmlChar *p = NULL;
   2474 #endif
   2475     xmlURIPtr uri;
   2476     xmlChar *ret;
   2477     const xmlChar *absuri;
   2478 
   2479     if (path == NULL)
   2480 	return(NULL);
   2481 
   2482     /* sanitize filename starting with // so it can be used as URI */
   2483     if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/'))
   2484         path++;
   2485 
   2486     if ((uri = xmlParseURI((const char *) path)) != NULL) {
   2487 	xmlFreeURI(uri);
   2488 	return xmlStrdup(path);
   2489     }
   2490 
   2491     /* Check if this is an "absolute uri" */
   2492     absuri = xmlStrstr(path, BAD_CAST "://");
   2493     if (absuri != NULL) {
   2494         int l, j;
   2495 	unsigned char c;
   2496 	xmlChar *escURI;
   2497 
   2498         /*
   2499 	 * this looks like an URI where some parts have not been
   2500 	 * escaped leading to a parsing problem.  Check that the first
   2501 	 * part matches a protocol.
   2502 	 */
   2503 	l = absuri - path;
   2504 	/* Bypass if first part (part before the '://') is > 20 chars */
   2505 	if ((l <= 0) || (l > 20))
   2506 	    goto path_processing;
   2507 	/* Bypass if any non-alpha characters are present in first part */
   2508 	for (j = 0;j < l;j++) {
   2509 	    c = path[j];
   2510 	    if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))))
   2511 	        goto path_processing;
   2512 	}
   2513 
   2514 	/* Escape all except the characters specified in the supplied path */
   2515         escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;=");
   2516 	if (escURI != NULL) {
   2517 	    /* Try parsing the escaped path */
   2518 	    uri = xmlParseURI((const char *) escURI);
   2519 	    /* If successful, return the escaped string */
   2520 	    if (uri != NULL) {
   2521 	        xmlFreeURI(uri);
   2522 		return escURI;
   2523 	    }
   2524 	}
   2525     }
   2526 
   2527 path_processing:
   2528 /* For Windows implementations, replace backslashes with 'forward slashes' */
   2529 #if defined(_WIN32) && !defined(__CYGWIN__)
   2530     /*
   2531      * Create a URI structure
   2532      */
   2533     uri = xmlCreateURI();
   2534     if (uri == NULL) {		/* Guard against 'out of memory' */
   2535         return(NULL);
   2536     }
   2537 
   2538     len = xmlStrlen(path);
   2539     if ((len > 2) && IS_WINDOWS_PATH(path)) {
   2540         /* make the scheme 'file' */
   2541 	uri->scheme = xmlStrdup(BAD_CAST "file");
   2542 	/* allocate space for leading '/' + path + string terminator */
   2543 	uri->path = xmlMallocAtomic(len + 2);
   2544 	if (uri->path == NULL) {
   2545 	    xmlFreeURI(uri);	/* Guard agains 'out of memory' */
   2546 	    return(NULL);
   2547 	}
   2548 	/* Put in leading '/' plus path */
   2549 	uri->path[0] = '/';
   2550 	p = uri->path + 1;
   2551 	strncpy(p, path, len + 1);
   2552     } else {
   2553 	uri->path = xmlStrdup(path);
   2554 	if (uri->path == NULL) {
   2555 	    xmlFreeURI(uri);
   2556 	    return(NULL);
   2557 	}
   2558 	p = uri->path;
   2559     }
   2560     /* Now change all occurences of '\' to '/' */
   2561     while (*p != '\0') {
   2562 	if (*p == '\\')
   2563 	    *p = '/';
   2564 	p++;
   2565     }
   2566 
   2567     if (uri->scheme == NULL) {
   2568 	ret = xmlStrdup((const xmlChar *) uri->path);
   2569     } else {
   2570 	ret = xmlSaveUri(uri);
   2571     }
   2572 
   2573     xmlFreeURI(uri);
   2574 #else
   2575     ret = xmlStrdup((const xmlChar *) path);
   2576 #endif
   2577     return(ret);
   2578 }
   2579 
   2580 /**
   2581  * xmlPathToURI:
   2582  * @path:  the resource locator in a filesystem notation
   2583  *
   2584  * Constructs an URI expressing the existing path
   2585  *
   2586  * Returns a new URI, or a duplicate of the path parameter if the
   2587  * construction fails. The caller is responsible for freeing the memory
   2588  * occupied by the returned string. If there is insufficient memory available,
   2589  * or the argument is NULL, the function returns NULL.
   2590  */
   2591 xmlChar *
   2592 xmlPathToURI(const xmlChar *path)
   2593 {
   2594     xmlURIPtr uri;
   2595     xmlURI temp;
   2596     xmlChar *ret, *cal;
   2597 
   2598     if (path == NULL)
   2599         return(NULL);
   2600 
   2601     if ((uri = xmlParseURI((const char *) path)) != NULL) {
   2602 	xmlFreeURI(uri);
   2603 	return xmlStrdup(path);
   2604     }
   2605     cal = xmlCanonicPath(path);
   2606     if (cal == NULL)
   2607         return(NULL);
   2608 #if defined(_WIN32) && !defined(__CYGWIN__)
   2609     /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?)
   2610        If 'cal' is a valid URI allready then we are done here, as continuing would make
   2611        it invalid. */
   2612     if ((uri = xmlParseURI((const char *) cal)) != NULL) {
   2613 	xmlFreeURI(uri);
   2614 	return cal;
   2615     }
   2616     /* 'cal' can contain a relative path with backslashes. If that is processed
   2617        by xmlSaveURI, they will be escaped and the external entity loader machinery
   2618        will fail. So convert them to slashes. Misuse 'ret' for walking. */
   2619     ret = cal;
   2620     while (*ret != '\0') {
   2621 	if (*ret == '\\')
   2622 	    *ret = '/';
   2623 	ret++;
   2624     }
   2625 #endif
   2626     memset(&temp, 0, sizeof(temp));
   2627     temp.path = (char *) cal;
   2628     ret = xmlSaveUri(&temp);
   2629     xmlFree(cal);
   2630     return(ret);
   2631 }
   2632 #define bottom_uri
   2633 #include "elfgcchack.h"
   2634