2009-01-21 12:09:32 +00:00
|
|
|
/*
|
|
|
|
* documents.c: Implementation of the documents handling
|
|
|
|
*
|
|
|
|
* See Copyright for the status of this software.
|
|
|
|
*
|
|
|
|
* daniel@veillard.com
|
|
|
|
*/
|
|
|
|
|
2014-01-13 13:16:57 +00:00
|
|
|
#include "precomp.h"
|
2009-01-21 12:09:32 +00:00
|
|
|
|
|
|
|
#ifdef LIBXML_XINCLUDE_ENABLED
|
|
|
|
#include <libxml/xinclude.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define WITH_XSLT_DEBUG_DOCUMENTS
|
|
|
|
|
|
|
|
#ifdef WITH_XSLT_DEBUG
|
|
|
|
#define WITH_XSLT_DEBUG_DOCUMENTS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/************************************************************************
|
2013-02-09 11:40:29 +00:00
|
|
|
* *
|
|
|
|
* Hooks for the document loader *
|
|
|
|
* *
|
2009-01-21 12:09:32 +00:00
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltDocDefaultLoaderFunc:
|
|
|
|
* @URI: the URI of the document to load
|
|
|
|
* @dict: the dictionary to use when parsing that document
|
|
|
|
* @options: parsing options, a set of xmlParserOption
|
|
|
|
* @ctxt: the context, either a stylesheet or a transformation context
|
|
|
|
* @type: the xsltLoadType indicating the kind of loading required
|
|
|
|
*
|
|
|
|
* Default function to load document not provided by the compilation or
|
|
|
|
* transformation API themselve, for example when an xsl:import,
|
|
|
|
* xsl:include is found at compilation time or when a document()
|
|
|
|
* call is made at runtime.
|
|
|
|
*
|
|
|
|
* Returns the pointer to the document (which will be modified and
|
|
|
|
* freed by the engine later), or NULL in case of error.
|
|
|
|
*/
|
|
|
|
static xmlDocPtr
|
|
|
|
xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options,
|
|
|
|
void *ctxt ATTRIBUTE_UNUSED,
|
|
|
|
xsltLoadType type ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
xmlParserCtxtPtr pctxt;
|
|
|
|
xmlParserInputPtr inputStream;
|
|
|
|
xmlDocPtr doc;
|
|
|
|
|
|
|
|
pctxt = xmlNewParserCtxt();
|
|
|
|
if (pctxt == NULL)
|
|
|
|
return(NULL);
|
|
|
|
if ((dict != NULL) && (pctxt->dict != NULL)) {
|
|
|
|
xmlDictFree(pctxt->dict);
|
|
|
|
pctxt->dict = NULL;
|
|
|
|
}
|
|
|
|
if (dict != NULL) {
|
|
|
|
pctxt->dict = dict;
|
|
|
|
xmlDictReference(pctxt->dict);
|
|
|
|
#ifdef WITH_XSLT_DEBUG
|
|
|
|
xsltGenericDebug(xsltGenericDebugContext,
|
|
|
|
"Reusing dictionary for document\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
xmlCtxtUseOptions(pctxt, options);
|
|
|
|
inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt);
|
|
|
|
if (inputStream == NULL) {
|
|
|
|
xmlFreeParserCtxt(pctxt);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
inputPush(pctxt, inputStream);
|
|
|
|
if (pctxt->directory == NULL)
|
|
|
|
pctxt->directory = xmlParserGetDirectory((const char *) URI);
|
|
|
|
|
|
|
|
xmlParseDocument(pctxt);
|
|
|
|
|
|
|
|
if (pctxt->wellFormed) {
|
|
|
|
doc = pctxt->myDoc;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
doc = NULL;
|
|
|
|
xmlFreeDoc(pctxt->myDoc);
|
|
|
|
pctxt->myDoc = NULL;
|
|
|
|
}
|
|
|
|
xmlFreeParserCtxt(pctxt);
|
|
|
|
|
|
|
|
return(doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltSetLoaderFunc:
|
|
|
|
* @f: the new function to handle document loading.
|
|
|
|
*
|
|
|
|
* Set the new function to load document, if NULL it resets it to the
|
|
|
|
* default function.
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
|
2009-01-21 12:09:32 +00:00
|
|
|
void
|
|
|
|
xsltSetLoaderFunc(xsltDocLoaderFunc f) {
|
|
|
|
if (f == NULL)
|
|
|
|
xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
|
|
|
|
else
|
|
|
|
xsltDocDefaultLoader = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Module interfaces *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltNewDocument:
|
|
|
|
* @ctxt: an XSLT transformation context (or NULL)
|
|
|
|
* @doc: a parsed XML document
|
|
|
|
*
|
|
|
|
* Register a new document, apply key computations
|
|
|
|
*
|
|
|
|
* Returns a handler to the document
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
xsltDocumentPtr
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) {
|
|
|
|
xsltDocumentPtr cur;
|
|
|
|
|
|
|
|
cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
|
|
|
|
if (cur == NULL) {
|
|
|
|
xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
|
|
|
|
"xsltNewDocument : malloc failed\n");
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
memset(cur, 0, sizeof(xsltDocument));
|
|
|
|
cur->doc = doc;
|
|
|
|
if (ctxt != NULL) {
|
|
|
|
if (! XSLT_IS_RES_TREE_FRAG(doc)) {
|
|
|
|
cur->next = ctxt->docList;
|
|
|
|
ctxt->docList = cur;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* A key with a specific name for a specific document
|
|
|
|
* will only be computed if there's a call to the key()
|
|
|
|
* function using that specific name for that specific
|
|
|
|
* document. I.e. computation of keys will be done in
|
|
|
|
* xsltGetKey() (keys.c) on an on-demand basis.
|
|
|
|
*
|
|
|
|
* xsltInitCtxtKeys(ctxt, cur); not called here anymore
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
return(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltNewStyleDocument:
|
|
|
|
* @style: an XSLT style sheet
|
|
|
|
* @doc: a parsed XML document
|
|
|
|
*
|
|
|
|
* Register a new document, apply key computations
|
|
|
|
*
|
|
|
|
* Returns a handler to the document
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
xsltDocumentPtr
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) {
|
|
|
|
xsltDocumentPtr cur;
|
|
|
|
|
|
|
|
cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
|
|
|
|
if (cur == NULL) {
|
|
|
|
xsltTransformError(NULL, style, (xmlNodePtr) doc,
|
|
|
|
"xsltNewStyleDocument : malloc failed\n");
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
memset(cur, 0, sizeof(xsltDocument));
|
|
|
|
cur->doc = doc;
|
|
|
|
if (style != NULL) {
|
|
|
|
cur->next = style->docList;
|
|
|
|
style->docList = cur;
|
|
|
|
}
|
|
|
|
return(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltFreeStyleDocuments:
|
|
|
|
* @style: an XSLT stylesheet (representing a stylesheet-level)
|
|
|
|
*
|
|
|
|
* Frees the node-trees (and xsltDocument structures) of all
|
|
|
|
* stylesheet-modules of the stylesheet-level represented by
|
2013-02-09 11:40:29 +00:00
|
|
|
* the given @style.
|
2009-01-21 12:09:32 +00:00
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
void
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltFreeStyleDocuments(xsltStylesheetPtr style) {
|
|
|
|
xsltDocumentPtr doc, cur;
|
|
|
|
#ifdef XSLT_REFACTORED_XSLT_NSCOMP
|
|
|
|
xsltNsMapPtr nsMap;
|
|
|
|
#endif
|
2013-02-09 11:40:29 +00:00
|
|
|
|
2009-01-21 12:09:32 +00:00
|
|
|
if (style == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef XSLT_REFACTORED_XSLT_NSCOMP
|
|
|
|
if (XSLT_HAS_INTERNAL_NSMAP(style))
|
|
|
|
nsMap = XSLT_GET_INTERNAL_NSMAP(style);
|
|
|
|
else
|
2013-02-09 11:40:29 +00:00
|
|
|
nsMap = NULL;
|
|
|
|
#endif
|
2009-01-21 12:09:32 +00:00
|
|
|
|
|
|
|
cur = style->docList;
|
|
|
|
while (cur != NULL) {
|
|
|
|
doc = cur;
|
|
|
|
cur = cur->next;
|
|
|
|
#ifdef XSLT_REFACTORED_XSLT_NSCOMP
|
|
|
|
/*
|
|
|
|
* Restore all changed namespace URIs of ns-decls.
|
|
|
|
*/
|
|
|
|
if (nsMap)
|
|
|
|
xsltRestoreDocumentNamespaces(nsMap, doc->doc);
|
|
|
|
#endif
|
|
|
|
xsltFreeDocumentKeys(doc);
|
|
|
|
if (!doc->main)
|
|
|
|
xmlFreeDoc(doc->doc);
|
|
|
|
xmlFree(doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltFreeDocuments:
|
|
|
|
* @ctxt: an XSLT transformation context
|
|
|
|
*
|
|
|
|
* Free up all the space used by the loaded documents
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
void
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltFreeDocuments(xsltTransformContextPtr ctxt) {
|
|
|
|
xsltDocumentPtr doc, cur;
|
|
|
|
|
|
|
|
cur = ctxt->docList;
|
|
|
|
while (cur != NULL) {
|
|
|
|
doc = cur;
|
|
|
|
cur = cur->next;
|
|
|
|
xsltFreeDocumentKeys(doc);
|
|
|
|
if (!doc->main)
|
|
|
|
xmlFreeDoc(doc->doc);
|
|
|
|
xmlFree(doc);
|
|
|
|
}
|
|
|
|
cur = ctxt->styleList;
|
|
|
|
while (cur != NULL) {
|
|
|
|
doc = cur;
|
|
|
|
cur = cur->next;
|
|
|
|
xsltFreeDocumentKeys(doc);
|
|
|
|
if (!doc->main)
|
|
|
|
xmlFreeDoc(doc->doc);
|
|
|
|
xmlFree(doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltLoadDocument:
|
|
|
|
* @ctxt: an XSLT transformation context
|
|
|
|
* @URI: the computed URI of the document
|
|
|
|
*
|
|
|
|
* Try to load a document (not a stylesheet)
|
|
|
|
* within the XSLT transformation context
|
|
|
|
*
|
|
|
|
* Returns the new xsltDocumentPtr or NULL in case of error
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
xsltDocumentPtr
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) {
|
|
|
|
xsltDocumentPtr ret;
|
|
|
|
xmlDocPtr doc;
|
|
|
|
|
|
|
|
if ((ctxt == NULL) || (URI == NULL))
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Security framework check
|
|
|
|
*/
|
|
|
|
if (ctxt->sec != NULL) {
|
|
|
|
int res;
|
2013-02-09 11:40:29 +00:00
|
|
|
|
2009-01-21 12:09:32 +00:00
|
|
|
res = xsltCheckRead(ctxt->sec, ctxt, URI);
|
|
|
|
if (res == 0) {
|
|
|
|
xsltTransformError(ctxt, NULL, NULL,
|
|
|
|
"xsltLoadDocument: read rights for %s denied\n",
|
|
|
|
URI);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the context list to find the document if preparsed
|
|
|
|
*/
|
|
|
|
ret = ctxt->docList;
|
|
|
|
while (ret != NULL) {
|
|
|
|
if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
|
|
|
|
(xmlStrEqual(ret->doc->URL, URI)))
|
|
|
|
return(ret);
|
|
|
|
ret = ret->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
|
|
|
|
(void *) ctxt, XSLT_LOAD_DOCUMENT);
|
|
|
|
|
|
|
|
if (doc == NULL)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
if (ctxt->xinclude != 0) {
|
|
|
|
#ifdef LIBXML_XINCLUDE_ENABLED
|
|
|
|
#if LIBXML_VERSION >= 20603
|
|
|
|
xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
|
|
|
|
#else
|
|
|
|
xmlXIncludeProcess(doc);
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
xsltTransformError(ctxt, NULL, NULL,
|
|
|
|
"xsltLoadDocument(%s) : XInclude processing not compiled in\n",
|
|
|
|
URI);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Apply white-space stripping if asked for
|
|
|
|
*/
|
|
|
|
if (xsltNeedElemSpaceHandling(ctxt))
|
|
|
|
xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
|
|
|
|
if (ctxt->debugStatus == XSLT_DEBUG_NONE)
|
|
|
|
xmlXPathOrderDocElems(doc);
|
|
|
|
|
|
|
|
ret = xsltNewDocument(ctxt, doc);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltLoadStyleDocument:
|
|
|
|
* @style: an XSLT style sheet
|
|
|
|
* @URI: the computed URI of the document
|
|
|
|
*
|
|
|
|
* Try to load a stylesheet document within the XSLT transformation context
|
|
|
|
*
|
|
|
|
* Returns the new xsltDocumentPtr or NULL in case of error
|
|
|
|
*/
|
2013-02-09 11:40:29 +00:00
|
|
|
xsltDocumentPtr
|
2009-01-21 12:09:32 +00:00
|
|
|
xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
|
|
|
|
xsltDocumentPtr ret;
|
|
|
|
xmlDocPtr doc;
|
|
|
|
xsltSecurityPrefsPtr sec;
|
|
|
|
|
|
|
|
if ((style == NULL) || (URI == NULL))
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Security framework check
|
|
|
|
*/
|
|
|
|
sec = xsltGetDefaultSecurityPrefs();
|
|
|
|
if (sec != NULL) {
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = xsltCheckRead(sec, NULL, URI);
|
|
|
|
if (res == 0) {
|
|
|
|
xsltTransformError(NULL, NULL, NULL,
|
|
|
|
"xsltLoadStyleDocument: read rights for %s denied\n",
|
|
|
|
URI);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the context list to find the document if preparsed
|
|
|
|
*/
|
|
|
|
ret = style->docList;
|
|
|
|
while (ret != NULL) {
|
|
|
|
if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
|
|
|
|
(xmlStrEqual(ret->doc->URL, URI)))
|
|
|
|
return(ret);
|
|
|
|
ret = ret->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
|
|
|
|
(void *) style, XSLT_LOAD_STYLESHEET);
|
|
|
|
if (doc == NULL)
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
ret = xsltNewStyleDocument(style, doc);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xsltFindDocument:
|
|
|
|
* @ctxt: an XSLT transformation context
|
|
|
|
* @doc: a parsed XML document
|
|
|
|
*
|
|
|
|
* Try to find a document within the XSLT transformation context.
|
|
|
|
* This will not find document infos for temporary
|
|
|
|
* Result Tree Fragments.
|
|
|
|
*
|
|
|
|
* Returns the desired xsltDocumentPtr or NULL in case of error
|
|
|
|
*/
|
|
|
|
xsltDocumentPtr
|
|
|
|
xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
|
|
|
|
xsltDocumentPtr ret;
|
|
|
|
|
|
|
|
if ((ctxt == NULL) || (doc == NULL))
|
|
|
|
return(NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the context list to find the document
|
|
|
|
*/
|
|
|
|
ret = ctxt->docList;
|
|
|
|
while (ret != NULL) {
|
|
|
|
if (ret->doc == doc)
|
|
|
|
return(ret);
|
|
|
|
ret = ret->next;
|
|
|
|
}
|
|
|
|
if (doc == ctxt->style->doc)
|
|
|
|
return(ctxt->document);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|