diff --git a/ChangeLog b/ChangeLog index 1ed7cc2d..26e9eb02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Mon Jun 7 16:57:43 HKT 2004 William Brack + + * uri.c, include/libxml/uri.h: added a new routine + xmlBuildRelativeURI needed for enhancement of xinclude.c + * xinclude.c: changed handling of xml:base (bug 135864) + * result/XInclude/*: results of 5 tests changed as a result + of the above change + Fri Jun 4 11:27:37 CEST 2004 Daniel Veillard * test/schemas/* result/schemas/*: added a bunch of tests from diff --git a/include/libxml/uri.h b/include/libxml/uri.h index 5e29e631..90ed06ec 100644 --- a/include/libxml/uri.h +++ b/include/libxml/uri.h @@ -47,8 +47,11 @@ struct _xmlURI { XMLPUBFUN xmlURIPtr XMLCALL xmlCreateURI (void); XMLPUBFUN xmlChar * XMLCALL - xmlBuildURI (const xmlChar *URI, - const xmlChar *base); + xmlBuildURI (const xmlChar *URI, + const xmlChar *base); +XMLPUBFUN xmlChar * XMLCALL + xmlBuildRelativeURI (const xmlChar *URI, + const xmlChar *base); XMLPUBFUN xmlURIPtr XMLCALL xmlParseURI (const char *str); XMLPUBFUN int XMLCALL diff --git a/result/XInclude/docids.xml b/result/XInclude/docids.xml index 389a216d..52a46143 100644 --- a/result/XInclude/docids.xml +++ b/result/XInclude/docids.xml @@ -11,7 +11,7 @@ - + diff --git a/result/XInclude/include.xml b/result/XInclude/include.xml index bee9da28..90296b40 100644 --- a/result/XInclude/include.xml +++ b/result/XInclude/include.xml @@ -1,7 +1,7 @@ - +

something

really

simple

diff --git a/result/XInclude/nodes.xml b/result/XInclude/nodes.xml index 301b70d5..c4e93fa6 100644 --- a/result/XInclude/nodes.xml +++ b/result/XInclude/nodes.xml @@ -1,5 +1,5 @@ -

something

really

simple

+

something

really

simple

diff --git a/result/XInclude/nodes2.xml b/result/XInclude/nodes2.xml index 34ef5f05..419f8b08 100644 --- a/result/XInclude/nodes2.xml +++ b/result/XInclude/nodes2.xml @@ -1,5 +1,5 @@ -

something

really

simple

+

something

really

simple

diff --git a/result/XInclude/recursive.xml b/result/XInclude/recursive.xml index c218c7d3..25f993c2 100644 --- a/result/XInclude/recursive.xml +++ b/result/XInclude/recursive.xml @@ -1,3 +1,3 @@ -is a test +is a test diff --git a/uri.c b/uri.c index 1b2c08a0..fe75e785 100644 --- a/uri.c +++ b/uri.c @@ -1971,6 +1971,181 @@ done: return(val); } +/** + * xmlBuildRelativeURI: + * @URI: the URI reference under consideration + * @base: the base value + * + * Expresses the URI of the reference in terms relative to the + * base. Some examples of this operation include: + * base = "http://site1.com/docs/book1.html" + * URI input URI returned + * docs/pic1.gif pic1.gif + * docs/img/pic1.gif img/pic1.gif + * img/pic1.gif ../img/pic1.gif + * http://site1.com/docs/pic1.gif pic1.gif + * http://site2.com/docs/pic1.gif http://site2.com/docs/pic1.gif + * + * base = "docs/book1.html" + * URI input URI returned + * docs/pic1.gif pic1.gif + * docs/img/pic1.gif img/pic1.gif + * img/pic1.gif ../img/pic1.gif + * http://site1.com/docs/pic1.gif http://site1.com/docs/pic1.gif + * + * + * Note: if the URI reference is really wierd or complicated, it may be + * worthwhile to first convert it into a "nice" one by calling + * xmlBuildURI (using 'base') before calling this routine, + * since this routine (for reasonable efficiency) assumes URI has + * already been through some validation. + * + * Returns a new URI string (to be freed by the caller) or NULL in case + * error. + */ +xmlChar * +xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) +{ + xmlChar *val = NULL; + int ret; + int ix; + int pos = 0; + int nbslash = 0; + xmlURIPtr ref = NULL; + xmlURIPtr bas = NULL; + xmlChar *bptr, *uptr, *vptr; + + if ((URI == NULL) || (*URI == 0)) + return NULL; + /* + * Special case - if URI starts with '.', we assume it's already + * in relative form, so nothing to do. + */ + if (*URI == '.') { + val = xmlStrdup (URI); + goto done; + } + + /* + * First parse URI into a standard form + */ + ref = xmlCreateURI (); + if (ref == NULL) + return NULL; + ret = xmlParseURIReference (ref, (const char *) URI); + if (ret != 0) + goto done; /* Error in URI, return NULL */ + + /* + * Next parse base into the same standard form + */ + if ((base == NULL) || (*base == 0)) { + val = xmlStrdup (URI); + goto done; + } + bas = xmlCreateURI (); + if (bas == NULL) + goto done; + ret = xmlParseURIReference (bas, (const char *) base); + if (ret != 0) + goto done; /* Error in base, return NULL */ + + /* + * If the scheme / server on the URI differs from the base, + * just return the URI + */ + if ((ref->scheme != NULL) && + ((bas->scheme == NULL) || + xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme) || + xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server))) { + val = xmlStrdup (URI); + goto done; + } + + /* + * At this point (at last!) we can compare the two paths + * + * First we compare the two strings and find where they first differ + */ + bptr = (xmlChar *)bas->path; + if ((*bptr == '/') && (ref->path[0] != '/')) + bptr++; + while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0)) + pos++; + + if (bptr[pos] == ref->path[pos]) { + val = NULL; /* if no differences, return NULL */ + goto done; /* (I can't imagine why anyone would do this) */ + } + + /* + * In URI, "back up" to the last '/' encountered. This will be the + * beginning of the "unique" suffix of URI + */ + ix = pos; + if ((ref->path[ix] == '/') && (ix > 0)) + ix--; + for (; ix > 0; ix--) { + if (ref->path[ix] == '/') + break; + } + if (ix == 0) + uptr = (xmlChar *)ref->path; + else + uptr = (xmlChar *)&ref->path[ix + 1]; + + /* + * In base, count the number of '/' from the differing point + */ + if (bptr[pos] != ref->path[pos]) { /* check for trivial URI == base */ + for (; bptr[ix] != 0; ix++) { + if (bptr[ix] == '/') + nbslash++; + } + } + + if (nbslash == 0) { + val = xmlStrdup (uptr); + goto done; + } + nbslash--; + + /* + * Allocate just enough space for the returned string - + * length of the remainder of the URI, plus enough space + * for the "../" groups, plus one for the terminator + */ + ix = xmlStrlen (uptr) + 1; + val = (xmlChar *) xmlMalloc (ix + 3 * nbslash); + if (val == NULL) { + goto done; + } + vptr = val; + /* + * Put in as many "../" as needed + */ + for (; nbslash>0; nbslash--) { + *vptr++ = '.'; + *vptr++ = '.'; + *vptr++ = '/'; + } + /* + * Finish up with the end of the URI + */ + memcpy (vptr, uptr, ix); + + done: + /* + * Free the working variables + */ + if (ref != NULL) + xmlFreeURI (ref); + if (bas != NULL) + xmlFreeURI (bas); + + return val; +} + /** * xmlCanonicPath: * @path: the resource locator in a filesystem notation diff --git a/xinclude.c b/xinclude.c index 347e0ad8..4f1db12c 100644 --- a/xinclude.c +++ b/xinclude.c @@ -81,6 +81,7 @@ struct _xmlXIncludeCtxt { int nbErrors; /* the number of errors detected */ int legacy; /* using XINCLUDE_OLD_NS */ int parseFlags; /* the flags used for parsing XML documents */ + xmlChar * base; /* the current xml:base */ }; static int @@ -677,6 +678,11 @@ xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, newctxt->urlNr = ctxt->urlNr; newctxt->urlTab = ctxt->urlTab; + /* + * Inherit the existing base + */ + newctxt->base = ctxt->base; + /* * Inherit the documents already in use by other includes */ @@ -1631,12 +1637,26 @@ loaded: */ if ((doc != NULL) && (URL != NULL) && (xmlStrchr(URL, (xmlChar) '/'))) { xmlNodePtr node; + xmlChar *relURI; - node = ctxt->incTab[nr]->inc; - while (node != NULL) { - if (node->type == XML_ELEMENT_NODE) - xmlNodeSetBase(node, URL); - node = node->next; + /* + * The base is only adjusted if necessary for the existing base + */ + relURI = xmlBuildRelativeURI(URL, ctxt->base); + if (relURI == NULL) { /* Error return */ + xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, + XML_XINCLUDE_HREF_URI, + "trying to build relative URI from %s\n", URL); + } else { + if (xmlStrchr(relURI, (xmlChar) '/')) { + node = ctxt->incTab[nr]->inc; + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) + xmlNodeSetBase(node, relURI); + node = node->next; + } + } + xmlFree(relURI); } } if ((nr < ctxt->incNr) && (ctxt->incTab[nr]->doc != NULL) && @@ -1814,6 +1834,7 @@ xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, int nr) { newctxt = xmlXIncludeNewContext(ctxt->doc); if (newctxt == NULL) return (-1); + newctxt->base = ctxt->base; /* Inherit the base from the existing context */ xmlXIncludeSetFlags(newctxt, ctxt->parseFlags); ret = xmlXIncludeDoProcess(newctxt, ctxt->doc, fallback->children); if (ctxt->nbErrors > 0) @@ -1867,6 +1888,7 @@ xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) { xmlChar *href; xmlChar *parse; xmlChar *base; + xmlChar *oldBase; xmlChar *URI; int xml = 1; /* default Issue 64 */ int ret; @@ -1947,14 +1969,23 @@ xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) { #endif /* - * Cleanup + * Save the base for this include (saving the current one) */ + oldBase = ctxt->base; + ctxt->base = base; + if (xml) { ret = xmlXIncludeLoadDoc(ctxt, URI, nr); /* xmlXIncludeGetFragment(ctxt, cur, URI); */ } else { ret = xmlXIncludeLoadTxt(ctxt, URI, nr); } + + /* + * Restore the original base before checking for fallback + */ + ctxt->base = oldBase; + if (ret < 0) { xmlNodePtr children; @@ -2296,6 +2327,7 @@ xmlXIncludeProcessFlags(xmlDocPtr doc, int flags) { ctxt = xmlXIncludeNewContext(doc); if (ctxt == NULL) return(-1); + ctxt->base = (xmlChar *)doc->URL; xmlXIncludeSetFlags(ctxt, flags); ret = xmlXIncludeDoProcess(ctxt, doc, tree); if ((ret >= 0) && (ctxt->nbErrors > 0)) @@ -2339,6 +2371,7 @@ xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) { ctxt = xmlXIncludeNewContext(tree->doc); if (ctxt == NULL) return(-1); + ctxt->base = xmlNodeGetBase(tree->doc, tree); xmlXIncludeSetFlags(ctxt, flags); ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree); if ((ret >= 0) && (ctxt->nbErrors > 0))